From d0bfb22c223d645e554290ca82581eb06f94ac3b Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 19 Sep 2016 23:46:51 +0000 Subject: [PATCH] lib: Initial refactor pass on CLI backend Shotgun commit: * Remove shim sources from Makefile.am * Move new types to command.c / command.h * Rewrite command.c / command.h * Refactor shim types to real types in matcher and parser * Initial refactor pass on vty.c Signed-off-by: Quentin Young --- lib/Makefile.am | 4 +- lib/command.c | 2997 ++++++++----------------------------------- lib/command.h | 405 ++---- lib/command_match.c | 59 +- lib/command_parse.y | 16 +- lib/vty.c | 1106 ++++++++-------- 6 files changed, 1219 insertions(+), 3368 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index d12f9ed6d9..b7bb434776 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -11,7 +11,7 @@ libzebra_la_LDFLAGS = -version-info 0:0:0 libzebra_la_SOURCES = \ network.c pid_output.c getopt.c getopt1.c daemon.c \ checksum.c vector.c linklist.c vty.c \ - graph.c command_parse.y command_lex.l command_match.c grammar_sandbox.c \ + graph.c command_parse.y command_lex.l command_match.c \ command.c \ sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \ filter.c routemap.c distribute.c stream.c str.c log.c plist.c \ @@ -28,7 +28,7 @@ libzebra_la_LIBADD = @LIB_REGEX@ @LIBCAP@ pkginclude_HEADERS = \ buffer.h checksum.h filter.h getopt.h hash.h \ if.h linklist.h log.h \ - graph.h command_match.h grammar_sandbox.h \ + graph.h command_match.h \ command.h \ memory.h network.h prefix.h routemap.h distribute.h sockunion.h \ str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \ diff --git a/lib/command.c b/lib/command.c index 5bc157c851..f7800aa724 100644 --- a/lib/command.c +++ b/lib/command.c @@ -5,7 +5,7 @@ Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") This file is part of GNU Zebra. - + GNU Zebra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your @@ -29,53 +29,19 @@ Boston, MA 02111-1307, USA. */ #include #include "thread.h" #include "vector.h" +#include "linklist.h" #include "vty.h" #include "command.h" #include "workqueue.h" #include "vrf.h" +#include "command_match.h" +#include "command_parse.h" + /* Command vector which includes some level of command lists. Normally each daemon maintains each own cmdvec. */ vector cmdvec = NULL; -struct cmd_token token_cr; -char *command_cr = NULL; - -/** - * Filter types. These tell the parser whether to allow - * partial matching on tokens. - */ -enum filter_type -{ - FILTER_RELAXED, - FILTER_STRICT -}; - -/** - * Command matcher result value. - */ -enum matcher_rv -{ - MATCHER_OK, - MATCHER_COMPLETE, - MATCHER_INCOMPLETE, - MATCHER_NO_MATCH, - MATCHER_AMBIGUOUS, - MATCHER_EXCEED_ARGC_MAX -}; - -/** - * Defines which matcher_rv values constitute - * an error. Should be used against matcher_rv - * return values to do basic error checking. - */ -#define MATCHER_ERROR(matcher_rv) \ - ( (matcher_rv) == MATCHER_INCOMPLETE \ - || (matcher_rv) == MATCHER_NO_MATCH \ - || (matcher_rv) == MATCHER_AMBIGUOUS \ - || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \ - ) - /* Host information structure. */ struct host host; @@ -129,7 +95,7 @@ static const struct facility_map { int facility; const char *name; size_t match; -} syslog_facilities[] = +} syslog_facilities[] = { { LOG_KERN, "kern", 1 }, { LOG_USER, "user", 2 }, @@ -181,7 +147,7 @@ static int level_match(const char *s) { int level ; - + for ( level = 0 ; zlog_priority [level] != NULL ; level ++ ) if (!strncmp (s, zlog_priority[level], 2)) return level; @@ -201,7 +167,7 @@ print_version (const char *progname) /* Utility function to concatenate argv argument into a single string with inserting ' ' character between each argument. */ char * -argv_concat (const char **argv, int argc, int shift) +argv_concat (struct cmd_token **argv, int argc, int shift) { int i; size_t len; @@ -210,14 +176,14 @@ argv_concat (const char **argv, int argc, int shift) len = 0; for (i = shift; i < argc; i++) - len += strlen(argv[i])+1; + len += strlen(argv[i]->arg)+1; if (!len) return NULL; p = str = XMALLOC(MTYPE_TMP, len); for (i = shift; i < argc; i++) { size_t arglen; - memcpy(p, argv[i], (arglen = strlen(argv[i]))); + memcpy(p, argv[i]->arg, (arglen = strlen(argv[i]->arg))); p += arglen; *p++ = ' '; } @@ -227,12 +193,12 @@ argv_concat (const char **argv, int argc, int shift) /* Install top node of command vector. */ void -install_node (struct cmd_node *node, - int (*func) (struct vty *)) +install_node (struct cmd_node *node, + int (*func) (struct vty *)) { vector_set_index (cmdvec, node->node, node); node->func = func; - node->cmd_vector = vector_init (VECTOR_MIN_SIZE); + node->cmdgraph = graph_new (); } /* Breaking up string into each command piece. I assume given @@ -245,10 +211,10 @@ cmd_make_strvec (const char *string) char *token; int strlen; vector strvec; - + if (string == NULL) return NULL; - + cp = string; /* Skip white spaces. */ @@ -266,12 +232,12 @@ cmd_make_strvec (const char *string) strvec = vector_init (VECTOR_MIN_SIZE); /* Copy each command piece and set into vector. */ - while (1) + while (1) { start = cp; while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && - *cp != '\0') - cp++; + *cp != '\0') + cp++; strlen = cp - start; token = XMALLOC (MTYPE_STRVEC, strlen + 1); memcpy (token, start, strlen); @@ -279,11 +245,11 @@ cmd_make_strvec (const char *string) vector_set (strvec, token); while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && - *cp != '\0') - cp++; + *cp != '\0') + cp++; if (*cp == '\0') - return strvec; + return strvec; } } @@ -304,388 +270,6 @@ cmd_free_strvec (vector v) vector_free (v); } -/** - * State structure for command format parser. Tracks - * parse tree position and miscellaneous state variables. - * Used when building a command vector from format strings. - */ -struct format_parser_state -{ - vector topvect; /* Top level vector */ - vector intvect; /* Intermediate level vector, used when there's - a multiple in a keyword. */ - vector curvect; /* current vector where read tokens should be - appended. */ - - const char *string; /* pointer to command string, not modified */ - const char *cp; /* pointer in command string, moved along while - parsing */ - const char *dp; /* pointer in description string, moved along while - parsing */ - - int in_keyword; /* flag to remember if we are in a keyword group */ - int in_multiple; /* flag to remember if we are in a multiple group */ - int just_read_word; /* flag to remember if the last thing we read was a - real word and not some abstract token */ -}; - -static void -format_parser_error(struct format_parser_state *state, const char *message) -{ - int offset = state->cp - state->string + 1; - - fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string); - fprintf(stderr, " %*c\n", offset, '^'); - fprintf(stderr, "%s at offset %d.\n", message, offset); - fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n"); - exit(1); -} - -/** - * Reads out one section of a help string from state->dp. - * Leading whitespace is trimmed and the string is read until - * a newline is reached. - * - * @param[out] state format parser state - * @return the help string token read - */ -static char * -format_parser_desc_str(struct format_parser_state *state) -{ - const char *cp, *start; - char *token; - int strlen; - - cp = state->dp; - - if (cp == NULL) - return NULL; - - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') - cp++; - - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; - - start = cp; - - while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') - cp++; - - strlen = cp - start; - token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); - memcpy (token, start, strlen); - *(token + strlen) = '\0'; - - state->dp = cp; - - return token; -} - -/** - * Transitions format parser state into keyword parsing mode. - * A cmd_token struct, `token`, representing this keyword token is initialized - * and appended to state->curvect. token->keyword is initialized as a vector of - * vector, a new vector is initialized and added to token->keyword, and - * state->curvect is set to point at this vector. When control returns to the - * caller newly parsed tokens will be added to this vector. - * - * In short: - * state->curvect[HEAD] = new cmd_token - * state->curvect[HEAD]->keyword[0] = new vector - * state->curvect = state->curvect[HEAD]->keyword[0] - * - * @param[out] state state struct to transition - */ -static void -format_parser_begin_keyword(struct format_parser_state *state) -{ - struct cmd_token *token; - vector keyword_vect; - - if (state->in_keyword - || state->in_multiple) - format_parser_error(state, "Unexpected '{'"); - - state->cp++; - state->in_keyword = 1; - - token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); - token->type = TOKEN_KEYWORD; - token->keyword = vector_init(VECTOR_MIN_SIZE); - - keyword_vect = vector_init(VECTOR_MIN_SIZE); - vector_set(token->keyword, keyword_vect); - - vector_set(state->curvect, token); - state->curvect = keyword_vect; -} - -/** - * Transitions format parser state into multiple parsing mode. - * A cmd_token struct, `token`, representing this multiple token is initialized - * and appended to state->curvect. token->multiple is initialized as a vector - * of cmd_token and state->curvect is set to point at token->multiple. If - * state->curvect != state->topvect (i.e. this multiple token is nested inside - * another composite token) then a pointer to state->curvect is saved in - * state->intvect. - * - * In short: - * state->curvect[HEAD] = new cmd_token - * state->curvect[HEAD]->multiple = new vector - * state->intvect = state->curvect IFF nested token - * state->curvect = state->curvect[HEAD]->multiple - * - * @param[out] state state struct to transition - */ -static void -format_parser_begin_multiple(struct format_parser_state *state) -{ - struct cmd_token *token; - - if (state->in_keyword == 1) - format_parser_error(state, "Keyword starting with '('"); - - if (state->in_multiple) - format_parser_error(state, "Nested group"); - - state->cp++; - state->in_multiple = 1; - state->just_read_word = 0; - - token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); - token->type = TOKEN_MULTIPLE; - token->multiple = vector_init(VECTOR_MIN_SIZE); - - vector_set(state->curvect, token); - if (state->curvect != state->topvect) - state->intvect = state->curvect; - state->curvect = token->multiple; -} - -/** - * Transition format parser state out of keyword parsing mode. - * This function is called upon encountering '}'. - * state->curvect is reassigned to the top level vector (as - * keywords cannot be nested) and state flags are set appropriately. - * - * @param[out] state state struct to transition - */ -static void -format_parser_end_keyword(struct format_parser_state *state) -{ - if (state->in_multiple - || !state->in_keyword) - format_parser_error(state, "Unexpected '}'"); - - if (state->in_keyword == 1) - format_parser_error(state, "Empty keyword group"); - - state->cp++; - state->in_keyword = 0; - state->curvect = state->topvect; -} - -/** - * Transition format parser state out of multiple parsing mode. - * This function is called upon encountering ')'. - * state->curvect is reassigned to its parent vector (state->intvect - * if the multiple token being exited was nested inside another token, - * state->topvect otherwise) and state flags are set appropriately. - * - * @param[out] state state struct to transition - */ -static void -format_parser_end_multiple(struct format_parser_state *state) -{ - char *dummy; - - if (!state->in_multiple) - format_parser_error(state, "Unexpected ')'"); - - if (vector_active(state->curvect) == 0) - format_parser_error(state, "Empty multiple section"); - - if (!state->just_read_word) - { - /* There are constructions like - * 'show ip ospf database ... (self-originate|)' - * in use. - * The old parser reads a description string for the - * word '' between |) which will never match. - * Simulate this behvaior by dropping the next desc - * string in such a case. */ - - dummy = format_parser_desc_str(state); - XFREE(MTYPE_CMD_TOKENS, dummy); - } - - state->cp++; - state->in_multiple = 0; - - if (state->intvect) - state->curvect = state->intvect; - else - state->curvect = state->topvect; -} - -/** - * Format parser handler for pipe '|' character. - * This character separates subtokens in multiple and keyword type tokens. - * If the current token is a multiple keyword, the position pointer is - * simply moved past the pipe and state flags are set appropriately. - * If the current token is a keyword token, the position pointer is moved - * past the pipe. Then the cmd_token struct for the keyword is fetched and - * a new vector of cmd_token is appended to its vector of vector. Finally - * state->curvect is set to point at this new vector. - * - * In short: - * state->curvect = state->topvect[HEAD]->keyword[HEAD] = new vector - * - * @param[out] state state struct to transition - */ -static void -format_parser_handle_pipe(struct format_parser_state *state) -{ - struct cmd_token *keyword_token; - vector keyword_vect; - - if (state->in_multiple) - { - state->just_read_word = 0; - state->cp++; - } - else if (state->in_keyword) - { - state->in_keyword = 1; - state->cp++; - - keyword_token = vector_slot(state->topvect, - vector_active(state->topvect) - 1); - keyword_vect = vector_init(VECTOR_MIN_SIZE); - vector_set(keyword_token->keyword, keyword_vect); - state->curvect = keyword_vect; - } - else - { - format_parser_error(state, "Unexpected '|'"); - } -} - -/** - * Format parser handler for terminal tokens. - * Parses the token, appends it to state->curvect, and sets - * state flags appropriately. - * - * @param[out] state state struct for current format parser state - */ -static void -format_parser_read_word(struct format_parser_state *state) -{ - const char *start; - int len; - char *cmd; - struct cmd_token *token; - - start = state->cp; - - while (state->cp[0] != '\0' - && !strchr("\r\n(){}|", state->cp[0]) - && !isspace((int)state->cp[0])) - state->cp++; - - len = state->cp - start; - cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1); - memcpy(cmd, start, len); - cmd[len] = '\0'; - - token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); - token->type = TOKEN_TERMINAL; - if (strcmp (cmd, "A.B.C.D") == 0) - token->terminal = TERMINAL_IPV4; - else if (strcmp (cmd, "A.B.C.D/M") == 0) - token->terminal = TERMINAL_IPV4_PREFIX; - else if (strcmp (cmd, "X:X::X:X") == 0) - token->terminal = TERMINAL_IPV6; - else if (strcmp (cmd, "X:X::X:X/M") == 0) - token->terminal = TERMINAL_IPV6_PREFIX; - else if (cmd[0] == '[') - token->terminal = TERMINAL_OPTION; - else if (cmd[0] == '.') - token->terminal = TERMINAL_VARARG; - else if (cmd[0] == '<') - token->terminal = TERMINAL_RANGE; - else if (cmd[0] >= 'A' && cmd[0] <= 'Z') - token->terminal = TERMINAL_VARIABLE; - else - token->terminal = TERMINAL_LITERAL; - - token->cmd = cmd; - token->desc = format_parser_desc_str(state); - vector_set(state->curvect, token); - - if (state->in_keyword == 1) - state->in_keyword = 2; - - state->just_read_word = 1; -} - -/** - * Parse a given command format string and build a tree of tokens from - * it that is suitable to be used by the command subsystem. - * - * @param string Command format string. - * @param descstr Description string. - * @return A vector of struct cmd_token representing the given command, - * or NULL on error. - */ -static vector -cmd_parse_format(const char *string, const char *descstr) -{ - struct format_parser_state state; - - if (string == NULL) - return NULL; - - memset(&state, 0, sizeof(state)); - state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE); - state.cp = state.string = string; - state.dp = descstr; - - while (1) - { - while (isspace((int)state.cp[0]) && state.cp[0] != '\0') - state.cp++; - - switch (state.cp[0]) - { - case '\0': - if (state.in_keyword - || state.in_multiple) - format_parser_error(&state, "Unclosed group/keyword"); - return state.topvect; - case '{': - format_parser_begin_keyword(&state); - break; - case '(': - format_parser_begin_multiple(&state); - break; - case '}': - format_parser_end_keyword(&state); - break; - case ')': - format_parser_end_multiple(&state); - break; - case '|': - format_parser_handle_pipe(&state); - break; - default: - format_parser_read_word(&state); - } - } -} /* Return prompt character of specified node. */ const char * @@ -702,23 +286,23 @@ void install_element (enum node_type ntype, struct cmd_element *cmd) { struct cmd_node *cnode; - + /* cmd_init hasn't been called */ if (!cmdvec) return; - + cnode = vector_slot (cmdvec, ntype); - if (cnode == NULL) + if (cnode == NULL) { fprintf (stderr, "Command node %d doesn't exist, please check it\n", - ntype); - exit (1); + ntype); + exit (EXIT_FAILURE); } + // add node to command graph and command vector + command_parse_format (cnode->cmdgraph, cmd); vector_set (cnode->cmd_vector, cmd); - if (cmd->tokens == NULL) - cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); } static const unsigned char itoa64[] = @@ -727,7 +311,7 @@ static const unsigned char itoa64[] = static void to64(char *s, long v, int n) { - while (--n >= 0) + while (--n >= 0) { *s++ = itoa64[v&0x3f]; v >>= 6; @@ -742,7 +326,7 @@ zencrypt (const char *passwd) char *crypt (const char *, const char *); gettimeofday(&tv,0); - + to64(&salt[0], random(), 3); to64(&salt[3], tv.tv_usec, 3); salt[5] = '\0'; @@ -760,9 +344,9 @@ config_write_host (struct vty *vty) if (host.encrypt) { if (host.password_encrypt) - vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE); + vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE); if (host.enable_encrypt) - vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE); + vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE); } else { @@ -775,17 +359,17 @@ config_write_host (struct vty *vty) if (zlog_default->default_lvl != LOG_DEBUG) { vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s", - VTY_NEWLINE); + VTY_NEWLINE); vty_out (vty, "log trap %s%s", - zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); + zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); } if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) { vty_out (vty, "log file %s", host.logfile); if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl) - vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); vty_out (vty, "%s", VTY_NEWLINE); } @@ -793,8 +377,8 @@ config_write_host (struct vty *vty) { vty_out (vty, "log stdout"); if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl) - vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); vty_out (vty, "%s", VTY_NEWLINE); } @@ -802,27 +386,27 @@ config_write_host (struct vty *vty) vty_out(vty,"no log monitor%s",VTY_NEWLINE); else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl) vty_out(vty,"log monitor %s%s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) { vty_out (vty, "log syslog"); if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl) - vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); vty_out (vty, "%s", VTY_NEWLINE); } if (zlog_default->facility != LOG_DAEMON) vty_out (vty, "log facility %s%s", - facility_name(zlog_default->facility), VTY_NEWLINE); + facility_name(zlog_default->facility), VTY_NEWLINE); if (zlog_default->record_priority == 1) vty_out (vty, "log record-priority%s", VTY_NEWLINE); if (zlog_default->timestamp_precision > 0) vty_out (vty, "log timestamp precision %d%s", - zlog_default->timestamp_precision, VTY_NEWLINE); + zlog_default->timestamp_precision, VTY_NEWLINE); if (host.advanced) vty_out (vty, "service advanced-vty%s", VTY_NEWLINE); @@ -832,7 +416,7 @@ config_write_host (struct vty *vty) if (host.lines >= 0) vty_out (vty, "service terminal-length %d%s", host.lines, - VTY_NEWLINE); + VTY_NEWLINE); if (host.motdfile) vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE); @@ -850,1556 +434,152 @@ cmd_node_vector (vector v, enum node_type ntype) return cnode->cmd_vector; } -/* Completion match types. */ -enum match_type -{ - no_match, - extend_match, - ipv4_prefix_match, - ipv4_match, - ipv6_prefix_match, - ipv6_match, - range_match, - vararg_match, - partly_match, - exact_match -}; - -#define IPV4_ADDR_STR "0123456789." -#define IPV4_PREFIX_STR "0123456789./" - -/** - * Determines whether a string is a valid ipv4 token. - * - * @param[in] str the string to match - * @return exact_match if the string is an exact match, no_match/partly_match - * otherwise - */ -static enum match_type -cmd_ipv4_match (const char *str) -{ - struct sockaddr_in sin_dummy; - - if (str == NULL) - return partly_match; - - if (strspn (str, IPV4_ADDR_STR) != strlen (str)) - return no_match; - - if (inet_pton(AF_INET, str, &sin_dummy.sin_addr) != 1) - return no_match; - - return exact_match; -} - -static enum match_type -cmd_ipv4_prefix_match (const char *str) -{ - struct sockaddr_in sin_dummy; - const char *delim = "/\0"; - char *dupe, *prefix, *mask, *context, *endptr; - int nmask = -1; - - if (str == NULL) - return partly_match; - - if (strspn (str, IPV4_PREFIX_STR) != strlen (str)) - return no_match; - - /* tokenize to address + mask */ - dupe = XMALLOC(MTYPE_TMP, strlen(str)+1); - strncpy(dupe, str, strlen(str)+1); - prefix = strtok_r(dupe, delim, &context); - mask = strtok_r(NULL, delim, &context); - - if (!mask) - return partly_match; - - /* validate prefix */ - if (inet_pton(AF_INET, prefix, &sin_dummy.sin_addr) != 1) - return no_match; - - /* validate mask */ - nmask = strtol (mask, &endptr, 10); - if (*endptr != '\0' || nmask < 0 || nmask > 32) - return no_match; - - XFREE(MTYPE_TMP, dupe); - - return exact_match; -} - -#define IPV6_ADDR_STR "0123456789abcdefABCDEF:." -#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" - -#ifdef HAVE_IPV6 - -static enum match_type -cmd_ipv6_match (const char *str) +/* Utility function for getting command graph. */ +static struct graph * +cmd_node_graph (vector v, enum node_type ntype) { - struct sockaddr_in6 sin6_dummy; - int ret; - - if (str == NULL) - return partly_match; - - if (strspn (str, IPV6_ADDR_STR) != strlen (str)) - return no_match; - - /* use inet_pton that has a better support, - * for example inet_pton can support the automatic addresses: - * ::1.2.3.4 - */ - ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); - - if (ret == 1) - return exact_match; - - return no_match; + struct cmd_node *cnode = vector_slot (v, ntype); + return cnode->cmdgraph; } -static enum match_type -cmd_ipv6_prefix_match (const char *str) -{ - struct sockaddr_in6 sin6_dummy; - const char *delim = "/\0"; - char *dupe, *prefix, *mask, *context, *endptr; - int nmask = -1; - - if (str == NULL) - return partly_match; - - if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) - return no_match; - - /* tokenize to address + mask */ - dupe = XMALLOC(MTYPE_TMP, strlen(str)+1); - strncpy(dupe, str, strlen(str)+1); - prefix = strtok_r(dupe, delim, &context); - mask = strtok_r(NULL, delim, &context); - - if (!mask) - return partly_match; - - /* validate prefix */ - if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1) - return no_match; - - /* validate mask */ - nmask = strtol (mask, &endptr, 10); - if (*endptr != '\0' || nmask < 0 || nmask > 128) - return no_match; - - XFREE(MTYPE_TMP, dupe); - - return exact_match; +static int +cmd_try_do_shortcut (enum node_type node, char* first_word) { + if ( first_word != NULL && + node != AUTH_NODE && + node != VIEW_NODE && + node != AUTH_ENABLE_NODE && + node != ENABLE_NODE && + node != RESTRICTED_NODE && + 0 == strcmp( "do", first_word ) ) + return 1; + return 0; } -#endif /* HAVE_IPV6 */ - -#define DECIMAL_STRLEN_MAX 20 - +/** + * Compare function for cmd_token. + * Used with qsort to sort command completions. + */ static int -cmd_range_match (const char *range, const char *str) +compare_completions (const void *fst, const void *snd) { - char *p; - char buf[DECIMAL_STRLEN_MAX + 1]; - char *endptr = NULL; - signed long long min, max, val; - - if (str == NULL) - return 1; - - val = strtoll (str, &endptr, 10); - if (*endptr != '\0') - return 0; - val = llabs(val); - - range++; - p = strchr (range, '-'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy (buf, range, p - range); - buf[p - range] = '\0'; - min = strtoll (buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - range = p + 1; - p = strchr (range, '>'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy (buf, range, p - range); - buf[p - range] = '\0'; - max = strtoll (buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - if (val < min || val > max) - return 0; - - return 1; + struct cmd_token *first = *(struct cmd_token **) fst, + *secnd = *(struct cmd_token **) snd; + return strcmp (first->text, secnd->text); } -static enum match_type -cmd_word_match(struct cmd_token *token, - enum filter_type filter, - const char *word) +/** + * Takes a list of completions returned by command_complete, + * dedeuplicates them based on both text and description, + * and returns them as a vector. + */ +static vector +completions_to_vec (struct list *completions) { - const char *str; - enum match_type match_type; - - str = token->cmd; - - if (filter == FILTER_RELAXED) - if (!word || !strlen(word)) - return partly_match; + vector comps = vector_init (VECTOR_MIN_SIZE); - if (!word) - return no_match; - - switch (token->terminal) + struct listnode *ln; + struct cmd_token *token; + unsigned int i, exists; + for (ALL_LIST_ELEMENTS_RO(completions,ln,token)) + { + // linear search for token in completions vector + exists = 0; + for (i = 0; i < vector_active (comps) && !exists; i++) { - case TERMINAL_VARARG: - return vararg_match; - - case TERMINAL_RANGE: - if (cmd_range_match(str, word)) - return range_match; - break; - - case TERMINAL_IPV6: - match_type = cmd_ipv6_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv6_match; - break; - - case TERMINAL_IPV6_PREFIX: - match_type = cmd_ipv6_prefix_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv6_prefix_match; - break; - - case TERMINAL_IPV4: - match_type = cmd_ipv4_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv4_match; - break; - - case TERMINAL_IPV4_PREFIX: - match_type = cmd_ipv4_prefix_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv4_prefix_match; - break; - - case TERMINAL_OPTION: - case TERMINAL_VARIABLE: - return extend_match; - - case TERMINAL_LITERAL: - if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) - { - if (!strcmp(str, word)) - return exact_match; - return partly_match; - } - if (filter == FILTER_STRICT && !strcmp(str, word)) - return exact_match; - break; - - default: - assert (0); + struct cmd_token *curr = vector_slot (comps, i); + exists = !strcmp (curr->text, token->text) && + !strcmp (curr->desc, token->desc); } - return no_match; -} - -struct cmd_matcher -{ - struct cmd_element *cmd; /* The command element the matcher is using */ - enum filter_type filter; /* Whether to use strict or relaxed matching */ - vector vline; /* The tokenized commandline which is to be matched */ - unsigned int index; /* The index up to which matching should be done */ - - /* If set, construct a list of matches at the position given by index */ - enum match_type *match_type; - vector *match; - - unsigned int word_index; /* iterating over vline */ -}; - -static int -push_argument(int *argc, const char **argv, const char *arg) -{ - if (!arg || !strlen(arg)) - arg = NULL; - - if (!argc || !argv) - return 0; + if (!exists) + vector_set (comps, copy_cmd_token (token)); + } - if (*argc >= CMD_ARGC_MAX) - return -1; + // sort completions + qsort (comps->index, + vector_active (comps), + sizeof (void *), + &compare_completions); - argv[(*argc)++] = arg; - return 0; + return comps; } - -static void -cmd_matcher_record_match(struct cmd_matcher *matcher, - enum match_type match_type, - struct cmd_token *token) +/** + * Generates a vector of cmd_token representing possible completions + * on the current input. + * + * @param vline the vectorized input line + * @param vty the vty with the node to match on + * @param status pointer to matcher status code + */ +static vector +cmd_complete_command_real (vector vline, struct vty *vty, int *status) { - if (matcher->word_index != matcher->index) - return; + struct list *completions; + struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); - if (matcher->match) - { - if (!*matcher->match) - *matcher->match = vector_init(VECTOR_MIN_SIZE); - vector_set(*matcher->match, token); - } + enum matcher_rv rv = command_complete (cmdgraph, vline, &completions); - if (matcher->match_type) - { - if (match_type > *matcher->match_type) - *matcher->match_type = match_type; - } -} - -static int -cmd_matcher_words_left(struct cmd_matcher *matcher) -{ - return matcher->word_index < vector_active(matcher->vline); -} - -static const char* -cmd_matcher_get_word(struct cmd_matcher *matcher) -{ - assert(cmd_matcher_words_left(matcher)); - - return vector_slot(matcher->vline, matcher->word_index); -} - -static enum matcher_rv -cmd_matcher_match_terminal(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv) -{ - const char *word; - enum match_type word_match; - - assert(token->type == TOKEN_TERMINAL); - - if (!cmd_matcher_words_left(matcher)) - { - if (token->terminal == TERMINAL_OPTION) - return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ - else - return MATCHER_INCOMPLETE; - } - - word = cmd_matcher_get_word(matcher); - word_match = cmd_word_match(token, matcher->filter, word); - if (word_match == no_match) - return MATCHER_NO_MATCH; - - /* We have to record the input word as argument if it matched - * against a variable. */ - if (TERMINAL_RECORD (token->terminal)) - { - if (push_argument(argc, argv, word)) - return MATCHER_EXCEED_ARGC_MAX; - } - - cmd_matcher_record_match(matcher, word_match, token); - - matcher->word_index++; - - /* A vararg token should consume all left over words as arguments */ - if (token->terminal == TERMINAL_VARARG) - while (cmd_matcher_words_left(matcher)) - { - word = cmd_matcher_get_word(matcher); - if (word && strlen(word)) - push_argument(argc, argv, word); - matcher->word_index++; - } - - return MATCHER_OK; -} - -static enum matcher_rv -cmd_matcher_match_multiple(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv) -{ - enum match_type multiple_match; - unsigned int multiple_index; - const char *word; - const char *arg = NULL; - struct cmd_token *word_token; - enum match_type word_match; - - assert(token->type == TOKEN_MULTIPLE); - - multiple_match = no_match; - - if (!cmd_matcher_words_left(matcher)) - return MATCHER_INCOMPLETE; - - word = cmd_matcher_get_word(matcher); - for (multiple_index = 0; - multiple_index < vector_active(token->multiple); - multiple_index++) - { - word_token = vector_slot(token->multiple, multiple_index); - - word_match = cmd_word_match(word_token, matcher->filter, word); - if (word_match == no_match) - continue; - - cmd_matcher_record_match(matcher, word_match, word_token); - - if (word_match > multiple_match) - { - multiple_match = word_match; - arg = word; - } - /* To mimic the behavior of the old command implementation, we - * tolerate any ambiguities here :/ */ - } - - matcher->word_index++; - - if (multiple_match == no_match) - return MATCHER_NO_MATCH; - - if (push_argument(argc, argv, arg)) - return MATCHER_EXCEED_ARGC_MAX; - - return MATCHER_OK; -} - -static enum matcher_rv -cmd_matcher_read_keywords(struct cmd_matcher *matcher, - struct cmd_token *token, - vector args_vector) -{ - unsigned int i; - unsigned long keyword_mask; - unsigned int keyword_found; - enum match_type keyword_match; - enum match_type word_match; - vector keyword_vector; - struct cmd_token *word_token; - const char *word; - int keyword_argc; - const char **keyword_argv; - enum matcher_rv rv = MATCHER_OK; - - keyword_mask = 0; - while (1) - { - if (!cmd_matcher_words_left(matcher)) - return MATCHER_OK; - - word = cmd_matcher_get_word(matcher); - - keyword_found = -1; - keyword_match = no_match; - for (i = 0; i < vector_active(token->keyword); i++) - { - if (keyword_mask & (1 << i)) - continue; - - keyword_vector = vector_slot(token->keyword, i); - word_token = vector_slot(keyword_vector, 0); - - word_match = cmd_word_match(word_token, matcher->filter, word); - if (word_match == no_match) - continue; - - cmd_matcher_record_match(matcher, word_match, word_token); - - if (word_match > keyword_match) - { - keyword_match = word_match; - keyword_found = i; - } - else if (word_match == keyword_match) - { - if (matcher->word_index != matcher->index || args_vector) - return MATCHER_AMBIGUOUS; - } - } - - if (keyword_found == (unsigned int)-1) - return MATCHER_NO_MATCH; - - matcher->word_index++; - - if (matcher->word_index > matcher->index) - return MATCHER_OK; - - keyword_mask |= (1 << keyword_found); - - if (args_vector) - { - keyword_argc = 0; - keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*)); - /* We use -1 as a marker for unused fields as NULL might be a valid value */ - for (i = 0; i < CMD_ARGC_MAX + 1; i++) - keyword_argv[i] = (void*)-1; - vector_set_index(args_vector, keyword_found, keyword_argv); - } - else - { - keyword_argv = NULL; - } - - keyword_vector = vector_slot(token->keyword, keyword_found); - /* the keyword itself is at 0. We are only interested in the arguments, - * so start counting at 1. */ - for (i = 1; i < vector_active(keyword_vector); i++) - { - word_token = vector_slot(keyword_vector, i); - - switch (word_token->type) - { - case TOKEN_TERMINAL: - rv = cmd_matcher_match_terminal(matcher, word_token, - &keyword_argc, keyword_argv); - break; - case TOKEN_MULTIPLE: - rv = cmd_matcher_match_multiple(matcher, word_token, - &keyword_argc, keyword_argv); - break; - case TOKEN_KEYWORD: - assert(!"Keywords should never be nested."); - break; - } - - if (MATCHER_ERROR(rv)) - return rv; - - if (matcher->word_index > matcher->index) - return MATCHER_OK; - } - } - /* not reached */ -} - -static enum matcher_rv -cmd_matcher_build_keyword_args(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv, - vector keyword_args_vector) -{ - unsigned int i, j; - const char **keyword_args; - vector keyword_vector; - struct cmd_token *word_token; - const char *arg; - enum matcher_rv rv; - - rv = MATCHER_OK; - - if (keyword_args_vector == NULL) - return rv; - - for (i = 0; i < vector_active(token->keyword); i++) - { - keyword_vector = vector_slot(token->keyword, i); - keyword_args = vector_lookup(keyword_args_vector, i); - - if (vector_active(keyword_vector) == 1) - { - /* this is a keyword without arguments */ - if (keyword_args) - { - word_token = vector_slot(keyword_vector, 0); - arg = word_token->cmd; - } - else - { - arg = NULL; - } - - if (push_argument(argc, argv, arg)) - rv = MATCHER_EXCEED_ARGC_MAX; - } - else - { - /* this is a keyword with arguments */ - if (keyword_args) - { - /* the keyword was present, so just fill in the arguments */ - for (j = 0; keyword_args[j] != (void*)-1; j++) - if (push_argument(argc, argv, keyword_args[j])) - rv = MATCHER_EXCEED_ARGC_MAX; - XFREE(MTYPE_TMP, keyword_args); - } - else - { - /* the keyword was not present, insert NULL for the arguments - * the keyword would have taken. */ - for (j = 1; j < vector_active(keyword_vector); j++) - { - word_token = vector_slot(keyword_vector, j); - if ((word_token->type == TOKEN_TERMINAL - && TERMINAL_RECORD (word_token->terminal)) - || word_token->type == TOKEN_MULTIPLE) - { - if (push_argument(argc, argv, NULL)) - rv = MATCHER_EXCEED_ARGC_MAX; - } - } - } - } - } - vector_free(keyword_args_vector); - return rv; -} - -static enum matcher_rv -cmd_matcher_match_keyword(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv) -{ - vector keyword_args_vector; - enum matcher_rv reader_rv; - enum matcher_rv builder_rv; - - assert(token->type == TOKEN_KEYWORD); - - if (argc && argv) - keyword_args_vector = vector_init(VECTOR_MIN_SIZE); - else - keyword_args_vector = NULL; - - reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector); - builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc, - argv, keyword_args_vector); - /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */ - - if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv)) - return builder_rv; - - return reader_rv; -} - -static void -cmd_matcher_init(struct cmd_matcher *matcher, - struct cmd_element *cmd, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *match) -{ - memset(matcher, 0, sizeof(*matcher)); - - matcher->cmd = cmd; - matcher->filter = filter; - matcher->vline = vline; - matcher->index = index; - - matcher->match_type = match_type; - if (matcher->match_type) - *matcher->match_type = no_match; - matcher->match = match; - - matcher->word_index = 0; -} - -static enum matcher_rv -cmd_element_match(struct cmd_element *cmd_element, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *match, - int *argc, - const char **argv) -{ - struct cmd_matcher matcher; - unsigned int token_index; - enum matcher_rv rv = MATCHER_OK; - - cmd_matcher_init(&matcher, cmd_element, filter, - vline, index, match_type, match); - - if (argc != NULL) - *argc = 0; - - for (token_index = 0; - token_index < vector_active(cmd_element->tokens); - token_index++) - { - struct cmd_token *token = vector_slot(cmd_element->tokens, token_index); - - switch (token->type) - { - case TOKEN_TERMINAL: - rv = cmd_matcher_match_terminal(&matcher, token, argc, argv); - break; - case TOKEN_MULTIPLE: - rv = cmd_matcher_match_multiple(&matcher, token, argc, argv); - break; - case TOKEN_KEYWORD: - rv = cmd_matcher_match_keyword(&matcher, token, argc, argv); - } - - if (MATCHER_ERROR(rv)) - return rv; - - if (matcher.word_index > index) - return MATCHER_OK; - } - - /* return MATCHER_COMPLETE if all words were consumed */ - if (matcher.word_index >= vector_active(vline)) - return MATCHER_COMPLETE; - - /* return MATCHER_COMPLETE also if only an empty word is left. */ - if (matcher.word_index == vector_active(vline) - 1 - && (!vector_slot(vline, matcher.word_index) - || !strlen((char*)vector_slot(vline, matcher.word_index)))) - return MATCHER_COMPLETE; - - return MATCHER_NO_MATCH; /* command is too long to match */ -} - -/** - * Filter a given vector of commands against a given commandline and - * calculate possible completions. - * - * @param commands A vector of struct cmd_element*. Commands that don't - * match against the given command line will be overwritten - * with NULL in that vector. - * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically - * determines how incomplete commands are handled, compare with - * cmd_word_match for details. - * @param vline A vector of char* containing the tokenized commandline. - * @param index Only match up to the given token of the commandline. - * @param match_type Record the type of the best match here. - * @param matches Record the matches here. For each cmd_element in the commands - * vector, a match vector will be created in the matches vector. - * That vector will contain all struct command_token* of the - * cmd_element which matched against the given vline at the given - * index. - * @return A code specifying if an error occured. If all went right, it's - * CMD_SUCCESS. - */ -static int -cmd_vector_filter(vector commands, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *matches) -{ - unsigned int i; - struct cmd_element *cmd_element; - enum match_type best_match; - enum match_type element_match; - enum matcher_rv matcher_rv; - - best_match = no_match; - *matches = vector_init(VECTOR_MIN_SIZE); - - for (i = 0; i < vector_active (commands); i++) - if ((cmd_element = vector_slot (commands, i)) != NULL) - { - vector_set_index(*matches, i, NULL); - matcher_rv = cmd_element_match(cmd_element, filter, - vline, index, - &element_match, - (vector*)&vector_slot(*matches, i), - NULL, NULL); - if (MATCHER_ERROR(matcher_rv)) - { - vector_slot(commands, i) = NULL; - if (matcher_rv == MATCHER_AMBIGUOUS) - return CMD_ERR_AMBIGUOUS; - if (matcher_rv == MATCHER_EXCEED_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } - else if (element_match > best_match) - { - best_match = element_match; - } - } - *match_type = best_match; - return CMD_SUCCESS; -} - -/** - * Check whether a given commandline is complete if used for a specific - * cmd_element. - * - * @param cmd_element A cmd_element against which the commandline should be - * checked. - * @param vline The tokenized commandline. - * @return 1 if the given commandline is complete, 0 otherwise. - */ -static int -cmd_is_complete(struct cmd_element *cmd_element, - vector vline) -{ - enum matcher_rv rv; - - rv = cmd_element_match(cmd_element, - FILTER_RELAXED, - vline, -1, - NULL, NULL, - NULL, NULL); - return (rv == MATCHER_COMPLETE); -} - -/** - * Parse a given commandline and construct a list of arguments for the - * given command_element. - * - * @param cmd_element The cmd_element for which we want to construct arguments. - * @param vline The tokenized commandline. - * @param argc Where to store the argument count. - * @param argv Where to store the argument list. Should be at least - * CMD_ARGC_MAX elements long. - * @return CMD_SUCCESS if everything went alright, an error otherwise. - */ -static int -cmd_parse(struct cmd_element *cmd_element, - vector vline, - int *argc, const char **argv) -{ - enum matcher_rv rv = cmd_element_match(cmd_element, - FILTER_RELAXED, - vline, -1, - NULL, NULL, - argc, argv); - switch (rv) - { - case MATCHER_COMPLETE: - return CMD_SUCCESS; - - case MATCHER_NO_MATCH: - return CMD_ERR_NO_MATCH; - - case MATCHER_AMBIGUOUS: - return CMD_ERR_AMBIGUOUS; - - case MATCHER_EXCEED_ARGC_MAX: - return CMD_ERR_EXEED_ARGC_MAX; - - default: - return CMD_ERR_INCOMPLETE; - } -} - -/* Check ambiguous match */ -static int -is_cmd_ambiguous (vector cmd_vector, - const char *command, - vector matches, - enum match_type type) -{ - unsigned int i; - unsigned int j; - const char *str = NULL; - const char *matched = NULL; - vector match_vector; - struct cmd_token *cmd_token; - - if (command == NULL) - command = ""; - - for (i = 0; i < vector_active (matches); i++) - if ((match_vector = vector_slot (matches, i)) != NULL) - { - int match = 0; - - for (j = 0; j < vector_active (match_vector); j++) - if ((cmd_token = vector_slot (match_vector, j)) != NULL) - { - enum match_type ret; - - assert(cmd_token->type == TOKEN_TERMINAL); - if (cmd_token->type != TOKEN_TERMINAL) - continue; - - str = cmd_token->cmd; - - switch (type) - { - case exact_match: - if (!TERMINAL_RECORD (cmd_token->terminal) - && strcmp (command, str) == 0) - match++; - break; - case partly_match: - if (!TERMINAL_RECORD (cmd_token->terminal) - && strncmp (command, str, strlen (command)) == 0) - { - if (matched && strcmp (matched, str) != 0) - return 1; /* There is ambiguous match. */ - else - matched = str; - match++; - } - break; - case range_match: - if (cmd_range_match (str, command)) - { - if (matched && strcmp (matched, str) != 0) - return 1; - else - matched = str; - match++; - } - break; -#ifdef HAVE_IPV6 - case ipv6_match: - if (cmd_token->terminal == TERMINAL_IPV6) - match++; - break; - case ipv6_prefix_match: - if ((ret = cmd_ipv6_prefix_match (command)) != no_match) - { - if (ret == partly_match) - return 2; /* There is incomplete match. */ - - match++; - } - break; -#endif /* HAVE_IPV6 */ - case ipv4_match: - if (cmd_token->terminal == TERMINAL_IPV4) - match++; - break; - case ipv4_prefix_match: - if ((ret = cmd_ipv4_prefix_match (command)) != no_match) - { - if (ret == partly_match) - return 2; /* There is incomplete match. */ - - match++; - } - break; - case extend_match: - if (TERMINAL_RECORD (cmd_token->terminal)) - match++; - break; - case no_match: - default: - break; - } - } - if (!match) - vector_slot (cmd_vector, i) = NULL; - } - return 0; -} - -/* If src matches dst return dst string, otherwise return NULL */ -static const char * -cmd_entry_function (const char *src, struct cmd_token *token) -{ - const char *dst = token->cmd; - - /* Skip variable arguments. */ - if (TERMINAL_RECORD (token->terminal)) - return NULL; - - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - /* Matched with input string. */ - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - - return NULL; -} - -/* If src matches dst return dst string, otherwise return NULL */ -/* This version will return the dst string always if it is - CMD_VARIABLE for '?' key processing */ -static const char * -cmd_entry_function_desc (const char *src, struct cmd_token *token) -{ - const char *dst = token->cmd; - - switch (token->terminal) + if (MATCHER_ERROR(rv)) + { + switch (rv) { - case TERMINAL_VARARG: - return dst; - - case TERMINAL_RANGE: - if (cmd_range_match (dst, src)) - return dst; - else - return NULL; - - case TERMINAL_IPV6: - if (cmd_ipv6_match (src)) - return dst; - else - return NULL; - - case TERMINAL_IPV6_PREFIX: - if (cmd_ipv6_prefix_match (src)) - return dst; - else - return NULL; - - case TERMINAL_IPV4: - if (cmd_ipv4_match (src)) - return dst; - else - return NULL; - - case TERMINAL_IPV4_PREFIX: - if (cmd_ipv4_prefix_match (src)) - return dst; - else - return NULL; - - /* Optional or variable commands always match on '?' */ - case TERMINAL_OPTION: - case TERMINAL_VARIABLE: - return dst; - - case TERMINAL_LITERAL: - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - else - return NULL; - + case MATCHER_AMBIGUOUS: + *status = CMD_ERR_AMBIGUOUS; default: - assert(0); - return NULL; - } -} - -/** - * Check whether a string is already present in a vector of strings. - * @param v A vector of char*. - * @param str A char*. - * @return 0 if str is already present in the vector, 1 otherwise. - */ -static int -cmd_unique_string (vector v, const char *str) -{ - unsigned int i; - char *match; - - for (i = 0; i < vector_active (v); i++) - if ((match = vector_slot (v, i)) != NULL) - if (strcmp (match, str) == 0) - return 0; - return 1; -} - -/** - * Check whether a struct cmd_token matching a given string is already - * present in a vector of struct cmd_token. - * @param v A vector of struct cmd_token*. - * @param str A char* which should be searched for. - * @return 0 if there is a struct cmd_token* with its cmd matching str, - * 1 otherwise. - */ -static int -desc_unique_string (vector v, const char *str) -{ - unsigned int i; - struct cmd_token *token; - - for (i = 0; i < vector_active (v); i++) - if ((token = vector_slot (v, i)) != NULL) - if (strcmp (token->cmd, str) == 0) - return 0; - return 1; -} - -static int -cmd_try_do_shortcut (enum node_type node, char* first_word) { - if ( first_word != NULL && - node != AUTH_NODE && - node != VIEW_NODE && - node != AUTH_ENABLE_NODE && - node != ENABLE_NODE && - node != RESTRICTED_NODE && - 0 == strcmp( "do", first_word ) ) - return 1; - return 0; -} - -static void -cmd_matches_free(vector *matches) -{ - unsigned int i; - vector cmd_matches; - - for (i = 0; i < vector_active(*matches); i++) - if ((cmd_matches = vector_slot(*matches, i)) != NULL) - vector_free(cmd_matches); - vector_free(*matches); - *matches = NULL; -} - -static int -cmd_describe_cmp(const void *a, const void *b) -{ - const struct cmd_token *first = *(struct cmd_token * const *)a; - const struct cmd_token *second = *(struct cmd_token * const *)b; - - return strcmp(first->cmd, second->cmd); -} - -static void -cmd_describe_sort(vector matchvec) -{ - qsort(matchvec->index, vector_active(matchvec), - sizeof(void*), cmd_describe_cmp); -} - -/* '?' describe command support. */ -static vector -cmd_describe_command_real (vector vline, struct vty *vty, int *status) -{ - unsigned int i; - vector cmd_vector; -#define INIT_MATCHVEC_SIZE 10 - vector matchvec; - struct cmd_element *cmd_element; - unsigned int index; - int ret; - enum match_type match; - char *command; - vector matches = NULL; - vector match_vector; - uint32_t command_found = 0; - const char *last_word; - - /* Set index. */ - if (vector_active (vline) == 0) - { - *status = CMD_ERR_NO_MATCH; - return NULL; - } - - index = vector_active (vline) - 1; - - /* Make copy vector of current node's command vector. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); - - /* Prepare match vector */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - - /* Filter commands and build a list how they could possibly continue. */ - for (i = 0; i <= index; i++) - { - command = vector_slot (vline, i); - - if (matches) - cmd_matches_free(&matches); - - ret = cmd_vector_filter(cmd_vector, - FILTER_RELAXED, - vline, i, - &match, - &matches); - - if (ret != CMD_SUCCESS) - { - vector_free (cmd_vector); - vector_free (matchvec); - cmd_matches_free(&matches); - *status = ret; - return NULL; - } - - /* The last match may well be ambigious, so break here */ - if (i == index) - break; - - if (match == vararg_match) - { - /* We found a vararg match - so we can throw out the current matches here - * and don't need to continue checking the command input */ - unsigned int j, k; - - for (j = 0; j < vector_active (matches); j++) - if ((match_vector = vector_slot (matches, j)) != NULL) - for (k = 0; k < vector_active (match_vector); k++) - { - struct cmd_token *token = vector_slot (match_vector, k); - vector_set (matchvec, token); - } - - *status = CMD_SUCCESS; - vector_set(matchvec, &token_cr); - vector_free (cmd_vector); - cmd_matches_free(&matches); - cmd_describe_sort(matchvec); - return matchvec; - } - - ret = is_cmd_ambiguous(cmd_vector, command, matches, match); - if (ret == 1) - { - vector_free (cmd_vector); - vector_free (matchvec); - cmd_matches_free(&matches); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - else if (ret == 2) - { - vector_free (cmd_vector); - vector_free (matchvec); - cmd_matches_free(&matches); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - } - - /* Make description vector. */ - for (i = 0; i < vector_active (matches); i++) { - if ((cmd_element = vector_slot (cmd_vector, i)) != NULL && - !(cmd_element->attr == CMD_ATTR_DEPRECATED || - cmd_element->attr == CMD_ATTR_HIDDEN)) - { - unsigned int j; - vector vline_trimmed; - - command_found++; - last_word = vector_slot(vline, vector_active(vline) - 1); - if (last_word == NULL || !strlen(last_word)) - { - vline_trimmed = vector_copy(vline); - vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1); - - if (cmd_is_complete(cmd_element, vline_trimmed) - && desc_unique_string(matchvec, command_cr)) - { - if (match != vararg_match) - vector_set(matchvec, &token_cr); - } - - vector_free(vline_trimmed); - } - - match_vector = vector_slot (matches, i); - if (match_vector) - for (j = 0; j < vector_active(match_vector); j++) - { - struct cmd_token *token = vector_slot(match_vector, j); - const char *string; - - string = cmd_entry_function_desc(command, token); - if (string && desc_unique_string(matchvec, string)) - vector_set(matchvec, token); - } - } - } - - /* - * We can get into this situation when the command is complete - * but the last part of the command is an optional piece of - * cli. - */ - last_word = vector_slot(vline, vector_active(vline) - 1); - if (command_found == 0 && (last_word == NULL || !strlen(last_word))) { - vector_set(matchvec, &token_cr); - } - - vector_free (cmd_vector); - cmd_matches_free(&matches); - - if (vector_slot (matchvec, 0) == NULL) - { - vector_free (matchvec); - *status = CMD_ERR_NO_MATCH; - return NULL; + *status = CMD_ERR_NO_MATCH; } - - *status = CMD_SUCCESS; - cmd_describe_sort(matchvec); - return matchvec; -} - -vector -cmd_describe_command (vector vline, struct vty *vty, int *status) -{ - vector ret; - - if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) - { - enum node_type onode; - vector shifted_vline; - unsigned int index; - - onode = vty->node; - vty->node = ENABLE_NODE; - /* We can try it on enable node, cos' the vty is authenticated */ - - shifted_vline = vector_init (vector_count(vline)); - /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } - - ret = cmd_describe_command_real (shifted_vline, vty, status); - - vector_free(shifted_vline); - vty->node = onode; - return ret; + return NULL; } + vector comps = completions_to_vec (completions); + list_delete (completions); - return cmd_describe_command_real (vline, vty, status); -} - - -/* Check LCD of matched command. */ -static int -cmd_lcd (char **matched) -{ - int i; - int j; - int lcd = -1; - char *s1, *s2; - char c1, c2; - - if (matched[0] == NULL || matched[1] == NULL) - return 0; - - for (i = 1; matched[i] != NULL; i++) - { - s1 = matched[i - 1]; - s2 = matched[i]; - - for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) - if (c1 != c2) - break; - - if (lcd < 0) - lcd = j; - else - { - if (lcd > j) - lcd = j; - } - } - return lcd; -} - -static int -cmd_complete_cmp(const void *a, const void *b) -{ - const char *first = *(char * const *)a; - const char *second = *(char * const *)b; - - if (!first) - { - if (!second) - return 0; - return 1; - } - if (!second) - return -1; - - return strcmp(first, second); -} - -static void -cmd_complete_sort(vector matchvec) -{ - qsort(matchvec->index, vector_active(matchvec), - sizeof(void*), cmd_complete_cmp); -} - -/* Command line completion support. */ -static char ** -cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib) -{ - unsigned int i; - vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); -#define INIT_MATCHVEC_SIZE 10 - vector matchvec; - unsigned int index; - char **match_str; - struct cmd_token *token; - char *command; - int lcd; - vector matches = NULL; - vector match_vector; - - if (vector_active (vline) == 0) - { - vector_free (cmd_vector); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - else - index = vector_active (vline) - 1; - - /* First, filter by command string */ - for (i = 0; i <= index; i++) - { - command = vector_slot (vline, i); - enum match_type match; - int ret; - - if (matches) - cmd_matches_free(&matches); - - /* First try completion match, if there is exactly match return 1 */ - ret = cmd_vector_filter(cmd_vector, - FILTER_RELAXED, - vline, i, - &match, - &matches); - - if (ret != CMD_SUCCESS) - { - vector_free(cmd_vector); - cmd_matches_free(&matches); - *status = ret; - return NULL; - } - - /* Break here - the completion mustn't be checked to be non-ambiguous */ - if (i == index) - break; - - /* If there is exact match then filter ambiguous match else check - ambiguousness. */ - ret = is_cmd_ambiguous (cmd_vector, command, matches, match); - if (ret == 1) - { - vector_free (cmd_vector); - cmd_matches_free(&matches); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - } - - /* Prepare match vector. */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - - /* Build the possible list of continuations into a list of completions */ - for (i = 0; i < vector_active (matches); i++) - if ((match_vector = vector_slot (matches, i))) - { - const char *string; - unsigned int j; + // set status code appropriately + switch (vector_active (comps)) + { + case 0: + *status = CMD_ERR_NO_MATCH; + break; + case 1: + *status = CMD_COMPLETE_FULL_MATCH; + break; + default: + *status = CMD_COMPLETE_LIST_MATCH; + } - for (j = 0; j < vector_active (match_vector); j++) - if ((token = vector_slot (match_vector, j))) - { - string = cmd_entry_function (vector_slot (vline, index), - token); - if (string && cmd_unique_string (matchvec, string)) - vector_set (matchvec, (islib != 0 ? - XSTRDUP (MTYPE_TMP, string) : - strdup (string) /* rl freed */)); - } - } + return comps; +} - /* We don't need cmd_vector any more. */ - vector_free (cmd_vector); - cmd_matches_free(&matches); +vector +cmd_describe_command (vector vline, struct vty *vty, int *status) +{ + vector ret; - /* No matched command */ - if (vector_slot (matchvec, 0) == NULL) + if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) { - vector_free (matchvec); + enum node_type onode; + vector shifted_vline; + unsigned int index; - /* In case of 'command \t' pattern. Do you need '?' command at - the end of the line. */ - if (vector_slot (vline, index) == '\0') - *status = CMD_ERR_NOTHING_TODO; - else - *status = CMD_ERR_NO_MATCH; - return NULL; - } + onode = vty->node; + vty->node = ENABLE_NODE; + /* We can try it on enable node, cos' the vty is authenticated */ - /* Only one matched */ - if (vector_slot (matchvec, 1) == NULL) - { - match_str = (char **) matchvec->index; - vector_only_wrapper_free (matchvec); - *status = CMD_COMPLETE_FULL_MATCH; - return match_str; - } - /* Make it sure last element is NULL. */ - vector_set (matchvec, NULL); + shifted_vline = vector_init (vector_count(vline)); + /* use memcpy? */ + for (index = 1; index < vector_active (vline); index++) + { + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); + } - /* Check LCD of matched strings. */ - if (vector_slot (vline, index) != NULL) - { - lcd = cmd_lcd ((char **) matchvec->index); - - if (lcd) - { - int len = strlen (vector_slot (vline, index)); - - if (len < lcd) - { - char *lcdstr; - - lcdstr = (islib != 0 ? - XMALLOC (MTYPE_TMP, lcd + 1) : - malloc(lcd + 1)); - memcpy (lcdstr, matchvec->index[0], lcd); - lcdstr[lcd] = '\0'; - - /* Free matchvec. */ - for (i = 0; i < vector_active (matchvec); i++) - { - if (vector_slot (matchvec, i)) - { - if (islib != 0) - XFREE (MTYPE_TMP, vector_slot (matchvec, i)); - else - free (vector_slot (matchvec, i)); - } - } - vector_free (matchvec); - - /* Make new matchvec. */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - vector_set (matchvec, lcdstr); - match_str = (char **) matchvec->index; - vector_only_wrapper_free (matchvec); - - *status = CMD_COMPLETE_MATCH; - return match_str; - } - } - } + ret = cmd_complete_command_real (shifted_vline, vty, status); + + vector_free(shifted_vline); + vty->node = onode; + return ret; + } - match_str = (char **) matchvec->index; - cmd_complete_sort(matchvec); - vector_only_wrapper_free (matchvec); - *status = CMD_COMPLETE_LIST_MATCH; - return match_str; + return cmd_complete_command_real (vline, vty, status); } char ** @@ -2419,19 +599,41 @@ cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } + for (index = 1; index < vector_active (vline); index++) + { + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); + } - ret = cmd_complete_command_real (shifted_vline, vty, status, islib); + // get token completions + vector comps = cmd_complete_command_real (shifted_vline, vty, status); + ret = XMALLOC (MTYPE_TMP, vector_active (comps) * sizeof (char *)); + for (unsigned int i = 0; i < vector_active (comps); i++) + { + struct cmd_token *token = vector_slot (comps, i); + ret[i] = XSTRDUP (MTYPE_TMP, token->text); + vector_unset (comps, i); + del_cmd_token (token); + } + vector_free (comps); vector_free(shifted_vline); vty->node = onode; return ret; } - return cmd_complete_command_real (vline, vty, status, islib); + // get token completions + vector comps = cmd_complete_command_real (vline, vty, status); + ret = XMALLOC (MTYPE_TMP, vector_active (comps) * sizeof (char *)); + for (unsigned int i = 0; i < vector_active (comps); i++) + { + struct cmd_token *token = vector_slot (comps, i); + ret[i] = XSTRDUP (MTYPE_TMP, token->text); + vector_unset (comps, i); + del_cmd_token (token); + } + vector_free (comps); + + return ret; } char ** @@ -2474,109 +676,47 @@ node_parent ( enum node_type node ) /* Execute command by argument vline vector. */ static int cmd_execute_command_real (vector vline, - enum filter_type filter, - struct vty *vty, - struct cmd_element **cmd) -{ - unsigned int i; - unsigned int index; - vector cmd_vector; - struct cmd_element *cmd_element; - struct cmd_element *matched_element; - unsigned int matched_count, incomplete_count; - int argc; - const char *argv[CMD_ARGC_MAX]; - enum match_type match = 0; - char *command; - int ret; - vector matches; - - /* Make copy of command elements. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); - - for (index = 0; index < vector_active (vline); index++) - { - command = vector_slot (vline, index); - ret = cmd_vector_filter(cmd_vector, - filter, - vline, index, - &match, - &matches); - - if (ret != CMD_SUCCESS) - { - cmd_matches_free(&matches); - return ret; - } - - if (match == vararg_match) - { - cmd_matches_free(&matches); - break; - } - - ret = is_cmd_ambiguous (cmd_vector, command, matches, match); - cmd_matches_free(&matches); - - if (ret == 1) - { - vector_free(cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - else if (ret == 2) - { - vector_free(cmd_vector); - return CMD_ERR_NO_MATCH; - } - } - - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; - - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i))) - { - if (cmd_is_complete(cmd_element, vline)) - { - matched_element = cmd_element; - matched_count++; - } - else - { - incomplete_count++; - } - } - - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); - - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) - { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; + enum filter_type filter, + struct vty *vty, + struct cmd_element **cmd) +{ + struct list *argv_list; + enum matcher_rv status; + struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); + status = command_match (cmdgraph, vline, &argv_list, cmd); + + // if matcher error, return corresponding CMD_ERR + if (MATCHER_ERROR(status)) + switch (status) + { + case MATCHER_INCOMPLETE: + return CMD_ERR_INCOMPLETE; + case MATCHER_AMBIGUOUS: + return CMD_ERR_AMBIGUOUS; + default: + return CMD_ERR_NO_MATCH; } - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; + // build argv array from argv list + struct cmd_token **argv = XMALLOC (MTYPE_TMP, argv_list->count * sizeof (struct cmd_token *)); + struct listnode *ln; + struct cmd_token *token; + unsigned int i = 0; + for (ALL_LIST_ELEMENTS_RO(argv_list,ln,token)) + argv[i++] = token; - ret = cmd_parse(matched_element, vline, &argc, argv); - if (ret != CMD_SUCCESS) - return ret; + int argc = argv_list->count; - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; + int ret; + if ((*cmd)->daemon) + ret = CMD_SUCCESS_DAEMON; + else + ret = (*cmd)->func (*cmd, vty, argc, argv); - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; + // delete list and cmd_token's in it + list_delete (argv_list); - /* Execute matched command. */ - return (*matched_element->func) (matched_element, vty, argc, argv); + return ret; } /** @@ -2596,7 +736,7 @@ cmd_execute_command_real (vector vline, */ int cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, - int vtysh) { + int vtysh) { int ret, saved_ret = 0; enum node_type onode, try_node; @@ -2612,7 +752,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) + for (index = 1; index < vector_active (vline); index++) vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd); @@ -2662,7 +802,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, */ int cmd_execute_command_strict (vector vline, struct vty *vty, - struct cmd_element **cmd) + struct cmd_element **cmd) { return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); } @@ -2706,7 +846,7 @@ command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) && - ret != CMD_SUCCESS && + ret != CMD_SUCCESS && ret != CMD_WARNING && vty->node > CONFIG_NODE) { vty->node = node_parent(vty->node); @@ -2717,11 +857,11 @@ command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use // stay at the same node if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) && - ret != CMD_SUCCESS && + ret != CMD_SUCCESS && ret != CMD_WARNING) { - vty->node = saved_node; - memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ); + vty->node = saved_node; + memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ); } } @@ -2740,13 +880,13 @@ config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num) while (fgets (vty->buf, VTY_BUFSIZ, fp)) { if (!error_ret) - ++(*line_num); + ++(*line_num); ret = command_config_read_one_line (vty, NULL, 0); if (ret != CMD_SUCCESS && ret != CMD_WARNING && - ret != CMD_ERR_NOTHING_TODO) - error_ret = ret; + ret != CMD_ERR_NOTHING_TODO) + error_ret = ret; } if (error_ret) { @@ -2774,7 +914,7 @@ DEFUN (config_terminal, } /* Enable command */ -DEFUN (enable, +DEFUN (enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") @@ -2790,7 +930,7 @@ DEFUN (enable, } /* Disable command */ -DEFUN (disable, +DEFUN (disable, config_disable_cmd, "disable", "Turn off privileged mode command\n") @@ -2812,9 +952,9 @@ DEFUN (config_exit, case ENABLE_NODE: case RESTRICTED_NODE: if (vty_shell (vty)) - exit (0); + exit (0); else - vty->status = VTY_CLOSE; + vty->status = VTY_CLOSE; break; case CONFIG_NODE: vty->node = ENABLE_NODE; @@ -2860,7 +1000,7 @@ ALIAS (config_exit, config_quit_cmd, "quit", "Exit current mode and down to previous mode\n") - + /* End of configuration. */ DEFUN (config_end, config_end_cmd, @@ -2915,7 +1055,7 @@ DEFUN (show_version, "Displays zebra version\n") { vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", - VTY_NEWLINE); + VTY_NEWLINE); vty_out (vty, "%s%s%s", QUAGGA_COPYRIGHT, GIT_INFO, VTY_NEWLINE); vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE, QUAGGA_CONFIG_ARGS, VTY_NEWLINE); @@ -2929,8 +1069,8 @@ DEFUN (config_help, "help", "Description of the interactive help system\n") { - vty_out (vty, - "Quagga VTY provides advanced help feature. When you need help,%s\ + vty_out (vty, + "Quagga VTY provides advanced help feature. When you need help,%s\ anytime at the command line please press '?'.%s\ %s\ If nothing matches, the help list will be empty and you must backup%s\ @@ -2942,8 +1082,8 @@ argument.%s\ 2. Partial help is provided when an abbreviated argument is entered%s\ and you want to know what arguments match the input%s\ (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); return CMD_SUCCESS; } @@ -2962,16 +1102,18 @@ DEFUN (config_list, && !(cmd->attr == CMD_ATTR_DEPRECATED || cmd->attr == CMD_ATTR_HIDDEN)) vty_out (vty, " %s%s", cmd->string, - VTY_NEWLINE); + VTY_NEWLINE); return CMD_SUCCESS; } /* Write current configuration into file. */ -DEFUN (config_write_file, - config_write_file_cmd, - "write file", +DEFUN (config_write, + config_write_cmd, + "write []", "Write running configuration to memory, network, or terminal\n" - "Write to configuration file\n") + "Write to configuration file\n" + "Write configuration currently in memory\n" + "Write configuration to terminal\n") { unsigned int i; int fd; @@ -2983,17 +1125,47 @@ DEFUN (config_write_file, struct vty *file_vty; struct stat conf_stat; + // if command was 'write terminal' or 'show running-config' + if (argc == 2 && (!strcmp(argv[1]->arg, "terminal") || + !strcmp(argv[1]->arg, "running-config"))) + { + if (vty->type == VTY_SHELL_SERV) + { + for (i = 0; i < vector_active (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + } + else + { + vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, + VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + + for (i = 0; i < vector_active (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + vty_out (vty, "end%s",VTY_NEWLINE); + } + return CMD_SUCCESS; + } + /* Check and see if we are operating under vtysh configuration */ if (host.config == NULL) { vty_out (vty, "Can't save to configuration file, using vtysh.%s", - VTY_NEWLINE); + VTY_NEWLINE); return CMD_WARNING; } /* Get filename. */ config_file = host.config; - + config_file_sav = XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1); strcpy (config_file_sav, config_file); @@ -3002,16 +1174,16 @@ DEFUN (config_write_file, config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8); sprintf (config_file_tmp, "%s.XXXXXX", config_file); - + /* Open file to configuration write. */ fd = mkstemp (config_file_tmp); if (fd < 0) { vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp, - VTY_NEWLINE); + VTY_NEWLINE); goto finished; } - + /* Make vty for configuration file. */ file_vty = vty_new (); file_vty->wfd = fd; @@ -3025,51 +1197,51 @@ DEFUN (config_write_file, for (i = 0; i < vector_active (cmdvec); i++) if ((node = vector_slot (cmdvec, i)) && node->func) { - if ((*node->func) (file_vty)) - vty_out (file_vty, "!\n"); + if ((*node->func) (file_vty)) + vty_out (file_vty, "!\n"); } vty_close (file_vty); if (stat(config_file, &conf_stat) >= 0) { if (unlink (config_file_sav) != 0) - if (errno != ENOENT) - { - vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, - VTY_NEWLINE); - goto finished; - } + if (errno != ENOENT) + { + vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + goto finished; + } if (link (config_file, config_file_sav) != 0) - { - vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, - VTY_NEWLINE); - goto finished; - } + { + vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + goto finished; + } sync (); if (unlink (config_file) != 0) - { - vty_out (vty, "Can't unlink configuration file %s.%s", config_file, - VTY_NEWLINE); - goto finished; - } + { + vty_out (vty, "Can't unlink configuration file %s.%s", config_file, + VTY_NEWLINE); + goto finished; + } } if (link (config_file_tmp, config_file) != 0) { vty_out (vty, "Can't save configuration file %s.%s", config_file, - VTY_NEWLINE); + VTY_NEWLINE); goto finished; } sync (); - + if (chmod (config_file, CONFIGFILE_MASK) != 0) { - vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s", - config_file, safe_strerror(errno), errno, VTY_NEWLINE); + vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s", + config_file, safe_strerror(errno), errno, VTY_NEWLINE); goto finished; } vty_out (vty, "Configuration saved to %s%s", config_file, - VTY_NEWLINE); + VTY_NEWLINE); ret = CMD_SUCCESS; finished: @@ -3079,62 +1251,15 @@ finished: return ret; } -ALIAS (config_write_file, - config_write_cmd, - "write", - "Write running configuration to memory, network, or terminal\n") - -ALIAS (config_write_file, - config_write_memory_cmd, - "write memory", - "Write running configuration to memory, network, or terminal\n" - "Write configuration to the file (same as write file)\n") - -ALIAS (config_write_file, +ALIAS (config_write, copy_runningconfig_startupconfig_cmd, - "copy running-config startup-config", + "copy running-config startup-config", "Copy configuration\n" "Copy running config to... \n" "Copy running config to startup config (same as write file)\n") /* Write current configuration into the terminal. */ -DEFUN (config_write_terminal, - config_write_terminal_cmd, - "write terminal", - "Write running configuration to memory, network, or terminal\n" - "Write to terminal\n") -{ - unsigned int i; - struct cmd_node *node; - - if (vty->type == VTY_SHELL_SERV) - { - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) - { - if ((*node->func) (vty)) - vty_out (vty, "!%s", VTY_NEWLINE); - } - } - else - { - vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, - VTY_NEWLINE); - vty_out (vty, "!%s", VTY_NEWLINE); - - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func) - { - if ((*node->func) (vty)) - vty_out (vty, "!%s", VTY_NEWLINE); - } - vty_out (vty, "end%s",VTY_NEWLINE); - } - return CMD_SUCCESS; -} - -/* Write current configuration into the terminal. */ -ALIAS (config_write_terminal, +ALIAS (config_write, show_running_config_cmd, "show running-config", SHOW_STR @@ -3145,7 +1270,7 @@ DEFUN (show_startup_config, show_startup_config_cmd, "show startup-config", SHOW_STR - "Contentes of startup configuration\n") + "Contents of startup configuration\n") { char buf[BUFSIZ]; FILE *confp; @@ -3154,7 +1279,7 @@ DEFUN (show_startup_config, if (confp == NULL) { vty_out (vty, "Can't open configuration file [%s] due to '%s'%s", - host.config, safe_strerror(errno), VTY_NEWLINE); + host.config, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3163,7 +1288,7 @@ DEFUN (show_startup_config, char *cp = buf; while (*cp != '\r' && *cp != '\n' && *cp != '\0') - cp++; + cp++; *cp = '\0'; vty_out (vty, "%s%s", buf, VTY_NEWLINE); @@ -3175,13 +1300,15 @@ DEFUN (show_startup_config, } /* Hostname configuration */ -DEFUN (config_hostname, +DEFUN (config_hostname, hostname_cmd, "hostname WORD", "Set system's network name\n" "This system's network name\n") { - if (!isalpha((int) *argv[0])) + struct cmd_token *word = argv[1]; + + if (!isalpha((int) word->arg[0])) { vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE); return CMD_WARNING; @@ -3189,12 +1316,12 @@ DEFUN (config_hostname, if (host.name) XFREE (MTYPE_HOST, host.name); - - host.name = XSTRDUP (MTYPE_HOST, argv[0]); + + host.name = XSTRDUP (MTYPE_HOST, word->arg); return CMD_SUCCESS; } -DEFUN (config_no_hostname, +DEFUN (config_no_hostname, no_hostname_cmd, "no hostname [HOSTNAME]", NO_STR @@ -3209,42 +1336,26 @@ DEFUN (config_no_hostname, /* VTY interface password set. */ DEFUN (config_password, password_cmd, - "password (8|) WORD", + "password [8] WORD", "Assign the terminal connection password\n" "Specifies a HIDDEN password will follow\n" - "dummy string \n" - "The HIDDEN line password string\n") + "The password string\n") { - /* Argument check. */ - if (argc == 0) - { - vty_out (vty, "Please specify password.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (argc == 2) - { - if (*argv[0] == '8') - { - if (host.password) - XFREE (MTYPE_HOST, host.password); - host.password = NULL; - if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); - return CMD_SUCCESS; - } - else - { - vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } - } + if (argc == 3) // '8' was specified + { + if (host.password) + XFREE (MTYPE_HOST, host.password); + host.password = NULL; + if (host.password_encrypt) + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[2]->arg); + return CMD_SUCCESS; + } - if (!isalnum ((int) *argv[0])) + if (!isalnum (argv[1]->arg[0])) { - vty_out (vty, - "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); return CMD_WARNING; } @@ -3255,62 +1366,50 @@ DEFUN (config_password, password_cmd, if (host.encrypt) { if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[1]->arg)); } else - host.password = XSTRDUP (MTYPE_HOST, argv[0]); + host.password = XSTRDUP (MTYPE_HOST, argv[1]->arg); return CMD_SUCCESS; } -ALIAS (config_password, password_text_cmd, - "password LINE", - "Assign the terminal connection password\n" - "The UNENCRYPTED (cleartext) line password\n") - /* VTY enable password set. */ DEFUN (config_enable_password, enable_password_cmd, - "enable password (8|) WORD", + "enable password [8] WORD", "Modify enable password parameters\n" "Assign the privileged level password\n" "Specifies a HIDDEN password will follow\n" "dummy string \n" "The HIDDEN 'enable' password string\n") { - /* Argument check. */ - if (argc == 0) - { - vty_out (vty, "Please specify password.%s", VTY_NEWLINE); - return CMD_WARNING; - } - /* Crypt type is specified. */ - if (argc == 2) + if (argc == 4) { - if (*argv[0] == '8') - { - if (host.enable) - XFREE (MTYPE_HOST, host.enable); - host.enable = NULL; - - if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); - - return CMD_SUCCESS; - } + if (argv[2]->arg[0] == '8') + { + if (host.enable) + XFREE (MTYPE_HOST, host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[3]->arg); + + return CMD_SUCCESS; + } else - { - vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } + { + vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } } - if (!isalnum ((int) *argv[0])) + if (!isalnum (argv[2]->arg[0])) { - vty_out (vty, - "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); return CMD_WARNING; } @@ -3322,22 +1421,15 @@ DEFUN (config_enable_password, enable_password_cmd, if (host.encrypt) { if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[2]->arg)); } else - host.enable = XSTRDUP (MTYPE_HOST, argv[0]); + host.enable = XSTRDUP (MTYPE_HOST, argv[2]->arg); return CMD_SUCCESS; } -ALIAS (config_enable_password, - enable_password_text_cmd, - "enable password LINE", - "Modify enable password parameters\n" - "Assign the privileged level password\n" - "The UNENCRYPTED (cleartext) 'enable' password\n") - /* VTY enable password delete. */ DEFUN (no_config_enable_password, no_enable_password_cmd, "no enable password", @@ -3355,7 +1447,7 @@ DEFUN (no_config_enable_password, no_enable_password_cmd, return CMD_SUCCESS; } - + DEFUN (service_password_encrypt, service_password_encrypt_cmd, "service password-encryption", @@ -3370,13 +1462,13 @@ DEFUN (service_password_encrypt, if (host.password) { if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); + XFREE (MTYPE_HOST, host.password_encrypt); host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password)); } if (host.enable) { if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); + XFREE (MTYPE_HOST, host.enable_encrypt); host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable)); } @@ -3407,7 +1499,7 @@ DEFUN (no_service_password_encrypt, } DEFUN (config_terminal_length, config_terminal_length_cmd, - "terminal length <0-512>", + "terminal length (0-512)", "Set terminal line parameters\n" "Set number of lines on a screen\n" "Number of lines on screen (0 for no pausing)\n") @@ -3415,7 +1507,7 @@ DEFUN (config_terminal_length, config_terminal_length_cmd, int lines; char *endptr = NULL; - lines = strtol (argv[0], &endptr, 10); + lines = strtol (argv[2]->arg, &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') { vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3437,7 +1529,7 @@ DEFUN (config_terminal_no_length, config_terminal_no_length_cmd, } DEFUN (service_terminal_length, service_terminal_length_cmd, - "service terminal-length <0-512>", + "service terminal-length (0-512)", "Set up miscellaneous service\n" "System wide terminal length configuration\n" "Number of lines of VTY (0 means no line control)\n") @@ -3445,7 +1537,7 @@ DEFUN (service_terminal_length, service_terminal_length_cmd, int lines; char *endptr = NULL; - lines = strtol (argv[0], &endptr, 10); + lines = strtol (argv[2]->arg, &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') { vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3457,7 +1549,7 @@ DEFUN (service_terminal_length, service_terminal_length_cmd, } DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, - "no service terminal-length [<0-512>]", + "no service terminal-length [(0-512)]", NO_STR "Set up miscellaneous service\n" "System wide terminal length configuration\n" @@ -3468,15 +1560,15 @@ DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, } DEFUN_HIDDEN (do_echo, - echo_cmd, - "echo .MESSAGE", - "Echo a message back to the vty\n" - "The message to echo\n") + echo_cmd, + "echo MESSAGE...", + "Echo a message back to the vty\n" + "The message to echo\n") { char *message; - vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""), - VTY_NEWLINE); + vty_out (vty, "%s%s", ((message = argv_concat (argv, argc, 0)) ? message : ""), + VTY_NEWLINE); if (message) XFREE(MTYPE_TMP, message); return CMD_SUCCESS; @@ -3484,7 +1576,7 @@ DEFUN_HIDDEN (do_echo, DEFUN (config_logmsg, config_logmsg_cmd, - "logmsg "LOG_LEVELS" .MESSAGE", + "logmsg "LOG_LEVELS" MESSAGE...", "Send a message to enabled logging destinations\n" LOG_LEVEL_DESC "The message to send\n") @@ -3492,7 +1584,7 @@ DEFUN (config_logmsg, int level; char *message; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[1]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : "")); @@ -3514,8 +1606,8 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s, facility %s, ident %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], - facility_name(zl->facility), zl->ident); + zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], + facility_name(zl->facility), zl->ident); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Stdout logging: "); @@ -3523,7 +1615,7 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); + zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Monitor logging: "); @@ -3531,7 +1623,7 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); + zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "File logging: "); @@ -3540,40 +1632,35 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s, filename %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], - zl->filename); + zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], + zl->filename); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Protocol name: %s%s", - zlog_proto_names[zl->protocol], VTY_NEWLINE); + zlog_proto_names[zl->protocol], VTY_NEWLINE); vty_out (vty, "Record priority: %s%s", - (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); + (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); vty_out (vty, "Timestamp precision: %d%s", - zl->timestamp_precision, VTY_NEWLINE); + zl->timestamp_precision, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN (config_log_stdout, config_log_stdout_cmd, - "log stdout", - "Logging control\n" - "Set stdout logging level\n") -{ - zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN (config_log_stdout_level, - config_log_stdout_level_cmd, - "log stdout "LOG_LEVELS, + "log stdout ["LOG_LEVELS"]", "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC) { + if (argc == 2) + { + zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); + return CMD_SUCCESS; + } int level; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[2]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog_set_level (NULL, ZLOG_DEST_STDOUT, level); return CMD_SUCCESS; @@ -3581,11 +1668,11 @@ DEFUN (config_log_stdout_level, DEFUN (no_config_log_stdout, no_config_log_stdout_cmd, - "no log stdout [LEVEL]", + "no log stdout ["LOG_LEVELS"]", NO_STR "Logging control\n" "Cancel logging to stdout\n" - "Logging level\n") + LOG_LEVEL_DESC) { zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); return CMD_SUCCESS; @@ -3593,24 +1680,19 @@ DEFUN (no_config_log_stdout, DEFUN (config_log_monitor, config_log_monitor_cmd, - "log monitor", - "Logging control\n" - "Set terminal line (monitor) logging level\n") -{ - zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN (config_log_monitor_level, - config_log_monitor_level_cmd, - "log monitor "LOG_LEVELS, + "log monitor ["LOG_LEVELS"]", "Logging control\n" "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) { + if (argc == 2) + { + zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); + return CMD_SUCCESS; + } int level; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[2]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog_set_level (NULL, ZLOG_DEST_MONITOR, level); return CMD_SUCCESS; @@ -3618,11 +1700,11 @@ DEFUN (config_log_monitor_level, DEFUN (no_config_log_monitor, no_config_log_monitor_cmd, - "no log monitor [LEVEL]", + "no log monitor ["LOG_LEVELS"]", NO_STR "Logging control\n" "Disable terminal line (monitor) logging\n" - "Logging level\n") + LOG_LEVEL_DESC) { zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); return CMD_SUCCESS; @@ -3634,19 +1716,19 @@ set_log_file(struct vty *vty, const char *fname, int loglevel) int ret; char *p = NULL; const char *fullpath; - + /* Path detection. */ if (! IS_DIRECTORY_SEP (*fname)) { char cwd[MAXPATHLEN+1]; cwd[MAXPATHLEN] = '\0'; - + if (getcwd (cwd, MAXPATHLEN) == NULL) { zlog_err ("config_log_file: Unable to alloc mem!"); return CMD_WARNING; } - + if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2)) == NULL) { @@ -3680,36 +1762,32 @@ set_log_file(struct vty *vty, const char *fname, int loglevel) DEFUN (config_log_file, config_log_file_cmd, - "log file FILENAME", - "Logging control\n" - "Logging to file\n" - "Logging filename\n") -{ - return set_log_file(vty, argv[0], zlog_default->default_lvl); -} - -DEFUN (config_log_file_level, - config_log_file_level_cmd, - "log file FILENAME "LOG_LEVELS, + "log file FILENAME [" LOG_LEVELS "]", "Logging control\n" "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC) { - int level; - - if ((level = level_match(argv[1])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - return set_log_file(vty, argv[0], level); + if (argc == 4) + { + int level; + if ((level = level_match(argv[3]->arg)) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + return set_log_file(vty, argv[2]->arg, level); + } + else + return set_log_file(vty, argv[2]->arg, zlog_default->default_lvl); } DEFUN (no_config_log_file, no_config_log_file_cmd, - "no log file [FILENAME]", + "no log file [FILENAME [LEVEL]]", NO_STR "Logging control\n" "Cancel logging to file\n" - "Logging file name\n") + "Logging file name\n" + "Logging file name\n" + "Logging level\n") { zlog_reset_file (NULL); @@ -3721,52 +1799,37 @@ DEFUN (no_config_log_file, return CMD_SUCCESS; } -ALIAS (no_config_log_file, - no_config_log_file_level_cmd, - "no log file FILENAME LEVEL", - NO_STR - "Logging control\n" - "Cancel logging to file\n" - "Logging file name\n" - "Logging level\n") - DEFUN (config_log_syslog, config_log_syslog_cmd, - "log syslog", - "Logging control\n" - "Set syslog logging level\n") -{ - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN (config_log_syslog_level, - config_log_syslog_level_cmd, - "log syslog "LOG_LEVELS, + "log syslog [" LOG_LEVELS "]", "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC) { - int level; - - if ((level = level_match(argv[0])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level); - return CMD_SUCCESS; + if (argc == 3) + { + int level; + if ((level = level_match (argv[2]->arg)) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level); + return CMD_SUCCESS; + } + else + { + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + return CMD_SUCCESS; + } } DEFUN_DEPRECATED (config_log_syslog_facility, - config_log_syslog_facility_cmd, - "log syslog facility "LOG_FACILITIES, - "Logging control\n" - "Logging goes to syslog\n" - "(Deprecated) Facility parameter for syslog messages\n" - LOG_FACILITY_DESC) + config_log_syslog_facility_cmd, + "log syslog facility "LOG_FACILITIES, + "Logging control\n" + "Logging goes to syslog\n" + "(Deprecated) Facility parameter for syslog messages\n" + LOG_FACILITY_DESC) { - int facility; - - if ((facility = facility_match(argv[0])) < 0) - return CMD_ERR_NO_MATCH; + int facility = facility_match(argv[3]->arg); zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); zlog_default->facility = facility; @@ -3775,25 +1838,17 @@ DEFUN_DEPRECATED (config_log_syslog_facility, DEFUN (no_config_log_syslog, no_config_log_syslog_cmd, - "no log syslog [LEVEL]", + "no log syslog [" LOG_FACILITIES "] ["LOG_LEVELS"]", NO_STR "Logging control\n" "Cancel logging to syslog\n" - "Logging level\n") + LOG_FACILITY_DESC + LOG_LEVEL_DESC) { zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); return CMD_SUCCESS; } -ALIAS (no_config_log_syslog, - no_config_log_syslog_facility_cmd, - "no log syslog facility "LOG_FACILITIES, - NO_STR - "Logging control\n" - "Logging goes to syslog\n" - "Facility parameter for syslog messages\n" - LOG_FACILITY_DESC) - DEFUN (config_log_facility, config_log_facility_cmd, "log facility "LOG_FACILITIES, @@ -3801,37 +1856,35 @@ DEFUN (config_log_facility, "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) { - int facility; + int facility = facility_match(argv[2]->arg); - if ((facility = facility_match(argv[0])) < 0) - return CMD_ERR_NO_MATCH; zlog_default->facility = facility; return CMD_SUCCESS; } DEFUN (no_config_log_facility, no_config_log_facility_cmd, - "no log facility [FACILITY]", + "no log facility ["LOG_FACILITIES"]", NO_STR "Logging control\n" "Reset syslog facility to default (daemon)\n" - "Syslog facility\n") + LOG_FACILITY_DESC) { zlog_default->facility = LOG_DAEMON; return CMD_SUCCESS; } DEFUN_DEPRECATED (config_log_trap, - config_log_trap_cmd, - "log trap "LOG_LEVELS, - "Logging control\n" - "(Deprecated) Set logging level and default for all destinations\n" - LOG_LEVEL_DESC) + config_log_trap_cmd, + "log trap "LOG_LEVELS, + "Logging control\n" + "(Deprecated) Set logging level and default for all destinations\n" + LOG_LEVEL_DESC) { int new_level ; int i; - - if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) + + if ((new_level = level_match(argv[2]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog_default->default_lvl = new_level; @@ -3842,12 +1895,12 @@ DEFUN_DEPRECATED (config_log_trap, } DEFUN_DEPRECATED (no_config_log_trap, - no_config_log_trap_cmd, - "no log trap [LEVEL]", - NO_STR - "Logging control\n" - "Permit all logging information\n" - "Logging level\n") + no_config_log_trap_cmd, + "no log trap ["LOG_LEVELS"]", + NO_STR + "Logging control\n" + "Permit all logging information\n" + LOG_LEVEL_DESC) { zlog_default->default_lvl = LOG_DEBUG; return CMD_SUCCESS; @@ -3882,14 +1935,8 @@ DEFUN (config_log_timestamp_precision, "Set the timestamp precision\n" "Number of subsecond digits\n") { - if (argc != 1) - { - vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); - return CMD_WARNING; - } - VTY_GET_INTEGER_RANGE("Timestamp Precision", - zlog_default->timestamp_precision, argv[0], 0, 6); + zlog_default->timestamp_precision, argv[3]->arg, 0, 6); return CMD_SUCCESS; } @@ -3923,7 +1970,7 @@ DEFUN (banner_motd_file, "Banner from a file\n" "Filename\n") { - return cmd_banner_motd_file (argv[0]); + return cmd_banner_motd_file (argv[3]->arg); } DEFUN (banner_motd_default, @@ -3945,7 +1992,7 @@ DEFUN (no_banner_motd, "Strings for motd\n") { host.motd = NULL; - if (host.motdfile) + if (host.motdfile) XFREE (MTYPE_HOST, host.motdfile); host.motdfile = NULL; return CMD_SUCCESS; @@ -4000,9 +2047,6 @@ install_default (enum node_type node) install_element (node, &config_help_cmd); install_element (node, &config_list_cmd); - install_element (node, &config_write_terminal_cmd); - install_element (node, &config_write_file_cmd); - install_element (node, &config_write_memory_cmd); install_element (node, &config_write_cmd); install_element (node, &show_running_config_cmd); } @@ -4011,12 +2055,6 @@ install_default (enum node_type node) void cmd_init (int terminal) { - command_cr = XSTRDUP(MTYPE_CMD_TOKENS, ""); - token_cr.type = TOKEN_TERMINAL; - token_cr.terminal = TERMINAL_LITERAL; - token_cr.cmd = command_cr; - token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); - /* Allocate initial top vector of commands. */ cmdvec = vector_init (VECTOR_MIN_SIZE); @@ -4085,33 +2123,24 @@ cmd_init (int terminal) install_default (CONFIG_NODE); } - + install_element (CONFIG_NODE, &hostname_cmd); install_element (CONFIG_NODE, &no_hostname_cmd); if (terminal) { install_element (CONFIG_NODE, &password_cmd); - install_element (CONFIG_NODE, &password_text_cmd); install_element (CONFIG_NODE, &enable_password_cmd); - install_element (CONFIG_NODE, &enable_password_text_cmd); install_element (CONFIG_NODE, &no_enable_password_cmd); install_element (CONFIG_NODE, &config_log_stdout_cmd); - install_element (CONFIG_NODE, &config_log_stdout_level_cmd); install_element (CONFIG_NODE, &no_config_log_stdout_cmd); install_element (CONFIG_NODE, &config_log_monitor_cmd); - install_element (CONFIG_NODE, &config_log_monitor_level_cmd); install_element (CONFIG_NODE, &no_config_log_monitor_cmd); install_element (CONFIG_NODE, &config_log_file_cmd); - install_element (CONFIG_NODE, &config_log_file_level_cmd); install_element (CONFIG_NODE, &no_config_log_file_cmd); - install_element (CONFIG_NODE, &no_config_log_file_level_cmd); install_element (CONFIG_NODE, &config_log_syslog_cmd); - install_element (CONFIG_NODE, &config_log_syslog_level_cmd); - install_element (CONFIG_NODE, &config_log_syslog_facility_cmd); install_element (CONFIG_NODE, &no_config_log_syslog_cmd); - install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd); install_element (CONFIG_NODE, &config_log_facility_cmd); install_element (CONFIG_NODE, &no_config_log_facility_cmd); install_element (CONFIG_NODE, &config_log_trap_cmd); @@ -4131,7 +2160,7 @@ cmd_init (int terminal) install_element (VIEW_NODE, &show_thread_cpu_cmd); install_element (ENABLE_NODE, &show_thread_cpu_cmd); install_element (RESTRICTED_NODE, &show_thread_cpu_cmd); - + install_element (ENABLE_NODE, &clear_thread_cpu_cmd); install_element (VIEW_NODE, &show_work_queues_cmd); install_element (ENABLE_NODE, &show_work_queues_cmd); @@ -4142,62 +2171,54 @@ cmd_init (int terminal) srandom(time(NULL)); } -static void -cmd_terminate_token(struct cmd_token *token) +struct cmd_token * +new_cmd_token (enum cmd_token_type type, char *text, char *desc) { - unsigned int i, j; - vector keyword_vect; + struct cmd_token *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token)); + token->type = type; + token->text = text; + token->desc = desc; + token->arg = NULL; - if (token->multiple) - { - for (i = 0; i < vector_active(token->multiple); i++) - cmd_terminate_token(vector_slot(token->multiple, i)); - vector_free(token->multiple); - token->multiple = NULL; - } + return token; +} - if (token->keyword) - { - for (i = 0; i < vector_active(token->keyword); i++) - { - keyword_vect = vector_slot(token->keyword, i); - for (j = 0; j < vector_active(keyword_vect); j++) - cmd_terminate_token(vector_slot(keyword_vect, j)); - vector_free(keyword_vect); - } - vector_free(token->keyword); - token->keyword = NULL; - } +void +del_cmd_token (struct cmd_token *token) +{ + if (!token) return; - XFREE(MTYPE_CMD_TOKENS, token->cmd); - XFREE(MTYPE_CMD_TOKENS, token->desc); + if (token->text) + XFREE (MTYPE_CMD_TOKENS, token->text); + if (token->desc) + XFREE (MTYPE_CMD_TOKENS, token->desc); + if (token->arg) + XFREE (MTYPE_CMD_TOKENS, token->arg); - XFREE(MTYPE_CMD_TOKENS, token); + XFREE (MTYPE_CMD_TOKENS, token); } -static void -cmd_terminate_element(struct cmd_element *cmd) +struct cmd_token * +copy_cmd_token (struct cmd_token *token) { - unsigned int i; - - if (cmd->tokens == NULL) - return; - - for (i = 0; i < vector_active(cmd->tokens); i++) - cmd_terminate_token(vector_slot(cmd->tokens, i)); + struct cmd_token *copy = new_cmd_token (token->type, NULL, NULL); + copy->value = token->value; + copy->max = token->max; + copy->min = token->min; + copy->text = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL; + copy->desc = token->desc ? XSTRDUP (MTYPE_CMD_TOKENS, token->desc) : NULL; + copy->arg = token->arg ? XSTRDUP (MTYPE_CMD_TOKENS, token->arg) : NULL; - vector_free(cmd->tokens); - cmd->tokens = NULL; + return copy; } void del_cmd_element(struct cmd_element *cmd) { if (!cmd) return; - free ((char*) cmd->string); - free ((char*) cmd->doc); - cmd_terminate_element(cmd); - free (cmd); + XFREE (MTYPE_CMD_TOKENS, cmd->string); + XFREE (MTYPE_CMD_TOKENS, cmd->doc); + XFREE (MTYPE_CMD_TOKENS, cmd); } struct cmd_element * @@ -4208,7 +2229,6 @@ copy_cmd_element(struct cmd_element *cmd) el->func = cmd->func; el->doc = cmd->doc ? XSTRDUP(MTYPE_CMD_TOKENS, cmd->doc) : NULL; el->daemon = cmd->daemon; - el->tokens = cmd->tokens ? vector_copy(cmd->tokens) : NULL; el->attr = cmd->attr; return el; } @@ -4216,33 +2236,22 @@ copy_cmd_element(struct cmd_element *cmd) void cmd_terminate () { - unsigned int i, j; struct cmd_node *cmd_node; - struct cmd_element *cmd_element; - vector cmd_node_v; if (cmdvec) { - for (i = 0; i < vector_active (cmdvec); i++) + for (unsigned int i = 0; i < vector_active (cmdvec); i++) if ((cmd_node = vector_slot (cmdvec, i)) != NULL) - { - cmd_node_v = cmd_node->cmd_vector; - - for (j = 0; j < vector_active (cmd_node_v); j++) - if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL) - cmd_terminate_element(cmd_element); - - vector_free (cmd_node_v); - } + { + // deleting the graph delets the cmd_element as well + graph_delete_graph (cmd_node->cmdgraph); + vector_free (cmd_node->cmd_vector); + } vector_free (cmdvec); cmdvec = NULL; } - if (command_cr) - XFREE(MTYPE_CMD_TOKENS, command_cr); - if (token_cr.desc) - XFREE(MTYPE_CMD_TOKENS, token_cr.desc); if (host.name) XFREE (MTYPE_HOST, host.name); if (host.password) diff --git a/lib/command.h b/lib/command.h index 6d0fb18061..786539034b 100644 --- a/lib/command.h +++ b/lib/command.h @@ -8,7 +8,7 @@ * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your * option) any later version. - * + * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @@ -26,6 +26,7 @@ #include "vector.h" #include "vty.h" #include "lib/route_types.h" +#include "graph.h" /* Host configuration variable */ struct host @@ -60,133 +61,136 @@ struct host }; /* There are some command levels which called from command node. */ -enum node_type +enum node_type { - AUTH_NODE, /* Authentication mode of vty interface. */ - RESTRICTED_NODE, /* Restricted view mode */ - VIEW_NODE, /* View node. Default mode of vty interface. */ - AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ - ENABLE_NODE, /* Enable node. */ - CONFIG_NODE, /* Config node. Default mode of config file. */ - SERVICE_NODE, /* Service node. */ - DEBUG_NODE, /* Debug node. */ + AUTH_NODE, /* Authentication mode of vty interface. */ + RESTRICTED_NODE, /* Restricted view mode */ + VIEW_NODE, /* View node. Default mode of vty interface. */ + AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ + ENABLE_NODE, /* Enable node. */ + CONFIG_NODE, /* Config node. Default mode of config file. */ + SERVICE_NODE, /* Service node. */ + DEBUG_NODE, /* Debug node. */ VRF_DEBUG_NODE, /* Vrf Debug node. */ - AAA_NODE, /* AAA node. */ - KEYCHAIN_NODE, /* Key-chain node. */ - KEYCHAIN_KEY_NODE, /* Key-chain key node. */ - VRF_NODE, /* VRF mode node. */ - INTERFACE_NODE, /* Interface mode node. */ - ZEBRA_NODE, /* zebra connection node. */ - TABLE_NODE, /* rtm_table selection node. */ - RIP_NODE, /* RIP protocol mode node. */ - RIPNG_NODE, /* RIPng protocol mode node. */ - BGP_NODE, /* BGP protocol mode which includes BGP4+ */ - BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ - BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ - BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ - BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ - BGP_IPV6_NODE, /* BGP IPv6 address family */ - BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ - BGP_ENCAP_NODE, /* BGP ENCAP SAFI */ - BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ - OSPF_NODE, /* OSPF protocol mode */ - OSPF6_NODE, /* OSPF protocol for IPv6 mode */ - ISIS_NODE, /* ISIS protocol mode */ - PIM_NODE, /* PIM protocol mode */ - MASC_NODE, /* MASC for multicast. */ - IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ - IP_NODE, /* Static ip route node. */ - ACCESS_NODE, /* Access list node. */ - PREFIX_NODE, /* Prefix list node. */ - ACCESS_IPV6_NODE, /* Access list node. */ - PREFIX_IPV6_NODE, /* Prefix list node. */ - AS_LIST_NODE, /* AS list node. */ - COMMUNITY_LIST_NODE, /* Community list node. */ - RMAP_NODE, /* Route map node. */ - SMUX_NODE, /* SNMP configuration node. */ - DUMP_NODE, /* Packet dump node. */ - FORWARDING_NODE, /* IP forwarding node. */ + AAA_NODE, /* AAA node. */ + KEYCHAIN_NODE, /* Key-chain node. */ + KEYCHAIN_KEY_NODE, /* Key-chain key node. */ + VRF_NODE, /* VRF mode node. */ + INTERFACE_NODE, /* Interface mode node. */ + ZEBRA_NODE, /* zebra connection node. */ + TABLE_NODE, /* rtm_table selection node. */ + RIP_NODE, /* RIP protocol mode node. */ + RIPNG_NODE, /* RIPng protocol mode node. */ + BGP_NODE, /* BGP protocol mode which includes BGP4+ */ + BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ + BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ + BGP_IPV6_NODE, /* BGP IPv6 address family */ + BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ + BGP_ENCAP_NODE, /* BGP ENCAP SAFI */ + BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ + OSPF_NODE, /* OSPF protocol mode */ + OSPF6_NODE, /* OSPF protocol for IPv6 mode */ + ISIS_NODE, /* ISIS protocol mode */ + PIM_NODE, /* PIM protocol mode */ + MASC_NODE, /* MASC for multicast. */ + IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ + IP_NODE, /* Static ip route node. */ + ACCESS_NODE, /* Access list node. */ + PREFIX_NODE, /* Prefix list node. */ + ACCESS_IPV6_NODE, /* Access list node. */ + PREFIX_IPV6_NODE, /* Prefix list node. */ + AS_LIST_NODE, /* AS list node. */ + COMMUNITY_LIST_NODE, /* Community list node. */ + RMAP_NODE, /* Route map node. */ + SMUX_NODE, /* SNMP configuration node. */ + DUMP_NODE, /* Packet dump node. */ + FORWARDING_NODE, /* IP forwarding node. */ PROTOCOL_NODE, /* protocol filtering node */ - VTY_NODE, /* Vty node. */ + VTY_NODE, /* Vty node. */ }; /* Node which has some commands and prompt string and configuration function pointer . */ -struct cmd_node +struct cmd_node { /* Node index. */ - enum node_type node; + enum node_type node; /* Prompt character at vty interface. */ - const char *prompt; + const char *prompt; /* Is this node's configuration goes to vtysh ? */ int vtysh; - + /* Node's configuration write function */ int (*func) (struct vty *); + /* Node's command graph */ + struct graph *cmdgraph; + /* Vector of this node's command list. */ - vector cmd_vector; + vector cmd_vector; }; -enum +/** + * Types for tokens. + * + * The type determines what kind of data the token can match (in the + * matching use case) or hold (in the argv use case). + */ +enum cmd_token_type { - CMD_ATTR_DEPRECATED = 1, - CMD_ATTR_HIDDEN, + WORD_TKN, // words + NUMBER_TKN, // integral numbers + VARIABLE_TKN, // almost anything + RANGE_TKN, // integer range + IPV4_TKN, // IPV4 addresses + IPV4_PREFIX_TKN, // IPV4 network prefixes + IPV6_TKN, // IPV6 prefixes + IPV6_PREFIX_TKN, // IPV6 network prefixes + + /* plumbing types */ + SELECTOR_TKN, // marks beginning of selector + OPTION_TKN, // marks beginning of option + NUL_TKN, // dummy token + START_TKN, // first token in line + END_TKN, // last token in line }; -/* Structure of command element. */ -struct cmd_element +/** + * Token struct. + */ +struct cmd_token { - const char *string; /* Command specification by string. */ - int (*func) (struct cmd_element *, struct vty *, int, const char *[]); - const char *doc; /* Documentation of this command. */ - int daemon; /* Daemon to which this command belong. */ - vector tokens; /* Vector of cmd_tokens */ - u_char attr; /* Command attributes */ -}; + enum cmd_token_type type; // token type + char *text; // token text + char *desc; // token description -enum cmd_token_type -{ - TOKEN_TERMINAL = 0, - TOKEN_MULTIPLE, - TOKEN_KEYWORD, + long long value; // for numeric types + long long min, max; // for ranges + + char *arg; // user input that matches this token }; -enum cmd_terminal_type +enum { - _TERMINAL_BUG = 0, - TERMINAL_LITERAL, - TERMINAL_OPTION, - TERMINAL_VARIABLE, - TERMINAL_VARARG, - TERMINAL_RANGE, - TERMINAL_IPV4, - TERMINAL_IPV4_PREFIX, - TERMINAL_IPV6, - TERMINAL_IPV6_PREFIX, + CMD_ATTR_DEPRECATED = 1, + CMD_ATTR_HIDDEN, }; -/* argument to be recorded on argv[] if it's not a literal */ -#define TERMINAL_RECORD(t) ((t) >= TERMINAL_OPTION) - -/* Command description structure. */ -struct cmd_token +/* Structure of command element. */ +struct cmd_element { - enum cmd_token_type type; - enum cmd_terminal_type terminal; - - /* Used for type == MULTIPLE */ - vector multiple; /* vector of cmd_token, type == FINAL */ - - /* Used for type == KEYWORD */ - vector keyword; /* vector of vector of cmd_tokens */ + const char *string; /* Command specification by string. */ + const char *doc; /* Documentation of this command. */ + int daemon; /* Daemon to which this command belong. */ + u_char attr; /* Command attributes */ - /* Used for type == TERMINAL */ - char *cmd; /* Command string. */ - char *desc; /* Command's description. */ + /* handler function for command */ + int (*func) (struct cmd_element *, struct vty *, int, struct cmd_token *[]); }; /* Return value of the commands. */ @@ -207,7 +211,7 @@ struct cmd_token #define CMD_ARGC_MAX 25 /* Turn off these macros when uisng cpp with extract.pl */ -#ifndef VTYSH_EXTRACT_PL +#ifndef VTYSH_EXTRACT_PL /* helper defines for end-user DEFUN* macros */ #define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ @@ -221,179 +225,15 @@ struct cmd_token }; #define DEFUN_CMD_FUNC_DECL(funcname) \ - static int funcname (struct cmd_element *, struct vty *, int, const char *[]); + static int funcname (struct cmd_element *, struct vty *, int, struct cmd_token *[]); #define DEFUN_CMD_FUNC_TEXT(funcname) \ static int funcname \ (struct cmd_element *self __attribute__ ((unused)), \ struct vty *vty __attribute__ ((unused)), \ int argc __attribute__ ((unused)), \ - const char *argv[] __attribute__ ((unused)) ) + struct cmd_token *argv[] __attribute__ ((unused)) ) -/* DEFUN for vty command interafce. Little bit hacky ;-). - * - * DEFUN(funcname, cmdname, cmdstr, helpstr) - * - * funcname - * ======== - * - * Name of the function that will be defined. - * - * cmdname - * ======= - * - * Name of the struct that will be defined for the command. - * - * cmdstr - * ====== - * - * The cmdstr defines the command syntax. It is used by the vty subsystem - * and vtysh to perform matching and completion in the cli. So you have to take - * care to construct it adhering to the following grammar. The names used - * for the production rules losely represent the names used in lib/command.c - * - * cmdstr = cmd_token , { " " , cmd_token } ; - * - * cmd_token = cmd_terminal - * | cmd_multiple - * | cmd_keyword ; - * - * cmd_terminal_fixed = fixed_string - * | variable - * | range - * | ipv4 - * | ipv4_prefix - * | ipv6 - * | ipv6_prefix ; - * - * cmd_terminal = cmd_terminal_fixed - * | option - * | vararg ; - * - * multiple_part = cmd_terminal_fixed ; - * cmd_multiple = "(" , multiple_part , ( "|" | { "|" , multiple_part } ) , ")" ; - * - * keyword_part = fixed_string , { " " , ( cmd_terminal_fixed | cmd_multiple ) } ; - * cmd_keyword = "{" , keyword_part , { "|" , keyword_part } , "}" ; - * - * lowercase = "a" | ... | "z" ; - * uppercase = "A" | ... | "Z" ; - * digit = "0" | ... | "9" ; - * number = digit , { digit } ; - * - * fixed_string = (lowercase | digit) , { lowercase | digit | uppercase | "-" | "_" } ; - * variable = uppercase , { uppercase | "_" } ; - * range = "<" , number , "-" , number , ">" ; - * ipv4 = "A.B.C.D" ; - * ipv4_prefix = "A.B.C.D/M" ; - * ipv6 = "X:X::X:X" ; - * ipv6_prefix = "X:X::X:X/M" ; - * option = "[" , variable , "]" ; - * vararg = "." , variable ; - * - * To put that all in a textual description: A cmdstr is a sequence of tokens, - * separated by spaces. - * - * Terminal Tokens: - * - * A very simple cmdstring would be something like: "show ip bgp". It consists - * of three Terminal Tokens, each containing a fixed string. When this command - * is called, no arguments will be passed down to the function implementing it, - * as it only consists of fixed strings. - * - * Apart from fixed strings, Terminal Tokens can also contain variables: - * An example would be "show ip bgp A.B.C.D". This command expects an IPv4 - * as argument. As this is a variable, the IP address entered by the user will - * be passed down as an argument. Apart from two exceptions, the other options - * for Terminal Tokens behave exactly as we just discussed and only make a - * difference for the CLI. The two exceptions will be discussed in the next - * paragraphs. - * - * A Terminal Token can contain a so called option match. This is a simple - * string variable that the user may omit. An example would be: - * "show interface [IFNAME]". If the user calls this without an interface as - * argument, no arguments will be passed down to the function implementing - * this command. Otherwise, the interface name will be provided to the function - * as a regular argument. - - * Also, a Terminal Token can contain a so called vararg. This is used e.g. in - * "show ip bgp regexp .LINE". The last token is a vararg match and will - * consume all the arguments the user inputs on the command line and append - * those to the list of arguments passed down to the function implementing this - * command. (Therefore, it doesn't make much sense to have any tokens after a - * vararg because the vararg will already consume all the words the user entered - * in the CLI) - * - * Multiple Tokens: - * - * The Multiple Token type can be used if there are multiple possibilities what - * arguments may be used for a command, but it should map to the same function - * nonetheless. An example would be "ip route A.B.C.D/M (reject|blackhole)" - * In that case both "reject" and "blackhole" would be acceptable as last - * arguments. The words matched by Multiple Tokens are always added to the - * argument list, even if they are matched by fixed strings. Such a Multiple - * Token can contain almost any type of token that would also be acceptable - * for a Terminal Token, the exception are optional variables and varag. - * - * There is one special case that is used in some places of Quagga that should be - * pointed out here shortly. An example would be "password (8|) WORD". This - * construct is used to have fixed strings communicated as arguments. (The "8" - * will be passed down as an argument in this case) It does not mean that - * the "8" is optional. Another historic and possibly surprising property of - * this construct is that it consumes two parts of helpstr. (Help - * strings will be explained later) - * - * Keyword Tokens: - * - * There are commands that take a lot of different and possibly optional arguments. - * An example from ospf would be the "default-information originate" command. This - * command takes a lot of optional arguments that may be provided in any order. - * To accomodate such commands, the Keyword Token has been implemented. - * Using the keyword token, the "default-information originate" command and all - * its possible options can be represented using this single cmdstr: - * "default-information originate \ - * {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}" - * - * Keywords always start with a fixed string and may be followed by arguments. - * Except optional variables and vararg, everything is permitted here. - * - * For the special case of a keyword without arguments, either NULL or the - * keyword itself will be pushed as an argument, depending on whether the - * keyword is present. - * For the other keywords, arguments will be only pushed for - * variables/Multiple Tokens. If the keyword is not present, the arguments that - * would have been pushed will be substituted by NULL. - * - * A few examples: - * "default information originate metric-type 1 metric 1000" - * would yield the following arguments: - * { NULL, "1000", "1", NULL } - * - * "default information originate always route-map RMAP-DEFAULT" - * would yield the following arguments: - * { "always", NULL, NULL, "RMAP-DEFAULT" } - * - * helpstr - * ======= - * - * The helpstr is used to show a short explantion for the commands that - * are available when the user presses '?' on the CLI. It is the concatenation - * of the helpstrings for all the tokens that make up the command. - * - * There should be one helpstring for each token in the cmdstr except those - * containing other tokens, like Multiple or Keyword Tokens. For those, there - * will only be the helpstrings of the contained tokens. - * - * The individual helpstrings are expected to be in the same order as their - * respective Tokens appear in the cmdstr. They should each be terminated with - * a linefeed. The last helpstring should be terminated with a linefeed as well. - * - * Care should also be taken to avoid having similar tokens with different - * helpstrings. Imagine e.g. the commands "show ip ospf" and "show ip bgp". - * they both contain a helpstring for "show", but only one will be displayed - * when the user enters "sh?". If those two helpstrings differ, it is not - * defined which one will be shown and the behavior is therefore unpredictable. - */ #define DEFUN(funcname, cmdname, cmdstr, helpstr) \ DEFUN_CMD_FUNC_DECL(funcname) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ @@ -506,41 +346,41 @@ struct cmd_token #define IP6_STR "IPv6 Information\n" #define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n" #define OSPF6_ROUTER_STR "Enable a routing process\n" -#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" -#define SECONDS_STR "<1-65535> Seconds\n" +#define OSPF6_INSTANCE_STR "(1-65535) Instance ID\n" +#define SECONDS_STR "(1-65535) Seconds\n" #define ROUTE_STR "Routing Table\n" #define PREFIX_LIST_STR "Build a prefix list\n" #define OSPF6_DUMP_TYPE_LIST \ -"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)" +"" #define ISIS_STR "IS-IS information\n" #define AREA_TAG_STR "[area tag]\n" -#define COMMUNITY_AANN_STR "Community number where AA and NN are <0-65535>\n" -#define COMMUNITY_VAL_STR "Community number in AA:NN format (where AA and NN are <0-65535>) or local-AS|no-advertise|no-export|internet or additive\n" +#define COMMUNITY_AANN_STR "Community number where AA and NN are (0-65535)\n" +#define COMMUNITY_VAL_STR "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet or additive\n" #define CONF_BACKUP_EXT ".sav" /* IPv4 only machine should not accept IPv6 address for peer's IP address. So we replace VTY command string like below. */ #ifdef HAVE_IPV6 -#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) " -#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) " +#define NEIGHBOR_CMD "neighbor " +#define NO_NEIGHBOR_CMD "no neighbor " #define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n" -#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) " -#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) " +#define NEIGHBOR_CMD2 "neighbor " +#define NO_NEIGHBOR_CMD2 "no neighbor " #define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nInterface name or neighbor tag\n" #define NEIGHBOR_ADDR_STR3 "Neighbor address\nIPv6 address\nInterface name\n" #else #define NEIGHBOR_CMD "neighbor A.B.C.D " #define NO_NEIGHBOR_CMD "no neighbor A.B.C.D " #define NEIGHBOR_ADDR_STR "Neighbor address\n" -#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) " -#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) " +#define NEIGHBOR_CMD2 "neighbor " +#define NO_NEIGHBOR_CMD2 "no neighbor " #define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n" #endif /* HAVE_IPV6 */ /* Dynamic neighbor (listen range) configuration */ #ifdef HAVE_IPV6 -#define LISTEN_RANGE_CMD "bgp listen range (A.B.C.D/M|X:X::X:X/M) " +#define LISTEN_RANGE_CMD "bgp listen range " #define LISTEN_RANGE_ADDR_STR "Neighbor address\nNeighbor IPv6 address\n" #else #define LISTEN_RANGE_CMD "bgp listen range A.B.C.D/M " @@ -555,7 +395,7 @@ extern void install_element (enum node_type, struct cmd_element *); /* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated string with a space between each element (allocated using XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */ -extern char *argv_concat (const char **argv, int argc, int shift); +extern char *argv_concat (struct cmd_token **argv, int argc, int shift); extern vector cmd_make_strvec (const char *); extern void cmd_free_strvec (vector); @@ -577,6 +417,14 @@ del_cmd_element(struct cmd_element *); struct cmd_element * copy_cmd_element(struct cmd_element *cmd); +/* memory management for cmd_token */ +struct cmd_token * +new_cmd_token (enum cmd_token_type, char *, char *); +void +del_cmd_token (struct cmd_token *); +struct cmd_token * +copy_cmd_token (struct cmd_token *); + /* Export typical functions. */ extern struct cmd_element config_end_cmd; extern struct cmd_element config_exit_cmd; @@ -591,8 +439,9 @@ extern void print_version (const char *); extern int cmd_banner_motd_file (const char *); /* struct host global, ick */ -extern struct host host; +extern struct host host; + +/* text for command */ +#define CMD_CR_TEXT "" -/* "" global */ -extern char *command_cr; #endif /* _ZEBRA_COMMAND_H */ diff --git a/lib/command_match.c b/lib/command_match.c index dce3292709..d7c2222ce6 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -25,7 +25,6 @@ #include #include "command_match.h" #include "command_parse.h" -#include "grammar_sandbox.h" #include "memory.h" /* matcher helper prototypes */ @@ -36,16 +35,16 @@ static struct list * command_match_r (struct graph_node *, vector, unsigned int); static int -score_precedence (enum cmd_token_type_t); +score_precedence (enum cmd_token_type); static enum match_type -min_match_level (enum cmd_token_type_t); +min_match_level (enum cmd_token_type); static void del_arglist (struct list *); -static struct cmd_token_t * -disambiguate_tokens (struct cmd_token_t *, struct cmd_token_t *, char *); +static struct cmd_token * +disambiguate_tokens (struct cmd_token *, struct cmd_token *, char *); static struct list * disambiguate (struct list *, struct list *, vector, unsigned int); @@ -55,7 +54,7 @@ compare_completions (const void *, const void *); /* token matcher prototypes */ static enum match_type -match_token (struct cmd_token_t *, char *); +match_token (struct cmd_token *, char *); static enum match_type match_ipv4 (const char *); @@ -70,16 +69,16 @@ static enum match_type match_ipv6_prefix (const char *); static enum match_type -match_range (struct cmd_token_t *, const char *); +match_range (struct cmd_token *, const char *); static enum match_type -match_word (struct cmd_token_t *, const char *); +match_word (struct cmd_token *, const char *); static enum match_type -match_number (struct cmd_token_t *, const char *); +match_number (struct cmd_token *, const char *); static enum match_type -match_variable (struct cmd_token_t *, const char *); +match_variable (struct cmd_token *, const char *); /* matching functions */ static enum matcher_rv matcher_rv; @@ -105,7 +104,7 @@ command_match (struct graph *cmdgraph, struct listnode *tail = listtail (*argv); // delete dummy start node - del_cmd_token ((struct cmd_token_t *) head->data); + del_cmd_token ((struct cmd_token *) head->data); list_delete_node (*argv, head); // get cmd_element out of list tail @@ -179,7 +178,7 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n) assert (n < vector_active (vline)); // get the minimum match level that can count as a full match - struct cmd_token_t *token = start->data; + struct cmd_token *token = start->data; enum match_type minmatch = min_match_level (token->type); // get the current operating input token @@ -205,7 +204,7 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n) // if we've matched all input we're looking for END_TKN if (n+1 == vector_active (vline)) { - struct cmd_token_t *tok = gn->data; + struct cmd_token *tok = gn->data; if (tok->type == END_TKN) { currbest = list_new(); @@ -255,8 +254,8 @@ command_match_r (struct graph_node *start, vector vline, unsigned int n) else { // copy token, set arg and prepend to currbest - struct cmd_token_t *token = start->data; - struct cmd_token_t *copy = copy_cmd_token (token); + struct cmd_token *token = start->data; + struct cmd_token *copy = copy_cmd_token (token); copy->arg = XSTRDUP (MTYPE_CMD_TOKENS, input_token); list_add_node_prev (currbest, currbest->head, copy); matcher_rv = MATCHER_OK; @@ -301,7 +300,7 @@ command_complete (struct graph *graph, for (ALL_LIST_ELEMENTS_RO (current,node,gn)) { - struct cmd_token_t *token = gn->data; + struct cmd_token *token = gn->data; switch (match_token (token, input_token)) { case partly_match: @@ -359,7 +358,7 @@ add_nexthops (struct list *list, struct graph_node *node) for (unsigned int i = 0; i < vector_active (node->to); i++) { child = vector_slot (node->to, i); - struct cmd_token_t *token = child->data; + struct cmd_token *token = child->data; switch (token->type) { case OPTION_TKN: @@ -384,7 +383,7 @@ add_nexthops (struct list *list, struct graph_node *node) * @return minimum match level needed to for a token to fully match */ static enum match_type -min_match_level (enum cmd_token_type_t type) +min_match_level (enum cmd_token_type type) { switch (type) { @@ -406,7 +405,7 @@ min_match_level (enum cmd_token_type_t type) * @return precedence score */ static int -score_precedence (enum cmd_token_type_t type) +score_precedence (enum cmd_token_type type) { switch (type) { @@ -437,9 +436,9 @@ score_precedence (enum cmd_token_type_t type) * @param[in] token the token being matched * @return the best-matching node, or NULL if the two are entirely ambiguous */ -static struct cmd_token_t * -disambiguate_tokens (struct cmd_token_t *first, - struct cmd_token_t *second, +static struct cmd_token * +disambiguate_tokens (struct cmd_token *first, + struct cmd_token *second, char *input_token) { // if the types are different, simply go off of type precedence @@ -465,8 +464,8 @@ disambiguate_tokens (struct cmd_token_t *first, /** * Picks the better of two possible matches for an input line. * - * @param[in] first candidate list of cmd_token_t matching vline - * @param[in] second candidate list of cmd_token_t matching vline + * @param[in] first candidate list of cmd_token matching vline + * @param[in] second candidate list of cmd_token matching vline * @param[in] vline the input line being matched * @param[in] n index into vline to start comparing at * @return the best-matching list, or NULL if the two are entirely ambiguous @@ -483,7 +482,7 @@ disambiguate (struct list *first, struct listnode *fnode = listhead (first), *snode = listhead (second); - struct cmd_token_t *ftok = listgetdata (fnode), + struct cmd_token *ftok = listgetdata (fnode), *stok = listgetdata (snode), *best = NULL; @@ -527,7 +526,7 @@ del_arglist (struct list *list) /*---------- token level matching functions ----------*/ static enum match_type -match_token (struct cmd_token_t *token, char *input_token) +match_token (struct cmd_token *token, char *input_token) { switch (token->type) { case WORD_TKN: @@ -761,7 +760,7 @@ match_ipv6_prefix (const char *str) #endif static enum match_type -match_range (struct cmd_token_t *token, const char *str) +match_range (struct cmd_token *token, const char *str) { assert (token->type == RANGE_TKN); @@ -782,7 +781,7 @@ match_range (struct cmd_token_t *token, const char *str) } static enum match_type -match_word (struct cmd_token_t *token, const char *word) +match_word (struct cmd_token *token, const char *word) { assert (token->type == WORD_TKN); @@ -804,7 +803,7 @@ match_word (struct cmd_token_t *token, const char *word) } static enum match_type -match_number (struct cmd_token_t *token, const char *word) +match_number (struct cmd_token *token, const char *word) { assert (token->type == NUMBER_TKN); @@ -819,7 +818,7 @@ match_number (struct cmd_token_t *token, const char *word) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890:" static enum match_type -match_variable (struct cmd_token_t *token, const char *word) +match_variable (struct cmd_token *token, const char *word) { assert (token->type == VARIABLE_TKN); diff --git a/lib/command_parse.y b/lib/command_parse.y index 1922ba2297..089b506b03 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -36,7 +36,6 @@ #include "command.h" #include "graph.h" #include "memory.h" - #include "grammar_sandbox.h" extern int yylex (void); @@ -115,11 +114,11 @@ add_edge_dedup (struct graph_node *, struct graph_node *); static int - cmp_token (struct cmd_token_t *, struct cmd_token_t *); + cmp_token (struct cmd_token *, struct cmd_token *); static struct graph_node * new_token_node (struct graph *, - enum cmd_token_type_t type, + enum cmd_token_type type, char *text, char *doc); static void @@ -250,7 +249,7 @@ placeholder_token: | RANGE { $$ = new_token_node (graph, RANGE_TKN, XSTRDUP(MTYPE_CMD_TOKENS, $1), doc_next()); - struct cmd_token_t *token = $$->data; + struct cmd_token *token = $$->data; // get the numbers out yylval.string++; @@ -263,7 +262,6 @@ placeholder_token: free ($1); } -; /* productions */ selector: '<' selector_seq_seq '>' @@ -453,9 +451,9 @@ doc_next() } static struct graph_node * -new_token_node (struct graph *graph, enum cmd_token_type_t type, char *text, char *doc) +new_token_node (struct graph *graph, enum cmd_token_type type, char *text, char *doc) { - struct cmd_token_t *token = new_cmd_token (type, text, doc); + struct cmd_token *token = new_cmd_token (type, text, doc); return graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token); } @@ -469,7 +467,7 @@ node_adjacent (struct graph_node *first, struct graph_node *second) for (unsigned int i = 0; i < vector_active (first->to); i++) { adj = vector_slot (first->to, i); - struct cmd_token_t *ftok = adj->data, + struct cmd_token *ftok = adj->data, *stok = second->data; if (cmp_token (ftok, stok)) return adj; @@ -503,7 +501,7 @@ add_edge_dedup (struct graph_node *from, struct graph_node *to) * for parsing purposes and determines overall graph structure. */ static int -cmp_token (struct cmd_token_t *first, struct cmd_token_t *second) +cmp_token (struct cmd_token *first, struct cmd_token *second) { // compare types if (first->type != second->type) return 0; diff --git a/lib/vty.c b/lib/vty.c index 831ae8e556..61b0c3b4be 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. + * 02111-1307, USA. */ #include @@ -41,7 +41,7 @@ #include /* Vty events */ -enum event +enum event { VTY_SERV, VTY_READ, @@ -117,37 +117,37 @@ vty_out (struct vty *vty, const char *format, ...) /* Initial buffer is not enough. */ if (len < 0 || len >= size) - { - while (1) - { - if (len > -1) - size = len + 1; - else - size = size * 2; - - p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); - if (! p) - return -1; - - va_start (args, format); - len = vsnprintf (p, size, format, args); - va_end (args); - - if (len > -1 && len < size) - break; - } - } + { + while (1) + { + if (len > -1) + size = len + 1; + else + size = size * 2; + + p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); + if (! p) + return -1; + + va_start (args, format); + len = vsnprintf (p, size, format, args); + va_end (args); + + if (len > -1 && len < size) + break; + } + } /* When initial buffer is enough to store all output. */ if (! p) - p = buf; + p = buf; /* Pointer p must point out buffer. */ buffer_put (vty->obuf, (u_char *) p, len); /* If p is not different with buf, it is allocated buffer. */ if (p != buf) - XFREE (MTYPE_VTY_OUT_BUF, p); + XFREE (MTYPE_VTY_OUT_BUF, p); } return len; @@ -155,7 +155,7 @@ vty_out (struct vty *vty, const char *format, ...) static int vty_log_out (struct vty *vty, const char *level, const char *proto_str, - const char *format, struct timestamp_control *ctl, va_list va) + const char *format, struct timestamp_control *ctl, va_list va) { int ret; int len; @@ -189,13 +189,13 @@ vty_log_out (struct vty *vty, const char *level, const char *proto_str, if (write(vty->wfd, buf, len) < 0) { if (ERRNO_IO_RETRY(errno)) - /* Kernel buffer is full, probably too much debugging output, so just - drop the data and ignore. */ - return -1; + /* Kernel buffer is full, probably too much debugging output, so just + drop the data and ignore. */ + return -1; /* Fatal I/O error. */ vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ zlog_warn("%s: write failed to vty client fd %d, closing: %s", - __func__, vty->fd, safe_strerror(errno)); + __func__, vty->fd, safe_strerror(errno)); buffer_reset(vty->obuf); /* cannot call vty_close, because a parent routine may still try to access the vty struct */ @@ -211,7 +211,7 @@ void vty_time_print (struct vty *vty, int cr) { char buf[QUAGGA_TIMESTAMP_LEN]; - + if (quagga_timestamp(0, buf, sizeof(buf)) == 0) { zlog (NULL, LOG_INFO, "quagga_timestamp error"); @@ -236,20 +236,20 @@ vty_hello (struct vty *vty) f = fopen (host.motdfile, "r"); if (f) - { - while (fgets (buf, sizeof (buf), f)) - { - char *s; - /* work backwards to ignore trailling isspace() */ - for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); - s--); - *s = '\0'; - vty_out (vty, "%s%s", buf, VTY_NEWLINE); - } - fclose (f); - } + { + while (fgets (buf, sizeof (buf), f)) + { + char *s; + /* work backwards to ignore trailling isspace() */ + for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); + s--); + *s = '\0'; + vty_out (vty, "%s%s", buf, VTY_NEWLINE); + } + fclose (f); + } else - vty_out (vty, "MOTD file not found%s", VTY_NEWLINE); + vty_out (vty, "MOTD file not found%s", VTY_NEWLINE); } else if (host.motd) vty_out (vty, "%s", host.motd); @@ -266,10 +266,10 @@ vty_prompt (struct vty *vty) { hostname = host.name; if (!hostname) - { - uname (&names); - hostname = names.nodename; - } + { + uname (&names); + hostname = names.nodename; + } vty_out (vty, cmd_prompt (vty->node), hostname); } } @@ -322,7 +322,7 @@ vty_new () { struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty)); - new->obuf = buffer_new(0); /* Use default buffer size. */ + new->obuf = buffer_new(0); /* Use default buffer size. */ new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); new->error_buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); new->max = VTY_BUFSIZ; @@ -343,19 +343,19 @@ vty_auth (struct vty *vty, char *buf) { case AUTH_NODE: if (host.encrypt) - passwd = host.password_encrypt; + passwd = host.password_encrypt; else - passwd = host.password; + passwd = host.password; if (host.advanced) - next_node = host.enable ? VIEW_NODE : ENABLE_NODE; + next_node = host.enable ? VIEW_NODE : ENABLE_NODE; else - next_node = VIEW_NODE; + next_node = VIEW_NODE; break; case AUTH_ENABLE_NODE: if (host.encrypt) - passwd = host.enable_encrypt; + passwd = host.enable_encrypt; else - passwd = host.enable; + passwd = host.enable; next_node = ENABLE_NODE; break; } @@ -363,9 +363,9 @@ vty_auth (struct vty *vty, char *buf) if (passwd) { if (host.encrypt) - fail = strcmp (crypt(buf, passwd), passwd); + fail = strcmp (crypt(buf, passwd), passwd); else - fail = strcmp (buf, passwd); + fail = strcmp (buf, passwd); } else fail = 1; @@ -373,26 +373,26 @@ vty_auth (struct vty *vty, char *buf) if (! fail) { vty->fail = 0; - vty->node = next_node; /* Success ! */ + vty->node = next_node; /* Success ! */ } else { vty->fail++; if (vty->fail >= 3) - { - if (vty->node == AUTH_NODE) - { - vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); - vty->status = VTY_CLOSE; - } - else - { - /* AUTH_ENABLE_NODE */ - vty->fail = 0; - vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); - vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE; - } - } + { + if (vty->node == AUTH_NODE) + { + vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + } + else + { + /* AUTH_ENABLE_NODE */ + vty->fail = 0; + vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); + vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE; + } + } } } @@ -419,7 +419,7 @@ vty_command (struct vty *vty, char *buf) if (cp != NULL && *cp != '\0') { unsigned i; - char vty_str[VTY_BUFSIZ]; + char vty_str[VTY_BUFSIZ]; char prompt_str[VTY_BUFSIZ]; /* format the base vty info */ @@ -461,14 +461,14 @@ vty_command (struct vty *vty, char *buf) protocolname = zlog_proto_names[zlog_default->protocol]; else protocolname = zlog_proto_names[ZLOG_NONE]; - + #ifdef CONSUMED_TIME_CHECK GETRUSAGE(&after); if ((realtime = thread_consumed_time(&after, &before, &cputime)) > - CONSUMED_TIME_CHECK) + CONSUMED_TIME_CHECK) /* Warn about CPU hog that must be fixed. */ zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s", - realtime/1000, cputime/1000, buf); + realtime/1000, cputime/1000, buf); } #endif /* CONSUMED_TIME_CHECK */ @@ -476,18 +476,18 @@ vty_command (struct vty *vty, char *buf) switch (ret) { case CMD_WARNING: - if (vty->type == VTY_FILE) - vty_out (vty, "Warning...%s", VTY_NEWLINE); - break; + if (vty->type == VTY_FILE) + vty_out (vty, "Warning...%s", VTY_NEWLINE); + break; case CMD_ERR_AMBIGUOUS: - vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); - break; + vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + break; case CMD_ERR_NO_MATCH: - vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); - break; + vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); + break; case CMD_ERR_INCOMPLETE: - vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); - break; + vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); + break; } cmd_free_strvec (vline); @@ -685,7 +685,7 @@ vty_forward_word (struct vty *vty) { while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') vty_forward_char (vty); - + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') vty_forward_char (vty); } @@ -782,14 +782,14 @@ vty_delete_char (struct vty *vty) } if (vty->cp == vty->length) - return; /* completion need here? */ + return; /* completion need here? */ size = vty->length - vty->cp; vty->length--; memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); vty->buf[vty->length] = '\0'; - + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) return; @@ -819,7 +819,7 @@ vty_kill_line (struct vty *vty) int size; size = vty->length - vty->cp; - + if (size == 0) return; @@ -914,7 +914,7 @@ vty_complete_command (struct vty *vty) vector_set (vline, NULL); matched = cmd_complete_command_lib (vline, vty, &ret, 1); - + cmd_free_strvec (vline); vty_out (vty, "%s", VTY_NEWLINE); @@ -949,12 +949,12 @@ vty_complete_command (struct vty *vty) break; case CMD_COMPLETE_LIST_MATCH: for (i = 0; matched[i] != NULL; i++) - { - if (i != 0 && ((i % 6) == 0)) - vty_out (vty, "%s", VTY_NEWLINE); - vty_out (vty, "%-10s ", matched[i]); - XFREE (MTYPE_TMP, matched[i]); - } + { + if (i != 0 && ((i % 6) == 0)) + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%-10s ", matched[i]); + XFREE (MTYPE_TMP, matched[i]); + } vty_out (vty, "%s", VTY_NEWLINE); vty_prompt (vty); @@ -973,14 +973,12 @@ vty_complete_command (struct vty *vty) static void vty_describe_fold (struct vty *vty, int cmd_width, - unsigned int desc_width, struct cmd_token *token) + unsigned int desc_width, struct cmd_token *token) { char *buf; const char *cmd, *p; int pos; - cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd; - if (desc_width <= 0) { vty_out (vty, " %-*s %s%s", cmd_width, cmd, token->desc, VTY_NEWLINE); @@ -1028,7 +1026,7 @@ vty_describe_command (struct vty *vty) vline = vector_init (1); vector_set (vline, NULL); } - else + else if (isspace ((int) vty->buf[vty->length - 1])) vector_set (vline, NULL); @@ -1047,24 +1045,22 @@ vty_describe_command (struct vty *vty) vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); goto out; break; - } + } /* Get width of command string. */ width = 0; for (i = 0; i < vector_active (describe); i++) if ((token = vector_slot (describe, i)) != NULL) { - unsigned int len; + unsigned int len; - if (token->cmd[0] == '\0') - continue; + if (token->text[0] == '\0') + continue; - len = strlen (token->cmd); - if (token->cmd[0] == '.') - len--; + len = strlen (token->text); - if (width < len) - width = len; + if (width < len) + width = len; } /* Get width of description string. */ @@ -1074,45 +1070,45 @@ vty_describe_command (struct vty *vty) for (i = 0; i < vector_active (describe); i++) if ((token = vector_slot (describe, i)) != NULL) { - if (token->cmd[0] == '\0') - continue; - - if (strcmp (token->cmd, command_cr) == 0) - { - token_cr = token; - continue; - } - - if (!token->desc) - vty_out (vty, " %-s%s", - token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, - VTY_NEWLINE); - else if (desc_width >= strlen (token->desc)) - vty_out (vty, " %-*s %s%s", width, - token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, - token->desc, VTY_NEWLINE); - else - vty_describe_fold (vty, width, desc_width, token); + if (token->text[0] == '\0') + continue; + + if (strcmp (token->text, CMD_CR_TEXT) == 0) + { + token_cr = token; + continue; + } + + if (!token->desc) + vty_out (vty, " %-s%s", + token->text, + VTY_NEWLINE); + else if (desc_width >= strlen (token->desc)) + vty_out (vty, " %-*s %s%s", width, + token->text, + token->desc, VTY_NEWLINE); + else + vty_describe_fold (vty, width, desc_width, token); #if 0 - vty_out (vty, " %-*s %s%s", width - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str ? desc->str : "", VTY_NEWLINE); + vty_out (vty, " %-*s %s%s", width + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str ? desc->str : "", VTY_NEWLINE); #endif /* 0 */ } if ((token = token_cr)) { if (!token->desc) - vty_out (vty, " %-s%s", - token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, - VTY_NEWLINE); + vty_out (vty, " %-s%s", + token->text, + VTY_NEWLINE); else if (desc_width >= strlen (token->desc)) - vty_out (vty, " %-*s %s%s", width, - token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, - token->desc, VTY_NEWLINE); + vty_out (vty, " %-*s %s%s", width, + token->text, + token->desc, VTY_NEWLINE); else - vty_describe_fold (vty, width, desc_width, token); + vty_describe_fold (vty, width, desc_width, token); } out: @@ -1217,41 +1213,41 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) for (i = 0; i < nbytes; i++) { switch (buf[i]) - { - case IAC: - vty_out (vty, "IAC "); - break; - case WILL: - vty_out (vty, "WILL "); - break; - case WONT: - vty_out (vty, "WONT "); - break; - case DO: - vty_out (vty, "DO "); - break; - case DONT: - vty_out (vty, "DONT "); - break; - case SB: - vty_out (vty, "SB "); - break; - case SE: - vty_out (vty, "SE "); - break; - case TELOPT_ECHO: - vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); - break; - case TELOPT_SGA: - vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); - break; - case TELOPT_NAWS: - vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); - break; - default: - vty_out (vty, "%x ", buf[i]); - break; - } + { + case IAC: + vty_out (vty, "IAC "); + break; + case WILL: + vty_out (vty, "WILL "); + break; + case WONT: + vty_out (vty, "WONT "); + break; + case DO: + vty_out (vty, "DO "); + break; + case DONT: + vty_out (vty, "DONT "); + break; + case SB: + vty_out (vty, "SB "); + break; + case SE: + vty_out (vty, "SE "); + break; + case TELOPT_ECHO: + vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); + break; + case TELOPT_SGA: + vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); + break; + case TELOPT_NAWS: + vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); + break; + default: + vty_out (vty, "%x ", buf[i]); + break; + } } vty_out (vty, "%s", VTY_NEWLINE); @@ -1264,42 +1260,42 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) vty->iac_sb_in_progress = 1; return 0; break; - case SE: + case SE: { - if (!vty->iac_sb_in_progress) - return 0; - - if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) - { - vty->iac_sb_in_progress = 0; - return 0; - } - switch (vty->sb_buf[0]) - { - case TELOPT_NAWS: - if (vty->sb_len != TELNET_NAWS_SB_LEN) - zlog_warn("RFC 1073 violation detected: telnet NAWS option " - "should send %d characters, but we received %lu", - TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); - else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) - zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, " - "too small to handle the telnet NAWS option", - (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); - else - { - vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); - vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); + if (!vty->iac_sb_in_progress) + return 0; + + if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) + { + vty->iac_sb_in_progress = 0; + return 0; + } + switch (vty->sb_buf[0]) + { + case TELOPT_NAWS: + if (vty->sb_len != TELNET_NAWS_SB_LEN) + zlog_warn("RFC 1073 violation detected: telnet NAWS option " + "should send %d characters, but we received %lu", + TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); + else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) + zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, " + "too small to handle the telnet NAWS option", + (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); + else + { + vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); + vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); #ifdef TELNET_OPTION_DEBUG - vty_out(vty, "TELNET NAWS window size negotiation completed: " - "width %d, height %d%s", - vty->width, vty->height, VTY_NEWLINE); + vty_out(vty, "TELNET NAWS window size negotiation completed: " + "width %d, height %d%s", + vty->width, vty->height, VTY_NEWLINE); #endif - } - break; - } - vty->iac_sb_in_progress = 0; - return 0; - break; + } + break; + } + vty->iac_sb_in_progress = 0; + return 0; + break; } default: break; @@ -1324,7 +1320,7 @@ vty_execute (struct vty *vty) default: ret = vty_command (vty, vty->buf); if (vty->type == VTY_TERM) - vty_hist_add (vty); + vty_hist_add (vty); break; } @@ -1394,187 +1390,187 @@ vty_read (struct thread *thread) if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { if (nbytes < 0) - { - if (ERRNO_IO_RETRY(errno)) - { - vty_event (VTY_READ, vty_sock, vty); - return 0; - } - vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: read error on vty client fd %d, closing: %s", - __func__, vty->fd, safe_strerror(errno)); + { + if (ERRNO_IO_RETRY(errno)) + { + vty_event (VTY_READ, vty_sock, vty); + return 0; + } + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + zlog_warn("%s: read error on vty client fd %d, closing: %s", + __func__, vty->fd, safe_strerror(errno)); buffer_reset(vty->obuf); - } + } vty->status = VTY_CLOSE; } - for (i = 0; i < nbytes; i++) + for (i = 0; i < nbytes; i++) { if (buf[i] == IAC) - { - if (!vty->iac) - { - vty->iac = 1; - continue; - } - else - { - vty->iac = 0; - } - } - + { + if (!vty->iac) + { + vty->iac = 1; + continue; + } + else + { + vty->iac = 0; + } + } + if (vty->iac_sb_in_progress && !vty->iac) - { - if (vty->sb_len < sizeof(vty->sb_buf)) - vty->sb_buf[vty->sb_len] = buf[i]; - vty->sb_len++; - continue; - } + { + if (vty->sb_len < sizeof(vty->sb_buf)) + vty->sb_buf[vty->sb_len] = buf[i]; + vty->sb_len++; + continue; + } if (vty->iac) - { - /* In case of telnet command */ - int ret = 0; - ret = vty_telnet_option (vty, buf + i, nbytes - i); - vty->iac = 0; - i += ret; - continue; - } - + { + /* In case of telnet command */ + int ret = 0; + ret = vty_telnet_option (vty, buf + i, nbytes - i); + vty->iac = 0; + i += ret; + continue; + } + if (vty->status == VTY_MORE) - { - switch (buf[i]) - { - case CONTROL('C'): - case 'q': - case 'Q': - vty_buffer_reset (vty); - break; + { + switch (buf[i]) + { + case CONTROL('C'): + case 'q': + case 'Q': + vty_buffer_reset (vty); + break; #if 0 /* More line does not work for "show ip bgp". */ - case '\n': - case '\r': - vty->status = VTY_MORELINE; - break; + case '\n': + case '\r': + vty->status = VTY_MORELINE; + break; #endif - default: - break; - } - continue; - } + default: + break; + } + continue; + } /* Escape character. */ if (vty->escape == VTY_ESCAPE) - { - vty_escape_map (buf[i], vty); - continue; - } + { + vty_escape_map (buf[i], vty); + continue; + } /* Pre-escape status. */ if (vty->escape == VTY_PRE_ESCAPE) - { - switch (buf[i]) - { - case '[': - vty->escape = VTY_ESCAPE; - break; - case 'b': - vty_backward_word (vty); - vty->escape = VTY_NORMAL; - break; - case 'f': - vty_forward_word (vty); - vty->escape = VTY_NORMAL; - break; - case 'd': - vty_forward_kill_word (vty); - vty->escape = VTY_NORMAL; - break; - case CONTROL('H'): - case 0x7f: - vty_backward_kill_word (vty); - vty->escape = VTY_NORMAL; - break; - default: - vty->escape = VTY_NORMAL; - break; - } - continue; - } + { + switch (buf[i]) + { + case '[': + vty->escape = VTY_ESCAPE; + break; + case 'b': + vty_backward_word (vty); + vty->escape = VTY_NORMAL; + break; + case 'f': + vty_forward_word (vty); + vty->escape = VTY_NORMAL; + break; + case 'd': + vty_forward_kill_word (vty); + vty->escape = VTY_NORMAL; + break; + case CONTROL('H'): + case 0x7f: + vty_backward_kill_word (vty); + vty->escape = VTY_NORMAL; + break; + default: + vty->escape = VTY_NORMAL; + break; + } + continue; + } switch (buf[i]) - { - case CONTROL('A'): - vty_beginning_of_line (vty); - break; - case CONTROL('B'): - vty_backward_char (vty); - break; - case CONTROL('C'): - vty_stop_input (vty); - break; - case CONTROL('D'): - vty_delete_char (vty); - break; - case CONTROL('E'): - vty_end_of_line (vty); - break; - case CONTROL('F'): - vty_forward_char (vty); - break; - case CONTROL('H'): - case 0x7f: - vty_delete_backward_char (vty); - break; - case CONTROL('K'): - vty_kill_line (vty); - break; - case CONTROL('N'): - vty_next_line (vty); - break; - case CONTROL('P'): - vty_previous_line (vty); - break; - case CONTROL('T'): - vty_transpose_chars (vty); - break; - case CONTROL('U'): - vty_kill_line_from_beginning (vty); - break; - case CONTROL('W'): - vty_backward_kill_word (vty); - break; - case CONTROL('Z'): - vty_end_config (vty); - break; - case '\n': - case '\r': - vty_out (vty, "%s", VTY_NEWLINE); - vty_execute (vty); - break; - case '\t': - vty_complete_command (vty); - break; - case '?': - if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) - vty_self_insert (vty, buf[i]); - else - vty_describe_command (vty); - break; - case '\033': - if (i + 1 < nbytes && buf[i + 1] == '[') - { - vty->escape = VTY_ESCAPE; - i++; - } - else - vty->escape = VTY_PRE_ESCAPE; - break; - default: - if (buf[i] > 31 && buf[i] < 127) - vty_self_insert (vty, buf[i]); - break; - } + { + case CONTROL('A'): + vty_beginning_of_line (vty); + break; + case CONTROL('B'): + vty_backward_char (vty); + break; + case CONTROL('C'): + vty_stop_input (vty); + break; + case CONTROL('D'): + vty_delete_char (vty); + break; + case CONTROL('E'): + vty_end_of_line (vty); + break; + case CONTROL('F'): + vty_forward_char (vty); + break; + case CONTROL('H'): + case 0x7f: + vty_delete_backward_char (vty); + break; + case CONTROL('K'): + vty_kill_line (vty); + break; + case CONTROL('N'): + vty_next_line (vty); + break; + case CONTROL('P'): + vty_previous_line (vty); + break; + case CONTROL('T'): + vty_transpose_chars (vty); + break; + case CONTROL('U'): + vty_kill_line_from_beginning (vty); + break; + case CONTROL('W'): + vty_backward_kill_word (vty); + break; + case CONTROL('Z'): + vty_end_config (vty); + break; + case '\n': + case '\r': + vty_out (vty, "%s", VTY_NEWLINE); + vty_execute (vty); + break; + case '\t': + vty_complete_command (vty); + break; + case '?': + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + vty_self_insert (vty, buf[i]); + else + vty_describe_command (vty); + break; + case '\033': + if (i + 1 < nbytes && buf[i + 1] == '[') + { + vty->escape = VTY_ESCAPE; + i++; + } + else + vty->escape = VTY_PRE_ESCAPE; + break; + default: + if (buf[i] > 31 && buf[i] < 127) + vty_self_insert (vty, buf[i]); + break; + } } /* Check status. */ @@ -1614,36 +1610,36 @@ vty_flush (struct thread *thread) flushrc = buffer_flush_available(vty->obuf, vty_sock); else if (vty->status == VTY_MORELINE) flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, - 1, erase, 0); + 1, erase, 0); else flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, - vty->lines >= 0 ? vty->lines : - vty->height, - erase, 0); + vty->lines >= 0 ? vty->lines : + vty->height, + erase, 0); switch (flushrc) { case BUFFER_ERROR: vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ zlog_warn("buffer_flush failed on vty client fd %d, closing", - vty->fd); + vty->fd); buffer_reset(vty->obuf); vty_close(vty); return 0; case BUFFER_EMPTY: if (vty->status == VTY_CLOSE) - vty_close (vty); + vty_close (vty); else - { - vty->status = VTY_NORMAL; - if (vty->lines == 0) - vty_event (VTY_READ, vty_sock, vty); - } + { + vty->status = VTY_NORMAL; + if (vty->lines == 0) + vty_event (VTY_READ, vty_sock, vty); + } break; case BUFFER_PENDING: /* There is more data waiting to be written. */ vty->status = VTY_MORE; if (vty->lines == 0) - vty_event (VTY_WRITE, vty_sock, vty); + vty_event (VTY_WRITE, vty_sock, vty); break; } @@ -1698,9 +1694,9 @@ vty_create (int vty_sock, union sockunion *su) if (restricted_mode) vty->node = RESTRICTED_NODE; else if (host.advanced) - vty->node = ENABLE_NODE; + vty->node = ENABLE_NODE; else - vty->node = VIEW_NODE; + vty->node = VIEW_NODE; } if (host.lines >= 0) vty->lines = host.lines; @@ -1709,12 +1705,12 @@ vty_create (int vty_sock, union sockunion *su) { /* Vty is not available if password isn't set. */ if (host.password == NULL && host.password_encrypt == NULL) - { - vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); - vty->status = VTY_CLOSE; - vty_close (vty); - return NULL; - } + { + vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + vty_close (vty); + return NULL; + } } /* Say hello to the world. */ @@ -1834,17 +1830,17 @@ vty_accept (struct thread *thread) if (p.family == AF_INET && vty_accesslist_name) { if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) && - (access_list_apply (acl, &p) == FILTER_DENY)) - { - zlog (NULL, LOG_INFO, "Vty connection refused from %s", - sockunion2str (&su, buf, SU_ADDRSTRLEN)); - close (vty_sock); - - /* continue accepting connections */ - vty_event (VTY_SERV, accept_sock, NULL); - - return 0; - } + (access_list_apply (acl, &p) == FILTER_DENY)) + { + zlog (NULL, LOG_INFO, "Vty connection refused from %s", + sockunion2str (&su, buf, SU_ADDRSTRLEN)); + close (vty_sock); + + /* continue accepting connections */ + vty_event (VTY_SERV, accept_sock, NULL); + + return 0; + } } #ifdef HAVE_IPV6 @@ -1852,29 +1848,29 @@ vty_accept (struct thread *thread) if (p.family == AF_INET6 && vty_ipv6_accesslist_name) { if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) && - (access_list_apply (acl, &p) == FILTER_DENY)) - { - zlog (NULL, LOG_INFO, "Vty connection refused from %s", - sockunion2str (&su, buf, SU_ADDRSTRLEN)); - close (vty_sock); - - /* continue accepting connections */ - vty_event (VTY_SERV, accept_sock, NULL); - - return 0; - } + (access_list_apply (acl, &p) == FILTER_DENY)) + { + zlog (NULL, LOG_INFO, "Vty connection refused from %s", + sockunion2str (&su, buf, SU_ADDRSTRLEN)); + close (vty_sock); + + /* continue accepting connections */ + vty_event (VTY_SERV, accept_sock, NULL); + + return 0; + } } #endif /* HAVE_IPV6 */ - + on = 1; - ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, - (char *) &on, sizeof (on)); + ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &on, sizeof (on)); if (ret < 0) - zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", - safe_strerror (errno)); + zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", + safe_strerror (errno)); zlog (NULL, LOG_INFO, "Vty connection from %s", - sockunion2str (&su, buf, SU_ADDRSTRLEN)); + sockunion2str (&su, buf, SU_ADDRSTRLEN)); vty_create (vty_sock, &su); @@ -1913,14 +1909,14 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) { if (ainfo->ai_family != AF_INET #ifdef HAVE_IPV6 - && ainfo->ai_family != AF_INET6 + && ainfo->ai_family != AF_INET6 #endif /* HAVE_IPV6 */ - ) - continue; + ) + continue; sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); if (sock < 0) - continue; + continue; sockopt_v6only (ainfo->ai_family, sock); sockopt_reuseaddr (sock); @@ -1928,17 +1924,17 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen); if (ret < 0) - { - close (sock); /* Avoid sd leak. */ - continue; - } + { + close (sock); /* Avoid sd leak. */ + continue; + } ret = listen (sock, 3); - if (ret < 0) - { - close (sock); /* Avoid sd leak. */ - continue; - } + if (ret < 0) + { + close (sock); /* Avoid sd leak. */ + continue; + } vty_event (VTY_SERV, sock, NULL); } @@ -1969,7 +1965,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) case AF_INET6: naddr=&su.sin6.sin6_addr; break; -#endif +#endif } if(naddr) @@ -1977,11 +1973,11 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) { case -1: zlog_err("bad address %s",addr); - naddr=NULL; - break; + naddr=NULL; + break; case 0: zlog_err("error translating address %s: %s",addr,safe_strerror(errno)); - naddr=NULL; + naddr=NULL; } /* Make new socket. */ @@ -1998,16 +1994,16 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family) if (ret < 0) { zlog_warn("can't bind socket"); - close (accept_sock); /* Avoid sd leak. */ + close (accept_sock); /* Avoid sd leak. */ return; } /* Listen socket under queue 3. */ ret = listen (accept_sock, 3); - if (ret < 0) + if (ret < 0) { zlog (NULL, LOG_WARNING, "can't listen socket"); - close (accept_sock); /* Avoid sd leak. */ + close (accept_sock); /* Avoid sd leak. */ return; } @@ -2029,7 +2025,7 @@ vty_serv_un (const char *path) struct sockaddr_un serv; mode_t old_mask; struct zprivs_ids_t ids; - + /* First of all, unlink existing socket */ unlink (path); @@ -2058,7 +2054,7 @@ vty_serv_un (const char *path) if (ret < 0) { zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno)); - close (sock); /* Avoid sd leak. */ + close (sock); /* Avoid sd leak. */ return; } @@ -2066,14 +2062,14 @@ vty_serv_un (const char *path) if (ret < 0) { zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno)); - close (sock); /* Avoid sd leak. */ + close (sock); /* Avoid sd leak. */ return; } umask (old_mask); zprivs_get_ids(&ids); - + if (ids.gid_vty > 0) { /* set group of socket */ @@ -2097,7 +2093,7 @@ vtysh_accept (struct thread *thread) int client_len; struct sockaddr_un client; struct vty *vty; - + accept_sock = THREAD_FD (thread); vty_event (VTYSH_SERV, accept_sock, NULL); @@ -2106,7 +2102,7 @@ vtysh_accept (struct thread *thread) client_len = sizeof (struct sockaddr_un); sock = accept (accept_sock, (struct sockaddr *) &client, - (socklen_t *) &client_len); + (socklen_t *) &client_len); if (sock < 0) { @@ -2121,7 +2117,7 @@ vtysh_accept (struct thread *thread) close (sock); return -1; } - + #ifdef VTYSH_DEBUG printf ("VTY shell accept\n"); #endif /* VTYSH_DEBUG */ @@ -2176,16 +2172,16 @@ vtysh_read (struct thread *thread) if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0) { if (nbytes < 0) - { - if (ERRNO_IO_RETRY(errno)) - { - vty_event (VTYSH_READ, sock, vty); - return 0; - } - vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: read failed on vtysh client fd %d, closing: %s", - __func__, sock, safe_strerror(errno)); - } + { + if (ERRNO_IO_RETRY(errno)) + { + vty_event (VTYSH_READ, sock, vty); + return 0; + } + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + zlog_warn("%s: read failed on vtysh client fd %d, closing: %s", + __func__, sock, safe_strerror(errno)); + } buffer_reset(vty->obuf); vty_close (vty); #ifdef VTYSH_DEBUG @@ -2203,25 +2199,25 @@ vtysh_read (struct thread *thread) vty_ensure(vty, vty->length+1); vty->buf[vty->length++] = *p; if (*p == '\0') - { - /* Pass this line to parser. */ - ret = vty_execute (vty); - /* Note that vty_execute clears the command buffer and resets - vty->length to 0. */ + { + /* Pass this line to parser. */ + ret = vty_execute (vty); + /* Note that vty_execute clears the command buffer and resets + vty->length to 0. */ - /* Return result. */ + /* Return result. */ #ifdef VTYSH_DEBUG - printf ("result: %d\n", ret); - printf ("vtysh node: %d\n", vty->node); + printf ("result: %d\n", ret); + printf ("vtysh node: %d\n", vty->node); #endif /* VTYSH_DEBUG */ - header[3] = ret; - buffer_put(vty->obuf, header, 4); + header[3] = ret; + buffer_put(vty->obuf, header, 4); - if (!vty->t_write && (vtysh_flush(vty) < 0)) - /* Try to flush results; exit if a write error occurs. */ - return 0; - } + if (!vty->t_write && (vtysh_flush(vty) < 0)) + /* Try to flush results; exit if a write error occurs. */ + return 0; + } } vty_event (VTYSH_READ, sock, vty); @@ -2350,14 +2346,14 @@ vty_read_file (FILE *confp) vty->fd = STDIN_FILENO; vty->type = VTY_FILE; vty->node = CONFIG_NODE; - + /* Execute configuration file */ ret = config_from_file (vty, confp, &line_num); /* Flush any previous errors before printing messages below */ buffer_flush_all (vty->obuf, vty->fd); - if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) + if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) { switch (ret) { @@ -2369,7 +2365,7 @@ vty_read_file (FILE *confp) break; } fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n", - line_num, vty->error_buf); + line_num, vty->error_buf); } vty_close (vty); @@ -2384,7 +2380,7 @@ vty_use_backup_config (char *fullpath) int tmp, sav; int c; char buffer[512]; - + fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1); strcpy (fullpath_sav, fullpath); strcat (fullpath_sav, CONF_BACKUP_EXT); @@ -2396,7 +2392,7 @@ vty_use_backup_config (char *fullpath) fullpath_tmp = malloc (strlen (fullpath) + 8); sprintf (fullpath_tmp, "%s.XXXXXX", fullpath); - + /* Open file to configuration write. */ tmp = mkstemp (fullpath_tmp); if (tmp < 0) @@ -2414,21 +2410,21 @@ vty_use_backup_config (char *fullpath) free (fullpath_tmp); return NULL; } - + while((c = read (sav, buffer, 512)) > 0) { if (write (tmp, buffer, c) <= 0) - { - free (fullpath_sav); - free (fullpath_tmp); - close (sav); - close (tmp); - return NULL; - } + { + free (fullpath_sav); + free (fullpath_tmp); + close (sav); + close (tmp); + return NULL; + } } close (sav); close (tmp); - + if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0) { unlink (fullpath_tmp); @@ -2436,12 +2432,12 @@ vty_use_backup_config (char *fullpath) free (fullpath_tmp); return NULL; } - + if (link (fullpath_tmp, fullpath) == 0) ret = fopen (fullpath, "r"); unlink (fullpath_tmp); - + free (fullpath_sav); free (fullpath_tmp); return ret; @@ -2463,12 +2459,12 @@ vty_read_config (char *config_file, if (! IS_DIRECTORY_SEP (config_file[0])) { if (getcwd (cwd, MAXPATHLEN) == NULL) - { - fprintf (stderr, "Failure to determine Current Working Directory %d!\n", errno); - exit (1); - } - tmp = XMALLOC (MTYPE_TMP, - strlen (cwd) + strlen (config_file) + 2); + { + fprintf (stderr, "Failure to determine Current Working Directory %d!\n", errno); + exit (1); + } + tmp = XMALLOC (MTYPE_TMP, + strlen (cwd) + strlen (config_file) + 2); sprintf (tmp, "%s/%s", cwd, config_file); fullpath = tmp; } @@ -2481,14 +2477,14 @@ vty_read_config (char *config_file, { fprintf (stderr, "%s: failed to open configuration file %s: %s\n", __func__, fullpath, safe_strerror (errno)); - + confp = vty_use_backup_config (fullpath); if (confp) fprintf (stderr, "WARNING: using backup configuration file!\n"); else { - fprintf (stderr, "can't open configuration file [%s]\n", - config_file); + fprintf (stderr, "can't open configuration file [%s]\n", + config_file); exit(1); } } @@ -2520,7 +2516,7 @@ vty_read_config (char *config_file, { ret = stat (integrate_default, &conf_stat); if (ret >= 0) - goto tmp_free_and_out; + goto tmp_free_and_out; } #endif /* VTYSH */ confp = fopen (config_default_dir, "r"); @@ -2528,7 +2524,7 @@ vty_read_config (char *config_file, { fprintf (stderr, "%s: failed to open configuration file %s: %s\n", __func__, config_default_dir, safe_strerror (errno)); - + confp = vty_use_backup_config (config_default_dir); if (confp) { @@ -2538,10 +2534,10 @@ vty_read_config (char *config_file, else { fprintf (stderr, "can't open configuration file [%s]\n", - config_default_dir); - goto tmp_free_and_out; + config_default_dir); + goto tmp_free_and_out; } - } + } else fullpath = config_default_dir; } @@ -2560,23 +2556,23 @@ tmp_free_and_out: /* Small utility function which output log to the VTY. */ void vty_log (const char *level, const char *proto_str, - const char *format, struct timestamp_control *ctl, va_list va) + const char *format, struct timestamp_control *ctl, va_list va) { unsigned int i; struct vty *vty; - + if (!vtyvec) return; for (i = 0; i < vector_active (vtyvec); i++) if ((vty = vector_slot (vtyvec, i)) != NULL) if (vty->monitor) - { - va_list ac; - va_copy(ac, va); - vty_log_out (vty, level, proto_str, format, ctl, ac); - va_end(ac); - } + { + va_list ac; + va_copy(ac, va); + vty_log_out (vty, level, proto_str, format, ctl, ac); + va_end(ac); + } } /* Async-signal-safe version of vty_log for fixed strings. */ @@ -2590,7 +2586,7 @@ vty_log_fixed (char *buf, size_t len) /* vty may not have been initialised */ if (!vtyvec) return; - + iov[0].iov_base = buf; iov[0].iov_len = len; iov[1].iov_base = crlf; @@ -2600,13 +2596,13 @@ vty_log_fixed (char *buf, size_t len) { struct vty *vty; if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor) - /* N.B. We don't care about the return code, since process is - most likely just about to die anyway. */ - if (writev(vty->wfd, iov, 2) == -1) - { - fprintf(stderr, "Failure to writev: %d\n", errno); - exit(-1); - } + /* N.B. We don't care about the return code, since process is + most likely just about to die anyway. */ + if (writev(vty->wfd, iov, 2) == -1) + { + fprintf(stderr, "Failure to writev: %d\n", errno); + exit(-1); + } } } @@ -2663,28 +2659,28 @@ vty_event (enum event event, int sock, struct vty *vty) /* Time out treatment. */ if (vty->v_timeout) - { - if (vty->t_timeout) - thread_cancel (vty->t_timeout); - vty->t_timeout = - thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); - } + { + if (vty->t_timeout) + thread_cancel (vty->t_timeout); + vty->t_timeout = + thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); + } break; case VTY_WRITE: if (! vty->t_write) - vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock); + vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock); break; case VTY_TIMEOUT_RESET: if (vty->t_timeout) - { - thread_cancel (vty->t_timeout); - vty->t_timeout = NULL; - } + { + thread_cancel (vty->t_timeout); + vty->t_timeout = NULL; + } if (vty->v_timeout) - { - vty->t_timeout = - thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); - } + { + vty->t_timeout = + thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); + } break; } } @@ -2700,8 +2696,8 @@ DEFUN (config_who, for (i = 0; i < vector_active (vtyvec); i++) if ((v = vector_slot (vtyvec, i)) != NULL) vty_out (vty, "%svty[%d] connected from %s.%s", - v->config ? "*" : " ", - i, v->address, VTY_NEWLINE); + v->config ? "*" : " ", + i, v->address, VTY_NEWLINE); return CMD_SUCCESS; } @@ -2794,7 +2790,7 @@ DEFUN (no_vty_access_class, if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0]))) { vty_out (vty, "Access-class is not currently applied to vty%s", - VTY_NEWLINE); + VTY_NEWLINE); return CMD_WARNING; } @@ -2835,7 +2831,7 @@ DEFUN (no_vty_ipv6_access_class, (argc && strcmp(vty_ipv6_accesslist_name, argv[0]))) { vty_out (vty, "IPv6 access-class is not currently applied to vty%s", - VTY_NEWLINE); + VTY_NEWLINE); return CMD_WARNING; } @@ -2947,13 +2943,13 @@ DEFUN (show_history, for (index = vty->hindex + 1; index != vty->hindex;) { if (index == VTY_MAXHIST) - { - index = 0; - continue; - } + { + index = 0; + continue; + } if (vty->hist[index] != NULL) - vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE); + vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE); index++; } @@ -2980,22 +2976,22 @@ vty_config_write (struct vty *vty) if (vty_accesslist_name) vty_out (vty, " access-class %s%s", - vty_accesslist_name, VTY_NEWLINE); + vty_accesslist_name, VTY_NEWLINE); if (vty_ipv6_accesslist_name) vty_out (vty, " ipv6 access-class %s%s", - vty_ipv6_accesslist_name, VTY_NEWLINE); + vty_ipv6_accesslist_name, VTY_NEWLINE); /* exec-timeout */ if (vty_timeout_val != VTY_TIMEOUT_DEFAULT) - vty_out (vty, " exec-timeout %ld %ld%s", - vty_timeout_val / 60, - vty_timeout_val % 60, VTY_NEWLINE); + vty_out (vty, " exec-timeout %ld %ld%s", + vty_timeout_val / 60, + vty_timeout_val % 60, VTY_NEWLINE); /* login */ if (no_password_check) vty_out (vty, " no login%s", VTY_NEWLINE); - + if (restricted_mode != restricted_mode_default) { if (restricted_mode_default) @@ -3006,7 +3002,7 @@ vty_config_write (struct vty *vty) if (do_log_commands) vty_out (vty, "log commands%s", VTY_NEWLINE); - + vty_out (vty, "!%s", VTY_NEWLINE); return CMD_SUCCESS; @@ -3030,16 +3026,16 @@ vty_reset () for (i = 0; i < vector_active (vtyvec); i++) if ((vty = vector_slot (vtyvec, i)) != NULL) { - buffer_reset (vty->obuf); - vty->status = VTY_CLOSE; - vty_close (vty); + buffer_reset (vty->obuf); + vty->status = VTY_CLOSE; + vty_close (vty); } for (i = 0; i < vector_active (Vvty_serv_thread); i++) if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL) { - thread_cancel (vty_serv_thread); - vector_slot (Vvty_serv_thread, i) = NULL; + thread_cancel (vty_serv_thread); + vector_slot (Vvty_serv_thread, i) = NULL; close (i); } @@ -3074,15 +3070,15 @@ vty_save_cwd (void) * Hence not worrying about it too much. */ if (!chdir (SYSCONFDIR)) - { - fprintf(stderr, "Failure to chdir to %s, errno: %d\n", SYSCONFDIR, errno); - exit(-1); - } + { + fprintf(stderr, "Failure to chdir to %s, errno: %d\n", SYSCONFDIR, errno); + exit(-1); + } if (getcwd (cwd, MAXPATHLEN) == NULL) - { - fprintf(stderr, "Failure to getcwd, errno: %d\n", errno); - exit(-1); - } + { + fprintf(stderr, "Failure to getcwd, errno: %d\n", errno); + exit(-1); + } } vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1); -- 2.39.5