summaryrefslogtreecommitdiffstats
path: root/lib/command_execute.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/command_execute.c')
-rw-r--r--lib/command_execute.c524
1 files changed, 331 insertions, 193 deletions
diff --git a/lib/command_execute.c b/lib/command_execute.c
index cbf260b9..32698d94 100644
--- a/lib/command_execute.c
+++ b/lib/command_execute.c
@@ -40,135 +40,226 @@
/*------------------------------------------------------------------------------
* Construct cmd_exec object and initialise.
*
- * The following are set by uty_cmd_prepare() which is called after something
- * has been pushed/popped on the vin/vout stacks, and before any command
- * execution starts:
+ * This is done when a command loop is entered.
*
- * - parse_type
- * - reflect_enabled
- * - out_enabled
- *
- * to reflect the then current VTY state.
+ * The initial cmd_context reflects the vty->type and initial vty->node.
*/
extern cmd_exec
cmd_exec_new(vty vty)
{
- cmd_exec exec ;
+ cmd_exec exec ;
+ cmd_context context ;
exec = XCALLOC(MTYPE_CMD_EXEC, sizeof(struct cmd_exec)) ;
/* Zeroising has set:
*
- * vty = X -- set below
+ * vty = X -- set below
*
- * line = NULL -- no command line, yet
- * to_do = cmd_do_nothing
+ * action = all zeros
*
- * parse_type = cmd_parse_standard
+ * context = NULL -- see below
*
- * reflect_enabled = false -- not enabled
- * out_enabled = false -- not enabled
+ * parsed = NULL -- see below
*
- * parsed all zeros -- empty parsed object (embedded)
+ * password_failures = 0 -- none, yet
*
- * state = exec_null
- * locus = NULL -- not significant in exec_null
- * ret = CMD_SUCCESS
+ * state = exec_null
+ * locus = NULL -- not significant in exec_null
+ * ret = CMD_SUCCESS
*
- * cq = NULL -- no mqb (qpthreads)
- * -- no thread (legacy thread)
+ * cq = NULL -- no mqb (qpthreads)
+ * -- no thread (legacy thread)
*/
- confirm(cmd_do_nothing == 0) ;
- confirm(cmd_parse_standard == 0) ;
- confirm(CMD_PARSED_INIT_ALL_ZEROS) ;
- confirm(exec_null == 0) ;
- confirm(CMD_SUCCESS == 0) ;
+ confirm(CMD_ACTION_ALL_ZEROS) ; /* action */
+ confirm(exec_null == 0) ; /* state */
+ confirm(CMD_SUCCESS == 0) ; /* ret */
+
+ exec->vty = vty ;
+
+ exec->parsed = cmd_parsed_new() ;
+
+ /* Initialise the context */
+ VTY_ASSERT_LOCKED() ;
+
+ exec->context = context = cmd_context_new() ;
+
+ context->node = vty->node ;
+
+ context->parse_execution = true ;
+ context->parse_only = false ;
+ context->reflect_enabled = false ;
+
+ context->onode = NULL_NODE ;
+
+ context->dir_cd = qpath_dup(host.cwd) ;
+ context->dir_home = qpath_dup(host.config_dir) ;
+
+ switch (vty->type)
+ {
+ case VTY_TERMINAL:
+ context->full_lex = true ;
+
+ context->parse_strict = false ;
+ context->parse_no_do = false ;
+ context->parse_no_tree = false ;
+
+ context->can_enable = context->node >= ENABLE_NODE ;
+ context->can_auth_enable = true ;
+
+ context->dir_here = qpath_dup(context->dir_cd) ;
+
+ break ;
+
+ case VTY_SHELL_SERVER:
+ zabort("missing VTY_SHELL_SERVER context") ;
+
+ context->full_lex = true ;
+
+ context->parse_strict = false ;
+ context->parse_no_do = false ;
+ context->parse_no_tree = false ;
+
+ context->can_enable = true ;
+ context->can_auth_enable = false ;
- exec->vty = vty ;
+ context->dir_here = qpath_dup(context->dir_cd) ;
+
+ break ;
+
+ case VTY_CONFIG_READ:
+ context->full_lex = false ;
+
+ context->parse_strict = true ;
+ context->parse_no_do = true ;
+ context->parse_no_tree = false ;
+
+ context->can_enable = true ;
+ context->can_auth_enable = false ;
+
+ context->dir_here = qpath_dup(context->dir_home) ;
+
+ break ;
+
+ default:
+ zabort("vty type unknown to cmd_exec_new()") ;
+ } ;
return exec ;
} ;
/*------------------------------------------------------------------------------
- * Destroy cmd_exec object.
+ * Make a new context -- returns a completely empty context object.
*/
-extern cmd_exec
-cmd_exec_free(cmd_exec exec)
+extern cmd_context
+cmd_context_new(void)
{
- cmd_parsed_reset(exec->parsed, keep_it) ;
+ return XCALLOC(MTYPE_CMD_EXEC, sizeof(struct cmd_context)) ;
+} ;
+/*------------------------------------------------------------------------------
+ * Save current context and update for new, child vin.
+ *
+ * - updates the dir_here if required
+ *
+ * - cannot inherit can_enable unless is ENABLE_NODE or better
+ *
+ * - cannot inherit can_auth_enable, no how
+ */
+extern cmd_context
+cmd_context_new_save(cmd_context context, qpath file_here)
+{
+ cmd_context saved ;
+ saved = cmd_context_new() ;
- XFREE(MTYPE_CMD_EXEC, exec) ;
+ *saved = *context ; /* copy as is */
- return NULL ;
-} ;
+ /* The saved copy of the context now owns the current paths, so now need
+ * to duplicate (or set new) paths.
+ */
+ context->dir_cd = qpath_dup(saved->dir_cd) ;
+ context->dir_home = qpath_dup(saved->dir_home) ;
+
+ if (file_here == NULL)
+ context->dir_here = qpath_dup(saved->dir_here) ;
+ else
+ {
+ context->dir_here = qpath_dup(file_here) ;
+ qpath_shave(context->dir_here) ;
+ } ;
+ /* The inheritance of can_enable is tricky. Will not bequeath can_enable
+ * is is not already in ENABLE_NODE or better !
+ */
+ if (context->node <= ENABLE_NODE)
+ context->can_enable = false ;
-/*==============================================================================
- *
- */
+ context->can_auth_enable = false ;
+
+ return saved ;
+} ;
/*------------------------------------------------------------------------------
- * Set new node.
+ * Restore given context -- frees the copy restored from.
*
- * If old node >= CONFIG_NODE, and new node < CONFIG_NODE, give up the config
- * symbol of power.
+ * Has to free the directories in the context being restored to.
*
- * Returns: CMD_SUCCESS -- OK
- * CMD_CLOSE -- if new node == NODE_NULL
+ * Returns NULL.
*/
-static cmd_return_code_t
-cmd_set_node(vty vty, node_type_t node)
+extern cmd_context
+cmd_context_restore(cmd_context dst, cmd_context src)
{
- if ((vty->node >= MIN_CONFIG_NODE) && (node < MIN_CONFIG_NODE))
- vty_config_unlock(vty, node) ;
- else
- vty->node = node ;
+ assert(src != NULL) ;
+
+ qpath_free(dst->dir_cd) ;
+ qpath_free(dst->dir_home) ;
+ qpath_free(dst->dir_here) ;
+
+ *dst = *src ; /* copy as is */
- return (vty->node != NULL_NODE) ? CMD_SUCCESS : CMD_CLOSE ;
+ return cmd_context_free(src, true) ;
} ;
/*------------------------------------------------------------------------------
- * Command line "end" command
+ * Free the given context.
*
- * Falls back to the current node's end_to node. If leaves configuration
- * mode, give away the configuration symbol of power.
- *
- * Generally, for all configuration nodes end -> NODE_ENABLE (releasing the
- * configuration lock), and all other nodes end does nothing.
- *
- * Returns: CMD_SUCCESS -- OK
- * CMD_CLOSE -- if new node == NODE_NULL
+ * If the context is a copy of an existing context, then must not free the
+ * directories -- they will be freed when that existing context is freed.
*/
-extern cmd_return_code_t
-cmd_end(vty vty)
+extern cmd_context
+cmd_context_free(cmd_context context, bool copy)
{
- return cmd_set_node(vty, cmd_node_end_to(vty->node)) ;
+ if (context != NULL)
+ {
+ if (!copy)
+ {
+ qpath_free(context->dir_cd) ;
+ qpath_free(context->dir_home) ;
+ qpath_free(context->dir_here) ;
+ } ;
+
+ XFREE(MTYPE_CMD_EXEC, context) ; /* sets context = NULL */
+ } ;
+
+ return context ;
} ;
/*------------------------------------------------------------------------------
- * Command line "exit" command -- aka "quit"
- *
- * Falls back to the current node's exit_to node. If leaves configuration
- * mode, give away the configuration symbol of power.
- *
- * Generally:
- *
- * - for all configuration nodes > NODE_CONFIG exit -> parent node.
- *
- * - for NODE_CONFIG exit -> ENABLE_NODE (and release configuration symbol
- * of power)
- *
- * - for all nodes < NODE_CONFIG -> close the VTY
- *
- * Returns: CMD_SUCCESS -- OK
- * CMD_CLOSE -- if new node == NODE_NULL
+ * Destroy cmd_exec object.
*/
-extern cmd_return_code_t
-cmd_exit(vty vty)
+extern cmd_exec
+cmd_exec_free(cmd_exec exec)
{
- return cmd_set_node(vty, cmd_node_exit_to(vty->node)) ;
+ if (exec != NULL)
+ {
+ exec->parsed = cmd_parsed_free(exec->parsed) ;
+ exec->context = cmd_context_free(exec->context, false) ; /* not a copy */
+
+ XFREE(MTYPE_CMD_EXEC, exec) ;
+ } ;
+
+ return NULL ;
} ;
/*==============================================================================
@@ -236,16 +327,12 @@ cmd_execute_command(struct vty *vty,
* 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.
+ * Returns: cmd_return_code for last command or I/O operation
*
- * If
+ * when returns, the entire vin/vout stack will have been closed.
*
- * If return code is not CMD_SUCCESS, the the output buffering contains the
- * output from the last command attempted.
+ * If reaches EOF on the config file, returns CMD_SUCCESS. If anything else
+ * happens, will generate an error message and exits the command loop.
*/
extern cmd_return_code_t
cmd_read_config(struct vty *vty, cmd_command first_cmd, bool ignore_warning)
@@ -254,89 +341,122 @@ cmd_read_config(struct vty *vty, cmd_command first_cmd, bool ignore_warning)
cmd_parsed parsed = exec->parsed ;
cmd_return_code_t ret;
+ ret = CMD_SUCCESS ; /* so far, so good */
+
while (1)
{
- /* Need a command line, pops pipes as required */
- ret = vty_cmd_fetch_line(vty) ; /* sets exec->line */
+ /* Deal with anything which is not success !
+ */
+ if ((ret != CMD_SUCCESS) && (ret != CMD_EMPTY))
+ {
+ /* Will drop straight out of the loop if have anything other
+ * than CMD_HIATUS, CMD_EOF or CMD_CLOSE, which are all signals
+ * that some adjustment to the vin/vout stacks is required,
+ * or that we are all done here.
+ *
+ * Everything else is deemed to be an error that stops the
+ * command loop.
+ */
+ if ((ret != CMD_HIATUS) && (ret != CMD_EOF) && (ret != CMD_CLOSE))
+ break ;
+
+ ret = vty_cmd_hiatus(vty, ret) ;
+ /* for CMD_EOF & CMD_HIATUS only */
+ if (ret == CMD_EOF)
+ return CMD_SUCCESS ; /* eof on the config file is
+ the expected outcome ! */
+ if (ret != CMD_SUCCESS)
+ break ;
+ } ;
+
+ /* If all is well, need another command line */
+
+ ret = vty_cmd_fetch_line(vty) ; /* sets exec->action */
if (ret != CMD_SUCCESS)
- break ; /* stop on any and all problems */
+ continue ;
/* Parse the command line we now have */
- cmd_tokenise(parsed, exec->line) ;
- ret = cmd_parse_command(parsed, vty->node,
- exec->parse_type | cmd_parse_execution) ;
+ assert(exec->action->to_do == cmd_do_command) ;
- if (ret == CMD_EMPTY)
- continue ; /* easy if empty line */
+ cmd_tokenize(parsed, exec->action->line, exec->context->full_lex) ;
+ ret = cmd_parse_command(parsed, exec->context) ;
if (ret != CMD_SUCCESS)
- break ; /* stop on *any* parsing issue */
+ continue ;
/* Special handling before first active line. */
if (first_cmd != NULL)
{
if (first_cmd != parsed->cmd)
{
- ret = (*first_cmd->func)(first_cmd, vty, 0, NULL) ;
+ ret = (*first_cmd->func)(first_cmd, vty, -1, NULL) ;
if (ret != CMD_SUCCESS)
- break ; /* stop on *any* issue with "default" */
+ continue ;
} ;
first_cmd = NULL ;
} ;
/* reflection now..... */
- if (exec->reflect_enabled)
- vty_cmd_reflect_line(vty) ;
+ if (exec->reflect)
+ {
+ ret = vty_cmd_reflect_line(vty) ;
+ if (ret != CMD_SUCCESS)
+ continue ;
+ } ;
/* Pipe work, if any */
if ((parsed->parts & cmd_parts_pipe) != 0)
{
ret = cmd_open_pipes(vty) ;
if (ret != CMD_SUCCESS)
- break ;
+ continue ;
} ;
/* Command execution, if any */
if ((parsed->parts & cmd_part_command) != 0)
- {
- ret = cmd_execute(vty) ;
+ ret = cmd_execute(vty) ;
- if (ret != CMD_SUCCESS)
- {
- /* If ignoring warnings, treat CMD_WARNING as CMD_SUCCESS */
- if (ignore_warning && (ret == CMD_WARNING))
- ret = CMD_SUCCESS ;
-
- /* Treat CMD_CLOSE as CMD_SUCCESS */
- else if (ret == CMD_CLOSE)
- ret = CMD_SUCCESS ;
-
- /* Everything else -> stop */
- else
- break ;
- } ;
- } ;
-
- /* When we get here the last command was CMD_SUCCESS !
- * (Or CMD_WARNING and ignore_warning.)
- *
- * Deals with closing out pipe(s) if required.
- */
- vty_cmd_success(vty) ;
+ /* Deal with success (or suppressed warning). */
+ if ((ret == CMD_SUCCESS) || ((ret == CMD_WARNING) && ignore_warning))
+ ret = vty_cmd_success(vty) ;
} ;
- /* Deal with any errors */
- if (ret == CMD_EOF)
- return CMD_SUCCESS ;
+ /* Arrives here if:
+ *
+ * - vty_cmd_fetch_line() returns anything except CMD_SUCCESS, CMD_EOF or
+ * CMD_HIATUS -- which are not errors.
+ *
+ * - any other operation returns anything except CMD_SUCCESS
+ * (or CMD_WARNING, if they are being ignored), CMD_EOF or CMD_CLOSE.
+ *
+ * Deal with any errors -- generate suitable error messages and close back
+ * to (but excluding) vout_base.
+ *
+ * CMD_SUCCESS and CMD_EMPTY are impossible at this point -- they should
+ * have been dealt with in the loop.
+ *
+ * CMD_EOF is also impossible -- vty_cmd_fetch_line() or vty_cmd_hiatus()
+ * can return that, but that will have been dealt with.
+ *
+ * CMD_CLOSE is also impossible -- commands can return that, but that will
+ * have been dealt with.
+ *
+ * CMD_WAITING is not valid for blocking vio !
+ */
+ assert(ret != CMD_SUCCESS) ;
+ assert(ret != CMD_EMPTY) ;
+ assert(ret != CMD_EOF) ;
+ assert(ret != CMD_CLOSE) ;
+ assert(ret != CMD_WAITING) ;
+
+ vty_cmd_hiatus(vty, ret) ;
return ret ;
} ;
/*==============================================================================
*/
-static qstring cmd_get_pipe_file_name(cmd_parsed parsed, uint ti, uint nt) ;
-
/*------------------------------------------------------------------------------
* Open in and/or out pipes
@@ -352,109 +472,127 @@ cmd_open_pipes(vty vty)
cmd_exec exec = vty->exec ;
cmd_parsed parsed = exec->parsed ;
cmd_return_code_t ret ;
+ vty_io vio ;
+
+ VTY_LOCK() ;
+ vio = vty->vio ;
ret = CMD_SUCCESS ;
+ uty_cmd_depth_mark(vio) ; /* about to push vin and/or vout */
+
/* Deal with any in pipe stuff */
if ((parsed->parts & cmd_part_in_pipe) != 0)
{
- if ((parsed->in_pipe & cmd_pipe_file) != 0)
- {
- qstring name ;
- name = cmd_get_pipe_file_name(parsed, parsed->first_in_pipe,
- parsed->num_in_pipe) ;
+ qstring args ;
- ret = vty_cmd_open_in_pipe_file(vty, name,
- (parsed->in_pipe & cmd_pipe_reflect) != 0) ;
+ args = cmd_tokens_concat(parsed, parsed->first_in_pipe,
+ parsed->num_in_pipe) ;
- qs_reset(name, free_it) ;
- }
+ if ((parsed->in_pipe & cmd_pipe_file) != 0)
+ ret = uty_cmd_open_in_pipe_file(vio, exec->context, args,
+ parsed->in_pipe) ;
else if ((parsed->in_pipe & cmd_pipe_shell) != 0)
- {
- }
+ ret = uty_cmd_open_in_pipe_shell(vio, exec->context, args,
+ parsed->in_pipe) ;
else
zabort("invalid in pipe state") ;
- if (ret != CMD_SUCCESS)
- return ret ;
+ qs_free(args) ;
} ;
/* Deal with any out pipe stuff */
- if ((parsed->parts & cmd_part_out_pipe) != 0)
+ if (((parsed->parts & cmd_part_out_pipe) != 0) && (ret == CMD_SUCCESS))
{
- if ((parsed->out_pipe & cmd_pipe_file) != 0)
- {
- qstring name ;
- name = cmd_get_pipe_file_name(parsed, parsed->first_out_pipe,
- parsed->num_out_pipe) ;
+ qstring args ;
- ret = vty_cmd_open_out_pipe_file(vty, name,
- ((parsed->out_pipe & cmd_pipe_append) != 0)) ;
+ args = cmd_tokens_concat(parsed, parsed->first_out_pipe,
+ parsed->num_out_pipe) ;
- qs_reset(name, free_it) ;
- }
+ if ((parsed->out_pipe & cmd_pipe_file) != 0)
+ ret = uty_cmd_open_out_pipe_file(vio, exec->context, args,
+ parsed->out_pipe) ;
else if ((parsed->out_pipe & cmd_pipe_shell) != 0)
- {
- ret = vty_cmd_open_out_dev_null(vty) ;
- }
+ ret = uty_cmd_open_out_pipe_shell(vio, exec->context, args,
+ parsed->out_pipe) ;
else if ((parsed->out_pipe & cmd_pipe_dev_null) != 0)
- {
- ret = vty_cmd_open_out_dev_null(vty) ;
- }
+ ret = uty_cmd_open_out_dev_null(vio) ;
else
zabort("invalid out pipe state") ;
- if (ret != CMD_SUCCESS)
- return ret ;
+ qs_free(args) ;
} ;
- return CMD_SUCCESS ;
+ VTY_UNLOCK() ;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
- * Get pipe file name
+ * Command Execution.
*
- * Returns a brand new qstring that must be discarded after use.
+ * If !parse_only, set vty->node and dispatch the command.
*
- * Pro tem this just gets the token value !! TODO
- */
-static qstring
-cmd_get_pipe_file_name(cmd_parsed parsed, uint ti, uint nt)
-{
- cmd_token t ;
-
- assert(nt == 2) ;
-
- t = cmd_token_get(parsed->tokens, ti + 1) ;
-
- return qs_copy(NULL, t->qs) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Command Execution
+ * Returns: CMD_SUCCESS -- it all want very well
+ * CMD_WARNING -- not so good: warning message sent by vty_out()
+ * CMD_ERROR -- not so good: warning message sent by vty_out()
+ * CMD_CLOSE -- close the current input
*
- * Returns:
+ * NB: the distinction between CMD_WARNING and CMD_ERROR is that CMD_WARNING
+ * may be ignored when reading a configuration file.
*
- * - have command ready for execution -- CMD_SUCCESS
- * - reached end of command stream -- CMD_CLOSE
- * - encounter error of some kind -- CMD_WARNING, CMD_ERROR, etc
+ * NB: no other returns are acceptable !
*/
extern cmd_return_code_t
cmd_execute(vty vty)
{
- cmd_parsed parsed = vty->exec->parsed ;
- cmd_command cmd = parsed->cmd ;
+ cmd_parsed parsed = vty->exec->parsed ;
+ cmd_context context = vty->exec->context ;
+ cmd_command cmd = parsed->cmd ;
+
cmd_return_code_t ret ;
- node_type_t onode ;
- onode = vty->node ;
vty->node = parsed->cnode ;
- ret = (*(cmd->func))(cmd, vty, cmd_arg_vector_argc(parsed),
- cmd_arg_vector_argv(parsed)) ;
+ if (context->parse_only)
+ ret = CMD_SUCCESS ;
+ else
+ ret = (*(cmd->func))(cmd, vty, cmd_arg_vector_argc(parsed),
+ cmd_arg_vector_argv(parsed)) ;
+
+ if (ret == CMD_SUCCESS)
+ {
+ /* If the node is changed by the command, do that now and make sure
+ * that the configuration symbol of power is straight.
+ *
+ * If the new node is >= CONFIG_NODE, then MUST already have acquired
+ * the symbol of power (otherwise the command would have failed !)
+ *
+ * If the new node is < CONFIG_NODE, then we will here release the
+ * symbol of power iff we are at the vin_base !
+ *
+ * If the new node is NULL_NODE, then treat as CMD_CLOSE.
+ */
+ if (context->node != parsed->nnode)
+ {
+ context->node = parsed->nnode ;
+ vty_cmd_config_lock_check(vty, context->node) ;
+
+ if (context->node == NULL_NODE)
+ ret = CMD_CLOSE ;
+ } ;
- if (((parsed->parts & cmd_part_do) != 0) && (vty->node == ENABLE_NODE))
- vty->node = onode ;
+ /* The command should (no longer) change the vty->node, but if it does,
+ * it had better be to the same as what the parser expected -- for if
+ * not, that will break "parse_only" and generally cause confusion.
+ */
+ qassert((vty->node == parsed->cnode) || (vty->node == parsed->nnode)) ;
+ }
+ else
+ {
+ /* Enforce restrictions on return codes. */
+ assert((ret == CMD_WARNING) || (ret == CMD_ERROR)
+ || (ret == CMD_CLOSE)) ;
+ } ;
return ret ;
} ;