summaryrefslogtreecommitdiffstats
path: root/lib/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/command.c')
-rw-r--r--lib/command.c4199
1 files changed, 1143 insertions, 3056 deletions
diff --git a/lib/command.c b/lib/command.c
index 67a9ab04..4057bcfc 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -21,83 +21,285 @@ 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 "vty_local.h"
+#include "vty_command.h"
+#include "vty_io.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 */
-/* Command vector which includes some level of command lists. Normally
- each daemon maintains each own cmdvec. */
-vector cmdvec = NULL;
+ /* Log filename. */
+ .logfile = NULL,
-struct desc desc_cr;
-char *command_cr = NULL;
+ /* config file name of this host */
+ .config_file = NULL,
+ .config_dir = NULL,
-/* Host information structure. */
-struct host host;
+ /* Flags for services */
+ .advanced = false,
+ .encrypt = false,
-/* Standard command node structures. */
+ /* 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 ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ 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() ;
+
+ 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) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * 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
-};
+ .node = CONFIG_NODE,
+ .prompt = "%s(config)# ",
-/* 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";
+ .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 +307,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 +331,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 +398,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 +414,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 +435,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 +452,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 +626,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_node cn ;
- /* cmd_init hasn't been called */
- if (!cmdvec)
- return;
+ cn = vector_get_item (node_vector, ntype);
- cnode = vector_get_item (cmdvec, 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];
- gettimeofday(&tv,0);
+ extern char *crypt (const char *, const char *) ;
- to64(&salt[0], random(), 3);
- to64(&salt[3], tv.tv_usec, 3);
- salt[5] = '\0';
+ r = qt_random(*passwd) ;
- return crypt (passwd, salt);
+ 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) ;
}
/* 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);
-
- 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;
-}
+ uty_out (vio, "no banner motd\n");
-#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.
- *
- * See above for the ranking of matches.
+ * Enter ENABLE_NODE, possibly via password check.
*
- * 2. for "partial match", look out for matching more than one keyword, and
- * return 1 if finds that.
+ * If the parser established that can enter ENABLE_NODE directly, that's what
+ * happens.
*
- * 3. for "range match", look out for matching more than one range, 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.
*
- * 4. for ipv4_prefix_match and ipv6_prefix_match, if get a "partial match",
- * return 2.
+ * The authentication itself may fail...
*
- * 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 ;
+ if (vty->exec->parsed->nnode == ENABLE_NODE)
+ return CMD_SUCCESS ;
- 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. */
-
- 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);
-}
-
-/*------------------------------------------------------------------------------
- * 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;
+ return CMD_SUCCESS ; /* will end to parsed->nnode */
}
-/*------------------------------------------------------------------------------
- * 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;
+ /* Set up the file names. */
+ config_name = qpath_string(path) ;
- 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);
+ save = qpath_dup(path) ;
+ qpath_extend_str(save, CONF_BACKUP_EXT) ;
+ save_name = qpath_string(save) ;
+ temp = qpath_dup(path) ;
+ qpath_extend_str(temp, ".XXXXXX") ;
+ temp_name = qpath_char_string(temp) ;
- config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
- sprintf (config_file_tmp, "%s.XXXXXX", config_file);
-
- /* Open file to configuration write. */
- fd = mkstemp (config_file_tmp);
+ /* 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. */
+ /* Make vty for configuration file. */
vty_open_config_write(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 ;
- err = vty_close_config_write(vty) ;
- close(fd) ;
+ 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");
- if (err != 0)
+ retw = vty_cmd_out_push(vty) ; /* Push stuff so far */
+
+ if (retw != CMD_SUCCESS)
+ break ;
+ } ;
+ } ;
+
+ ret = vty_close_config_write(vty, (retw != CMD_SUCCESS)) ;
+
+ 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 +1208,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 +1216,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 +1289,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 +1303,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 +1322,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 +1387,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 +1412,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 +1427,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 +1461,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 +1501,15 @@ 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 +1525,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 +1536,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 +1547,17 @@ 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.
+ */
+
DEFUN_CALL (config_logmsg,
config_logmsg_cmd,
"logmsg "LOG_LEVELS" .MESSAGE",
@@ -3578,8 +1573,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;
}
@@ -3712,54 +1706,34 @@ DEFUN_CALL (no_config_log_monitor,
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';
+ int err ;
- 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);
-
- 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 +1748,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 +1758,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 +1770,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 +1964,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 +2009,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 +2024,114 @@ 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)
+/*==============================================================================
+ * Current directory handling
+ */
+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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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)
{
- if (host.config)
- XFREE (MTYPE_HOST, host.config);
- host.config = XSTRDUP (MTYPE_HOST, filename);
+ 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_cmd_set_full_lex(vty, (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 +2147,31 @@ 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)) ;
+
+ 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 +2190,41 @@ 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 (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);
}
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, &copy_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);
if (terminal)
{
@@ -4130,9 +2239,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 +2282,72 @@ 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_string) ; /* sets 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) ;
+} ;