blob: f20065f13bdc3875cff71bb3af3406eefbae76d7 [file] [log] [blame]
ajs274a4a42004-12-07 15:39:31 +00001/*
ajs274a4a42004-12-07 15:39:31 +00002 Command interpreter routine for virtual terminal [aka TeletYpe]
paul718e3742002-12-13 20:15:29 +00003 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
Christian Frankecd40b322013-09-30 12:27:51 +00004 Copyright (C) 2013 by Open Source Routing.
5 Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
paul718e3742002-12-13 20:15:29 +00006
7This file is part of GNU Zebra.
8
9GNU Zebra is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published
11by the Free Software Foundation; either version 2, or (at your
12option) any later version.
13
14GNU Zebra is distributed in the hope that it will be useful, but
15WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with GNU Zebra; see the file COPYING. If not, write to the
21Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22Boston, MA 02111-1307, USA. */
23
24#include <zebra.h>
25
paulb21b19c2003-06-15 01:28:29 +000026
paul718e3742002-12-13 20:15:29 +000027#include "memory.h"
28#include "log.h"
gdt5e4fa162004-03-16 14:38:36 +000029#include <lib/version.h>
paul9ab68122003-01-18 01:16:20 +000030#include "thread.h"
paulb21b19c2003-06-15 01:28:29 +000031#include "vector.h"
32#include "vty.h"
33#include "command.h"
paul354d1192005-04-25 16:26:42 +000034#include "workqueue.h"
paul718e3742002-12-13 20:15:29 +000035
36/* Command vector which includes some level of command lists. Normally
37 each daemon maintains each own cmdvec. */
pauleb820af2005-09-05 11:54:13 +000038vector cmdvec = NULL;
paul718e3742002-12-13 20:15:29 +000039
Christian Frankecd40b322013-09-30 12:27:51 +000040struct cmd_token token_cr;
Chris Caputo228da422009-07-18 05:44:03 +000041char *command_cr = NULL;
42
Christian Frankecd40b322013-09-30 12:27:51 +000043enum filter_type
44{
45 FILTER_RELAXED,
46 FILTER_STRICT
47};
48
49enum matcher_rv
50{
51 MATCHER_OK,
52 MATCHER_COMPLETE,
53 MATCHER_INCOMPLETE,
54 MATCHER_NO_MATCH,
55 MATCHER_AMBIGUOUS,
56 MATCHER_EXCEED_ARGC_MAX
57};
58
59#define MATCHER_ERROR(matcher_rv) \
60 ( (matcher_rv) == MATCHER_INCOMPLETE \
61 || (matcher_rv) == MATCHER_NO_MATCH \
62 || (matcher_rv) == MATCHER_AMBIGUOUS \
63 || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \
64 )
65
paul718e3742002-12-13 20:15:29 +000066/* Host information structure. */
67struct host host;
68
paul718e3742002-12-13 20:15:29 +000069/* Standard command node structures. */
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080070static struct cmd_node auth_node =
paul718e3742002-12-13 20:15:29 +000071{
72 AUTH_NODE,
73 "Password: ",
74};
75
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080076static struct cmd_node view_node =
paul718e3742002-12-13 20:15:29 +000077{
78 VIEW_NODE,
79 "%s> ",
80};
81
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080082static struct cmd_node restricted_node =
Paul Jakma62687ff2008-08-23 14:27:06 +010083{
84 RESTRICTED_NODE,
85 "%s$ ",
86};
87
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080088static struct cmd_node auth_enable_node =
paul718e3742002-12-13 20:15:29 +000089{
90 AUTH_ENABLE_NODE,
91 "Password: ",
92};
93
Stephen Hemminger7fc626d2008-12-01 11:10:34 -080094static struct cmd_node enable_node =
paul718e3742002-12-13 20:15:29 +000095{
96 ENABLE_NODE,
97 "%s# ",
98};
99
Stephen Hemminger7fc626d2008-12-01 11:10:34 -0800100static struct cmd_node config_node =
paul718e3742002-12-13 20:15:29 +0000101{
102 CONFIG_NODE,
103 "%s(config)# ",
104 1
105};
hasso6590f2c2004-10-19 20:40:08 +0000106
107/* Default motd string. */
Everton Marques74b4fad2014-09-18 12:06:53 -0300108static const char *default_motd =
hasso6590f2c2004-10-19 20:40:08 +0000109"\r\n\
110Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
111" QUAGGA_COPYRIGHT "\r\n\
David Lamparter0be793e2012-11-27 01:34:56 +0000112" GIT_INFO "\r\n";
hasso6590f2c2004-10-19 20:40:08 +0000113
ajs274a4a42004-12-07 15:39:31 +0000114
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300115static const struct facility_map {
ajs274a4a42004-12-07 15:39:31 +0000116 int facility;
117 const char *name;
118 size_t match;
119} syslog_facilities[] =
120 {
121 { LOG_KERN, "kern", 1 },
122 { LOG_USER, "user", 2 },
123 { LOG_MAIL, "mail", 1 },
124 { LOG_DAEMON, "daemon", 1 },
125 { LOG_AUTH, "auth", 1 },
126 { LOG_SYSLOG, "syslog", 1 },
127 { LOG_LPR, "lpr", 2 },
128 { LOG_NEWS, "news", 1 },
129 { LOG_UUCP, "uucp", 2 },
130 { LOG_CRON, "cron", 1 },
131#ifdef LOG_FTP
132 { LOG_FTP, "ftp", 1 },
133#endif
134 { LOG_LOCAL0, "local0", 6 },
135 { LOG_LOCAL1, "local1", 6 },
136 { LOG_LOCAL2, "local2", 6 },
137 { LOG_LOCAL3, "local3", 6 },
138 { LOG_LOCAL4, "local4", 6 },
139 { LOG_LOCAL5, "local5", 6 },
140 { LOG_LOCAL6, "local6", 6 },
141 { LOG_LOCAL7, "local7", 6 },
142 { 0, NULL, 0 },
143 };
144
145static const char *
146facility_name(int facility)
147{
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300148 const struct facility_map *fm;
ajs274a4a42004-12-07 15:39:31 +0000149
150 for (fm = syslog_facilities; fm->name; fm++)
151 if (fm->facility == facility)
152 return fm->name;
153 return "";
154}
155
156static int
157facility_match(const char *str)
158{
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300159 const struct facility_map *fm;
ajs274a4a42004-12-07 15:39:31 +0000160
161 for (fm = syslog_facilities; fm->name; fm++)
162 if (!strncmp(str,fm->name,fm->match))
163 return fm->facility;
164 return -1;
165}
166
167static int
168level_match(const char *s)
169{
170 int level ;
171
172 for ( level = 0 ; zlog_priority [level] != NULL ; level ++ )
173 if (!strncmp (s, zlog_priority[level], 2))
174 return level;
175 return ZLOG_DISABLED;
176}
177
ajscb585b62005-01-14 17:09:38 +0000178/* This is called from main when a daemon is invoked with -v or --version. */
hasso6590f2c2004-10-19 20:40:08 +0000179void
180print_version (const char *progname)
181{
ajscb585b62005-01-14 17:09:38 +0000182 printf ("%s version %s\n", progname, QUAGGA_VERSION);
183 printf ("%s\n", QUAGGA_COPYRIGHT);
David Lamparter7abd8752014-11-22 10:43:29 -0800184 printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS);
hasso6590f2c2004-10-19 20:40:08 +0000185}
186
David Lamparter6b0655a2014-06-04 06:53:35 +0200187
paul718e3742002-12-13 20:15:29 +0000188/* Utility function to concatenate argv argument into a single string
189 with inserting ' ' character between each argument. */
190char *
paul42d49862004-10-13 05:22:18 +0000191argv_concat (const char **argv, int argc, int shift)
paul718e3742002-12-13 20:15:29 +0000192{
193 int i;
ajsf6834d42005-01-28 20:28:35 +0000194 size_t len;
paul718e3742002-12-13 20:15:29 +0000195 char *str;
ajsf6834d42005-01-28 20:28:35 +0000196 char *p;
paul718e3742002-12-13 20:15:29 +0000197
ajsf6834d42005-01-28 20:28:35 +0000198 len = 0;
199 for (i = shift; i < argc; i++)
200 len += strlen(argv[i])+1;
201 if (!len)
202 return NULL;
203 p = str = XMALLOC(MTYPE_TMP, len);
paul718e3742002-12-13 20:15:29 +0000204 for (i = shift; i < argc; i++)
205 {
ajsf6834d42005-01-28 20:28:35 +0000206 size_t arglen;
207 memcpy(p, argv[i], (arglen = strlen(argv[i])));
208 p += arglen;
209 *p++ = ' ';
paul718e3742002-12-13 20:15:29 +0000210 }
ajsf6834d42005-01-28 20:28:35 +0000211 *(p-1) = '\0';
paul718e3742002-12-13 20:15:29 +0000212 return str;
213}
214
215/* Install top node of command vector. */
216void
217install_node (struct cmd_node *node,
218 int (*func) (struct vty *))
219{
220 vector_set_index (cmdvec, node->node, node);
221 node->func = func;
222 node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
223}
224
paul718e3742002-12-13 20:15:29 +0000225/* Breaking up string into each command piece. I assume given
226 character is separated by a space character. Return value is a
227 vector which includes char ** data element. */
228vector
hassoea8e9d92004-10-07 21:32:14 +0000229cmd_make_strvec (const char *string)
paul718e3742002-12-13 20:15:29 +0000230{
hassoea8e9d92004-10-07 21:32:14 +0000231 const char *cp, *start;
232 char *token;
paul718e3742002-12-13 20:15:29 +0000233 int strlen;
234 vector strvec;
235
236 if (string == NULL)
237 return NULL;
238
239 cp = string;
240
241 /* Skip white spaces. */
242 while (isspace ((int) *cp) && *cp != '\0')
243 cp++;
244
245 /* Return if there is only white spaces */
246 if (*cp == '\0')
247 return NULL;
248
249 if (*cp == '!' || *cp == '#')
250 return NULL;
251
252 /* Prepare return vector. */
253 strvec = vector_init (VECTOR_MIN_SIZE);
254
255 /* Copy each command piece and set into vector. */
256 while (1)
257 {
258 start = cp;
259 while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
260 *cp != '\0')
261 cp++;
262 strlen = cp - start;
263 token = XMALLOC (MTYPE_STRVEC, strlen + 1);
264 memcpy (token, start, strlen);
265 *(token + strlen) = '\0';
266 vector_set (strvec, token);
267
268 while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
269 *cp != '\0')
270 cp++;
271
272 if (*cp == '\0')
273 return strvec;
274 }
275}
276
277/* Free allocated string vector. */
278void
279cmd_free_strvec (vector v)
280{
hasso8c328f12004-10-05 21:01:23 +0000281 unsigned int i;
paul718e3742002-12-13 20:15:29 +0000282 char *cp;
283
284 if (!v)
285 return;
286
paul55468c82005-03-14 20:19:01 +0000287 for (i = 0; i < vector_active (v); i++)
paul718e3742002-12-13 20:15:29 +0000288 if ((cp = vector_slot (v, i)) != NULL)
289 XFREE (MTYPE_STRVEC, cp);
290
291 vector_free (v);
292}
293
Christian Frankecd40b322013-09-30 12:27:51 +0000294struct format_parser_state
295{
296 vector topvect; /* Top level vector */
297 vector intvect; /* Intermediate level vector, used when there's
298 * a multiple in a keyword. */
299 vector curvect; /* current vector where read tokens should be
300 appended. */
301
302 const char *string; /* pointer to command string, not modified */
303 const char *cp; /* pointer in command string, moved along while
304 parsing */
305 const char *dp; /* pointer in description string, moved along while
306 parsing */
307
308 int in_keyword; /* flag to remember if we are in a keyword group */
309 int in_multiple; /* flag to remember if we are in a multiple group */
310 int just_read_word; /* flag to remember if the last thing we red was a
311 * real word and not some abstract token */
312};
313
314static void
315format_parser_error(struct format_parser_state *state, const char *message)
316{
317 int offset = state->cp - state->string + 1;
318
319 fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string);
320 fprintf(stderr, " %*c\n", offset, '^');
321 fprintf(stderr, "%s at offset %d.\n", message, offset);
322 fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n");
323 exit(1);
324}
325
ajs274a4a42004-12-07 15:39:31 +0000326static char *
Christian Frankecd40b322013-09-30 12:27:51 +0000327format_parser_desc_str(struct format_parser_state *state)
paul718e3742002-12-13 20:15:29 +0000328{
hasso6ad96ea2004-10-07 19:33:46 +0000329 const char *cp, *start;
330 char *token;
paul718e3742002-12-13 20:15:29 +0000331 int strlen;
Christian Frankecd40b322013-09-30 12:27:51 +0000332
333 cp = state->dp;
paul718e3742002-12-13 20:15:29 +0000334
335 if (cp == NULL)
336 return NULL;
337
338 /* Skip white spaces. */
339 while (isspace ((int) *cp) && *cp != '\0')
340 cp++;
341
342 /* Return if there is only white spaces */
343 if (*cp == '\0')
344 return NULL;
345
346 start = cp;
347
348 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
349 cp++;
350
351 strlen = cp - start;
Christian Frankecd40b322013-09-30 12:27:51 +0000352 token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1);
paul718e3742002-12-13 20:15:29 +0000353 memcpy (token, start, strlen);
354 *(token + strlen) = '\0';
355
Christian Frankecd40b322013-09-30 12:27:51 +0000356 state->dp = cp;
paul718e3742002-12-13 20:15:29 +0000357
358 return token;
359}
360
Christian Frankecd40b322013-09-30 12:27:51 +0000361static void
362format_parser_begin_keyword(struct format_parser_state *state)
paul718e3742002-12-13 20:15:29 +0000363{
Christian Frankecd40b322013-09-30 12:27:51 +0000364 struct cmd_token *token;
365 vector keyword_vect;
paul718e3742002-12-13 20:15:29 +0000366
Christian Frankecd40b322013-09-30 12:27:51 +0000367 if (state->in_keyword
368 || state->in_multiple)
369 format_parser_error(state, "Unexpected '{'");
paul718e3742002-12-13 20:15:29 +0000370
Christian Frankecd40b322013-09-30 12:27:51 +0000371 state->cp++;
372 state->in_keyword = 1;
paul718e3742002-12-13 20:15:29 +0000373
Christian Frankecd40b322013-09-30 12:27:51 +0000374 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
375 token->type = TOKEN_KEYWORD;
376 token->keyword = vector_init(VECTOR_MIN_SIZE);
paul718e3742002-12-13 20:15:29 +0000377
Christian Frankecd40b322013-09-30 12:27:51 +0000378 keyword_vect = vector_init(VECTOR_MIN_SIZE);
379 vector_set(token->keyword, keyword_vect);
380
381 vector_set(state->curvect, token);
382 state->curvect = keyword_vect;
383}
384
385static void
386format_parser_begin_multiple(struct format_parser_state *state)
387{
388 struct cmd_token *token;
389
390 if (state->in_keyword == 1)
391 format_parser_error(state, "Keyword starting with '('");
392
393 if (state->in_multiple)
394 format_parser_error(state, "Nested group");
395
396 state->cp++;
397 state->in_multiple = 1;
398 state->just_read_word = 0;
399
400 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
401 token->type = TOKEN_MULTIPLE;
402 token->multiple = vector_init(VECTOR_MIN_SIZE);
403
404 vector_set(state->curvect, token);
405 if (state->curvect != state->topvect)
406 state->intvect = state->curvect;
407 state->curvect = token->multiple;
408}
409
410static void
411format_parser_end_keyword(struct format_parser_state *state)
412{
413 if (state->in_multiple
414 || !state->in_keyword)
415 format_parser_error(state, "Unexpected '}'");
416
417 if (state->in_keyword == 1)
418 format_parser_error(state, "Empty keyword group");
419
420 state->cp++;
421 state->in_keyword = 0;
422 state->curvect = state->topvect;
423}
424
425static void
426format_parser_end_multiple(struct format_parser_state *state)
427{
428 char *dummy;
429
430 if (!state->in_multiple)
431 format_parser_error(state, "Unepexted ')'");
432
433 if (vector_active(state->curvect) == 0)
434 format_parser_error(state, "Empty multiple section");
435
436 if (!state->just_read_word)
paul718e3742002-12-13 20:15:29 +0000437 {
Christian Frankecd40b322013-09-30 12:27:51 +0000438 /* There are constructions like
439 * 'show ip ospf database ... (self-originate|)'
440 * in use.
441 * The old parser reads a description string for the
442 * word '' between |) which will never match.
443 * Simulate this behvaior by dropping the next desc
444 * string in such a case. */
paul718e3742002-12-13 20:15:29 +0000445
Christian Frankecd40b322013-09-30 12:27:51 +0000446 dummy = format_parser_desc_str(state);
447 XFREE(MTYPE_CMD_TOKENS, dummy);
448 }
paul718e3742002-12-13 20:15:29 +0000449
Christian Frankecd40b322013-09-30 12:27:51 +0000450 state->cp++;
451 state->in_multiple = 0;
paul718e3742002-12-13 20:15:29 +0000452
Christian Frankecd40b322013-09-30 12:27:51 +0000453 if (state->intvect)
454 state->curvect = state->intvect;
455 else
456 state->curvect = state->topvect;
457}
paul718e3742002-12-13 20:15:29 +0000458
Christian Frankecd40b322013-09-30 12:27:51 +0000459static void
460format_parser_handle_pipe(struct format_parser_state *state)
461{
462 struct cmd_token *keyword_token;
463 vector keyword_vect;
paul718e3742002-12-13 20:15:29 +0000464
Christian Frankecd40b322013-09-30 12:27:51 +0000465 if (state->in_multiple)
466 {
467 state->just_read_word = 0;
468 state->cp++;
469 }
470 else if (state->in_keyword)
471 {
472 state->in_keyword = 1;
473 state->cp++;
paul718e3742002-12-13 20:15:29 +0000474
Christian Frankecd40b322013-09-30 12:27:51 +0000475 keyword_token = vector_slot(state->topvect,
476 vector_active(state->topvect) - 1);
477 keyword_vect = vector_init(VECTOR_MIN_SIZE);
478 vector_set(keyword_token->keyword, keyword_vect);
479 state->curvect = keyword_vect;
480 }
481 else
482 {
483 format_parser_error(state, "Unexpected '|'");
paul718e3742002-12-13 20:15:29 +0000484 }
485}
486
Christian Frankecd40b322013-09-30 12:27:51 +0000487static void
488format_parser_read_word(struct format_parser_state *state)
paul718e3742002-12-13 20:15:29 +0000489{
Christian Frankecd40b322013-09-30 12:27:51 +0000490 const char *start;
491 int len;
492 char *cmd;
493 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +0000494
Christian Frankecd40b322013-09-30 12:27:51 +0000495 start = state->cp;
496
497 while (state->cp[0] != '\0'
498 && !strchr("\r\n(){}|", state->cp[0])
499 && !isspace((int)state->cp[0]))
500 state->cp++;
501
502 len = state->cp - start;
503 cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1);
504 memcpy(cmd, start, len);
505 cmd[len] = '\0';
506
507 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
508 token->type = TOKEN_TERMINAL;
509 token->cmd = cmd;
510 token->desc = format_parser_desc_str(state);
511 vector_set(state->curvect, token);
512
513 if (state->in_keyword == 1)
514 state->in_keyword = 2;
515
516 state->just_read_word = 1;
517}
518
519/**
520 * Parse a given command format string and build a tree of tokens from
521 * it that is suitable to be used by the command subsystem.
522 *
523 * @param string Command format string.
524 * @param descstr Description string.
525 * @return A vector of struct cmd_token representing the given command,
526 * or NULL on error.
527 */
528static vector
529cmd_parse_format(const char *string, const char *descstr)
530{
531 struct format_parser_state state;
532
533 if (string == NULL)
534 return NULL;
535
536 memset(&state, 0, sizeof(state));
537 state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE);
538 state.cp = state.string = string;
539 state.dp = descstr;
540
541 while (1)
paul718e3742002-12-13 20:15:29 +0000542 {
Christian Frankecd40b322013-09-30 12:27:51 +0000543 while (isspace((int)state.cp[0]) && state.cp[0] != '\0')
544 state.cp++;
545
546 switch (state.cp[0])
547 {
548 case '\0':
549 if (state.in_keyword
550 || state.in_multiple)
551 format_parser_error(&state, "Unclosed group/keyword");
552 return state.topvect;
553 case '{':
554 format_parser_begin_keyword(&state);
555 break;
556 case '(':
557 format_parser_begin_multiple(&state);
558 break;
559 case '}':
560 format_parser_end_keyword(&state);
561 break;
562 case ')':
563 format_parser_end_multiple(&state);
564 break;
565 case '|':
566 format_parser_handle_pipe(&state);
567 break;
568 default:
569 format_parser_read_word(&state);
570 }
paul718e3742002-12-13 20:15:29 +0000571 }
paul718e3742002-12-13 20:15:29 +0000572}
573
574/* Return prompt character of specified node. */
hasso8c328f12004-10-05 21:01:23 +0000575const char *
paul718e3742002-12-13 20:15:29 +0000576cmd_prompt (enum node_type node)
577{
578 struct cmd_node *cnode;
579
580 cnode = vector_slot (cmdvec, node);
581 return cnode->prompt;
582}
583
584/* Install a command into a node. */
585void
586install_element (enum node_type ntype, struct cmd_element *cmd)
587{
588 struct cmd_node *cnode;
pauleb820af2005-09-05 11:54:13 +0000589
590 /* cmd_init hasn't been called */
591 if (!cmdvec)
592 return;
593
paul718e3742002-12-13 20:15:29 +0000594 cnode = vector_slot (cmdvec, ntype);
595
596 if (cnode == NULL)
597 {
598 fprintf (stderr, "Command node %d doesn't exist, please check it\n",
599 ntype);
600 exit (1);
601 }
602
603 vector_set (cnode->cmd_vector, cmd);
Christian Frankecd40b322013-09-30 12:27:51 +0000604 if (cmd->tokens == NULL)
605 cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);
paul718e3742002-12-13 20:15:29 +0000606}
607
Stephen Hemminger2d362d12009-12-21 12:54:58 +0300608static const unsigned char itoa64[] =
paul718e3742002-12-13 20:15:29 +0000609"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
610
ajs274a4a42004-12-07 15:39:31 +0000611static void
paul718e3742002-12-13 20:15:29 +0000612to64(char *s, long v, int n)
613{
614 while (--n >= 0)
615 {
616 *s++ = itoa64[v&0x3f];
617 v >>= 6;
618 }
619}
620
ajs274a4a42004-12-07 15:39:31 +0000621static char *
622zencrypt (const char *passwd)
paul718e3742002-12-13 20:15:29 +0000623{
624 char salt[6];
625 struct timeval tv;
626 char *crypt (const char *, const char *);
627
628 gettimeofday(&tv,0);
629
630 to64(&salt[0], random(), 3);
631 to64(&salt[3], tv.tv_usec, 3);
632 salt[5] = '\0';
633
634 return crypt (passwd, salt);
635}
636
637/* This function write configuration of this host. */
ajs274a4a42004-12-07 15:39:31 +0000638static int
paul718e3742002-12-13 20:15:29 +0000639config_write_host (struct vty *vty)
640{
641 if (host.name)
642 vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
643
644 if (host.encrypt)
645 {
646 if (host.password_encrypt)
647 vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
648 if (host.enable_encrypt)
649 vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
650 }
651 else
652 {
653 if (host.password)
654 vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
655 if (host.enable)
656 vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
657 }
658
ajs274a4a42004-12-07 15:39:31 +0000659 if (zlog_default->default_lvl != LOG_DEBUG)
ajs82146b82004-12-07 17:15:55 +0000660 {
661 vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
662 VTY_NEWLINE);
663 vty_out (vty, "log trap %s%s",
664 zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
665 }
paul718e3742002-12-13 20:15:29 +0000666
ajs274a4a42004-12-07 15:39:31 +0000667 if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
paul12ab19f2003-07-26 06:14:55 +0000668 {
ajs274a4a42004-12-07 15:39:31 +0000669 vty_out (vty, "log file %s", host.logfile);
670 if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
671 vty_out (vty, " %s",
672 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
paul12ab19f2003-07-26 06:14:55 +0000673 vty_out (vty, "%s", VTY_NEWLINE);
674 }
ajs274a4a42004-12-07 15:39:31 +0000675
676 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
677 {
678 vty_out (vty, "log stdout");
679 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
680 vty_out (vty, " %s",
681 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
682 vty_out (vty, "%s", VTY_NEWLINE);
683 }
684
685 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
686 vty_out(vty,"no log monitor%s",VTY_NEWLINE);
687 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
688 vty_out(vty,"log monitor %s%s",
689 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
690
691 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
692 {
693 vty_out (vty, "log syslog");
694 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
695 vty_out (vty, " %s",
696 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
697 vty_out (vty, "%s", VTY_NEWLINE);
698 }
699
700 if (zlog_default->facility != LOG_DAEMON)
701 vty_out (vty, "log facility %s%s",
702 facility_name(zlog_default->facility), VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +0000703
704 if (zlog_default->record_priority == 1)
705 vty_out (vty, "log record-priority%s", VTY_NEWLINE);
706
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +0000707 if (zlog_default->timestamp_precision > 0)
708 vty_out (vty, "log timestamp precision %d%s",
709 zlog_default->timestamp_precision, VTY_NEWLINE);
710
paul718e3742002-12-13 20:15:29 +0000711 if (host.advanced)
712 vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
713
714 if (host.encrypt)
715 vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
716
717 if (host.lines >= 0)
718 vty_out (vty, "service terminal-length %d%s", host.lines,
719 VTY_NEWLINE);
720
paul3b0c5d92005-03-08 10:43:43 +0000721 if (host.motdfile)
722 vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
723 else if (! host.motd)
paul718e3742002-12-13 20:15:29 +0000724 vty_out (vty, "no banner motd%s", VTY_NEWLINE);
725
726 return 1;
727}
728
729/* Utility function for getting command vector. */
ajs274a4a42004-12-07 15:39:31 +0000730static vector
paul718e3742002-12-13 20:15:29 +0000731cmd_node_vector (vector v, enum node_type ntype)
732{
733 struct cmd_node *cnode = vector_slot (v, ntype);
734 return cnode->cmd_vector;
735}
736
ajs274a4a42004-12-07 15:39:31 +0000737#if 0
738/* Filter command vector by symbol. This function is not actually used;
739 * should it be deleted? */
740static int
paul718e3742002-12-13 20:15:29 +0000741cmd_filter_by_symbol (char *command, char *symbol)
742{
743 int i, lim;
744
745 if (strcmp (symbol, "IPV4_ADDRESS") == 0)
746 {
747 i = 0;
748 lim = strlen (command);
749 while (i < lim)
750 {
751 if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/'))
752 return 1;
753 i++;
754 }
755 return 0;
756 }
757 if (strcmp (symbol, "STRING") == 0)
758 {
759 i = 0;
760 lim = strlen (command);
761 while (i < lim)
762 {
763 if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-'))
764 return 1;
765 i++;
766 }
767 return 0;
768 }
769 if (strcmp (symbol, "IFNAME") == 0)
770 {
771 i = 0;
772 lim = strlen (command);
773 while (i < lim)
774 {
775 if (! isalnum ((int) command[i]))
776 return 1;
777 i++;
778 }
779 return 0;
780 }
781 return 0;
782}
ajs274a4a42004-12-07 15:39:31 +0000783#endif
paul718e3742002-12-13 20:15:29 +0000784
785/* Completion match types. */
786enum match_type
787{
788 no_match,
789 extend_match,
790 ipv4_prefix_match,
791 ipv4_match,
792 ipv6_prefix_match,
793 ipv6_match,
794 range_match,
795 vararg_match,
796 partly_match,
797 exact_match
798};
799
ajs274a4a42004-12-07 15:39:31 +0000800static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000801cmd_ipv4_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000802{
hasso8c328f12004-10-05 21:01:23 +0000803 const char *sp;
paul718e3742002-12-13 20:15:29 +0000804 int dots = 0, nums = 0;
805 char buf[4];
806
807 if (str == NULL)
808 return partly_match;
809
810 for (;;)
811 {
812 memset (buf, 0, sizeof (buf));
813 sp = str;
814 while (*str != '\0')
815 {
816 if (*str == '.')
817 {
818 if (dots >= 3)
819 return no_match;
820
821 if (*(str + 1) == '.')
822 return no_match;
823
824 if (*(str + 1) == '\0')
825 return partly_match;
826
827 dots++;
828 break;
829 }
830 if (!isdigit ((int) *str))
831 return no_match;
832
833 str++;
834 }
835
836 if (str - sp > 3)
837 return no_match;
838
839 strncpy (buf, sp, str - sp);
840 if (atoi (buf) > 255)
841 return no_match;
842
843 nums++;
844
845 if (*str == '\0')
846 break;
847
848 str++;
849 }
850
851 if (nums < 4)
852 return partly_match;
853
854 return exact_match;
855}
856
ajs274a4a42004-12-07 15:39:31 +0000857static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000858cmd_ipv4_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000859{
hasso8c328f12004-10-05 21:01:23 +0000860 const char *sp;
paul718e3742002-12-13 20:15:29 +0000861 int dots = 0;
862 char buf[4];
863
864 if (str == NULL)
865 return partly_match;
866
867 for (;;)
868 {
869 memset (buf, 0, sizeof (buf));
870 sp = str;
871 while (*str != '\0' && *str != '/')
872 {
873 if (*str == '.')
874 {
875 if (dots == 3)
876 return no_match;
877
878 if (*(str + 1) == '.' || *(str + 1) == '/')
879 return no_match;
880
881 if (*(str + 1) == '\0')
882 return partly_match;
883
884 dots++;
885 break;
886 }
887
888 if (!isdigit ((int) *str))
889 return no_match;
890
891 str++;
892 }
893
894 if (str - sp > 3)
895 return no_match;
896
897 strncpy (buf, sp, str - sp);
898 if (atoi (buf) > 255)
899 return no_match;
900
901 if (dots == 3)
902 {
903 if (*str == '/')
904 {
905 if (*(str + 1) == '\0')
906 return partly_match;
907
908 str++;
909 break;
910 }
911 else if (*str == '\0')
912 return partly_match;
913 }
914
915 if (*str == '\0')
916 return partly_match;
917
918 str++;
919 }
920
921 sp = str;
922 while (*str != '\0')
923 {
924 if (!isdigit ((int) *str))
925 return no_match;
926
927 str++;
928 }
929
930 if (atoi (sp) > 32)
931 return no_match;
932
933 return exact_match;
934}
935
936#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
937#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
938#define STATE_START 1
939#define STATE_COLON 2
940#define STATE_DOUBLE 3
941#define STATE_ADDR 4
942#define STATE_DOT 5
943#define STATE_SLASH 6
944#define STATE_MASK 7
945
paul22e0a9e2003-07-11 17:55:46 +0000946#ifdef HAVE_IPV6
947
ajs274a4a42004-12-07 15:39:31 +0000948static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000949cmd_ipv6_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000950{
hasso726f9b22003-05-25 21:04:54 +0000951 struct sockaddr_in6 sin6_dummy;
952 int ret;
paul718e3742002-12-13 20:15:29 +0000953
954 if (str == NULL)
955 return partly_match;
956
957 if (strspn (str, IPV6_ADDR_STR) != strlen (str))
958 return no_match;
959
hasso726f9b22003-05-25 21:04:54 +0000960 /* use inet_pton that has a better support,
961 * for example inet_pton can support the automatic addresses:
962 * ::1.2.3.4
963 */
964 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
965
966 if (ret == 1)
967 return exact_match;
968
Roman Hoog Antink7c9c6ae2012-05-09 06:35:34 +0000969 return no_match;
paul718e3742002-12-13 20:15:29 +0000970}
971
ajs274a4a42004-12-07 15:39:31 +0000972static enum match_type
hasso8c328f12004-10-05 21:01:23 +0000973cmd_ipv6_prefix_match (const char *str)
paul718e3742002-12-13 20:15:29 +0000974{
975 int state = STATE_START;
976 int colons = 0, nums = 0, double_colon = 0;
977 int mask;
hasso8c328f12004-10-05 21:01:23 +0000978 const char *sp = NULL;
paul718e3742002-12-13 20:15:29 +0000979 char *endptr = NULL;
980
981 if (str == NULL)
982 return partly_match;
983
984 if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
985 return no_match;
986
987 while (*str != '\0' && state != STATE_MASK)
988 {
989 switch (state)
990 {
991 case STATE_START:
992 if (*str == ':')
993 {
994 if (*(str + 1) != ':' && *(str + 1) != '\0')
995 return no_match;
996 colons--;
997 state = STATE_COLON;
998 }
999 else
1000 {
1001 sp = str;
1002 state = STATE_ADDR;
1003 }
1004
1005 continue;
1006 case STATE_COLON:
1007 colons++;
1008 if (*(str + 1) == '/')
1009 return no_match;
1010 else if (*(str + 1) == ':')
1011 state = STATE_DOUBLE;
1012 else
1013 {
1014 sp = str + 1;
1015 state = STATE_ADDR;
1016 }
1017 break;
1018 case STATE_DOUBLE:
1019 if (double_colon)
1020 return no_match;
1021
1022 if (*(str + 1) == ':')
1023 return no_match;
1024 else
1025 {
1026 if (*(str + 1) != '\0' && *(str + 1) != '/')
1027 colons++;
1028 sp = str + 1;
1029
1030 if (*(str + 1) == '/')
1031 state = STATE_SLASH;
1032 else
1033 state = STATE_ADDR;
1034 }
1035
1036 double_colon++;
1037 nums += 1;
1038 break;
1039 case STATE_ADDR:
1040 if (*(str + 1) == ':' || *(str + 1) == '.'
1041 || *(str + 1) == '\0' || *(str + 1) == '/')
1042 {
1043 if (str - sp > 3)
1044 return no_match;
1045
1046 for (; sp <= str; sp++)
1047 if (*sp == '/')
1048 return no_match;
1049
1050 nums++;
1051
1052 if (*(str + 1) == ':')
1053 state = STATE_COLON;
1054 else if (*(str + 1) == '.')
David Lamparteraa5cf242012-07-19 16:11:50 +02001055 {
1056 if (colons || double_colon)
1057 state = STATE_DOT;
1058 else
1059 return no_match;
1060 }
paul718e3742002-12-13 20:15:29 +00001061 else if (*(str + 1) == '/')
1062 state = STATE_SLASH;
1063 }
1064 break;
1065 case STATE_DOT:
1066 state = STATE_ADDR;
1067 break;
1068 case STATE_SLASH:
1069 if (*(str + 1) == '\0')
1070 return partly_match;
1071
1072 state = STATE_MASK;
1073 break;
1074 default:
1075 break;
1076 }
1077
1078 if (nums > 11)
1079 return no_match;
1080
1081 if (colons > 7)
1082 return no_match;
1083
1084 str++;
1085 }
1086
1087 if (state < STATE_MASK)
1088 return partly_match;
1089
1090 mask = strtol (str, &endptr, 10);
1091 if (*endptr != '\0')
1092 return no_match;
1093
1094 if (mask < 0 || mask > 128)
1095 return no_match;
1096
1097/* I don't know why mask < 13 makes command match partly.
1098 Forgive me to make this comments. I Want to set static default route
1099 because of lack of function to originate default in ospf6d; sorry
1100 yasu
1101 if (mask < 13)
1102 return partly_match;
1103*/
1104
1105 return exact_match;
1106}
1107
paul22e0a9e2003-07-11 17:55:46 +00001108#endif /* HAVE_IPV6 */
1109
paul718e3742002-12-13 20:15:29 +00001110#define DECIMAL_STRLEN_MAX 10
1111
ajs274a4a42004-12-07 15:39:31 +00001112static int
hasso8c328f12004-10-05 21:01:23 +00001113cmd_range_match (const char *range, const char *str)
paul718e3742002-12-13 20:15:29 +00001114{
1115 char *p;
1116 char buf[DECIMAL_STRLEN_MAX + 1];
1117 char *endptr = NULL;
1118 unsigned long min, max, val;
1119
1120 if (str == NULL)
1121 return 1;
1122
1123 val = strtoul (str, &endptr, 10);
1124 if (*endptr != '\0')
1125 return 0;
1126
1127 range++;
1128 p = strchr (range, '-');
1129 if (p == NULL)
1130 return 0;
1131 if (p - range > DECIMAL_STRLEN_MAX)
1132 return 0;
1133 strncpy (buf, range, p - range);
1134 buf[p - range] = '\0';
1135 min = strtoul (buf, &endptr, 10);
1136 if (*endptr != '\0')
1137 return 0;
1138
1139 range = p + 1;
1140 p = strchr (range, '>');
1141 if (p == NULL)
1142 return 0;
1143 if (p - range > DECIMAL_STRLEN_MAX)
1144 return 0;
1145 strncpy (buf, range, p - range);
1146 buf[p - range] = '\0';
1147 max = strtoul (buf, &endptr, 10);
1148 if (*endptr != '\0')
1149 return 0;
1150
1151 if (val < min || val > max)
1152 return 0;
1153
1154 return 1;
1155}
1156
ajs274a4a42004-12-07 15:39:31 +00001157static enum match_type
Christian Frankecd40b322013-09-30 12:27:51 +00001158cmd_word_match(struct cmd_token *token,
1159 enum filter_type filter,
1160 const char *word)
paul718e3742002-12-13 20:15:29 +00001161{
hasso8c328f12004-10-05 21:01:23 +00001162 const char *str;
paul718e3742002-12-13 20:15:29 +00001163 enum match_type match_type;
paul909a2152005-03-14 17:41:45 +00001164
Christian Frankecd40b322013-09-30 12:27:51 +00001165 str = token->cmd;
paul718e3742002-12-13 20:15:29 +00001166
Christian Frankecd40b322013-09-30 12:27:51 +00001167 if (filter == FILTER_RELAXED)
1168 if (!word || !strlen(word))
1169 return partly_match;
paul718e3742002-12-13 20:15:29 +00001170
Christian Frankecd40b322013-09-30 12:27:51 +00001171 if (!word)
1172 return no_match;
paul909a2152005-03-14 17:41:45 +00001173
Christian Frankecd40b322013-09-30 12:27:51 +00001174 if (CMD_VARARG(str))
1175 {
1176 return vararg_match;
1177 }
1178 else if (CMD_RANGE(str))
1179 {
1180 if (cmd_range_match(str, word))
1181 return range_match;
1182 }
paul22e0a9e2003-07-11 17:55:46 +00001183#ifdef HAVE_IPV6
Christian Frankecd40b322013-09-30 12:27:51 +00001184 else if (CMD_IPV6(str))
1185 {
1186 match_type = cmd_ipv6_match(word);
1187 if ((filter == FILTER_RELAXED && match_type != no_match)
1188 || (filter == FILTER_STRICT && match_type == exact_match))
1189 return ipv6_match;
1190 }
1191 else if (CMD_IPV6_PREFIX(str))
1192 {
1193 match_type = cmd_ipv6_prefix_match(word);
1194 if ((filter == FILTER_RELAXED && match_type != no_match)
1195 || (filter == FILTER_STRICT && match_type == exact_match))
1196 return ipv6_prefix_match;
1197 }
1198#endif /* HAVE_IPV6 */
1199 else if (CMD_IPV4(str))
1200 {
1201 match_type = cmd_ipv4_match(word);
1202 if ((filter == FILTER_RELAXED && match_type != no_match)
1203 || (filter == FILTER_STRICT && match_type == exact_match))
1204 return ipv4_match;
1205 }
1206 else if (CMD_IPV4_PREFIX(str))
1207 {
1208 match_type = cmd_ipv4_prefix_match(word);
1209 if ((filter == FILTER_RELAXED && match_type != no_match)
1210 || (filter == FILTER_STRICT && match_type == exact_match))
1211 return ipv4_prefix_match;
1212 }
1213 else if (CMD_OPTION(str) || CMD_VARIABLE(str))
1214 {
1215 return extend_match;
1216 }
1217 else
1218 {
1219 if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word)))
1220 {
1221 if (!strcmp(str, word))
1222 return exact_match;
1223 return partly_match;
1224 }
1225 if (filter == FILTER_STRICT && !strcmp(str, word))
1226 return exact_match;
1227 }
paul718e3742002-12-13 20:15:29 +00001228
Christian Frankecd40b322013-09-30 12:27:51 +00001229 return no_match;
paul718e3742002-12-13 20:15:29 +00001230}
1231
Christian Frankecd40b322013-09-30 12:27:51 +00001232struct cmd_matcher
1233{
1234 struct cmd_element *cmd; /* The command element the matcher is using */
1235 enum filter_type filter; /* Whether to use strict or relaxed matching */
1236 vector vline; /* The tokenized commandline which is to be matched */
1237 unsigned int index; /* The index up to which matching should be done */
1238
1239 /* If set, construct a list of matches at the position given by index */
1240 enum match_type *match_type;
1241 vector *match;
1242
1243 unsigned int word_index; /* iterating over vline */
1244};
1245
1246static int
1247push_argument(int *argc, const char **argv, const char *arg)
1248{
1249 if (!arg || !strlen(arg))
1250 arg = NULL;
1251
1252 if (!argc || !argv)
1253 return 0;
1254
1255 if (*argc >= CMD_ARGC_MAX)
1256 return -1;
1257
1258 argv[(*argc)++] = arg;
1259 return 0;
1260}
1261
1262static void
1263cmd_matcher_record_match(struct cmd_matcher *matcher,
1264 enum match_type match_type,
1265 struct cmd_token *token)
1266{
1267 if (matcher->word_index != matcher->index)
1268 return;
1269
1270 if (matcher->match)
1271 {
1272 if (!*matcher->match)
1273 *matcher->match = vector_init(VECTOR_MIN_SIZE);
1274 vector_set(*matcher->match, token);
1275 }
1276
1277 if (matcher->match_type)
1278 {
1279 if (match_type > *matcher->match_type)
1280 *matcher->match_type = match_type;
1281 }
1282}
1283
1284static int
1285cmd_matcher_words_left(struct cmd_matcher *matcher)
1286{
1287 return matcher->word_index < vector_active(matcher->vline);
1288}
1289
1290static const char*
1291cmd_matcher_get_word(struct cmd_matcher *matcher)
1292{
1293 assert(cmd_matcher_words_left(matcher));
1294
1295 return vector_slot(matcher->vline, matcher->word_index);
1296}
1297
1298static enum matcher_rv
1299cmd_matcher_match_terminal(struct cmd_matcher *matcher,
1300 struct cmd_token *token,
1301 int *argc, const char **argv)
1302{
1303 const char *word;
1304 enum match_type word_match;
1305
1306 assert(token->type == TOKEN_TERMINAL);
1307
1308 if (!cmd_matcher_words_left(matcher))
1309 {
1310 if (CMD_OPTION(token->cmd))
1311 return MATCHER_OK; /* missing optional args are NOT pushed as NULL */
1312 else
1313 return MATCHER_INCOMPLETE;
1314 }
1315
1316 word = cmd_matcher_get_word(matcher);
1317 word_match = cmd_word_match(token, matcher->filter, word);
1318 if (word_match == no_match)
1319 return MATCHER_NO_MATCH;
1320
1321 /* We have to record the input word as argument if it matched
1322 * against a variable. */
1323 if (CMD_VARARG(token->cmd)
1324 || CMD_VARIABLE(token->cmd)
1325 || CMD_OPTION(token->cmd))
1326 {
1327 if (push_argument(argc, argv, word))
1328 return MATCHER_EXCEED_ARGC_MAX;
1329 }
1330
1331 cmd_matcher_record_match(matcher, word_match, token);
1332
1333 matcher->word_index++;
1334
1335 /* A vararg token should consume all left over words as arguments */
1336 if (CMD_VARARG(token->cmd))
1337 while (cmd_matcher_words_left(matcher))
1338 {
1339 word = cmd_matcher_get_word(matcher);
1340 if (word && strlen(word))
1341 push_argument(argc, argv, word);
1342 matcher->word_index++;
1343 }
1344
1345 return MATCHER_OK;
1346}
1347
1348static enum matcher_rv
1349cmd_matcher_match_multiple(struct cmd_matcher *matcher,
1350 struct cmd_token *token,
1351 int *argc, const char **argv)
1352{
1353 enum match_type multiple_match;
1354 unsigned int multiple_index;
1355 const char *word;
David Lamparterab90fc02015-03-03 09:07:25 +01001356 const char *arg = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001357 struct cmd_token *word_token;
1358 enum match_type word_match;
1359
1360 assert(token->type == TOKEN_MULTIPLE);
1361
1362 multiple_match = no_match;
1363
1364 if (!cmd_matcher_words_left(matcher))
1365 return MATCHER_INCOMPLETE;
1366
1367 word = cmd_matcher_get_word(matcher);
1368 for (multiple_index = 0;
1369 multiple_index < vector_active(token->multiple);
1370 multiple_index++)
1371 {
1372 word_token = vector_slot(token->multiple, multiple_index);
1373
1374 word_match = cmd_word_match(word_token, matcher->filter, word);
1375 if (word_match == no_match)
1376 continue;
1377
1378 cmd_matcher_record_match(matcher, word_match, word_token);
1379
1380 if (word_match > multiple_match)
1381 {
1382 multiple_match = word_match;
1383 arg = word;
1384 }
1385 /* To mimic the behavior of the old command implementation, we
1386 * tolerate any ambiguities here :/ */
1387 }
1388
1389 matcher->word_index++;
1390
1391 if (multiple_match == no_match)
1392 return MATCHER_NO_MATCH;
1393
1394 if (push_argument(argc, argv, arg))
1395 return MATCHER_EXCEED_ARGC_MAX;
1396
1397 return MATCHER_OK;
1398}
1399
1400static enum matcher_rv
1401cmd_matcher_read_keywords(struct cmd_matcher *matcher,
1402 struct cmd_token *token,
1403 vector args_vector)
paul718e3742002-12-13 20:15:29 +00001404{
hasso8c328f12004-10-05 21:01:23 +00001405 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00001406 unsigned long keyword_mask;
1407 unsigned int keyword_found;
1408 enum match_type keyword_match;
1409 enum match_type word_match;
1410 vector keyword_vector;
1411 struct cmd_token *word_token;
1412 const char *word;
1413 int keyword_argc;
1414 const char **keyword_argv;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001415 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001416
1417 keyword_mask = 0;
1418 while (1)
1419 {
1420 if (!cmd_matcher_words_left(matcher))
1421 return MATCHER_OK;
1422
1423 word = cmd_matcher_get_word(matcher);
1424
1425 keyword_found = -1;
1426 keyword_match = no_match;
1427 for (i = 0; i < vector_active(token->keyword); i++)
1428 {
1429 if (keyword_mask & (1 << i))
1430 continue;
1431
1432 keyword_vector = vector_slot(token->keyword, i);
1433 word_token = vector_slot(keyword_vector, 0);
1434
1435 word_match = cmd_word_match(word_token, matcher->filter, word);
1436 if (word_match == no_match)
1437 continue;
1438
1439 cmd_matcher_record_match(matcher, word_match, word_token);
1440
1441 if (word_match > keyword_match)
1442 {
1443 keyword_match = word_match;
1444 keyword_found = i;
1445 }
1446 else if (word_match == keyword_match)
1447 {
1448 if (matcher->word_index != matcher->index || args_vector)
1449 return MATCHER_AMBIGUOUS;
1450 }
1451 }
1452
1453 if (keyword_found == (unsigned int)-1)
1454 return MATCHER_NO_MATCH;
1455
1456 matcher->word_index++;
1457
1458 if (matcher->word_index > matcher->index)
1459 return MATCHER_OK;
1460
1461 keyword_mask |= (1 << keyword_found);
1462
1463 if (args_vector)
1464 {
1465 keyword_argc = 0;
1466 keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*));
1467 /* We use -1 as a marker for unused fields as NULL might be a valid value */
1468 for (i = 0; i < CMD_ARGC_MAX + 1; i++)
1469 keyword_argv[i] = (void*)-1;
1470 vector_set_index(args_vector, keyword_found, keyword_argv);
1471 }
1472 else
1473 {
1474 keyword_argv = NULL;
1475 }
1476
1477 keyword_vector = vector_slot(token->keyword, keyword_found);
1478 /* the keyword itself is at 0. We are only interested in the arguments,
1479 * so start counting at 1. */
1480 for (i = 1; i < vector_active(keyword_vector); i++)
1481 {
1482 word_token = vector_slot(keyword_vector, i);
1483
1484 switch (word_token->type)
1485 {
1486 case TOKEN_TERMINAL:
1487 rv = cmd_matcher_match_terminal(matcher, word_token,
1488 &keyword_argc, keyword_argv);
1489 break;
1490 case TOKEN_MULTIPLE:
1491 rv = cmd_matcher_match_multiple(matcher, word_token,
1492 &keyword_argc, keyword_argv);
1493 break;
1494 case TOKEN_KEYWORD:
1495 assert(!"Keywords should never be nested.");
1496 break;
1497 }
1498
1499 if (MATCHER_ERROR(rv))
1500 return rv;
1501
1502 if (matcher->word_index > matcher->index)
1503 return MATCHER_OK;
1504 }
1505 }
1506 /* not reached */
1507}
1508
1509static enum matcher_rv
1510cmd_matcher_build_keyword_args(struct cmd_matcher *matcher,
1511 struct cmd_token *token,
1512 int *argc, const char **argv,
1513 vector keyword_args_vector)
1514{
1515 unsigned int i, j;
1516 const char **keyword_args;
1517 vector keyword_vector;
1518 struct cmd_token *word_token;
1519 const char *arg;
1520 enum matcher_rv rv;
1521
1522 rv = MATCHER_OK;
1523
1524 if (keyword_args_vector == NULL)
1525 return rv;
1526
1527 for (i = 0; i < vector_active(token->keyword); i++)
1528 {
1529 keyword_vector = vector_slot(token->keyword, i);
1530 keyword_args = vector_lookup(keyword_args_vector, i);
1531
1532 if (vector_active(keyword_vector) == 1)
1533 {
1534 /* this is a keyword without arguments */
1535 if (keyword_args)
1536 {
1537 word_token = vector_slot(keyword_vector, 0);
1538 arg = word_token->cmd;
1539 }
1540 else
1541 {
1542 arg = NULL;
1543 }
1544
1545 if (push_argument(argc, argv, arg))
1546 rv = MATCHER_EXCEED_ARGC_MAX;
1547 }
1548 else
1549 {
1550 /* this is a keyword with arguments */
1551 if (keyword_args)
1552 {
1553 /* the keyword was present, so just fill in the arguments */
1554 for (j = 0; keyword_args[j] != (void*)-1; j++)
1555 if (push_argument(argc, argv, keyword_args[j]))
1556 rv = MATCHER_EXCEED_ARGC_MAX;
1557 XFREE(MTYPE_TMP, keyword_args);
1558 }
1559 else
1560 {
1561 /* the keyword was not present, insert NULL for the arguments
1562 * the keyword would have taken. */
1563 for (j = 1; j < vector_active(keyword_vector); j++)
1564 {
1565 word_token = vector_slot(keyword_vector, j);
1566 if ((word_token->type == TOKEN_TERMINAL
1567 && (CMD_VARARG(word_token->cmd)
1568 || CMD_VARIABLE(word_token->cmd)
1569 || CMD_OPTION(word_token->cmd)))
1570 || word_token->type == TOKEN_MULTIPLE)
1571 {
1572 if (push_argument(argc, argv, NULL))
1573 rv = MATCHER_EXCEED_ARGC_MAX;
1574 }
1575 }
1576 }
1577 }
1578 }
1579 vector_free(keyword_args_vector);
1580 return rv;
1581}
1582
1583static enum matcher_rv
1584cmd_matcher_match_keyword(struct cmd_matcher *matcher,
1585 struct cmd_token *token,
1586 int *argc, const char **argv)
1587{
1588 vector keyword_args_vector;
1589 enum matcher_rv reader_rv;
1590 enum matcher_rv builder_rv;
1591
1592 assert(token->type == TOKEN_KEYWORD);
1593
1594 if (argc && argv)
1595 keyword_args_vector = vector_init(VECTOR_MIN_SIZE);
1596 else
1597 keyword_args_vector = NULL;
1598
1599 reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);
1600 builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc,
1601 argv, keyword_args_vector);
1602 /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */
1603
1604 if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv))
1605 return builder_rv;
1606
1607 return reader_rv;
1608}
1609
1610static void
1611cmd_matcher_init(struct cmd_matcher *matcher,
1612 struct cmd_element *cmd,
1613 enum filter_type filter,
1614 vector vline,
1615 unsigned int index,
1616 enum match_type *match_type,
1617 vector *match)
1618{
1619 memset(matcher, 0, sizeof(*matcher));
1620
1621 matcher->cmd = cmd;
1622 matcher->filter = filter;
1623 matcher->vline = vline;
1624 matcher->index = index;
1625
1626 matcher->match_type = match_type;
1627 if (matcher->match_type)
1628 *matcher->match_type = no_match;
1629 matcher->match = match;
1630
1631 matcher->word_index = 0;
1632}
1633
1634static enum matcher_rv
1635cmd_element_match(struct cmd_element *cmd_element,
1636 enum filter_type filter,
1637 vector vline,
1638 unsigned int index,
1639 enum match_type *match_type,
1640 vector *match,
1641 int *argc,
1642 const char **argv)
1643{
1644 struct cmd_matcher matcher;
1645 unsigned int token_index;
Paul Jakma7aa9dce2014-09-19 14:42:23 +01001646 enum matcher_rv rv = MATCHER_NO_MATCH;
Christian Frankecd40b322013-09-30 12:27:51 +00001647
1648 cmd_matcher_init(&matcher, cmd_element, filter,
1649 vline, index, match_type, match);
1650
1651 if (argc != NULL)
1652 *argc = 0;
1653
1654 for (token_index = 0;
1655 token_index < vector_active(cmd_element->tokens);
1656 token_index++)
1657 {
1658 struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
1659
1660 switch (token->type)
1661 {
1662 case TOKEN_TERMINAL:
1663 rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
1664 break;
1665 case TOKEN_MULTIPLE:
1666 rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
1667 break;
1668 case TOKEN_KEYWORD:
1669 rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
1670 }
1671
1672 if (MATCHER_ERROR(rv))
1673 return rv;
1674
1675 if (matcher.word_index > index)
1676 return MATCHER_OK;
1677 }
1678
1679 /* return MATCHER_COMPLETE if all words were consumed */
1680 if (matcher.word_index >= vector_active(vline))
1681 return MATCHER_COMPLETE;
1682
1683 /* return MATCHER_COMPLETE also if only an empty word is left. */
1684 if (matcher.word_index == vector_active(vline) - 1
1685 && (!vector_slot(vline, matcher.word_index)
1686 || !strlen((char*)vector_slot(vline, matcher.word_index))))
1687 return MATCHER_COMPLETE;
1688
1689 return MATCHER_NO_MATCH; /* command is too long to match */
1690}
1691
1692/**
1693 * Filter a given vector of commands against a given commandline and
1694 * calculate possible completions.
1695 *
1696 * @param commands A vector of struct cmd_element*. Commands that don't
1697 * match against the given command line will be overwritten
1698 * with NULL in that vector.
1699 * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
1700 * determines how incomplete commands are handled, compare with
1701 * cmd_word_match for details.
1702 * @param vline A vector of char* containing the tokenized commandline.
1703 * @param index Only match up to the given token of the commandline.
1704 * @param match_type Record the type of the best match here.
1705 * @param matches Record the matches here. For each cmd_element in the commands
1706 * vector, a match vector will be created in the matches vector.
1707 * That vector will contain all struct command_token* of the
1708 * cmd_element which matched against the given vline at the given
1709 * index.
1710 * @return A code specifying if an error occured. If all went right, it's
1711 * CMD_SUCCESS.
1712 */
1713static int
1714cmd_vector_filter(vector commands,
1715 enum filter_type filter,
1716 vector vline,
1717 unsigned int index,
1718 enum match_type *match_type,
1719 vector *matches)
1720{
1721 unsigned int i;
paul718e3742002-12-13 20:15:29 +00001722 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00001723 enum match_type best_match;
1724 enum match_type element_match;
1725 enum matcher_rv matcher_rv;
paul909a2152005-03-14 17:41:45 +00001726
Christian Frankecd40b322013-09-30 12:27:51 +00001727 best_match = no_match;
1728 *matches = vector_init(VECTOR_MIN_SIZE);
paul718e3742002-12-13 20:15:29 +00001729
Christian Frankecd40b322013-09-30 12:27:51 +00001730 for (i = 0; i < vector_active (commands); i++)
1731 if ((cmd_element = vector_slot (commands, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001732 {
Christian Frankecd40b322013-09-30 12:27:51 +00001733 vector_set_index(*matches, i, NULL);
1734 matcher_rv = cmd_element_match(cmd_element, filter,
1735 vline, index,
1736 &element_match,
1737 (vector*)&vector_slot(*matches, i),
1738 NULL, NULL);
1739 if (MATCHER_ERROR(matcher_rv))
1740 {
1741 vector_slot(commands, i) = NULL;
1742 if (matcher_rv == MATCHER_AMBIGUOUS)
1743 return CMD_ERR_AMBIGUOUS;
1744 if (matcher_rv == MATCHER_EXCEED_ARGC_MAX)
1745 return CMD_ERR_EXEED_ARGC_MAX;
1746 }
1747 else if (element_match > best_match)
1748 {
1749 best_match = element_match;
1750 }
paul718e3742002-12-13 20:15:29 +00001751 }
Christian Frankecd40b322013-09-30 12:27:51 +00001752 *match_type = best_match;
1753 return CMD_SUCCESS;
1754}
1755
1756/**
1757 * Check whether a given commandline is complete if used for a specific
1758 * cmd_element.
1759 *
1760 * @param cmd_element A cmd_element against which the commandline should be
1761 * checked.
1762 * @param vline The tokenized commandline.
1763 * @return 1 if the given commandline is complete, 0 otherwise.
1764 */
1765static int
1766cmd_is_complete(struct cmd_element *cmd_element,
1767 vector vline)
1768{
1769 enum matcher_rv rv;
1770
1771 rv = cmd_element_match(cmd_element,
1772 FILTER_RELAXED,
1773 vline, -1,
1774 NULL, NULL,
1775 NULL, NULL);
1776 return (rv == MATCHER_COMPLETE);
1777}
1778
1779/**
1780 * Parse a given commandline and construct a list of arguments for the
1781 * given command_element.
1782 *
1783 * @param cmd_element The cmd_element for which we want to construct arguments.
1784 * @param vline The tokenized commandline.
1785 * @param argc Where to store the argument count.
1786 * @param argv Where to store the argument list. Should be at least
1787 * CMD_ARGC_MAX elements long.
1788 * @return CMD_SUCCESS if everything went alright, an error otherwise.
1789 */
1790static int
1791cmd_parse(struct cmd_element *cmd_element,
1792 vector vline,
1793 int *argc, const char **argv)
1794{
1795 enum matcher_rv rv = cmd_element_match(cmd_element,
1796 FILTER_RELAXED,
1797 vline, -1,
1798 NULL, NULL,
1799 argc, argv);
1800 switch (rv)
1801 {
1802 case MATCHER_COMPLETE:
1803 return CMD_SUCCESS;
1804
1805 case MATCHER_NO_MATCH:
1806 return CMD_ERR_NO_MATCH;
1807
1808 case MATCHER_AMBIGUOUS:
1809 return CMD_ERR_AMBIGUOUS;
1810
1811 case MATCHER_EXCEED_ARGC_MAX:
1812 return CMD_ERR_EXEED_ARGC_MAX;
1813
1814 default:
1815 return CMD_ERR_INCOMPLETE;
1816 }
paul718e3742002-12-13 20:15:29 +00001817}
1818
1819/* Check ambiguous match */
ajs274a4a42004-12-07 15:39:31 +00001820static int
Christian Frankecd40b322013-09-30 12:27:51 +00001821is_cmd_ambiguous (vector cmd_vector,
1822 const char *command,
1823 vector matches,
1824 enum match_type type)
paul718e3742002-12-13 20:15:29 +00001825{
hasso8c328f12004-10-05 21:01:23 +00001826 unsigned int i;
1827 unsigned int j;
1828 const char *str = NULL;
hasso8c328f12004-10-05 21:01:23 +00001829 const char *matched = NULL;
Christian Frankecd40b322013-09-30 12:27:51 +00001830 vector match_vector;
1831 struct cmd_token *cmd_token;
paul909a2152005-03-14 17:41:45 +00001832
Christian Frankecd40b322013-09-30 12:27:51 +00001833 if (command == NULL)
1834 command = "";
1835
1836 for (i = 0; i < vector_active (matches); i++)
1837 if ((match_vector = vector_slot (matches, i)) != NULL)
paul718e3742002-12-13 20:15:29 +00001838 {
1839 int match = 0;
1840
Christian Frankecd40b322013-09-30 12:27:51 +00001841 for (j = 0; j < vector_active (match_vector); j++)
1842 if ((cmd_token = vector_slot (match_vector, j)) != NULL)
paul909a2152005-03-14 17:41:45 +00001843 {
1844 enum match_type ret;
Christian Frankecd40b322013-09-30 12:27:51 +00001845
1846 assert(cmd_token->type == TOKEN_TERMINAL);
1847 if (cmd_token->type != TOKEN_TERMINAL)
1848 continue;
1849
1850 str = cmd_token->cmd;
paul718e3742002-12-13 20:15:29 +00001851
paul909a2152005-03-14 17:41:45 +00001852 switch (type)
1853 {
1854 case exact_match:
1855 if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
1856 && strcmp (command, str) == 0)
1857 match++;
1858 break;
1859 case partly_match:
1860 if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
1861 && strncmp (command, str, strlen (command)) == 0)
1862 {
1863 if (matched && strcmp (matched, str) != 0)
1864 return 1; /* There is ambiguous match. */
1865 else
1866 matched = str;
1867 match++;
1868 }
1869 break;
1870 case range_match:
1871 if (cmd_range_match (str, command))
1872 {
1873 if (matched && strcmp (matched, str) != 0)
1874 return 1;
1875 else
1876 matched = str;
1877 match++;
1878 }
1879 break;
1880#ifdef HAVE_IPV6
1881 case ipv6_match:
1882 if (CMD_IPV6 (str))
1883 match++;
1884 break;
1885 case ipv6_prefix_match:
1886 if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
1887 {
1888 if (ret == partly_match)
1889 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001890
paul909a2152005-03-14 17:41:45 +00001891 match++;
1892 }
1893 break;
1894#endif /* HAVE_IPV6 */
1895 case ipv4_match:
1896 if (CMD_IPV4 (str))
paul718e3742002-12-13 20:15:29 +00001897 match++;
paul909a2152005-03-14 17:41:45 +00001898 break;
1899 case ipv4_prefix_match:
1900 if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
1901 {
1902 if (ret == partly_match)
1903 return 2; /* There is incomplete match. */
paul718e3742002-12-13 20:15:29 +00001904
paul909a2152005-03-14 17:41:45 +00001905 match++;
1906 }
1907 break;
1908 case extend_match:
1909 if (CMD_OPTION (str) || CMD_VARIABLE (str))
paul718e3742002-12-13 20:15:29 +00001910 match++;
paul909a2152005-03-14 17:41:45 +00001911 break;
1912 case no_match:
1913 default:
1914 break;
1915 }
1916 }
1917 if (!match)
Christian Frankecd40b322013-09-30 12:27:51 +00001918 vector_slot (cmd_vector, i) = NULL;
paul718e3742002-12-13 20:15:29 +00001919 }
1920 return 0;
1921}
1922
1923/* If src matches dst return dst string, otherwise return NULL */
ajs274a4a42004-12-07 15:39:31 +00001924static const char *
hasso8c328f12004-10-05 21:01:23 +00001925cmd_entry_function (const char *src, const char *dst)
paul718e3742002-12-13 20:15:29 +00001926{
1927 /* Skip variable arguments. */
1928 if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) ||
1929 CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst))
1930 return NULL;
1931
1932 /* In case of 'command \t', given src is NULL string. */
1933 if (src == NULL)
1934 return dst;
1935
1936 /* Matched with input string. */
1937 if (strncmp (src, dst, strlen (src)) == 0)
1938 return dst;
1939
1940 return NULL;
1941}
1942
1943/* If src matches dst return dst string, otherwise return NULL */
1944/* This version will return the dst string always if it is
1945 CMD_VARIABLE for '?' key processing */
ajs274a4a42004-12-07 15:39:31 +00001946static const char *
hasso8c328f12004-10-05 21:01:23 +00001947cmd_entry_function_desc (const char *src, const char *dst)
paul718e3742002-12-13 20:15:29 +00001948{
1949 if (CMD_VARARG (dst))
1950 return dst;
1951
1952 if (CMD_RANGE (dst))
1953 {
1954 if (cmd_range_match (dst, src))
1955 return dst;
1956 else
1957 return NULL;
1958 }
1959
paul22e0a9e2003-07-11 17:55:46 +00001960#ifdef HAVE_IPV6
paul718e3742002-12-13 20:15:29 +00001961 if (CMD_IPV6 (dst))
1962 {
1963 if (cmd_ipv6_match (src))
1964 return dst;
1965 else
1966 return NULL;
1967 }
1968
1969 if (CMD_IPV6_PREFIX (dst))
1970 {
1971 if (cmd_ipv6_prefix_match (src))
1972 return dst;
1973 else
1974 return NULL;
1975 }
paul22e0a9e2003-07-11 17:55:46 +00001976#endif /* HAVE_IPV6 */
paul718e3742002-12-13 20:15:29 +00001977
1978 if (CMD_IPV4 (dst))
1979 {
1980 if (cmd_ipv4_match (src))
1981 return dst;
1982 else
1983 return NULL;
1984 }
1985
1986 if (CMD_IPV4_PREFIX (dst))
1987 {
1988 if (cmd_ipv4_prefix_match (src))
1989 return dst;
1990 else
1991 return NULL;
1992 }
1993
1994 /* Optional or variable commands always match on '?' */
1995 if (CMD_OPTION (dst) || CMD_VARIABLE (dst))
1996 return dst;
1997
1998 /* In case of 'command \t', given src is NULL string. */
1999 if (src == NULL)
2000 return dst;
2001
2002 if (strncmp (src, dst, strlen (src)) == 0)
2003 return dst;
2004 else
2005 return NULL;
2006}
2007
Christian Frankecd40b322013-09-30 12:27:51 +00002008/**
2009 * Check whether a string is already present in a vector of strings.
2010 * @param v A vector of char*.
2011 * @param str A char*.
2012 * @return 0 if str is already present in the vector, 1 otherwise.
2013 */
ajs274a4a42004-12-07 15:39:31 +00002014static int
hasso8c328f12004-10-05 21:01:23 +00002015cmd_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002016{
hasso8c328f12004-10-05 21:01:23 +00002017 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002018 char *match;
2019
paul55468c82005-03-14 20:19:01 +00002020 for (i = 0; i < vector_active (v); i++)
paul718e3742002-12-13 20:15:29 +00002021 if ((match = vector_slot (v, i)) != NULL)
2022 if (strcmp (match, str) == 0)
2023 return 0;
2024 return 1;
2025}
2026
Christian Frankecd40b322013-09-30 12:27:51 +00002027/**
2028 * Check whether a struct cmd_token matching a given string is already
2029 * present in a vector of struct cmd_token.
2030 * @param v A vector of struct cmd_token*.
2031 * @param str A char* which should be searched for.
2032 * @return 0 if there is a struct cmd_token* with its cmd matching str,
2033 * 1 otherwise.
2034 */
ajs274a4a42004-12-07 15:39:31 +00002035static int
hasso8c328f12004-10-05 21:01:23 +00002036desc_unique_string (vector v, const char *str)
paul718e3742002-12-13 20:15:29 +00002037{
hasso8c328f12004-10-05 21:01:23 +00002038 unsigned int i;
Christian Frankecd40b322013-09-30 12:27:51 +00002039 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002040
paul55468c82005-03-14 20:19:01 +00002041 for (i = 0; i < vector_active (v); i++)
Christian Frankecd40b322013-09-30 12:27:51 +00002042 if ((token = vector_slot (v, i)) != NULL)
2043 if (strcmp (token->cmd, str) == 0)
2044 return 0;
2045 return 1;
paul718e3742002-12-13 20:15:29 +00002046}
2047
ajs274a4a42004-12-07 15:39:31 +00002048static int
paulb92938a2002-12-13 21:20:42 +00002049cmd_try_do_shortcut (enum node_type node, char* first_word) {
2050 if ( first_word != NULL &&
2051 node != AUTH_NODE &&
2052 node != VIEW_NODE &&
2053 node != AUTH_ENABLE_NODE &&
2054 node != ENABLE_NODE &&
Paul Jakma62687ff2008-08-23 14:27:06 +01002055 node != RESTRICTED_NODE &&
paulb92938a2002-12-13 21:20:42 +00002056 0 == strcmp( "do", first_word ) )
2057 return 1;
2058 return 0;
2059}
2060
Christian Frankecd40b322013-09-30 12:27:51 +00002061static void
2062cmd_matches_free(vector *matches)
2063{
2064 unsigned int i;
2065 vector cmd_matches;
2066
2067 for (i = 0; i < vector_active(*matches); i++)
2068 if ((cmd_matches = vector_slot(*matches, i)) != NULL)
2069 vector_free(cmd_matches);
2070 vector_free(*matches);
2071 *matches = NULL;
2072}
2073
2074static int
2075cmd_describe_cmp(const void *a, const void *b)
2076{
2077 const struct cmd_token *first = *(struct cmd_token * const *)a;
2078 const struct cmd_token *second = *(struct cmd_token * const *)b;
2079
2080 return strcmp(first->cmd, second->cmd);
2081}
2082
2083static void
2084cmd_describe_sort(vector matchvec)
2085{
2086 qsort(matchvec->index, vector_active(matchvec),
2087 sizeof(void*), cmd_describe_cmp);
2088}
2089
paul718e3742002-12-13 20:15:29 +00002090/* '?' describe command support. */
ajs274a4a42004-12-07 15:39:31 +00002091static vector
paulb92938a2002-12-13 21:20:42 +00002092cmd_describe_command_real (vector vline, struct vty *vty, int *status)
paul718e3742002-12-13 20:15:29 +00002093{
paulb8961472005-03-14 17:35:52 +00002094 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002095 vector cmd_vector;
2096#define INIT_MATCHVEC_SIZE 10
2097 vector matchvec;
2098 struct cmd_element *cmd_element;
paulb8961472005-03-14 17:35:52 +00002099 unsigned int index;
paul54aba542003-08-21 20:28:24 +00002100 int ret;
2101 enum match_type match;
2102 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002103 vector matches = NULL;
2104 vector match_vector;
paul718e3742002-12-13 20:15:29 +00002105
2106 /* Set index. */
paul55468c82005-03-14 20:19:01 +00002107 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002108 {
2109 *status = CMD_ERR_NO_MATCH;
2110 return NULL;
2111 }
Christian Frankecd40b322013-09-30 12:27:51 +00002112
2113 index = vector_active (vline) - 1;
2114
paul718e3742002-12-13 20:15:29 +00002115 /* Make copy vector of current node's command vector. */
2116 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2117
2118 /* Prepare match vector */
2119 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2120
Christian Frankecd40b322013-09-30 12:27:51 +00002121 /* Filter commands and build a list how they could possibly continue. */
2122 for (i = 0; i <= index; i++)
2123 {
2124 command = vector_slot (vline, i);
paul718e3742002-12-13 20:15:29 +00002125
Christian Frankecd40b322013-09-30 12:27:51 +00002126 if (matches)
2127 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002128
Christian Frankecd40b322013-09-30 12:27:51 +00002129 ret = cmd_vector_filter(cmd_vector,
2130 FILTER_RELAXED,
2131 vline, i,
2132 &match,
2133 &matches);
paul718e3742002-12-13 20:15:29 +00002134
Christian Frankecd40b322013-09-30 12:27:51 +00002135 if (ret != CMD_SUCCESS)
2136 {
2137 vector_free (cmd_vector);
2138 vector_free (matchvec);
2139 cmd_matches_free(&matches);
2140 *status = ret;
2141 return NULL;
2142 }
paul718e3742002-12-13 20:15:29 +00002143
Christian Frankecd40b322013-09-30 12:27:51 +00002144 /* The last match may well be ambigious, so break here */
2145 if (i == index)
2146 break;
paul718e3742002-12-13 20:15:29 +00002147
Christian Frankecd40b322013-09-30 12:27:51 +00002148 if (match == vararg_match)
2149 {
2150 /* We found a vararg match - so we can throw out the current matches here
2151 * and don't need to continue checking the command input */
2152 unsigned int j, k;
2153
2154 for (j = 0; j < vector_active (matches); j++)
2155 if ((match_vector = vector_slot (matches, j)) != NULL)
2156 for (k = 0; k < vector_active (match_vector); k++)
2157 {
2158 struct cmd_token *token = vector_slot (match_vector, k);
2159 vector_set (matchvec, token);
2160 }
2161
2162 *status = CMD_SUCCESS;
2163 vector_set(matchvec, &token_cr);
2164 vector_free (cmd_vector);
2165 cmd_matches_free(&matches);
2166 cmd_describe_sort(matchvec);
2167 return matchvec;
2168 }
2169
2170 ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
2171 if (ret == 1)
2172 {
2173 vector_free (cmd_vector);
2174 vector_free (matchvec);
2175 cmd_matches_free(&matches);
2176 *status = CMD_ERR_AMBIGUOUS;
2177 return NULL;
2178 }
2179 else if (ret == 2)
2180 {
2181 vector_free (cmd_vector);
2182 vector_free (matchvec);
2183 cmd_matches_free(&matches);
2184 *status = CMD_ERR_NO_MATCH;
2185 return NULL;
2186 }
2187 }
paul54aba542003-08-21 20:28:24 +00002188
paul718e3742002-12-13 20:15:29 +00002189 /* Make description vector. */
Christian Frankecd40b322013-09-30 12:27:51 +00002190 for (i = 0; i < vector_active (matches); i++)
paul718e3742002-12-13 20:15:29 +00002191 if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
2192 {
Christian Frankecd40b322013-09-30 12:27:51 +00002193 unsigned int j;
2194 const char *last_word;
2195 vector vline_trimmed;
paul718e3742002-12-13 20:15:29 +00002196
Christian Frankecd40b322013-09-30 12:27:51 +00002197 last_word = vector_slot(vline, vector_active(vline) - 1);
2198 if (last_word == NULL || !strlen(last_word))
2199 {
2200 vline_trimmed = vector_copy(vline);
2201 vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1);
paul718e3742002-12-13 20:15:29 +00002202
Christian Frankecd40b322013-09-30 12:27:51 +00002203 if (cmd_is_complete(cmd_element, vline_trimmed)
2204 && desc_unique_string(matchvec, command_cr))
2205 {
2206 if (match != vararg_match)
2207 vector_set(matchvec, &token_cr);
2208 }
Chris Caputo228da422009-07-18 05:44:03 +00002209
Christian Frankecd40b322013-09-30 12:27:51 +00002210 vector_free(vline_trimmed);
2211 }
2212
2213 match_vector = vector_slot (matches, i);
2214 if (match_vector)
2215 for (j = 0; j < vector_active(match_vector); j++)
2216 {
2217 struct cmd_token *token = vector_slot(match_vector, j);
2218 const char *string;
2219
2220 string = cmd_entry_function_desc(command, token->cmd);
2221 if (string && desc_unique_string(matchvec, string))
2222 vector_set(matchvec, token);
2223 }
paul718e3742002-12-13 20:15:29 +00002224 }
2225 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002226 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002227
2228 if (vector_slot (matchvec, 0) == NULL)
2229 {
2230 vector_free (matchvec);
paul909a2152005-03-14 17:41:45 +00002231 *status = CMD_ERR_NO_MATCH;
Paul Jakma5fc60512006-05-12 23:24:09 +00002232 return NULL;
paul718e3742002-12-13 20:15:29 +00002233 }
paul718e3742002-12-13 20:15:29 +00002234
Paul Jakma5fc60512006-05-12 23:24:09 +00002235 *status = CMD_SUCCESS;
Christian Frankecd40b322013-09-30 12:27:51 +00002236 cmd_describe_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002237 return matchvec;
2238}
2239
paulb92938a2002-12-13 21:20:42 +00002240vector
2241cmd_describe_command (vector vline, struct vty *vty, int *status)
2242{
2243 vector ret;
2244
2245 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2246 {
2247 enum node_type onode;
2248 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002249 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002250
2251 onode = vty->node;
2252 vty->node = ENABLE_NODE;
2253 /* We can try it on enable node, cos' the vty is authenticated */
2254
2255 shifted_vline = vector_init (vector_count(vline));
2256 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002257 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002258 {
2259 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2260 }
2261
2262 ret = cmd_describe_command_real (shifted_vline, vty, status);
2263
2264 vector_free(shifted_vline);
2265 vty->node = onode;
2266 return ret;
2267 }
2268
2269
2270 return cmd_describe_command_real (vline, vty, status);
2271}
2272
2273
paul718e3742002-12-13 20:15:29 +00002274/* Check LCD of matched command. */
ajs274a4a42004-12-07 15:39:31 +00002275static int
paul718e3742002-12-13 20:15:29 +00002276cmd_lcd (char **matched)
2277{
2278 int i;
2279 int j;
2280 int lcd = -1;
2281 char *s1, *s2;
2282 char c1, c2;
2283
2284 if (matched[0] == NULL || matched[1] == NULL)
2285 return 0;
2286
2287 for (i = 1; matched[i] != NULL; i++)
2288 {
2289 s1 = matched[i - 1];
2290 s2 = matched[i];
2291
2292 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2293 if (c1 != c2)
2294 break;
2295
2296 if (lcd < 0)
2297 lcd = j;
2298 else
2299 {
2300 if (lcd > j)
2301 lcd = j;
2302 }
2303 }
2304 return lcd;
2305}
2306
Christian Frankecd40b322013-09-30 12:27:51 +00002307static int
2308cmd_complete_cmp(const void *a, const void *b)
2309{
2310 const char *first = *(char * const *)a;
2311 const char *second = *(char * const *)b;
2312
2313 if (!first)
2314 {
2315 if (!second)
2316 return 0;
2317 return 1;
2318 }
2319 if (!second)
2320 return -1;
2321
2322 return strcmp(first, second);
2323}
2324
2325static void
2326cmd_complete_sort(vector matchvec)
2327{
2328 qsort(matchvec->index, vector_active(matchvec),
2329 sizeof(void*), cmd_complete_cmp);
2330}
2331
paul718e3742002-12-13 20:15:29 +00002332/* Command line completion support. */
ajs274a4a42004-12-07 15:39:31 +00002333static char **
paulb92938a2002-12-13 21:20:42 +00002334cmd_complete_command_real (vector vline, struct vty *vty, int *status)
paul718e3742002-12-13 20:15:29 +00002335{
paulb8961472005-03-14 17:35:52 +00002336 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002337 vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2338#define INIT_MATCHVEC_SIZE 10
2339 vector matchvec;
paulb8961472005-03-14 17:35:52 +00002340 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002341 char **match_str;
Christian Frankecd40b322013-09-30 12:27:51 +00002342 struct cmd_token *token;
paul718e3742002-12-13 20:15:29 +00002343 char *command;
2344 int lcd;
Christian Frankecd40b322013-09-30 12:27:51 +00002345 vector matches = NULL;
2346 vector match_vector;
paul718e3742002-12-13 20:15:29 +00002347
paul55468c82005-03-14 20:19:01 +00002348 if (vector_active (vline) == 0)
paulb8961472005-03-14 17:35:52 +00002349 {
Paul Jakmad2519962006-05-12 23:19:37 +00002350 vector_free (cmd_vector);
paulb8961472005-03-14 17:35:52 +00002351 *status = CMD_ERR_NO_MATCH;
2352 return NULL;
2353 }
2354 else
paul55468c82005-03-14 20:19:01 +00002355 index = vector_active (vline) - 1;
paulb8961472005-03-14 17:35:52 +00002356
Christian Frankecd40b322013-09-30 12:27:51 +00002357 /* First, filter by command string */
2358 for (i = 0; i <= index; i++)
2359 {
2360 command = vector_slot (vline, i);
2361 enum match_type match;
2362 int ret;
paul718e3742002-12-13 20:15:29 +00002363
Christian Frankecd40b322013-09-30 12:27:51 +00002364 if (matches)
2365 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002366
Christian Frankecd40b322013-09-30 12:27:51 +00002367 /* First try completion match, if there is exactly match return 1 */
2368 ret = cmd_vector_filter(cmd_vector,
2369 FILTER_RELAXED,
2370 vline, i,
2371 &match,
2372 &matches);
2373
2374 if (ret != CMD_SUCCESS)
2375 {
2376 vector_free(cmd_vector);
2377 cmd_matches_free(&matches);
2378 *status = ret;
2379 return NULL;
2380 }
2381
2382 /* Break here - the completion mustn't be checked to be non-ambiguous */
2383 if (i == index)
2384 break;
2385
2386 /* If there is exact match then filter ambiguous match else check
2387 ambiguousness. */
2388 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2389 if (ret == 1)
2390 {
2391 vector_free (cmd_vector);
2392 cmd_matches_free(&matches);
2393 *status = CMD_ERR_AMBIGUOUS;
2394 return NULL;
2395 }
2396 /*
paul909a2152005-03-14 17:41:45 +00002397 else if (ret == 2)
2398 {
2399 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002400 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002401 *status = CMD_ERR_NO_MATCH;
2402 return NULL;
2403 }
2404 */
Christian Frankecd40b322013-09-30 12:27:51 +00002405 }
paul909a2152005-03-14 17:41:45 +00002406
paul718e3742002-12-13 20:15:29 +00002407 /* Prepare match vector. */
2408 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2409
Christian Frankecd40b322013-09-30 12:27:51 +00002410 /* Build the possible list of continuations into a list of completions */
2411 for (i = 0; i < vector_active (matches); i++)
2412 if ((match_vector = vector_slot (matches, i)))
paul718e3742002-12-13 20:15:29 +00002413 {
hasso8c328f12004-10-05 21:01:23 +00002414 const char *string;
Christian Frankecd40b322013-09-30 12:27:51 +00002415 unsigned int j;
paul909a2152005-03-14 17:41:45 +00002416
Christian Frankecd40b322013-09-30 12:27:51 +00002417 for (j = 0; j < vector_active (match_vector); j++)
2418 if ((token = vector_slot (match_vector, j)))
paul909a2152005-03-14 17:41:45 +00002419 {
paulb8961472005-03-14 17:35:52 +00002420 if ((string =
2421 cmd_entry_function (vector_slot (vline, index),
Christian Frankecd40b322013-09-30 12:27:51 +00002422 token->cmd)))
paul909a2152005-03-14 17:41:45 +00002423 if (cmd_unique_string (matchvec, string))
2424 vector_set (matchvec, XSTRDUP (MTYPE_TMP, string));
2425 }
paul718e3742002-12-13 20:15:29 +00002426 }
2427
2428 /* We don't need cmd_vector any more. */
2429 vector_free (cmd_vector);
Christian Frankecd40b322013-09-30 12:27:51 +00002430 cmd_matches_free(&matches);
paul718e3742002-12-13 20:15:29 +00002431
2432 /* No matched command */
2433 if (vector_slot (matchvec, 0) == NULL)
2434 {
2435 vector_free (matchvec);
2436
2437 /* In case of 'command \t' pattern. Do you need '?' command at
2438 the end of the line. */
2439 if (vector_slot (vline, index) == '\0')
2440 *status = CMD_ERR_NOTHING_TODO;
2441 else
2442 *status = CMD_ERR_NO_MATCH;
2443 return NULL;
2444 }
2445
2446 /* Only one matched */
2447 if (vector_slot (matchvec, 1) == NULL)
2448 {
2449 match_str = (char **) matchvec->index;
2450 vector_only_wrapper_free (matchvec);
2451 *status = CMD_COMPLETE_FULL_MATCH;
2452 return match_str;
2453 }
2454 /* Make it sure last element is NULL. */
2455 vector_set (matchvec, NULL);
2456
2457 /* Check LCD of matched strings. */
2458 if (vector_slot (vline, index) != NULL)
2459 {
2460 lcd = cmd_lcd ((char **) matchvec->index);
2461
2462 if (lcd)
2463 {
2464 int len = strlen (vector_slot (vline, index));
paul909a2152005-03-14 17:41:45 +00002465
paul718e3742002-12-13 20:15:29 +00002466 if (len < lcd)
2467 {
2468 char *lcdstr;
paul909a2152005-03-14 17:41:45 +00002469
Christian Frankecd40b322013-09-30 12:27:51 +00002470 lcdstr = XMALLOC (MTYPE_TMP, lcd + 1);
paul718e3742002-12-13 20:15:29 +00002471 memcpy (lcdstr, matchvec->index[0], lcd);
2472 lcdstr[lcd] = '\0';
2473
2474 /* match_str = (char **) &lcdstr; */
2475
2476 /* Free matchvec. */
paul55468c82005-03-14 20:19:01 +00002477 for (i = 0; i < vector_active (matchvec); i++)
paul718e3742002-12-13 20:15:29 +00002478 {
2479 if (vector_slot (matchvec, i))
Christian Frankecd40b322013-09-30 12:27:51 +00002480 XFREE (MTYPE_TMP, vector_slot (matchvec, i));
paul718e3742002-12-13 20:15:29 +00002481 }
2482 vector_free (matchvec);
2483
paul909a2152005-03-14 17:41:45 +00002484 /* Make new matchvec. */
paul718e3742002-12-13 20:15:29 +00002485 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2486 vector_set (matchvec, lcdstr);
2487 match_str = (char **) matchvec->index;
2488 vector_only_wrapper_free (matchvec);
2489
2490 *status = CMD_COMPLETE_MATCH;
2491 return match_str;
2492 }
2493 }
2494 }
2495
2496 match_str = (char **) matchvec->index;
Christian Frankecd40b322013-09-30 12:27:51 +00002497 cmd_complete_sort(matchvec);
paul718e3742002-12-13 20:15:29 +00002498 vector_only_wrapper_free (matchvec);
2499 *status = CMD_COMPLETE_LIST_MATCH;
2500 return match_str;
2501}
2502
paulb92938a2002-12-13 21:20:42 +00002503char **
paul9ab68122003-01-18 01:16:20 +00002504cmd_complete_command (vector vline, struct vty *vty, int *status)
paulb92938a2002-12-13 21:20:42 +00002505{
2506 char **ret;
2507
2508 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2509 {
2510 enum node_type onode;
2511 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002512 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002513
2514 onode = vty->node;
2515 vty->node = ENABLE_NODE;
2516 /* We can try it on enable node, cos' the vty is authenticated */
2517
2518 shifted_vline = vector_init (vector_count(vline));
2519 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002520 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002521 {
2522 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2523 }
2524
2525 ret = cmd_complete_command_real (shifted_vline, vty, status);
2526
2527 vector_free(shifted_vline);
2528 vty->node = onode;
2529 return ret;
2530 }
2531
2532
2533 return cmd_complete_command_real (vline, vty, status);
2534}
2535
2536/* return parent node */
2537/* MUST eventually converge on CONFIG_NODE */
hasso13bfca72005-01-23 21:42:25 +00002538enum node_type
ajs274a4a42004-12-07 15:39:31 +00002539node_parent ( enum node_type node )
paulb92938a2002-12-13 21:20:42 +00002540{
2541 enum node_type ret;
2542
paul9ab68122003-01-18 01:16:20 +00002543 assert (node > CONFIG_NODE);
2544
2545 switch (node)
2546 {
2547 case BGP_VPNV4_NODE:
2548 case BGP_IPV4_NODE:
2549 case BGP_IPV4M_NODE:
2550 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002551 case BGP_IPV6M_NODE:
paul9ab68122003-01-18 01:16:20 +00002552 ret = BGP_NODE;
2553 break;
2554 case KEYCHAIN_KEY_NODE:
2555 ret = KEYCHAIN_NODE;
2556 break;
2557 default:
2558 ret = CONFIG_NODE;
paulb92938a2002-12-13 21:20:42 +00002559 }
2560
2561 return ret;
2562}
2563
paul718e3742002-12-13 20:15:29 +00002564/* Execute command by argument vline vector. */
ajs274a4a42004-12-07 15:39:31 +00002565static int
Christian Frankecd40b322013-09-30 12:27:51 +00002566cmd_execute_command_real (vector vline,
2567 enum filter_type filter,
2568 struct vty *vty,
paulb8961472005-03-14 17:35:52 +00002569 struct cmd_element **cmd)
paul718e3742002-12-13 20:15:29 +00002570{
hasso8c328f12004-10-05 21:01:23 +00002571 unsigned int i;
2572 unsigned int index;
paul718e3742002-12-13 20:15:29 +00002573 vector cmd_vector;
2574 struct cmd_element *cmd_element;
2575 struct cmd_element *matched_element;
2576 unsigned int matched_count, incomplete_count;
2577 int argc;
paul9035efa2004-10-10 11:56:56 +00002578 const char *argv[CMD_ARGC_MAX];
paul718e3742002-12-13 20:15:29 +00002579 enum match_type match = 0;
paul718e3742002-12-13 20:15:29 +00002580 char *command;
Christian Frankecd40b322013-09-30 12:27:51 +00002581 int ret;
2582 vector matches;
paul718e3742002-12-13 20:15:29 +00002583
2584 /* Make copy of command elements. */
2585 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2586
paul55468c82005-03-14 20:19:01 +00002587 for (index = 0; index < vector_active (vline); index++)
Christian Frankecd40b322013-09-30 12:27:51 +00002588 {
2589 command = vector_slot (vline, index);
2590 ret = cmd_vector_filter(cmd_vector,
2591 filter,
2592 vline, index,
2593 &match,
2594 &matches);
paul718e3742002-12-13 20:15:29 +00002595
Christian Frankecd40b322013-09-30 12:27:51 +00002596 if (ret != CMD_SUCCESS)
2597 {
2598 cmd_matches_free(&matches);
2599 return ret;
2600 }
paul718e3742002-12-13 20:15:29 +00002601
Christian Frankecd40b322013-09-30 12:27:51 +00002602 if (match == vararg_match)
2603 {
2604 cmd_matches_free(&matches);
paul909a2152005-03-14 17:41:45 +00002605 break;
Christian Frankecd40b322013-09-30 12:27:51 +00002606 }
paul718e3742002-12-13 20:15:29 +00002607
Christian Frankecd40b322013-09-30 12:27:51 +00002608 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2609 cmd_matches_free(&matches);
2610
2611 if (ret == 1)
2612 {
2613 vector_free(cmd_vector);
2614 return CMD_ERR_AMBIGUOUS;
2615 }
2616 else if (ret == 2)
2617 {
2618 vector_free(cmd_vector);
2619 return CMD_ERR_NO_MATCH;
2620 }
2621 }
paul718e3742002-12-13 20:15:29 +00002622
2623 /* Check matched count. */
2624 matched_element = NULL;
2625 matched_count = 0;
2626 incomplete_count = 0;
2627
paul55468c82005-03-14 20:19:01 +00002628 for (i = 0; i < vector_active (cmd_vector); i++)
paulb8961472005-03-14 17:35:52 +00002629 if ((cmd_element = vector_slot (cmd_vector, i)))
paul718e3742002-12-13 20:15:29 +00002630 {
Christian Frankecd40b322013-09-30 12:27:51 +00002631 if (cmd_is_complete(cmd_element, vline))
paul718e3742002-12-13 20:15:29 +00002632 {
2633 matched_element = cmd_element;
paul718e3742002-12-13 20:15:29 +00002634 matched_count++;
2635 }
2636 else
2637 {
2638 incomplete_count++;
2639 }
2640 }
paul909a2152005-03-14 17:41:45 +00002641
paul718e3742002-12-13 20:15:29 +00002642 /* Finish of using cmd_vector. */
2643 vector_free (cmd_vector);
2644
paul909a2152005-03-14 17:41:45 +00002645 /* To execute command, matched_count must be 1. */
2646 if (matched_count == 0)
paul718e3742002-12-13 20:15:29 +00002647 {
2648 if (incomplete_count)
2649 return CMD_ERR_INCOMPLETE;
2650 else
2651 return CMD_ERR_NO_MATCH;
2652 }
2653
paul909a2152005-03-14 17:41:45 +00002654 if (matched_count > 1)
paul718e3742002-12-13 20:15:29 +00002655 return CMD_ERR_AMBIGUOUS;
2656
Christian Frankecd40b322013-09-30 12:27:51 +00002657 ret = cmd_parse(matched_element, vline, &argc, argv);
2658 if (ret != CMD_SUCCESS)
2659 return ret;
paul718e3742002-12-13 20:15:29 +00002660
2661 /* For vtysh execution. */
2662 if (cmd)
2663 *cmd = matched_element;
2664
2665 if (matched_element->daemon)
2666 return CMD_SUCCESS_DAEMON;
2667
2668 /* Execute matched command. */
2669 return (*matched_element->func) (matched_element, vty, argc, argv);
2670}
2671
Christian Frankecd40b322013-09-30 12:27:51 +00002672/**
2673 * Execute a given command, handling things like "do ..." and checking
2674 * whether the given command might apply at a parent node if doesn't
2675 * apply for the current node.
2676 *
2677 * @param vline Command line input, vector of char* where each element is
2678 * one input token.
2679 * @param vty The vty context in which the command should be executed.
2680 * @param cmd Pointer where the struct cmd_element of the matched command
2681 * will be stored, if any. May be set to NULL if this info is
2682 * not needed.
2683 * @param vtysh If set != 0, don't lookup the command at parent nodes.
2684 * @return The status of the command that has been executed or an error code
2685 * as to why no command could be executed.
2686 */
paulb92938a2002-12-13 21:20:42 +00002687int
hasso87d683b2005-01-16 23:31:54 +00002688cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
2689 int vtysh) {
paul9ab68122003-01-18 01:16:20 +00002690 int ret, saved_ret, tried = 0;
2691 enum node_type onode, try_node;
2692
2693 onode = try_node = vty->node;
paulb92938a2002-12-13 21:20:42 +00002694
2695 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2696 {
2697 vector shifted_vline;
hasso8c328f12004-10-05 21:01:23 +00002698 unsigned int index;
paulb92938a2002-12-13 21:20:42 +00002699
2700 vty->node = ENABLE_NODE;
2701 /* We can try it on enable node, cos' the vty is authenticated */
2702
2703 shifted_vline = vector_init (vector_count(vline));
2704 /* use memcpy? */
paul55468c82005-03-14 20:19:01 +00002705 for (index = 1; index < vector_active (vline); index++)
paulb92938a2002-12-13 21:20:42 +00002706 {
2707 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2708 }
2709
Christian Frankecd40b322013-09-30 12:27:51 +00002710 ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002711
2712 vector_free(shifted_vline);
2713 vty->node = onode;
2714 return ret;
2715 }
2716
2717
Christian Frankecd40b322013-09-30 12:27:51 +00002718 saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paulb92938a2002-12-13 21:20:42 +00002719
hasso87d683b2005-01-16 23:31:54 +00002720 if (vtysh)
2721 return saved_ret;
2722
paulb92938a2002-12-13 21:20:42 +00002723 /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
paul9ab68122003-01-18 01:16:20 +00002724 while ( ret != CMD_SUCCESS && ret != CMD_WARNING
paulb92938a2002-12-13 21:20:42 +00002725 && vty->node > CONFIG_NODE )
2726 {
paul9ab68122003-01-18 01:16:20 +00002727 try_node = node_parent(try_node);
2728 vty->node = try_node;
Christian Frankecd40b322013-09-30 12:27:51 +00002729 ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
paul9ab68122003-01-18 01:16:20 +00002730 tried = 1;
2731 if (ret == CMD_SUCCESS || ret == CMD_WARNING)
paulb92938a2002-12-13 21:20:42 +00002732 {
paul9ab68122003-01-18 01:16:20 +00002733 /* succesfull command, leave the node as is */
paulb92938a2002-12-13 21:20:42 +00002734 return ret;
2735 }
paulb92938a2002-12-13 21:20:42 +00002736 }
paul9ab68122003-01-18 01:16:20 +00002737 /* no command succeeded, reset the vty to the original node and
2738 return the error for this node */
2739 if ( tried )
2740 vty->node = onode;
2741 return saved_ret;
pauleda031f2003-01-18 00:39:19 +00002742}
2743
Christian Frankecd40b322013-09-30 12:27:51 +00002744/**
2745 * Execute a given command, matching it strictly against the current node.
2746 * This mode is used when reading config files.
2747 *
2748 * @param vline Command line input, vector of char* where each element is
2749 * one input token.
2750 * @param vty The vty context in which the command should be executed.
2751 * @param cmd Pointer where the struct cmd_element* of the matched command
2752 * will be stored, if any. May be set to NULL if this info is
2753 * not needed.
2754 * @return The status of the command that has been executed or an error code
2755 * as to why no command could be executed.
2756 */
paul718e3742002-12-13 20:15:29 +00002757int
paul909a2152005-03-14 17:41:45 +00002758cmd_execute_command_strict (vector vline, struct vty *vty,
paul718e3742002-12-13 20:15:29 +00002759 struct cmd_element **cmd)
2760{
Christian Frankecd40b322013-09-30 12:27:51 +00002761 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
paul718e3742002-12-13 20:15:29 +00002762}
2763
2764/* Configration make from file. */
2765int
Steve Hillea555002009-07-28 16:36:14 -04002766config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
paul718e3742002-12-13 20:15:29 +00002767{
2768 int ret;
Steve Hillea555002009-07-28 16:36:14 -04002769 *line_num = 0;
paul718e3742002-12-13 20:15:29 +00002770 vector vline;
2771
2772 while (fgets (vty->buf, VTY_BUFSIZ, fp))
2773 {
Steve Hillea555002009-07-28 16:36:14 -04002774 ++(*line_num);
paul718e3742002-12-13 20:15:29 +00002775 vline = cmd_make_strvec (vty->buf);
2776
2777 /* In case of comment line */
2778 if (vline == NULL)
2779 continue;
2780 /* Execute configuration command : this is strict match */
2781 ret = cmd_execute_command_strict (vline, vty, NULL);
2782
2783 /* Try again with setting node to CONFIG_NODE */
paulb92938a2002-12-13 21:20:42 +00002784 while (ret != CMD_SUCCESS && ret != CMD_WARNING
hassoddd85ed2004-10-13 08:18:07 +00002785 && ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE)
2786 {
paulb92938a2002-12-13 21:20:42 +00002787 vty->node = node_parent(vty->node);
hassoddd85ed2004-10-13 08:18:07 +00002788 ret = cmd_execute_command_strict (vline, vty, NULL);
2789 }
paul9ab68122003-01-18 01:16:20 +00002790
paul718e3742002-12-13 20:15:29 +00002791 cmd_free_strvec (vline);
2792
hassoddd85ed2004-10-13 08:18:07 +00002793 if (ret != CMD_SUCCESS && ret != CMD_WARNING
2794 && ret != CMD_ERR_NOTHING_TODO)
paul718e3742002-12-13 20:15:29 +00002795 return ret;
2796 }
2797 return CMD_SUCCESS;
2798}
2799
2800/* Configration from terminal */
2801DEFUN (config_terminal,
2802 config_terminal_cmd,
2803 "configure terminal",
2804 "Configuration from vty interface\n"
2805 "Configuration terminal\n")
2806{
2807 if (vty_config_lock (vty))
2808 vty->node = CONFIG_NODE;
2809 else
2810 {
2811 vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
2812 return CMD_WARNING;
2813 }
2814 return CMD_SUCCESS;
2815}
2816
2817/* Enable command */
2818DEFUN (enable,
2819 config_enable_cmd,
2820 "enable",
2821 "Turn on privileged mode command\n")
2822{
2823 /* If enable password is NULL, change to ENABLE_NODE */
2824 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2825 vty->type == VTY_SHELL_SERV)
2826 vty->node = ENABLE_NODE;
2827 else
2828 vty->node = AUTH_ENABLE_NODE;
2829
2830 return CMD_SUCCESS;
2831}
2832
2833/* Disable command */
2834DEFUN (disable,
2835 config_disable_cmd,
2836 "disable",
2837 "Turn off privileged mode command\n")
2838{
2839 if (vty->node == ENABLE_NODE)
2840 vty->node = VIEW_NODE;
2841 return CMD_SUCCESS;
2842}
2843
2844/* Down vty node level. */
2845DEFUN (config_exit,
2846 config_exit_cmd,
2847 "exit",
2848 "Exit current mode and down to previous mode\n")
2849{
2850 switch (vty->node)
2851 {
2852 case VIEW_NODE:
2853 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01002854 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00002855 if (vty_shell (vty))
2856 exit (0);
2857 else
2858 vty->status = VTY_CLOSE;
2859 break;
2860 case CONFIG_NODE:
2861 vty->node = ENABLE_NODE;
2862 vty_config_unlock (vty);
2863 break;
2864 case INTERFACE_NODE:
2865 case ZEBRA_NODE:
2866 case BGP_NODE:
2867 case RIP_NODE:
2868 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01002869 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00002870 case OSPF_NODE:
2871 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00002872 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00002873 case KEYCHAIN_NODE:
2874 case MASC_NODE:
2875 case RMAP_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02002876 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00002877 case VTY_NODE:
2878 vty->node = CONFIG_NODE;
2879 break;
2880 case BGP_VPNV4_NODE:
2881 case BGP_IPV4_NODE:
2882 case BGP_IPV4M_NODE:
2883 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002884 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00002885 vty->node = BGP_NODE;
2886 break;
2887 case KEYCHAIN_KEY_NODE:
2888 vty->node = KEYCHAIN_NODE;
2889 break;
2890 default:
2891 break;
2892 }
2893 return CMD_SUCCESS;
2894}
2895
2896/* quit is alias of exit. */
2897ALIAS (config_exit,
2898 config_quit_cmd,
2899 "quit",
2900 "Exit current mode and down to previous mode\n")
2901
2902/* End of configuration. */
2903DEFUN (config_end,
2904 config_end_cmd,
2905 "end",
2906 "End current mode and change to enable mode.")
2907{
2908 switch (vty->node)
2909 {
2910 case VIEW_NODE:
2911 case ENABLE_NODE:
Paul Jakma62687ff2008-08-23 14:27:06 +01002912 case RESTRICTED_NODE:
paul718e3742002-12-13 20:15:29 +00002913 /* Nothing to do. */
2914 break;
2915 case CONFIG_NODE:
2916 case INTERFACE_NODE:
2917 case ZEBRA_NODE:
2918 case RIP_NODE:
2919 case RIPNG_NODE:
Paul Jakma57345092011-12-25 17:52:09 +01002920 case BABEL_NODE:
paul718e3742002-12-13 20:15:29 +00002921 case BGP_NODE:
2922 case BGP_VPNV4_NODE:
2923 case BGP_IPV4_NODE:
2924 case BGP_IPV4M_NODE:
2925 case BGP_IPV6_NODE:
paul1e836592005-08-22 22:39:56 +00002926 case BGP_IPV6M_NODE:
paul718e3742002-12-13 20:15:29 +00002927 case RMAP_NODE:
2928 case OSPF_NODE:
2929 case OSPF6_NODE:
jardin9e867fe2003-12-23 08:56:18 +00002930 case ISIS_NODE:
paul718e3742002-12-13 20:15:29 +00002931 case KEYCHAIN_NODE:
2932 case KEYCHAIN_KEY_NODE:
2933 case MASC_NODE:
Everton Marques42e30782009-11-18 17:19:43 -02002934 case PIM_NODE:
paul718e3742002-12-13 20:15:29 +00002935 case VTY_NODE:
2936 vty_config_unlock (vty);
2937 vty->node = ENABLE_NODE;
2938 break;
2939 default:
2940 break;
2941 }
2942 return CMD_SUCCESS;
2943}
2944
2945/* Show version. */
2946DEFUN (show_version,
2947 show_version_cmd,
2948 "show version",
2949 SHOW_STR
2950 "Displays zebra version\n")
2951{
hasso12f6ea22005-03-07 08:35:39 +00002952 vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
2953 VTY_NEWLINE);
David Lamparter0be793e2012-11-27 01:34:56 +00002954 vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
David Lamparter7abd8752014-11-22 10:43:29 -08002955 vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE,
2956 QUAGGA_CONFIG_ARGS, VTY_NEWLINE);
paul718e3742002-12-13 20:15:29 +00002957
2958 return CMD_SUCCESS;
2959}
2960
2961/* Help display function for all node. */
2962DEFUN (config_help,
2963 config_help_cmd,
2964 "help",
2965 "Description of the interactive help system\n")
2966{
2967 vty_out (vty,
hasso6590f2c2004-10-19 20:40:08 +00002968 "Quagga VTY provides advanced help feature. When you need help,%s\
paul718e3742002-12-13 20:15:29 +00002969anytime at the command line please press '?'.%s\
2970%s\
2971If nothing matches, the help list will be empty and you must backup%s\
2972 until entering a '?' shows the available options.%s\
2973Two styles of help are provided:%s\
29741. Full help is available when you are ready to enter a%s\
2975command argument (e.g. 'show ?') and describes each possible%s\
2976argument.%s\
29772. Partial help is provided when an abbreviated argument is entered%s\
2978 and you want to know what arguments match the input%s\
2979 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2980 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
2981 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
2982 return CMD_SUCCESS;
2983}
2984
2985/* Help display function for all node. */
2986DEFUN (config_list,
2987 config_list_cmd,
2988 "list",
2989 "Print command list\n")
2990{
hasso8c328f12004-10-05 21:01:23 +00002991 unsigned int i;
paul718e3742002-12-13 20:15:29 +00002992 struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
2993 struct cmd_element *cmd;
2994
paul55468c82005-03-14 20:19:01 +00002995 for (i = 0; i < vector_active (cnode->cmd_vector); i++)
paul4275b1d2005-03-09 13:42:23 +00002996 if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
2997 && !(cmd->attr == CMD_ATTR_DEPRECATED
2998 || cmd->attr == CMD_ATTR_HIDDEN))
paul718e3742002-12-13 20:15:29 +00002999 vty_out (vty, " %s%s", cmd->string,
3000 VTY_NEWLINE);
3001 return CMD_SUCCESS;
3002}
3003
3004/* Write current configuration into file. */
3005DEFUN (config_write_file,
3006 config_write_file_cmd,
3007 "write file",
3008 "Write running configuration to memory, network, or terminal\n"
3009 "Write to configuration file\n")
3010{
hasso8c328f12004-10-05 21:01:23 +00003011 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003012 int fd;
3013 struct cmd_node *node;
3014 char *config_file;
3015 char *config_file_tmp = NULL;
3016 char *config_file_sav = NULL;
paul05865c92005-10-26 05:49:54 +00003017 int ret = CMD_WARNING;
paul718e3742002-12-13 20:15:29 +00003018 struct vty *file_vty;
3019
3020 /* Check and see if we are operating under vtysh configuration */
3021 if (host.config == NULL)
3022 {
3023 vty_out (vty, "Can't save to configuration file, using vtysh.%s",
3024 VTY_NEWLINE);
3025 return CMD_WARNING;
3026 }
3027
3028 /* Get filename. */
3029 config_file = host.config;
3030
paul05865c92005-10-26 05:49:54 +00003031 config_file_sav =
3032 XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
paul718e3742002-12-13 20:15:29 +00003033 strcpy (config_file_sav, config_file);
3034 strcat (config_file_sav, CONF_BACKUP_EXT);
3035
3036
paul05865c92005-10-26 05:49:54 +00003037 config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
paul718e3742002-12-13 20:15:29 +00003038 sprintf (config_file_tmp, "%s.XXXXXX", config_file);
3039
3040 /* Open file to configuration write. */
3041 fd = mkstemp (config_file_tmp);
3042 if (fd < 0)
3043 {
3044 vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
3045 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003046 goto finished;
paul718e3742002-12-13 20:15:29 +00003047 }
3048
3049 /* Make vty for configuration file. */
3050 file_vty = vty_new ();
3051 file_vty->fd = fd;
3052 file_vty->type = VTY_FILE;
3053
3054 /* Config file header print. */
3055 vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
3056 vty_time_print (file_vty, 1);
3057 vty_out (file_vty, "!\n");
3058
paul55468c82005-03-14 20:19:01 +00003059 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003060 if ((node = vector_slot (cmdvec, i)) && node->func)
3061 {
3062 if ((*node->func) (file_vty))
3063 vty_out (file_vty, "!\n");
3064 }
3065 vty_close (file_vty);
3066
3067 if (unlink (config_file_sav) != 0)
3068 if (errno != ENOENT)
3069 {
3070 vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
3071 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003072 goto finished;
paul718e3742002-12-13 20:15:29 +00003073 }
3074 if (link (config_file, config_file_sav) != 0)
3075 {
3076 vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
3077 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003078 goto finished;
paul718e3742002-12-13 20:15:29 +00003079 }
3080 sync ();
3081 if (unlink (config_file) != 0)
3082 {
3083 vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
3084 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003085 goto finished;
paul718e3742002-12-13 20:15:29 +00003086 }
3087 if (link (config_file_tmp, config_file) != 0)
3088 {
3089 vty_out (vty, "Can't save configuration file %s.%s", config_file,
3090 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003091 goto finished;
paul718e3742002-12-13 20:15:29 +00003092 }
paul718e3742002-12-13 20:15:29 +00003093 sync ();
3094
gdtaa593d52003-12-22 20:15:53 +00003095 if (chmod (config_file, CONFIGFILE_MASK) != 0)
3096 {
3097 vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
ajs6099b3b2004-11-20 02:06:59 +00003098 config_file, safe_strerror(errno), errno, VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003099 goto finished;
gdtaa593d52003-12-22 20:15:53 +00003100 }
3101
paul718e3742002-12-13 20:15:29 +00003102 vty_out (vty, "Configuration saved to %s%s", config_file,
3103 VTY_NEWLINE);
paul05865c92005-10-26 05:49:54 +00003104 ret = CMD_SUCCESS;
3105
3106finished:
3107 unlink (config_file_tmp);
3108 XFREE (MTYPE_TMP, config_file_tmp);
3109 XFREE (MTYPE_TMP, config_file_sav);
3110 return ret;
paul718e3742002-12-13 20:15:29 +00003111}
3112
3113ALIAS (config_write_file,
3114 config_write_cmd,
3115 "write",
3116 "Write running configuration to memory, network, or terminal\n")
3117
3118ALIAS (config_write_file,
3119 config_write_memory_cmd,
3120 "write memory",
3121 "Write running configuration to memory, network, or terminal\n"
3122 "Write configuration to the file (same as write file)\n")
3123
3124ALIAS (config_write_file,
3125 copy_runningconfig_startupconfig_cmd,
3126 "copy running-config startup-config",
3127 "Copy configuration\n"
3128 "Copy running config to... \n"
3129 "Copy running config to startup config (same as write file)\n")
3130
3131/* Write current configuration into the terminal. */
3132DEFUN (config_write_terminal,
3133 config_write_terminal_cmd,
3134 "write terminal",
3135 "Write running configuration to memory, network, or terminal\n"
3136 "Write to terminal\n")
3137{
hasso8c328f12004-10-05 21:01:23 +00003138 unsigned int i;
paul718e3742002-12-13 20:15:29 +00003139 struct cmd_node *node;
3140
3141 if (vty->type == VTY_SHELL_SERV)
3142 {
paul55468c82005-03-14 20:19:01 +00003143 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003144 if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
3145 {
3146 if ((*node->func) (vty))
3147 vty_out (vty, "!%s", VTY_NEWLINE);
3148 }
3149 }
3150 else
3151 {
3152 vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3153 VTY_NEWLINE);
3154 vty_out (vty, "!%s", VTY_NEWLINE);
3155
paul55468c82005-03-14 20:19:01 +00003156 for (i = 0; i < vector_active (cmdvec); i++)
paul718e3742002-12-13 20:15:29 +00003157 if ((node = vector_slot (cmdvec, i)) && node->func)
3158 {
3159 if ((*node->func) (vty))
3160 vty_out (vty, "!%s", VTY_NEWLINE);
3161 }
3162 vty_out (vty, "end%s",VTY_NEWLINE);
3163 }
3164 return CMD_SUCCESS;
3165}
3166
3167/* Write current configuration into the terminal. */
3168ALIAS (config_write_terminal,
3169 show_running_config_cmd,
3170 "show running-config",
3171 SHOW_STR
3172 "running configuration\n")
3173
3174/* Write startup configuration into the terminal. */
3175DEFUN (show_startup_config,
3176 show_startup_config_cmd,
3177 "show startup-config",
3178 SHOW_STR
3179 "Contentes of startup configuration\n")
3180{
3181 char buf[BUFSIZ];
3182 FILE *confp;
3183
3184 confp = fopen (host.config, "r");
3185 if (confp == NULL)
3186 {
3187 vty_out (vty, "Can't open configuration file [%s]%s",
3188 host.config, VTY_NEWLINE);
3189 return CMD_WARNING;
3190 }
3191
3192 while (fgets (buf, BUFSIZ, confp))
3193 {
3194 char *cp = buf;
3195
3196 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3197 cp++;
3198 *cp = '\0';
3199
3200 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
3201 }
3202
3203 fclose (confp);
3204
3205 return CMD_SUCCESS;
3206}
3207
3208/* Hostname configuration */
3209DEFUN (config_hostname,
3210 hostname_cmd,
3211 "hostname WORD",
3212 "Set system's network name\n"
3213 "This system's network name\n")
3214{
3215 if (!isalpha((int) *argv[0]))
3216 {
3217 vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
3218 return CMD_WARNING;
3219 }
3220
3221 if (host.name)
paul05865c92005-10-26 05:49:54 +00003222 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003223
paul05865c92005-10-26 05:49:54 +00003224 host.name = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003225 return CMD_SUCCESS;
3226}
3227
3228DEFUN (config_no_hostname,
3229 no_hostname_cmd,
3230 "no hostname [HOSTNAME]",
3231 NO_STR
3232 "Reset system's network name\n"
3233 "Host name of this router\n")
3234{
3235 if (host.name)
paul05865c92005-10-26 05:49:54 +00003236 XFREE (MTYPE_HOST, host.name);
paul718e3742002-12-13 20:15:29 +00003237 host.name = NULL;
3238 return CMD_SUCCESS;
3239}
3240
3241/* VTY interface password set. */
3242DEFUN (config_password, password_cmd,
3243 "password (8|) WORD",
3244 "Assign the terminal connection password\n"
3245 "Specifies a HIDDEN password will follow\n"
3246 "dummy string \n"
3247 "The HIDDEN line password string\n")
3248{
3249 /* Argument check. */
3250 if (argc == 0)
3251 {
3252 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3253 return CMD_WARNING;
3254 }
3255
3256 if (argc == 2)
3257 {
3258 if (*argv[0] == '8')
3259 {
3260 if (host.password)
paul05865c92005-10-26 05:49:54 +00003261 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003262 host.password = NULL;
3263 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003264 XFREE (MTYPE_HOST, host.password_encrypt);
3265 host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003266 return CMD_SUCCESS;
3267 }
3268 else
3269 {
3270 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3271 return CMD_WARNING;
3272 }
3273 }
3274
3275 if (!isalnum ((int) *argv[0]))
3276 {
3277 vty_out (vty,
3278 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3279 return CMD_WARNING;
3280 }
3281
3282 if (host.password)
paul05865c92005-10-26 05:49:54 +00003283 XFREE (MTYPE_HOST, host.password);
paul718e3742002-12-13 20:15:29 +00003284 host.password = NULL;
3285
3286 if (host.encrypt)
3287 {
3288 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003289 XFREE (MTYPE_HOST, host.password_encrypt);
3290 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003291 }
3292 else
paul05865c92005-10-26 05:49:54 +00003293 host.password = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003294
3295 return CMD_SUCCESS;
3296}
3297
3298ALIAS (config_password, password_text_cmd,
3299 "password LINE",
3300 "Assign the terminal connection password\n"
3301 "The UNENCRYPTED (cleartext) line password\n")
3302
3303/* VTY enable password set. */
3304DEFUN (config_enable_password, enable_password_cmd,
3305 "enable password (8|) WORD",
3306 "Modify enable password parameters\n"
3307 "Assign the privileged level password\n"
3308 "Specifies a HIDDEN password will follow\n"
3309 "dummy string \n"
3310 "The HIDDEN 'enable' password string\n")
3311{
3312 /* Argument check. */
3313 if (argc == 0)
3314 {
3315 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3316 return CMD_WARNING;
3317 }
3318
3319 /* Crypt type is specified. */
3320 if (argc == 2)
3321 {
3322 if (*argv[0] == '8')
3323 {
3324 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003325 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003326 host.enable = NULL;
3327
3328 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003329 XFREE (MTYPE_HOST, host.enable_encrypt);
3330 host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
paul718e3742002-12-13 20:15:29 +00003331
3332 return CMD_SUCCESS;
3333 }
3334 else
3335 {
3336 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3337 return CMD_WARNING;
3338 }
3339 }
3340
3341 if (!isalnum ((int) *argv[0]))
3342 {
3343 vty_out (vty,
3344 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3345 return CMD_WARNING;
3346 }
3347
3348 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003349 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003350 host.enable = NULL;
3351
3352 /* Plain password input. */
3353 if (host.encrypt)
3354 {
3355 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003356 XFREE (MTYPE_HOST, host.enable_encrypt);
3357 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
paul718e3742002-12-13 20:15:29 +00003358 }
3359 else
paul05865c92005-10-26 05:49:54 +00003360 host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
paul718e3742002-12-13 20:15:29 +00003361
3362 return CMD_SUCCESS;
3363}
3364
3365ALIAS (config_enable_password,
3366 enable_password_text_cmd,
3367 "enable password LINE",
3368 "Modify enable password parameters\n"
3369 "Assign the privileged level password\n"
3370 "The UNENCRYPTED (cleartext) 'enable' password\n")
3371
3372/* VTY enable password delete. */
3373DEFUN (no_config_enable_password, no_enable_password_cmd,
3374 "no enable password",
3375 NO_STR
3376 "Modify enable password parameters\n"
3377 "Assign the privileged level password\n")
3378{
3379 if (host.enable)
paul05865c92005-10-26 05:49:54 +00003380 XFREE (MTYPE_HOST, host.enable);
paul718e3742002-12-13 20:15:29 +00003381 host.enable = NULL;
3382
3383 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003384 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003385 host.enable_encrypt = NULL;
3386
3387 return CMD_SUCCESS;
3388}
3389
3390DEFUN (service_password_encrypt,
3391 service_password_encrypt_cmd,
3392 "service password-encryption",
3393 "Set up miscellaneous service\n"
3394 "Enable encrypted passwords\n")
3395{
3396 if (host.encrypt)
3397 return CMD_SUCCESS;
3398
3399 host.encrypt = 1;
3400
3401 if (host.password)
3402 {
3403 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003404 XFREE (MTYPE_HOST, host.password_encrypt);
3405 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
paul718e3742002-12-13 20:15:29 +00003406 }
3407 if (host.enable)
3408 {
3409 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003410 XFREE (MTYPE_HOST, host.enable_encrypt);
3411 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
paul718e3742002-12-13 20:15:29 +00003412 }
3413
3414 return CMD_SUCCESS;
3415}
3416
3417DEFUN (no_service_password_encrypt,
3418 no_service_password_encrypt_cmd,
3419 "no service password-encryption",
3420 NO_STR
3421 "Set up miscellaneous service\n"
3422 "Enable encrypted passwords\n")
3423{
3424 if (! host.encrypt)
3425 return CMD_SUCCESS;
3426
3427 host.encrypt = 0;
3428
3429 if (host.password_encrypt)
paul05865c92005-10-26 05:49:54 +00003430 XFREE (MTYPE_HOST, host.password_encrypt);
paul718e3742002-12-13 20:15:29 +00003431 host.password_encrypt = NULL;
3432
3433 if (host.enable_encrypt)
paul05865c92005-10-26 05:49:54 +00003434 XFREE (MTYPE_HOST, host.enable_encrypt);
paul718e3742002-12-13 20:15:29 +00003435 host.enable_encrypt = NULL;
3436
3437 return CMD_SUCCESS;
3438}
3439
3440DEFUN (config_terminal_length, config_terminal_length_cmd,
3441 "terminal length <0-512>",
3442 "Set terminal line parameters\n"
3443 "Set number of lines on a screen\n"
3444 "Number of lines on screen (0 for no pausing)\n")
3445{
3446 int lines;
3447 char *endptr = NULL;
3448
3449 lines = strtol (argv[0], &endptr, 10);
3450 if (lines < 0 || lines > 512 || *endptr != '\0')
3451 {
3452 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3453 return CMD_WARNING;
3454 }
3455 vty->lines = lines;
3456
3457 return CMD_SUCCESS;
3458}
3459
3460DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
3461 "terminal no length",
3462 "Set terminal line parameters\n"
3463 NO_STR
3464 "Set number of lines on a screen\n")
3465{
3466 vty->lines = -1;
3467 return CMD_SUCCESS;
3468}
3469
3470DEFUN (service_terminal_length, service_terminal_length_cmd,
3471 "service terminal-length <0-512>",
3472 "Set up miscellaneous service\n"
3473 "System wide terminal length configuration\n"
3474 "Number of lines of VTY (0 means no line control)\n")
3475{
3476 int lines;
3477 char *endptr = NULL;
3478
3479 lines = strtol (argv[0], &endptr, 10);
3480 if (lines < 0 || lines > 512 || *endptr != '\0')
3481 {
3482 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3483 return CMD_WARNING;
3484 }
3485 host.lines = lines;
3486
3487 return CMD_SUCCESS;
3488}
3489
3490DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
3491 "no service terminal-length [<0-512>]",
3492 NO_STR
3493 "Set up miscellaneous service\n"
3494 "System wide terminal length configuration\n"
3495 "Number of lines of VTY (0 means no line control)\n")
3496{
3497 host.lines = -1;
3498 return CMD_SUCCESS;
3499}
3500
ajs2885f722004-12-17 23:16:33 +00003501DEFUN_HIDDEN (do_echo,
3502 echo_cmd,
3503 "echo .MESSAGE",
3504 "Echo a message back to the vty\n"
3505 "The message to echo\n")
3506{
3507 char *message;
3508
ajsf6834d42005-01-28 20:28:35 +00003509 vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
3510 VTY_NEWLINE);
3511 if (message)
3512 XFREE(MTYPE_TMP, message);
ajs2885f722004-12-17 23:16:33 +00003513 return CMD_SUCCESS;
3514}
3515
ajs274a4a42004-12-07 15:39:31 +00003516DEFUN (config_logmsg,
3517 config_logmsg_cmd,
3518 "logmsg "LOG_LEVELS" .MESSAGE",
3519 "Send a message to enabled logging destinations\n"
3520 LOG_LEVEL_DESC
3521 "The message to send\n")
3522{
3523 int level;
3524 char *message;
3525
3526 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3527 return CMD_ERR_NO_MATCH;
3528
Christian Hammersfc951862011-03-23 13:07:55 +03003529 zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
ajsf6834d42005-01-28 20:28:35 +00003530 if (message)
3531 XFREE(MTYPE_TMP, message);
ajs274a4a42004-12-07 15:39:31 +00003532 return CMD_SUCCESS;
3533}
3534
3535DEFUN (show_logging,
3536 show_logging_cmd,
3537 "show logging",
3538 SHOW_STR
3539 "Show current logging configuration\n")
3540{
3541 struct zlog *zl = zlog_default;
3542
3543 vty_out (vty, "Syslog logging: ");
3544 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3545 vty_out (vty, "disabled");
3546 else
3547 vty_out (vty, "level %s, facility %s, ident %s",
3548 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3549 facility_name(zl->facility), zl->ident);
3550 vty_out (vty, "%s", VTY_NEWLINE);
3551
3552 vty_out (vty, "Stdout logging: ");
3553 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3554 vty_out (vty, "disabled");
3555 else
3556 vty_out (vty, "level %s",
3557 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3558 vty_out (vty, "%s", VTY_NEWLINE);
3559
3560 vty_out (vty, "Monitor logging: ");
3561 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3562 vty_out (vty, "disabled");
3563 else
3564 vty_out (vty, "level %s",
3565 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3566 vty_out (vty, "%s", VTY_NEWLINE);
3567
3568 vty_out (vty, "File logging: ");
3569 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
3570 !zl->fp)
3571 vty_out (vty, "disabled");
3572 else
3573 vty_out (vty, "level %s, filename %s",
3574 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3575 zl->filename);
3576 vty_out (vty, "%s", VTY_NEWLINE);
3577
3578 vty_out (vty, "Protocol name: %s%s",
3579 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3580 vty_out (vty, "Record priority: %s%s",
3581 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00003582 vty_out (vty, "Timestamp precision: %d%s",
3583 zl->timestamp_precision, VTY_NEWLINE);
ajs274a4a42004-12-07 15:39:31 +00003584
3585 return CMD_SUCCESS;
3586}
3587
paul718e3742002-12-13 20:15:29 +00003588DEFUN (config_log_stdout,
3589 config_log_stdout_cmd,
3590 "log stdout",
3591 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003592 "Set stdout logging level\n")
paul718e3742002-12-13 20:15:29 +00003593{
ajs274a4a42004-12-07 15:39:31 +00003594 zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3595 return CMD_SUCCESS;
3596}
3597
3598DEFUN (config_log_stdout_level,
3599 config_log_stdout_level_cmd,
3600 "log stdout "LOG_LEVELS,
3601 "Logging control\n"
3602 "Set stdout logging level\n"
3603 LOG_LEVEL_DESC)
3604{
3605 int level;
3606
3607 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3608 return CMD_ERR_NO_MATCH;
3609 zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
paul718e3742002-12-13 20:15:29 +00003610 return CMD_SUCCESS;
3611}
3612
3613DEFUN (no_config_log_stdout,
3614 no_config_log_stdout_cmd,
ajs274a4a42004-12-07 15:39:31 +00003615 "no log stdout [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003616 NO_STR
3617 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003618 "Cancel logging to stdout\n"
3619 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003620{
ajs274a4a42004-12-07 15:39:31 +00003621 zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003622 return CMD_SUCCESS;
3623}
3624
ajs274a4a42004-12-07 15:39:31 +00003625DEFUN (config_log_monitor,
3626 config_log_monitor_cmd,
3627 "log monitor",
paul718e3742002-12-13 20:15:29 +00003628 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003629 "Set terminal line (monitor) logging level\n")
3630{
3631 zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3632 return CMD_SUCCESS;
3633}
3634
3635DEFUN (config_log_monitor_level,
3636 config_log_monitor_level_cmd,
3637 "log monitor "LOG_LEVELS,
3638 "Logging control\n"
3639 "Set terminal line (monitor) logging level\n"
3640 LOG_LEVEL_DESC)
3641{
3642 int level;
3643
3644 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3645 return CMD_ERR_NO_MATCH;
3646 zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
3647 return CMD_SUCCESS;
3648}
3649
3650DEFUN (no_config_log_monitor,
3651 no_config_log_monitor_cmd,
3652 "no log monitor [LEVEL]",
3653 NO_STR
3654 "Logging control\n"
3655 "Disable terminal line (monitor) logging\n"
3656 "Logging level\n")
3657{
3658 zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3659 return CMD_SUCCESS;
3660}
3661
3662static int
3663set_log_file(struct vty *vty, const char *fname, int loglevel)
paul718e3742002-12-13 20:15:29 +00003664{
3665 int ret;
paul9035efa2004-10-10 11:56:56 +00003666 char *p = NULL;
3667 const char *fullpath;
3668
paul718e3742002-12-13 20:15:29 +00003669 /* Path detection. */
ajs274a4a42004-12-07 15:39:31 +00003670 if (! IS_DIRECTORY_SEP (*fname))
paul718e3742002-12-13 20:15:29 +00003671 {
paul9035efa2004-10-10 11:56:56 +00003672 char cwd[MAXPATHLEN+1];
3673 cwd[MAXPATHLEN] = '\0';
3674
3675 if (getcwd (cwd, MAXPATHLEN) == NULL)
3676 {
3677 zlog_err ("config_log_file: Unable to alloc mem!");
3678 return CMD_WARNING;
3679 }
3680
ajs274a4a42004-12-07 15:39:31 +00003681 if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
paul9035efa2004-10-10 11:56:56 +00003682 == NULL)
3683 {
3684 zlog_err ("config_log_file: Unable to alloc mem!");
3685 return CMD_WARNING;
3686 }
ajs274a4a42004-12-07 15:39:31 +00003687 sprintf (p, "%s/%s", cwd, fname);
paul9035efa2004-10-10 11:56:56 +00003688 fullpath = p;
paul718e3742002-12-13 20:15:29 +00003689 }
3690 else
ajs274a4a42004-12-07 15:39:31 +00003691 fullpath = fname;
paul718e3742002-12-13 20:15:29 +00003692
ajs274a4a42004-12-07 15:39:31 +00003693 ret = zlog_set_file (NULL, fullpath, loglevel);
paul718e3742002-12-13 20:15:29 +00003694
paul9035efa2004-10-10 11:56:56 +00003695 if (p)
3696 XFREE (MTYPE_TMP, p);
3697
paul718e3742002-12-13 20:15:29 +00003698 if (!ret)
3699 {
ajs274a4a42004-12-07 15:39:31 +00003700 vty_out (vty, "can't open logfile %s\n", fname);
paul718e3742002-12-13 20:15:29 +00003701 return CMD_WARNING;
3702 }
3703
3704 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003705 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003706
paul05865c92005-10-26 05:49:54 +00003707 host.logfile = XSTRDUP (MTYPE_HOST, fname);
paul718e3742002-12-13 20:15:29 +00003708
3709 return CMD_SUCCESS;
3710}
3711
ajs274a4a42004-12-07 15:39:31 +00003712DEFUN (config_log_file,
3713 config_log_file_cmd,
3714 "log file FILENAME",
3715 "Logging control\n"
3716 "Logging to file\n"
3717 "Logging filename\n")
3718{
3719 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3720}
3721
3722DEFUN (config_log_file_level,
3723 config_log_file_level_cmd,
3724 "log file FILENAME "LOG_LEVELS,
3725 "Logging control\n"
3726 "Logging to file\n"
3727 "Logging filename\n"
3728 LOG_LEVEL_DESC)
3729{
3730 int level;
3731
3732 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3733 return CMD_ERR_NO_MATCH;
3734 return set_log_file(vty, argv[0], level);
3735}
3736
paul718e3742002-12-13 20:15:29 +00003737DEFUN (no_config_log_file,
3738 no_config_log_file_cmd,
3739 "no log file [FILENAME]",
3740 NO_STR
3741 "Logging control\n"
3742 "Cancel logging to file\n"
3743 "Logging file name\n")
3744{
3745 zlog_reset_file (NULL);
3746
3747 if (host.logfile)
paul05865c92005-10-26 05:49:54 +00003748 XFREE (MTYPE_HOST, host.logfile);
paul718e3742002-12-13 20:15:29 +00003749
3750 host.logfile = NULL;
3751
3752 return CMD_SUCCESS;
3753}
3754
ajs274a4a42004-12-07 15:39:31 +00003755ALIAS (no_config_log_file,
3756 no_config_log_file_level_cmd,
3757 "no log file FILENAME LEVEL",
3758 NO_STR
3759 "Logging control\n"
3760 "Cancel logging to file\n"
3761 "Logging file name\n"
3762 "Logging level\n")
3763
paul718e3742002-12-13 20:15:29 +00003764DEFUN (config_log_syslog,
3765 config_log_syslog_cmd,
3766 "log syslog",
3767 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003768 "Set syslog logging level\n")
paul718e3742002-12-13 20:15:29 +00003769{
ajs274a4a42004-12-07 15:39:31 +00003770 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003771 return CMD_SUCCESS;
3772}
3773
ajs274a4a42004-12-07 15:39:31 +00003774DEFUN (config_log_syslog_level,
3775 config_log_syslog_level_cmd,
3776 "log syslog "LOG_LEVELS,
paul12ab19f2003-07-26 06:14:55 +00003777 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003778 "Set syslog logging level\n"
3779 LOG_LEVEL_DESC)
paul12ab19f2003-07-26 06:14:55 +00003780{
ajs274a4a42004-12-07 15:39:31 +00003781 int level;
paul12ab19f2003-07-26 06:14:55 +00003782
ajs274a4a42004-12-07 15:39:31 +00003783 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3784 return CMD_ERR_NO_MATCH;
3785 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
3786 return CMD_SUCCESS;
3787}
paul12ab19f2003-07-26 06:14:55 +00003788
ajs274a4a42004-12-07 15:39:31 +00003789DEFUN_DEPRECATED (config_log_syslog_facility,
3790 config_log_syslog_facility_cmd,
3791 "log syslog facility "LOG_FACILITIES,
3792 "Logging control\n"
3793 "Logging goes to syslog\n"
3794 "(Deprecated) Facility parameter for syslog messages\n"
3795 LOG_FACILITY_DESC)
3796{
3797 int facility;
paul12ab19f2003-07-26 06:14:55 +00003798
ajs274a4a42004-12-07 15:39:31 +00003799 if ((facility = facility_match(argv[0])) < 0)
3800 return CMD_ERR_NO_MATCH;
3801
3802 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
paul12ab19f2003-07-26 06:14:55 +00003803 zlog_default->facility = facility;
paul718e3742002-12-13 20:15:29 +00003804 return CMD_SUCCESS;
3805}
3806
3807DEFUN (no_config_log_syslog,
3808 no_config_log_syslog_cmd,
ajs274a4a42004-12-07 15:39:31 +00003809 "no log syslog [LEVEL]",
paul718e3742002-12-13 20:15:29 +00003810 NO_STR
3811 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003812 "Cancel logging to syslog\n"
3813 "Logging level\n")
paul718e3742002-12-13 20:15:29 +00003814{
ajs274a4a42004-12-07 15:39:31 +00003815 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
paul718e3742002-12-13 20:15:29 +00003816 return CMD_SUCCESS;
3817}
3818
paul12ab19f2003-07-26 06:14:55 +00003819ALIAS (no_config_log_syslog,
3820 no_config_log_syslog_facility_cmd,
ajs274a4a42004-12-07 15:39:31 +00003821 "no log syslog facility "LOG_FACILITIES,
paul12ab19f2003-07-26 06:14:55 +00003822 NO_STR
3823 "Logging control\n"
3824 "Logging goes to syslog\n"
3825 "Facility parameter for syslog messages\n"
ajs274a4a42004-12-07 15:39:31 +00003826 LOG_FACILITY_DESC)
paul12ab19f2003-07-26 06:14:55 +00003827
ajs274a4a42004-12-07 15:39:31 +00003828DEFUN (config_log_facility,
3829 config_log_facility_cmd,
3830 "log facility "LOG_FACILITIES,
paul718e3742002-12-13 20:15:29 +00003831 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003832 "Facility parameter for syslog messages\n"
3833 LOG_FACILITY_DESC)
paul718e3742002-12-13 20:15:29 +00003834{
ajs274a4a42004-12-07 15:39:31 +00003835 int facility;
3836
3837 if ((facility = facility_match(argv[0])) < 0)
3838 return CMD_ERR_NO_MATCH;
3839 zlog_default->facility = facility;
3840 return CMD_SUCCESS;
paul718e3742002-12-13 20:15:29 +00003841}
3842
ajs274a4a42004-12-07 15:39:31 +00003843DEFUN (no_config_log_facility,
3844 no_config_log_facility_cmd,
3845 "no log facility [FACILITY]",
paul718e3742002-12-13 20:15:29 +00003846 NO_STR
3847 "Logging control\n"
ajs274a4a42004-12-07 15:39:31 +00003848 "Reset syslog facility to default (daemon)\n"
3849 "Syslog facility\n")
paul718e3742002-12-13 20:15:29 +00003850{
ajs274a4a42004-12-07 15:39:31 +00003851 zlog_default->facility = LOG_DAEMON;
3852 return CMD_SUCCESS;
3853}
3854
3855DEFUN_DEPRECATED (config_log_trap,
3856 config_log_trap_cmd,
3857 "log trap "LOG_LEVELS,
3858 "Logging control\n"
3859 "(Deprecated) Set logging level and default for all destinations\n"
3860 LOG_LEVEL_DESC)
3861{
3862 int new_level ;
3863 int i;
3864
3865 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3866 return CMD_ERR_NO_MATCH;
3867
3868 zlog_default->default_lvl = new_level;
3869 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3870 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3871 zlog_default->maxlvl[i] = new_level;
3872 return CMD_SUCCESS;
3873}
3874
3875DEFUN_DEPRECATED (no_config_log_trap,
3876 no_config_log_trap_cmd,
3877 "no log trap [LEVEL]",
3878 NO_STR
3879 "Logging control\n"
3880 "Permit all logging information\n"
3881 "Logging level\n")
3882{
3883 zlog_default->default_lvl = LOG_DEBUG;
paul718e3742002-12-13 20:15:29 +00003884 return CMD_SUCCESS;
3885}
3886
3887DEFUN (config_log_record_priority,
3888 config_log_record_priority_cmd,
3889 "log record-priority",
3890 "Logging control\n"
3891 "Log the priority of the message within the message\n")
3892{
3893 zlog_default->record_priority = 1 ;
3894 return CMD_SUCCESS;
3895}
3896
3897DEFUN (no_config_log_record_priority,
3898 no_config_log_record_priority_cmd,
3899 "no log record-priority",
3900 NO_STR
3901 "Logging control\n"
3902 "Do not log the priority of the message within the message\n")
3903{
3904 zlog_default->record_priority = 0 ;
3905 return CMD_SUCCESS;
3906}
3907
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00003908DEFUN (config_log_timestamp_precision,
3909 config_log_timestamp_precision_cmd,
3910 "log timestamp precision <0-6>",
3911 "Logging control\n"
3912 "Timestamp configuration\n"
3913 "Set the timestamp precision\n"
3914 "Number of subsecond digits\n")
3915{
3916 if (argc != 1)
3917 {
3918 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
3919 return CMD_WARNING;
3920 }
3921
3922 VTY_GET_INTEGER_RANGE("Timestamp Precision",
3923 zlog_default->timestamp_precision, argv[0], 0, 6);
3924 return CMD_SUCCESS;
3925}
3926
3927DEFUN (no_config_log_timestamp_precision,
3928 no_config_log_timestamp_precision_cmd,
3929 "no log timestamp precision",
3930 NO_STR
3931 "Logging control\n"
3932 "Timestamp configuration\n"
3933 "Reset the timestamp precision to the default value of 0\n")
3934{
3935 zlog_default->timestamp_precision = 0 ;
3936 return CMD_SUCCESS;
3937}
3938
paul3b0c5d92005-03-08 10:43:43 +00003939DEFUN (banner_motd_file,
3940 banner_motd_file_cmd,
3941 "banner motd file [FILE]",
3942 "Set banner\n"
3943 "Banner for motd\n"
3944 "Banner from a file\n"
3945 "Filename\n")
3946{
paulb45da6f2005-03-08 15:16:57 +00003947 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00003948 XFREE (MTYPE_HOST, host.motdfile);
3949 host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
paulb45da6f2005-03-08 15:16:57 +00003950
paul3b0c5d92005-03-08 10:43:43 +00003951 return CMD_SUCCESS;
3952}
paul718e3742002-12-13 20:15:29 +00003953
3954DEFUN (banner_motd_default,
3955 banner_motd_default_cmd,
3956 "banner motd default",
3957 "Set banner string\n"
3958 "Strings for motd\n"
3959 "Default string\n")
3960{
3961 host.motd = default_motd;
3962 return CMD_SUCCESS;
3963}
3964
3965DEFUN (no_banner_motd,
3966 no_banner_motd_cmd,
3967 "no banner motd",
3968 NO_STR
3969 "Set banner string\n"
3970 "Strings for motd\n")
3971{
3972 host.motd = NULL;
paul22085182005-03-08 16:00:12 +00003973 if (host.motdfile)
paul05865c92005-10-26 05:49:54 +00003974 XFREE (MTYPE_HOST, host.motdfile);
paul3b0c5d92005-03-08 10:43:43 +00003975 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00003976 return CMD_SUCCESS;
3977}
3978
3979/* Set config filename. Called from vty.c */
3980void
3981host_config_set (char *filename)
3982{
Chris Caputo228da422009-07-18 05:44:03 +00003983 if (host.config)
3984 XFREE (MTYPE_HOST, host.config);
paul05865c92005-10-26 05:49:54 +00003985 host.config = XSTRDUP (MTYPE_HOST, filename);
paul718e3742002-12-13 20:15:29 +00003986}
3987
3988void
3989install_default (enum node_type node)
3990{
3991 install_element (node, &config_exit_cmd);
3992 install_element (node, &config_quit_cmd);
3993 install_element (node, &config_end_cmd);
3994 install_element (node, &config_help_cmd);
3995 install_element (node, &config_list_cmd);
3996
3997 install_element (node, &config_write_terminal_cmd);
3998 install_element (node, &config_write_file_cmd);
3999 install_element (node, &config_write_memory_cmd);
4000 install_element (node, &config_write_cmd);
4001 install_element (node, &show_running_config_cmd);
4002}
4003
4004/* Initialize command interface. Install basic nodes and commands. */
4005void
4006cmd_init (int terminal)
4007{
Christian Frankecd40b322013-09-30 12:27:51 +00004008 command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
4009 token_cr.type = TOKEN_TERMINAL;
4010 token_cr.cmd = command_cr;
4011 token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
Chris Caputo228da422009-07-18 05:44:03 +00004012
paul718e3742002-12-13 20:15:29 +00004013 /* Allocate initial top vector of commands. */
4014 cmdvec = vector_init (VECTOR_MIN_SIZE);
4015
4016 /* Default host value settings. */
4017 host.name = NULL;
4018 host.password = NULL;
4019 host.enable = NULL;
4020 host.logfile = NULL;
4021 host.config = NULL;
4022 host.lines = -1;
4023 host.motd = default_motd;
paul3b0c5d92005-03-08 10:43:43 +00004024 host.motdfile = NULL;
paul718e3742002-12-13 20:15:29 +00004025
4026 /* Install top nodes. */
4027 install_node (&view_node, NULL);
4028 install_node (&enable_node, NULL);
4029 install_node (&auth_node, NULL);
4030 install_node (&auth_enable_node, NULL);
Paul Jakma62687ff2008-08-23 14:27:06 +01004031 install_node (&restricted_node, NULL);
paul718e3742002-12-13 20:15:29 +00004032 install_node (&config_node, config_write_host);
4033
4034 /* Each node's basic commands. */
4035 install_element (VIEW_NODE, &show_version_cmd);
4036 if (terminal)
4037 {
4038 install_element (VIEW_NODE, &config_list_cmd);
4039 install_element (VIEW_NODE, &config_exit_cmd);
4040 install_element (VIEW_NODE, &config_quit_cmd);
4041 install_element (VIEW_NODE, &config_help_cmd);
4042 install_element (VIEW_NODE, &config_enable_cmd);
4043 install_element (VIEW_NODE, &config_terminal_length_cmd);
4044 install_element (VIEW_NODE, &config_terminal_no_length_cmd);
ajs274a4a42004-12-07 15:39:31 +00004045 install_element (VIEW_NODE, &show_logging_cmd);
ajs2885f722004-12-17 23:16:33 +00004046 install_element (VIEW_NODE, &echo_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004047
4048 install_element (RESTRICTED_NODE, &config_list_cmd);
4049 install_element (RESTRICTED_NODE, &config_exit_cmd);
4050 install_element (RESTRICTED_NODE, &config_quit_cmd);
4051 install_element (RESTRICTED_NODE, &config_help_cmd);
4052 install_element (RESTRICTED_NODE, &config_enable_cmd);
4053 install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
4054 install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
4055 install_element (RESTRICTED_NODE, &echo_cmd);
paul718e3742002-12-13 20:15:29 +00004056 }
4057
4058 if (terminal)
4059 {
4060 install_default (ENABLE_NODE);
4061 install_element (ENABLE_NODE, &config_disable_cmd);
4062 install_element (ENABLE_NODE, &config_terminal_cmd);
4063 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4064 }
4065 install_element (ENABLE_NODE, &show_startup_config_cmd);
4066 install_element (ENABLE_NODE, &show_version_cmd);
paul718e3742002-12-13 20:15:29 +00004067
4068 if (terminal)
paul718e3742002-12-13 20:15:29 +00004069 {
hassoe7168df2004-10-03 20:11:32 +00004070 install_element (ENABLE_NODE, &config_terminal_length_cmd);
4071 install_element (ENABLE_NODE, &config_terminal_no_length_cmd);
ajs274a4a42004-12-07 15:39:31 +00004072 install_element (ENABLE_NODE, &show_logging_cmd);
ajs2885f722004-12-17 23:16:33 +00004073 install_element (ENABLE_NODE, &echo_cmd);
ajs274a4a42004-12-07 15:39:31 +00004074 install_element (ENABLE_NODE, &config_logmsg_cmd);
hassoe7168df2004-10-03 20:11:32 +00004075
4076 install_default (CONFIG_NODE);
hassoea8e9d92004-10-07 21:32:14 +00004077 }
4078
4079 install_element (CONFIG_NODE, &hostname_cmd);
4080 install_element (CONFIG_NODE, &no_hostname_cmd);
hassoe7168df2004-10-03 20:11:32 +00004081
hassoea8e9d92004-10-07 21:32:14 +00004082 if (terminal)
4083 {
hassoe7168df2004-10-03 20:11:32 +00004084 install_element (CONFIG_NODE, &password_cmd);
4085 install_element (CONFIG_NODE, &password_text_cmd);
4086 install_element (CONFIG_NODE, &enable_password_cmd);
4087 install_element (CONFIG_NODE, &enable_password_text_cmd);
4088 install_element (CONFIG_NODE, &no_enable_password_cmd);
4089
paul718e3742002-12-13 20:15:29 +00004090 install_element (CONFIG_NODE, &config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004091 install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
paul718e3742002-12-13 20:15:29 +00004092 install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
ajs274a4a42004-12-07 15:39:31 +00004093 install_element (CONFIG_NODE, &config_log_monitor_cmd);
4094 install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
4095 install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
paul718e3742002-12-13 20:15:29 +00004096 install_element (CONFIG_NODE, &config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004097 install_element (CONFIG_NODE, &config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004098 install_element (CONFIG_NODE, &no_config_log_file_cmd);
ajs274a4a42004-12-07 15:39:31 +00004099 install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
paul718e3742002-12-13 20:15:29 +00004100 install_element (CONFIG_NODE, &config_log_syslog_cmd);
ajs274a4a42004-12-07 15:39:31 +00004101 install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
paul12ab19f2003-07-26 06:14:55 +00004102 install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004103 install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
paul12ab19f2003-07-26 06:14:55 +00004104 install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
ajs274a4a42004-12-07 15:39:31 +00004105 install_element (CONFIG_NODE, &config_log_facility_cmd);
4106 install_element (CONFIG_NODE, &no_config_log_facility_cmd);
paul718e3742002-12-13 20:15:29 +00004107 install_element (CONFIG_NODE, &config_log_trap_cmd);
4108 install_element (CONFIG_NODE, &no_config_log_trap_cmd);
4109 install_element (CONFIG_NODE, &config_log_record_priority_cmd);
4110 install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
Andrew J. Schorr1ed72e02007-04-28 22:14:10 +00004111 install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
4112 install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
paul718e3742002-12-13 20:15:29 +00004113 install_element (CONFIG_NODE, &service_password_encrypt_cmd);
4114 install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
4115 install_element (CONFIG_NODE, &banner_motd_default_cmd);
paul3b0c5d92005-03-08 10:43:43 +00004116 install_element (CONFIG_NODE, &banner_motd_file_cmd);
paul718e3742002-12-13 20:15:29 +00004117 install_element (CONFIG_NODE, &no_banner_motd_cmd);
4118 install_element (CONFIG_NODE, &service_terminal_length_cmd);
4119 install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
paul718e3742002-12-13 20:15:29 +00004120
paul354d1192005-04-25 16:26:42 +00004121 install_element (VIEW_NODE, &show_thread_cpu_cmd);
4122 install_element (ENABLE_NODE, &show_thread_cpu_cmd);
Paul Jakma62687ff2008-08-23 14:27:06 +01004123 install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
Paul Jakmae276eb82010-01-09 16:15:00 +00004124
4125 install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
paul354d1192005-04-25 16:26:42 +00004126 install_element (VIEW_NODE, &show_work_queues_cmd);
4127 install_element (ENABLE_NODE, &show_work_queues_cmd);
paul9ab68122003-01-18 01:16:20 +00004128 }
paul718e3742002-12-13 20:15:29 +00004129 srand(time(NULL));
4130}
Chris Caputo228da422009-07-18 05:44:03 +00004131
Christian Frankecd40b322013-09-30 12:27:51 +00004132static void
4133cmd_terminate_token(struct cmd_token *token)
4134{
4135 unsigned int i, j;
4136 vector keyword_vect;
4137
4138 if (token->multiple)
4139 {
4140 for (i = 0; i < vector_active(token->multiple); i++)
4141 cmd_terminate_token(vector_slot(token->multiple, i));
4142 vector_free(token->multiple);
4143 token->multiple = NULL;
4144 }
4145
4146 if (token->keyword)
4147 {
4148 for (i = 0; i < vector_active(token->keyword); i++)
4149 {
4150 keyword_vect = vector_slot(token->keyword, i);
4151 for (j = 0; j < vector_active(keyword_vect); j++)
4152 cmd_terminate_token(vector_slot(keyword_vect, j));
4153 vector_free(keyword_vect);
4154 }
4155 vector_free(token->keyword);
4156 token->keyword = NULL;
4157 }
4158
4159 XFREE(MTYPE_CMD_TOKENS, token->cmd);
4160 XFREE(MTYPE_CMD_TOKENS, token->desc);
4161
4162 XFREE(MTYPE_CMD_TOKENS, token);
4163}
4164
4165static void
4166cmd_terminate_element(struct cmd_element *cmd)
4167{
4168 unsigned int i;
4169
4170 if (cmd->tokens == NULL)
4171 return;
4172
4173 for (i = 0; i < vector_active(cmd->tokens); i++)
4174 cmd_terminate_token(vector_slot(cmd->tokens, i));
4175
4176 vector_free(cmd->tokens);
4177 cmd->tokens = NULL;
4178}
4179
Chris Caputo228da422009-07-18 05:44:03 +00004180void
4181cmd_terminate ()
4182{
Christian Frankecd40b322013-09-30 12:27:51 +00004183 unsigned int i, j;
Chris Caputo228da422009-07-18 05:44:03 +00004184 struct cmd_node *cmd_node;
4185 struct cmd_element *cmd_element;
Christian Frankecd40b322013-09-30 12:27:51 +00004186 vector cmd_node_v;
Chris Caputo228da422009-07-18 05:44:03 +00004187
4188 if (cmdvec)
4189 {
4190 for (i = 0; i < vector_active (cmdvec); i++)
4191 if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
4192 {
4193 cmd_node_v = cmd_node->cmd_vector;
4194
4195 for (j = 0; j < vector_active (cmd_node_v); j++)
Christian Frankecd40b322013-09-30 12:27:51 +00004196 if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
4197 cmd_terminate_element(cmd_element);
Chris Caputo228da422009-07-18 05:44:03 +00004198
4199 vector_free (cmd_node_v);
4200 }
4201
4202 vector_free (cmdvec);
4203 cmdvec = NULL;
4204 }
4205
4206 if (command_cr)
Christian Frankecd40b322013-09-30 12:27:51 +00004207 XFREE(MTYPE_CMD_TOKENS, command_cr);
4208 if (token_cr.desc)
4209 XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
Chris Caputo228da422009-07-18 05:44:03 +00004210 if (host.name)
4211 XFREE (MTYPE_HOST, host.name);
4212 if (host.password)
4213 XFREE (MTYPE_HOST, host.password);
4214 if (host.password_encrypt)
4215 XFREE (MTYPE_HOST, host.password_encrypt);
4216 if (host.enable)
4217 XFREE (MTYPE_HOST, host.enable);
4218 if (host.enable_encrypt)
4219 XFREE (MTYPE_HOST, host.enable_encrypt);
4220 if (host.logfile)
4221 XFREE (MTYPE_HOST, host.logfile);
4222 if (host.motdfile)
4223 XFREE (MTYPE_HOST, host.motdfile);
4224 if (host.config)
4225 XFREE (MTYPE_HOST, host.config);
4226}