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.c603
1 files changed, 603 insertions, 0 deletions
diff --git a/lib/command_execute.c b/lib/command_execute.c
new file mode 100644
index 00000000..cc457ba3
--- /dev/null
+++ b/lib/command_execute.c
@@ -0,0 +1,603 @@
+/* Command Line Execution
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Recast and extended: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * 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 "misc.h"
+
+#include "command_local.h"
+#include "command_parse.h"
+#include "command_execute.h"
+#include "command_queue.h"
+#include "vty_common.h"
+#include "vty_command.h"
+#include "memory.h"
+
+/*==============================================================================
+ * Construct and destroy cmd_exec object.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Construct cmd_exec object and initialise.
+ *
+ * This is done when a command loop is entered.
+ *
+ * The initial cmd_context reflects the vty->type and initial vty->node.
+ */
+extern cmd_exec
+cmd_exec_new(vty vty)
+{
+ cmd_exec exec ;
+ cmd_context context ;
+
+ exec = XCALLOC(MTYPE_CMD_EXEC, sizeof(struct cmd_exec)) ;
+
+ /* Zeroising has set:
+ *
+ * vty = X -- set below
+ *
+ * action = all zeros
+ *
+ * context = NULL -- see below
+ *
+ * parsed = NULL -- see below
+ *
+ * password_failures = 0 -- none, yet
+ *
+ * state = exec_null
+ * locus = NULL -- not significant in exec_null
+ * ret = CMD_SUCCESS
+ *
+ * cq = NULL -- no mqb (qpthreads)
+ * -- no thread (legacy thread)
+ */
+ 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 ;
+
+ 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make a new context -- returns a completely empty context object.
+ */
+extern cmd_context
+cmd_context_new(void)
+{
+ 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() ;
+
+ *saved = *context ; /* copy as is */
+
+ /* 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Restore given context -- frees the copy restored from.
+ *
+ * Has to free the directories in the context being restored to.
+ *
+ * Returns NULL.
+ */
+extern cmd_context
+cmd_context_restore(cmd_context dst, cmd_context src)
+{
+ assert(src != NULL) ;
+
+ qpath_free(dst->dir_cd) ;
+ qpath_free(dst->dir_home) ;
+ qpath_free(dst->dir_here) ;
+
+ *dst = *src ; /* copy as is */
+
+ return cmd_context_free(src, true) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the given context.
+ *
+ * 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_context
+cmd_context_free(cmd_context context, bool copy)
+{
+ 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Destroy cmd_exec object.
+ */
+extern cmd_exec
+cmd_exec_free(cmd_exec exec)
+{
+ 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 ;
+} ;
+
+/*==============================================================================
+ *
+ */
+/*------------------------------------------------------------------------------
+ * Parse and execute a command.
+ *
+ * The command is given by vty->buf and vty->node.
+ *
+ * Uses vty->parsed.
+ *
+ * -- use exact/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.
+ */
+#if 0
+extern enum cmd_return_code
+cmd_execute_command(struct vty *vty,
+ cmd_parse_type_t type, struct cmd_command **cmd)
+{
+ enum cmd_return_code ret ;
+
+ /* Try to parse in vty->node or, if required, ancestors thereof. */
+ ret = cmd_parse_command(vty->exec->parsed, vty->line, vty->node, type) ;
+
+ if (cmd != NULL)
+ *cmd = vty->exec->parsed->cmd ; /* for vtysh */
+
+ if (ret == CMD_SUCCESS)
+ ret = cmd_dispatch(vty, cmd_may_queue) ;
+
+ return ret ;
+} ;
+#endif
+
+/*------------------------------------------------------------------------------
+ * 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 or I/O operation
+ *
+ * when returns, the entire vin/vout stack will have been closed.
+ *
+ * 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)
+{
+ cmd_exec exec = vty->exec ;
+ cmd_parsed parsed = exec->parsed ;
+ cmd_return_code_t ret;
+
+ ret = CMD_SUCCESS ; /* so far, so good */
+
+ while (1)
+ {
+ /* 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)
+ continue ;
+
+ /* Parse the command line we now have */
+ assert(exec->action->to_do == cmd_do_command) ;
+
+ cmd_tokenize(parsed, exec->action->line, exec->context->full_lex) ;
+ ret = cmd_parse_command(parsed, exec->context) ;
+
+ if (ret != CMD_SUCCESS)
+ continue ;
+
+ /* Special handling before first active line. */
+ if (first_cmd != NULL)
+ {
+ if (first_cmd != parsed->cmd)
+ {
+ ret = (*first_cmd->func)(first_cmd, vty, -1, NULL) ;
+ if (ret != CMD_SUCCESS)
+ continue ;
+ } ;
+ first_cmd = NULL ;
+ } ;
+
+ /* reflection now..... */
+ 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)
+ continue ;
+ } ;
+
+ /* Command execution, if any */
+ if ((parsed->parts & cmd_part_command) != 0)
+ ret = cmd_execute(vty) ;
+
+ /* Deal with success (or suppressed warning). */
+ if ((ret == CMD_SUCCESS) || ((ret == CMD_WARNING) && ignore_warning))
+ ret = vty_cmd_success(vty) ;
+ } ;
+
+ /* 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 ;
+} ;
+
+/*==============================================================================
+ */
+
+/*------------------------------------------------------------------------------
+ * Open in and/or out pipes
+ *
+ * * Returns:
+ *
+ * - OK -- CMD_SUCCESS
+ * - error -- CMD_ERROR, etc
+ */
+extern cmd_return_code_t
+cmd_open_pipes(vty vty)
+{
+ cmd_exec exec = vty->exec ;
+ cmd_parsed parsed = exec->parsed ;
+ cmd_return_code_t ret ;
+ vty_io vio ;
+ bool sync_depth ;
+ VTY_LOCK() ;
+ vio = vty->vio ;
+
+ ret = CMD_SUCCESS ;
+
+ sync_depth = false ; /* assuming no in pipe */
+
+ /* Deal with any in pipe stuff */
+ if ((parsed->parts & cmd_part_in_pipe) != 0)
+ {
+ qstring args ;
+
+ args = cmd_tokens_concat(parsed, parsed->first_in_pipe,
+ parsed->num_in_pipe) ;
+
+ 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") ;
+
+ qs_free(args) ;
+
+ sync_depth = true ;
+ } ;
+
+ /* Deal with any out pipe stuff */
+ if (((parsed->parts & cmd_part_out_pipe) != 0) && (ret == CMD_SUCCESS))
+ {
+ qstring args ;
+
+ args = cmd_tokens_concat(parsed, parsed->first_out_pipe,
+ parsed->num_out_pipe) ;
+
+ 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 = uty_cmd_open_out_pipe_shell(vio, exec->context, args,
+ parsed->out_pipe) ;
+ else if ((parsed->out_pipe & cmd_pipe_dev_null) != 0)
+ ret = uty_cmd_open_out_dev_null(vio) ;
+ else
+ zabort("invalid out pipe state") ;
+
+ if (sync_depth && (ret == CMD_SUCCESS))
+ uty_vout_sync_depth(vio) ;
+
+ qs_free(args) ;
+ } ;
+
+ VTY_UNLOCK() ;
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command Execution.
+ *
+ * If !parse_only, set vty->node and dispatch the command.
+ *
+ * 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
+ *
+ * NB: the distinction between CMD_WARNING and CMD_ERROR is that CMD_WARNING
+ * may be ignored when reading a configuration file.
+ *
+ * NB: no other returns are acceptable !
+ */
+extern cmd_return_code_t
+cmd_execute(vty vty)
+{
+ cmd_parsed parsed = vty->exec->parsed ;
+ cmd_context context = vty->exec->context ;
+ cmd_command cmd = parsed->cmd ;
+
+ cmd_return_code_t ret ;
+
+ vty->node = parsed->cnode ;
+
+ 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 ;
+ } ;
+
+ /* 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 ;
+} ;