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