diff options
author | Chris Hall <GMCH@hestia.halldom.com> | 2010-03-16 01:35:19 +0000 |
---|---|---|
committer | Chris Hall <GMCH@hestia.halldom.com> | 2010-03-16 01:35:19 +0000 |
commit | d87a9d74eab06082ea49313083ffa0aa41f666f9 (patch) | |
tree | 7c6f7ae0be39683b7c90ea298454ec28d49406cb /lib/command.c | |
parent | 05fb7fd0421b395c089bb08dd0e0d78d3746b8cf (diff) | |
download | quagga-d87a9d74eab06082ea49313083ffa0aa41f666f9.tar.bz2 quagga-d87a9d74eab06082ea49313083ffa0aa41f666f9.tar.xz |
Major update
bgpd/bgp_advertise.c
bgpd/bgp_advertise.h
The adj_in and adj_out objects are now put on a list based
on the peer to whom the route belongs.
The adj_in and adj_out objects also now point to the bgp_node
which they are routes for.
This substantially reduces the work needed to shut down a peer.
bgpd/bgp_damp.c
Changes to adj_in and adj_out forced small change to macros
used in bgp_damp.c to manage its lists.
bgpd/bgp_debug.c
Replaced direct access to vty->node by the required vty_get_node().
bgpd/bgp_dump.c
Changes to the names of fields in bgp_info structures.
bgpd/bgp_engine.h
Modified the debug and trace functions.
bgpd/bgp_fsm.c
Make use of sockunion2str() consistent with common usage.
Improved some documentation.
bgpd/bgp_main.c
Use the newly extended qpn_add_hook_function() facility.
bgpd/bgp_mplsvpn.c
Changes to the names of fields in bgp_info structures.
bgpd/bgp_msg_read.c
Bug fix: correct handling of capability code length.
Improvement: better casting in calculation of message length.
bgpd/bgp_msg_write.c
Bug fix: correct byte ordering of bgp_id in open message.
bgpd/bgp_network.c
Bug fix: correct handling of incoming connections.
Takes advantage of improvements in sockunion.c.
bgpd/bgp_nexthop.c
Changes to the names of fields in bgp_info structures.
bgpd/bgp_open_state.c
Remove mistaken #include of memtypes.h
bgpd/bgp_packet.c
Improvements to handling of withdrawing routes for peers.
bgpd/bgp_peer.c
Tidying up the state of peers as they are enabled and disabled.
Improvements to handling of withdrawing routes for peers.
bgpd/bgp_peer.h
Adding list bases for lists of routes originated by the peer.
bgpd/bgp_peer_index.c
Bug fix: correct freeing of peer indexes.
bgpd/bgp_route.c
Implement lists of bgp_info based in the owning peer. Adjust
for name changes to bgp_info fields.
Reimplemented all the clearing functions to use the lists of
items that belong to the peer -- rather than searching route
tables for stuff to withdraw.
Changed work queue handling for added/changed routes, so
that queues run through existing items, rather than having
queues of auxiliary items -- lower memory overhead.
bgpd/bgp_route.h
Added fields to bgp_info to allow all bgp_info originated by
each peer to live on lists based in the peer. And changed the
name of existing fields to avoid confusion.
bgpd/bgp_routemap.c
Removing redundant code and fixing a memory leak.
bgpd/bgp_table.h
Based work queue for added/changed routes directly in the
table, rather than having auxiliary structures.
bgpd/bgp_vty.c
Use vty_get_node() and vty_set_node() rather than direct
access to the vty field.
bgpd/bgpd.c
Implement changes to route clearing.
bgpd/bgpd.h
Changes to work queue handling.
lib/buffer.c
Changes to allow embedded buffer structures.
lib/buffer.h
Moved struct buffer here so that could have embedded buffer
structurs.
lib/command.c
Substantial tidy up and document exercise.
Restructured the top level command processing and finding
of descriptions and command completion.
Removal of unpleasant messing around with the insides of
vector structures.
Movement of some command actions to vty.c.
Uses uty.h to pick up the "private" functions from vty.c
et al.
lib/command.h
Moved the "node" values to node_type.h, so that can use an
enum node_type in places where cannot include command.h.
lib/command_queue.c
Updated to cope with the called command changing the node
value.
Improved handling of revoked commands, so the the command
line handler does not get stuck waiting for a command to
complete which has been revoked !
lib/command_queue.h
Improved message format.
lib/if.c
Use vty_set_node().
lib/keychain.c
Use vty_set_node().
new lib/keystroke.c
new lib/keystroke.h
New code to implement a keystroke FIFO.
This moves some complexity out of the command handler.
The handling of mixtures of escapes and Telnet IACs is
tightened up.
It would be possible to extend this to, say, UTF-8.
Regularises the "stealing" of keystrokes for the "--more--"
output handling... which was a bit hit and miss.
new lib/list_util.c
new lib/list_util.h
New code to implement various forms of linked list,
where the list pointers are embedded in structures.
lib/log.c
Changed the handling of log messages, so that all types
of log output (except syslog) use the same message buffer
scheme, and the message is constructed once and once
only.
Changes to the handling of VTY_LOCK() etc.
Uses uty.h to pick up the "private" functions from vty.c
et al.
lib/log.h
Changes to the buffering of log messages.
new lib/mem_tracker.c
New code to track memory allocation/deallocation, for
debug purposes.
lib/memory.c
lib/memory.h
Updated to allow the use of the mem_tracker.
lib/memtypes.awk
Made the memtypes into a named enum MTYPE.
lib/memtypes.c
Various new memory types.
lib/mqueue.c
lib/mqueue.h
Add mqueue_finish function for close-down.
lib/network.c
lib/network.h
Added non-blocking read_nb() and write_nb().
new lib/node_type.h
As above.
lib/plist.c
Remove vty_puts() which wasn't a good idea.
lib/qlib_init.c
Added qps_init() to first stage and mqueue_finish to
finish.
lib/qpnexus.c
lib/qpnexus.h
More flexible hooks for in_thread_init and in_thread_final.
lib/qpselect.c
lib/qpselect.h
Added qps_start_up() to build the required maps once and
for all.
Added qdebug to control the debug checks and validation.
Improved validation and test functions.
new lib/qstring.c
new lib/qstring.h
New code for limited flexible string handling.
lib/qtimers.c
Added qdebug to control the debug checks and validation.
lib/routemap.c
Use vty_set_node().
lib/sockunion.c
lib/sockunion.h
Tidied up and regularised the handling of sin_len and
sin6_len.
Created common function for setting port into socket.
Created common function for initialisation/allocation of
new sockunion.
Reduced various functions by using common sub-functions.
Rationalised some code.
Added sockunion_listen() and sockunion_new_sockaddr().
Renamed sockunion_new() to sockunion_new_prefix().
Improved some logging messages.
Added documentation.
new lib/uty.h
Functions etc. used only by vty/command/log/vty_io and
vty_cli.
lib/vector.c
lib/vector.h
Added vector_t type.
Removed VECTOR_INDEX, vector_only_wrapper_free() and
vector_only_index_free() -- following improvement of
code in command.c.
Added vector_set_min_length(), vector_set_new_min_length()
and vector_length() functions.
new lib/vio_fifo.c
new lib/vio_fifo.h
New code to manage simple FIFO of indefinite length.
lib/vty.c
lib/vty.h
Reworked. Broken into vty.c, vty_io.c and vty_cli.c.
new lib/vty_cli.c
new lib/vty_cli.h
CLI handling parts of the vty family.
new lib/vty_io.c
new lib/vty_io.h
I/O parts of the vty family.
lib/workqueue.h
Introduced tyedefs for the various call-back entries.
new tests/test-list_util.c
Tests for the list-util stuff.
vtysh/vtysh.c
Small change to interface for cmd_execute_command()
Diffstat (limited to 'lib/command.c')
-rw-r--r-- | lib/command.c | 1383 |
1 files changed, 860 insertions, 523 deletions
diff --git a/lib/command.c b/lib/command.c index 2c9ca64a..251c8963 100644 --- a/lib/command.c +++ b/lib/command.c @@ -30,6 +30,8 @@ Boston, MA 02111-1307, USA. */ #include "thread.h" #include "vector.h" #include "vty.h" +#include "uty.h" +#include "qstring.h" #include "command.h" #include "workqueue.h" #include "command_queue.h" @@ -242,10 +244,29 @@ sort_node () } } -/* 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 +/*------------------------------------------------------------------------------ + * 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) { const char *cp, *start; @@ -258,58 +279,69 @@ cmd_make_strvec (const char *string) cp = string; - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') + /* Skip white spaces. */ + while (isspace((int) *cp)) cp++; - /* Return if there is only white spaces */ - if (*cp == '\0') + /* Return if line is empty or effectively empty */ + if ((*cp == '\0') || (*cp == '!') || (*cp == '#')) return NULL; - if (*cp == '!' || *cp == '#') - return NULL; + /* Prepare return vector -- expect some reasonable number of tokens. */ + strvec = vector_init (10) ; - /* Prepare return vector. */ - strvec = vector_init (0); - - /* Copy each command piece and set into vector. */ + /* Copy each command piece and set into vector. */ while (1) { start = cp; - while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && - *cp != '\0') - cp++; + while (!isspace((int) *cp) && (*cp != '\0')) + cp++ ; /* eat token characters */ strlen = cp - start; token = XMALLOC (MTYPE_STRVEC, strlen + 1); memcpy (token, start, strlen); *(token + strlen) = '\0'; - vector_set (strvec, token); + vector_push_item(strvec, token); - while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && - *cp != '\0') - cp++; + while (isspace((int) *cp)) + cp++ ; /* skip white-space */ if (*cp == '\0') return strvec; } } -/* Free allocated string vector. */ -void -cmd_free_strvec (vector v) +/*------------------------------------------------------------------------------ + * Add given string to vector of strings. + * + * Create vector if required. + */ +extern vector +cmd_add_to_strvec (vector strvec, const char* str) { - unsigned int i; - char *cp; + 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 * @@ -472,7 +504,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_slot (cmdvec, node); + + if (cnode == NULL) + { + zlog_err("Could not find prompt for node %d for", node) ; + return NULL ; + } ; + return cnode->prompt; } @@ -683,21 +727,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 { - 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 +819,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 +914,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 @@ -952,6 +1042,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) { @@ -1085,6 +1188,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,124 +1243,182 @@ 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; 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; + for (i = 0; i < vector_active (cmd_v); i++) + { + const char *str; + struct cmd_element *cmd_element; + vector descvec; + struct desc *desc; + unsigned int j; + int matched ; - descvec = vector_slot (cmd_element->strvec, index); + /* Skip past cmd_v entries that have already been set NULL */ + if ((cmd_element = vector_slot (cmd_v, i)) == NULL) + continue ; - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - str = desc->cmd; + /* Discard cmd_v entry that has no token at the current position */ + if (index >= vector_active (cmd_element->strvec)) + { + vector_slot (cmd_v, i) = NULL; + continue ; + } ; - 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; + /* See if get any sort of match at current position */ + matched = 0 ; + descvec = vector_slot (cmd_element->strvec, index); - matched++; - } - } + 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; + + matched++; + } + } #ifdef HAVE_IPV6 - 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 (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++; + } + } #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 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++; + } ; + } ; + + /* Discard cmd_v entry that has no match at this position */ + if (!matched) + vector_slot (cmd_v, i) = NULL; + } - 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; } -/* Filter vector by command character with index. */ +/*------------------------------------------------------------------------------ + * 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; @@ -1261,13 +1430,13 @@ cmd_filter_by_string (char *command, vector v, unsigned int index) 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) + for (i = 0; i < vector_active (cmd_v); i++) + if ((cmd_element = vector_slot (cmd_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; + vector_slot (cmd_v, i) = NULL; else { unsigned int j; @@ -1349,15 +1518,49 @@ cmd_filter_by_string (char *command, vector v, unsigned int index) } } if (!matched) - vector_slot (v, i) = NULL; + vector_slot (cmd_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 '/'). + */ 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; @@ -1367,8 +1570,8 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) vector descvec; struct desc *desc; - for (i = 0; i < vector_active (v); i++) - if ((cmd_element = vector_slot (v, i)) != NULL) + for (i = 0; i < vector_active (cmd_v); i++) + if ((cmd_element = vector_slot (cmd_v, i)) != NULL) { int match = 0; @@ -1447,25 +1650,27 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) } } if (!match) - vector_slot (v, i) = NULL; + vector_slot (cmd_v, i) = NULL; } return 0; } -/* If src matches dst return dst string, otherwise return NULL */ +/*------------------------------------------------------------------------------ + * 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,8 +1742,12 @@ cmd_entry_function_desc (const char *src, const char *dst) return NULL; } -/* Check same string element existence. If it isn't there return - 1. */ +/*------------------------------------------------------------------------------ + * Check same string element existence. + * + * Returns: 0 => found same string in the vector + * 1 => NOT found same string in the vector + */ static int cmd_unique_string (vector v, const char *str) { @@ -1569,15 +1778,9 @@ desc_unique_string (vector v, const char *str) static int cmd_try_do_shortcut (enum node_type node, char* first_word) { - if ( first_word != NULL && - node != AUTH_NODE && - node != VIEW_NODE && - node != AUTH_ENABLE_NODE && - node != ENABLE_NODE && - node != RESTRICTED_NODE && - 0 == strcmp( "do", first_word ) ) - return 1; - return 0; + return (node >= MIN_DO_SHORTCUT_NODE) + && (first_word != NULL) + && (strcmp( "do", first_word) == 0) ? 1 : 0 ; } /* '?' describe command support. */ @@ -1717,6 +1920,19 @@ cmd_describe_command_real (vector vline, int node, 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, int node, int *status) { @@ -1745,134 +1961,166 @@ cmd_describe_command (vector vline, int node, int *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 ; - for (i = 1; matched[i] != NULL; i++) + ss = vector_get_item(matchvec, 0) ; + lcd = strlen(ss) ; + + 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 ** +/*------------------------------------------------------------------------------ + * 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, 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 ; + /* Stop immediately if the vline is empty. */ if (vector_active (vline) == 0) { - vector_free (cmd_vector); *status = CMD_ERR_NO_MATCH; return NULL; } - else - index = vector_active (vline) - 1; - /* First, filter by 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_active (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_slot (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_slot(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_active (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_slot (cmd_v, i)) == NULL) + continue ; - /* We don't need cmd_vector any more. */ - vector_free (cmd_vector); + vector strvec = cmd_element->strvec; + + /* Check field length */ + if (index >= vector_active (strvec)) + { + vector_slot (cmd_v, i) = NULL; + continue ; + } + + descvec = vector_slot (strvec, index); + + for (j = 0; j < vector_active (descvec); j++) + if ((desc = vector_slot (descvec, j)) != NULL) + { + string = cmd_entry_function(token, desc->cmd) ; + if ((string != NULL) && cmd_unique_string(matchvec, string)) + cmd_add_to_strvec (matchvec, string) ; + } + } ; + + n = vector_end(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') + if (*token == '\0') *status = CMD_ERR_NOTHING_TODO; else *status = CMD_ERR_NO_MATCH; @@ -1880,66 +2128,50 @@ cmd_complete_command_real (vector vline, int node, int *status) } /* XXX: TODO: stop poking around inside vector */ - /* Only one matched */ - if (vector_slot (matchvec, 1) == NULL) + /* Only one matched */ + if (n == 1) { - match_str = (char **) matchvec->VECTOR_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->VECTOR_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->VECTOR_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->VECTOR_INDEX; - vector_only_wrapper_free (matchvec); + vector_push_item(matchvec, lcdstr) ; *status = CMD_COMPLETE_MATCH; - return match_str; + return matchvec ; } } } - match_str = (char **) matchvec->VECTOR_INDEX; - vector_only_wrapper_free (matchvec); *status = CMD_COMPLETE_LIST_MATCH; - return match_str; + return matchvec ; } -char ** +/*------------------------------------------------------------------------------ + * 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(node, vector_slot(vline, 0) ) ) { @@ -1964,13 +2196,14 @@ cmd_complete_command (vector vline, int node, int *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) @@ -1980,69 +2213,101 @@ 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; - default: - ret = CONFIG_NODE; - } + return KEYCHAIN_NODE; - return ret; -} + default: + return CONFIG_NODE; + } +} + +/*------------------------------------------------------------------------------ + * 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() + * + * Takes the node from parsed->cnode. + * + * Returns: CMD_SUCCESS => successfully parsed command, and the result is + * in the given parsed structure, ready for execution. + * + * CMD_SUCCESS_DAEMON => as CMD_SUCCESS, and the command has a + * "daemon" value. + * + * otherwise => some failure to parse + * + * NB: the argv[] in the parsed structure contains *copies* of the char* + * pointers in the given vline. + * + * The vline may not be released until the parsed structure is. + * + * The parsed structure may be released without worrying about the contents + * of the argv[]. + */ +enum cmd_parse_type +{ + cmd_parse_completion = 0, + cmd_parse_strict = 1, +}; -/* Execute command by argument vline vector. */ static int -cmd_execute_command_real (vector vline, struct vty *vty, - struct cmd_element **cmd, qpn_nexus dest_nexus) +cmd_parse_command(vector vline, int first, struct cmd_parsed* parsed, + enum cmd_parse_type type) { - unsigned int i; - unsigned int index; - vector cmd_vector; + unsigned int i ; + unsigned int ivl ; + unsigned index ; + vector cmd_v; 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; - /* Make copy of command elements. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty_get_node(vty))); + int strict = (type == cmd_parse_strict) ; - for (index = 0; index < vector_active (vline); index++) - if ((command = vector_slot (vline, index))) + parsed->cmd = NULL ; /* return this if parsing fails */ + parsed->argc = 0 ; + + /* Make copy of command elements. */ + cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode)); + + /* Look for an unambiguous result */ + index = 0 ; + for (ivl = first; ivl < vector_active (vline); ivl++) + if ((command = vector_slot (vline, ivl)) != NULL) { - int ret; + int ret ; - match = cmd_filter_by_completion (command, cmd_vector, index); + index = ivl - first ; + match = strict ? cmd_filter_by_string(command, cmd_v, index) + : cmd_filter_by_completion(command, cmd_v, index) ; if (match == vararg_match) break; - ret = is_cmd_ambiguous (command, cmd_vector, index, match); + ret = is_cmd_ambiguous (command, cmd_v, index, match); - if (ret == 1) + if (ret != 0) { - vector_free (cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - else if (ret == 2) - { - vector_free (cmd_vector); - return CMD_ERR_NO_MATCH; + assert((ret == 1) || (ret == 2)) ; + vector_free (cmd_v); + return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ; } } - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; + /* Check matched count. */ + matched_element = NULL; + matched_count = 0; incomplete_count = 0; - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i))) + for (i = 0; i < vector_active (cmd_v); i++) + if ((cmd_element = vector_slot (cmd_v, i)) != NULL) { if (match == vararg_match || index >= cmd_element->cmdsize) { @@ -2058,133 +2323,255 @@ cmd_execute_command_real (vector vline, struct vty *vty, } } - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); + /* Finished with cmd_v. */ + vector_free (cmd_v); - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) + /* To execute command, matched_count must be 1. */ + if (matched_count != 1) { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; + if (matched_count == 0) + return (incomplete_count) ? CMD_ERR_INCOMPLETE : CMD_ERR_NO_MATCH ; else - return CMD_ERR_NO_MATCH; - } - - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; + return CMD_ERR_AMBIGUOUS ; + } ; - /* Argument treatment */ - varflag = 0; - argc = 0; + /* Found command -- process the arguments ready for execution */ + varflag = 0 ; + argc = 0 ; - for (i = 0; i < vector_active (vline); i++) + for (ivl = first; ivl < vector_active (vline); ivl++) { - if (varflag) - argv[argc++] = vector_slot (vline, i); - else + int take = varflag ; + + if (!varflag) { - vector descvec = vector_slot (matched_element->strvec, i); + int index = ivl - first ; + vector descvec = vector_slot (matched_element->strvec, index); if (vector_active (descvec) == 1) { struct desc *desc = vector_slot (descvec, 0); if (CMD_VARARG (desc->cmd)) - varflag = 1; - - if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) - argv[argc++] = vector_slot (vline, i); + take = varflag = 1 ; + else + take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ; } else - argv[argc++] = vector_slot (vline, i); + take = 1 ; } - if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } + if (take) + { + if (argc >= CMD_ARGC_MAX) + return CMD_ERR_EXCEED_ARGC_MAX ; + parsed->argv[argc++] = vector_slot (vline, ivl); + } ; + } ; - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; + /* Everything checks out... ready to execute command */ + parsed->cmd = matched_element ; + parsed->argc = argc ; - if (matched_element->daemon) + if (parsed->cmd->daemon) return CMD_SUCCESS_DAEMON; - /* Execute matched command. */ - if (qpthreads_enabled && !(matched_element->attr & CMD_ATTR_CALL)) - { - /* Don't do it now, but send to bgp qpthread */ - cq_enqueue(matched_element, vty, argc, argv, dest_nexus); - return CMD_QUEUED; - } - else - { - return (*matched_element->func) (matched_element, vty, argc, argv); - } -} + return CMD_SUCCESS ; +} ; + +/*------------------------------------------------------------------------------ + * Parse a command in the given "node", or any of its ancestors. + * + * Takes the node to start in from parsed->cnode. + * + * Returns: CMD_SUCCESS => successfully parsed command, and the result is + * in the given parsed structure, ready for execution. + * + * NB: parsed->cnode may have changed. + * + * CMD_SUCCESS_DAEMON => as CMD_SUCCESS, and the command has a + * "daemon" value. + * + * otherwise => some failure to parse + * + * NB: returns error from attempt to parse in the + * starting parsed->cnode (which is returned + * unchanged). + * + * See cmd_parse_command() for more detail. + */ +static int +cmd_parse_command_tree(vector vline, int first, struct cmd_parsed* parsed, + enum cmd_parse_type type) +{ + int ret ; + int first_ret ; + enum node_type first_node ; -int -cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, - qpn_nexus dest_nexus, int vtysh) { - int ret, saved_ret, tried = 0; - enum node_type onode, try_node; + /* Try in the current node */ + ret = cmd_parse_command(vline, first, parsed, type) ; - onode = try_node = vty_get_node(vty); + if ((ret == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON)) + return ret ; /* done if found command */ - if ( cmd_try_do_shortcut(vty_get_node(vty), vector_slot(vline, 0) ) ) - { - vector shifted_vline; - unsigned int index; + /* Try in parent node(s) */ + first_node = parsed->cnode ; + first_ret = ret ; - vty_set_node(vty, ENABLE_NODE); - /* We can try it on enable node, cos' the vty is authenticated */ + while (parsed->cnode <= CONFIG_NODE) + { + parsed->cnode = node_parent(parsed->cnode) ; + ret = cmd_parse_command(vline, first, parsed, type) ; - 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 == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON)) + return ret ; /* done if found command */ + } ; - ret = cmd_execute_command_real (shifted_vline, vty, cmd, dest_nexus); + parsed->cnode = first_node ; /* restore node state */ + return first_ret ; /* return original result */ +} - vector_free(shifted_vline); - vty_set_node(vty, onode); - return ret; - } +/*------------------------------------------------------------------------------ + * Prepare to parse command -- deal with possible "do" shortcut. + * + * Initialises parsed structure, setting cnode and onode to vty->node. + * + * Checks to see if this is a valid "do" shortcut, and adjusts cnode if so. + * + * Returns: 0 => not "do" -- first word of actual command is first word + * 1 => is "do" -- first word of actual command is second word + */ +static int +cmd_pre_command(struct vty* vty, struct cmd_parsed* parsed, vector vline) +{ + int first ; + parsed->onode = parsed->cnode = vty_get_node(vty) ; - saved_ret = ret = cmd_execute_command_real (vline, vty, cmd, dest_nexus); + parsed->cmd = NULL ; + parsed->argc = 0 ; + /* 'do' forces command to be parsed in ENABLE_NODE (if allowed) */ + if (cmd_try_do_shortcut(parsed->cnode, vector_slot(vline, 0))) + { + parsed->cnode = ENABLE_NODE ; + parsed->do_shortcut = 1 ; + first = 1 ; + } + else + { + parsed->do_shortcut = 0 ; + first = 0 ; + } ; + + return first ; +} ; + +/*------------------------------------------------------------------------------ + * 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 + * action = true unless command return is being revoked + */ +extern void +cmd_post_command(struct vty* vty, struct cmd_parsed* parsed, int ret, + int action) +{ + if (parsed->do_shortcut) + { + if (vty_get_node(vty) == ENABLE_NODE) + vty_set_node(vty, parsed->onode) ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * Execute command: + * + * -- use cmd_parse_completion type parsing. + * + * -- unless vtysh: if does not parse in current node, try ancestors + * + * If does not find in any ancestor, return error from current node. + * + * -- implement the "do" shortcut + * + * 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. + */ +extern int +cmd_execute_command (vector vline, struct vty *vty, + struct cmd_element **cmd, qpn_nexus to_nexus, + qpn_nexus from_nexus, int vtysh) +{ + int ret ; + int first ; + struct cmd_parsed parsed ; + + /* Set up parsed structure & deal with "do" shortcut prefix */ + first = cmd_pre_command(vty, &parsed, vline) ; + + /* Try to parse in parsed.cnode node or, if required, ancestors thereof. */ if (vtysh) - return saved_ret; + ret = cmd_parse_command(vline, first, &parsed, cmd_parse_completion) ; + else + ret = cmd_parse_command_tree(vline, first, &parsed, cmd_parse_completion) ; - /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ - while ( ret != CMD_SUCCESS && ret != CMD_WARNING - && vty_get_node(vty) > CONFIG_NODE ) + if (cmd) + *cmd = parsed.cmd ; /* for vtysh */ + + if (ret != CMD_SUCCESS) + return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */ + + /* Execute the parsed command */ + + vty_set_node(vty, parsed.cnode) ; + + if (qpthreads_enabled && !(parsed.cmd->attr & CMD_ATTR_CALL)) { - try_node = node_parent(try_node); - vty_set_node(vty, try_node); - ret = cmd_execute_command_real (vline, vty, cmd, dest_nexus); - tried = 1; - if (ret == CMD_SUCCESS || ret == CMD_WARNING) - { - /* succesfull command, leave the node as is */ - return ret; - } + /* Don't do it now, but send to bgp qpthread */ + cq_enqueue(vty, &parsed, to_nexus, from_nexus) ; + ret = CMD_QUEUED ; } - /* no command succeeded, reset the vty to the original node and - return the error for this node */ - if ( tried ) - vty_set_node(vty, onode); - return saved_ret; -} - -/* Execute command by argument readline. */ -int + else + { + ret = (*(parsed.cmd->func))(parsed.cmd, vty, parsed.argc, parsed.argv) ; + + cmd_post_command(vty, &parsed, ret, 1) ; + } ; + + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Execute command by argument readline. + * + * -- use cmd_parse_strict type parsing. + * + * -- unless vtysh: if does not parse in current node, try ancestors + * + * If does not find in any ancestor, return error from current node. + * + * -- does NOT implement the "do" shortcut + * + * At all times executes the command immediately (no queueing, even if + * qpthreads_enabled). + * + * The vty->node may be changed either when parsing or executing the command. + */ +extern int cmd_execute_command_strict (vector vline, struct vty *vty, - struct cmd_element **cmd) + struct cmd_element **cmd, int vtysh) { +#if 0 /* replaced by cmd_parse_command() */ unsigned int i; unsigned int index; vector cmd_vector; @@ -2285,62 +2672,74 @@ cmd_execute_command_strict (vector vline, struct vty *vty, } if (argc >= CMD_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; + return CMD_ERR_EXCEED_ARGC_MAX; } +#endif + + struct cmd_parsed parsed ; + int ret ; + + /* Try to parse in parsed.cnode node or, if required, ancestors thereof. */ + if (vtysh) + ret = cmd_parse_command(vline, 0, &parsed, cmd_parse_strict) ; + else + ret = cmd_parse_command_tree(vline, 0, &parsed, cmd_parse_strict) ; - /* For vtysh execution. */ if (cmd) - *cmd = matched_element; + *cmd = parsed.cmd ; /* for vtysh */ - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; + if (ret != CMD_SUCCESS) + return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */ - /* Now execute matched command */ - return (*matched_element->func) (matched_element, vty, argc, argv); + /* Now execute matched command */ + vty_set_node(vty, parsed.cnode) ; + + return (*(parsed.cmd->func)) (parsed.cmd, vty, parsed.argc, parsed.argv); } -/* Configration make from file. */ +/*------------------------------------------------------------------------------ + * Read configuration from file. + * + */ int -config_from_file (struct vty *vty, FILE *fp, void (*after_first_cmd)(void)) +config_from_file (struct vty *vty, FILE *fp, void (*after_first_cmd)(void), + qstring buf) { int ret; vector vline; int first_cmd = 1; - while (fgets (vty->buf, VTY_BUFSIZ, fp)) + while (fgets (buf->body, buf->size, fp)) { - vline = cmd_make_strvec (vty->buf); + vline = cmd_make_strvec (buf->body); - /* In case of comment line */ + /* In case of comment line */ if (vline == NULL) continue; - /* Execute configuration command : this is strict match */ - ret = cmd_execute_command_strict (vline, vty, NULL); - /* special handling for after the first command */ + /* Execute configuration command : this is strict match */ + ret = cmd_execute_command_strict (vline, vty, NULL, 0); + + /* special handling for after the first command */ if (first_cmd && after_first_cmd) { after_first_cmd(); first_cmd = 0; } - /* Try again with setting node to CONFIG_NODE */ - while (ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_ERR_NOTHING_TODO && vty_get_node(vty) != CONFIG_NODE) - { - vty_set_node(vty, node_parent(vty_get_node(vty))); - ret = cmd_execute_command_strict (vline, vty, NULL); - } - cmd_free_strvec (vline); + /* TODO: why does config file not stop on CMD_WARNING ?? */ if (ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_ERR_NOTHING_TODO) + && ret != CMD_ERR_NOTHING_TODO) return ret; - } + } ; + return CMD_SUCCESS; } +/*----------------------------------------------------------------------------*/ + /* Configration from terminal */ DEFUN_CALL (config_terminal, config_terminal_cmd, @@ -2348,14 +2747,11 @@ DEFUN_CALL (config_terminal, "Configuration from vty interface\n" "Configuration terminal\n") { - if (vty_config_lock (vty)) - vty_set_node(vty, 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 */ @@ -2391,47 +2787,7 @@ DEFUN_CALL (config_exit, "exit", "Exit current mode and down to previous mode\n") { - switch (vty_get_node(vty)) - { - case VIEW_NODE: - case ENABLE_NODE: - case RESTRICTED_NODE: - if (vty_shell (vty)) - exit (0); - else - vty_set_status(vty, VTY_CLOSE); - break; - case CONFIG_NODE: - vty_set_node(vty, 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_set_node(vty, CONFIG_NODE); - break; - case BGP_VPNV4_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV6_NODE: - case BGP_IPV6M_NODE: - vty_set_node(vty, BGP_NODE); - break; - case KEYCHAIN_KEY_NODE: - vty_set_node(vty, KEYCHAIN_NODE); - break; - default: - break; - } + vty_cmd_exit(vty) ; return CMD_SUCCESS; } @@ -2447,38 +2803,7 @@ DEFUN_CALL (config_end, "end", "End current mode and change to enable mode.") { - switch (vty_get_node(vty)) - { - 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_set_node(vty, ENABLE_NODE); - break; - default: - break; - } + vty_cmd_end(vty) ; return CMD_SUCCESS; } @@ -2539,7 +2864,7 @@ DEFUN_CALL (config_list, } /* Write current configuration into file. */ -DEFUN_CALL (config_write_file, +DEFUN (config_write_file, config_write_file_cmd, "write file", "Write running configuration to memory, network, or terminal\n" @@ -2645,18 +2970,18 @@ finished: return ret; } -ALIAS_CALL (config_write_file, +ALIAS (config_write_file, config_write_cmd, "write", "Write running configuration to memory, network, or terminal\n") -ALIAS_CALL (config_write_file, +ALIAS (config_write_file, config_write_memory_cmd, "write memory", "Write running configuration to memory, network, or terminal\n" "Write configuration to the file (same as write file)\n") -ALIAS_CALL (config_write_file, +ALIAS (config_write_file, copy_runningconfig_startupconfig_cmd, "copy running-config startup-config", "Copy configuration\n" @@ -2664,7 +2989,7 @@ ALIAS_CALL (config_write_file, "Copy running config to startup config (same as write file)\n") /* Write current configuration into the terminal. */ -DEFUN_CALL (config_write_terminal, +DEFUN (config_write_terminal, config_write_terminal_cmd, "write terminal", "Write running configuration to memory, network, or terminal\n" @@ -2700,14 +3025,14 @@ DEFUN_CALL (config_write_terminal, } /* Write current configuration into the terminal. */ -ALIAS_CALL (config_write_terminal, +ALIAS (config_write_terminal, show_running_config_cmd, "show running-config", SHOW_STR "running configuration\n") /* Write startup configuration into the terminal. */ -DEFUN_CALL (show_startup_config, +DEFUN (show_startup_config, show_startup_config_cmd, "show startup-config", SHOW_STR @@ -2753,10 +3078,16 @@ DEFUN_CALL (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; } @@ -2767,9 +3098,15 @@ DEFUN_CALL (config_no_hostname, "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; } |