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