diff options
Diffstat (limited to 'lib/command.c')
-rw-r--r-- | lib/command.c | 4310 |
1 files changed, 1222 insertions, 3088 deletions
diff --git a/lib/command.c b/lib/command.c index 67a9ab04..ee855360 100644 --- a/lib/command.c +++ b/lib/command.c @@ -21,83 +21,286 @@ along with GNU Zebra; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include <zebra.h> +#include "misc.h" +#include "version.h" +#include <ctype.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/utsname.h> #include "memory.h" -#include "log.h" -#include <lib/version.h> #include "thread.h" #include "vector.h" -#include "vty.h" -#include "uty.h" #include "qstring.h" +#include "qtime.h" +#include "workqueue.h" #include "command.h" +#include "command_local.h" +#include "command_parse.h" #include "command_execute.h" -#include "workqueue.h" #include "command_queue.h" +#include "log_local.h" +#include "vty_local.h" +#include "vty_command.h" +#include "vty_io.h" +#include "vty_log.h" +#include "network.h" + +/* Vector of cmd_node, one for each known node, built during daemon + * initialisation. + * + * Declared extern in command_local.h, so it can get at it. + */ +vector node_vector = NULL ; + +/*============================================================================== + * Default motd string and debug hello message. + */ +static const char* default_motd = +"\n" +"Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ")\n" + QUAGGA_COPYRIGHT "\n" +"\n" ; + +const char* debug_banner = + QUAGGA_PROGNAME " version " QUAGGA_VERSION " QDEBUG=" QDEBUG_NAME " " + __DATE__ " " __TIME__ ; + +/*============================================================================== + * Host information structure -- shared across command/vty + * + * Must have VTY_LOCK() or not be running multiple pthreads to access this ! + */ +struct host host = +{ + /* Host name of this router. */ + .name = NULL, /* set by cmd_init */ + .name_set = false, + .name_gen = 0, /* set by cmd_init */ + + /* Password for vty interface. */ + .password = NULL, + .password_encrypted = false, + + /* Enable password */ + .enable = NULL, + .enable_encrypted = false, + + /* System wide terminal lines. */ + .lines = -1, /* unset */ + + /* Log filename. */ + .logfile = NULL, + + /* config file name of this host */ + .config_file = NULL, + .config_dir = NULL, + + /* Flags for services */ + .advanced = false, + .encrypt = false, + + /* Banner configuration. */ + .motd = NULL, + .motdfile = NULL, + + /* Nobody has the config symbol of power */ + .config = false, + .config_brand = 0, + + /* allow VTY to start without password */ + .no_password_check = false, + + /* if VTY starts without password, use RESTRICTED_NODE */ + .restricted_mode = false, + + /* Vty timeout value -- see "exec timeout" command */ + .vty_timeout_val = VTY_TIMEOUT_DEFAULT, + + /* Vty access-class command */ + .vty_accesslist_name = NULL, + + /* Vty access-class for IPv6. */ + .vty_ipv6_accesslist_name = NULL, + + /* Current directory -- initialised in cmd_getcwd() */ + .cwd = NULL, +} ; + +/*============================================================================== + * host.name handling. + * + * If the host.name is set explicitly by command then host.name_set is true, + * and things are simple. + * + * Otherwise, need to ask the system. Does that once at start up, and if the + * host.name is unset by command -- so there should always be a valid host.name. + * + * However, it is conceivable that the host name changes (!). So, when asking + * for cmd_host_name(), can ask for the system to be asked afresh (if the name + * is not explicitly set). + * + * The VTY watch-dog timer refreshes the host.name on a regular basis, + * cmd_check_host_name(), so need not ask for a fresh host.name, unless wish + * to guarantee to be absolutely up to date. + * + * The VTY prompts need the current host name, but that is debounced using the + * host.name_gen value. host.name_gen is incremented each time the host.name + * actually changes. It is thought unlikely that this will ever wrap round, + * but it is guaranteed not to be zero. + * + * The fact that the host.name can change is reflected in the need to hold + * the VTY_LOCK while have the host.name in hand. + */ + +static void cmd_get_sys_host_name(void) ; +static void cmd_new_host_name(const char* name) ; + +/*------------------------------------------------------------------------------ + * Get the host name: (a) from an explicit host name command + * or: (b) from the last time the system was asked. + * + * Note that the system is asked regularly by the watch dog. + * + * NB: needs to be VTY_LOCK() *or* not running qpthreads. + */ +extern const char* +cmd_host_name(bool fresh) +{ + VTY_ASSERT_LOCKED() ; + + if (!host.name_set && fresh) + cmd_get_sys_host_name() ; + + return host.name ; +} ; + +/*------------------------------------------------------------------------------ + * Set (or unset) the host name from an explicit host name command. + * + * If unsets, immediately asks the system for the system host name (which may + * be the same !). + */ +static cmd_return_code_t +cmd_set_host_name(const char* name) +{ + VTY_LOCK() ; + + host.name_set = (name != NULL) ; + if (host.name_set) + cmd_new_host_name(name) ; + else + cmd_get_sys_host_name() ; + + VTY_UNLOCK() ; + return CMD_SUCCESS ; +} ; + +/*------------------------------------------------------------------------------ + * Get the host name from the system and set host.name to that. + * + * NB: result will not be NULL, whatever happens will get at least an empty + * '\0' terminated string. + * + * NB: called early in the morning to initialise host.name and to set + * host.name_gen != 0. + */ +static void +cmd_get_sys_host_name(void) +{ + struct utsname info ; -/* Command vector which includes some level of command lists. Normally - each daemon maintains each own cmdvec. */ -vector cmdvec = NULL; + VTY_ASSERT_LOCKED() ; -struct desc desc_cr; -char *command_cr = NULL; + uname (&info) ; + cmd_new_host_name(info.nodename) ; +} ; + +/*------------------------------------------------------------------------------ + * Set host.name to (possibly) new value. + * + * Does nothing if new name == old name, otherwise increments name_gen. + */ +static void +cmd_new_host_name(const char* name) +{ + VTY_ASSERT_LOCKED() ; -/* Host information structure. */ -struct host host; + if ((host.name == NULL) || (strcmp(host.name, name) != 0)) + { + XFREE(MTYPE_HOST, host.name) ; + host.name = XSTRDUP(MTYPE_HOST, name) ; + do ++host.name_gen ; while (host.name_gen == 0) ; + } ; +} ; -/* Standard command node structures. */ +/*============================================================================== + * node structures for the basic nodes. + * + * Covers everything up to and including the CONFIG_NODE. + */ static struct cmd_node auth_node = { - AUTH_NODE, - "Password: ", + .node = AUTH_NODE, + .prompt = "Password: ", + + .parent = AUTH_NODE, /* self => no parent */ + .exit_to = NULL_NODE, /* close ! */ + .end_to = AUTH_NODE, /* self => no end */ }; static struct cmd_node view_node = { - VIEW_NODE, - "%s> ", + .node = VIEW_NODE, + .prompt = "%s> ", + + .parent = VIEW_NODE, /* self => no parent */ + .exit_to = NULL_NODE, /* close ! */ + .end_to = VIEW_NODE, /* self => no end */ }; static struct cmd_node restricted_node = { - RESTRICTED_NODE, - "%s$ ", + .node = RESTRICTED_NODE, + .prompt = "%s$ ", + + .parent = RESTRICTED_NODE, /* self => no parent */ + .exit_to = NULL_NODE, /* close ! */ + .end_to = RESTRICTED_NODE, /* self => no end */ }; static struct cmd_node auth_enable_node = { - AUTH_ENABLE_NODE, - "Password: ", + .node = AUTH_ENABLE_NODE, + .prompt = "Password: ", + + .parent = AUTH_ENABLE_NODE, /* self => no parent */ + .exit_to = VIEW_NODE, /* fall back */ + .end_to = AUTH_ENABLE_NODE, /* self => no end */ }; static struct cmd_node enable_node = { - ENABLE_NODE, - "%s# ", + .node = ENABLE_NODE, + .prompt = "%s# ", + + .parent = ENABLE_NODE, /* self => no parent */ + .exit_to = NULL_NODE, /* close ! */ + .end_to = ENABLE_NODE, /* self => no end */ }; static struct cmd_node config_node = { - CONFIG_NODE, - "%s(config)# ", - 1 -}; - -/* Default motd string. */ -static const char *default_motd = -"\r\n\ -Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\ -" QUAGGA_COPYRIGHT "\r\n\ -\r\n"; + .node = CONFIG_NODE, + .prompt = "%s(config)# ", + .parent = ENABLE_NODE, /* more or less */ + .exit_to = ENABLE_NODE, /* exit == end for CONFIG_NODE */ + .end_to = ENABLE_NODE, /* standard end action */ -#ifdef QDEBUG -const char* debug_banner = - QUAGGA_PROGNAME " version " QUAGGA_VERSION " QDEBUG=" QDEBUG_NAME " " - __DATE__ " " __TIME__ ; -#endif + .config_to_vtysh = true +}; static const struct facility_map { int facility; @@ -105,16 +308,16 @@ static const struct facility_map { size_t match; } syslog_facilities[] = { - { LOG_KERN, "kern", 1 }, - { LOG_USER, "user", 2 }, - { LOG_MAIL, "mail", 1 }, + { LOG_KERN, "kern", 1 }, + { LOG_USER, "user", 2 }, + { LOG_MAIL, "mail", 1 }, { LOG_DAEMON, "daemon", 1 }, - { LOG_AUTH, "auth", 1 }, + { LOG_AUTH, "auth", 1 }, { LOG_SYSLOG, "syslog", 1 }, - { LOG_LPR, "lpr", 2 }, - { LOG_NEWS, "news", 1 }, - { LOG_UUCP, "uucp", 2 }, - { LOG_CRON, "cron", 1 }, + { LOG_LPR, "lpr", 2 }, + { LOG_NEWS, "news", 1 }, + { LOG_UUCP, "uucp", 2 }, + { LOG_CRON, "cron", 1 }, #ifdef LOG_FTP { LOG_FTP, "ftp", 1 }, #endif @@ -129,6 +332,31 @@ static const struct facility_map { { 0, NULL, 0 }, }; +#if 0 +static enum cmd_return_code +cmd_pipe_func(cmd_command self DEFUN_CMD_ARG_UNUSED, + struct vty* vty DEFUN_CMD_ARG_UNUSED, + int argc DEFUN_CMD_ARG_UNUSED, + argv_t argv DEFUN_CMD_ARG_UNUSED) +{ + return CMD_SUCCESS ; +} ; + +static struct cmd_command cmd_pipe_element = +{ + .string = "< or <|", /* Dummy */ + .func = cmd_pipe_func, + .doc = "Pipe input to command processor", + .daemon = 0, + .items = NULL, + .cmdsize = 0, + .config = NULL, + .subconfig = NULL, + .attr = CMD_ATTR_SIMPLE, +} ; +#endif + + static const char * facility_name(int facility) { @@ -171,9 +399,12 @@ print_version (const char *progname) } -/* Utility function to concatenate argv argument into a single string - with inserting ' ' character between each argument. */ -char * +/*------------------------------------------------------------------------------ + * Concatenate argv argument into a single string, inserting ' ' between each + * argument. + * + */ +extern char * argv_concat (const char* const* argv, int argc, int shift) { int i; @@ -184,8 +415,10 @@ argv_concat (const char* const* argv, int argc, int shift) len = 0; for (i = shift; i < argc; i++) len += strlen(argv[i])+1; + if (!len) return NULL; + p = str = XMALLOC(MTYPE_TMP, len); for (i = shift; i < argc; i++) { @@ -203,8 +436,8 @@ argv_concat (const char* const* argv, int argc, int shift) static int cmp_node (const void *p, const void *q) { - const struct cmd_element *a = *(struct cmd_element * const *)p; - const struct cmd_element *b = *(struct cmd_element * const *)q; + const struct cmd_command *a = *(struct cmd_command * const *)p; + const struct cmd_command *b = *(struct cmd_command * const *)q; return strcmp (a->string, b->string); } @@ -220,209 +453,172 @@ cmp_desc (const void *p, const void *q) #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 ; - - for (i = 0; i < vector_length(cmdvec); i++) - { - struct cmd_node *cnode; - vector cmd_vector ; - unsigned int j; - - cnode = vector_get_item(cmdvec, i) ; - - 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. + * Install top node of command vector. * - * Discards leading and trailing white-space. + * Initialised as follows: * - * Treats lines that start with '!' or '#' (after any leading white-space) - * as empty -- these are comment lines. + * .node -- must be set before node is installed + * .prompt -- must be set before node is installed + * .config_to_vtysh -- must be set true/false before node is installed * - * Tokens are non-whitespace separated by one or more white-space. + * .parent -- may be set, or if 0 (node_null) will be set to default + * .exit_to -- may be set, or if 0 (node_null) will be set to default + * .end_to -- may be set, or if 0 (node_null) will be set to default * - * White-space is anything that isspace() thinks is a space. (Which in the - * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.) + * .config_write -- is set from parameter * - * Returns: NULL => empty line (after white-space trimming) or comment line. - * otherwise: is vector containing one or more tokens. + * .cmd_vector -- initialised empty + * + * Default parent node: + * + * * all nodes > NODE_CONFIG have NODE_CONFIG as parent + * * node == NODE_CONFIG has ENABLE_NODE as parent + * * all nodes < NODE_CONFIG are their own parents (including ENABLE_NODE) * - * .... + * Default exit_to: * + * * all nodes > NODE_CONFIG exit_to their parent + * * node == NODE_CONFIG exit_to ENABLE_NODE (its parent) + * * all nodes < NODE_CONFIG exit_to close * - * Note: all the tokens in the vector have at least one character, and no - * entries are NULL. + * Default end_to: + * + * * all nodes >= NODE_CONFIG end_to ENABLE_NODE + * * all nodes < NODE_CONFIG end_to themselves * - * 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) +extern void +install_node (struct cmd_node *node, + int (*config_write) (struct vty *)) { - char *token, *tp ; - const char *cp, *sp, *ep, *op ; + confirm(NULL_NODE == 0) ; /* unset value for .parent, .end_to & .exit_to */ - /* Reset any existing vline, and empty the qstring if given. */ - if (vline != NULL) - vector_set_length(vline, 0) ; + if (node->parent == NULL_NODE) + { + if (node->node > CONFIG_NODE) + node->parent = CONFIG_NODE ; + else if (node->node == CONFIG_NODE) + node->parent = ENABLE_NODE ; + else + node->parent = node->node ; + } ; - qs_clear(qs) ; + if (node->end_to == NULL_NODE) + { + if (node->node >= CONFIG_NODE) + node->end_to = ENABLE_NODE ; + else + node->end_to = node->node ; + } ; - /* Strip leading and trailing white-space and deal with empty or effectively - * empty lines -- comment lines are treated as effectively empty. - */ - cp = string; + if (node->exit_to == NULL_NODE) + { + if (node->node >= CONFIG_NODE) + node->exit_to = node->parent ; + else + node->exit_to = NULL_NODE ; + } ; - if (string == NULL) - return NULL; + node->config_write = config_write ; - while (isspace((int) *cp)) - cp++; + vector_init_new(node->cmd_vector, 0) ; /* embedded */ - ep = cp + strlen(cp) ; + vector_set_index (node_vector, node->node, node); +} ; - while ((ep > cp) && (isspace((int)*(ep - 1)))) - --ep ; +/*------------------------------------------------------------------------------ + * Return address of cmd_node -- asserts is not NULL ! + */ +static cmd_node +cmd_cmd_node(node_type_t node) +{ + cmd_node cn ; - if ((cp == ep) || (*cp == '!') || (*cp == '#')) - return NULL; + cn = vector_get_item(node_vector, node) ; + if (cn != NULL) + return cn ; - /* Prepare return vector -- expect some reasonable number of tokens. */ - if (vline == NULL) - vline = vector_init(10) ; + zabort("invalid node") ; +} ; - /* 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) - { - qs_set_n(qs, cp, ep - cp) ; - tp = (char*)qs->body ; /* start at the beginning */ - } - else - tp = NULL ; /* not used, but not undefined */ +/*------------------------------------------------------------------------------ + * Return parent node + */ +extern node_type_t +cmd_node_parent(node_type_t node) +{ + return (cmd_cmd_node(node))->parent ; +} ; - op = cp ; /* original start position */ +/*------------------------------------------------------------------------------ + * Return exit_to node + */ +extern node_type_t +cmd_node_exit_to(node_type_t node) +{ + return (cmd_cmd_node(node))->exit_to ; +} ; - /* 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) +/*------------------------------------------------------------------------------ + * Return end_to node + */ +extern node_type_t +cmd_node_end_to(node_type_t node) +{ + return (cmd_cmd_node(node))->end_to ; +} ; + +/*------------------------------------------------------------------------------ + * Sorting of all node cmd_vectors. + */ + +/* Compare two command's string. Used in sort_node (). */ +static int +cmp_node (const struct cmd_command **a, const struct cmd_command **b) +{ + return strcmp ((*a)->string, (*b)->string); +} + +/* Sort each node's command element according to command string. */ +extern void +sort_node () +{ + unsigned int i ; + + for (i = 0; i < vector_length(node_vector); i++) { - while (isspace((int) *cp)) - cp++ ; /* skip white-space */ + struct cmd_node *cn; + vector cmd_vector ; - sp = cp ; - while ((cp < ep) && !isspace((int) *cp)) - cp++ ; /* eat token characters */ + cn = vector_get_item(node_vector, i) ; - if (qs == NULL) - { - /* creating array of MTYPE_STRVEC */ - size_t len ; + if (cn == NULL) + continue ; - 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 */ - } ; + cmd_vector = cn->cmd_vector; + if (cmd_vector == NULL) + continue ; - vector_push_item(vline, token); + vector_sort(cmd_vector, (vector_sort_cmp*)cmp_node) ; } ; +} ; - return vline ; -} - +#if 0 /*------------------------------------------------------------------------------ - * 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'.) + * Take string and break it into tokens -- see cmd_make_tokens(). * * 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(). + * otherwise: is vector containing one or more tokens in qstrings. */ extern vector cmd_make_strvec (const char *string) { - return cmd_make_vline(NULL, NULL, string) ; +#if 0 + return cmd_tokenize(NULL, string) ; +#error sort this one out +#endif + return NULL ; } ; /*------------------------------------------------------------------------------ @@ -431,2722 +627,569 @@ cmd_make_strvec (const char *string) * Create vector if required. */ extern vector -cmd_add_to_strvec (vector strvec, const char* str) +cmd_add_to_strvec (vector items, const char* str) { - if (strvec == NULL) - strvec = vector_init(1) ; + if (items == NULL) + items = vector_init(1) ; - vector_push_item(strvec, XSTRDUP(MTYPE_STRVEC, str)); + vector_push_item(items, XSTRDUP(MTYPE_STRVEC, str)); - return strvec ; + return items ; } ; /*------------------------------------------------------------------------------ * Free allocated string vector (if any) and all its contents. * - * Note that this is perfectly happy with strvec == NULL. + * Note that this is perfectly happy with items == NULL. */ extern void -cmd_free_strvec (vector strvec) +cmd_free_strvec (vector items) { char *cp; - /* Note that vector_ream_free() returns NULL if strvec == NULL */ - while((cp = vector_ream_free(strvec)) != NULL) + /* Note that vector_ream_free() returns NULL if items == NULL */ + while((cp = vector_ream(items, free_it)) != NULL) XFREE (MTYPE_STRVEC, cp); } ; -/*----------------------------------------------------------------------------*/ - -/* Fetch next description. Used in cmd_make_descvec(). */ -static char * -cmd_desc_str (const char **string) -{ - const char *cp, *start; - char *token; - int strlen; - - cp = *string; - - if (cp == NULL) - return NULL; - - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') - cp++; - - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; - - start = cp; - - while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') - cp++; - - strlen = cp - start; - token = XMALLOC (MTYPE_STRVEC, strlen + 1); - memcpy (token, start, strlen); - *(token + strlen) = '\0'; - - *string = cp; - - return token; -} - -/* New string vector. */ -static vector -cmd_make_descvec (const char *string, const char *descstr) -{ - int multiple = 0; - const char *sp; - char *token; - int len; - const char *cp; - const char *dp; - vector allvec; - vector strvec = NULL; - struct desc *desc; - - cp = string; - dp = descstr; - - if (cp == NULL) - return NULL; - - allvec = vector_init (0); - - while (1) - { - while (isspace ((int) *cp) && *cp != '\0') - cp++; - - if (*cp == '(') - { - multiple = 1; - cp++; - } - if (*cp == ')') - { - multiple = 0; - cp++; - } - if (*cp == '|') - { - if (! multiple) - { - fprintf (stderr, "Command parse error!: %s\n", string); - exit (1); - } - cp++; - } - - while (isspace ((int) *cp) && *cp != '\0') - cp++; - - if (*cp == '(') - { - multiple = 1; - cp++; - } - - if (*cp == '\0') - return allvec; - - sp = cp; - - while (! (isspace ((int) *cp) || *cp == '\r' || *cp == '\n' || *cp == ')' || *cp == '|') && *cp != '\0') - cp++; - - len = cp - sp; - - token = XMALLOC (MTYPE_STRVEC, len + 1); - memcpy (token, sp, len); - *(token + len) = '\0'; - - desc = XCALLOC (MTYPE_DESC, sizeof (struct desc)); - desc->cmd = token; - desc->str = cmd_desc_str (&dp); - - if (multiple) - { - if (multiple == 1) - { - strvec = vector_init (0); - vector_set (allvec, strvec); - } - multiple++; - } - else - { - strvec = vector_init (0); - vector_set (allvec, strvec); - } - vector_set (strvec, desc); - } -} - -/* Count mandantory string vector size. This is to determine inputed - command has enough command length. */ -static int -cmd_cmdsize (vector strvec) -{ - unsigned int i; - int size = 0; - - for (i = 0; i < vector_length(strvec); i++) - { - 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; -} +#endif -/* Return prompt character of specified node. */ -const char * -cmd_prompt (enum node_type node) +/*------------------------------------------------------------------------------ + * Return prompt string for the specified node. + */ +extern const char * +cmd_prompt(node_type_t node) { - struct cmd_node *cnode; + struct cmd_node *cn ; - assert(cmdvec != NULL) ; - assert(cmdvec->p_items != NULL) ; + assert(node_vector != NULL) ; + assert(node_vector->p_items != NULL) ; - cnode = NULL ; - if (node < cmdvec->limit) - cnode = vector_get_item (cmdvec, node); + cn = NULL ; + if (node < node_vector->limit) + cn = vector_get_item (node_vector, node); - if (cnode == NULL) + if (cn == NULL) { zlog_err("Could not find prompt for node %d for", node) ; return NULL ; } ; - return cnode->prompt; + return cn->prompt; } -/* Install a command into a node. */ -void -install_element (enum node_type ntype, struct cmd_element *cmd) +/*------------------------------------------------------------------------------ + * Install a command into a node. + * + */ +extern void +install_element(node_type_t ntype, cmd_command cmd) { - struct cmd_node *cnode; - - /* cmd_init hasn't been called */ - if (!cmdvec) - return; + cmd_node cn ; - cnode = vector_get_item (cmdvec, ntype); + cn = vector_get_item (node_vector, ntype); - if (cnode == NULL) + if (cn == NULL) { fprintf (stderr, "Command node %d doesn't exist, please check it\n", ntype); exit (1); } - vector_set (cnode->cmd_vector, cmd); + vector_set (cn->cmd_vector, cmd); - if (cmd->strvec == NULL) - cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc); + /* A cmd_command may appear in a number of cmd_vectors, but the cmd->items + * etc. need only be set up once. + * + * It is assumed that once a cmd_command has been installed it will never be + * changed ! + * + * Need now to "compile" the command if not already compiled. + */ + if (cmd->items == NULL) + cmd_compile(cmd); - cmd->cmdsize = cmd_cmdsize (cmd->strvec); -} + /* Post compilation check for reasonable cmd_command ! */ + cmd_compile_check(cmd) ; +} ; +/*============================================================================== + * Password encryption + */ static const unsigned char itoa64[] = -"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -static void -to64(char *s, long v, int n) -{ - while (--n >= 0) - { - *s++ = itoa64[v&0x3f]; - v >>= 6; - } -} + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; -static char * +/* Uses the usual crypt() function. + * + * Note that crypt() is not thread safe ! + */ +static const char * zencrypt (const char *passwd) { - char salt[6]; - struct timeval tv; - char *crypt (const char *, const char *); + uint32_t r ; + char salt[3]; + + extern char *crypt (const char *, const char *) ; - gettimeofday(&tv,0); + r = qt_random(*passwd) ; - to64(&salt[0], random(), 3); - to64(&salt[3], tv.tv_usec, 3); - salt[5] = '\0'; + salt[0] = itoa64[(r >> (32 - 5)) & 0x3F] ; /* ms 5 */ + salt[1] = itoa64[(r >> (32 - 10)) & 0x3F] ; /* next ms 5 */ + salt[2] = '\0'; - return crypt (passwd, salt); + return crypt(passwd, salt) ; } /* This function write configuration of this host. */ static int -config_write_host (struct vty *vty) +config_write_host (vty vty) { + vty_io vio ; + + VTY_LOCK() ; + + vio = vty->vio ; + if (qpthreads_enabled) - vty_out (vty, "threaded%s", VTY_NEWLINE); + uty_out (vio, "threaded\n"); - if (host.name) - vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE); + if (host.name_set) + uty_out (vio, "hostname %s\n", host.name); - if (host.encrypt) + if (host.password != NULL) { - if (host.password_encrypt) - 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); - } - else + if (host.password_encrypted) + uty_out (vio, "password 8 %s\n", host.password); + else + uty_out (vio, "password %s\n", host.password); + } ; + + if (host.enable != NULL) { - if (host.password) - vty_out (vty, "password %s%s", host.password, VTY_NEWLINE); - if (host.enable) - vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE); - } + if (host.enable_encrypted) + uty_out (vio, "enable password 8 %s\n", host.enable); + else + uty_out (vio, "enable password %s\n", host.enable); + } ; 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_get_default_lvl(NULL)], VTY_NEWLINE); + uty_out (vio, "! N.B. The 'log trap' command is deprecated.\n"); + uty_out (vio, "log trap %s\n", zlog_priority[zlog_get_default_lvl(NULL)]); } if (host.logfile && (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != ZLOG_DISABLED)) { - vty_out (vty, "log file %s", host.logfile); + uty_out (vio, "log file %s", qpath_string(host.logfile)); if (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != zlog_get_default_lvl(NULL)) - vty_out (vty, " %s", + uty_out (vio, " %s", zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)]); - vty_out (vty, "%s", VTY_NEWLINE); + uty_out (vio, "\n"); } if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != ZLOG_DISABLED) { - vty_out (vty, "log stdout"); + uty_out (vio, "log stdout"); if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != zlog_get_default_lvl(NULL)) - vty_out (vty, " %s", + uty_out (vio, " %s", zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)]); - vty_out (vty, "%s", VTY_NEWLINE); + uty_out (vio, "\n"); } if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) == ZLOG_DISABLED) - vty_out(vty,"no log monitor%s",VTY_NEWLINE); + uty_out(vio,"no log monitor%s",VTY_NEWLINE); else if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) != zlog_get_default_lvl(NULL)) - vty_out(vty,"log monitor %s%s", - zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)],VTY_NEWLINE); + uty_out(vio,"log monitor %s\n", + zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)]); if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != ZLOG_DISABLED) { - vty_out (vty, "log syslog"); + uty_out (vio, "log syslog"); if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != zlog_get_default_lvl(NULL)) - vty_out (vty, " %s", + uty_out (vio, " %s", zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG)]); - vty_out (vty, "%s", VTY_NEWLINE); + uty_out (vio, "\n"); } if (zlog_get_facility(NULL) != LOG_DAEMON) - vty_out (vty, "log facility %s%s", - facility_name(zlog_get_facility(NULL)), VTY_NEWLINE); + uty_out (vio, "log facility %s\n", facility_name(zlog_get_facility(NULL))); if (zlog_get_record_priority(NULL) == 1) - vty_out (vty, "log record-priority%s", VTY_NEWLINE); + uty_out (vio, "log record-priority\n"); if (zlog_get_timestamp_precision(NULL) > 0) - vty_out (vty, "log timestamp precision %d%s", - zlog_get_timestamp_precision(NULL), VTY_NEWLINE); + uty_out (vio, "log timestamp precision %d\n", + zlog_get_timestamp_precision(NULL)); if (host.advanced) - vty_out (vty, "service advanced-vty%s", VTY_NEWLINE); + uty_out (vio, "service advanced-vty\n"); if (host.encrypt) - vty_out (vty, "service password-encryption%s", VTY_NEWLINE); + uty_out (vio, "service password-encryption\n"); if (host.lines >= 0) - vty_out (vty, "service terminal-length %d%s", host.lines, - VTY_NEWLINE); + uty_out (vio, "service terminal-length %d\n", host.lines); - if (host.motdfile) - vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE); + if (host.motdfile) + uty_out (vio, "banner motd file %s\n", qpath_string(host.motdfile)); else if (! host.motd) - vty_out (vty, "no banner motd%s", VTY_NEWLINE); + uty_out (vio, "no banner motd\n"); - return 1; -} - -/* Utility function for getting command vector. */ -static vector -cmd_node_vector (vector v, enum node_type ntype) -{ - struct cmd_node *cnode = vector_get_item (v, ntype); - return cnode->cmd_vector; -} - -#if 0 -/* Filter command vector by symbol. This function is not actually used; - * should it be deleted? */ -static int -cmd_filter_by_symbol (char *command, char *symbol) -{ - int i, lim; + VTY_UNLOCK() ; - if (strcmp (symbol, "IPV4_ADDRESS") == 0) - { - i = 0; - lim = strlen (command); - while (i < lim) - { - if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/')) - return 1; - i++; - } - return 0; - } - if (strcmp (symbol, "STRING") == 0) - { - i = 0; - lim = strlen (command); - while (i < lim) - { - if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-')) - return 1; - i++; - } - return 0; - } - if (strcmp (symbol, "IFNAME") == 0) - { - i = 0; - lim = strlen (command); - while (i < lim) - { - if (! isalnum ((int) command[i])) - return 1; - i++; - } - return 0; - } - return 0; + return 1; } -#endif /*============================================================================== - * Match functions. + * Commands and other stuff related to: * - * Is the given string a, possibly incomplete, value of the required kind ? - */ - -/* Completion match types. */ -enum match_type -{ - no_match, /* nope */ - extend_match, - - ipv4_prefix_match, - ipv4_match, - ipv6_prefix_match, - ipv6_match, - range_match, - vararg_match, - - partly_match, /* OK as far as it went */ - exact_match /* Syntactically complete */ -}; - -/*------------------------------------------------------------------------------ - * Is this an IPv4 Address: + * * end (and ^Z) -- go to ENABLE_NODE (aka Privileged Exec) if above that, + * otherwise do nothing. * - * 999.999.999.999 -- where no part may be > 255 + * This is installed in all nodes. * - * TODO: cmd_ipv4_match() seems to accept leading '.' ? - * TODO: cmd_ipv4_match() seems to accept leading zeros ? + * * exit -- go to parent node, if in CONFIG_NODE or any of its + * sub-nodes. * - * Returns: no_match -- improperly formed - * partly_match -- accepts empty string - * exact_match -- syntactically complete - */ -static enum match_type -cmd_ipv4_match (const char *str) -{ - const char *sp; - int dots = 0, nums = 0; - char buf[4]; - - if (str == NULL) - return partly_match; - - for (;;) - { - memset (buf, 0, sizeof (buf)); - sp = str; - while (*str != '\0') - { - if (*str == '.') - { - if (dots >= 3) - return no_match; - - if (*(str + 1) == '.') - return no_match; - - if (*(str + 1) == '\0') - return partly_match; - - dots++; - break; - } - if (!isdigit ((int) *str)) - return no_match; - - str++; - } - - if (str - sp > 3) - return no_match; - - strncpy (buf, sp, str - sp); - if (atoi (buf) > 255) - return no_match; - - nums++; - - if (*str == '\0') - break; - - str++; - } - - if (nums < 4) - return partly_match; - - return exact_match; -} - -/*------------------------------------------------------------------------------ - * Is this an IPv4 Prefix: + * Parent of CONFIG_NODE is ENABLE_NODE. * - * 999.999.999.999/99 -- where no part may be > 255, - * and prefix length may not be > 32 + * For all other nodes, this is EOF (which for the + * terminal is "close"). * - * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ? - * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ? + * This is installed in all nodes. * - * Returns: no_match -- improperly formed - * partly_match -- accepts empty string - * exact_match -- syntactically complete + * * enable -- go to ENABLE_NODE, if can. * - * 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) -{ - const char *sp; - int dots = 0; - char buf[4]; - - if (str == NULL) - return partly_match; - - for (;;) - { - memset (buf, 0, sizeof (buf)); - sp = str; - while (*str != '\0' && *str != '/') - { - if (*str == '.') - { - if (dots == 3) - return no_match; - - if (*(str + 1) == '.' || *(str + 1) == '/') - return no_match; - - if (*(str + 1) == '\0') - return partly_match; - - dots++; - break; - } - - if (!isdigit ((int) *str)) - return no_match; - - str++; - } - - if (str - sp > 3) - return no_match; - - strncpy (buf, sp, str - sp); - if (atoi (buf) > 255) - return no_match; - - if (dots == 3) - { - if (*str == '/') - { - if (*(str + 1) == '\0') - return partly_match; - - str++; - break; - } - else if (*str == '\0') - return partly_match; - } - - if (*str == '\0') - return partly_match; - - str++; - } - - sp = str; - while (*str != '\0') - { - if (!isdigit ((int) *str)) - return no_match; - - str++; - } - - if (atoi (sp) > 32) - return no_match; - - return exact_match; -} - -/*------------------------------------------------------------------------------ - * Is this an IPv6 Address: + * This is installed in VIEW_NODE and RESTRICTED_NODE. * - * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ? + * It is also installed in ENABLE_NODE (and hence is + * available anywhere), where it is a synonym for 'end' ! * - * 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 -#define STATE_COLON 2 -#define STATE_DOUBLE 3 -#define STATE_ADDR 4 -#define STATE_DOT 5 -#define STATE_SLASH 6 -#define STATE_MASK 7 - -#ifdef HAVE_IPV6 - -static enum match_type -cmd_ipv6_match (const char *str) -{ - int state = STATE_START; - int colons = 0, nums = 0, double_colon = 0; - const char *sp = NULL; - struct sockaddr_in6 sin6_dummy; - int ret; - - if (str == NULL) - return partly_match; - - if (strspn (str, IPV6_ADDR_STR) != strlen (str)) - return no_match; - - /* use inet_pton that has a better support, - * for example inet_pton can support the automatic addresses: - * ::1.2.3.4 - */ - ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); - - if (ret == 1) - return exact_match; - - while (*str != '\0') - { - switch (state) - { - case STATE_START: - if (*str == ':') - { - if (*(str + 1) != ':' && *(str + 1) != '\0') - return no_match; - colons--; - state = STATE_COLON; - } - else - { - sp = str; - state = STATE_ADDR; - } - - continue; - case STATE_COLON: - colons++; - if (*(str + 1) == ':') - state = STATE_DOUBLE; - else - { - sp = str + 1; - state = STATE_ADDR; - } - break; - case STATE_DOUBLE: - if (double_colon) - return no_match; - - if (*(str + 1) == ':') - return no_match; - else - { - if (*(str + 1) != '\0') - colons++; - sp = str + 1; - state = STATE_ADDR; - } - - double_colon++; - nums++; - break; - case STATE_ADDR: - if (*(str + 1) == ':' || *(str + 1) == '\0') - { - if (str - sp > 3) - return no_match; - - nums++; - state = STATE_COLON; - } - if (*(str + 1) == '.') - state = STATE_DOT; - break; - case STATE_DOT: - state = STATE_ADDR; - break; - default: - break; - } - - if (nums > 8) - return no_match; - - if (colons > 7) - return no_match; - - str++; - } - -#if 0 - if (nums < 11) - return partly_match; -#endif /* 0 */ - - return exact_match; -} - -/*------------------------------------------------------------------------------ - * Is this an IPv6 Prefix: + * For configuration reading (VTY_CONFIG_READ) and for + * VTY_SHELL_SERVER, no password is required. * - * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ? - * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ? + * For VTY_TERMINAL, must already have authenticated + * once, or must be able to enter AUTH_ENABLE_NODE. * - * 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) -{ - int state = STATE_START; - int colons = 0, nums = 0, double_colon = 0; - int mask; - const char *sp = NULL; - char *endptr = NULL; - - if (str == NULL) - return partly_match; - - if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) - return no_match; - - while (*str != '\0' && state != STATE_MASK) - { - switch (state) - { - case STATE_START: - if (*str == ':') - { - if (*(str + 1) != ':' && *(str + 1) != '\0') - return no_match; - colons--; - state = STATE_COLON; - } - else - { - sp = str; - state = STATE_ADDR; - } - - continue; - case STATE_COLON: - colons++; - if (*(str + 1) == '/') - return no_match; - else if (*(str + 1) == ':') - state = STATE_DOUBLE; - else - { - sp = str + 1; - state = STATE_ADDR; - } - break; - case STATE_DOUBLE: - if (double_colon) - return no_match; - - if (*(str + 1) == ':') - return no_match; - else - { - if (*(str + 1) != '\0' && *(str + 1) != '/') - colons++; - sp = str + 1; - - if (*(str + 1) == '/') - state = STATE_SLASH; - else - state = STATE_ADDR; - } - - double_colon++; - nums += 1; - break; - case STATE_ADDR: - if (*(str + 1) == ':' || *(str + 1) == '.' - || *(str + 1) == '\0' || *(str + 1) == '/') - { - if (str - sp > 3) - return no_match; - - for (; sp <= str; sp++) - if (*sp == '/') - return no_match; - - nums++; - - if (*(str + 1) == ':') - state = STATE_COLON; - else if (*(str + 1) == '.') - state = STATE_DOT; - else if (*(str + 1) == '/') - state = STATE_SLASH; - } - break; - case STATE_DOT: - state = STATE_ADDR; - break; - case STATE_SLASH: - if (*(str + 1) == '\0') - return partly_match; - - state = STATE_MASK; - break; - default: - break; - } - - if (nums > 11) - return no_match; - - if (colons > 7) - return no_match; - - str++; - } - - if (state < STATE_MASK) - return partly_match; - - mask = strtol (str, &endptr, 10); - if (*endptr != '\0') - return no_match; - - 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 - yasu - if (mask < 13) - return partly_match; -*/ - - return exact_match; -} - -#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 -cmd_range_match (const char *range, const char *str) -{ - char *p; - char buf[DECIMAL_STRLEN_MAX + 1]; - char *endptr = NULL; - unsigned long min, max, val; - - if (str == NULL) - return 1; - - val = strtoul (str, &endptr, 10); - if (*endptr != '\0') - return 0; - - range++; - p = strchr (range, '-'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy (buf, range, p - range); - buf[p - range] = '\0'; - min = strtoul (buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - range = p + 1; - p = strchr (range, '>'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy (buf, range, p - range); - buf[p - range] = '\0'; - max = strtoul (buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - if (val < min || val > max) - return 0; - - return 1; -} - -/*============================================================================== - * 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. + * * disable -- go to VIEW_NODE (aka User Exec). * + * This is installed in ENABLE_NODE *only*. * + * Note, however, that all ENABLE_NODE commands are + * available at ENABLE_NODE and above ! */ /*------------------------------------------------------------------------------ - * 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: + * Enter CONFIG_NODE, possibly via password check. * - * no_match => no match of any kind + * If the parser established that can enter CONFIG_NODE directly, that's what + * happens. * - * 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 ) + * If the parser established that must authenticate, then may fail here if + * we are not in the right state to run the authentication. * - * partly_match => saw partial match for a keyword - * exact_match => saw exact match for a keyword + * The authentication itself may fail... * - * 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. + * NB: installed in VIEW_NODE, RESTRICTED_NODE and ENABLE_NODE. */ -static enum match_type -cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index) -{ - unsigned int i; - unsigned int k; - enum match_type match_type; - - match_type = no_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++; - } - } -#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++; - } - } -#endif /* HAVE_IPV6 */ - 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++; - } ; - } ; - - /* Keep cmd_v entry that has a match at this position */ - if (matched) - vector_set_item(cmd_v, k++, cmd_element) ; - } ; - - vector_set_length(cmd_v, k) ; /* discard what did not keep */ - - return match_type; +DEFUN_ATTR (config_terminal, + config_terminal_cmd, + "configure terminal", + "Configuration from vty interface\n" + "Configuration terminal\n", + CMD_ATTR_DIRECT + cmd_sp_configure) +{ + if (vty->exec->parsed->nnode == CONFIG_NODE) + return vty_cmd_config_lock(vty) ; ; + + /* Otherwise, must authenticate to enter CONFIG_NODE. */ + return vty_cmd_can_auth_enable(vty) ; } ; -/*------------------------------------------------------------------------------ - * 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 cmd_v, unsigned int index) -{ - unsigned int i ; - unsigned int k ; - enum match_type match_type; - - match_type = no_match; - - /* 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++; - } ; - } ; - } ; - - /* 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 match_type; -} +ALIAS_ATTR (config_terminal, + config_enable_configure_cmd, + "enable configure", + "Turn on privileged mode command\n" + "Configuration terminal\n", + CMD_ATTR_DIRECT + cmd_sp_configure) /*------------------------------------------------------------------------------ - * 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. + * Enter ENABLE_NODE, possibly via password check. * - * See above for the ranking of matches. + * If the parser established that can enter ENABLE_NODE directly, that's what + * happens. * - * 2. for "partial match", look out for matching more than one keyword, and - * return 1 if finds that. + * If the parser established that must authenticate, then may fail here if + * we are not in the right state to run the authentication. * - * 3. for "range match", look out for matching more than one range, and - * return 1 if finds that. + * The authentication itself may fail... * - * 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. + * NB: installed in VIEW_NODE, RESTRICTED_NODE and ENABLE_NODE. */ -static int -is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type) +DEFUN_ATTR (enable, + config_enable_cmd, + "enable", + "Turn on privileged mode command\n", + CMD_ATTR_DIRECT + cmd_sp_enable) { - unsigned int i; - unsigned int k; - int ret ; - - 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 ; - - desc = vector_get_item (descvec, j) ; - if (desc == NULL) - continue ; - - 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; - -#ifdef HAVE_IPV6 - case ipv6_match: - if (CMD_IPV6 (str)) - matched++; - 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)) - matched++; - break; - - 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. */ + if (vty->exec->parsed->nnode == ENABLE_NODE) + return CMD_SUCCESS ; - 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 ; + /* Otherwise, must authenticate to enter ENABLE_NODE. */ + return vty_cmd_can_auth_enable(vty) ; } ; /*------------------------------------------------------------------------------ - * If src matches dst return dst string, otherwise return NULL - * - * Returns NULL if dst is an option, variable of vararg. + * disable command: end enabled state -> VIEW_NODE. * - * NULL or empty src are deemed to match. + * NB: although only installed in ENABLE_NODE, it will be implicitly available + * in all higher nodes -- as a quick way of crashing out to VIEW_NODE ! */ -static const char * -cmd_entry_function (const char *src, const char *dst) +DEFUN_ATTR(disable, + config_disable_cmd, + "disable", + "Turn off privileged mode command\n", + CMD_ATTR_DIRECT + CMD_ATTR_NODE + VIEW_NODE) { - if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst)) - return NULL; - - if ((src == NULL) || (*src == '\0')) - return dst; - - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - - return NULL; -} - -/* If src matches dst return dst string, otherwise return NULL */ -/* This version will return the dst string always if it is - CMD_VARIABLE for '?' key processing */ -static const char * -cmd_entry_function_desc (const char *src, const char *dst) -{ - if (CMD_VARARG (dst)) - return dst; - - if (CMD_RANGE (dst)) - { - if (cmd_range_match (dst, src)) - return dst; - else - return NULL; - } - -#ifdef HAVE_IPV6 - if (CMD_IPV6 (dst)) - { - if (cmd_ipv6_match (src)) - return dst; - else - return NULL; - } - - if (CMD_IPV6_PREFIX (dst)) - { - if (cmd_ipv6_prefix_match (src)) - return dst; - else - return NULL; - } -#endif /* HAVE_IPV6 */ - - if (CMD_IPV4 (dst)) - { - if (cmd_ipv4_match (src)) - return dst; - else - return NULL; - } - - if (CMD_IPV4_PREFIX (dst)) - { - if (cmd_ipv4_prefix_match (src)) - return dst; - else - return NULL; - } - - /* Optional or variable commands always match on '?' */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst)) - return dst; - - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - else - return NULL; + return CMD_SUCCESS ; /* will disable to parsed->nnode */ } /*------------------------------------------------------------------------------ - * Check same string element existence. - * - * Returns: 0 => found same string in the vector - * 1 => NOT found same string in the vector + * exit command: down one node level, including exit command processor. */ -static bool -cmd_unique_string (vector v, const char *str) -{ - unsigned int i; - char *match; - - 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; -} - -/* Compare string to description vector. If there is same string - return 1 else return 0. */ -static bool -desc_unique_string (vector v, const char *str) +DEFUN_ATTR(config_exit, + config_exit_cmd, + "exit", + "Exit current mode and down to previous mode\n", + CMD_ATTR_DIRECT + cmd_sp_exit) { - unsigned int i; - struct desc *desc; - - 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 bool -cmd_try_do_shortcut (enum node_type node, char* first_word) { - return (node >= MIN_DO_SHORTCUT_NODE) - && (first_word != NULL) - && (strcmp( "do", first_word) == 0) ? 1 : 0 ; + return CMD_SUCCESS ; /* will exit to parsed->nnode */ } -/* '?' describe command support. */ -static vector -cmd_describe_command_real (vector vline, int node, int *status) -{ - unsigned int i; - vector cmd_vector; -#define INIT_MATCHVEC_SIZE 10 - vector matchvec; - struct cmd_element *cmd_element; - unsigned int index; - int ret; - enum match_type match; - char *command; - - /* Set index. */ - if (vector_length (vline) == 0) - { - *status = CMD_ERR_NO_MATCH; - return NULL; - } - else - index = vector_length (vline) - 1; - - /* Make copy vector of current node's command vector. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, node)); - - /* Prepare match vector */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - - /* Filter commands. */ - /* Only words precedes current word will be checked in this loop. */ - for (i = 0; i < index; 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_length (cmd_vector); j++) - if ((cmd_element = vector_get_item (cmd_vector, j)) != NULL - && (vector_length (cmd_element->strvec))) - { - 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_get_item (descvec, k); - vector_set (matchvec, desc); - } - } - - vector_set (matchvec, &desc_cr); - vector_free (cmd_vector); - - return matchvec; - } - - if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1) - { - vector_free (cmd_vector); - vector_free (matchvec); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - else if (ret == 2) - { - vector_free (cmd_vector); - vector_free (matchvec); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - } - - /* Prepare match vector */ - /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ - - /* 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_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); - } - } ; - } ; - - vector_free (cmd_vector); - - if (vector_length(matchvec) == 0) - { - vector_free (matchvec); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - - *status = CMD_SUCCESS; - return matchvec; -} +/* quit is alias of exit. */ +ALIAS_ATTR (config_exit, + config_quit_cmd, + "quit", + "Exit current mode and down to previous mode\n", + CMD_ATTR_DIRECT + cmd_sp_exit) ; /*------------------------------------------------------------------------------ - * 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.) + * end command: down to enable mode. */ -vector -cmd_describe_command (vector vline, int node, int *status) +DEFUN_ATTR (config_end, + config_end_cmd, + "end", + "End current mode and change to enable mode\n", + CMD_ATTR_DIRECT + cmd_sp_end) { - vector ret; - - if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) ) - { - vector shifted_vline; - unsigned int index; - - /* 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_length (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } - - ret = cmd_describe_command_real (shifted_vline, ENABLE_NODE, status); - - vector_free(shifted_vline); - return ret; - } - - return cmd_describe_command_real (vline, node, status); + return CMD_SUCCESS ; /* will end to parsed->nnode */ } -/*------------------------------------------------------------------------------ - * 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 (vector matchvec) -{ - int n ; - int i ; - int lcd ; - char *sp, *sq, *ss ; - - n = vector_end(matchvec) ; - if (n < 2) - return 0 ; - - ss = vector_get_item(matchvec, 0) ; - lcd = strlen(ss) ; - - for (i = 1 ; i < n ; i++) - { - sq = ss ; - ss = vector_get_item(matchvec, i) ; - sp = ss ; - - while ((*sp == *sq) && (*sp != '\0')) - { - ++sp ; - ++sq ; - } ; - - if (lcd > (sp - ss)) - lcd = (sp - ss) ; - } - return lcd; -} - -/*------------------------------------------------------------------------------ - * Command line completion support. - */ -static vector -cmd_complete_command_real (vector vline, int node, int *status) -{ - unsigned int i; - 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; - struct desc *desc; - vector descvec; - char *token; - int n ; - - /* Stop immediately if the vline is empty. */ - if (vector_length (vline) == 0) - { - *status = CMD_ERR_NO_MATCH; - return NULL; - } - - /* Take (shallow) copy of cmdvec for given node. */ - cmd_v = vector_copy (cmd_node_vector (cmdvec, node)); - - /* First, filter upto, but excluding last token */ - last_ivl = vector_length (vline) - 1; - - 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. */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - - /* Now we got into completion */ - index = last_ivl ; - token = vector_get_item(vline, last_ivl) ; /* is now the last token */ - - for (i = 0; i < vector_length (cmd_v); i++) - { - unsigned int j; - const char *string; - - if ((cmd_element = vector_get_item (cmd_v, i)) == NULL) - continue ; - - 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 (n == 0) - { - vector_free (matchvec); - - /* In case of 'command \t' pattern. Do you need '?' command at - the end of the line. */ - if (*token == '\0') - *status = CMD_COMPLETE_ALREADY; - else - *status = CMD_ERR_NO_MATCH; - return NULL; - } - - /* Only one matched */ - if (n == 1) - { - *status = CMD_COMPLETE_FULL_MATCH; - return matchvec ; - } - - /* Check LCD of matched strings. */ - if (token != NULL) - { - unsigned lcd = cmd_lcd (matchvec) ; - - if (lcd != 0) - { - if (strlen(token) < lcd) - { - char *lcdstr; - - lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1); - memcpy (lcdstr, vector_get_item(matchvec, 0), lcd) ; - lcdstr[lcd] = '\0'; - - cmd_free_strvec(matchvec) ; /* discard the match vector */ - - matchvec = vector_init (1); - vector_push_item(matchvec, lcdstr) ; - - *status = CMD_COMPLETE_MATCH; - return matchvec ; - } - } - } - - *status = CMD_COMPLETE_LIST_MATCH; - return matchvec ; -} - -/*------------------------------------------------------------------------------ - * Can the current command be completed ? - */ -extern vector -cmd_complete_command (vector vline, int node, int *status) -{ - vector ret; - - if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) ) - { - vector shifted_vline; - unsigned int index; - - /* 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_length (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } - - ret = cmd_complete_command_real (shifted_vline, ENABLE_NODE, status); - - vector_free(shifted_vline); - return ret; - } - - return cmd_complete_command_real (vline, node, status); -} - -/*------------------------------------------------------------------------------ - * Return parent node - * - * All nodes > CONFIG_NODE are descended from CONFIG_NODE - */ -enum node_type -node_parent ( enum node_type node ) -{ - assert (node > CONFIG_NODE); - - switch (node) - { - case BGP_VPNV4_NODE: - case BGP_IPV4_NODE: - case BGP_IPV4M_NODE: - case BGP_IPV6_NODE: - case BGP_IPV6M_NODE: - return BGP_NODE; - - case KEYCHAIN_KEY_NODE: - return KEYCHAIN_NODE; - - default: - return CONFIG_NODE; - } -} - -/*------------------------------------------------------------------------------ - * Initialise a new struct cmd_parsed, allocating if required - */ -extern cmd_parsed -cmd_parse_init_new(cmd_parsed parsed) -{ - 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 - */ - - return parsed ; -} ; - -/*------------------------------------------------------------------------------ - * 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) ; - - 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 ; - - /* Initialise the parsed structure -- assuming no 'do' */ - if (vty->parsed == NULL) - vty->parsed = cmd_parse_init_new(NULL) ; - parsed = vty->parsed ; - - parsed->onode = parsed->cnode = vty->node ; - - parsed->cmd = NULL ; - parsed->do_shortcut = 0 ; - - /* Parse the line into words -- set up parsed->words and parsed->vline */ - cmd_make_vline(&parsed->vline, &parsed->words, vty->buf) ; - - if (vector_length(&parsed->vline) == 0) - return CMD_EMPTY ; /* NB: parsed->cmd == NULL */ - - /* 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))) - { - parsed->cnode = ENABLE_NODE ; - parsed->do_shortcut = 1 ; - } ; - - /* Try in the current node */ - ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ; - - if (ret != CMD_SUCCESS) - { - 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' */ - - /* Try in parent node(s) */ - first_ret = ret ; - - 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; - - /* Need length of vline, discounting the first entry if required */ - first = parsed->do_shortcut ? 1 : 0 ; - - assert(vector_length(&parsed->vline) >= first) ; - ivl = vector_length(&parsed->vline) - first ; - - /* Make copy of command elements. */ - cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode)); - - /* Look for an unambiguous result */ - for (index = 0 ; index < ivl; index++) - { - int ret ; - - command = vector_get_item(&parsed->vline, index + first) ; - if (command == NULL) - continue ; - - 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_v, index, match); - - if (ret != 0) - { - 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; - incomplete_count = 0; - - 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++; - } - } ; - - /* Finished with cmd_v. */ - vector_free (cmd_v); - - /* 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 ; - - for (index = 0; index < ivl ; index++) - { - int take = varflag ; - - if (!varflag) - { - 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 ; - } - - if (take) - vector_assign_item(&parsed->vline, argc++, index + first) ; - } ; - - vector_set_length(&parsed->vline, argc) ; /* set to new length */ - - /* Everything checks out... ready to execute command */ - parsed->cmd = matched_element ; - - return CMD_SUCCESS ; -} ; - -/*------------------------------------------------------------------------------ - * 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 (parsed->cmd == NULL) - return CMD_SUCCESS ; /* NULL commands are easy */ - - vty->node = parsed->cnode ; - - if (no_queue || !vty_cli_nexus) - { - ret = cmd_dispatch_call(vty) ; - cmd_post_command(vty, ret) ; - } - else - { - /* Don't do it now, but send to bgp qpthread */ - if (parsed->cmd->attr & CMD_ATTR_CALL) - cq_enqueue(vty, vty_cli_nexus) ; - else - 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; - - vty->buf = buf->body ; - vty->lineno = 0 ; - - ret = CMD_SUCCESS ; /* in case file is empty */ - vty_out_clear(vty) ; - - while (fgets (buf->body, buf->size, fp)) - { - ++vty->lineno ; - - /* Execute configuration command : this is strict match */ - ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ; - - if (ret == CMD_EMPTY) - continue ; /* skip empty/comment */ - - if (ret != CMD_SUCCESS) - break ; /* stop on *any* parsing issue */ - - /* 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 ; - } - - /* Standard command handling */ - ret = cmd_dispatch(vty, cmd_no_queue) ; - - 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 */ - } ; - - vty_out_clear(vty) ; - } ; - - if (ret == CMD_EMPTY) - ret = CMD_SUCCESS ; /* OK if end on empty line */ - - return ret ; -} ; - -/*----------------------------------------------------------------------------*/ - -/* Configration from terminal */ -DEFUN_CALL (config_terminal, - config_terminal_cmd, - "configure terminal", - "Configuration from vty interface\n" - "Configuration terminal\n") -{ - 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_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_shell_serv(vty)) - vty_set_node(vty, ENABLE_NODE); - else - vty_set_node(vty, AUTH_ENABLE_NODE); - - return CMD_SUCCESS; -} - -/* Disable command */ -DEFUN_CALL (disable, - config_disable_cmd, - "disable", - "Turn off privileged mode command\n") -{ - if (vty_get_node(vty) == ENABLE_NODE) - vty_set_node(vty, VIEW_NODE); - return CMD_SUCCESS; -} - -/* Down vty node level. */ -DEFUN_CALL (config_exit, - config_exit_cmd, - "exit", - "Exit current mode and down to previous mode\n") +/* Show version. */ +DEFUN_CALL (show_version, + show_version_cmd, + "show version", + SHOW_STR + "Displays zebra version\n") { - return vty_cmd_exit(vty) ; -} + VTY_LOCK() ; -/* quit is alias of exit. */ -ALIAS_CALL (config_exit, - config_quit_cmd, - "quit", - "Exit current mode and down to previous mode\n") - -/* End of configuration. */ -DEFUN_CALL (config_end, - config_end_cmd, - "end", - "End current mode and change to enable mode.") -{ - return vty_cmd_end(vty) ; -} + uty_out (vty->vio, "Quagga %s (%s).\n", QUAGGA_VERSION, + (host.name != NULL) ? host.name : "") ; + uty_out (vty->vio, "%s\n", QUAGGA_COPYRIGHT); -/* Show version. */ -DEFUN_CALL (show_version, - show_version_cmd, - "show version", - SHOW_STR - "Displays zebra version\n") -{ - vty_out (vty, "Quagga %s (%s).%s", QUAGGA_VERSION, host.name?host.name:"", - VTY_NEWLINE); - vty_out (vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE); + VTY_UNLOCK() ; return CMD_SUCCESS; } -/* Help display function for all node. */ +/* Help display function for all node. */ DEFUN_CALL (config_help, config_help_cmd, "help", "Description of the interactive help system\n") { vty_out (vty, - "Quagga VTY provides advanced help feature. When you need help,%s\ -anytime at the command line please press '?'.%s\ -%s\ -If nothing matches, the help list will be empty and you must backup%s\ - until entering a '?' shows the available options.%s\ -Two styles of help are provided:%s\ -1. Full help is available when you are ready to enter a%s\ -command argument (e.g. 'show ?') and describes each possible%s\ -argument.%s\ -2. Partial help is provided when an abbreviated argument is entered%s\ - and you want to know what arguments match the input%s\ - (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + "Quagga VTY provides advanced help feature. When you need help,\n" + "anytime at the command line please press '?'.\n" + "\n" + "If nothing matches, the help list will be empty and you must backup\n" + "until entering a '?' shows the available options.\n" + "Two styles of help are provided:\n" + " 1. Full help is available when you are ready to enter a\n" + " command argument (e.g. 'show ?') and describes each possible\n" + " argument.\n" + " 2. Partial help is provided when an abbreviated argument is entered\n" + " and you want to know what arguments match the input\n" + " (e.g. 'show me?'.)\n" + "\n") ; return CMD_SUCCESS; } -/* Help display function for all node. */ +/* Help display function for all node. */ DEFUN_CALL (config_list, - config_list_cmd, - "list", - "Print command list\n") + config_list_cmd, + "list", + "Print command list\n") { unsigned int i; - struct cmd_node *cnode = vector_get_item (cmdvec, vty_get_node(vty)); - struct cmd_element *cmd; - - 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); + struct cmd_node *cn ; + struct cmd_command *cmd; + + cn = vector_get_item (node_vector, vty->node); + + for (i = 0; i < vector_length (cn->cmd_vector); i++) + if ( ((cmd = vector_get_item (cn->cmd_vector, i)) != NULL) + && ((cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)) == 0) ) + vty_out (vty, " %s\n", cmd->string); + return CMD_SUCCESS; } -/* Write current configuration into file. */ +/* Write current configuration into file. */ DEFUN (config_write_file, config_write_file_cmd, "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; - - /* Check and see if we are operating under vtysh configuration */ - if (host.config == NULL) + qpath path ; + qpath temp ; + qpath save ; + cmd_return_code_t ret, retw ; + unsigned int i ; + int fd, err ; + struct cmd_node *cn; + const char *config_name ; + const char *save_name ; + char* temp_name ; + + err = 0 ; /* so far, so good */ + + VTY_LOCK() ; + path = (host.config_file != NULL) ? qpath_dup(host.config_file) : NULL ; + VTY_UNLOCK() ; + + /* Check and see if we are operating under vtysh configuration */ + if (path == NULL) { - vty_out (vty, "Can't save to configuration file, using vtysh.%s", - VTY_NEWLINE); + vty_out (vty, "%% Cannot save to configuration file, using vtysh.\n"); return CMD_WARNING; } - /* Get filename. */ - config_file = host.config; - - config_file_sav = - XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1); - strcpy (config_file_sav, config_file); - strcat (config_file_sav, CONF_BACKUP_EXT); + /* Set up the file names. */ + config_name = qpath_string(path) ; + save = qpath_dup(path) ; + qpath_extend_str(save, CONF_BACKUP_EXT) ; + save_name = qpath_string(save) ; - config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8); - sprintf (config_file_tmp, "%s.XXXXXX", config_file); + temp = qpath_dup(path) ; + qpath_extend_str(temp, ".XXXXXX") ; + temp_name = qpath_char_string(temp) ; - /* Open file to configuration write. */ - fd = mkstemp (config_file_tmp); + /* Open file to configuration write. */ + fd = mkstemp (temp_name); if (fd < 0) { - vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp, - VTY_NEWLINE); + err = errno ; + vty_out (vty, "%% Can't open configuration file %s", temp_name) ; goto finished; } - /* Make vty for configuration file. */ - vty_open_config_write(vty, fd) ; + /* Make vty for configuration file. */ + vty_config_write_open(vty, fd) ; - /* Config file header print. */ vty_out (vty, "!\n! Zebra configuration saved from vty\n! "); vty_time_print (vty, 1); vty_out (vty, "!\n"); - for (i = 0; i < vector_length (cmdvec); i++) - if ((node = vector_get_item (cmdvec, i)) && node->func) - { - if ((*node->func) (vty)) - vty_out (vty, "!\n"); - } + retw = CMD_SUCCESS ; + + for (i = 0; i < vector_length (node_vector); i++) + { + if ((cn = vector_get_item (node_vector, i)) && cn->config_write) + { + if ((*cn->config_write) (vty)) + vty_out (vty, "!\n"); - err = vty_close_config_write(vty) ; - close(fd) ; + retw = vty_cmd_out_push(vty) ; /* Push stuff so far */ - if (err != 0) + if (retw != CMD_SUCCESS) + break ; + } ; + } ; + + ret = vty_config_write_close(vty) ; + + if ((ret != CMD_SUCCESS) || (retw != CMD_SUCCESS)) { - vty_out (vty, "Failed while writing configuration file %s.%s", - config_file_tmp, VTY_NEWLINE); + vty_out (vty, "%% Failed while writing configuration file %s.\n", + temp_name) ; goto finished; } - if (unlink (config_file_sav) != 0) + /* Now move files around to make .sav and the real file */ + ret = CMD_WARNING ; + + if (unlink (save_name) != 0) if (errno != ENOENT) { - vty_out (vty, "Can't unlink backup configuration file %s.%s", - config_file_sav, VTY_NEWLINE); + err = errno ; + vty_out (vty, "%% Can't unlink backup configuration file %s", + save_name) ; goto finished; } ; - if (link (config_file, config_file_sav) != 0) + if (link (config_name, save_name) != 0) { - vty_out (vty, "Can't backup old configuration file %s.%s", - config_file_sav, VTY_NEWLINE); + err = errno ; + vty_out (vty, "%% Can't backup old configuration file %s", config_name) ; goto finished; } ; sync () ; - if (unlink (config_file) != 0) + if (unlink (config_name) != 0) { - vty_out (vty, "Can't unlink configuration file %s.%s", - config_file, VTY_NEWLINE); + err = errno ; + vty_out (vty, "%% Can't unlink configuration file %s", config_name); goto finished; } ; - if (link (config_file_tmp, config_file) != 0) + if (link (temp_name, config_name) != 0) { - vty_out (vty, "Can't save configuration file %s.%s", - config_file, VTY_NEWLINE); + err = errno ; + vty_out (vty, "%% Can't save configuration file %s", config_name); goto finished; } ; sync (); - if (chmod (config_file, CONFIGFILE_MASK) != 0) + if (chmod (config_name, CONFIGFILE_MASK) != 0) { - vty_out (vty, "Can't chmod configuration file %s: %s (%s).\n", - config_file, errtostr(errno, 0).str, errtoname(errno, 0).str); + err = errno ; + vty_out (vty, "%% Can't chmod configuration file %s", config_name) ; goto finished; } - vty_out (vty, "Configuration saved to %s\n", config_file); + vty_out (vty, "Configuration saved to %s\n", config_name) ; ret = CMD_SUCCESS; finished: - unlink (config_file_tmp); - XFREE (MTYPE_TMP, config_file_tmp); - XFREE (MTYPE_TMP, config_file_sav); + if (err != 0) + vty_out(vty, ": %s (%s).\n", errtostr(errno, 0).str, + errtoname(errno, 0).str) ; + if (fd >= 0) + unlink (temp_name); + + qpath_free(temp) ; + qpath_free(save) ; + qpath_free(path) ; + return ret; -} +} ; ALIAS (config_write_file, config_write_cmd, @@ -3166,7 +1209,7 @@ ALIAS (config_write_file, "Copy running config to... \n" "Copy running config to startup config (same as write file)\n") -/* Write current configuration into the terminal. */ +/* Write current configuration into the terminal. */ DEFUN (config_write_terminal, config_write_terminal_cmd, "write terminal", @@ -3174,76 +1217,71 @@ DEFUN (config_write_terminal, "Write to terminal\n") { unsigned int i; - struct cmd_node *node; + bool vtysh_config ; - if (vty_shell_serv(vty)) - { - 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); - } - } - else + vtysh_config = (vty->type == VTY_SHELL_SERVER) ; + + if (!vtysh_config) + vty_out (vty, "\n" + "Current configuration:\n" + "!\n") ; + + for (i = 0 ; i < vector_length(node_vector) ; i++) { - vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, - VTY_NEWLINE); - vty_out (vty, "!%s", VTY_NEWLINE); - - 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); - } - vty_out (vty, "end%s",VTY_NEWLINE); - } + cmd_node node ; + + node = vector_get_item(node_vector, i) ; + + if (node == NULL) + continue ; + + if (vtysh_config && !node->config_to_vtysh) + continue ; + + if ((*node->config_write != NULL) && ((*node->config_write)(vty) != 0)) + vty_out (vty, "!\n") ; + } ; + + if (!vtysh_config) + vty_out (vty, "end\n") ; + return CMD_SUCCESS; } -/* Write current configuration into the terminal. */ +/* Write current configuration into the terminal. */ ALIAS (config_write_terminal, show_running_config_cmd, "show running-config", SHOW_STR "running configuration\n") -/* Write startup configuration into the terminal. */ +/* Write startup configuration into the terminal. */ DEFUN (show_startup_config, show_startup_config_cmd, "show startup-config", SHOW_STR - "Contentes of startup configuration\n") + "Contents of startup configuration\n") { - char buf[BUFSIZ]; - FILE *confp; + cmd_return_code_t ret ; + qpath path ; - confp = fopen (host.config, "r"); - if (confp == NULL) - { - vty_out (vty, "Can't open configuration file [%s]%s", - host.config, VTY_NEWLINE); - return CMD_WARNING; - } + VTY_LOCK() ; + path = (host.config_file != NULL) ? qpath_dup(host.config_file) : NULL ; + VTY_UNLOCK() ; - while (fgets (buf, BUFSIZ, confp)) + if (path == NULL) { - char *cp = buf; - - while (*cp != '\r' && *cp != '\n' && *cp != '\0') - cp++; - *cp = '\0'; - - vty_out (vty, "%s%s", buf, VTY_NEWLINE); - } + vty_out (vty, "%% Cannot show configuration file, using vtysh.\n"); + return CMD_WARNING; + } ; - fclose (confp); + ret = vty_cat_file(vty, path, "configuration file") ; + qpath_free(path) ; - return CMD_SUCCESS; + return ret ; } -/* Hostname configuration */ +/* Hostname configuration */ DEFUN_CALL (config_hostname, hostname_cmd, "hostname WORD", @@ -3252,21 +1290,11 @@ DEFUN_CALL (config_hostname, { if (!isalpha((int) *argv[0])) { - vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE); + vty_out (vty, "Please specify string starting with alphabet\n"); 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; + return cmd_set_host_name(argv[0]) ; } DEFUN_CALL (config_no_hostname, @@ -3276,26 +1304,18 @@ 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; + return cmd_set_host_name(NULL) ; } -/* VTY interface password set. */ -DEFUN_CALL (config_password, password_cmd, - "password (8|) WORD", - "Assign the terminal connection password\n" - "Specifies a HIDDEN password will follow\n" - "dummy string \n" - "The HIDDEN line password string\n") +/*------------------------------------------------------------------------------ + * Password setting function -- common for password and enable password. + */ +static cmd_return_code_t +do_set_password(vty vty, int argc, argv_t argv, char** p_password, + bool* p_encrypted) { + cmd_return_code_t ret ; + /* Argument check. */ if (argc == 0) { @@ -3303,47 +1323,63 @@ DEFUN_CALL (config_password, password_cmd, return CMD_WARNING; } + VTY_LOCK() ; + ret = CMD_SUCCESS ; + if (argc == 2) { + /* Encrypted password argument */ + if (*argv[0] == '8') - { - if (host.password) - XFREE (MTYPE_HOST, host.password); - host.password = NULL; - if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); - return CMD_SUCCESS; - } + { + XFREE(MTYPE_HOST, *p_password); + *p_encrypted = true ; + *p_password = XSTRDUP(MTYPE_HOST, argv[1]); + } else - { - vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } + { + vty_out(vty, "Unknown encryption type.\n"); + ret = CMD_WARNING; + } } - - if (!isalnum ((int) *argv[0])) + else { - vty_out (vty, - "Please specify string starting with alphanumeric%s", VTY_NEWLINE); - return CMD_WARNING; - } + /* Plaintext password argument */ - if (host.password) - XFREE (MTYPE_HOST, host.password); - host.password = NULL; + if (!isalnum ((int) *argv[0])) + { + vty_out(vty, "Please specify string starting with alphanumeric\n"); + ret = CMD_WARNING ; + } + else + { + /* If host.encrypt, only keeps the encrypted password. */ - if (host.encrypt) - { - if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); - } - else - host.password = XSTRDUP (MTYPE_HOST, argv[0]); + XFREE (MTYPE_HOST, *p_password); - return CMD_SUCCESS; -} + *p_encrypted = host.encrypt ; + if (*p_encrypted) + *p_password = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); + else + *p_password = XSTRDUP (MTYPE_HOST, argv[0]); + } ; + } ; + + VTY_UNLOCK() ; + return ret ; +} ; + +/* VTY interface password set. */ +DEFUN_CALL (config_password, password_cmd, + "password (8) WORD", + "Assign the terminal connection password\n" + "Specifies a HIDDEN password will follow\n" + "dummy string \n" + "The HIDDEN line password string\n") +{ + return do_set_password(vty, argc, argv, &host.password, + &host.password_encrypted) ; +} ; ALIAS_CALL (config_password, password_text_cmd, "password LINE", @@ -3352,65 +1388,16 @@ ALIAS_CALL (config_password, password_text_cmd, /* VTY enable password set. */ DEFUN_CALL (config_enable_password, enable_password_cmd, - "enable password (8|) WORD", + "enable password (8) WORD", "Modify enable password parameters\n" "Assign the privileged level password\n" "Specifies a HIDDEN password will follow\n" "dummy string \n" "The HIDDEN 'enable' password string\n") { - /* Argument check. */ - if (argc == 0) - { - vty_out (vty, "Please specify password.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - /* Crypt type is specified. */ - if (argc == 2) - { - if (*argv[0] == '8') - { - if (host.enable) - XFREE (MTYPE_HOST, host.enable); - host.enable = NULL; - - if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); - - return CMD_SUCCESS; - } - else - { - vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - - if (!isalnum ((int) *argv[0])) - { - vty_out (vty, - "Please specify string starting with alphanumeric%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (host.enable) - XFREE (MTYPE_HOST, host.enable); - host.enable = NULL; - - /* Plain password input. */ - if (host.encrypt) - { - if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); - } - else - host.enable = XSTRDUP (MTYPE_HOST, argv[0]); - - return CMD_SUCCESS; -} + return do_set_password(vty, argc, argv, &host.enable, + &host.enable_encrypted) ; +} ; ALIAS_CALL (config_enable_password, enable_password_text_cmd, @@ -3426,14 +1413,12 @@ DEFUN_CALL (no_config_enable_password, no_enable_password_cmd, "Modify enable password parameters\n" "Assign the privileged level password\n") { - if (host.enable) - XFREE (MTYPE_HOST, host.enable); - host.enable = NULL; + VTY_LOCK() ; - if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = NULL; + host.enable_encrypted = false ; + XFREE (MTYPE_HOST, host.enable); + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3443,24 +1428,30 @@ DEFUN_CALL (service_password_encrypt, "Set up miscellaneous service\n" "Enable encrypted passwords\n") { - if (host.encrypt) - return CMD_SUCCESS; + VTY_LOCK() ; - host.encrypt = 1; + host.encrypt = true ; - if (host.password) + /* If we have an unencrypted password in hand, convert that now. + * If not, retain any already encrypted password. + */ + if (!host.password_encrypted && (host.password != NULL)) { - if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password)); - } - if (host.enable) + char* plain = host.password ; + host.password = XSTRDUP(MTYPE_HOST, zencrypt (plain)) ; + XFREE(MTYPE_HOST, plain) ; + host.password_encrypted = true ; + } ; + + if (!host.enable_encrypted && (host.enable != NULL)) { - if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable)); - } + char* plain = host.enable ; + host.enable = XSTRDUP(MTYPE_HOST, zencrypt (plain)) ; + XFREE(MTYPE_HOST, plain) ; + host.enable_encrypted = true ; + } ; + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3471,19 +1462,13 @@ DEFUN_CALL (no_service_password_encrypt, "Set up miscellaneous service\n" "Enable encrypted passwords\n") { - if (! host.encrypt) - return CMD_SUCCESS; - - host.encrypt = 0; + VTY_LOCK() ; - if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = NULL; + /* Keep any existing passwords, encrypted or not. */ - if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = NULL; + host.encrypt = false ; + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3517,6 +1502,17 @@ DEFUN_CALL (config_terminal_no_length, config_terminal_no_length_cmd, return CMD_SUCCESS; } +static cmd_return_code_t +set_host_lines(int lines) +{ + VTY_LOCK() ; + + host.lines = lines ; + + VTY_UNLOCK() ; + return CMD_SUCCESS ; +} ; + DEFUN_CALL (service_terminal_length, service_terminal_length_cmd, "service terminal-length <0-512>", "Set up miscellaneous service\n" @@ -3532,10 +1528,9 @@ DEFUN_CALL (service_terminal_length, service_terminal_length_cmd, vty_out (vty, "length is malformed%s", VTY_NEWLINE); return CMD_WARNING; } - host.lines = lines; - return CMD_SUCCESS; -} + return set_host_lines(lines) ; +} ; DEFUN_CALL (no_service_terminal_length, no_service_terminal_length_cmd, "no service terminal-length [<0-512>]", @@ -3544,8 +1539,7 @@ DEFUN_CALL (no_service_terminal_length, no_service_terminal_length_cmd, "System wide terminal length configuration\n" "Number of lines of VTY (0 means no line control)\n") { - host.lines = -1; - return CMD_SUCCESS; + return set_host_lines(-1) ; } DEFUN_HID_CALL (do_echo, @@ -3556,13 +1550,25 @@ DEFUN_HID_CALL (do_echo, { char *message; - vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""), - VTY_NEWLINE); - if (message) - XFREE(MTYPE_TMP, message); + message = argv_concat(argv, argc, 0) ; + vty_out (vty, "%s\n", message ? message : "") ; + XFREE(MTYPE_TMP, message); + return CMD_SUCCESS; } +/*============================================================================== + * Logging configuration. + * + * Each VTY has its own logging level for monitor logging, and its own + * enable/disable. When logging is enabled, the current monitor logging + * level is set for the VTY. + * + * The monitor logging level is a bit special -- setting this level affects + * the current VTY (if it is a VTY_TERMINAL) and any future VTY. It also + * affects the level which will be written away to any configuration file. + */ + DEFUN_CALL (config_logmsg, config_logmsg_cmd, "logmsg "LOG_LEVELS" .MESSAGE", @@ -3578,8 +1584,7 @@ DEFUN_CALL (config_logmsg, message = argv_concat(argv, argc, 1); zlog(NULL, level, "%s", (message ? message : "")); - if (message) - XFREE(MTYPE_TMP, message); + XFREE(MTYPE_TMP, message); return CMD_SUCCESS; } @@ -3589,51 +1594,43 @@ DEFUN_CALL (show_logging, SHOW_STR "Show current logging configuration\n") { + int lvl ; + vty_out (vty, "Syslog logging: "); - if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) == ZLOG_DISABLED) - vty_out (vty, "disabled"); + if ((lvl = zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG)) == ZLOG_DISABLED) + vty_out (vty, "disabled\n"); else - vty_out (vty, "level %s, facility %s, ident %s", - 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, "level %s, facility %s, ident %s\n", zlog_priority[lvl], + facility_name(zlog_get_facility(NULL)), zlog_get_ident(NULL)) ; vty_out (vty, "Stdout logging: "); - if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) == ZLOG_DISABLED) - vty_out (vty, "disabled"); + if ((lvl = zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)) == ZLOG_DISABLED) + vty_out (vty, "disabled\n"); else - vty_out (vty, "level %s", - zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)]); - vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "level %s\n", zlog_priority[lvl]) ; vty_out (vty, "Monitor logging: "); - if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) == ZLOG_DISABLED) - vty_out (vty, "disabled"); + if ((lvl = zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)) == ZLOG_DISABLED) + vty_out (vty, "disabled\n"); else - vty_out (vty, "level %s", - zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)]); - vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "level %s\n", zlog_priority[lvl]); vty_out (vty, "File logging: "); - if ((zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) == ZLOG_DISABLED) || - !zlog_is_file(NULL)) - vty_out (vty, "disabled"); + if (((lvl = zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)) == ZLOG_DISABLED) || + !zlog_is_file(NULL)) + vty_out (vty, "disabled\n"); else { char * filename = zlog_get_filename(NULL); - vty_out (vty, "level %s, filename %s", - zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)], - filename); + vty_out (vty, "level %s, filename %s\n", zlog_priority[lvl], filename) ; free(filename); } - vty_out (vty, "%s", VTY_NEWLINE); - vty_out (vty, "Protocol name: %s%s", - zlog_get_proto_name(NULL), VTY_NEWLINE); - vty_out (vty, "Record priority: %s%s", - (zlog_get_record_priority(NULL) ? "enabled" : "disabled"), VTY_NEWLINE); - vty_out (vty, "Timestamp precision: %d%s", - zlog_get_timestamp_precision(NULL), VTY_NEWLINE); + vty_out (vty, "Protocol name: %s\n", zlog_get_proto_name(NULL)); + vty_out (vty, "Record priority: %s\n", + (zlog_get_record_priority(NULL) ? "enabled" : "disabled")) ; + vty_out (vty, "Timestamp precision: %d\n", + zlog_get_timestamp_precision(NULL)) ; return CMD_SUCCESS; } @@ -3681,7 +1678,9 @@ DEFUN_CALL (config_log_monitor, "Logging control\n" "Set terminal line (monitor) logging level\n") { - zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_get_default_lvl(NULL)); + int level = zlog_get_default_lvl(NULL) ; + zlog_set_level (NULL, ZLOG_DEST_MONITOR, level) ; + vty_set_monitor_level(vty, level) ; return CMD_SUCCESS; } @@ -3696,7 +1695,9 @@ DEFUN_CALL (config_log_monitor_level, if ((level = level_match(argv[0])) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; + zlog_set_level (NULL, ZLOG_DEST_MONITOR, level); + vty_set_monitor_level(vty, level) ; return CMD_SUCCESS; } @@ -3709,57 +1710,38 @@ DEFUN_CALL (no_config_log_monitor, "Logging level\n") { zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + vty_set_monitor_level(vty, ZLOG_DISABLED) ; return CMD_SUCCESS; } +/*------------------------------------------------------------------------------ + * Set new logging file and level -- "log file FILENAME [LEVEL]" + * + * Note that even if fail to open the new log file, will set host.logfile. + * + * Failure here is an error. + */ static int 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) - { - zlog_err ("config_log_file: Unable to alloc mem!"); - return CMD_WARNING; - } - sprintf (p, "%s/%s", cwd, fname); - fullpath = p; - } - else - fullpath = fname; - - ret = zlog_set_file (NULL, fullpath, loglevel); + int err ; - if (p) - XFREE (MTYPE_TMP, p); + VTY_LOCK() ; - if (!ret) - { - vty_out (vty, "can't open logfile %s\n", fname); - return CMD_WARNING; - } + host.logfile = uty_cmd_path_name_complete(host.logfile, fname, + vty->exec->context) ; + err = zlog_set_file (NULL, qpath_string(host.logfile), loglevel) ; - if (host.logfile) - XFREE (MTYPE_HOST, host.logfile); + VTY_UNLOCK() ; - host.logfile = XSTRDUP (MTYPE_HOST, fname); + if (err == 0) + return CMD_SUCCESS ; - return CMD_SUCCESS; + vty_out(vty, "%% failed to open log file %s: %s (%s)\n", + qpath_string(host.logfile), + errtostr(err, 0).str, + errtoname(err, 0).str) ; + return CMD_WARNING ; } DEFUN_CALL (config_log_file, @@ -3774,7 +1756,7 @@ DEFUN_CALL (config_log_file, DEFUN_CALL (config_log_file_level, config_log_file_level_cmd, - "log file FILENAME "LOG_LEVELS, + "log file FILENAME " LOG_LEVELS, "Logging control\n" "Logging to file\n" "Logging filename\n" @@ -3784,6 +1766,7 @@ DEFUN_CALL (config_log_file_level, if ((level = level_match(argv[1])) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; + return set_log_file(vty, argv[0], level); } @@ -3795,13 +1778,13 @@ DEFUN_CALL (no_config_log_file, "Cancel logging to file\n" "Logging file name\n") { - zlog_reset_file (NULL); + VTY_LOCK() ; - if (host.logfile) - XFREE (MTYPE_HOST, host.logfile); + zlog_reset_file (NULL); - host.logfile = NULL; + host.logfile = qpath_free(host.logfile) ; + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -3989,20 +1972,43 @@ DEFUN_CALL (no_config_log_timestamp_precision, return CMD_SUCCESS; } +/*============================================================================== + * MOTD commands and set up. + * + * Note that can set a MOTD file that does not exist at the time. A friendly + * message warns about this, but it is not an error. The message will not be + * seen while reading the configuration file -- but it is not worth stopping + * the configuration file reader for this ! + */ DEFUN_CALL (banner_motd_file, banner_motd_file_cmd, - "banner motd file [FILE]", + "banner motd file FILE", "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n") { - if (host.motdfile) - XFREE (MTYPE_HOST, host.motdfile); - host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]); + int err ; - return CMD_SUCCESS; -} + VTY_LOCK() ; + + host.motdfile = uty_cmd_path_name_complete(host.motdfile, + argv[0], vty->exec->context) ; + err = qpath_stat_is_file(host.motdfile) ; + + if (err != 0) + { + vty_out(vty, "NB: '%s': ", qpath_string(host.motdfile)) ; + if (err < 0) + vty_out(vty, "is not a file\n") ; + else + vty_out(vty, "%s (%s)\n", errtostr(err, 0).str, + errtoname(err, 0).str) ; + } ; + + VTY_UNLOCK() ; + return CMD_SUCCESS ; +} ; DEFUN_CALL (banner_motd_default, banner_motd_default_cmd, @@ -4011,7 +2017,11 @@ DEFUN_CALL (banner_motd_default, "Strings for motd\n" "Default string\n") { - host.motd = default_motd; + VTY_LOCK() ; + + host.motd = default_motd ; + + VTY_UNLOCK() ; return CMD_SUCCESS; } @@ -4022,23 +2032,149 @@ DEFUN_CALL (no_banner_motd, "Set banner string\n" "Strings for motd\n") { - host.motd = NULL; - if (host.motdfile) - XFREE (MTYPE_HOST, host.motdfile); - host.motdfile = NULL; + VTY_LOCK() ; + + host.motd = NULL ; + host.motdfile = qpath_free(host.motdfile) ; + + VTY_UNLOCK() ; return CMD_SUCCESS; } -/* Set config filename. Called from vty.c */ -void -host_config_set (char *filename) +/*------------------------------------------------------------------------------ + * Set current directory + */ +DEFUN_CALL (do_chdir, + chdir_cmd, + "chdir DIR", + "Set current directory\n" + "Directory to set\n") +{ + cmd_return_code_t ret ; + qpath path ; + int err ; + + ret = CMD_SUCCESS ; + + path = uty_cmd_path_name_complete(NULL, argv[0], vty->exec->context) ; + err = qpath_stat_is_directory(path) ; + + if (err == 0) + qpath_copy(vty->exec->context->dir_cd, path) ; + else + { + vty_out(vty, "%% chdir %s: ", qpath_string(path)) ; + if (err < 0) + vty_out(vty, "is not a directory\n") ; + else + vty_out(vty, "%s (%s)\n", errtostr(err, 0).str, + errtoname(err, 0).str) ; + ret = CMD_WARNING ; + } ; + + qpath_free(path) ; + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Show given directory path + */ +DEFUN_CALL (do_shdir, + shdir_cmd, + "shdir DIR", + "Show directory\n" + "Directory to show\n") { - if (host.config) - XFREE (MTYPE_HOST, host.config); - host.config = XSTRDUP (MTYPE_HOST, filename); + cmd_return_code_t ret ; + qpath path ; + int err ; + + ret = CMD_SUCCESS ; + + path = uty_cmd_path_name_complete(NULL, argv[0], vty->exec->context) ; + err = qpath_stat_is_directory(path) ; + + if (err == 0) + vty_out(vty, "%s\n", qpath_string(path)) ; + else + { + vty_out(vty, "%% %s: ", qpath_string(path)) ; + if (err < 0) + vty_out(vty, "is not a directory\n") ; + else + vty_out(vty, "%s (%s)\n", errtostr(err, 0).str, + errtoname(err, 0).str) ; + ret = CMD_WARNING ; + } ; + + qpath_free(path) ; + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Get cwd. + * + * This is done very early in the morning, before lowering privileges, to + * minimise chance of not being able to get the cwd. If cwd itself is not + * accessible in lowered privilege state, that will later become clear. + * + * Sets host.cwd, which is torn down in cmd_terminate(). + * + */ +extern void +cmd_getcwd(void) +{ + host.cwd = qpath_getcwd(NULL) ; + + if (host.cwd == NULL) + { + fprintf(stderr, "Cannot getcwd()\n") ; + exit(1) ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * Set host.config_file and host.config_dir. + */ +extern void +cmd_host_config_set (qpath config_file) +{ + VTY_LOCK() ; + + host.config_file = qpath_copy(host.config_file, config_file) ; + host.config_dir = qpath_copy(host.config_dir, config_file) ; + + qpath_shave(host.config_dir) ; + + VTY_UNLOCK() ; } -void +/*------------------------------------------------------------------------------ + * Set the lexical level for further command processing. + */ +DEFUN_CALL (lexical_level, + lexical_level_cmd, + "lexical-level <0-1>", + "Set lexical level\n" + "The required lexical level\n") +{ + int level ; + + level = strtol(argv[0], NULL, 0) ; + + vty->exec->context->full_lex = (level != 0) ; + + return CMD_SUCCESS; +} + +/*============================================================================== + * Command handling initialisation and termination. + */ + +/*------------------------------------------------------------------------------ + * Install copy of the default commands in the given node. + */ +extern void install_default (enum node_type node) { install_element (node, &config_exit_cmd); @@ -4054,26 +2190,33 @@ install_default (enum node_type node) install_element (node, &show_running_config_cmd); } -/* Initialize command interface. Install basic nodes and commands. */ -void -cmd_init (int terminal) +/*------------------------------------------------------------------------------ + * Initialise command handling. + * + * Install basic nodes and commands. Initialise the host structure. + * + * Sets srand(time(NULL)) + */ +extern void +cmd_init (bool terminal) { - command_cr = XSTRDUP(MTYPE_STRVEC, "<cr>"); - desc_cr.cmd = command_cr; - desc_cr.str = XSTRDUP(MTYPE_STRVEC, ""); - - /* Allocate initial top vector of commands. */ - cmdvec = vector_init (0); - - /* Default host value settings. */ - host.name = NULL; - host.password = NULL; - host.enable = NULL; - host.logfile = NULL; - host.config = NULL; - host.lines = -1; - host.motd = default_motd; - host.motdfile = NULL; + srand(time(NULL)) ; + + cmd_parser_init() ; + + if (host.cwd == NULL) /* in case cmd_cwd() not called, yet */ + cmd_getcwd() ; + + /* Allocate initial top vector of commands. */ + node_vector = vector_init(0); + + /* Set default motd */ + host.motd = default_motd ; + host.config_brand = rand() ; + + /* Default host value settings are already set, see above */ + + cmd_get_sys_host_name() ; /* start with system name & name_gen == 1 */ /* Install top nodes. */ install_node (&view_node, NULL); @@ -4092,30 +2235,44 @@ cmd_init (int terminal) install_element (VIEW_NODE, &config_quit_cmd); install_element (VIEW_NODE, &config_help_cmd); install_element (VIEW_NODE, &config_enable_cmd); + install_element (VIEW_NODE, &config_enable_configure_cmd); + install_element (VIEW_NODE, &config_terminal_cmd); install_element (VIEW_NODE, &config_terminal_length_cmd); install_element (VIEW_NODE, &config_terminal_no_length_cmd); install_element (VIEW_NODE, &show_logging_cmd); install_element (VIEW_NODE, &echo_cmd); + install_element (VIEW_NODE, &chdir_cmd); + install_element (VIEW_NODE, &shdir_cmd); install_element (RESTRICTED_NODE, &config_list_cmd); install_element (RESTRICTED_NODE, &config_exit_cmd); install_element (RESTRICTED_NODE, &config_quit_cmd); install_element (RESTRICTED_NODE, &config_help_cmd); install_element (RESTRICTED_NODE, &config_enable_cmd); + install_element (RESTRICTED_NODE, &config_enable_configure_cmd); + install_element (RESTRICTED_NODE, &config_terminal_cmd); install_element (RESTRICTED_NODE, &config_terminal_length_cmd); install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd); install_element (RESTRICTED_NODE, &echo_cmd); + install_element (RESTRICTED_NODE, &chdir_cmd); + install_element (RESTRICTED_NODE, &shdir_cmd); } if (terminal) { install_default (ENABLE_NODE); install_element (ENABLE_NODE, &config_disable_cmd); + install_element (ENABLE_NODE, &config_enable_cmd); + install_element (ENABLE_NODE, &config_enable_configure_cmd); install_element (ENABLE_NODE, &config_terminal_cmd); install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); } + install_element (ENABLE_NODE, &show_startup_config_cmd); install_element (ENABLE_NODE, &show_version_cmd); + install_element (ENABLE_NODE, &lexical_level_cmd); + install_element (ENABLE_NODE, &chdir_cmd); + install_element (ENABLE_NODE, &shdir_cmd); if (terminal) { @@ -4130,9 +2287,12 @@ cmd_init (int terminal) install_element (CONFIG_NODE, &hostname_cmd); install_element (CONFIG_NODE, &no_hostname_cmd); + install_element (CONFIG_NODE, &lexical_level_cmd); if (terminal) { + install_element (CONFIG_NODE, &echo_cmd); + install_element (CONFIG_NODE, &password_cmd); install_element (CONFIG_NODE, &password_text_cmd); install_element (CONFIG_NODE, &enable_password_cmd); @@ -4170,97 +2330,71 @@ cmd_init (int terminal) install_element (CONFIG_NODE, &service_terminal_length_cmd); install_element (CONFIG_NODE, &no_service_terminal_length_cmd); + install_element (RESTRICTED_NODE, &show_thread_cpu_cmd); install_element (VIEW_NODE, &show_thread_cpu_cmd); install_element (ENABLE_NODE, &show_thread_cpu_cmd); - install_element (RESTRICTED_NODE, &show_thread_cpu_cmd); - + install_element (ENABLE_NODE, &clear_thread_cpu_cmd); install_element (VIEW_NODE, &show_work_queues_cmd); install_element (ENABLE_NODE, &show_work_queues_cmd); - } - srand(time(NULL)); -} + } ; +} ; +/*------------------------------------------------------------------------------ + * Close down command interface. + * + * Dismantle the node_vector and all commands. + * + * Clear out the host structure. + */ void cmd_terminate () { - unsigned int i, j, k, l; - struct cmd_node *cmd_node; - struct cmd_element *cmd_element; - struct desc *desc; - vector cmd_node_v, cmd_element_v, desc_v; + cmd_node cmd_node; + cmd_command cmd ; - if (cmdvec) + /* Ream out the vector of command nodes. */ + while ((cmd_node = vector_ream(node_vector, free_it)) != NULL) { - for (i = 0; i < vector_length (cmdvec); i++) + /* Ream out the (embedded) vector of commands per node. */ + while ((cmd = vector_ream(cmd_node->cmd_vector, keep_it)) != NULL) { - cmd_node = vector_get_item (cmdvec, i) ; - if (cmd_node == NULL) - continue ; - - cmd_node_v = cmd_node->cmd_vector; + /* Ream out the vector of items for each command. + * + * Note that each cmd is a static structure, which may appear in + * more than one cmd_vector -- but the "compiled" portions are + * dynamically allocated. + */ + cmd_item next_item ; - for (j = 0; j < vector_length (cmd_node_v); j++) + while ((next_item = vector_ream(cmd->items, free_it)) != NULL) { - cmd_element = vector_get_item (cmd_node_v, j) ; - if (cmd_element == NULL) - continue ; - - cmd_element_v = cmd_element->strvec ; - if (cmd_element_v == NULL) - continue ; - - for (k = 0; k < vector_length (cmd_element_v); k++) + do { - 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); + cmd_item item ; + item = next_item ; + next_item = item->next ; + XFREE(MTYPE_CMD_ITEM, item); + } + while (next_item != NULL) ; } ; - vector_free (cmd_node_v); - } ; + cmd->items = NULL ; /* gone */ - vector_free (cmdvec); - cmdvec = NULL; + XFREE (MTYPE_CMD_STRING, cmd->r_doc) ; + } ; } - if (command_cr) - XFREE(MTYPE_STRVEC, command_cr); - if (desc_cr.str) - XFREE(MTYPE_STRVEC, desc_cr.str); - if (host.name) - XFREE (MTYPE_HOST, host.name); - if (host.password) - XFREE (MTYPE_HOST, host.password); - if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - if (host.enable) - XFREE (MTYPE_HOST, host.enable); - if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - if (host.logfile) - XFREE (MTYPE_HOST, host.logfile); - if (host.motdfile) - XFREE (MTYPE_HOST, host.motdfile); - if (host.config) - XFREE (MTYPE_HOST, host.config); -} + node_vector = NULL ; + + XFREE(MTYPE_HOST, host.name); + XFREE(MTYPE_HOST, host.password); + XFREE(MTYPE_HOST, host.enable); + host.logfile = qpath_free(host.logfile) ; + host.motdfile = qpath_free(host.motdfile) ; + host.config_file = qpath_free(host.config_file) ; + host.config_dir = qpath_free(host.config_dir) ; + XFREE(MTYPE_HOST, host.vty_accesslist_name); + XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name); + host.cwd = qpath_free(host.cwd) ; +} ; |