diff options
Diffstat (limited to 'lib/command.c')
-rw-r--r-- | lib/command.c | 2940 |
1 files changed, 1738 insertions, 1202 deletions
diff --git a/lib/command.c b/lib/command.c index 264e0f7b..67a9ab04 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1,11 +1,11 @@ /* $Id$ - + Command interpreter routine for virtual terminal [aka TeletYpe] Copyright (C) 1997, 98, 99 Kunihiro Ishiguro 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 @@ -30,8 +30,12 @@ Boston, MA 02111-1307, USA. */ #include "thread.h" #include "vector.h" #include "vty.h" +#include "uty.h" +#include "qstring.h" #include "command.h" +#include "command_execute.h" #include "workqueue.h" +#include "command_queue.h" /* Command vector which includes some level of command lists. Normally each daemon maintains each own cmdvec. */ @@ -89,11 +93,17 @@ Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ \r\n"; +#ifdef QDEBUG +const char* debug_banner = + QUAGGA_PROGNAME " version " QUAGGA_VERSION " QDEBUG=" QDEBUG_NAME " " + __DATE__ " " __TIME__ ; +#endif + 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 }, @@ -145,7 +155,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; @@ -160,11 +170,11 @@ print_version (const char *progname) printf ("%s\n", QUAGGA_COPYRIGHT); } - + /* 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 (const char* const* argv, int argc, int shift) { int i; size_t len; @@ -188,16 +198,7 @@ argv_concat (const char **argv, int argc, int shift) return str; } -/* Install top node of command vector. */ -void -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); -} - +#if 0 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< /* Compare two command's string. Used in sort_node (). */ static int cmp_node (const void *p, const void *q) @@ -217,102 +218,245 @@ cmp_desc (const void *p, const void *q) return strcmp (a->cmd, b->cmd); } +#endif //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + +/* Install top node of command vector. */ +void +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 (0); +} + +/* Compare two command's string. Used in sort_node (). */ +static int +cmp_node (const struct cmd_element **a, const struct cmd_element **b) +{ + return strcmp ((*a)->string, (*b)->string); +} + +static int +cmp_desc (const struct desc **a, const struct desc **b) +{ + return strcmp ((*a)->cmd, (*b)->cmd); +} + /* Sort each node's command element according to command string. */ void sort_node () { - unsigned int i, j; - struct cmd_node *cnode; - vector descvec; - struct cmd_element *cmd_element; + unsigned int i ; - for (i = 0; i < vector_active (cmdvec); i++) - if ((cnode = vector_slot (cmdvec, i)) != NULL) - { - vector cmd_vector = cnode->cmd_vector; - qsort (cmd_vector->index, vector_active (cmd_vector), - sizeof (void *), cmp_node); + for (i = 0; i < vector_length(cmdvec); i++) + { + struct cmd_node *cnode; + vector cmd_vector ; + unsigned int j; - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && vector_active (cmd_element->strvec)) - { - descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - qsort (descvec->index, vector_active (descvec), - sizeof (void *), cmp_desc); - } - } -} + cnode = vector_get_item(cmdvec, i) ; -/* Breaking up string into each command piece. I assume given - character is separated by a space character. Return value is a - vector which includes char ** data element. */ -vector -cmd_make_strvec (const char *string) + if (cnode == NULL) + continue ; + + cmd_vector = cnode->cmd_vector; + if (cmd_vector == NULL) + continue ; + + vector_sort(cmd_vector, (vector_sort_cmp*)cmp_node) ; + + for (j = 0; j < vector_length(cmd_vector); j++) + { + struct cmd_element *cmd_element ; + vector descvec ; + + cmd_element = vector_get_item (cmd_vector, j); + if (cmd_element == NULL) + continue ; + + descvec = vector_get_last_item(cmd_element->strvec) ; + if (descvec == NULL) + continue ; + + vector_sort(descvec, (vector_sort_cmp*)cmp_desc) ; + } ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * Take string and break it into tokens. + * + * Discards leading and trailing white-space. + * + * Treats lines that start with '!' or '#' (after any leading white-space) + * as empty -- these are comment lines. + * + * Tokens are non-whitespace separated by one or more white-space. + * + * White-space is anything that isspace() thinks is a space. (Which in the + * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.) + * + * Returns: NULL => empty line (after white-space trimming) or comment line. + * otherwise: is vector containing one or more tokens. + * + * .... + * + * + * Note: all the tokens in the vector have at least one character, and no + * entries are NULL. + * + * NB: it is the caller's responsibility to release the vector and its contents, + * see cmd_free_strvec(). + */ +static vector +cmd_make_vline(vector vline, qstring qs, const char *string) { - const char *cp, *start; - char *token; - int strlen; - vector strvec; - + char *token, *tp ; + const char *cp, *sp, *ep, *op ; + + /* Reset any existing vline, and empty the qstring if given. */ + if (vline != NULL) + vector_set_length(vline, 0) ; + + qs_clear(qs) ; + + /* Strip leading and trailing white-space and deal with empty or effectively + * empty lines -- comment lines are treated as effectively empty. + */ + cp = string; + if (string == NULL) return NULL; - - cp = string; - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') + while (isspace((int) *cp)) cp++; - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; + ep = cp + strlen(cp) ; - if (*cp == '!' || *cp == '#') + while ((ep > cp) && (isspace((int)*(ep - 1)))) + --ep ; + + if ((cp == ep) || (*cp == '!') || (*cp == '#')) return NULL; - /* Prepare return vector. */ - strvec = vector_init (VECTOR_MIN_SIZE); + /* Prepare return vector -- expect some reasonable number of tokens. */ + if (vline == NULL) + vline = vector_init(10) ; - /* Copy each command piece and set into vector. */ - while (1) + /* If writing the words to a qstring, copy the body of the original (less + * any leading/trailing whitespace) to the qstring and '\0' terminate. + */ + if (qs != NULL) { - start = cp; - while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && - *cp != '\0') - cp++; - strlen = cp - start; - token = XMALLOC (MTYPE_STRVEC, strlen + 1); - memcpy (token, start, strlen); - *(token + strlen) = '\0'; - vector_set (strvec, token); - - while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && - *cp != '\0') - cp++; - - if (*cp == '\0') - return strvec; + qs_set_n(qs, cp, ep - cp) ; + tp = (char*)qs->body ; /* start at the beginning */ } + else + tp = NULL ; /* not used, but not undefined */ + + op = cp ; /* original start position */ + + /* Now have string cp..ep with no leading/trailing whitespace. + * + * If using a qstring, a copy of that exists at tp, complete with terminating + * '\0'. Writes '\0' terminators after each word found -- overwriting first + * separating white-space or the '\0' at the end. + * + * If not using a qstring, construct a new MTYPE_STRVEC for each word. + */ + while (cp < ep) + { + while (isspace((int) *cp)) + cp++ ; /* skip white-space */ + + sp = cp ; + while ((cp < ep) && !isspace((int) *cp)) + cp++ ; /* eat token characters */ + + if (qs == NULL) + { + /* creating array of MTYPE_STRVEC */ + size_t len ; + + len = cp - sp ; + token = XMALLOC (MTYPE_STRVEC, len + 1); + memcpy (token, sp, len); + *(token + len) = '\0'; + } + else + { + /* using qstring */ + token = tp + (sp - op) ; /* token in qstring */ + *(tp + (cp - op)) = '\0' ; /* terminate */ + } ; + + vector_push_item(vline, token); + } ; + + return vline ; } -/* Free allocated string vector. */ -void -cmd_free_strvec (vector v) +/*------------------------------------------------------------------------------ + * Take string and break it into tokens. + * + * Discards leading and trailing white-space. + * + * Treats lines that start with '!' or '#' (after any leading white-space) + * as empty -- these are comment lines. + * + * Tokens are non-whitespace separated by one or more white-space. + * + * White-space is anything that isspace() thinks is a space. (Which in the + * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.) + * + * Returns: NULL => empty line (after white-space trimming) or comment line. + * otherwise: is vector containing one or more tokens. + * + * Note: all the tokens in the vector have at least one character, and no + * entries are NULL. + * + * NB: it is the caller's responsibility to release the vector and its contents, + * see cmd_free_strvec(). + */ +extern vector +cmd_make_strvec (const char *string) { - unsigned int i; - char *cp; + return cmd_make_vline(NULL, NULL, string) ; +} ; + +/*------------------------------------------------------------------------------ + * Add given string to vector of strings. + * + * Create vector if required. + */ +extern vector +cmd_add_to_strvec (vector strvec, const char* str) +{ + if (strvec == NULL) + strvec = vector_init(1) ; - if (!v) - return; + vector_push_item(strvec, XSTRDUP(MTYPE_STRVEC, str)); - for (i = 0; i < vector_active (v); i++) - if ((cp = vector_slot (v, i)) != NULL) - XFREE (MTYPE_STRVEC, cp); + return strvec ; +} ; - vector_free (v); -} +/*------------------------------------------------------------------------------ + * Free allocated string vector (if any) and all its contents. + * + * Note that this is perfectly happy with strvec == NULL. + */ +extern void +cmd_free_strvec (vector strvec) +{ + char *cp; + + /* Note that vector_ream_free() returns NULL if strvec == NULL */ + while((cp = vector_ream_free(strvec)) != NULL) + XFREE (MTYPE_STRVEC, cp); +} ; + +/*----------------------------------------------------------------------------*/ /* Fetch next description. Used in cmd_make_descvec(). */ static char * @@ -321,7 +465,7 @@ cmd_desc_str (const char **string) const char *cp, *start; char *token; int strlen; - + cp = *string; if (cp == NULL) @@ -370,7 +514,7 @@ cmd_make_descvec (const char *string, const char *descstr) if (cp == NULL) return NULL; - allvec = vector_init (VECTOR_MIN_SIZE); + allvec = vector_init (0); while (1) { @@ -396,7 +540,7 @@ cmd_make_descvec (const char *string, const char *descstr) } cp++; } - + while (isspace ((int) *cp) && *cp != '\0') cp++; @@ -406,7 +550,7 @@ cmd_make_descvec (const char *string, const char *descstr) cp++; } - if (*cp == '\0') + if (*cp == '\0') return allvec; sp = cp; @@ -428,14 +572,14 @@ cmd_make_descvec (const char *string, const char *descstr) { if (multiple == 1) { - strvec = vector_init (VECTOR_MIN_SIZE); + strvec = vector_init (0); vector_set (allvec, strvec); } multiple++; } else { - strvec = vector_init (VECTOR_MIN_SIZE); + strvec = vector_init (0); vector_set (allvec, strvec); } vector_set (strvec, desc); @@ -449,23 +593,27 @@ cmd_cmdsize (vector strvec) { unsigned int i; int size = 0; - vector descvec; - struct desc *desc; - for (i = 0; i < vector_active (strvec); i++) - if ((descvec = vector_slot (strvec, i)) != NULL) + for (i = 0; i < vector_length(strvec); i++) { - if ((vector_active (descvec)) == 1 - && (desc = vector_slot (descvec, 0)) != NULL) - { - if (desc->cmd == NULL || CMD_OPTION (desc->cmd)) - return size; - else - size++; - } - else - size++; - } + vector descvec; + + descvec = vector_get_item (strvec, i) ; + if (descvec == NULL) + continue ; + + if (vector_length(descvec) == 1) + { + struct desc *desc; + + desc = vector_get_item(descvec, 0) ; + if (desc != NULL) + if (desc->cmd == NULL || CMD_OPTION (desc->cmd)) + break ; + } + size++; + } ; + return size; } @@ -475,7 +623,19 @@ cmd_prompt (enum node_type node) { struct cmd_node *cnode; - cnode = vector_slot (cmdvec, node); + assert(cmdvec != NULL) ; + assert(cmdvec->p_items != NULL) ; + + cnode = NULL ; + if (node < cmdvec->limit) + cnode = vector_get_item (cmdvec, node); + + if (cnode == NULL) + { + zlog_err("Could not find prompt for node %d for", node) ; + return NULL ; + } ; + return cnode->prompt; } @@ -484,14 +644,14 @@ 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) + cnode = vector_get_item (cmdvec, ntype); + + if (cnode == NULL) { fprintf (stderr, "Command node %d doesn't exist, please check it\n", ntype); @@ -512,7 +672,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; @@ -527,7 +687,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'; @@ -539,15 +699,18 @@ zencrypt (const char *passwd) static int config_write_host (struct vty *vty) { + if (qpthreads_enabled) + vty_out (vty, "threaded%s", VTY_NEWLINE); + if (host.name) vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE); 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 { @@ -557,57 +720,57 @@ config_write_host (struct vty *vty) vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE); } - if (zlog_default->default_lvl != LOG_DEBUG) + if (zlog_get_default_lvl(NULL) != LOG_DEBUG) { vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s", VTY_NEWLINE); vty_out (vty, "log trap %s%s", - zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); + zlog_priority[zlog_get_default_lvl(NULL)], VTY_NEWLINE); } - if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) + if (host.logfile && (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != ZLOG_DISABLED)) { vty_out (vty, "log file %s", host.logfile); - if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl) + if (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != zlog_get_default_lvl(NULL)) vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); + zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)]); vty_out (vty, "%s", VTY_NEWLINE); } - if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) + if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != ZLOG_DISABLED) { vty_out (vty, "log stdout"); - if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl) + if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != zlog_get_default_lvl(NULL)) vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); + zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)]); vty_out (vty, "%s", VTY_NEWLINE); } - if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) + if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) == ZLOG_DISABLED) vty_out(vty,"no log monitor%s",VTY_NEWLINE); - else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl) + else if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) != zlog_get_default_lvl(NULL)) vty_out(vty,"log monitor %s%s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); + zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)],VTY_NEWLINE); - if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) + if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != ZLOG_DISABLED) { vty_out (vty, "log syslog"); - if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl) + if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != zlog_get_default_lvl(NULL)) vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); + zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG)]); vty_out (vty, "%s", VTY_NEWLINE); } - if (zlog_default->facility != LOG_DAEMON) + if (zlog_get_facility(NULL) != LOG_DAEMON) vty_out (vty, "log facility %s%s", - facility_name(zlog_default->facility), VTY_NEWLINE); + facility_name(zlog_get_facility(NULL)), VTY_NEWLINE); - if (zlog_default->record_priority == 1) + if (zlog_get_record_priority(NULL) == 1) vty_out (vty, "log record-priority%s", VTY_NEWLINE); - if (zlog_default->timestamp_precision > 0) + if (zlog_get_timestamp_precision(NULL) > 0) vty_out (vty, "log timestamp precision %d%s", - zlog_default->timestamp_precision, VTY_NEWLINE); + zlog_get_timestamp_precision(NULL), VTY_NEWLINE); if (host.advanced) vty_out (vty, "service advanced-vty%s", VTY_NEWLINE); @@ -631,7 +794,7 @@ config_write_host (struct vty *vty) static vector cmd_node_vector (vector v, enum node_type ntype) { - struct cmd_node *cnode = vector_slot (v, ntype); + struct cmd_node *cnode = vector_get_item (v, ntype); return cnode->cmd_vector; } @@ -683,21 +846,41 @@ cmd_filter_by_symbol (char *command, char *symbol) } #endif +/*============================================================================== + * Match functions. + * + * Is the given string a, possibly incomplete, value of the required kind ? + */ + /* Completion match types. */ -enum match_type +enum match_type { - no_match, + no_match, /* nope */ extend_match, + ipv4_prefix_match, ipv4_match, ipv6_prefix_match, ipv6_match, range_match, vararg_match, - partly_match, - exact_match + + partly_match, /* OK as far as it went */ + exact_match /* Syntactically complete */ }; +/*------------------------------------------------------------------------------ + * Is this an IPv4 Address: + * + * 999.999.999.999 -- where no part may be > 255 + * + * TODO: cmd_ipv4_match() seems to accept leading '.' ? + * TODO: cmd_ipv4_match() seems to accept leading zeros ? + * + * Returns: no_match -- improperly formed + * partly_match -- accepts empty string + * exact_match -- syntactically complete + */ static enum match_type cmd_ipv4_match (const char *str) { @@ -755,6 +938,22 @@ cmd_ipv4_match (const char *str) return exact_match; } +/*------------------------------------------------------------------------------ + * Is this an IPv4 Prefix: + * + * 999.999.999.999/99 -- where no part may be > 255, + * and prefix length may not be > 32 + * + * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ? + * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ? + * + * Returns: no_match -- improperly formed + * partly_match -- accepts empty string + * exact_match -- syntactically complete + * + * NB: partly_match is returned for anything valid before the '/', but which + * has no '/' or no number after the '/'. + */ static enum match_type cmd_ipv4_prefix_match (const char *str) { @@ -834,6 +1033,16 @@ cmd_ipv4_prefix_match (const char *str) return exact_match; } +/*------------------------------------------------------------------------------ + * Is this an IPv6 Address: + * + * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ? + * + * Returns: no_match -- improperly formed + * partly_match -- accepts empty string + * exact_match -- syntactically complete + */ + #define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%" #define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/" #define STATE_START 1 @@ -866,7 +1075,7 @@ cmd_ipv6_match (const char *str) * ::1.2.3.4 */ ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); - + if (ret == 1) return exact_match; @@ -952,6 +1161,19 @@ cmd_ipv6_match (const char *str) return exact_match; } +/*------------------------------------------------------------------------------ + * Is this an IPv6 Prefix: + * + * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ? + * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ? + * + * Returns: no_match -- improperly formed + * partly_match -- accepts empty string + * exact_match -- syntactically complete + * + * NB: partly_match is returned for anything valid before the '/', but which + * has no '/' or no number after the '/'. + */ static enum match_type cmd_ipv6_prefix_match (const char *str) { @@ -1071,7 +1293,7 @@ cmd_ipv6_prefix_match (const char *str) if (mask < 0 || mask > 128) return no_match; - + /* I don't know why mask < 13 makes command match partly. Forgive me to make this comments. I Want to set static default route because of lack of function to originate default in ospf6d; sorry @@ -1085,6 +1307,14 @@ cmd_ipv6_prefix_match (const char *str) #endif /* HAVE_IPV6 */ +/*------------------------------------------------------------------------------ + * Is this a decimal number in the allowed range: + * + * Returns: 1 => OK -- *including* empty string + * 0 => not a valid number, or not in required range + * (or invalid range !!) + */ + #define DECIMAL_STRLEN_MAX 10 static int @@ -1132,340 +1362,496 @@ cmd_range_match (const char *range, const char *str) return 1; } -/* Make completion match and return match type flag. */ +/*============================================================================== + * Command "filtering". + * + * The command parsing process starts with a (shallow) copy of the cmd_vector + * entry for the current "node". + * + * So cmd_v contains pointers to struct cmd_element values. When match fails, + * the pointer is set NULL -- so parsing is a process of reducing the cmd_v + * down to just the entries that match. + * + * Each cmd_element has a vector "strvec", which contains an entry for each + * "token" position. That entry is a vector containing the possible values at + * that position. + * + * + */ + +/*------------------------------------------------------------------------------ + * Make completion match and return match type flag. + * + * Takes: command -- address of candidate token + * cmd_v -- vector of commands that is being reduced/filtered + * index -- index of token (position in line -- 0 == first) + * + * Returns: any of the enum match_type values: + * + * no_match => no match of any kind + * + * extend_match => saw an optional token + * ipv4_prefix_match ) + * ipv4_match ) + * ipv6_prefix_match ) saw full or partial match for this + * ipv6_match ) + * range_match ) + * vararg_match ) + * + * partly_match => saw partial match for a keyword + * exact_match => saw exact match for a keyword + * + * Note that these return values are in ascending order of preference. So, + * if there are multiple possibilities at this position, will return the one + * furthest down this list. + */ static enum match_type -cmd_filter_by_completion (char *command, vector v, unsigned int index) +cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index) { unsigned int i; - const char *str; - struct cmd_element *cmd_element; + unsigned int k; enum match_type match_type; - vector descvec; - struct desc *desc; match_type = no_match; - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) - { - if (index >= vector_active (cmd_element->strvec)) - vector_slot (v, i) = NULL; - else - { - unsigned int j; - int matched = 0; - - descvec = vector_slot (cmd_element->strvec, index); - - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; - - if (CMD_VARARG (str)) - { - if (match_type < vararg_match) - match_type = vararg_match; - matched++; - } - else if (CMD_RANGE (str)) - { - if (cmd_range_match (str, command)) - { - if (match_type < range_match) - match_type = range_match; + /* If command and cmd_element string does not match, remove from vector */ + k = 0 ; + for (i = 0; i < vector_length (cmd_v); i++) + { + const char *str; + struct cmd_element *cmd_element; + vector descvec; + struct desc *desc; + unsigned int j; + int matched ; + + cmd_element = vector_get_item(cmd_v, i) ; + + /* Skip past cmd_v entries that have already been set NULL */ + if (cmd_element == NULL) + continue ; + + /* Discard cmd_v entry that has no token at the current position */ + descvec = vector_get_item(cmd_element->strvec, index) ; + if (descvec == NULL) + continue ; + + /* See if get any sort of match at current position */ + matched = 0 ; + for (j = 0; j < vector_length (descvec); j++) + { + desc = vector_get_item(descvec, j) ; + if (desc == NULL) + continue ; + + str = desc->cmd; + + if (CMD_VARARG (str)) + { + if (match_type < vararg_match) + match_type = vararg_match; + matched++; + } + else if (CMD_RANGE (str)) + { + if (cmd_range_match (str, command)) + { + if (match_type < range_match) + match_type = range_match; - matched++; - } - } + matched++; + } + } #ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command)) - { - if (match_type < ipv6_match) - match_type = ipv6_match; + else if (CMD_IPV6 (str)) + { + if (cmd_ipv6_match (command)) + { + if (match_type < ipv6_match) + match_type = ipv6_match; - matched++; - } - } - else if (CMD_IPV6_PREFIX (str)) - { - if (cmd_ipv6_prefix_match (command)) - { - if (match_type < ipv6_prefix_match) - match_type = ipv6_prefix_match; + matched++; + } + } + else if (CMD_IPV6_PREFIX (str)) + { + if (cmd_ipv6_prefix_match (command)) + { + if (match_type < ipv6_prefix_match) + match_type = ipv6_prefix_match; - matched++; - } - } + matched++; + } + } #endif /* HAVE_IPV6 */ - else if (CMD_IPV4 (str)) - { - if (cmd_ipv4_match (command)) - { - if (match_type < ipv4_match) - match_type = ipv4_match; + else if (CMD_IPV4 (str)) + { + if (cmd_ipv4_match (command)) + { + if (match_type < ipv4_match) + match_type = ipv4_match; - matched++; - } - } - else if (CMD_IPV4_PREFIX (str)) - { - if (cmd_ipv4_prefix_match (command)) - { - if (match_type < ipv4_prefix_match) - match_type = ipv4_prefix_match; - matched++; - } - } - else - /* Check is this point's argument optional ? */ - if (CMD_OPTION (str) || CMD_VARIABLE (str)) - { - if (match_type < extend_match) - match_type = extend_match; - matched++; - } - else if (strncmp (command, str, strlen (command)) == 0) - { - if (strcmp (command, str) == 0) - match_type = exact_match; - else - { - if (match_type < partly_match) - match_type = partly_match; - } - matched++; - } - } - if (!matched) - vector_slot (v, i) = NULL; - } - } - return match_type; -} + matched++; + } + } + else if (CMD_IPV4_PREFIX (str)) + { + if (cmd_ipv4_prefix_match (command)) + { + if (match_type < ipv4_prefix_match) + match_type = ipv4_prefix_match; + matched++; + } + } + else if (CMD_OPTION (str) || CMD_VARIABLE (str)) + /* Check is this point's argument optional ? */ + { + if (match_type < extend_match) + match_type = extend_match; + matched++; + } + else if (strncmp (command, str, strlen (command)) == 0) + { + if (strcmp (command, str) == 0) + match_type = exact_match; + else + { + if (match_type < partly_match) + match_type = partly_match; + } + matched++; + } ; + } ; + + /* Keep cmd_v entry that has a match at this position */ + if (matched) + vector_set_item(cmd_v, k++, cmd_element) ; + } ; -/* Filter vector by command character with index. */ + vector_set_length(cmd_v, k) ; /* discard what did not keep */ + + return match_type; +} ; + +/*------------------------------------------------------------------------------ + * Filter vector by command character with index. + * + * This appears to be identical to cmd_filter_by_completion(), except that + * when matching keywords, requires an exact match. + * + * TODO: see if can merge cmd_filter_by_completion() & cmd_filter_by_string() + */ static enum match_type -cmd_filter_by_string (char *command, vector v, unsigned int index) +cmd_filter_by_string (char *command, vector cmd_v, unsigned int index) { - unsigned int i; - const char *str; - struct cmd_element *cmd_element; + unsigned int i ; + unsigned int k ; enum match_type match_type; - vector descvec; - struct desc *desc; match_type = no_match; - /* If command and cmd_element string does not match set NULL to vector */ - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) - { - /* If given index is bigger than max string vector of command, - set NULL */ - if (index >= vector_active (cmd_element->strvec)) - vector_slot (v, i) = NULL; - else - { - unsigned int j; - int matched = 0; + /* If command and cmd_element string do match, keep in vector */ + k = 0 ; + for (i = 0; i < vector_length(cmd_v); i++) + { + unsigned int j; + int matched ; + const char *str; + struct cmd_element *cmd_element; + vector descvec; + struct desc *desc; + + cmd_element = vector_get_item(cmd_v, i) ; + + /* Skip past NULL cmd_v entries (just in case) */ + if (cmd_element == NULL) + continue ; + + /* Discard cmd_v entry that has no token at the current position */ + descvec = vector_get_item (cmd_element->strvec, index) ; + if (descvec == NULL) + continue ; + + /* See if have a match against any of the current possibilities */ + matched = 0 ; + for (j = 0; j < vector_length(descvec); j++) + { + desc = vector_get_item (descvec, j) ; + if (desc == NULL) + continue ; + + str = desc->cmd; + + if (CMD_VARARG (str)) + { + if (match_type < vararg_match) + match_type = vararg_match; + matched++; + } + else if (CMD_RANGE (str)) + { + if (cmd_range_match (str, command)) + { + if (match_type < range_match) + match_type = range_match; + matched++; + } + } +#ifdef HAVE_IPV6 + else if (CMD_IPV6 (str)) + { + if (cmd_ipv6_match (command) == exact_match) + { + if (match_type < ipv6_match) + match_type = ipv6_match; + matched++; + } + } + else if (CMD_IPV6_PREFIX (str)) + { + if (cmd_ipv6_prefix_match (command) == exact_match) + { + if (match_type < ipv6_prefix_match) + match_type = ipv6_prefix_match; + matched++; + } + } +#endif /* HAVE_IPV6 */ + else if (CMD_IPV4 (str)) + { + if (cmd_ipv4_match (command) == exact_match) + { + if (match_type < ipv4_match) + match_type = ipv4_match; + matched++; + } + } + else if (CMD_IPV4_PREFIX (str)) + { + if (cmd_ipv4_prefix_match (command) == exact_match) + { + if (match_type < ipv4_prefix_match) + match_type = ipv4_prefix_match; + matched++; + } + } + else if (CMD_OPTION (str) || CMD_VARIABLE (str)) + { + if (match_type < extend_match) + match_type = extend_match; + matched++; + } + else + { + if (strcmp (command, str) == 0) + { + match_type = exact_match; + matched++; + } ; + } ; + } ; - descvec = vector_slot (cmd_element->strvec, index); + /* Keep cmd_element if have a match */ + if (matched) + vector_set_item(cmd_v, k++, cmd_element) ; + } ; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; + vector_set_length(cmd_v, k) ; /* discard what did not keep */ - if (CMD_VARARG (str)) - { - if (match_type < vararg_match) - match_type = vararg_match; - matched++; - } - else if (CMD_RANGE (str)) - { - if (cmd_range_match (str, command)) - { - if (match_type < range_match) - match_type = range_match; - matched++; - } - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command) == exact_match) - { - if (match_type < ipv6_match) - match_type = ipv6_match; - matched++; - } - } - else if (CMD_IPV6_PREFIX (str)) - { - if (cmd_ipv6_prefix_match (command) == exact_match) - { - if (match_type < ipv6_prefix_match) - match_type = ipv6_prefix_match; - matched++; - } - } -#endif /* HAVE_IPV6 */ - else if (CMD_IPV4 (str)) - { - if (cmd_ipv4_match (command) == exact_match) - { - if (match_type < ipv4_match) - match_type = ipv4_match; - matched++; - } - } - else if (CMD_IPV4_PREFIX (str)) - { - if (cmd_ipv4_prefix_match (command) == exact_match) - { - if (match_type < ipv4_prefix_match) - match_type = ipv4_prefix_match; - matched++; - } - } - else if (CMD_OPTION (str) || CMD_VARIABLE (str)) - { - if (match_type < extend_match) - match_type = extend_match; - matched++; - } - else - { - if (strcmp (command, str) == 0) - { - match_type = exact_match; - matched++; - } - } - } - if (!matched) - vector_slot (v, i) = NULL; - } - } return match_type; } -/* Check ambiguous match */ +/*------------------------------------------------------------------------------ + * Check for ambiguous match + * + * Given the best that cmd_filter_by_completion() or cmd_filter_by_string() + * found, check as follows: + * + * 1. discard all commands for which do not have the type of match selected. + * + * See above for the ranking of matches. + * + * 2. for "partial match", look out for matching more than one keyword, and + * return 1 if finds that. + * + * 3. for "range match", look out for matching more than one range, and + * return 1 if finds that. + * + * 4. for ipv4_prefix_match and ipv6_prefix_match, if get a "partial match", + * return 2. + * + * This appears to catch things which are supposed to be prefixes, but + * do not have a '/' or do not have any digits after the '/'. + * + * Takes: command -- address of candidate token + * cmd_v -- vector of commands that is being reduced/filtered + * index -- index of token (position in line -- 0 == first) + * type -- as returned by cmd_filter_by_completion() + * or cmd_filter_by_string() + * + * Returns: 0 => not ambiguous + * 1 => ambiguous -- the candidate token matches more than one + * keyword, or the candidate number matches more + * than one number range. + * 2 => partial match for ipv4_prefix or ipv6_prefix + * (missing '/' or no digits after '/'). + * + * NB: it is assumed that cannot find both 1 and 2 states. But in any case, + * returns 1 in preference. + */ static int -is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) +is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type) { unsigned int i; - unsigned int j; - const char *str = NULL; - struct cmd_element *cmd_element; - const char *matched = NULL; - vector descvec; - struct desc *desc; + unsigned int k; + int ret ; - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) - { - int match = 0; + ret = 0 ; /* all's well so far */ + k = 0 ; /* nothing kept, yet */ + + for (i = 0; i < vector_length (cmd_v); i++) + { + unsigned int j; + struct cmd_element *cmd_element; + const char *str_matched ; + vector descvec; + struct desc *desc; + int matched ; + enum match_type mt ; + + cmd_element = vector_get_item (cmd_v, i) ; + + /* Skip past NULL cmd_v entries (just in case) */ + if (cmd_element == NULL) + continue ; + + /* The cmd_v entry MUST have a token at the current position */ + descvec = vector_get_item (cmd_element->strvec, index) ; + assert(descvec != NULL) ; + + /* See if have a match against any of the current possibilities + * + * str_matched is set the first time get a partial string match, + * or the first time get a number range match. + * + * If get a second partial string match or number range match, then + * unless + */ + str_matched = NULL ; + matched = 0; + for (j = 0; j < vector_length (descvec); j++) + { + enum match_type ret; + const char *str ; - descvec = vector_slot (cmd_element->strvec, index); + desc = vector_get_item (descvec, j) ; + if (desc == NULL) + continue ; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - enum match_type ret; - - str = desc->cmd; + str = desc->cmd; + + switch (type) + { + case exact_match: + if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + && strcmp (command, str) == 0) + matched++; + break; + + case partly_match: + if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + && strncmp (command, str, strlen (command)) == 0) + { + if (str_matched && (strcmp (str_matched, str) != 0)) + ret = 1; /* There is ambiguous match. */ + else + str_matched = str; + matched++; + } + break; + + case range_match: + if (cmd_range_match (str, command)) + { + if (str_matched && strcmp (str_matched, str) != 0) + ret = 1; + else + str_matched = str; + matched++; + } + break; - switch (type) - { - case exact_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) - && strcmp (command, str) == 0) - match++; - break; - case partly_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) - && 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_IPV6 (str)) - 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. */ + case ipv6_match: + if (CMD_IPV6 (str)) + matched++; + break; - match++; - } - break; + case ipv6_prefix_match: + if ((mt = cmd_ipv6_prefix_match (command)) != no_match) + { + if (mt == partly_match) + if (ret != 1) + ret = 2; /* There is incomplete match. */ + + matched++; + } + break; #endif /* HAVE_IPV6 */ - case ipv4_match: - if (CMD_IPV4 (str)) - 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 (CMD_OPTION (str) || CMD_VARIABLE (str)) - match++; - break; - case no_match: - default: - break; - } - } - if (!match) - vector_slot (v, i) = NULL; - } - return 0; -} + case ipv4_match: + if (CMD_IPV4 (str)) + matched++; + break; -/* If src matches dst return dst string, otherwise return NULL */ + case ipv4_prefix_match: + if ((mt = cmd_ipv4_prefix_match (command)) != no_match) + { + if (mt == partly_match) + if (ret != 1) + ret = 2; /* There is incomplete match. */ + + matched++; + } + break; + + case extend_match: + if (CMD_OPTION (str) || CMD_VARIABLE (str)) + matched++; + break; + + case no_match: + default: + break; + } ; + } ; + + /* Keep cmd_element if have a match */ + if (matched) + vector_set_item(cmd_v, k++, cmd_element) ; + } ; + + vector_set_length(cmd_v, k) ; /* discard what did not keep */ + + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * If src matches dst return dst string, otherwise return NULL + * + * Returns NULL if dst is an option, variable of vararg. + * + * NULL or empty src are deemed to match. + */ static const char * cmd_entry_function (const char *src, const char *dst) { - /* Skip variable arguments. */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) || - CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst)) + if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst)) return NULL; - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) + if ((src == NULL) || (*src == '\0')) return dst; - /* Matched with input string. */ if (strncmp (src, dst, strlen (src)) == 0) return dst; @@ -1537,16 +1923,20 @@ cmd_entry_function_desc (const char *src, const char *dst) return NULL; } -/* Check same string element existence. If it isn't there return - 1. */ -static int +/*------------------------------------------------------------------------------ + * Check same string element existence. + * + * Returns: 0 => found same string in the vector + * 1 => NOT found same string in the vector + */ +static bool 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) + for (i = 0; i < vector_length (v); i++) + if ((match = vector_get_item (v, i)) != NULL) if (strcmp (match, str) == 0) return 0; return 1; @@ -1554,35 +1944,29 @@ cmd_unique_string (vector v, const char *str) /* Compare string to description vector. If there is same string return 1 else return 0. */ -static int +static bool desc_unique_string (vector v, const char *str) { unsigned int i; struct desc *desc; - for (i = 0; i < vector_active (v); i++) - if ((desc = vector_slot (v, i)) != NULL) + for (i = 0; i < vector_length (v); i++) + if ((desc = vector_get_item (v, i)) != NULL) if (strcmp (desc->cmd, str) == 0) return 1; return 0; } -static int +static bool 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; + return (node >= MIN_DO_SHORTCUT_NODE) + && (first_word != NULL) + && (strcmp( "do", first_word) == 0) ? 1 : 0 ; } /* '?' describe command support. */ static vector -cmd_describe_command_real (vector vline, struct vty *vty, int *status) +cmd_describe_command_real (vector vline, int node, int *status) { unsigned int i; vector cmd_vector; @@ -1595,16 +1979,16 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) char *command; /* Set index. */ - if (vector_active (vline) == 0) + if (vector_length (vline) == 0) { *status = CMD_ERR_NO_MATCH; return NULL; } else - index = vector_active (vline) - 1; - + index = vector_length (vline) - 1; + /* Make copy vector of current node's command vector. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + cmd_vector = vector_copy (cmd_node_vector (cmdvec, node)); /* Prepare match vector */ matchvec = vector_init (INIT_MATCHVEC_SIZE); @@ -1612,29 +1996,29 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) /* Filter commands. */ /* Only words precedes current word will be checked in this loop. */ for (i = 0; i < index; i++) - if ((command = vector_slot (vline, i))) + if ((command = vector_get_item (vline, i))) { match = cmd_filter_by_completion (command, cmd_vector, i); - + if (match == vararg_match) { struct cmd_element *cmd_element; vector descvec; unsigned int j, k; - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && (vector_active (cmd_element->strvec))) + for (j = 0; j < vector_length (cmd_vector); j++) + if ((cmd_element = vector_get_item (cmd_vector, j)) != NULL + && (vector_length (cmd_element->strvec))) { - descvec = vector_slot (cmd_element->strvec, - vector_active (cmd_element->strvec) - 1); - for (k = 0; k < vector_active (descvec); k++) + descvec = vector_get_item (cmd_element->strvec, + vector_length (cmd_element->strvec) - 1); + for (k = 0; k < vector_length (descvec); k++) { - struct desc *desc = vector_slot (descvec, k); + struct desc *desc = vector_get_item (descvec, k); vector_set (matchvec, desc); } } - + vector_set (matchvec, &desc_cr); vector_free (cmd_vector); @@ -1660,53 +2044,58 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) /* Prepare match vector */ /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ - /* Make sure that cmd_vector is filtered based on current word */ - command = vector_slot (vline, index); + /* Make sure that cmd_vector is filtered based on current word */ + command = vector_get_item (vline, index); if (command) match = cmd_filter_by_completion (command, cmd_vector, index); /* Make description vector. */ - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) - { - vector strvec = cmd_element->strvec; + for (i = 0; i < vector_length (cmd_vector); i++) + { + vector strvec ; + + cmd_element = vector_get_item (cmd_vector, i) ; + if (cmd_element == NULL) + continue ; + + /* Ignore cmd_element if no tokens at index position. + * + * Deal with special case of possible <cr> completion. + */ + strvec = cmd_element->strvec; + if (index >= vector_length (strvec)) + { + if (command == NULL && index == vector_length (strvec)) + { + if (!desc_unique_string (matchvec, command_cr)) + vector_push_item(matchvec, &desc_cr); + } + continue ; + } ; + + /* Check if command is completed. */ + unsigned int j; + vector descvec = vector_get_item (strvec, index); + struct desc *desc; + + for (j = 0; j < vector_length (descvec); j++) + if ((desc = vector_get_item (descvec, j))) + { + const char *string; + + string = cmd_entry_function_desc (command, desc->cmd); + if (string) + { + /* Uniqueness check */ + if (!desc_unique_string (matchvec, string)) + vector_push_item(matchvec, desc); + } + } ; + } ; - /* if command is NULL, index may be equal to vector_active */ - if (command && index >= vector_active (strvec)) - vector_slot (cmd_vector, i) = NULL; - else - { - /* Check if command is completed. */ - if (command == NULL && index == vector_active (strvec)) - { - if (!desc_unique_string (matchvec, command_cr)) - vector_set (matchvec, &desc_cr); - } - else - { - unsigned int j; - vector descvec = vector_slot (strvec, index); - struct desc *desc; - - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - const char *string; - - string = cmd_entry_function_desc (command, desc->cmd); - if (string) - { - /* Uniqueness check */ - if (!desc_unique_string (matchvec, string)) - vector_set (matchvec, desc); - } - } - } - } - } vector_free (cmd_vector); - if (vector_slot (matchvec, 0) == NULL) + if (vector_length(matchvec) == 0) { vector_free (matchvec); *status = CMD_ERR_NO_MATCH; @@ -1717,269 +2106,285 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) return matchvec; } +/*------------------------------------------------------------------------------ + * Get description of current (partial) command + * + * Returns: NULL => no description available + * + * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS + * + * or: address of vector of "struct desc" values available. + * + * NB: when a vector is returned it is the caller's responsibility to + * vector_free() it. (The contents are all effectively const, so do not + * themselves need to be freed.) + */ vector -cmd_describe_command (vector vline, struct vty *vty, int *status) +cmd_describe_command (vector vline, int node, int *status) { vector ret; - if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) + if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) ) { - enum node_type onode; - vector shifted_vline; + 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++) + for (index = 1; index < vector_length (vline); index++) { vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } - ret = cmd_describe_command_real (shifted_vline, vty, status); + ret = cmd_describe_command_real (shifted_vline, ENABLE_NODE, status); vector_free(shifted_vline); - vty->node = onode; return ret; } - - return cmd_describe_command_real (vline, vty, status); + return cmd_describe_command_real (vline, node, status); } - -/* Check LCD of matched command. */ +/*------------------------------------------------------------------------------ + * Check LCD of matched command. + * + * Scan list of matched keywords, and by comparing them pair-wise, find the + * longest common leading substring. + * + * Returns: 0 if zero or one matched keywords + * length of longest common leading substring, otherwise. + */ static int -cmd_lcd (char **matched) +cmd_lcd (vector matchvec) { - int i; - int j; - int lcd = -1; - char *s1, *s2; - char c1, c2; + int n ; + int i ; + int lcd ; + char *sp, *sq, *ss ; - if (matched[0] == NULL || matched[1] == NULL) - return 0; + n = vector_end(matchvec) ; + if (n < 2) + return 0 ; + + ss = vector_get_item(matchvec, 0) ; + lcd = strlen(ss) ; - for (i = 1; matched[i] != NULL; i++) + for (i = 1 ; i < n ; i++) { - s1 = matched[i - 1]; - s2 = matched[i]; + sq = ss ; + ss = vector_get_item(matchvec, i) ; + sp = ss ; - for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) - if (c1 != c2) - break; + while ((*sp == *sq) && (*sp != '\0')) + { + ++sp ; + ++sq ; + } ; - if (lcd < 0) - lcd = j; - else - { - if (lcd > j) - lcd = j; - } + if (lcd > (sp - ss)) + lcd = (sp - ss) ; } return lcd; } -/* Command line completion support. */ -static char ** -cmd_complete_command_real (vector vline, struct vty *vty, int *status) +/*------------------------------------------------------------------------------ + * Command line completion support. + */ +static vector +cmd_complete_command_real (vector vline, int node, int *status) { unsigned int i; - vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + unsigned int ivl ; + unsigned int last_ivl ; + vector cmd_v ; #define INIT_MATCHVEC_SIZE 10 vector matchvec; struct cmd_element *cmd_element; unsigned int index; - char **match_str; struct desc *desc; vector descvec; - char *command; - int lcd; + char *token; + int n ; - if (vector_active (vline) == 0) + /* Stop immediately if the vline is empty. */ + if (vector_length (vline) == 0) { - vector_free (cmd_vector); *status = CMD_ERR_NO_MATCH; return NULL; } - else - index = vector_active (vline) - 1; - /* First, filter by preceeding command string */ - for (i = 0; i < index; i++) - if ((command = vector_slot (vline, i))) - { - enum match_type match; - int ret; + /* Take (shallow) copy of cmdvec for given node. */ + cmd_v = vector_copy (cmd_node_vector (cmdvec, node)); - /* First try completion match, if there is exactly match return 1 */ - match = cmd_filter_by_completion (command, cmd_vector, i); + /* First, filter upto, but excluding last token */ + last_ivl = vector_length (vline) - 1; - /* If there is exact match then filter ambiguous match else check - ambiguousness. */ - if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) - { - vector_free (cmd_vector); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - /* - else if (ret == 2) - { - vector_free (cmd_vector); + for (ivl = 0; ivl < last_ivl; ivl++) + { + enum match_type match; + int ret; + + /* TODO: does this test make any sense ? */ + if ((token = vector_get_item (vline, ivl)) == NULL) + continue ; + + /* First try completion match, return best kind of match */ + index = ivl ; + match = cmd_filter_by_completion (token, cmd_v, index) ; + + /* Eliminate all but the selected kind of match */ + ret = is_cmd_ambiguous (token, cmd_v, index, match) ; + + if (ret == 1) + { + /* ret == 1 => either token matches more than one keyword + * or token matches more than one number range + */ + vector_free (cmd_v); + *status = CMD_ERR_AMBIGUOUS; + return NULL; + } +#if 0 + /* For command completion purposes do not appear to care about + * incomplete ipv4 or ipv6 prefixes (missing '/' or digits after). + */ + else if (ret == 2) + { + vector_free (cmd_v); *status = CMD_ERR_NO_MATCH; return NULL; - } - */ + } +#endif } - - /* Prepare match vector. */ + + /* Prepare match vector. */ matchvec = vector_init (INIT_MATCHVEC_SIZE); - /* Now we got into completion */ - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i))) - { - const char *string; - vector strvec = cmd_element->strvec; + /* Now we got into completion */ + index = last_ivl ; + token = vector_get_item(vline, last_ivl) ; /* is now the last token */ - /* Check field length */ - if (index >= vector_active (strvec)) - vector_slot (cmd_vector, i) = NULL; - else - { - unsigned int j; + for (i = 0; i < vector_length (cmd_v); i++) + { + unsigned int j; + const char *string; - descvec = vector_slot (strvec, index); - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - if ((string = - cmd_entry_function (vector_slot (vline, index), - desc->cmd))) - if (cmd_unique_string (matchvec, string)) - vector_set (matchvec, XSTRDUP (MTYPE_TMP, string)); - } - } - } + if ((cmd_element = vector_get_item (cmd_v, i)) == NULL) + continue ; - /* We don't need cmd_vector any more. */ - vector_free (cmd_vector); + descvec = vector_get_item (cmd_element->strvec, index); + if (descvec == NULL) + continue ; + + for (j = 0; j < vector_length (descvec); j++) + { + desc = vector_get_item (descvec, j) ; + if (desc == NULL) + continue ; + + string = cmd_entry_function(token, desc->cmd) ; + if ((string != NULL) && cmd_unique_string(matchvec, string)) + cmd_add_to_strvec (matchvec, string) ; + } ; + } ; + + n = vector_length(matchvec) ; /* number of entries in the matchvec */ + + /* We don't need cmd_v any more. */ + vector_free (cmd_v); /* No matched command */ - if (vector_slot (matchvec, 0) == NULL) + if (n == 0) { vector_free (matchvec); /* 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; + if (*token == '\0') + *status = CMD_COMPLETE_ALREADY; else *status = CMD_ERR_NO_MATCH; return NULL; } - /* Only one matched */ - if (vector_slot (matchvec, 1) == NULL) + /* Only one matched */ + if (n == 1) { - match_str = (char **) matchvec->index; - vector_only_wrapper_free (matchvec); *status = CMD_COMPLETE_FULL_MATCH; - return match_str; + return matchvec ; } - /* Make it sure last element is NULL. */ - vector_set (matchvec, NULL); - /* Check LCD of matched strings. */ - if (vector_slot (vline, index) != NULL) + /* Check LCD of matched strings. */ + if (token != NULL) { - lcd = cmd_lcd ((char **) matchvec->index); + unsigned lcd = cmd_lcd (matchvec) ; - if (lcd) + if (lcd != 0) { - int len = strlen (vector_slot (vline, index)); - - if (len < lcd) + if (strlen(token) < lcd) { char *lcdstr; lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1); - memcpy (lcdstr, matchvec->index[0], lcd); + memcpy (lcdstr, vector_get_item(matchvec, 0), lcd) ; lcdstr[lcd] = '\0'; - /* match_str = (char **) &lcdstr; */ + cmd_free_strvec(matchvec) ; /* discard the match vector */ - /* Free matchvec. */ - for (i = 0; i < vector_active (matchvec); i++) - { - if (vector_slot (matchvec, i)) - XFREE (MTYPE_STRVEC, 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); + matchvec = vector_init (1); + vector_push_item(matchvec, lcdstr) ; *status = CMD_COMPLETE_MATCH; - return match_str; + return matchvec ; } } } - match_str = (char **) matchvec->index; - vector_only_wrapper_free (matchvec); *status = CMD_COMPLETE_LIST_MATCH; - return match_str; + return matchvec ; } -char ** -cmd_complete_command (vector vline, struct vty *vty, int *status) +/*------------------------------------------------------------------------------ + * Can the current command be completed ? + */ +extern vector +cmd_complete_command (vector vline, int node, int *status) { - char **ret; + vector ret; - if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) + if ( cmd_try_do_shortcut(node, vector_get_item(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++) + for (index = 1; index < vector_length (vline); index++) { vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } - ret = cmd_complete_command_real (shifted_vline, vty, status); + ret = cmd_complete_command_real (shifted_vline, ENABLE_NODE, status); vector_free(shifted_vline); - vty->node = onode; return ret; } - - return cmd_complete_command_real (vline, vty, status); + return cmd_complete_command_real (vline, node, status); } -/* return parent node */ -/* MUST eventually converge on CONFIG_NODE */ +/*------------------------------------------------------------------------------ + * Return parent node + * + * All nodes > CONFIG_NODE are descended from CONFIG_NODE + */ enum node_type node_parent ( enum node_type node ) { - enum node_type ret; - assert (node > CONFIG_NODE); switch (node) @@ -1989,493 +2394,585 @@ node_parent ( enum node_type node ) case BGP_IPV4M_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: - ret = BGP_NODE; - break; + return BGP_NODE; + case KEYCHAIN_KEY_NODE: - ret = KEYCHAIN_NODE; - break; + return KEYCHAIN_NODE; + default: - ret = CONFIG_NODE; + return CONFIG_NODE; } - - return ret; } -/* Execute command by argument vline vector. */ -static int -cmd_execute_command_real (vector vline, struct vty *vty, - struct cmd_element **cmd) +/*------------------------------------------------------------------------------ + * Initialise a new struct cmd_parsed, allocating if required + */ +extern cmd_parsed +cmd_parse_init_new(cmd_parsed parsed) { - 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; - int varflag; - char *command; + if (parsed == NULL) + parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ; + else + memset(parsed, 0, sizeof(*parsed)) ; + + /* Zeroising the structure has set: + * + * cmd = NULL -- no command parsed, yet + * cnode -- no node set, yet + * + * do_shortcut -- false + * onode -- not material (do_shortcut is false) + * + * line = zeroised qstring -- empty + * words = zeroised qstring -- empty + * + * vline = zeroised vector -- empty + * + * so nothing else to do + */ - /* Make copy of command elements. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + return parsed ; +} ; - for (index = 0; index < vector_active (vline); index++) - if ((command = vector_slot (vline, index))) - { - int ret; +/*------------------------------------------------------------------------------ + * Initialise a new struct cmd_parsed, allocating if required + */ +extern cmd_parsed +cmd_parse_reset(cmd_parsed parsed, bool free_structure) +{ + if (parsed != NULL) + { + qs_reset_keep(&parsed->words) ; + vector_reset_keep(&parsed->vline) ; - match = cmd_filter_by_completion (command, cmd_vector, index); + if (free_structure) + XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */ + else + cmd_parse_init_new(parsed) ; + } ; + + return parsed ; +} ; + +/*------------------------------------------------------------------------------ + * Parse a command in the given "node", if possible, ready for execution. + * + * If 'strict': use cmd_filter_by_string() + * otherwise: use cmd_filter_by_completion() + * + * If 'do': see if there is a 'do' at the front and proceed accordingly. + * + * If 'tree': move up the node tree to find command if not found in the + * current node. + */ + +static enum cmd_return_code +cmd_parse_this(struct cmd_parsed* parsed, bool strict) ; + +/*------------------------------------------------------------------------------ + * Parse a command in the given "node", or (if required) any of its ancestors. + * + * Returns: CMD_SUCCESS => successfully parsed command, and the result is + * in the given parsed structure, ready for execution. + * + * NB: parsed->cnode may have changed. + * + * NB: parsed->cmd->daemon => daemon + * + * CMD_EMPTY => empty or comment line + * + * NB: parsed->cmd == NULL + * + * CMD_SUCCESS_DAEMON => parsed successfully. Something for vtysh ?? + * + * CMD_ERR_NO_MATCH ) + * CMD_ERR_AMBIGUOUS ) failed to parse + * CMD_ERR_INCOMPLETE ) + * + * NB: if has failed to parse in the current node + * and in any ancestor nodes, returns the error + * from the attempt to parse in the current node + * (parsed->cnode which is returned unchanged). + * + * NB: the command line MUST be preserved until the parsed command is no + * longer required -- no copy is made. + * + * NB: expects to have free run of everything in the vty structure (except + * the contents of the vty_io sub-structure) until the command completes. + * + * See elsewhere for description of parsed structure. + */ +extern enum cmd_return_code +cmd_parse_command(struct vty* vty, enum cmd_parse_type type) +{ + enum cmd_return_code ret ; + enum cmd_return_code first_ret ; + cmd_parsed parsed ; - if (match == vararg_match) - break; - - ret = is_cmd_ambiguous (command, cmd_vector, index, match); + /* Initialise the parsed structure -- assuming no 'do' */ + if (vty->parsed == NULL) + vty->parsed = cmd_parse_init_new(NULL) ; + parsed = vty->parsed ; - if (ret == 1) - { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - else if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; - } - } + parsed->onode = parsed->cnode = vty->node ; - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; + parsed->cmd = NULL ; + parsed->do_shortcut = 0 ; - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i))) - { - if (match == vararg_match || index >= cmd_element->cmdsize) - { - matched_element = cmd_element; -#if 0 - printf ("DEBUG: %s\n", cmd_element->string); -#endif - matched_count++; - } - else - { - incomplete_count++; - } - } + /* Parse the line into words -- set up parsed->words and parsed->vline */ + cmd_make_vline(&parsed->vline, &parsed->words, vty->buf) ; - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); + if (vector_length(&parsed->vline) == 0) + return CMD_EMPTY ; /* NB: parsed->cmd == NULL */ - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) + /* If allowed to 'do', see if there. + * + * 'do' forces command to be parsed in ENABLE_NODE (if allowed) + */ + if ((type & cmd_parse_do) && + cmd_try_do_shortcut(parsed->cnode, vector_get_item(&parsed->vline, 0))) { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; - } + parsed->cnode = ENABLE_NODE ; + parsed->do_shortcut = 1 ; + } ; - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; + /* Try in the current node */ + ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ; - /* Argument treatment */ - varflag = 0; - argc = 0; - - for (i = 0; i < vector_active (vline); i++) + if (ret != CMD_SUCCESS) { - if (varflag) - argv[argc++] = vector_slot (vline, i); - else - { - vector descvec = vector_slot (matched_element->strvec, i); + if (((type & cmd_parse_tree) == 0) || parsed->do_shortcut) + return ret ; /* done if not allowed to walk tree + or just tried to parse a 'do' */ - if (vector_active (descvec) == 1) - { - struct desc *desc = vector_slot (descvec, 0); + /* Try in parent node(s) */ + first_ret = ret ; - if (CMD_VARARG (desc->cmd)) - varflag = 1; - - if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) - argv[argc++] = vector_slot (vline, i); - } - else - argv[argc++] = vector_slot (vline, i); - } + while (ret != CMD_SUCCESS) + { + if (parsed->cnode <= CONFIG_NODE) + { + parsed->cnode = parsed->onode ; /* restore node state */ + return first_ret ; /* return original result */ + } ; + + parsed->cnode = node_parent(parsed->cnode) ; + ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ; + } ; + } ; + + return vty->parsed->cmd->daemon ? CMD_SUCCESS_DAEMON + : CMD_SUCCESS ; +} ; + +/*------------------------------------------------------------------------------ + * Work function for cmd_parse_command + * + * Takes a parsed structure, with the: + * + * cnode -- node to parse in + * vline -- the line broken into words + * do_shortcut -- true if first word is 'do' (to be ignored) + * + * and parses either strictly or with command completion. + * + * If successful, reduces the vline structure down to the variable portions, + * ie to the argv[] for the command function. + * + * Returns: CMD_SUCCESS -- parsed successfully + * CMD_ERR_NO_MATCH ) + * CMD_ERR_AMBIGUOUS ) failed to parse + * CMD_ERR_INCOMPLETE ) + */ +static enum cmd_return_code +cmd_parse_this(cmd_parsed parsed, bool strict) +{ + unsigned int i ; + unsigned int ivl ; + unsigned index ; + unsigned first ; + unsigned argc ; + vector cmd_v; + struct cmd_element *cmd_element; + struct cmd_element *matched_element; + unsigned int matched_count, incomplete_count; + enum match_type match = 0; + int varflag; + char *command; - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } + /* Need length of vline, discounting the first entry if required */ + first = parsed->do_shortcut ? 1 : 0 ; - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; + assert(vector_length(&parsed->vline) >= first) ; + ivl = vector_length(&parsed->vline) - first ; - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; + /* Make copy of command elements. */ + cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode)); - /* Execute matched command. */ - return (*matched_element->func) (matched_element, vty, argc, argv); -} + /* Look for an unambiguous result */ + for (index = 0 ; index < ivl; index++) + { + int ret ; -int -cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, - int vtysh) { - int ret, saved_ret, tried = 0; - enum node_type onode, try_node; + command = vector_get_item(&parsed->vline, index + first) ; + if (command == NULL) + continue ; - onode = try_node = vty->node; + match = strict ? cmd_filter_by_string(command, cmd_v, index) + : cmd_filter_by_completion(command, cmd_v, index) ; - if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) - { - vector shifted_vline; - unsigned int index; + if (match == vararg_match) + break; - vty->node = ENABLE_NODE; - /* We can try it on enable node, cos' the vty is authenticated */ + ret = is_cmd_ambiguous (command, cmd_v, index, match); - 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)); + if (ret != 0) + { + assert((ret == 1) || (ret == 2)) ; + vector_free (cmd_v); + return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ; } + } ; - ret = cmd_execute_command_real (shifted_vline, vty, cmd); + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; + incomplete_count = 0; - vector_free(shifted_vline); - vty->node = onode; - return ret; - } + for (i = 0; i < vector_length(cmd_v); i++) + { + cmd_element = vector_get_item(cmd_v, i) ; + if (cmd_element == NULL) + continue ; + if (match == vararg_match || index >= cmd_element->cmdsize) + { + matched_element = cmd_element; +#if 0 + printf ("DEBUG: %s\n", cmd_element->string); +#endif + matched_count++; + } + else + { + incomplete_count++; + } + } ; - saved_ret = ret = cmd_execute_command_real (vline, vty, cmd); + /* Finished with cmd_v. */ + vector_free (cmd_v); - if (vtysh) - return saved_ret; + /* To execute command, matched_count must be 1. */ + if (matched_count != 1) + { + if (matched_count == 0) + return (incomplete_count) ? CMD_ERR_INCOMPLETE : CMD_ERR_NO_MATCH ; + else + return CMD_ERR_AMBIGUOUS ; + } ; + + /* Found command -- process the arguments ready for execution */ + varflag = 0 ; + argc = 0 ; - /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ - while ( ret != CMD_SUCCESS && ret != CMD_WARNING - && vty->node > CONFIG_NODE ) + for (index = 0; index < ivl ; index++) { - try_node = node_parent(try_node); - vty->node = try_node; - ret = cmd_execute_command_real (vline, vty, cmd); - tried = 1; - if (ret == CMD_SUCCESS || ret == CMD_WARNING) + int take = varflag ; + + if (!varflag) { - /* succesfull command, leave the node as is */ - return ret; + vector descvec = vector_get_item (matched_element->strvec, index); + + if (vector_length (descvec) == 1) + { + struct desc *desc = vector_get_item (descvec, 0); + + if (CMD_VARARG (desc->cmd)) + take = varflag = 1 ; + else + take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ; + } + else + take = 1 ; } - } - /* no command succeeded, reset the vty to the original node and - return the error for this node */ - if ( tried ) - vty->node = onode; - return saved_ret; -} -/* Execute command by argument readline. */ -int -cmd_execute_command_strict (vector vline, 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]; - int varflag; - enum match_type match = 0; - char *command; + if (take) + vector_assign_item(&parsed->vline, argc++, index + first) ; + } ; - /* Make copy of command element */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + vector_set_length(&parsed->vline, argc) ; /* set to new length */ - for (index = 0; index < vector_active (vline); index++) - if ((command = vector_slot (vline, index))) - { - int ret; - - match = cmd_filter_by_string (vector_slot (vline, index), - cmd_vector, index); + /* Everything checks out... ready to execute command */ + parsed->cmd = matched_element ; - /* If command meets '.VARARG' then finish matching. */ - if (match == vararg_match) - break; - - ret = is_cmd_ambiguous (command, cmd_vector, index, match); - if (ret == 1) - { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; - } - } + return CMD_SUCCESS ; +} ; - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; - for (i = 0; i < vector_active (cmd_vector); i++) - if (vector_slot (cmd_vector, i) != NULL) - { - cmd_element = vector_slot (cmd_vector, i); +/*------------------------------------------------------------------------------ + * Dispatch a parsed command. + * + * Returns: command return code. NB: may be CMD_QUEUED (unless no_queue). + * + * NB: expects to have free run of everything in the vty structure (except + * the contents of the vty_io sub-structure) until the command completes. + */ +extern enum cmd_return_code +cmd_dispatch(struct vty* vty, bool no_queue) +{ + cmd_parsed parsed = vty->parsed ; + enum cmd_return_code ret ; - if (match == vararg_match || index >= cmd_element->cmdsize) - { - matched_element = cmd_element; - matched_count++; - } - else - incomplete_count++; - } + if (parsed->cmd == NULL) + return CMD_SUCCESS ; /* NULL commands are easy */ - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); + vty->node = parsed->cnode ; - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) + if (no_queue || !vty_cli_nexus) { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; + ret = cmd_dispatch_call(vty) ; + cmd_post_command(vty, ret) ; } - - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; - - /* Argument treatment */ - varflag = 0; - argc = 0; - - for (i = 0; i < vector_active (vline); i++) + else { - if (varflag) - argv[argc++] = vector_slot (vline, i); + /* Don't do it now, but send to bgp qpthread */ + if (parsed->cmd->attr & CMD_ATTR_CALL) + cq_enqueue(vty, vty_cli_nexus) ; else - { - vector descvec = vector_slot (matched_element->strvec, i); + cq_enqueue(vty, vty_cmd_nexus) ; + + ret = CMD_QUEUED ; + } ; + + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Tidy up after executing command. + * + * This is separated out so that can be called when queued command completes. + * + * If have just processed a "do" shortcut command, and it has not set the + * vty->node to something other than ENABLE_NODE, then restore to the original + * state. + * + * Arguments: ret = CMD_XXXX -- NB: CMD_QUEUED => command revoked + */ +extern void +cmd_post_command(struct vty* vty, int ret) +{ + if (vty->parsed->do_shortcut) + { + if (vty->node == ENABLE_NODE) + vty->node = vty->parsed->onode ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * Parse and execute a command. + * + * The command is given by vty->buf and vty->node. + * + * Uses vty->parsed. + * + * -- use strict/completion parsing, as required. + * + * -- parse in current node and in ancestors, as required + * + * If does not find in any ancestor, return error from current node. + * + * -- implement the "do" shortcut, as required + * + * If qpthreads_enabled, then may queue the command rather than execute it + * here. + * + * The vty->node may be changed during the execution of the command, and may + * be returned changed once the command has completed. + * + * NB: expects to have free run of everything in the vty structure (except + * the contents of the vty_io sub-structure) until the command completes. + */ +extern enum cmd_return_code +cmd_execute_command(struct vty *vty, + enum cmd_parse_type type, struct cmd_element **cmd) +{ + enum cmd_return_code ret ; + + /* Try to parse in vty->node or, if required, ancestors thereof. */ + ret = cmd_parse_command(vty, type) ; + + if (cmd != NULL) + *cmd = vty->parsed->cmd ; /* for vtysh */ + + if (ret == CMD_SUCCESS) + ret = cmd_dispatch(vty, 0) ; + else if (ret == CMD_EMPTY) + ret = CMD_SUCCESS ; + + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Read configuration from file. + * + * In the qpthreads world this assumes that it is running with the vty + * locked, and that all commands are to be executed directly. + * + * If the 'first_cmd' argument is not NULL it is the address of the first + * command that is expected to appear. If the first command is not this, then + * the 'first_cmd' is called with argv == NULL (and argc == 0) to signal the + * command is being invoked by default. + * + * Command processing continues while CMD_SUCCESS is returned by the command + * parser and command execution. + * + * If 'ignore_warning' is set, then any CMD_WARNING returned by command + * execution is converted to CMD_SUCCESS. Note that any CMD_WARNING returned + * by command parsing (or in execution of any default 'first_cmd'). + * + * Returns: cmd_return_code for last command + * vty->buf is last line processed + * vty->lineno is number of last line processed (1 is first) + * + * If the file is empty, will return CMD_SUCCESS. + * + * Never returns CMD_EMPTY -- that counts as CMD_SUCCESS. + * + * If + * + * If return code is not CMD_SUCCESS, the the output buffering contains the + * output from the last command attempted. + */ +extern enum cmd_return_code +config_from_file (struct vty *vty, FILE *fp, struct cmd_element* first_cmd, + qstring buf, bool ignore_warning) +{ + enum cmd_return_code ret; - if (vector_active (descvec) == 1) - { - struct desc *desc = vector_slot (descvec, 0); + vty->buf = buf->body ; + vty->lineno = 0 ; - if (CMD_VARARG (desc->cmd)) - varflag = 1; + ret = CMD_SUCCESS ; /* in case file is empty */ + vty_out_clear(vty) ; - if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) - argv[argc++] = vector_slot (vline, i); - } - else - argv[argc++] = vector_slot (vline, i); - } + while (fgets (buf->body, buf->size, fp)) + { + ++vty->lineno ; - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } + /* Execute configuration command : this is strict match */ + ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ; - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; + if (ret == CMD_EMPTY) + continue ; /* skip empty/comment */ - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; + if (ret != CMD_SUCCESS) + break ; /* stop on *any* parsing issue */ - /* Now execute matched command */ - return (*matched_element->func) (matched_element, vty, argc, argv); -} + /* special handling before of first command */ + if (first_cmd != NULL) + { + if (first_cmd != vty->parsed->cmd) + { + ret = (*first_cmd->func)(first_cmd, vty, 0, NULL) ; + if (ret != CMD_SUCCESS) + break ; /* stop on *any* issue with "default" */ + } ; + first_cmd = NULL ; + } -/* Configration make from file. */ -int -config_from_file (struct vty *vty, FILE *fp) -{ - int ret; - vector vline; + /* Standard command handling */ + ret = cmd_dispatch(vty, cmd_no_queue) ; - while (fgets (vty->buf, VTY_BUFSIZ, fp)) - { - vline = cmd_make_strvec (vty->buf); + if (ret != CMD_SUCCESS) + { + /* Ignore CMD_WARNING if required + * + * Ignore CMD_CLOSE at all times. + */ + if ( ((ret == CMD_WARNING) && ignore_warning) + || (ret == CMD_CLOSE) ) + ret = CMD_SUCCESS ; /* in case at EOF */ + else + break ; /* stop */ + } ; - /* In case of comment line */ - if (vline == NULL) - continue; - /* Execute configuration command : this is strict match */ - ret = cmd_execute_command_strict (vline, vty, NULL); + vty_out_clear(vty) ; + } ; - /* Try again with setting node to CONFIG_NODE */ - while (ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) - { - vty->node = node_parent(vty->node); - ret = cmd_execute_command_strict (vline, vty, NULL); - } + if (ret == CMD_EMPTY) + ret = CMD_SUCCESS ; /* OK if end on empty line */ - cmd_free_strvec (vline); + return ret ; +} ; - if (ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_ERR_NOTHING_TODO) - return ret; - } - return CMD_SUCCESS; -} +/*----------------------------------------------------------------------------*/ /* Configration from terminal */ -DEFUN (config_terminal, +DEFUN_CALL (config_terminal, config_terminal_cmd, "configure terminal", "Configuration from vty interface\n" "Configuration terminal\n") { - if (vty_config_lock (vty)) - vty->node = CONFIG_NODE; - else - { - vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; + if (vty_config_lock (vty, CONFIG_NODE)) + return CMD_SUCCESS; + + vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE); + return CMD_WARNING; } /* Enable command */ -DEFUN (enable, +DEFUN_CALL (enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") { /* If enable password is NULL, change to ENABLE_NODE */ if ((host.enable == NULL && host.enable_encrypt == NULL) || - vty->type == VTY_SHELL_SERV) - vty->node = ENABLE_NODE; + vty_shell_serv(vty)) + vty_set_node(vty, ENABLE_NODE); else - vty->node = AUTH_ENABLE_NODE; + vty_set_node(vty, AUTH_ENABLE_NODE); return CMD_SUCCESS; } /* Disable command */ -DEFUN (disable, +DEFUN_CALL (disable, config_disable_cmd, "disable", "Turn off privileged mode command\n") { - if (vty->node == ENABLE_NODE) - vty->node = VIEW_NODE; + if (vty_get_node(vty) == ENABLE_NODE) + vty_set_node(vty, VIEW_NODE); return CMD_SUCCESS; } /* Down vty node level. */ -DEFUN (config_exit, +DEFUN_CALL (config_exit, config_exit_cmd, "exit", "Exit current mode and down to previous mode\n") { - switch (vty->node) - { - case VIEW_NODE: - case ENABLE_NODE: - case RESTRICTED_NODE: - if (vty_shell (vty)) - exit (0); - else - vty->status = VTY_CLOSE; - break; - case CONFIG_NODE: - vty->node = ENABLE_NODE; - vty_config_unlock (vty); - break; - case INTERFACE_NODE: - case ZEBRA_NODE: - case BGP_NODE: - case RIP_NODE: - case RIPNG_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case ISIS_NODE: - case KEYCHAIN_NODE: - case MASC_NODE: - case RMAP_NODE: - case VTY_NODE: - vty->node = CONFIG_NODE; - break; - case BGP_VPNV4_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV6_NODE: - case BGP_IPV6M_NODE: - vty->node = BGP_NODE; - break; - case KEYCHAIN_KEY_NODE: - vty->node = KEYCHAIN_NODE; - break; - default: - break; - } - return CMD_SUCCESS; + return vty_cmd_exit(vty) ; } /* quit is alias of exit. */ -ALIAS (config_exit, +ALIAS_CALL (config_exit, config_quit_cmd, "quit", "Exit current mode and down to previous mode\n") - + /* End of configuration. */ -DEFUN (config_end, +DEFUN_CALL (config_end, config_end_cmd, "end", "End current mode and change to enable mode.") { - switch (vty->node) - { - case VIEW_NODE: - case ENABLE_NODE: - case RESTRICTED_NODE: - /* Nothing to do. */ - break; - case CONFIG_NODE: - case INTERFACE_NODE: - case ZEBRA_NODE: - case RIP_NODE: - case RIPNG_NODE: - case BGP_NODE: - case BGP_VPNV4_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV6_NODE: - case BGP_IPV6M_NODE: - case RMAP_NODE: - case OSPF_NODE: - case OSPF6_NODE: - case ISIS_NODE: - case KEYCHAIN_NODE: - case KEYCHAIN_KEY_NODE: - case MASC_NODE: - case VTY_NODE: - vty_config_unlock (vty); - vty->node = ENABLE_NODE; - break; - default: - break; - } - return CMD_SUCCESS; + return vty_cmd_end(vty) ; } /* Show version. */ -DEFUN (show_version, +DEFUN_CALL (show_version, show_version_cmd, "show version", SHOW_STR @@ -2489,12 +2986,12 @@ DEFUN (show_version, } /* Help display function for all node. */ -DEFUN (config_help, +DEFUN_CALL (config_help, config_help_cmd, "help", "Description of the interactive help system\n") { - vty_out (vty, + vty_out (vty, "Quagga VTY provides advanced help feature. When you need help,%s\ anytime at the command line please press '?'.%s\ %s\ @@ -2513,39 +3010,38 @@ argument.%s\ } /* Help display function for all node. */ -DEFUN (config_list, +DEFUN_CALL (config_list, config_list_cmd, "list", "Print command list\n") { unsigned int i; - struct cmd_node *cnode = vector_slot (cmdvec, vty->node); + struct cmd_node *cnode = vector_get_item (cmdvec, vty_get_node(vty)); struct cmd_element *cmd; - for (i = 0; i < vector_active (cnode->cmd_vector); i++) - if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL - && !(cmd->attr == CMD_ATTR_DEPRECATED - || cmd->attr == CMD_ATTR_HIDDEN)) + for (i = 0; i < vector_length (cnode->cmd_vector); i++) + if ((cmd = vector_get_item (cnode->cmd_vector, i)) != NULL + && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))) vty_out (vty, " %s%s", cmd->string, VTY_NEWLINE); return CMD_SUCCESS; } /* Write current configuration into file. */ -DEFUN (config_write_file, +DEFUN (config_write_file, config_write_file_cmd, - "write file", + "write file", "Write running configuration to memory, network, or terminal\n" "Write to configuration file\n") { unsigned int i; int fd; + int err; struct cmd_node *node; char *config_file; char *config_file_tmp = NULL; char *config_file_sav = NULL; int ret = CMD_WARNING; - struct vty *file_vty; /* Check and see if we are operating under vtysh configuration */ if (host.config == NULL) @@ -2557,7 +3053,7 @@ DEFUN (config_write_file, /* 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); @@ -2566,7 +3062,7 @@ 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) @@ -2575,62 +3071,74 @@ DEFUN (config_write_file, VTY_NEWLINE); goto finished; } - + /* Make vty for configuration file. */ - file_vty = vty_new (); - file_vty->fd = fd; - file_vty->type = VTY_FILE; + vty_open_config_write(vty, fd) ; /* Config file header print. */ - vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! "); - vty_time_print (file_vty, 1); - vty_out (file_vty, "!\n"); + vty_out (vty, "!\n! Zebra configuration saved from vty\n! "); + vty_time_print (vty, 1); + vty_out (vty, "!\n"); - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func) + for (i = 0; i < vector_length (cmdvec); i++) + if ((node = vector_get_item (cmdvec, i)) && node->func) { - if ((*node->func) (file_vty)) - vty_out (file_vty, "!\n"); + if ((*node->func) (vty)) + vty_out (vty, "!\n"); } - vty_close (file_vty); + + err = vty_close_config_write(vty) ; + close(fd) ; + + if (err != 0) + { + vty_out (vty, "Failed while writing configuration file %s.%s", + config_file_tmp, VTY_NEWLINE); + goto finished; + } 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); + 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); + vty_out (vty, "Can't backup old configuration file %s.%s", + config_file_sav, VTY_NEWLINE); goto finished; - } - sync (); + } ; + + sync () ; + if (unlink (config_file) != 0) { - vty_out (vty, "Can't unlink configuration file %s.%s", config_file, - VTY_NEWLINE); + 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_out (vty, "Can't save configuration file %s.%s", + config_file, 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 (%s).\n", + config_file, errtostr(errno, 0).str, errtoname(errno, 0).str); goto finished; } - vty_out (vty, "Configuration saved to %s%s", config_file, - VTY_NEWLINE); + vty_out (vty, "Configuration saved to %s\n", config_file); + ret = CMD_SUCCESS; finished: @@ -2640,20 +3148,20 @@ finished: return ret; } -ALIAS (config_write_file, +ALIAS (config_write_file, config_write_cmd, - "write", + "write", "Write running configuration to memory, network, or terminal\n") -ALIAS (config_write_file, +ALIAS (config_write_file, config_write_memory_cmd, - "write memory", + "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_file, 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") @@ -2668,10 +3176,10 @@ DEFUN (config_write_terminal, unsigned int i; struct cmd_node *node; - if (vty->type == VTY_SHELL_SERV) + if (vty_shell_serv(vty)) { - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) + for (i = 0; i < vector_length (cmdvec); i++) + if ((node = vector_get_item (cmdvec, i)) && node->func && node->vtysh) { if ((*node->func) (vty)) vty_out (vty, "!%s", VTY_NEWLINE); @@ -2683,8 +3191,8 @@ DEFUN (config_write_terminal, VTY_NEWLINE); vty_out (vty, "!%s", VTY_NEWLINE); - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func) + for (i = 0; i < vector_length (cmdvec); i++) + if ((node = vector_get_item (cmdvec, i)) && node->func) { if ((*node->func) (vty)) vty_out (vty, "!%s", VTY_NEWLINE); @@ -2736,7 +3244,7 @@ DEFUN (show_startup_config, } /* Hostname configuration */ -DEFUN (config_hostname, +DEFUN_CALL (config_hostname, hostname_cmd, "hostname WORD", "Set system's network name\n" @@ -2748,28 +3256,40 @@ DEFUN (config_hostname, return CMD_WARNING; } + VTY_LOCK() ; + if (host.name) XFREE (MTYPE_HOST, host.name); - + host.name = XSTRDUP (MTYPE_HOST, argv[0]); + uty_set_host_name(host.name) ; + + VTY_UNLOCK() ; + return CMD_SUCCESS; } -DEFUN (config_no_hostname, +DEFUN_CALL (config_no_hostname, no_hostname_cmd, "no hostname [HOSTNAME]", NO_STR "Reset system's network name\n" "Host name of this router\n") { + VTY_LOCK() ; + if (host.name) XFREE (MTYPE_HOST, host.name); host.name = NULL; + uty_set_host_name(host.name) ; + + VTY_UNLOCK() ; + return CMD_SUCCESS; } /* VTY interface password set. */ -DEFUN (config_password, password_cmd, +DEFUN_CALL (config_password, password_cmd, "password (8|) WORD", "Assign the terminal connection password\n" "Specifies a HIDDEN password will follow\n" @@ -2804,7 +3324,7 @@ DEFUN (config_password, password_cmd, if (!isalnum ((int) *argv[0])) { - vty_out (vty, + vty_out (vty, "Please specify string starting with alphanumeric%s", VTY_NEWLINE); return CMD_WARNING; } @@ -2825,13 +3345,13 @@ DEFUN (config_password, password_cmd, return CMD_SUCCESS; } -ALIAS (config_password, password_text_cmd, +ALIAS_CALL (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, +DEFUN_CALL (config_enable_password, enable_password_cmd, "enable password (8|) WORD", "Modify enable password parameters\n" "Assign the privileged level password\n" @@ -2870,7 +3390,7 @@ DEFUN (config_enable_password, enable_password_cmd, if (!isalnum ((int) *argv[0])) { - vty_out (vty, + vty_out (vty, "Please specify string starting with alphanumeric%s", VTY_NEWLINE); return CMD_WARNING; } @@ -2892,7 +3412,7 @@ DEFUN (config_enable_password, enable_password_cmd, return CMD_SUCCESS; } -ALIAS (config_enable_password, +ALIAS_CALL (config_enable_password, enable_password_text_cmd, "enable password LINE", "Modify enable password parameters\n" @@ -2900,7 +3420,7 @@ ALIAS (config_enable_password, "The UNENCRYPTED (cleartext) 'enable' password\n") /* VTY enable password delete. */ -DEFUN (no_config_enable_password, no_enable_password_cmd, +DEFUN_CALL (no_config_enable_password, no_enable_password_cmd, "no enable password", NO_STR "Modify enable password parameters\n" @@ -2916,8 +3436,8 @@ DEFUN (no_config_enable_password, no_enable_password_cmd, return CMD_SUCCESS; } - -DEFUN (service_password_encrypt, + +DEFUN_CALL (service_password_encrypt, service_password_encrypt_cmd, "service password-encryption", "Set up miscellaneous service\n" @@ -2944,7 +3464,7 @@ DEFUN (service_password_encrypt, return CMD_SUCCESS; } -DEFUN (no_service_password_encrypt, +DEFUN_CALL (no_service_password_encrypt, no_service_password_encrypt_cmd, "no service password-encryption", NO_STR @@ -2967,7 +3487,7 @@ DEFUN (no_service_password_encrypt, return CMD_SUCCESS; } -DEFUN (config_terminal_length, config_terminal_length_cmd, +DEFUN_CALL (config_terminal_length, config_terminal_length_cmd, "terminal length <0-512>", "Set terminal line parameters\n" "Set number of lines on a screen\n" @@ -2982,22 +3502,22 @@ DEFUN (config_terminal_length, config_terminal_length_cmd, vty_out (vty, "length is malformed%s", VTY_NEWLINE); return CMD_WARNING; } - vty->lines = lines; + vty_set_lines(vty, lines); return CMD_SUCCESS; } -DEFUN (config_terminal_no_length, config_terminal_no_length_cmd, +DEFUN_CALL (config_terminal_no_length, config_terminal_no_length_cmd, "terminal no length", "Set terminal line parameters\n" NO_STR "Set number of lines on a screen\n") { - vty->lines = -1; + vty_set_lines(vty, -1); return CMD_SUCCESS; } -DEFUN (service_terminal_length, service_terminal_length_cmd, +DEFUN_CALL (service_terminal_length, service_terminal_length_cmd, "service terminal-length <0-512>", "Set up miscellaneous service\n" "System wide terminal length configuration\n" @@ -3017,7 +3537,7 @@ DEFUN (service_terminal_length, service_terminal_length_cmd, return CMD_SUCCESS; } -DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, +DEFUN_CALL (no_service_terminal_length, no_service_terminal_length_cmd, "no service terminal-length [<0-512>]", NO_STR "Set up miscellaneous service\n" @@ -3028,7 +3548,7 @@ DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, return CMD_SUCCESS; } -DEFUN_HIDDEN (do_echo, +DEFUN_HID_CALL (do_echo, echo_cmd, "echo .MESSAGE", "Echo a message back to the vty\n" @@ -3043,7 +3563,7 @@ DEFUN_HIDDEN (do_echo, return CMD_SUCCESS; } -DEFUN (config_logmsg, +DEFUN_CALL (config_logmsg, config_logmsg_cmd, "logmsg "LOG_LEVELS" .MESSAGE", "Send a message to enabled logging destinations\n" @@ -3056,76 +3576,79 @@ DEFUN (config_logmsg, if ((level = level_match(argv[0])) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; - zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : "")); + message = argv_concat(argv, argc, 1); + zlog(NULL, level, "%s", (message ? message : "")); if (message) XFREE(MTYPE_TMP, message); return CMD_SUCCESS; } -DEFUN (show_logging, +DEFUN_CALL (show_logging, show_logging_cmd, "show logging", SHOW_STR "Show current logging configuration\n") { - struct zlog *zl = zlog_default; - vty_out (vty, "Syslog logging: "); - if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED) + if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) == ZLOG_DISABLED) 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[zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG)], + facility_name(zlog_get_facility(NULL)), zlog_get_ident(NULL)); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Stdout logging: "); - if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED) + if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) == ZLOG_DISABLED) vty_out (vty, "disabled"); else vty_out (vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); + zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)]); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Monitor logging: "); - if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED) + if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) == ZLOG_DISABLED) vty_out (vty, "disabled"); else vty_out (vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); + zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)]); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "File logging: "); - if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || - !zl->fp) + if ((zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) == ZLOG_DISABLED) || + !zlog_is_file(NULL)) vty_out (vty, "disabled"); else - vty_out (vty, "level %s, filename %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], - zl->filename); + { + char * filename = zlog_get_filename(NULL); + vty_out (vty, "level %s, filename %s", + zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)], + filename); + free(filename); + } vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Protocol name: %s%s", - zlog_proto_names[zl->protocol], VTY_NEWLINE); + zlog_get_proto_name(NULL), VTY_NEWLINE); vty_out (vty, "Record priority: %s%s", - (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); + (zlog_get_record_priority(NULL) ? "enabled" : "disabled"), VTY_NEWLINE); vty_out (vty, "Timestamp precision: %d%s", - zl->timestamp_precision, VTY_NEWLINE); + zlog_get_timestamp_precision(NULL), VTY_NEWLINE); return CMD_SUCCESS; } -DEFUN (config_log_stdout, +DEFUN_CALL (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); + zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_get_default_lvl(NULL)); return CMD_SUCCESS; } -DEFUN (config_log_stdout_level, +DEFUN_CALL (config_log_stdout_level, config_log_stdout_level_cmd, "log stdout "LOG_LEVELS, "Logging control\n" @@ -3140,7 +3663,7 @@ DEFUN (config_log_stdout_level, return CMD_SUCCESS; } -DEFUN (no_config_log_stdout, +DEFUN_CALL (no_config_log_stdout, no_config_log_stdout_cmd, "no log stdout [LEVEL]", NO_STR @@ -3152,17 +3675,17 @@ DEFUN (no_config_log_stdout, return CMD_SUCCESS; } -DEFUN (config_log_monitor, +DEFUN_CALL (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); + zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_get_default_lvl(NULL)); return CMD_SUCCESS; } -DEFUN (config_log_monitor_level, +DEFUN_CALL (config_log_monitor_level, config_log_monitor_level_cmd, "log monitor "LOG_LEVELS, "Logging control\n" @@ -3177,7 +3700,7 @@ DEFUN (config_log_monitor_level, return CMD_SUCCESS; } -DEFUN (no_config_log_monitor, +DEFUN_CALL (no_config_log_monitor, no_config_log_monitor_cmd, "no log monitor [LEVEL]", NO_STR @@ -3195,19 +3718,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) { @@ -3239,17 +3762,17 @@ set_log_file(struct vty *vty, const char *fname, int loglevel) return CMD_SUCCESS; } -DEFUN (config_log_file, +DEFUN_CALL (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); + return set_log_file(vty, argv[0], zlog_get_default_lvl(NULL)); } -DEFUN (config_log_file_level, +DEFUN_CALL (config_log_file_level, config_log_file_level_cmd, "log file FILENAME "LOG_LEVELS, "Logging control\n" @@ -3264,7 +3787,7 @@ DEFUN (config_log_file_level, return set_log_file(vty, argv[0], level); } -DEFUN (no_config_log_file, +DEFUN_CALL (no_config_log_file, no_config_log_file_cmd, "no log file [FILENAME]", NO_STR @@ -3282,7 +3805,7 @@ DEFUN (no_config_log_file, return CMD_SUCCESS; } -ALIAS (no_config_log_file, +ALIAS_CALL (no_config_log_file, no_config_log_file_level_cmd, "no log file FILENAME LEVEL", NO_STR @@ -3291,17 +3814,17 @@ ALIAS (no_config_log_file, "Logging file name\n" "Logging level\n") -DEFUN (config_log_syslog, +DEFUN_CALL (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); + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_get_default_lvl(NULL)); return CMD_SUCCESS; } -DEFUN (config_log_syslog_level, +DEFUN_CALL (config_log_syslog_level, config_log_syslog_level_cmd, "log syslog "LOG_LEVELS, "Logging control\n" @@ -3316,7 +3839,7 @@ DEFUN (config_log_syslog_level, return CMD_SUCCESS; } -DEFUN_DEPRECATED (config_log_syslog_facility, +DEFUN_DEP_CALL (config_log_syslog_facility, config_log_syslog_facility_cmd, "log syslog facility "LOG_FACILITIES, "Logging control\n" @@ -3329,12 +3852,12 @@ DEFUN_DEPRECATED (config_log_syslog_facility, if ((facility = facility_match(argv[0])) < 0) return CMD_ERR_NO_MATCH; - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); - zlog_default->facility = facility; + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_get_default_lvl(NULL)); + zlog_set_facility(NULL, facility); return CMD_SUCCESS; } -DEFUN (no_config_log_syslog, +DEFUN_CALL (no_config_log_syslog, no_config_log_syslog_cmd, "no log syslog [LEVEL]", NO_STR @@ -3346,7 +3869,7 @@ DEFUN (no_config_log_syslog, return CMD_SUCCESS; } -ALIAS (no_config_log_syslog, +ALIAS_CALL (no_config_log_syslog, no_config_log_syslog_facility_cmd, "no log syslog facility "LOG_FACILITIES, NO_STR @@ -3355,7 +3878,7 @@ ALIAS (no_config_log_syslog, "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) -DEFUN (config_log_facility, +DEFUN_CALL (config_log_facility, config_log_facility_cmd, "log facility "LOG_FACILITIES, "Logging control\n" @@ -3366,11 +3889,11 @@ DEFUN (config_log_facility, if ((facility = facility_match(argv[0])) < 0) return CMD_ERR_NO_MATCH; - zlog_default->facility = facility; + zlog_set_facility(NULL, facility); return CMD_SUCCESS; } -DEFUN (no_config_log_facility, +DEFUN_CALL (no_config_log_facility, no_config_log_facility_cmd, "no log facility [FACILITY]", NO_STR @@ -3378,11 +3901,11 @@ DEFUN (no_config_log_facility, "Reset syslog facility to default (daemon)\n" "Syslog facility\n") { - zlog_default->facility = LOG_DAEMON; + zlog_set_facility(NULL, LOG_DAEMON); return CMD_SUCCESS; } -DEFUN_DEPRECATED (config_log_trap, +DEFUN_DEP_CALL (config_log_trap, config_log_trap_cmd, "log trap "LOG_LEVELS, "Logging control\n" @@ -3390,19 +3913,15 @@ DEFUN_DEPRECATED (config_log_trap, LOG_LEVEL_DESC) { int new_level ; - int i; - + if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; - zlog_default->default_lvl = new_level; - for (i = 0; i < ZLOG_NUM_DESTS; i++) - if (zlog_default->maxlvl[i] != ZLOG_DISABLED) - zlog_default->maxlvl[i] = new_level; + zlog_set_default_lvl_dest (NULL, new_level); return CMD_SUCCESS; } -DEFUN_DEPRECATED (no_config_log_trap, +DEFUN_DEP_CALL (no_config_log_trap, no_config_log_trap_cmd, "no log trap [LEVEL]", NO_STR @@ -3410,32 +3929,32 @@ DEFUN_DEPRECATED (no_config_log_trap, "Permit all logging information\n" "Logging level\n") { - zlog_default->default_lvl = LOG_DEBUG; + zlog_set_default_lvl(NULL, LOG_DEBUG); return CMD_SUCCESS; } -DEFUN (config_log_record_priority, +DEFUN_CALL (config_log_record_priority, config_log_record_priority_cmd, "log record-priority", "Logging control\n" "Log the priority of the message within the message\n") { - zlog_default->record_priority = 1 ; + zlog_set_record_priority(NULL, 1) ; return CMD_SUCCESS; } -DEFUN (no_config_log_record_priority, +DEFUN_CALL (no_config_log_record_priority, no_config_log_record_priority_cmd, "no log record-priority", NO_STR "Logging control\n" "Do not log the priority of the message within the message\n") { - zlog_default->record_priority = 0 ; + zlog_set_record_priority(NULL, 0) ; return CMD_SUCCESS; } -DEFUN (config_log_timestamp_precision, +DEFUN_CALL (config_log_timestamp_precision, config_log_timestamp_precision_cmd, "log timestamp precision <0-6>", "Logging control\n" @@ -3443,6 +3962,8 @@ DEFUN (config_log_timestamp_precision, "Set the timestamp precision\n" "Number of subsecond digits\n") { + int timestamp_precision; + if (argc != 1) { vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); @@ -3450,11 +3971,13 @@ DEFUN (config_log_timestamp_precision, } VTY_GET_INTEGER_RANGE("Timestamp Precision", - zlog_default->timestamp_precision, argv[0], 0, 6); + timestamp_precision, argv[0], 0, 6); + zlog_set_timestamp_precision(NULL, timestamp_precision); + return CMD_SUCCESS; } -DEFUN (no_config_log_timestamp_precision, +DEFUN_CALL (no_config_log_timestamp_precision, no_config_log_timestamp_precision_cmd, "no log timestamp precision", NO_STR @@ -3462,11 +3985,11 @@ DEFUN (no_config_log_timestamp_precision, "Timestamp configuration\n" "Reset the timestamp precision to the default value of 0\n") { - zlog_default->timestamp_precision = 0 ; + zlog_set_timestamp_precision(NULL, 0); return CMD_SUCCESS; } -DEFUN (banner_motd_file, +DEFUN_CALL (banner_motd_file, banner_motd_file_cmd, "banner motd file [FILE]", "Set banner\n" @@ -3481,7 +4004,7 @@ DEFUN (banner_motd_file, return CMD_SUCCESS; } -DEFUN (banner_motd_default, +DEFUN_CALL (banner_motd_default, banner_motd_default_cmd, "banner motd default", "Set banner string\n" @@ -3492,7 +4015,7 @@ DEFUN (banner_motd_default, return CMD_SUCCESS; } -DEFUN (no_banner_motd, +DEFUN_CALL (no_banner_motd, no_banner_motd_cmd, "no banner motd", NO_STR @@ -3500,7 +4023,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; @@ -3540,7 +4063,7 @@ cmd_init (int terminal) desc_cr.str = XSTRDUP(MTYPE_STRVEC, ""); /* Allocate initial top vector of commands. */ - cmdvec = vector_init (VECTOR_MIN_SIZE); + cmdvec = vector_init (0); /* Default host value settings. */ host.name = NULL; @@ -3604,7 +4127,7 @@ cmd_init (int terminal) install_default (CONFIG_NODE); } - + install_element (CONFIG_NODE, &hostname_cmd); install_element (CONFIG_NODE, &no_hostname_cmd); @@ -3669,39 +4192,52 @@ cmd_terminate () if (cmdvec) { - for (i = 0; i < vector_active (cmdvec); i++) - if ((cmd_node = vector_slot (cmdvec, i)) != NULL) - { - cmd_node_v = cmd_node->cmd_vector; + for (i = 0; i < vector_length (cmdvec); i++) + { + cmd_node = vector_get_item (cmdvec, i) ; + if (cmd_node == NULL) + continue ; - for (j = 0; j < vector_active (cmd_node_v); j++) - if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL && - cmd_element->strvec != NULL) - { - cmd_element_v = cmd_element->strvec; - - for (k = 0; k < vector_active (cmd_element_v); k++) - if ((desc_v = vector_slot (cmd_element_v, k)) != NULL) - { - for (l = 0; l < vector_active (desc_v); l++) - if ((desc = vector_slot (desc_v, l)) != NULL) - { - if (desc->cmd) - XFREE (MTYPE_STRVEC, desc->cmd); - if (desc->str) - XFREE (MTYPE_STRVEC, desc->str); - - XFREE (MTYPE_DESC, desc); - } - vector_free (desc_v); - } - - cmd_element->strvec = NULL; - vector_free (cmd_element_v); - } + cmd_node_v = cmd_node->cmd_vector; + + for (j = 0; j < vector_length (cmd_node_v); j++) + { + cmd_element = vector_get_item (cmd_node_v, j) ; + if (cmd_element == NULL) + continue ; - vector_free (cmd_node_v); - } + cmd_element_v = cmd_element->strvec ; + if (cmd_element_v == NULL) + continue ; + + for (k = 0; k < vector_length (cmd_element_v); k++) + { + desc_v = vector_get_item (cmd_element_v, k) ; + if (desc_v == NULL) + continue ; + + for (l = 0; l < vector_length (desc_v); l++) + { + desc = vector_get_item (desc_v, l) ; + if (desc == NULL) + continue ; + + if (desc->cmd) + XFREE (MTYPE_STRVEC, desc->cmd); + if (desc->str) + XFREE (MTYPE_STRVEC, desc->str); + + XFREE (MTYPE_DESC, desc); + } ; + vector_free (desc_v); + } ; + + cmd_element->strvec = NULL; + vector_free (cmd_element_v); + } ; + + vector_free (cmd_node_v); + } ; vector_free (cmdvec); cmdvec = NULL; |