]> git.proxmox.com Git - mirror_frr.git/blame - lib/command.c
bgpd: Fix graceful restart capability minsize
[mirror_frr.git] / lib / command.c
CommitLineData
274a4a44 1/*
274a4a44 2 Command interpreter routine for virtual terminal [aka TeletYpe]
718e3744 3 Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
cd40b329
CF
4 Copyright (C) 2013 by Open Source Routing.
5 Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
718e3744 6
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
b21b19c5 26
718e3744 27#include "memory.h"
28#include "log.h"
5e4fa164 29#include <lib/version.h>
9ab6812d 30#include "thread.h"
b21b19c5 31#include "vector.h"
32#include "vty.h"
33#include "command.h"
354d119a 34#include "workqueue.h"
19dc275e 35#include "vrf.h"
718e3744 36
37/* Command vector which includes some level of command lists. Normally
38 each daemon maintains each own cmdvec. */
eb820afe 39vector cmdvec = NULL;
718e3744 40
cd40b329 41struct cmd_token token_cr;
228da428
CC
42char *command_cr = NULL;
43
cd40b329
CF
44enum filter_type
45{
46 FILTER_RELAXED,
47 FILTER_STRICT
48};
49
50enum matcher_rv
51{
52 MATCHER_OK,
53 MATCHER_COMPLETE,
54 MATCHER_INCOMPLETE,
55 MATCHER_NO_MATCH,
56 MATCHER_AMBIGUOUS,
57 MATCHER_EXCEED_ARGC_MAX
58};
59
60#define MATCHER_ERROR(matcher_rv) \
61 ( (matcher_rv) == MATCHER_INCOMPLETE \
62 || (matcher_rv) == MATCHER_NO_MATCH \
63 || (matcher_rv) == MATCHER_AMBIGUOUS \
64 || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \
65 )
66
718e3744 67/* Host information structure. */
68struct host host;
69
718e3744 70/* Standard command node structures. */
7fc626de 71static struct cmd_node auth_node =
718e3744 72{
73 AUTH_NODE,
74 "Password: ",
75};
76
7fc626de 77static struct cmd_node view_node =
718e3744 78{
79 VIEW_NODE,
80 "%s> ",
81};
82
7fc626de 83static struct cmd_node restricted_node =
62687ff1
PJ
84{
85 RESTRICTED_NODE,
86 "%s$ ",
87};
88
7fc626de 89static struct cmd_node auth_enable_node =
718e3744 90{
91 AUTH_ENABLE_NODE,
92 "Password: ",
93};
94
7fc626de 95static struct cmd_node enable_node =
718e3744 96{
97 ENABLE_NODE,
98 "%s# ",
99};
100
7fc626de 101static struct cmd_node config_node =
718e3744 102{
103 CONFIG_NODE,
104 "%s(config)# ",
105 1
106};
6590f2c3 107
108/* Default motd string. */
2d362d10 109static const char *default_motd =
6590f2c3 110"\r\n\
111Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
112" QUAGGA_COPYRIGHT "\r\n\
0be793e6 113" GIT_INFO "\r\n";
6590f2c3 114
274a4a44 115
2d362d10 116static const struct facility_map {
274a4a44 117 int facility;
118 const char *name;
119 size_t match;
120} syslog_facilities[] =
121 {
122 { LOG_KERN, "kern", 1 },
123 { LOG_USER, "user", 2 },
124 { LOG_MAIL, "mail", 1 },
125 { LOG_DAEMON, "daemon", 1 },
126 { LOG_AUTH, "auth", 1 },
127 { LOG_SYSLOG, "syslog", 1 },
128 { LOG_LPR, "lpr", 2 },
129 { LOG_NEWS, "news", 1 },
130 { LOG_UUCP, "uucp", 2 },
131 { LOG_CRON, "cron", 1 },
132#ifdef LOG_FTP
133 { LOG_FTP, "ftp", 1 },
134#endif
135 { LOG_LOCAL0, "local0", 6 },
136 { LOG_LOCAL1, "local1", 6 },
137 { LOG_LOCAL2, "local2", 6 },
138 { LOG_LOCAL3, "local3", 6 },
139 { LOG_LOCAL4, "local4", 6 },
140 { LOG_LOCAL5, "local5", 6 },
141 { LOG_LOCAL6, "local6", 6 },
142 { LOG_LOCAL7, "local7", 6 },
143 { 0, NULL, 0 },
144 };
145
146static const char *
147facility_name(int facility)
148{
2d362d10 149 const struct facility_map *fm;
274a4a44 150
151 for (fm = syslog_facilities; fm->name; fm++)
152 if (fm->facility == facility)
153 return fm->name;
154 return "";
155}
156
157static int
158facility_match(const char *str)
159{
2d362d10 160 const struct facility_map *fm;
274a4a44 161
162 for (fm = syslog_facilities; fm->name; fm++)
163 if (!strncmp(str,fm->name,fm->match))
164 return fm->facility;
165 return -1;
166}
167
168static int
169level_match(const char *s)
170{
171 int level ;
172
173 for ( level = 0 ; zlog_priority [level] != NULL ; level ++ )
174 if (!strncmp (s, zlog_priority[level], 2))
175 return level;
176 return ZLOG_DISABLED;
177}
178
cb585b65 179/* This is called from main when a daemon is invoked with -v or --version. */
6590f2c3 180void
181print_version (const char *progname)
182{
cb585b65 183 printf ("%s version %s\n", progname, QUAGGA_VERSION);
184 printf ("%s\n", QUAGGA_COPYRIGHT);
80db5ac1 185 printf ("configured with:\n\t%s\n", QUAGGA_CONFIG_ARGS);
6590f2c3 186}
187
6b0655a2 188
718e3744 189/* Utility function to concatenate argv argument into a single string
190 with inserting ' ' character between each argument. */
191char *
42d49865 192argv_concat (const char **argv, int argc, int shift)
718e3744 193{
194 int i;
f6834d4c 195 size_t len;
718e3744 196 char *str;
f6834d4c 197 char *p;
718e3744 198
f6834d4c 199 len = 0;
200 for (i = shift; i < argc; i++)
201 len += strlen(argv[i])+1;
202 if (!len)
203 return NULL;
204 p = str = XMALLOC(MTYPE_TMP, len);
718e3744 205 for (i = shift; i < argc; i++)
206 {
f6834d4c 207 size_t arglen;
208 memcpy(p, argv[i], (arglen = strlen(argv[i])));
209 p += arglen;
210 *p++ = ' ';
718e3744 211 }
f6834d4c 212 *(p-1) = '\0';
718e3744 213 return str;
214}
215
216/* Install top node of command vector. */
217void
218install_node (struct cmd_node *node,
219 int (*func) (struct vty *))
220{
221 vector_set_index (cmdvec, node->node, node);
222 node->func = func;
223 node->cmd_vector = vector_init (VECTOR_MIN_SIZE);
224}
225
718e3744 226/* Breaking up string into each command piece. I assume given
227 character is separated by a space character. Return value is a
228 vector which includes char ** data element. */
229vector
ea8e9d97 230cmd_make_strvec (const char *string)
718e3744 231{
ea8e9d97 232 const char *cp, *start;
233 char *token;
718e3744 234 int strlen;
235 vector strvec;
236
237 if (string == NULL)
238 return NULL;
239
240 cp = string;
241
242 /* Skip white spaces. */
243 while (isspace ((int) *cp) && *cp != '\0')
244 cp++;
245
246 /* Return if there is only white spaces */
247 if (*cp == '\0')
248 return NULL;
249
250 if (*cp == '!' || *cp == '#')
251 return NULL;
252
253 /* Prepare return vector. */
254 strvec = vector_init (VECTOR_MIN_SIZE);
255
256 /* Copy each command piece and set into vector. */
257 while (1)
258 {
259 start = cp;
260 while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
261 *cp != '\0')
262 cp++;
263 strlen = cp - start;
264 token = XMALLOC (MTYPE_STRVEC, strlen + 1);
265 memcpy (token, start, strlen);
266 *(token + strlen) = '\0';
267 vector_set (strvec, token);
268
269 while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
270 *cp != '\0')
271 cp++;
272
273 if (*cp == '\0')
274 return strvec;
275 }
276}
277
278/* Free allocated string vector. */
279void
280cmd_free_strvec (vector v)
281{
8c328f11 282 unsigned int i;
718e3744 283 char *cp;
284
285 if (!v)
286 return;
287
55468c86 288 for (i = 0; i < vector_active (v); i++)
718e3744 289 if ((cp = vector_slot (v, i)) != NULL)
290 XFREE (MTYPE_STRVEC, cp);
291
292 vector_free (v);
293}
294
cd40b329
CF
295struct format_parser_state
296{
297 vector topvect; /* Top level vector */
298 vector intvect; /* Intermediate level vector, used when there's
299 * a multiple in a keyword. */
300 vector curvect; /* current vector where read tokens should be
301 appended. */
302
303 const char *string; /* pointer to command string, not modified */
304 const char *cp; /* pointer in command string, moved along while
305 parsing */
306 const char *dp; /* pointer in description string, moved along while
307 parsing */
308
309 int in_keyword; /* flag to remember if we are in a keyword group */
310 int in_multiple; /* flag to remember if we are in a multiple group */
311 int just_read_word; /* flag to remember if the last thing we red was a
312 * real word and not some abstract token */
313};
314
315static void
316format_parser_error(struct format_parser_state *state, const char *message)
317{
318 int offset = state->cp - state->string + 1;
319
320 fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string);
321 fprintf(stderr, " %*c\n", offset, '^');
322 fprintf(stderr, "%s at offset %d.\n", message, offset);
323 fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n");
324 exit(1);
325}
326
274a4a44 327static char *
cd40b329 328format_parser_desc_str(struct format_parser_state *state)
718e3744 329{
6ad96ea1 330 const char *cp, *start;
331 char *token;
718e3744 332 int strlen;
cd40b329
CF
333
334 cp = state->dp;
718e3744 335
336 if (cp == NULL)
337 return NULL;
338
339 /* Skip white spaces. */
340 while (isspace ((int) *cp) && *cp != '\0')
341 cp++;
342
343 /* Return if there is only white spaces */
344 if (*cp == '\0')
345 return NULL;
346
347 start = cp;
348
349 while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
350 cp++;
351
352 strlen = cp - start;
cd40b329 353 token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1);
718e3744 354 memcpy (token, start, strlen);
355 *(token + strlen) = '\0';
356
cd40b329 357 state->dp = cp;
718e3744 358
359 return token;
360}
361
cd40b329
CF
362static void
363format_parser_begin_keyword(struct format_parser_state *state)
718e3744 364{
cd40b329
CF
365 struct cmd_token *token;
366 vector keyword_vect;
718e3744 367
cd40b329
CF
368 if (state->in_keyword
369 || state->in_multiple)
370 format_parser_error(state, "Unexpected '{'");
718e3744 371
cd40b329
CF
372 state->cp++;
373 state->in_keyword = 1;
718e3744 374
cd40b329
CF
375 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
376 token->type = TOKEN_KEYWORD;
377 token->keyword = vector_init(VECTOR_MIN_SIZE);
718e3744 378
cd40b329
CF
379 keyword_vect = vector_init(VECTOR_MIN_SIZE);
380 vector_set(token->keyword, keyword_vect);
718e3744 381
cd40b329
CF
382 vector_set(state->curvect, token);
383 state->curvect = keyword_vect;
384}
718e3744 385
cd40b329
CF
386static void
387format_parser_begin_multiple(struct format_parser_state *state)
388{
389 struct cmd_token *token;
718e3744 390
cd40b329
CF
391 if (state->in_keyword == 1)
392 format_parser_error(state, "Keyword starting with '('");
718e3744 393
cd40b329
CF
394 if (state->in_multiple)
395 format_parser_error(state, "Nested group");
718e3744 396
cd40b329
CF
397 state->cp++;
398 state->in_multiple = 1;
399 state->just_read_word = 0;
400
401 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
402 token->type = TOKEN_MULTIPLE;
403 token->multiple = vector_init(VECTOR_MIN_SIZE);
718e3744 404
cd40b329
CF
405 vector_set(state->curvect, token);
406 if (state->curvect != state->topvect)
407 state->intvect = state->curvect;
408 state->curvect = token->multiple;
409}
718e3744 410
cd40b329
CF
411static void
412format_parser_end_keyword(struct format_parser_state *state)
413{
414 if (state->in_multiple
415 || !state->in_keyword)
416 format_parser_error(state, "Unexpected '}'");
718e3744 417
cd40b329
CF
418 if (state->in_keyword == 1)
419 format_parser_error(state, "Empty keyword group");
718e3744 420
cd40b329
CF
421 state->cp++;
422 state->in_keyword = 0;
423 state->curvect = state->topvect;
424}
425
426static void
427format_parser_end_multiple(struct format_parser_state *state)
428{
429 char *dummy;
430
431 if (!state->in_multiple)
432 format_parser_error(state, "Unepexted ')'");
433
434 if (vector_active(state->curvect) == 0)
435 format_parser_error(state, "Empty multiple section");
436
437 if (!state->just_read_word)
438 {
439 /* There are constructions like
440 * 'show ip ospf database ... (self-originate|)'
441 * in use.
442 * The old parser reads a description string for the
443 * word '' between |) which will never match.
444 * Simulate this behvaior by dropping the next desc
445 * string in such a case. */
446
447 dummy = format_parser_desc_str(state);
448 XFREE(MTYPE_CMD_TOKENS, dummy);
718e3744 449 }
cd40b329
CF
450
451 state->cp++;
452 state->in_multiple = 0;
453
454 if (state->intvect)
455 state->curvect = state->intvect;
456 else
457 state->curvect = state->topvect;
718e3744 458}
459
cd40b329
CF
460static void
461format_parser_handle_pipe(struct format_parser_state *state)
718e3744 462{
cd40b329
CF
463 struct cmd_token *keyword_token;
464 vector keyword_vect;
718e3744 465
cd40b329 466 if (state->in_multiple)
718e3744 467 {
cd40b329
CF
468 state->just_read_word = 0;
469 state->cp++;
470 }
471 else if (state->in_keyword)
472 {
473 state->in_keyword = 1;
474 state->cp++;
475
476 keyword_token = vector_slot(state->topvect,
477 vector_active(state->topvect) - 1);
478 keyword_vect = vector_init(VECTOR_MIN_SIZE);
479 vector_set(keyword_token->keyword, keyword_vect);
480 state->curvect = keyword_vect;
481 }
482 else
483 {
484 format_parser_error(state, "Unexpected '|'");
485 }
486}
487
488static void
489format_parser_read_word(struct format_parser_state *state)
490{
491 const char *start;
492 int len;
493 char *cmd;
494 struct cmd_token *token;
495
496 start = state->cp;
497
498 while (state->cp[0] != '\0'
499 && !strchr("\r\n(){}|", state->cp[0])
500 && !isspace((int)state->cp[0]))
501 state->cp++;
502
503 len = state->cp - start;
504 cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1);
505 memcpy(cmd, start, len);
506 cmd[len] = '\0';
507
508 token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token));
509 token->type = TOKEN_TERMINAL;
510 token->cmd = cmd;
511 token->desc = format_parser_desc_str(state);
512 vector_set(state->curvect, token);
513
514 if (state->in_keyword == 1)
515 state->in_keyword = 2;
516
517 state->just_read_word = 1;
518}
519
520/**
521 * Parse a given command format string and build a tree of tokens from
522 * it that is suitable to be used by the command subsystem.
523 *
524 * @param string Command format string.
525 * @param descstr Description string.
526 * @return A vector of struct cmd_token representing the given command,
527 * or NULL on error.
528 */
529static vector
530cmd_parse_format(const char *string, const char *descstr)
531{
532 struct format_parser_state state;
533
534 if (string == NULL)
535 return NULL;
536
537 memset(&state, 0, sizeof(state));
538 state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE);
539 state.cp = state.string = string;
540 state.dp = descstr;
541
542 while (1)
543 {
544 while (isspace((int)state.cp[0]) && state.cp[0] != '\0')
545 state.cp++;
546
547 switch (state.cp[0])
548 {
549 case '\0':
550 if (state.in_keyword
551 || state.in_multiple)
552 format_parser_error(&state, "Unclosed group/keyword");
553 return state.topvect;
554 case '{':
555 format_parser_begin_keyword(&state);
556 break;
557 case '(':
558 format_parser_begin_multiple(&state);
559 break;
560 case '}':
561 format_parser_end_keyword(&state);
562 break;
563 case ')':
564 format_parser_end_multiple(&state);
565 break;
566 case '|':
567 format_parser_handle_pipe(&state);
568 break;
569 default:
570 format_parser_read_word(&state);
571 }
718e3744 572 }
718e3744 573}
574
575/* Return prompt character of specified node. */
8c328f11 576const char *
718e3744 577cmd_prompt (enum node_type node)
578{
579 struct cmd_node *cnode;
580
581 cnode = vector_slot (cmdvec, node);
582 return cnode->prompt;
583}
584
585/* Install a command into a node. */
586void
587install_element (enum node_type ntype, struct cmd_element *cmd)
588{
589 struct cmd_node *cnode;
eb820afe 590
591 /* cmd_init hasn't been called */
592 if (!cmdvec)
593 return;
594
718e3744 595 cnode = vector_slot (cmdvec, ntype);
596
597 if (cnode == NULL)
598 {
599 fprintf (stderr, "Command node %d doesn't exist, please check it\n",
600 ntype);
601 exit (1);
602 }
603
604 vector_set (cnode->cmd_vector, cmd);
cd40b329
CF
605 if (cmd->tokens == NULL)
606 cmd->tokens = cmd_parse_format(cmd->string, cmd->doc);
718e3744 607}
608
2d362d10 609static const unsigned char itoa64[] =
718e3744 610"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
611
274a4a44 612static void
718e3744 613to64(char *s, long v, int n)
614{
615 while (--n >= 0)
616 {
617 *s++ = itoa64[v&0x3f];
618 v >>= 6;
619 }
620}
621
274a4a44 622static char *
623zencrypt (const char *passwd)
718e3744 624{
625 char salt[6];
626 struct timeval tv;
627 char *crypt (const char *, const char *);
628
629 gettimeofday(&tv,0);
630
631 to64(&salt[0], random(), 3);
632 to64(&salt[3], tv.tv_usec, 3);
633 salt[5] = '\0';
634
635 return crypt (passwd, salt);
636}
637
638/* This function write configuration of this host. */
274a4a44 639static int
718e3744 640config_write_host (struct vty *vty)
641{
642 if (host.name)
643 vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
644
645 if (host.encrypt)
646 {
647 if (host.password_encrypt)
648 vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
649 if (host.enable_encrypt)
650 vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
651 }
652 else
653 {
654 if (host.password)
655 vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
656 if (host.enable)
657 vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
658 }
659
274a4a44 660 if (zlog_default->default_lvl != LOG_DEBUG)
82146b88 661 {
662 vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
663 VTY_NEWLINE);
664 vty_out (vty, "log trap %s%s",
665 zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
666 }
274a4a44 667
668 if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
669 {
670 vty_out (vty, "log file %s", host.logfile);
671 if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
672 vty_out (vty, " %s",
673 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
674 vty_out (vty, "%s", VTY_NEWLINE);
675 }
676
677 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
678 {
679 vty_out (vty, "log stdout");
680 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
681 vty_out (vty, " %s",
682 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
683 vty_out (vty, "%s", VTY_NEWLINE);
684 }
718e3744 685
274a4a44 686 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
687 vty_out(vty,"no log monitor%s",VTY_NEWLINE);
688 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
689 vty_out(vty,"log monitor %s%s",
690 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
718e3744 691
274a4a44 692 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
12ab19f1 693 {
694 vty_out (vty, "log syslog");
274a4a44 695 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
696 vty_out (vty, " %s",
697 zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
12ab19f1 698 vty_out (vty, "%s", VTY_NEWLINE);
699 }
274a4a44 700
701 if (zlog_default->facility != LOG_DAEMON)
702 vty_out (vty, "log facility %s%s",
703 facility_name(zlog_default->facility), VTY_NEWLINE);
718e3744 704
705 if (zlog_default->record_priority == 1)
706 vty_out (vty, "log record-priority%s", VTY_NEWLINE);
707
1ed72e0b
AS
708 if (zlog_default->timestamp_precision > 0)
709 vty_out (vty, "log timestamp precision %d%s",
710 zlog_default->timestamp_precision, VTY_NEWLINE);
711
718e3744 712 if (host.advanced)
713 vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
714
715 if (host.encrypt)
716 vty_out (vty, "service password-encryption%s", VTY_NEWLINE);
717
718 if (host.lines >= 0)
719 vty_out (vty, "service terminal-length %d%s", host.lines,
720 VTY_NEWLINE);
721
3b0c5d9a 722 if (host.motdfile)
723 vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
724 else if (! host.motd)
718e3744 725 vty_out (vty, "no banner motd%s", VTY_NEWLINE);
726
727 return 1;
728}
729
730/* Utility function for getting command vector. */
274a4a44 731static vector
718e3744 732cmd_node_vector (vector v, enum node_type ntype)
733{
734 struct cmd_node *cnode = vector_slot (v, ntype);
735 return cnode->cmd_vector;
736}
737
274a4a44 738#if 0
739/* Filter command vector by symbol. This function is not actually used;
740 * should it be deleted? */
741static int
718e3744 742cmd_filter_by_symbol (char *command, char *symbol)
743{
744 int i, lim;
745
746 if (strcmp (symbol, "IPV4_ADDRESS") == 0)
747 {
748 i = 0;
749 lim = strlen (command);
750 while (i < lim)
751 {
752 if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/'))
753 return 1;
754 i++;
755 }
756 return 0;
757 }
758 if (strcmp (symbol, "STRING") == 0)
759 {
760 i = 0;
761 lim = strlen (command);
762 while (i < lim)
763 {
764 if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-'))
765 return 1;
766 i++;
767 }
768 return 0;
769 }
770 if (strcmp (symbol, "IFNAME") == 0)
771 {
772 i = 0;
773 lim = strlen (command);
774 while (i < lim)
775 {
776 if (! isalnum ((int) command[i]))
777 return 1;
778 i++;
779 }
780 return 0;
781 }
782 return 0;
783}
274a4a44 784#endif
718e3744 785
786/* Completion match types. */
787enum match_type
788{
789 no_match,
790 extend_match,
791 ipv4_prefix_match,
792 ipv4_match,
793 ipv6_prefix_match,
794 ipv6_match,
795 range_match,
796 vararg_match,
797 partly_match,
798 exact_match
799};
800
274a4a44 801static enum match_type
8c328f11 802cmd_ipv4_match (const char *str)
718e3744 803{
8c328f11 804 const char *sp;
718e3744 805 int dots = 0, nums = 0;
806 char buf[4];
807
808 if (str == NULL)
809 return partly_match;
810
811 for (;;)
812 {
813 memset (buf, 0, sizeof (buf));
814 sp = str;
815 while (*str != '\0')
816 {
817 if (*str == '.')
818 {
819 if (dots >= 3)
820 return no_match;
821
822 if (*(str + 1) == '.')
823 return no_match;
824
825 if (*(str + 1) == '\0')
826 return partly_match;
827
828 dots++;
829 break;
830 }
831 if (!isdigit ((int) *str))
832 return no_match;
833
834 str++;
835 }
836
837 if (str - sp > 3)
838 return no_match;
839
840 strncpy (buf, sp, str - sp);
841 if (atoi (buf) > 255)
842 return no_match;
843
844 nums++;
845
846 if (*str == '\0')
847 break;
848
849 str++;
850 }
851
852 if (nums < 4)
853 return partly_match;
854
855 return exact_match;
856}
857
274a4a44 858static enum match_type
8c328f11 859cmd_ipv4_prefix_match (const char *str)
718e3744 860{
8c328f11 861 const char *sp;
718e3744 862 int dots = 0;
863 char buf[4];
864
865 if (str == NULL)
866 return partly_match;
867
868 for (;;)
869 {
870 memset (buf, 0, sizeof (buf));
871 sp = str;
872 while (*str != '\0' && *str != '/')
873 {
874 if (*str == '.')
875 {
876 if (dots == 3)
877 return no_match;
878
879 if (*(str + 1) == '.' || *(str + 1) == '/')
880 return no_match;
881
882 if (*(str + 1) == '\0')
883 return partly_match;
884
885 dots++;
886 break;
887 }
888
889 if (!isdigit ((int) *str))
890 return no_match;
891
892 str++;
893 }
894
895 if (str - sp > 3)
896 return no_match;
897
898 strncpy (buf, sp, str - sp);
899 if (atoi (buf) > 255)
900 return no_match;
901
902 if (dots == 3)
903 {
904 if (*str == '/')
905 {
906 if (*(str + 1) == '\0')
907 return partly_match;
908
909 str++;
910 break;
911 }
912 else if (*str == '\0')
913 return partly_match;
914 }
915
916 if (*str == '\0')
917 return partly_match;
918
919 str++;
920 }
921
922 sp = str;
923 while (*str != '\0')
924 {
925 if (!isdigit ((int) *str))
926 return no_match;
927
928 str++;
929 }
930
931 if (atoi (sp) > 32)
932 return no_match;
933
934 return exact_match;
935}
936
937#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
938#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
939#define STATE_START 1
940#define STATE_COLON 2
941#define STATE_DOUBLE 3
942#define STATE_ADDR 4
943#define STATE_DOT 5
944#define STATE_SLASH 6
945#define STATE_MASK 7
946
22e0a9e6 947#ifdef HAVE_IPV6
948
274a4a44 949static enum match_type
8c328f11 950cmd_ipv6_match (const char *str)
718e3744 951{
726f9b2b 952 struct sockaddr_in6 sin6_dummy;
953 int ret;
718e3744 954
955 if (str == NULL)
956 return partly_match;
957
958 if (strspn (str, IPV6_ADDR_STR) != strlen (str))
959 return no_match;
960
726f9b2b 961 /* use inet_pton that has a better support,
962 * for example inet_pton can support the automatic addresses:
963 * ::1.2.3.4
964 */
965 ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
966
967 if (ret == 1)
968 return exact_match;
969
7c9c6aeb 970 return no_match;
718e3744 971}
972
274a4a44 973static enum match_type
8c328f11 974cmd_ipv6_prefix_match (const char *str)
718e3744 975{
976 int state = STATE_START;
977 int colons = 0, nums = 0, double_colon = 0;
978 int mask;
8c328f11 979 const char *sp = NULL;
718e3744 980 char *endptr = NULL;
981
982 if (str == NULL)
983 return partly_match;
984
985 if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
986 return no_match;
987
988 while (*str != '\0' && state != STATE_MASK)
989 {
990 switch (state)
991 {
992 case STATE_START:
993 if (*str == ':')
994 {
995 if (*(str + 1) != ':' && *(str + 1) != '\0')
996 return no_match;
997 colons--;
998 state = STATE_COLON;
999 }
1000 else
1001 {
1002 sp = str;
1003 state = STATE_ADDR;
1004 }
1005
1006 continue;
1007 case STATE_COLON:
1008 colons++;
1009 if (*(str + 1) == '/')
1010 return no_match;
1011 else if (*(str + 1) == ':')
1012 state = STATE_DOUBLE;
1013 else
1014 {
1015 sp = str + 1;
1016 state = STATE_ADDR;
1017 }
1018 break;
1019 case STATE_DOUBLE:
1020 if (double_colon)
1021 return no_match;
1022
1023 if (*(str + 1) == ':')
1024 return no_match;
1025 else
1026 {
1027 if (*(str + 1) != '\0' && *(str + 1) != '/')
1028 colons++;
1029 sp = str + 1;
1030
1031 if (*(str + 1) == '/')
1032 state = STATE_SLASH;
1033 else
1034 state = STATE_ADDR;
1035 }
1036
1037 double_colon++;
1038 nums += 1;
1039 break;
1040 case STATE_ADDR:
1041 if (*(str + 1) == ':' || *(str + 1) == '.'
1042 || *(str + 1) == '\0' || *(str + 1) == '/')
1043 {
1044 if (str - sp > 3)
1045 return no_match;
1046
1047 for (; sp <= str; sp++)
1048 if (*sp == '/')
1049 return no_match;
1050
1051 nums++;
1052
1053 if (*(str + 1) == ':')
1054 state = STATE_COLON;
1055 else if (*(str + 1) == '.')
aa5cf24b
DL
1056 {
1057 if (colons || double_colon)
1058 state = STATE_DOT;
1059 else
1060 return no_match;
1061 }
718e3744 1062 else if (*(str + 1) == '/')
1063 state = STATE_SLASH;
1064 }
1065 break;
1066 case STATE_DOT:
1067 state = STATE_ADDR;
1068 break;
1069 case STATE_SLASH:
1070 if (*(str + 1) == '\0')
1071 return partly_match;
1072
1073 state = STATE_MASK;
1074 break;
1075 default:
1076 break;
1077 }
1078
1079 if (nums > 11)
1080 return no_match;
1081
1082 if (colons > 7)
1083 return no_match;
1084
1085 str++;
1086 }
1087
1088 if (state < STATE_MASK)
1089 return partly_match;
1090
1091 mask = strtol (str, &endptr, 10);
1092 if (*endptr != '\0')
1093 return no_match;
1094
1095 if (mask < 0 || mask > 128)
1096 return no_match;
1097
1098/* I don't know why mask < 13 makes command match partly.
1099 Forgive me to make this comments. I Want to set static default route
1100 because of lack of function to originate default in ospf6d; sorry
1101 yasu
1102 if (mask < 13)
1103 return partly_match;
1104*/
1105
1106 return exact_match;
1107}
1108
22e0a9e6 1109#endif /* HAVE_IPV6 */
1110
f702c39f 1111#define DECIMAL_STRLEN_MAX 20
718e3744 1112
274a4a44 1113static int
8c328f11 1114cmd_range_match (const char *range, const char *str)
718e3744 1115{
1116 char *p;
1117 char buf[DECIMAL_STRLEN_MAX + 1];
1118 char *endptr = NULL;
f702c39f 1119 signed long long min, max, val;
718e3744 1120
1121 if (str == NULL)
1122 return 1;
1123
f702c39f 1124 val = strtoll (str, &endptr, 10);
718e3744 1125 if (*endptr != '\0')
1126 return 0;
f702c39f 1127 val = llabs(val);
718e3744 1128
1129 range++;
1130 p = strchr (range, '-');
1131 if (p == NULL)
1132 return 0;
1133 if (p - range > DECIMAL_STRLEN_MAX)
1134 return 0;
1135 strncpy (buf, range, p - range);
1136 buf[p - range] = '\0';
f702c39f 1137 min = strtoll (buf, &endptr, 10);
718e3744 1138 if (*endptr != '\0')
1139 return 0;
1140
1141 range = p + 1;
1142 p = strchr (range, '>');
1143 if (p == NULL)
1144 return 0;
1145 if (p - range > DECIMAL_STRLEN_MAX)
1146 return 0;
1147 strncpy (buf, range, p - range);
1148 buf[p - range] = '\0';
f702c39f 1149 max = strtoll (buf, &endptr, 10);
718e3744 1150 if (*endptr != '\0')
1151 return 0;
1152
1153 if (val < min || val > max)
1154 return 0;
1155
1156 return 1;
1157}
1158
274a4a44 1159static enum match_type
cd40b329
CF
1160cmd_word_match(struct cmd_token *token,
1161 enum filter_type filter,
1162 const char *word)
718e3744 1163{
8c328f11 1164 const char *str;
718e3744 1165 enum match_type match_type;
909a2155 1166
cd40b329 1167 str = token->cmd;
718e3744 1168
cd40b329
CF
1169 if (filter == FILTER_RELAXED)
1170 if (!word || !strlen(word))
1171 return partly_match;
718e3744 1172
cd40b329
CF
1173 if (!word)
1174 return no_match;
909a2155 1175
cd40b329
CF
1176 if (CMD_VARARG(str))
1177 {
1178 return vararg_match;
1179 }
1180 else if (CMD_RANGE(str))
1181 {
1182 if (cmd_range_match(str, word))
1183 return range_match;
1184 }
22e0a9e6 1185#ifdef HAVE_IPV6
cd40b329
CF
1186 else if (CMD_IPV6(str))
1187 {
1188 match_type = cmd_ipv6_match(word);
1189 if ((filter == FILTER_RELAXED && match_type != no_match)
1190 || (filter == FILTER_STRICT && match_type == exact_match))
1191 return ipv6_match;
1192 }
1193 else if (CMD_IPV6_PREFIX(str))
1194 {
1195 match_type = cmd_ipv6_prefix_match(word);
1196 if ((filter == FILTER_RELAXED && match_type != no_match)
1197 || (filter == FILTER_STRICT && match_type == exact_match))
1198 return ipv6_prefix_match;
1199 }
1200#endif /* HAVE_IPV6 */
1201 else if (CMD_IPV4(str))
1202 {
1203 match_type = cmd_ipv4_match(word);
1204 if ((filter == FILTER_RELAXED && match_type != no_match)
1205 || (filter == FILTER_STRICT && match_type == exact_match))
1206 return ipv4_match;
1207 }
1208 else if (CMD_IPV4_PREFIX(str))
1209 {
1210 match_type = cmd_ipv4_prefix_match(word);
1211 if ((filter == FILTER_RELAXED && match_type != no_match)
1212 || (filter == FILTER_STRICT && match_type == exact_match))
1213 return ipv4_prefix_match;
1214 }
1215 else if (CMD_OPTION(str) || CMD_VARIABLE(str))
1216 {
1217 return extend_match;
1218 }
1219 else
1220 {
1221 if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word)))
1222 {
1223 if (!strcmp(str, word))
1224 return exact_match;
1225 return partly_match;
1226 }
1227 if (filter == FILTER_STRICT && !strcmp(str, word))
1228 return exact_match;
1229 }
909a2155 1230
cd40b329
CF
1231 return no_match;
1232}
909a2155 1233
cd40b329
CF
1234struct cmd_matcher
1235{
1236 struct cmd_element *cmd; /* The command element the matcher is using */
1237 enum filter_type filter; /* Whether to use strict or relaxed matching */
1238 vector vline; /* The tokenized commandline which is to be matched */
1239 unsigned int index; /* The index up to which matching should be done */
909a2155 1240
cd40b329
CF
1241 /* If set, construct a list of matches at the position given by index */
1242 enum match_type *match_type;
1243 vector *match;
1244
1245 unsigned int word_index; /* iterating over vline */
1246};
1247
1248static int
1249push_argument(int *argc, const char **argv, const char *arg)
1250{
1251 if (!arg || !strlen(arg))
1252 arg = NULL;
1253
1254 if (!argc || !argv)
1255 return 0;
1256
1257 if (*argc >= CMD_ARGC_MAX)
1258 return -1;
1259
1260 argv[(*argc)++] = arg;
1261 return 0;
1262}
1263
1264static void
1265cmd_matcher_record_match(struct cmd_matcher *matcher,
1266 enum match_type match_type,
1267 struct cmd_token *token)
1268{
1269 if (matcher->word_index != matcher->index)
1270 return;
1271
1272 if (matcher->match)
1273 {
1274 if (!*matcher->match)
1275 *matcher->match = vector_init(VECTOR_MIN_SIZE);
1276 vector_set(*matcher->match, token);
1277 }
1278
1279 if (matcher->match_type)
1280 {
1281 if (match_type > *matcher->match_type)
1282 *matcher->match_type = match_type;
1283 }
1284}
1285
1286static int
1287cmd_matcher_words_left(struct cmd_matcher *matcher)
1288{
1289 return matcher->word_index < vector_active(matcher->vline);
1290}
1291
1292static const char*
1293cmd_matcher_get_word(struct cmd_matcher *matcher)
1294{
1295 assert(cmd_matcher_words_left(matcher));
1296
1297 return vector_slot(matcher->vline, matcher->word_index);
1298}
1299
1300static enum matcher_rv
1301cmd_matcher_match_terminal(struct cmd_matcher *matcher,
1302 struct cmd_token *token,
1303 int *argc, const char **argv)
1304{
1305 const char *word;
1306 enum match_type word_match;
1307
1308 assert(token->type == TOKEN_TERMINAL);
1309
1310 if (!cmd_matcher_words_left(matcher))
1311 {
1312 if (CMD_OPTION(token->cmd))
1313 return MATCHER_OK; /* missing optional args are NOT pushed as NULL */
1314 else
1315 return MATCHER_INCOMPLETE;
1316 }
1317
1318 word = cmd_matcher_get_word(matcher);
1319 word_match = cmd_word_match(token, matcher->filter, word);
1320 if (word_match == no_match)
1321 return MATCHER_NO_MATCH;
1322
1323 /* We have to record the input word as argument if it matched
1324 * against a variable. */
1325 if (CMD_VARARG(token->cmd)
1326 || CMD_VARIABLE(token->cmd)
1327 || CMD_OPTION(token->cmd))
1328 {
1329 if (push_argument(argc, argv, word))
1330 return MATCHER_EXCEED_ARGC_MAX;
1331 }
1332
1333 cmd_matcher_record_match(matcher, word_match, token);
1334
1335 matcher->word_index++;
1336
1337 /* A vararg token should consume all left over words as arguments */
1338 if (CMD_VARARG(token->cmd))
1339 while (cmd_matcher_words_left(matcher))
1340 {
1341 word = cmd_matcher_get_word(matcher);
1342 if (word && strlen(word))
1343 push_argument(argc, argv, word);
1344 matcher->word_index++;
718e3744 1345 }
cd40b329
CF
1346
1347 return MATCHER_OK;
718e3744 1348}
1349
cd40b329
CF
1350static enum matcher_rv
1351cmd_matcher_match_multiple(struct cmd_matcher *matcher,
1352 struct cmd_token *token,
1353 int *argc, const char **argv)
1354{
1355 enum match_type multiple_match;
1356 unsigned int multiple_index;
1357 const char *word;
1358 const char *arg;
1359 struct cmd_token *word_token;
1360 enum match_type word_match;
1361
1362 assert(token->type == TOKEN_MULTIPLE);
1363
1364 multiple_match = no_match;
1365
1366 if (!cmd_matcher_words_left(matcher))
1367 return MATCHER_INCOMPLETE;
1368
1369 word = cmd_matcher_get_word(matcher);
1370 for (multiple_index = 0;
1371 multiple_index < vector_active(token->multiple);
1372 multiple_index++)
1373 {
1374 word_token = vector_slot(token->multiple, multiple_index);
1375
1376 word_match = cmd_word_match(word_token, matcher->filter, word);
1377 if (word_match == no_match)
1378 continue;
1379
1380 cmd_matcher_record_match(matcher, word_match, word_token);
1381
1382 if (word_match > multiple_match)
1383 {
1384 multiple_match = word_match;
1385 arg = word;
1386 }
1387 /* To mimic the behavior of the old command implementation, we
1388 * tolerate any ambiguities here :/ */
1389 }
1390
1391 matcher->word_index++;
1392
1393 if (multiple_match == no_match)
1394 return MATCHER_NO_MATCH;
1395
1396 if (push_argument(argc, argv, arg))
1397 return MATCHER_EXCEED_ARGC_MAX;
1398
1399 return MATCHER_OK;
1400}
1401
1402static enum matcher_rv
1403cmd_matcher_read_keywords(struct cmd_matcher *matcher,
1404 struct cmd_token *token,
1405 vector args_vector)
718e3744 1406{
8c328f11 1407 unsigned int i;
cd40b329
CF
1408 unsigned long keyword_mask;
1409 unsigned int keyword_found;
1410 enum match_type keyword_match;
1411 enum match_type word_match;
1412 vector keyword_vector;
1413 struct cmd_token *word_token;
1414 const char *word;
1415 int keyword_argc;
1416 const char **keyword_argv;
24873f0c 1417 enum matcher_rv rv = MATCHER_OK;
cd40b329
CF
1418
1419 keyword_mask = 0;
1420 while (1)
1421 {
1422 if (!cmd_matcher_words_left(matcher))
1423 return MATCHER_OK;
909a2155 1424
cd40b329 1425 word = cmd_matcher_get_word(matcher);
718e3744 1426
cd40b329
CF
1427 keyword_found = -1;
1428 keyword_match = no_match;
1429 for (i = 0; i < vector_active(token->keyword); i++)
1430 {
1431 if (keyword_mask & (1 << i))
1432 continue;
1433
1434 keyword_vector = vector_slot(token->keyword, i);
1435 word_token = vector_slot(keyword_vector, 0);
1436
1437 word_match = cmd_word_match(word_token, matcher->filter, word);
1438 if (word_match == no_match)
1439 continue;
1440
1441 cmd_matcher_record_match(matcher, word_match, word_token);
1442
1443 if (word_match > keyword_match)
1444 {
1445 keyword_match = word_match;
1446 keyword_found = i;
1447 }
1448 else if (word_match == keyword_match)
1449 {
1450 if (matcher->word_index != matcher->index || args_vector)
1451 return MATCHER_AMBIGUOUS;
1452 }
1453 }
718e3744 1454
cd40b329
CF
1455 if (keyword_found == (unsigned int)-1)
1456 return MATCHER_NO_MATCH;
718e3744 1457
cd40b329 1458 matcher->word_index++;
909a2155 1459
cd40b329
CF
1460 if (matcher->word_index > matcher->index)
1461 return MATCHER_OK;
1462
1463 keyword_mask |= (1 << keyword_found);
1464
1465 if (args_vector)
1466 {
1467 keyword_argc = 0;
1468 keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*));
1469 /* We use -1 as a marker for unused fields as NULL might be a valid value */
1470 for (i = 0; i < CMD_ARGC_MAX + 1; i++)
1471 keyword_argv[i] = (void*)-1;
1472 vector_set_index(args_vector, keyword_found, keyword_argv);
1473 }
1474 else
1475 {
1476 keyword_argv = NULL;
1477 }
1478
1479 keyword_vector = vector_slot(token->keyword, keyword_found);
1480 /* the keyword itself is at 0. We are only interested in the arguments,
1481 * so start counting at 1. */
1482 for (i = 1; i < vector_active(keyword_vector); i++)
1483 {
1484 word_token = vector_slot(keyword_vector, i);
1485
1486 switch (word_token->type)
1487 {
1488 case TOKEN_TERMINAL:
1489 rv = cmd_matcher_match_terminal(matcher, word_token,
1490 &keyword_argc, keyword_argv);
1491 break;
1492 case TOKEN_MULTIPLE:
1493 rv = cmd_matcher_match_multiple(matcher, word_token,
1494 &keyword_argc, keyword_argv);
1495 break;
1496 case TOKEN_KEYWORD:
1497 assert(!"Keywords should never be nested.");
1498 break;
1499 }
1500
1501 if (MATCHER_ERROR(rv))
1502 return rv;
1503
1504 if (matcher->word_index > matcher->index)
1505 return MATCHER_OK;
1506 }
1507 }
1508 /* not reached */
1509}
1510
1511static enum matcher_rv
1512cmd_matcher_build_keyword_args(struct cmd_matcher *matcher,
1513 struct cmd_token *token,
1514 int *argc, const char **argv,
1515 vector keyword_args_vector)
1516{
1517 unsigned int i, j;
1518 const char **keyword_args;
1519 vector keyword_vector;
1520 struct cmd_token *word_token;
1521 const char *arg;
1522 enum matcher_rv rv;
1523
1524 rv = MATCHER_OK;
1525
1526 if (keyword_args_vector == NULL)
1527 return rv;
1528
1529 for (i = 0; i < vector_active(token->keyword); i++)
1530 {
1531 keyword_vector = vector_slot(token->keyword, i);
1532 keyword_args = vector_lookup(keyword_args_vector, i);
1533
1534 if (vector_active(keyword_vector) == 1)
1535 {
1536 /* this is a keyword without arguments */
1537 if (keyword_args)
1538 {
1539 word_token = vector_slot(keyword_vector, 0);
1540 arg = word_token->cmd;
1541 }
1542 else
1543 {
1544 arg = NULL;
1545 }
1546
1547 if (push_argument(argc, argv, arg))
1548 rv = MATCHER_EXCEED_ARGC_MAX;
1549 }
1550 else
1551 {
1552 /* this is a keyword with arguments */
1553 if (keyword_args)
1554 {
1555 /* the keyword was present, so just fill in the arguments */
1556 for (j = 0; keyword_args[j] != (void*)-1; j++)
1557 if (push_argument(argc, argv, keyword_args[j]))
1558 rv = MATCHER_EXCEED_ARGC_MAX;
1559 XFREE(MTYPE_TMP, keyword_args);
1560 }
1561 else
1562 {
1563 /* the keyword was not present, insert NULL for the arguments
1564 * the keyword would have taken. */
1565 for (j = 1; j < vector_active(keyword_vector); j++)
1566 {
1567 word_token = vector_slot(keyword_vector, j);
1568 if ((word_token->type == TOKEN_TERMINAL
1569 && (CMD_VARARG(word_token->cmd)
1570 || CMD_VARIABLE(word_token->cmd)
1571 || CMD_OPTION(word_token->cmd)))
1572 || word_token->type == TOKEN_MULTIPLE)
1573 {
1574 if (push_argument(argc, argv, NULL))
1575 rv = MATCHER_EXCEED_ARGC_MAX;
1576 }
1577 }
1578 }
1579 }
1580 }
1581 vector_free(keyword_args_vector);
1582 return rv;
1583}
1584
1585static enum matcher_rv
1586cmd_matcher_match_keyword(struct cmd_matcher *matcher,
1587 struct cmd_token *token,
1588 int *argc, const char **argv)
1589{
1590 vector keyword_args_vector;
1591 enum matcher_rv reader_rv;
1592 enum matcher_rv builder_rv;
1593
1594 assert(token->type == TOKEN_KEYWORD);
1595
1596 if (argc && argv)
1597 keyword_args_vector = vector_init(VECTOR_MIN_SIZE);
1598 else
1599 keyword_args_vector = NULL;
1600
1601 reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector);
1602 builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc,
1603 argv, keyword_args_vector);
1604 /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */
1605
1606 if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv))
1607 return builder_rv;
1608
1609 return reader_rv;
1610}
1611
1612static void
1613cmd_matcher_init(struct cmd_matcher *matcher,
1614 struct cmd_element *cmd,
1615 enum filter_type filter,
1616 vector vline,
1617 unsigned int index,
1618 enum match_type *match_type,
1619 vector *match)
1620{
1621 memset(matcher, 0, sizeof(*matcher));
1622
1623 matcher->cmd = cmd;
1624 matcher->filter = filter;
1625 matcher->vline = vline;
1626 matcher->index = index;
1627
1628 matcher->match_type = match_type;
1629 if (matcher->match_type)
1630 *matcher->match_type = no_match;
1631 matcher->match = match;
1632
1633 matcher->word_index = 0;
1634}
1635
1636static enum matcher_rv
1637cmd_element_match(struct cmd_element *cmd_element,
1638 enum filter_type filter,
1639 vector vline,
1640 unsigned int index,
1641 enum match_type *match_type,
1642 vector *match,
1643 int *argc,
1644 const char **argv)
1645{
1646 struct cmd_matcher matcher;
1647 unsigned int token_index;
24873f0c 1648 enum matcher_rv rv = MATCHER_OK;
cd40b329
CF
1649
1650 cmd_matcher_init(&matcher, cmd_element, filter,
1651 vline, index, match_type, match);
1652
1653 if (argc != NULL)
1654 *argc = 0;
1655
1656 for (token_index = 0;
1657 token_index < vector_active(cmd_element->tokens);
1658 token_index++)
1659 {
1660 struct cmd_token *token = vector_slot(cmd_element->tokens, token_index);
1661
1662 switch (token->type)
1663 {
1664 case TOKEN_TERMINAL:
1665 rv = cmd_matcher_match_terminal(&matcher, token, argc, argv);
1666 break;
1667 case TOKEN_MULTIPLE:
1668 rv = cmd_matcher_match_multiple(&matcher, token, argc, argv);
1669 break;
1670 case TOKEN_KEYWORD:
1671 rv = cmd_matcher_match_keyword(&matcher, token, argc, argv);
1672 }
1673
1674 if (MATCHER_ERROR(rv))
1675 return rv;
1676
1677 if (matcher.word_index > index)
1678 return MATCHER_OK;
1679 }
1680
1681 /* return MATCHER_COMPLETE if all words were consumed */
1682 if (matcher.word_index >= vector_active(vline))
1683 return MATCHER_COMPLETE;
1684
1685 /* return MATCHER_COMPLETE also if only an empty word is left. */
1686 if (matcher.word_index == vector_active(vline) - 1
1687 && (!vector_slot(vline, matcher.word_index)
1688 || !strlen((char*)vector_slot(vline, matcher.word_index))))
1689 return MATCHER_COMPLETE;
1690
1691 return MATCHER_NO_MATCH; /* command is too long to match */
1692}
1693
1694/**
1695 * Filter a given vector of commands against a given commandline and
1696 * calculate possible completions.
1697 *
1698 * @param commands A vector of struct cmd_element*. Commands that don't
1699 * match against the given command line will be overwritten
1700 * with NULL in that vector.
1701 * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically
1702 * determines how incomplete commands are handled, compare with
1703 * cmd_word_match for details.
1704 * @param vline A vector of char* containing the tokenized commandline.
1705 * @param index Only match up to the given token of the commandline.
1706 * @param match_type Record the type of the best match here.
1707 * @param matches Record the matches here. For each cmd_element in the commands
1708 * vector, a match vector will be created in the matches vector.
1709 * That vector will contain all struct command_token* of the
1710 * cmd_element which matched against the given vline at the given
1711 * index.
1712 * @return A code specifying if an error occured. If all went right, it's
1713 * CMD_SUCCESS.
1714 */
1715static int
1716cmd_vector_filter(vector commands,
1717 enum filter_type filter,
1718 vector vline,
1719 unsigned int index,
1720 enum match_type *match_type,
1721 vector *matches)
1722{
1723 unsigned int i;
1724 struct cmd_element *cmd_element;
1725 enum match_type best_match;
1726 enum match_type element_match;
1727 enum matcher_rv matcher_rv;
1728
1729 best_match = no_match;
1730 *matches = vector_init(VECTOR_MIN_SIZE);
1731
1732 for (i = 0; i < vector_active (commands); i++)
1733 if ((cmd_element = vector_slot (commands, i)) != NULL)
1734 {
1735 vector_set_index(*matches, i, NULL);
1736 matcher_rv = cmd_element_match(cmd_element, filter,
1737 vline, index,
1738 &element_match,
1739 (vector*)&vector_slot(*matches, i),
1740 NULL, NULL);
1741 if (MATCHER_ERROR(matcher_rv))
1742 {
1743 vector_slot(commands, i) = NULL;
1744 if (matcher_rv == MATCHER_AMBIGUOUS)
1745 return CMD_ERR_AMBIGUOUS;
1746 if (matcher_rv == MATCHER_EXCEED_ARGC_MAX)
1747 return CMD_ERR_EXEED_ARGC_MAX;
1748 }
1749 else if (element_match > best_match)
1750 {
1751 best_match = element_match;
1752 }
718e3744 1753 }
cd40b329
CF
1754 *match_type = best_match;
1755 return CMD_SUCCESS;
1756}
1757
1758/**
1759 * Check whether a given commandline is complete if used for a specific
1760 * cmd_element.
1761 *
1762 * @param cmd_element A cmd_element against which the commandline should be
1763 * checked.
1764 * @param vline The tokenized commandline.
1765 * @return 1 if the given commandline is complete, 0 otherwise.
1766 */
1767static int
1768cmd_is_complete(struct cmd_element *cmd_element,
1769 vector vline)
1770{
1771 enum matcher_rv rv;
1772
1773 rv = cmd_element_match(cmd_element,
1774 FILTER_RELAXED,
1775 vline, -1,
1776 NULL, NULL,
1777 NULL, NULL);
1778 return (rv == MATCHER_COMPLETE);
1779}
1780
1781/**
1782 * Parse a given commandline and construct a list of arguments for the
1783 * given command_element.
1784 *
1785 * @param cmd_element The cmd_element for which we want to construct arguments.
1786 * @param vline The tokenized commandline.
1787 * @param argc Where to store the argument count.
1788 * @param argv Where to store the argument list. Should be at least
1789 * CMD_ARGC_MAX elements long.
1790 * @return CMD_SUCCESS if everything went alright, an error otherwise.
1791 */
1792static int
1793cmd_parse(struct cmd_element *cmd_element,
1794 vector vline,
1795 int *argc, const char **argv)
1796{
1797 enum matcher_rv rv = cmd_element_match(cmd_element,
1798 FILTER_RELAXED,
1799 vline, -1,
1800 NULL, NULL,
1801 argc, argv);
1802 switch (rv)
1803 {
1804 case MATCHER_COMPLETE:
1805 return CMD_SUCCESS;
1806
1807 case MATCHER_NO_MATCH:
1808 return CMD_ERR_NO_MATCH;
1809
1810 case MATCHER_AMBIGUOUS:
1811 return CMD_ERR_AMBIGUOUS;
1812
1813 case MATCHER_EXCEED_ARGC_MAX:
1814 return CMD_ERR_EXEED_ARGC_MAX;
1815
1816 default:
1817 return CMD_ERR_INCOMPLETE;
1818 }
718e3744 1819}
1820
1821/* Check ambiguous match */
274a4a44 1822static int
cd40b329
CF
1823is_cmd_ambiguous (vector cmd_vector,
1824 const char *command,
1825 vector matches,
1826 enum match_type type)
718e3744 1827{
8c328f11 1828 unsigned int i;
1829 unsigned int j;
1830 const char *str = NULL;
8c328f11 1831 const char *matched = NULL;
cd40b329
CF
1832 vector match_vector;
1833 struct cmd_token *cmd_token;
909a2155 1834
cd40b329
CF
1835 if (command == NULL)
1836 command = "";
1837
1838 for (i = 0; i < vector_active (matches); i++)
1839 if ((match_vector = vector_slot (matches, i)) != NULL)
718e3744 1840 {
1841 int match = 0;
1842
cd40b329
CF
1843 for (j = 0; j < vector_active (match_vector); j++)
1844 if ((cmd_token = vector_slot (match_vector, j)) != NULL)
909a2155 1845 {
1846 enum match_type ret;
cd40b329
CF
1847
1848 assert(cmd_token->type == TOKEN_TERMINAL);
1849 if (cmd_token->type != TOKEN_TERMINAL)
1850 continue;
1851
1852 str = cmd_token->cmd;
718e3744 1853
909a2155 1854 switch (type)
1855 {
1856 case exact_match:
1857 if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
1858 && strcmp (command, str) == 0)
718e3744 1859 match++;
909a2155 1860 break;
1861 case partly_match:
1862 if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
1863 && strncmp (command, str, strlen (command)) == 0)
1864 {
1865 if (matched && strcmp (matched, str) != 0)
1866 return 1; /* There is ambiguous match. */
1867 else
1868 matched = str;
1869 match++;
1870 }
1871 break;
1872 case range_match:
1873 if (cmd_range_match (str, command))
1874 {
1875 if (matched && strcmp (matched, str) != 0)
1876 return 1;
1877 else
1878 matched = str;
1879 match++;
1880 }
1881 break;
1882#ifdef HAVE_IPV6
1883 case ipv6_match:
1884 if (CMD_IPV6 (str))
718e3744 1885 match++;
909a2155 1886 break;
1887 case ipv6_prefix_match:
1888 if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
1889 {
1890 if (ret == partly_match)
1891 return 2; /* There is incomplete match. */
1892
1893 match++;
1894 }
1895 break;
1896#endif /* HAVE_IPV6 */
1897 case ipv4_match:
1898 if (CMD_IPV4 (str))
718e3744 1899 match++;
909a2155 1900 break;
1901 case ipv4_prefix_match:
1902 if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
1903 {
1904 if (ret == partly_match)
1905 return 2; /* There is incomplete match. */
1906
1907 match++;
1908 }
1909 break;
1910 case extend_match:
1911 if (CMD_OPTION (str) || CMD_VARIABLE (str))
718e3744 1912 match++;
909a2155 1913 break;
1914 case no_match:
1915 default:
1916 break;
1917 }
1918 }
1919 if (!match)
cd40b329 1920 vector_slot (cmd_vector, i) = NULL;
718e3744 1921 }
1922 return 0;
1923}
1924
1925/* If src matches dst return dst string, otherwise return NULL */
274a4a44 1926static const char *
8c328f11 1927cmd_entry_function (const char *src, const char *dst)
718e3744 1928{
1929 /* Skip variable arguments. */
1930 if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) ||
1931 CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst))
1932 return NULL;
1933
1934 /* In case of 'command \t', given src is NULL string. */
1935 if (src == NULL)
1936 return dst;
1937
1938 /* Matched with input string. */
1939 if (strncmp (src, dst, strlen (src)) == 0)
1940 return dst;
1941
1942 return NULL;
1943}
1944
1945/* If src matches dst return dst string, otherwise return NULL */
1946/* This version will return the dst string always if it is
1947 CMD_VARIABLE for '?' key processing */
274a4a44 1948static const char *
8c328f11 1949cmd_entry_function_desc (const char *src, const char *dst)
718e3744 1950{
1951 if (CMD_VARARG (dst))
1952 return dst;
1953
1954 if (CMD_RANGE (dst))
1955 {
1956 if (cmd_range_match (dst, src))
1957 return dst;
1958 else
1959 return NULL;
1960 }
1961
22e0a9e6 1962#ifdef HAVE_IPV6
718e3744 1963 if (CMD_IPV6 (dst))
1964 {
1965 if (cmd_ipv6_match (src))
1966 return dst;
1967 else
1968 return NULL;
1969 }
1970
1971 if (CMD_IPV6_PREFIX (dst))
1972 {
1973 if (cmd_ipv6_prefix_match (src))
1974 return dst;
1975 else
1976 return NULL;
1977 }
22e0a9e6 1978#endif /* HAVE_IPV6 */
718e3744 1979
1980 if (CMD_IPV4 (dst))
1981 {
1982 if (cmd_ipv4_match (src))
1983 return dst;
1984 else
1985 return NULL;
1986 }
1987
1988 if (CMD_IPV4_PREFIX (dst))
1989 {
1990 if (cmd_ipv4_prefix_match (src))
1991 return dst;
1992 else
1993 return NULL;
1994 }
1995
1996 /* Optional or variable commands always match on '?' */
1997 if (CMD_OPTION (dst) || CMD_VARIABLE (dst))
1998 return dst;
1999
2000 /* In case of 'command \t', given src is NULL string. */
2001 if (src == NULL)
2002 return dst;
2003
2004 if (strncmp (src, dst, strlen (src)) == 0)
2005 return dst;
2006 else
2007 return NULL;
2008}
2009
cd40b329
CF
2010/**
2011 * Check whether a string is already present in a vector of strings.
2012 * @param v A vector of char*.
2013 * @param str A char*.
2014 * @return 0 if str is already present in the vector, 1 otherwise.
2015 */
274a4a44 2016static int
8c328f11 2017cmd_unique_string (vector v, const char *str)
718e3744 2018{
8c328f11 2019 unsigned int i;
718e3744 2020 char *match;
2021
55468c86 2022 for (i = 0; i < vector_active (v); i++)
718e3744 2023 if ((match = vector_slot (v, i)) != NULL)
2024 if (strcmp (match, str) == 0)
2025 return 0;
2026 return 1;
2027}
2028
cd40b329
CF
2029/**
2030 * Check whether a struct cmd_token matching a given string is already
2031 * present in a vector of struct cmd_token.
2032 * @param v A vector of struct cmd_token*.
2033 * @param str A char* which should be searched for.
2034 * @return 0 if there is a struct cmd_token* with its cmd matching str,
2035 * 1 otherwise.
2036 */
274a4a44 2037static int
8c328f11 2038desc_unique_string (vector v, const char *str)
718e3744 2039{
8c328f11 2040 unsigned int i;
cd40b329 2041 struct cmd_token *token;
718e3744 2042
55468c86 2043 for (i = 0; i < vector_active (v); i++)
cd40b329
CF
2044 if ((token = vector_slot (v, i)) != NULL)
2045 if (strcmp (token->cmd, str) == 0)
2046 return 0;
2047 return 1;
718e3744 2048}
2049
274a4a44 2050static int
b92938a7 2051cmd_try_do_shortcut (enum node_type node, char* first_word) {
2052 if ( first_word != NULL &&
2053 node != AUTH_NODE &&
2054 node != VIEW_NODE &&
2055 node != AUTH_ENABLE_NODE &&
2056 node != ENABLE_NODE &&
62687ff1 2057 node != RESTRICTED_NODE &&
b92938a7 2058 0 == strcmp( "do", first_word ) )
2059 return 1;
2060 return 0;
2061}
2062
cd40b329
CF
2063static void
2064cmd_matches_free(vector *matches)
2065{
2066 unsigned int i;
2067 vector cmd_matches;
2068
2069 for (i = 0; i < vector_active(*matches); i++)
2070 if ((cmd_matches = vector_slot(*matches, i)) != NULL)
2071 vector_free(cmd_matches);
2072 vector_free(*matches);
2073 *matches = NULL;
2074}
2075
2076static int
2077cmd_describe_cmp(const void *a, const void *b)
2078{
2079 const struct cmd_token *first = *(struct cmd_token * const *)a;
2080 const struct cmd_token *second = *(struct cmd_token * const *)b;
2081
2082 return strcmp(first->cmd, second->cmd);
2083}
2084
2085static void
2086cmd_describe_sort(vector matchvec)
2087{
2088 qsort(matchvec->index, vector_active(matchvec),
2089 sizeof(void*), cmd_describe_cmp);
2090}
2091
718e3744 2092/* '?' describe command support. */
274a4a44 2093static vector
b92938a7 2094cmd_describe_command_real (vector vline, struct vty *vty, int *status)
718e3744 2095{
b8961476 2096 unsigned int i;
718e3744 2097 vector cmd_vector;
2098#define INIT_MATCHVEC_SIZE 10
2099 vector matchvec;
2100 struct cmd_element *cmd_element;
b8961476 2101 unsigned int index;
54aba54c 2102 int ret;
2103 enum match_type match;
2104 char *command;
cd40b329
CF
2105 vector matches = NULL;
2106 vector match_vector;
16cf945a
DS
2107 uint32_t command_found = 0;
2108 const char *last_word;
718e3744 2109
2110 /* Set index. */
55468c86 2111 if (vector_active (vline) == 0)
b8961476 2112 {
2113 *status = CMD_ERR_NO_MATCH;
2114 return NULL;
2115 }
cd40b329
CF
2116
2117 index = vector_active (vline) - 1;
2118
718e3744 2119 /* Make copy vector of current node's command vector. */
2120 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2121
2122 /* Prepare match vector */
2123 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2124
cd40b329
CF
2125 /* Filter commands and build a list how they could possibly continue. */
2126 for (i = 0; i <= index; i++)
2127 {
2128 command = vector_slot (vline, i);
718e3744 2129
cd40b329
CF
2130 if (matches)
2131 cmd_matches_free(&matches);
718e3744 2132
cd40b329
CF
2133 ret = cmd_vector_filter(cmd_vector,
2134 FILTER_RELAXED,
2135 vline, i,
2136 &match,
2137 &matches);
718e3744 2138
cd40b329
CF
2139 if (ret != CMD_SUCCESS)
2140 {
2141 vector_free (cmd_vector);
2142 vector_free (matchvec);
2143 cmd_matches_free(&matches);
2144 *status = ret;
2145 return NULL;
2146 }
718e3744 2147
cd40b329
CF
2148 /* The last match may well be ambigious, so break here */
2149 if (i == index)
2150 break;
2151
2152 if (match == vararg_match)
2153 {
2154 /* We found a vararg match - so we can throw out the current matches here
2155 * and don't need to continue checking the command input */
2156 unsigned int j, k;
2157
2158 for (j = 0; j < vector_active (matches); j++)
2159 if ((match_vector = vector_slot (matches, j)) != NULL)
2160 for (k = 0; k < vector_active (match_vector); k++)
2161 {
2162 struct cmd_token *token = vector_slot (match_vector, k);
2163 vector_set (matchvec, token);
2164 }
2165
2166 *status = CMD_SUCCESS;
2167 vector_set(matchvec, &token_cr);
2168 vector_free (cmd_vector);
2169 cmd_matches_free(&matches);
2170 cmd_describe_sort(matchvec);
2171 return matchvec;
2172 }
718e3744 2173
cd40b329
CF
2174 ret = is_cmd_ambiguous(cmd_vector, command, matches, match);
2175 if (ret == 1)
2176 {
2177 vector_free (cmd_vector);
2178 vector_free (matchvec);
2179 cmd_matches_free(&matches);
2180 *status = CMD_ERR_AMBIGUOUS;
2181 return NULL;
2182 }
2183 else if (ret == 2)
2184 {
2185 vector_free (cmd_vector);
2186 vector_free (matchvec);
2187 cmd_matches_free(&matches);
2188 *status = CMD_ERR_NO_MATCH;
2189 return NULL;
2190 }
2191 }
54aba54c 2192
718e3744 2193 /* Make description vector. */
16cf945a 2194 for (i = 0; i < vector_active (matches); i++) {
abaaab4e
DW
2195 if ((cmd_element = vector_slot (cmd_vector, i)) != NULL &&
2196 !(cmd_element->attr == CMD_ATTR_DEPRECATED ||
2197 cmd_element->attr == CMD_ATTR_HIDDEN))
718e3744 2198 {
cd40b329 2199 unsigned int j;
cd40b329 2200 vector vline_trimmed;
718e3744 2201
16cf945a 2202 command_found++;
cd40b329
CF
2203 last_word = vector_slot(vline, vector_active(vline) - 1);
2204 if (last_word == NULL || !strlen(last_word))
2205 {
2206 vline_trimmed = vector_copy(vline);
2207 vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1);
2208
2209 if (cmd_is_complete(cmd_element, vline_trimmed)
2210 && desc_unique_string(matchvec, command_cr))
2211 {
2212 if (match != vararg_match)
2213 vector_set(matchvec, &token_cr);
2214 }
2215
2216 vector_free(vline_trimmed);
2217 }
2218
2219 match_vector = vector_slot (matches, i);
2220 if (match_vector)
2221 for (j = 0; j < vector_active(match_vector); j++)
2222 {
2223 struct cmd_token *token = vector_slot(match_vector, j);
2224 const char *string;
2225
2226 string = cmd_entry_function_desc(command, token->cmd);
2227 if (string && desc_unique_string(matchvec, string))
2228 vector_set(matchvec, token);
2229 }
718e3744 2230 }
16cf945a
DS
2231 }
2232
2233 /*
2234 * We can get into this situation when the command is complete
2235 * but the last part of the command is an optional piece of
2236 * cli.
2237 */
2238 last_word = vector_slot(vline, vector_active(vline) - 1);
2239 if (command_found == 0 && (last_word == NULL || !strlen(last_word))) {
2240 vector_set(matchvec, &token_cr);
2241 }
2242
718e3744 2243 vector_free (cmd_vector);
cd40b329 2244 cmd_matches_free(&matches);
718e3744 2245
2246 if (vector_slot (matchvec, 0) == NULL)
2247 {
2248 vector_free (matchvec);
909a2155 2249 *status = CMD_ERR_NO_MATCH;
5fc60519 2250 return NULL;
718e3744 2251 }
718e3744 2252
5fc60519 2253 *status = CMD_SUCCESS;
cd40b329 2254 cmd_describe_sort(matchvec);
718e3744 2255 return matchvec;
2256}
2257
b92938a7 2258vector
2259cmd_describe_command (vector vline, struct vty *vty, int *status)
2260{
2261 vector ret;
2262
2263 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2264 {
2265 enum node_type onode;
2266 vector shifted_vline;
8c328f11 2267 unsigned int index;
b92938a7 2268
2269 onode = vty->node;
2270 vty->node = ENABLE_NODE;
2271 /* We can try it on enable node, cos' the vty is authenticated */
2272
2273 shifted_vline = vector_init (vector_count(vline));
2274 /* use memcpy? */
55468c86 2275 for (index = 1; index < vector_active (vline); index++)
b92938a7 2276 {
2277 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2278 }
2279
2280 ret = cmd_describe_command_real (shifted_vline, vty, status);
2281
2282 vector_free(shifted_vline);
2283 vty->node = onode;
2284 return ret;
2285 }
2286
2287
2288 return cmd_describe_command_real (vline, vty, status);
2289}
2290
2291
718e3744 2292/* Check LCD of matched command. */
274a4a44 2293static int
718e3744 2294cmd_lcd (char **matched)
2295{
2296 int i;
2297 int j;
2298 int lcd = -1;
2299 char *s1, *s2;
2300 char c1, c2;
2301
2302 if (matched[0] == NULL || matched[1] == NULL)
2303 return 0;
2304
2305 for (i = 1; matched[i] != NULL; i++)
2306 {
2307 s1 = matched[i - 1];
2308 s2 = matched[i];
2309
2310 for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
2311 if (c1 != c2)
2312 break;
2313
2314 if (lcd < 0)
2315 lcd = j;
2316 else
2317 {
2318 if (lcd > j)
2319 lcd = j;
2320 }
2321 }
2322 return lcd;
2323}
2324
cd40b329
CF
2325static int
2326cmd_complete_cmp(const void *a, const void *b)
2327{
2328 const char *first = *(char * const *)a;
2329 const char *second = *(char * const *)b;
2330
2331 if (!first)
2332 {
2333 if (!second)
2334 return 0;
2335 return 1;
2336 }
2337 if (!second)
2338 return -1;
2339
2340 return strcmp(first, second);
2341}
2342
2343static void
2344cmd_complete_sort(vector matchvec)
2345{
2346 qsort(matchvec->index, vector_active(matchvec),
2347 sizeof(void*), cmd_complete_cmp);
2348}
2349
718e3744 2350/* Command line completion support. */
274a4a44 2351static char **
b92938a7 2352cmd_complete_command_real (vector vline, struct vty *vty, int *status)
718e3744 2353{
b8961476 2354 unsigned int i;
718e3744 2355 vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2356#define INIT_MATCHVEC_SIZE 10
2357 vector matchvec;
b8961476 2358 unsigned int index;
718e3744 2359 char **match_str;
cd40b329 2360 struct cmd_token *token;
718e3744 2361 char *command;
2362 int lcd;
cd40b329
CF
2363 vector matches = NULL;
2364 vector match_vector;
718e3744 2365
55468c86 2366 if (vector_active (vline) == 0)
b8961476 2367 {
d2519962 2368 vector_free (cmd_vector);
b8961476 2369 *status = CMD_ERR_NO_MATCH;
2370 return NULL;
2371 }
2372 else
55468c86 2373 index = vector_active (vline) - 1;
b8961476 2374
cd40b329
CF
2375 /* First, filter by command string */
2376 for (i = 0; i <= index; i++)
2377 {
2378 command = vector_slot (vline, i);
2379 enum match_type match;
2380 int ret;
718e3744 2381
cd40b329
CF
2382 if (matches)
2383 cmd_matches_free(&matches);
718e3744 2384
cd40b329
CF
2385 /* First try completion match, if there is exactly match return 1 */
2386 ret = cmd_vector_filter(cmd_vector,
2387 FILTER_RELAXED,
2388 vline, i,
2389 &match,
2390 &matches);
2391
2392 if (ret != CMD_SUCCESS)
2393 {
2394 vector_free(cmd_vector);
2395 cmd_matches_free(&matches);
2396 *status = ret;
2397 return NULL;
2398 }
2399
2400 /* Break here - the completion mustn't be checked to be non-ambiguous */
2401 if (i == index)
2402 break;
2403
2404 /* If there is exact match then filter ambiguous match else check
2405 ambiguousness. */
2406 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2407 if (ret == 1)
2408 {
2409 vector_free (cmd_vector);
2410 cmd_matches_free(&matches);
2411 *status = CMD_ERR_AMBIGUOUS;
2412 return NULL;
2413 }
2414 /*
909a2155 2415 else if (ret == 2)
2416 {
2417 vector_free (cmd_vector);
cd40b329 2418 cmd_matches_free(&matches);
909a2155 2419 *status = CMD_ERR_NO_MATCH;
2420 return NULL;
2421 }
2422 */
cd40b329 2423 }
909a2155 2424
718e3744 2425 /* Prepare match vector. */
2426 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2427
cd40b329
CF
2428 /* Build the possible list of continuations into a list of completions */
2429 for (i = 0; i < vector_active (matches); i++)
2430 if ((match_vector = vector_slot (matches, i)))
718e3744 2431 {
8c328f11 2432 const char *string;
cd40b329 2433 unsigned int j;
718e3744 2434
cd40b329
CF
2435 for (j = 0; j < vector_active (match_vector); j++)
2436 if ((token = vector_slot (match_vector, j)))
909a2155 2437 {
b8961476 2438 if ((string =
2439 cmd_entry_function (vector_slot (vline, index),
cd40b329 2440 token->cmd)))
909a2155 2441 if (cmd_unique_string (matchvec, string))
2442 vector_set (matchvec, XSTRDUP (MTYPE_TMP, string));
2443 }
718e3744 2444 }
2445
2446 /* We don't need cmd_vector any more. */
2447 vector_free (cmd_vector);
cd40b329 2448 cmd_matches_free(&matches);
718e3744 2449
2450 /* No matched command */
2451 if (vector_slot (matchvec, 0) == NULL)
2452 {
2453 vector_free (matchvec);
2454
2455 /* In case of 'command \t' pattern. Do you need '?' command at
2456 the end of the line. */
2457 if (vector_slot (vline, index) == '\0')
2458 *status = CMD_ERR_NOTHING_TODO;
2459 else
2460 *status = CMD_ERR_NO_MATCH;
2461 return NULL;
2462 }
2463
2464 /* Only one matched */
2465 if (vector_slot (matchvec, 1) == NULL)
2466 {
2467 match_str = (char **) matchvec->index;
2468 vector_only_wrapper_free (matchvec);
2469 *status = CMD_COMPLETE_FULL_MATCH;
2470 return match_str;
2471 }
2472 /* Make it sure last element is NULL. */
2473 vector_set (matchvec, NULL);
2474
2475 /* Check LCD of matched strings. */
2476 if (vector_slot (vline, index) != NULL)
2477 {
2478 lcd = cmd_lcd ((char **) matchvec->index);
2479
2480 if (lcd)
2481 {
2482 int len = strlen (vector_slot (vline, index));
909a2155 2483
718e3744 2484 if (len < lcd)
2485 {
2486 char *lcdstr;
909a2155 2487
cd40b329 2488 lcdstr = XMALLOC (MTYPE_TMP, lcd + 1);
718e3744 2489 memcpy (lcdstr, matchvec->index[0], lcd);
2490 lcdstr[lcd] = '\0';
2491
2492 /* match_str = (char **) &lcdstr; */
2493
2494 /* Free matchvec. */
55468c86 2495 for (i = 0; i < vector_active (matchvec); i++)
718e3744 2496 {
2497 if (vector_slot (matchvec, i))
cd40b329 2498 XFREE (MTYPE_TMP, vector_slot (matchvec, i));
718e3744 2499 }
2500 vector_free (matchvec);
2501
909a2155 2502 /* Make new matchvec. */
718e3744 2503 matchvec = vector_init (INIT_MATCHVEC_SIZE);
2504 vector_set (matchvec, lcdstr);
2505 match_str = (char **) matchvec->index;
2506 vector_only_wrapper_free (matchvec);
2507
2508 *status = CMD_COMPLETE_MATCH;
2509 return match_str;
2510 }
2511 }
2512 }
2513
2514 match_str = (char **) matchvec->index;
cd40b329 2515 cmd_complete_sort(matchvec);
718e3744 2516 vector_only_wrapper_free (matchvec);
2517 *status = CMD_COMPLETE_LIST_MATCH;
2518 return match_str;
2519}
2520
b92938a7 2521char **
9ab6812d 2522cmd_complete_command (vector vline, struct vty *vty, int *status)
b92938a7 2523{
2524 char **ret;
2525
2526 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2527 {
2528 enum node_type onode;
2529 vector shifted_vline;
8c328f11 2530 unsigned int index;
b92938a7 2531
2532 onode = vty->node;
2533 vty->node = ENABLE_NODE;
2534 /* We can try it on enable node, cos' the vty is authenticated */
2535
2536 shifted_vline = vector_init (vector_count(vline));
2537 /* use memcpy? */
55468c86 2538 for (index = 1; index < vector_active (vline); index++)
b92938a7 2539 {
2540 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2541 }
2542
2543 ret = cmd_complete_command_real (shifted_vline, vty, status);
2544
2545 vector_free(shifted_vline);
2546 vty->node = onode;
2547 return ret;
2548 }
2549
2550
2551 return cmd_complete_command_real (vline, vty, status);
2552}
2553
2554/* return parent node */
2555/* MUST eventually converge on CONFIG_NODE */
13bfca7a 2556enum node_type
274a4a44 2557node_parent ( enum node_type node )
b92938a7 2558{
2559 enum node_type ret;
2560
9ab6812d 2561 assert (node > CONFIG_NODE);
2562
2563 switch (node)
2564 {
2565 case BGP_VPNV4_NODE:
2566 case BGP_IPV4_NODE:
2567 case BGP_IPV4M_NODE:
2568 case BGP_IPV6_NODE:
1e836590 2569 case BGP_IPV6M_NODE:
9ab6812d 2570 ret = BGP_NODE;
2571 break;
2572 case KEYCHAIN_KEY_NODE:
2573 ret = KEYCHAIN_NODE;
2574 break;
2575 default:
2576 ret = CONFIG_NODE;
b92938a7 2577 }
2578
2579 return ret;
2580}
2581
718e3744 2582/* Execute command by argument vline vector. */
274a4a44 2583static int
cd40b329
CF
2584cmd_execute_command_real (vector vline,
2585 enum filter_type filter,
2586 struct vty *vty,
b8961476 2587 struct cmd_element **cmd)
718e3744 2588{
8c328f11 2589 unsigned int i;
2590 unsigned int index;
718e3744 2591 vector cmd_vector;
2592 struct cmd_element *cmd_element;
2593 struct cmd_element *matched_element;
2594 unsigned int matched_count, incomplete_count;
2595 int argc;
9035efaa 2596 const char *argv[CMD_ARGC_MAX];
718e3744 2597 enum match_type match = 0;
718e3744 2598 char *command;
cd40b329
CF
2599 int ret;
2600 vector matches;
718e3744 2601
2602 /* Make copy of command elements. */
2603 cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
2604
55468c86 2605 for (index = 0; index < vector_active (vline); index++)
cd40b329
CF
2606 {
2607 command = vector_slot (vline, index);
2608 ret = cmd_vector_filter(cmd_vector,
2609 filter,
2610 vline, index,
2611 &match,
2612 &matches);
2613
2614 if (ret != CMD_SUCCESS)
2615 {
2616 cmd_matches_free(&matches);
2617 return ret;
2618 }
718e3744 2619
cd40b329
CF
2620 if (match == vararg_match)
2621 {
2622 cmd_matches_free(&matches);
909a2155 2623 break;
cd40b329 2624 }
718e3744 2625
cd40b329
CF
2626 ret = is_cmd_ambiguous (cmd_vector, command, matches, match);
2627 cmd_matches_free(&matches);
2628
2629 if (ret == 1)
2630 {
2631 vector_free(cmd_vector);
2632 return CMD_ERR_AMBIGUOUS;
2633 }
2634 else if (ret == 2)
2635 {
2636 vector_free(cmd_vector);
2637 return CMD_ERR_NO_MATCH;
2638 }
2639 }
718e3744 2640
2641 /* Check matched count. */
2642 matched_element = NULL;
2643 matched_count = 0;
2644 incomplete_count = 0;
2645
55468c86 2646 for (i = 0; i < vector_active (cmd_vector); i++)
b8961476 2647 if ((cmd_element = vector_slot (cmd_vector, i)))
718e3744 2648 {
cd40b329 2649 if (cmd_is_complete(cmd_element, vline))
718e3744 2650 {
2651 matched_element = cmd_element;
718e3744 2652 matched_count++;
2653 }
2654 else
2655 {
2656 incomplete_count++;
2657 }
2658 }
909a2155 2659
718e3744 2660 /* Finish of using cmd_vector. */
2661 vector_free (cmd_vector);
2662
909a2155 2663 /* To execute command, matched_count must be 1. */
2664 if (matched_count == 0)
718e3744 2665 {
2666 if (incomplete_count)
2667 return CMD_ERR_INCOMPLETE;
2668 else
2669 return CMD_ERR_NO_MATCH;
2670 }
2671
909a2155 2672 if (matched_count > 1)
718e3744 2673 return CMD_ERR_AMBIGUOUS;
2674
cd40b329
CF
2675 ret = cmd_parse(matched_element, vline, &argc, argv);
2676 if (ret != CMD_SUCCESS)
2677 return ret;
718e3744 2678
2679 /* For vtysh execution. */
2680 if (cmd)
2681 *cmd = matched_element;
2682
2683 if (matched_element->daemon)
2684 return CMD_SUCCESS_DAEMON;
2685
2686 /* Execute matched command. */
2687 return (*matched_element->func) (matched_element, vty, argc, argv);
2688}
2689
cd40b329
CF
2690/**
2691 * Execute a given command, handling things like "do ..." and checking
2692 * whether the given command might apply at a parent node if doesn't
2693 * apply for the current node.
2694 *
2695 * @param vline Command line input, vector of char* where each element is
2696 * one input token.
2697 * @param vty The vty context in which the command should be executed.
2698 * @param cmd Pointer where the struct cmd_element of the matched command
2699 * will be stored, if any. May be set to NULL if this info is
2700 * not needed.
2701 * @param vtysh If set != 0, don't lookup the command at parent nodes.
2702 * @return The status of the command that has been executed or an error code
2703 * as to why no command could be executed.
2704 */
eda031f6 2705int
87d683b0 2706cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
2707 int vtysh) {
9ab6812d 2708 int ret, saved_ret, tried = 0;
2709 enum node_type onode, try_node;
eda031f6 2710
9ab6812d 2711 onode = try_node = vty->node;
b92938a7 2712
2713 if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
2714 {
2715 vector shifted_vline;
8c328f11 2716 unsigned int index;
b92938a7 2717
2718 vty->node = ENABLE_NODE;
2719 /* We can try it on enable node, cos' the vty is authenticated */
2720
2721 shifted_vline = vector_init (vector_count(vline));
2722 /* use memcpy? */
55468c86 2723 for (index = 1; index < vector_active (vline); index++)
b92938a7 2724 {
2725 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
2726 }
2727
cd40b329 2728 ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
b92938a7 2729
2730 vector_free(shifted_vline);
2731 vty->node = onode;
2732 return ret;
2733 }
2734
2735
cd40b329 2736 saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
b92938a7 2737
87d683b0 2738 if (vtysh)
2739 return saved_ret;
2740
b92938a7 2741 /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
9ab6812d 2742 while ( ret != CMD_SUCCESS && ret != CMD_WARNING
b92938a7 2743 && vty->node > CONFIG_NODE )
2744 {
9ab6812d 2745 try_node = node_parent(try_node);
2746 vty->node = try_node;
cd40b329 2747 ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
9ab6812d 2748 tried = 1;
2749 if (ret == CMD_SUCCESS || ret == CMD_WARNING)
b92938a7 2750 {
9ab6812d 2751 /* succesfull command, leave the node as is */
b92938a7 2752 return ret;
2753 }
b92938a7 2754 }
9ab6812d 2755 /* no command succeeded, reset the vty to the original node and
2756 return the error for this node */
2757 if ( tried )
2758 vty->node = onode;
2759 return saved_ret;
b92938a7 2760}
2761
cd40b329
CF
2762/**
2763 * Execute a given command, matching it strictly against the current node.
2764 * This mode is used when reading config files.
2765 *
2766 * @param vline Command line input, vector of char* where each element is
2767 * one input token.
2768 * @param vty The vty context in which the command should be executed.
2769 * @param cmd Pointer where the struct cmd_element* of the matched command
2770 * will be stored, if any. May be set to NULL if this info is
2771 * not needed.
2772 * @return The status of the command that has been executed or an error code
2773 * as to why no command could be executed.
2774 */
718e3744 2775int
909a2155 2776cmd_execute_command_strict (vector vline, struct vty *vty,
718e3744 2777 struct cmd_element **cmd)
2778{
cd40b329 2779 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
718e3744 2780}
2781
bed578b8
DS
2782/**
2783 * Parse one line of config, walking up the parse tree attempting to find a match
2784 *
2785 * @param vty The vty context in which the command should be executed.
2786 * @param cmd Pointer where the struct cmd_element* of the match command
2787 * will be stored, if any. May be set to NULL if this info is
2788 * not needed.
2789 * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON
2790 * or not.
2791 * @return The status of the command that has been executed or an error code
2792 * as to why no command could be executed.
2793 */
2794int
2795command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon)
2796{
2797 vector vline;
2798 int saved_node;
2799 int ret;
2800
2801 vline = cmd_make_strvec (vty->buf);
2802
2803 /* In case of comment line */
2804 if (vline == NULL)
2805 return CMD_SUCCESS;
2806
2807 /* Execute configuration command : this is strict match */
2808 ret = cmd_execute_command_strict (vline, vty, cmd);
2809
2810 // Climb the tree and try the command again at each node
2811 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
fd715b78
DW
2812 !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) &&
2813 ret != CMD_SUCCESS &&
2814 ret != CMD_WARNING &&
2815 vty->node != CONFIG_NODE) {
bed578b8
DS
2816
2817 saved_node = vty->node;
2818
2819 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
fd715b78
DW
2820 !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) &&
2821 ret != CMD_SUCCESS &&
2822 ret != CMD_WARNING &&
73d2dad0 2823 vty->node > CONFIG_NODE) {
bed578b8 2824 vty->node = node_parent(vty->node);
fd715b78 2825 ret = cmd_execute_command_strict (vline, vty, cmd);
bed578b8
DS
2826 }
2827
2828 // If climbing the tree did not work then ignore the command and
2829 // stay at the same node
2830 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
fd715b78
DW
2831 !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) &&
2832 ret != CMD_SUCCESS &&
2833 ret != CMD_WARNING)
bed578b8
DS
2834 {
2835 vty->node = saved_node;
bed578b8
DS
2836 memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ);
2837 }
2838 }
2839
2840 cmd_free_strvec (vline);
2841
2842 return ret;
2843}
2844
5689fe5f 2845/* Configuration make from file. */
718e3744 2846int
13fbc82d 2847config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
718e3744 2848{
5689fe5f 2849 int ret, error_ret=0;
13fbc82d 2850 *line_num = 0;
718e3744 2851
2852 while (fgets (vty->buf, VTY_BUFSIZ, fp))
2853 {
13fbc82d
SH
2854 if (!error_ret)
2855 ++(*line_num);
2856
bed578b8 2857 ret = command_config_read_one_line (vty, NULL, 0);
718e3744 2858
5689fe5f 2859 if (ret != CMD_SUCCESS && ret != CMD_WARNING &&
bed578b8
DS
2860 ret != CMD_ERR_NOTHING_TODO)
2861 error_ret = ret;
718e3744 2862 }
5689fe5f
DW
2863
2864 if (error_ret) {
2865 return error_ret;
2866 }
2867
718e3744 2868 return CMD_SUCCESS;
2869}
2870
5689fe5f 2871/* Configuration from terminal */
718e3744 2872DEFUN (config_terminal,
2873 config_terminal_cmd,
2874 "configure terminal",
2875 "Configuration from vty interface\n"
2876 "Configuration terminal\n")
2877{
2878 if (vty_config_lock (vty))
2879 vty->node = CONFIG_NODE;
2880 else
2881 {
2882 vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
2883 return CMD_WARNING;
2884 }
2885 return CMD_SUCCESS;
2886}
2887
2888/* Enable command */
2889DEFUN (enable,
2890 config_enable_cmd,
2891 "enable",
2892 "Turn on privileged mode command\n")
2893{
2894 /* If enable password is NULL, change to ENABLE_NODE */
2895 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
2896 vty->type == VTY_SHELL_SERV)
2897 vty->node = ENABLE_NODE;
2898 else
2899 vty->node = AUTH_ENABLE_NODE;
2900
2901 return CMD_SUCCESS;
2902}
2903
2904/* Disable command */
2905DEFUN (disable,
2906 config_disable_cmd,
2907 "disable",
2908 "Turn off privileged mode command\n")
2909{
2910 if (vty->node == ENABLE_NODE)
2911 vty->node = VIEW_NODE;
2912 return CMD_SUCCESS;
2913}
2914
2915/* Down vty node level. */
2916DEFUN (config_exit,
2917 config_exit_cmd,
2918 "exit",
2919 "Exit current mode and down to previous mode\n")
2920{
2921 switch (vty->node)
2922 {
2923 case VIEW_NODE:
2924 case ENABLE_NODE:
62687ff1 2925 case RESTRICTED_NODE:
718e3744 2926 if (vty_shell (vty))
2927 exit (0);
2928 else
2929 vty->status = VTY_CLOSE;
2930 break;
2931 case CONFIG_NODE:
2932 vty->node = ENABLE_NODE;
2933 vty_config_unlock (vty);
2934 break;
2935 case INTERFACE_NODE:
f93e3f69 2936 case VRF_NODE:
718e3744 2937 case ZEBRA_NODE:
2938 case BGP_NODE:
2939 case RIP_NODE:
2940 case RIPNG_NODE:
2941 case OSPF_NODE:
2942 case OSPF6_NODE:
9e867fe6 2943 case ISIS_NODE:
718e3744 2944 case KEYCHAIN_NODE:
2945 case MASC_NODE:
2946 case RMAP_NODE:
12e41d03 2947 case PIM_NODE:
718e3744 2948 case VTY_NODE:
2949 vty->node = CONFIG_NODE;
2950 break;
2951 case BGP_VPNV4_NODE:
2952 case BGP_IPV4_NODE:
2953 case BGP_IPV4M_NODE:
2954 case BGP_IPV6_NODE:
1e836590 2955 case BGP_IPV6M_NODE:
718e3744 2956 vty->node = BGP_NODE;
2957 break;
2958 case KEYCHAIN_KEY_NODE:
2959 vty->node = KEYCHAIN_NODE;
2960 break;
2961 default:
2962 break;
2963 }
2964 return CMD_SUCCESS;
2965}
2966
2967/* quit is alias of exit. */
2968ALIAS (config_exit,
2969 config_quit_cmd,
2970 "quit",
2971 "Exit current mode and down to previous mode\n")
2972
2973/* End of configuration. */
2974DEFUN (config_end,
2975 config_end_cmd,
2976 "end",
2977 "End current mode and change to enable mode.")
2978{
2979 switch (vty->node)
2980 {
2981 case VIEW_NODE:
2982 case ENABLE_NODE:
62687ff1 2983 case RESTRICTED_NODE:
718e3744 2984 /* Nothing to do. */
2985 break;
2986 case CONFIG_NODE:
2987 case INTERFACE_NODE:
f93e3f69 2988 case VRF_NODE:
718e3744 2989 case ZEBRA_NODE:
2990 case RIP_NODE:
2991 case RIPNG_NODE:
2992 case BGP_NODE:
2993 case BGP_VPNV4_NODE:
2994 case BGP_IPV4_NODE:
2995 case BGP_IPV4M_NODE:
2996 case BGP_IPV6_NODE:
1e836590 2997 case BGP_IPV6M_NODE:
718e3744 2998 case RMAP_NODE:
2999 case OSPF_NODE:
3000 case OSPF6_NODE:
9e867fe6 3001 case ISIS_NODE:
718e3744 3002 case KEYCHAIN_NODE:
3003 case KEYCHAIN_KEY_NODE:
3004 case MASC_NODE:
12e41d03 3005 case PIM_NODE:
718e3744 3006 case VTY_NODE:
3007 vty_config_unlock (vty);
3008 vty->node = ENABLE_NODE;
3009 break;
3010 default:
3011 break;
3012 }
3013 return CMD_SUCCESS;
3014}
3015
3016/* Show version. */
3017DEFUN (show_version,
3018 show_version_cmd,
3019 "show version",
3020 SHOW_STR
3021 "Displays zebra version\n")
3022{
12f6ea23 3023 vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"",
3024 VTY_NEWLINE);
0be793e6 3025 vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
80db5ac1
DL
3026 vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE,
3027 QUAGGA_CONFIG_ARGS, VTY_NEWLINE);
718e3744 3028
3029 return CMD_SUCCESS;
3030}
3031
3032/* Help display function for all node. */
3033DEFUN (config_help,
3034 config_help_cmd,
3035 "help",
3036 "Description of the interactive help system\n")
3037{
3038 vty_out (vty,
6590f2c3 3039 "Quagga VTY provides advanced help feature. When you need help,%s\
718e3744 3040anytime at the command line please press '?'.%s\
3041%s\
3042If nothing matches, the help list will be empty and you must backup%s\
3043 until entering a '?' shows the available options.%s\
3044Two styles of help are provided:%s\
30451. Full help is available when you are ready to enter a%s\
3046command argument (e.g. 'show ?') and describes each possible%s\
3047argument.%s\
30482. Partial help is provided when an abbreviated argument is entered%s\
3049 and you want to know what arguments match the input%s\
3050 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3051 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
3052 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
3053 return CMD_SUCCESS;
3054}
3055
3056/* Help display function for all node. */
3057DEFUN (config_list,
3058 config_list_cmd,
3059 "list",
3060 "Print command list\n")
3061{
8c328f11 3062 unsigned int i;
718e3744 3063 struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
3064 struct cmd_element *cmd;
3065
55468c86 3066 for (i = 0; i < vector_active (cnode->cmd_vector); i++)
4275b1de 3067 if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
3068 && !(cmd->attr == CMD_ATTR_DEPRECATED
3069 || cmd->attr == CMD_ATTR_HIDDEN))
718e3744 3070 vty_out (vty, " %s%s", cmd->string,
3071 VTY_NEWLINE);
3072 return CMD_SUCCESS;
3073}
3074
3075/* Write current configuration into file. */
3076DEFUN (config_write_file,
3077 config_write_file_cmd,
3078 "write file",
3079 "Write running configuration to memory, network, or terminal\n"
3080 "Write to configuration file\n")
3081{
8c328f11 3082 unsigned int i;
718e3744 3083 int fd;
3084 struct cmd_node *node;
3085 char *config_file;
3086 char *config_file_tmp = NULL;
3087 char *config_file_sav = NULL;
05865c90 3088 int ret = CMD_WARNING;
718e3744 3089 struct vty *file_vty;
e4421165 3090 struct stat conf_stat;
718e3744 3091
3092 /* Check and see if we are operating under vtysh configuration */
3093 if (host.config == NULL)
3094 {
3095 vty_out (vty, "Can't save to configuration file, using vtysh.%s",
3096 VTY_NEWLINE);
3097 return CMD_WARNING;
3098 }
3099
3100 /* Get filename. */
3101 config_file = host.config;
3102
05865c90 3103 config_file_sav =
3104 XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
718e3744 3105 strcpy (config_file_sav, config_file);
3106 strcat (config_file_sav, CONF_BACKUP_EXT);
3107
3108
05865c90 3109 config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
718e3744 3110 sprintf (config_file_tmp, "%s.XXXXXX", config_file);
3111
3112 /* Open file to configuration write. */
3113 fd = mkstemp (config_file_tmp);
3114 if (fd < 0)
3115 {
3116 vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
3117 VTY_NEWLINE);
05865c90 3118 goto finished;
718e3744 3119 }
3120
3121 /* Make vty for configuration file. */
3122 file_vty = vty_new ();
3123 file_vty->fd = fd;
3124 file_vty->type = VTY_FILE;
3125
3126 /* Config file header print. */
3127 vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
3128 vty_time_print (file_vty, 1);
3129 vty_out (file_vty, "!\n");
3130
55468c86 3131 for (i = 0; i < vector_active (cmdvec); i++)
718e3744 3132 if ((node = vector_slot (cmdvec, i)) && node->func)
3133 {
3134 if ((*node->func) (file_vty))
3135 vty_out (file_vty, "!\n");
3136 }
3137 vty_close (file_vty);
3138
e4421165 3139 if (stat(config_file, &conf_stat) >= 0)
718e3744 3140 {
e4421165
DS
3141 if (unlink (config_file_sav) != 0)
3142 if (errno != ENOENT)
3143 {
3144 vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
3145 VTY_NEWLINE);
3146 goto finished;
3147 }
3148 if (link (config_file, config_file_sav) != 0)
3149 {
3150 vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
3151 VTY_NEWLINE);
3152 goto finished;
3153 }
3154 sync ();
3155 if (unlink (config_file) != 0)
3156 {
3157 vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
3158 VTY_NEWLINE);
3159 goto finished;
3160 }
718e3744 3161 }
3162 if (link (config_file_tmp, config_file) != 0)
3163 {
3164 vty_out (vty, "Can't save configuration file %s.%s", config_file,
3165 VTY_NEWLINE);
05865c90 3166 goto finished;
718e3744 3167 }
718e3744 3168 sync ();
3169
aa593d5e 3170 if (chmod (config_file, CONFIGFILE_MASK) != 0)
3171 {
3172 vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
6099b3b5 3173 config_file, safe_strerror(errno), errno, VTY_NEWLINE);
05865c90 3174 goto finished;
aa593d5e 3175 }
3176
718e3744 3177 vty_out (vty, "Configuration saved to %s%s", config_file,
3178 VTY_NEWLINE);
05865c90 3179 ret = CMD_SUCCESS;
3180
3181finished:
3182 unlink (config_file_tmp);
3183 XFREE (MTYPE_TMP, config_file_tmp);
3184 XFREE (MTYPE_TMP, config_file_sav);
3185 return ret;
718e3744 3186}
3187
3188ALIAS (config_write_file,
3189 config_write_cmd,
3190 "write",
3191 "Write running configuration to memory, network, or terminal\n")
3192
3193ALIAS (config_write_file,
3194 config_write_memory_cmd,
3195 "write memory",
3196 "Write running configuration to memory, network, or terminal\n"
3197 "Write configuration to the file (same as write file)\n")
3198
3199ALIAS (config_write_file,
3200 copy_runningconfig_startupconfig_cmd,
3201 "copy running-config startup-config",
3202 "Copy configuration\n"
3203 "Copy running config to... \n"
3204 "Copy running config to startup config (same as write file)\n")
3205
3206/* Write current configuration into the terminal. */
3207DEFUN (config_write_terminal,
3208 config_write_terminal_cmd,
3209 "write terminal",
3210 "Write running configuration to memory, network, or terminal\n"
3211 "Write to terminal\n")
3212{
8c328f11 3213 unsigned int i;
718e3744 3214 struct cmd_node *node;
3215
3216 if (vty->type == VTY_SHELL_SERV)
3217 {
55468c86 3218 for (i = 0; i < vector_active (cmdvec); i++)
718e3744 3219 if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
3220 {
3221 if ((*node->func) (vty))
3222 vty_out (vty, "!%s", VTY_NEWLINE);
3223 }
3224 }
3225 else
3226 {
3227 vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
3228 VTY_NEWLINE);
3229 vty_out (vty, "!%s", VTY_NEWLINE);
3230
55468c86 3231 for (i = 0; i < vector_active (cmdvec); i++)
718e3744 3232 if ((node = vector_slot (cmdvec, i)) && node->func)
3233 {
3234 if ((*node->func) (vty))
3235 vty_out (vty, "!%s", VTY_NEWLINE);
3236 }
3237 vty_out (vty, "end%s",VTY_NEWLINE);
3238 }
3239 return CMD_SUCCESS;
3240}
3241
3242/* Write current configuration into the terminal. */
3243ALIAS (config_write_terminal,
3244 show_running_config_cmd,
3245 "show running-config",
3246 SHOW_STR
3247 "running configuration\n")
3248
3249/* Write startup configuration into the terminal. */
3250DEFUN (show_startup_config,
3251 show_startup_config_cmd,
3252 "show startup-config",
3253 SHOW_STR
3254 "Contentes of startup configuration\n")
3255{
3256 char buf[BUFSIZ];
3257 FILE *confp;
3258
3259 confp = fopen (host.config, "r");
3260 if (confp == NULL)
3261 {
1db63918
DS
3262 vty_out (vty, "Can't open configuration file [%s] due to '%s'%s",
3263 host.config, safe_strerror(errno), VTY_NEWLINE);
718e3744 3264 return CMD_WARNING;
3265 }
3266
3267 while (fgets (buf, BUFSIZ, confp))
3268 {
3269 char *cp = buf;
3270
3271 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
3272 cp++;
3273 *cp = '\0';
3274
3275 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
3276 }
3277
3278 fclose (confp);
3279
3280 return CMD_SUCCESS;
3281}
3282
3283/* Hostname configuration */
3284DEFUN (config_hostname,
3285 hostname_cmd,
3286 "hostname WORD",
3287 "Set system's network name\n"
3288 "This system's network name\n")
3289{
3290 if (!isalpha((int) *argv[0]))
3291 {
3292 vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
3293 return CMD_WARNING;
3294 }
3295
3296 if (host.name)
05865c90 3297 XFREE (MTYPE_HOST, host.name);
718e3744 3298
05865c90 3299 host.name = XSTRDUP (MTYPE_HOST, argv[0]);
718e3744 3300 return CMD_SUCCESS;
3301}
3302
3303DEFUN (config_no_hostname,
3304 no_hostname_cmd,
3305 "no hostname [HOSTNAME]",
3306 NO_STR
3307 "Reset system's network name\n"
3308 "Host name of this router\n")
3309{
3310 if (host.name)
05865c90 3311 XFREE (MTYPE_HOST, host.name);
718e3744 3312 host.name = NULL;
3313 return CMD_SUCCESS;
3314}
3315
3316/* VTY interface password set. */
3317DEFUN (config_password, password_cmd,
3318 "password (8|) WORD",
3319 "Assign the terminal connection password\n"
3320 "Specifies a HIDDEN password will follow\n"
3321 "dummy string \n"
3322 "The HIDDEN line password string\n")
3323{
3324 /* Argument check. */
3325 if (argc == 0)
3326 {
3327 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3328 return CMD_WARNING;
3329 }
3330
3331 if (argc == 2)
3332 {
3333 if (*argv[0] == '8')
3334 {
3335 if (host.password)
05865c90 3336 XFREE (MTYPE_HOST, host.password);
718e3744 3337 host.password = NULL;
3338 if (host.password_encrypt)
05865c90 3339 XFREE (MTYPE_HOST, host.password_encrypt);
3340 host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
718e3744 3341 return CMD_SUCCESS;
3342 }
3343 else
3344 {
3345 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3346 return CMD_WARNING;
3347 }
3348 }
3349
3350 if (!isalnum ((int) *argv[0]))
3351 {
3352 vty_out (vty,
3353 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3354 return CMD_WARNING;
3355 }
3356
3357 if (host.password)
05865c90 3358 XFREE (MTYPE_HOST, host.password);
718e3744 3359 host.password = NULL;
3360
3361 if (host.encrypt)
3362 {
3363 if (host.password_encrypt)
05865c90 3364 XFREE (MTYPE_HOST, host.password_encrypt);
3365 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
718e3744 3366 }
3367 else
05865c90 3368 host.password = XSTRDUP (MTYPE_HOST, argv[0]);
718e3744 3369
3370 return CMD_SUCCESS;
3371}
3372
3373ALIAS (config_password, password_text_cmd,
3374 "password LINE",
3375 "Assign the terminal connection password\n"
3376 "The UNENCRYPTED (cleartext) line password\n")
3377
3378/* VTY enable password set. */
3379DEFUN (config_enable_password, enable_password_cmd,
3380 "enable password (8|) WORD",
3381 "Modify enable password parameters\n"
3382 "Assign the privileged level password\n"
3383 "Specifies a HIDDEN password will follow\n"
3384 "dummy string \n"
3385 "The HIDDEN 'enable' password string\n")
3386{
3387 /* Argument check. */
3388 if (argc == 0)
3389 {
3390 vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
3391 return CMD_WARNING;
3392 }
3393
3394 /* Crypt type is specified. */
3395 if (argc == 2)
3396 {
3397 if (*argv[0] == '8')
3398 {
3399 if (host.enable)
05865c90 3400 XFREE (MTYPE_HOST, host.enable);
718e3744 3401 host.enable = NULL;
3402
3403 if (host.enable_encrypt)
05865c90 3404 XFREE (MTYPE_HOST, host.enable_encrypt);
3405 host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
718e3744 3406
3407 return CMD_SUCCESS;
3408 }
3409 else
3410 {
3411 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
3412 return CMD_WARNING;
3413 }
3414 }
3415
3416 if (!isalnum ((int) *argv[0]))
3417 {
3418 vty_out (vty,
3419 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
3420 return CMD_WARNING;
3421 }
3422
3423 if (host.enable)
05865c90 3424 XFREE (MTYPE_HOST, host.enable);
718e3744 3425 host.enable = NULL;
3426
3427 /* Plain password input. */
3428 if (host.encrypt)
3429 {
3430 if (host.enable_encrypt)
05865c90 3431 XFREE (MTYPE_HOST, host.enable_encrypt);
3432 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
718e3744 3433 }
3434 else
05865c90 3435 host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
718e3744 3436
3437 return CMD_SUCCESS;
3438}
3439
3440ALIAS (config_enable_password,
3441 enable_password_text_cmd,
3442 "enable password LINE",
3443 "Modify enable password parameters\n"
3444 "Assign the privileged level password\n"
3445 "The UNENCRYPTED (cleartext) 'enable' password\n")
3446
3447/* VTY enable password delete. */
3448DEFUN (no_config_enable_password, no_enable_password_cmd,
3449 "no enable password",
3450 NO_STR
3451 "Modify enable password parameters\n"
3452 "Assign the privileged level password\n")
3453{
3454 if (host.enable)
05865c90 3455 XFREE (MTYPE_HOST, host.enable);
718e3744 3456 host.enable = NULL;
3457
3458 if (host.enable_encrypt)
05865c90 3459 XFREE (MTYPE_HOST, host.enable_encrypt);
718e3744 3460 host.enable_encrypt = NULL;
3461
3462 return CMD_SUCCESS;
3463}
3464
3465DEFUN (service_password_encrypt,
3466 service_password_encrypt_cmd,
3467 "service password-encryption",
3468 "Set up miscellaneous service\n"
3469 "Enable encrypted passwords\n")
3470{
3471 if (host.encrypt)
3472 return CMD_SUCCESS;
3473
3474 host.encrypt = 1;
3475
3476 if (host.password)
3477 {
3478 if (host.password_encrypt)
05865c90 3479 XFREE (MTYPE_HOST, host.password_encrypt);
3480 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
718e3744 3481 }
3482 if (host.enable)
3483 {
3484 if (host.enable_encrypt)
05865c90 3485 XFREE (MTYPE_HOST, host.enable_encrypt);
3486 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
718e3744 3487 }
3488
3489 return CMD_SUCCESS;
3490}
3491
3492DEFUN (no_service_password_encrypt,
3493 no_service_password_encrypt_cmd,
3494 "no service password-encryption",
3495 NO_STR
3496 "Set up miscellaneous service\n"
3497 "Enable encrypted passwords\n")
3498{
3499 if (! host.encrypt)
3500 return CMD_SUCCESS;
3501
3502 host.encrypt = 0;
3503
3504 if (host.password_encrypt)
05865c90 3505 XFREE (MTYPE_HOST, host.password_encrypt);
718e3744 3506 host.password_encrypt = NULL;
3507
3508 if (host.enable_encrypt)
05865c90 3509 XFREE (MTYPE_HOST, host.enable_encrypt);
718e3744 3510 host.enable_encrypt = NULL;
3511
3512 return CMD_SUCCESS;
3513}
3514
3515DEFUN (config_terminal_length, config_terminal_length_cmd,
3516 "terminal length <0-512>",
3517 "Set terminal line parameters\n"
3518 "Set number of lines on a screen\n"
3519 "Number of lines on screen (0 for no pausing)\n")
3520{
3521 int lines;
3522 char *endptr = NULL;
3523
3524 lines = strtol (argv[0], &endptr, 10);
3525 if (lines < 0 || lines > 512 || *endptr != '\0')
3526 {
3527 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3528 return CMD_WARNING;
3529 }
3530 vty->lines = lines;
3531
3532 return CMD_SUCCESS;
3533}
3534
3535DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
3536 "terminal no length",
3537 "Set terminal line parameters\n"
3538 NO_STR
3539 "Set number of lines on a screen\n")
3540{
3541 vty->lines = -1;
3542 return CMD_SUCCESS;
3543}
3544
3545DEFUN (service_terminal_length, service_terminal_length_cmd,
3546 "service terminal-length <0-512>",
3547 "Set up miscellaneous service\n"
3548 "System wide terminal length configuration\n"
3549 "Number of lines of VTY (0 means no line control)\n")
3550{
3551 int lines;
3552 char *endptr = NULL;
3553
3554 lines = strtol (argv[0], &endptr, 10);
3555 if (lines < 0 || lines > 512 || *endptr != '\0')
3556 {
3557 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
3558 return CMD_WARNING;
3559 }
3560 host.lines = lines;
3561
3562 return CMD_SUCCESS;
3563}
3564
3565DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
3566 "no service terminal-length [<0-512>]",
3567 NO_STR
3568 "Set up miscellaneous service\n"
3569 "System wide terminal length configuration\n"
3570 "Number of lines of VTY (0 means no line control)\n")
3571{
3572 host.lines = -1;
3573 return CMD_SUCCESS;
3574}
3575
2885f72d 3576DEFUN_HIDDEN (do_echo,
3577 echo_cmd,
3578 "echo .MESSAGE",
3579 "Echo a message back to the vty\n"
3580 "The message to echo\n")
3581{
3582 char *message;
3583
f6834d4c 3584 vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
3585 VTY_NEWLINE);
3586 if (message)
3587 XFREE(MTYPE_TMP, message);
2885f72d 3588 return CMD_SUCCESS;
3589}
3590
274a4a44 3591DEFUN (config_logmsg,
3592 config_logmsg_cmd,
3593 "logmsg "LOG_LEVELS" .MESSAGE",
3594 "Send a message to enabled logging destinations\n"
3595 LOG_LEVEL_DESC
3596 "The message to send\n")
3597{
3598 int level;
3599 char *message;
3600
3601 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3602 return CMD_ERR_NO_MATCH;
3603
fc95186c 3604 zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : ""));
f6834d4c 3605 if (message)
3606 XFREE(MTYPE_TMP, message);
274a4a44 3607 return CMD_SUCCESS;
3608}
3609
3610DEFUN (show_logging,
3611 show_logging_cmd,
3612 "show logging",
3613 SHOW_STR
3614 "Show current logging configuration\n")
3615{
3616 struct zlog *zl = zlog_default;
3617
3618 vty_out (vty, "Syslog logging: ");
3619 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
3620 vty_out (vty, "disabled");
3621 else
3622 vty_out (vty, "level %s, facility %s, ident %s",
3623 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
3624 facility_name(zl->facility), zl->ident);
3625 vty_out (vty, "%s", VTY_NEWLINE);
3626
3627 vty_out (vty, "Stdout logging: ");
3628 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
3629 vty_out (vty, "disabled");
3630 else
3631 vty_out (vty, "level %s",
3632 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
3633 vty_out (vty, "%s", VTY_NEWLINE);
3634
3635 vty_out (vty, "Monitor logging: ");
3636 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
3637 vty_out (vty, "disabled");
3638 else
3639 vty_out (vty, "level %s",
3640 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
3641 vty_out (vty, "%s", VTY_NEWLINE);
3642
3643 vty_out (vty, "File logging: ");
3644 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
3645 !zl->fp)
3646 vty_out (vty, "disabled");
3647 else
3648 vty_out (vty, "level %s, filename %s",
3649 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
3650 zl->filename);
3651 vty_out (vty, "%s", VTY_NEWLINE);
3652
3653 vty_out (vty, "Protocol name: %s%s",
3654 zlog_proto_names[zl->protocol], VTY_NEWLINE);
3655 vty_out (vty, "Record priority: %s%s",
3656 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
1ed72e0b
AS
3657 vty_out (vty, "Timestamp precision: %d%s",
3658 zl->timestamp_precision, VTY_NEWLINE);
274a4a44 3659
3660 return CMD_SUCCESS;
3661}
3662
718e3744 3663DEFUN (config_log_stdout,
3664 config_log_stdout_cmd,
3665 "log stdout",
3666 "Logging control\n"
274a4a44 3667 "Set stdout logging level\n")
718e3744 3668{
274a4a44 3669 zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
3670 return CMD_SUCCESS;
3671}
3672
3673DEFUN (config_log_stdout_level,
3674 config_log_stdout_level_cmd,
3675 "log stdout "LOG_LEVELS,
3676 "Logging control\n"
3677 "Set stdout logging level\n"
3678 LOG_LEVEL_DESC)
3679{
3680 int level;
3681
3682 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3683 return CMD_ERR_NO_MATCH;
3684 zlog_set_level (NULL, ZLOG_DEST_STDOUT, level);
718e3744 3685 return CMD_SUCCESS;
3686}
3687
3688DEFUN (no_config_log_stdout,
3689 no_config_log_stdout_cmd,
274a4a44 3690 "no log stdout [LEVEL]",
718e3744 3691 NO_STR
3692 "Logging control\n"
274a4a44 3693 "Cancel logging to stdout\n"
3694 "Logging level\n")
718e3744 3695{
274a4a44 3696 zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
718e3744 3697 return CMD_SUCCESS;
3698}
3699
274a4a44 3700DEFUN (config_log_monitor,
3701 config_log_monitor_cmd,
3702 "log monitor",
718e3744 3703 "Logging control\n"
274a4a44 3704 "Set terminal line (monitor) logging level\n")
3705{
3706 zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
3707 return CMD_SUCCESS;
3708}
3709
3710DEFUN (config_log_monitor_level,
3711 config_log_monitor_level_cmd,
3712 "log monitor "LOG_LEVELS,
3713 "Logging control\n"
3714 "Set terminal line (monitor) logging level\n"
3715 LOG_LEVEL_DESC)
3716{
3717 int level;
3718
3719 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3720 return CMD_ERR_NO_MATCH;
3721 zlog_set_level (NULL, ZLOG_DEST_MONITOR, level);
3722 return CMD_SUCCESS;
3723}
3724
3725DEFUN (no_config_log_monitor,
3726 no_config_log_monitor_cmd,
3727 "no log monitor [LEVEL]",
3728 NO_STR
3729 "Logging control\n"
3730 "Disable terminal line (monitor) logging\n"
3731 "Logging level\n")
3732{
3733 zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
3734 return CMD_SUCCESS;
3735}
3736
3737static int
3738set_log_file(struct vty *vty, const char *fname, int loglevel)
718e3744 3739{
3740 int ret;
9035efaa 3741 char *p = NULL;
3742 const char *fullpath;
3743
718e3744 3744 /* Path detection. */
274a4a44 3745 if (! IS_DIRECTORY_SEP (*fname))
718e3744 3746 {
9035efaa 3747 char cwd[MAXPATHLEN+1];
3748 cwd[MAXPATHLEN] = '\0';
3749
3750 if (getcwd (cwd, MAXPATHLEN) == NULL)
3751 {
3752 zlog_err ("config_log_file: Unable to alloc mem!");
3753 return CMD_WARNING;
3754 }
3755
274a4a44 3756 if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
9035efaa 3757 == NULL)
3758 {
3759 zlog_err ("config_log_file: Unable to alloc mem!");
3760 return CMD_WARNING;
3761 }
274a4a44 3762 sprintf (p, "%s/%s", cwd, fname);
9035efaa 3763 fullpath = p;
718e3744 3764 }
3765 else
274a4a44 3766 fullpath = fname;
718e3744 3767
274a4a44 3768 ret = zlog_set_file (NULL, fullpath, loglevel);
718e3744 3769
9035efaa 3770 if (p)
3771 XFREE (MTYPE_TMP, p);
3772
718e3744 3773 if (!ret)
3774 {
274a4a44 3775 vty_out (vty, "can't open logfile %s\n", fname);
718e3744 3776 return CMD_WARNING;
3777 }
3778
3779 if (host.logfile)
05865c90 3780 XFREE (MTYPE_HOST, host.logfile);
718e3744 3781
05865c90 3782 host.logfile = XSTRDUP (MTYPE_HOST, fname);
718e3744 3783
3784 return CMD_SUCCESS;
3785}
3786
274a4a44 3787DEFUN (config_log_file,
3788 config_log_file_cmd,
3789 "log file FILENAME",
3790 "Logging control\n"
3791 "Logging to file\n"
3792 "Logging filename\n")
3793{
3794 return set_log_file(vty, argv[0], zlog_default->default_lvl);
3795}
3796
3797DEFUN (config_log_file_level,
3798 config_log_file_level_cmd,
3799 "log file FILENAME "LOG_LEVELS,
3800 "Logging control\n"
3801 "Logging to file\n"
3802 "Logging filename\n"
3803 LOG_LEVEL_DESC)
3804{
3805 int level;
3806
3807 if ((level = level_match(argv[1])) == ZLOG_DISABLED)
3808 return CMD_ERR_NO_MATCH;
3809 return set_log_file(vty, argv[0], level);
3810}
3811
718e3744 3812DEFUN (no_config_log_file,
3813 no_config_log_file_cmd,
3814 "no log file [FILENAME]",
3815 NO_STR
3816 "Logging control\n"
3817 "Cancel logging to file\n"
3818 "Logging file name\n")
3819{
3820 zlog_reset_file (NULL);
3821
3822 if (host.logfile)
05865c90 3823 XFREE (MTYPE_HOST, host.logfile);
718e3744 3824
3825 host.logfile = NULL;
3826
3827 return CMD_SUCCESS;
3828}
3829
274a4a44 3830ALIAS (no_config_log_file,
3831 no_config_log_file_level_cmd,
3832 "no log file FILENAME LEVEL",
3833 NO_STR
3834 "Logging control\n"
3835 "Cancel logging to file\n"
3836 "Logging file name\n"
3837 "Logging level\n")
3838
718e3744 3839DEFUN (config_log_syslog,
3840 config_log_syslog_cmd,
3841 "log syslog",
3842 "Logging control\n"
274a4a44 3843 "Set syslog logging level\n")
718e3744 3844{
274a4a44 3845 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
12ab19f1 3846 return CMD_SUCCESS;
3847}
3848
274a4a44 3849DEFUN (config_log_syslog_level,
3850 config_log_syslog_level_cmd,
3851 "log syslog "LOG_LEVELS,
12ab19f1 3852 "Logging control\n"
274a4a44 3853 "Set syslog logging level\n"
3854 LOG_LEVEL_DESC)
3855{
3856 int level;
12ab19f1 3857
274a4a44 3858 if ((level = level_match(argv[0])) == ZLOG_DISABLED)
3859 return CMD_ERR_NO_MATCH;
3860 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level);
3861 return CMD_SUCCESS;
3862}
3863
3864DEFUN_DEPRECATED (config_log_syslog_facility,
3865 config_log_syslog_facility_cmd,
3866 "log syslog facility "LOG_FACILITIES,
3867 "Logging control\n"
3868 "Logging goes to syslog\n"
3869 "(Deprecated) Facility parameter for syslog messages\n"
3870 LOG_FACILITY_DESC)
3871{
3872 int facility;
3873
3874 if ((facility = facility_match(argv[0])) < 0)
3875 return CMD_ERR_NO_MATCH;
12ab19f1 3876
274a4a44 3877 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
3878 zlog_default->facility = facility;
718e3744 3879 return CMD_SUCCESS;
3880}
3881
3882DEFUN (no_config_log_syslog,
3883 no_config_log_syslog_cmd,
274a4a44 3884 "no log syslog [LEVEL]",
718e3744 3885 NO_STR
3886 "Logging control\n"
274a4a44 3887 "Cancel logging to syslog\n"
3888 "Logging level\n")
718e3744 3889{
274a4a44 3890 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
718e3744 3891 return CMD_SUCCESS;
3892}
3893
12ab19f1 3894ALIAS (no_config_log_syslog,
3895 no_config_log_syslog_facility_cmd,
274a4a44 3896 "no log syslog facility "LOG_FACILITIES,
12ab19f1 3897 NO_STR
3898 "Logging control\n"
3899 "Logging goes to syslog\n"
3900 "Facility parameter for syslog messages\n"
274a4a44 3901 LOG_FACILITY_DESC)
3902
3903DEFUN (config_log_facility,
3904 config_log_facility_cmd,
3905 "log facility "LOG_FACILITIES,
718e3744 3906 "Logging control\n"
274a4a44 3907 "Facility parameter for syslog messages\n"
3908 LOG_FACILITY_DESC)
718e3744 3909{
274a4a44 3910 int facility;
3911
3912 if ((facility = facility_match(argv[0])) < 0)
3913 return CMD_ERR_NO_MATCH;
3914 zlog_default->facility = facility;
3915 return CMD_SUCCESS;
718e3744 3916}
3917
274a4a44 3918DEFUN (no_config_log_facility,
3919 no_config_log_facility_cmd,
3920 "no log facility [FACILITY]",
718e3744 3921 NO_STR
3922 "Logging control\n"
274a4a44 3923 "Reset syslog facility to default (daemon)\n"
3924 "Syslog facility\n")
3925{
3926 zlog_default->facility = LOG_DAEMON;
3927 return CMD_SUCCESS;
3928}
3929
3930DEFUN_DEPRECATED (config_log_trap,
3931 config_log_trap_cmd,
3932 "log trap "LOG_LEVELS,
3933 "Logging control\n"
3934 "(Deprecated) Set logging level and default for all destinations\n"
3935 LOG_LEVEL_DESC)
3936{
3937 int new_level ;
3938 int i;
3939
3940 if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
3941 return CMD_ERR_NO_MATCH;
3942
3943 zlog_default->default_lvl = new_level;
3944 for (i = 0; i < ZLOG_NUM_DESTS; i++)
3945 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
3946 zlog_default->maxlvl[i] = new_level;
3947 return CMD_SUCCESS;
3948}
3949
3950DEFUN_DEPRECATED (no_config_log_trap,
3951 no_config_log_trap_cmd,
3952 "no log trap [LEVEL]",
3953 NO_STR
3954 "Logging control\n"
3955 "Permit all logging information\n"
3956 "Logging level\n")
718e3744 3957{
274a4a44 3958 zlog_default->default_lvl = LOG_DEBUG;
718e3744 3959 return CMD_SUCCESS;
3960}
3961
3962DEFUN (config_log_record_priority,
3963 config_log_record_priority_cmd,
3964 "log record-priority",
3965 "Logging control\n"
3966 "Log the priority of the message within the message\n")
3967{
3968 zlog_default->record_priority = 1 ;
3969 return CMD_SUCCESS;
3970}
3971
3972DEFUN (no_config_log_record_priority,
3973 no_config_log_record_priority_cmd,
3974 "no log record-priority",
3975 NO_STR
3976 "Logging control\n"
3977 "Do not log the priority of the message within the message\n")
3978{
3979 zlog_default->record_priority = 0 ;
3980 return CMD_SUCCESS;
3981}
3982
1ed72e0b
AS
3983DEFUN (config_log_timestamp_precision,
3984 config_log_timestamp_precision_cmd,
3985 "log timestamp precision <0-6>",
3986 "Logging control\n"
3987 "Timestamp configuration\n"
3988 "Set the timestamp precision\n"
3989 "Number of subsecond digits\n")
3990{
3991 if (argc != 1)
3992 {
3993 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
3994 return CMD_WARNING;
3995 }
3996
3997 VTY_GET_INTEGER_RANGE("Timestamp Precision",
3998 zlog_default->timestamp_precision, argv[0], 0, 6);
3999 return CMD_SUCCESS;
4000}
4001
4002DEFUN (no_config_log_timestamp_precision,
4003 no_config_log_timestamp_precision_cmd,
4004 "no log timestamp precision",
4005 NO_STR
4006 "Logging control\n"
4007 "Timestamp configuration\n"
4008 "Reset the timestamp precision to the default value of 0\n")
4009{
4010 zlog_default->timestamp_precision = 0 ;
4011 return CMD_SUCCESS;
4012}
4013
7cfc61d3
DS
4014int
4015cmd_banner_motd_file (const char *file)
4016{
4017 if (host.motdfile)
4018 XFREE (MTYPE_HOST, host.motdfile);
4019 host.motdfile = XSTRDUP (MTYPE_HOST, file);
4020
4021 return CMD_SUCCESS;
4022}
4023
3b0c5d9a 4024DEFUN (banner_motd_file,
4025 banner_motd_file_cmd,
4d833e55 4026 "banner motd file FILE",
3b0c5d9a 4027 "Set banner\n"
4028 "Banner for motd\n"
4029 "Banner from a file\n"
4030 "Filename\n")
4031{
7cfc61d3 4032 return cmd_banner_motd_file (argv[0]);
3b0c5d9a 4033}
718e3744 4034
4035DEFUN (banner_motd_default,
4036 banner_motd_default_cmd,
4037 "banner motd default",
4038 "Set banner string\n"
4039 "Strings for motd\n"
4040 "Default string\n")
4041{
4042 host.motd = default_motd;
4043 return CMD_SUCCESS;
4044}
4045
4046DEFUN (no_banner_motd,
4047 no_banner_motd_cmd,
4048 "no banner motd",
4049 NO_STR
4050 "Set banner string\n"
4051 "Strings for motd\n")
4052{
4053 host.motd = NULL;
22085181 4054 if (host.motdfile)
05865c90 4055 XFREE (MTYPE_HOST, host.motdfile);
3b0c5d9a 4056 host.motdfile = NULL;
718e3744 4057 return CMD_SUCCESS;
4058}
4059
4060/* Set config filename. Called from vty.c */
4061void
c0e8c16f 4062host_config_set (const char *filename)
718e3744 4063{
228da428
CC
4064 if (host.config)
4065 XFREE (MTYPE_HOST, host.config);
05865c90 4066 host.config = XSTRDUP (MTYPE_HOST, filename);
718e3744 4067}
4068
4069void
4070install_default (enum node_type node)
4071{
4072 install_element (node, &config_exit_cmd);
4073 install_element (node, &config_quit_cmd);
4074 install_element (node, &config_end_cmd);
4075 install_element (node, &config_help_cmd);
4076 install_element (node, &config_list_cmd);
4077
4078 install_element (node, &config_write_terminal_cmd);
4079 install_element (node, &config_write_file_cmd);
4080 install_element (node, &config_write_memory_cmd);
4081 install_element (node, &config_write_cmd);
4082 install_element (node, &show_running_config_cmd);
4083}
4084
4085/* Initialize command interface. Install basic nodes and commands. */
4086void
4087cmd_init (int terminal)
4088{
cd40b329
CF
4089 command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>");
4090 token_cr.type = TOKEN_TERMINAL;
4091 token_cr.cmd = command_cr;
4092 token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, "");
228da428 4093
718e3744 4094 /* Allocate initial top vector of commands. */
4095 cmdvec = vector_init (VECTOR_MIN_SIZE);
4096
4097 /* Default host value settings. */
4098 host.name = NULL;
4099 host.password = NULL;
4100 host.enable = NULL;
4101 host.logfile = NULL;
4102 host.config = NULL;
4103 host.lines = -1;
4104 host.motd = default_motd;
3b0c5d9a 4105 host.motdfile = NULL;
718e3744 4106
4107 /* Install top nodes. */
4108 install_node (&view_node, NULL);
4109 install_node (&enable_node, NULL);
4110 install_node (&auth_node, NULL);
4111 install_node (&auth_enable_node, NULL);
62687ff1 4112 install_node (&restricted_node, NULL);
718e3744 4113 install_node (&config_node, config_write_host);
4114
4115 /* Each node's basic commands. */
4116 install_element (VIEW_NODE, &show_version_cmd);
4117 if (terminal)
4118 {
4119 install_element (VIEW_NODE, &config_list_cmd);
4120 install_element (VIEW_NODE, &config_exit_cmd);
4121 install_element (VIEW_NODE, &config_quit_cmd);
4122 install_element (VIEW_NODE, &config_help_cmd);
4123 install_element (VIEW_NODE, &config_enable_cmd);
4124 install_element (VIEW_NODE, &config_terminal_length_cmd);
4125 install_element (VIEW_NODE, &config_terminal_no_length_cmd);
274a4a44 4126 install_element (VIEW_NODE, &show_logging_cmd);
2885f72d 4127 install_element (VIEW_NODE, &echo_cmd);
62687ff1
PJ
4128
4129 install_element (RESTRICTED_NODE, &config_list_cmd);
4130 install_element (RESTRICTED_NODE, &config_exit_cmd);
4131 install_element (RESTRICTED_NODE, &config_quit_cmd);
4132 install_element (RESTRICTED_NODE, &config_help_cmd);
4133 install_element (RESTRICTED_NODE, &config_enable_cmd);
4134 install_element (RESTRICTED_NODE, &config_terminal_length_cmd);
4135 install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd);
4136 install_element (RESTRICTED_NODE, &echo_cmd);
718e3744 4137 }
4138
4139 if (terminal)
4140 {
4141 install_default (ENABLE_NODE);
4142 install_element (ENABLE_NODE, &config_disable_cmd);
4143 install_element (ENABLE_NODE, &config_terminal_cmd);
4144 install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
4145 }
4146 install_element (ENABLE_NODE, &show_startup_config_cmd);
4147 install_element (ENABLE_NODE, &show_version_cmd);
718e3744 4148
718e3744 4149 if (terminal)
4150 {
e7168df4 4151 install_element (ENABLE_NODE, &config_terminal_length_cmd);
4152 install_element (ENABLE_NODE, &config_terminal_no_length_cmd);
274a4a44 4153 install_element (ENABLE_NODE, &show_logging_cmd);
2885f72d 4154 install_element (ENABLE_NODE, &echo_cmd);
274a4a44 4155 install_element (ENABLE_NODE, &config_logmsg_cmd);
e7168df4 4156
4157 install_default (CONFIG_NODE);
ea8e9d97 4158 }
4159
4160 install_element (CONFIG_NODE, &hostname_cmd);
4161 install_element (CONFIG_NODE, &no_hostname_cmd);
e7168df4 4162
ea8e9d97 4163 if (terminal)
4164 {
e7168df4 4165 install_element (CONFIG_NODE, &password_cmd);
4166 install_element (CONFIG_NODE, &password_text_cmd);
4167 install_element (CONFIG_NODE, &enable_password_cmd);
4168 install_element (CONFIG_NODE, &enable_password_text_cmd);
4169 install_element (CONFIG_NODE, &no_enable_password_cmd);
4170
718e3744 4171 install_element (CONFIG_NODE, &config_log_stdout_cmd);
274a4a44 4172 install_element (CONFIG_NODE, &config_log_stdout_level_cmd);
718e3744 4173 install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
274a4a44 4174 install_element (CONFIG_NODE, &config_log_monitor_cmd);
4175 install_element (CONFIG_NODE, &config_log_monitor_level_cmd);
4176 install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
718e3744 4177 install_element (CONFIG_NODE, &config_log_file_cmd);
274a4a44 4178 install_element (CONFIG_NODE, &config_log_file_level_cmd);
718e3744 4179 install_element (CONFIG_NODE, &no_config_log_file_cmd);
274a4a44 4180 install_element (CONFIG_NODE, &no_config_log_file_level_cmd);
718e3744 4181 install_element (CONFIG_NODE, &config_log_syslog_cmd);
274a4a44 4182 install_element (CONFIG_NODE, &config_log_syslog_level_cmd);
12ab19f1 4183 install_element (CONFIG_NODE, &config_log_syslog_facility_cmd);
718e3744 4184 install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
12ab19f1 4185 install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd);
274a4a44 4186 install_element (CONFIG_NODE, &config_log_facility_cmd);
4187 install_element (CONFIG_NODE, &no_config_log_facility_cmd);
718e3744 4188 install_element (CONFIG_NODE, &config_log_trap_cmd);
4189 install_element (CONFIG_NODE, &no_config_log_trap_cmd);
4190 install_element (CONFIG_NODE, &config_log_record_priority_cmd);
4191 install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
1ed72e0b
AS
4192 install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
4193 install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
718e3744 4194 install_element (CONFIG_NODE, &service_password_encrypt_cmd);
4195 install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
4196 install_element (CONFIG_NODE, &banner_motd_default_cmd);
3b0c5d9a 4197 install_element (CONFIG_NODE, &banner_motd_file_cmd);
718e3744 4198 install_element (CONFIG_NODE, &no_banner_motd_cmd);
4199 install_element (CONFIG_NODE, &service_terminal_length_cmd);
4200 install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
718e3744 4201
354d119a 4202 install_element (VIEW_NODE, &show_thread_cpu_cmd);
4203 install_element (ENABLE_NODE, &show_thread_cpu_cmd);
62687ff1 4204 install_element (RESTRICTED_NODE, &show_thread_cpu_cmd);
e276eb82
PJ
4205
4206 install_element (ENABLE_NODE, &clear_thread_cpu_cmd);
354d119a 4207 install_element (VIEW_NODE, &show_work_queues_cmd);
4208 install_element (ENABLE_NODE, &show_work_queues_cmd);
19dc275e
DS
4209
4210 vrf_install_commands ();
9ab6812d 4211 }
b06fd125 4212 srandom(time(NULL));
718e3744 4213}
228da428 4214
cd40b329
CF
4215static void
4216cmd_terminate_token(struct cmd_token *token)
4217{
4218 unsigned int i, j;
4219 vector keyword_vect;
4220
4221 if (token->multiple)
4222 {
4223 for (i = 0; i < vector_active(token->multiple); i++)
4224 cmd_terminate_token(vector_slot(token->multiple, i));
4225 vector_free(token->multiple);
4226 token->multiple = NULL;
4227 }
4228
4229 if (token->keyword)
4230 {
4231 for (i = 0; i < vector_active(token->keyword); i++)
4232 {
4233 keyword_vect = vector_slot(token->keyword, i);
4234 for (j = 0; j < vector_active(keyword_vect); j++)
4235 cmd_terminate_token(vector_slot(keyword_vect, j));
4236 vector_free(keyword_vect);
4237 }
4238 vector_free(token->keyword);
4239 token->keyword = NULL;
4240 }
4241
4242 XFREE(MTYPE_CMD_TOKENS, token->cmd);
4243 XFREE(MTYPE_CMD_TOKENS, token->desc);
4244
4245 XFREE(MTYPE_CMD_TOKENS, token);
4246}
4247
4248static void
4249cmd_terminate_element(struct cmd_element *cmd)
4250{
4251 unsigned int i;
4252
4253 if (cmd->tokens == NULL)
4254 return;
4255
4256 for (i = 0; i < vector_active(cmd->tokens); i++)
4257 cmd_terminate_token(vector_slot(cmd->tokens, i));
4258
4259 vector_free(cmd->tokens);
4260 cmd->tokens = NULL;
4261}
4262
228da428
CC
4263void
4264cmd_terminate ()
4265{
cd40b329 4266 unsigned int i, j;
228da428
CC
4267 struct cmd_node *cmd_node;
4268 struct cmd_element *cmd_element;
cd40b329 4269 vector cmd_node_v;
228da428
CC
4270
4271 if (cmdvec)
4272 {
4273 for (i = 0; i < vector_active (cmdvec); i++)
4274 if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
4275 {
4276 cmd_node_v = cmd_node->cmd_vector;
4277
4278 for (j = 0; j < vector_active (cmd_node_v); j++)
cd40b329
CF
4279 if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL)
4280 cmd_terminate_element(cmd_element);
228da428
CC
4281
4282 vector_free (cmd_node_v);
4283 }
4284
4285 vector_free (cmdvec);
4286 cmdvec = NULL;
4287 }
4288
4289 if (command_cr)
cd40b329
CF
4290 XFREE(MTYPE_CMD_TOKENS, command_cr);
4291 if (token_cr.desc)
4292 XFREE(MTYPE_CMD_TOKENS, token_cr.desc);
228da428
CC
4293 if (host.name)
4294 XFREE (MTYPE_HOST, host.name);
4295 if (host.password)
4296 XFREE (MTYPE_HOST, host.password);
4297 if (host.password_encrypt)
4298 XFREE (MTYPE_HOST, host.password_encrypt);
4299 if (host.enable)
4300 XFREE (MTYPE_HOST, host.enable);
4301 if (host.enable_encrypt)
4302 XFREE (MTYPE_HOST, host.enable_encrypt);
4303 if (host.logfile)
4304 XFREE (MTYPE_HOST, host.logfile);
4305 if (host.motdfile)
4306 XFREE (MTYPE_HOST, host.motdfile);
4307 if (host.config)
4308 XFREE (MTYPE_HOST, host.config);
4309}