summaryrefslogtreecommitdiffstats
path: root/lib/vty_command.c
diff options
context:
space:
mode:
authorChris Hall <chris.hall@highwayman.com>2011-02-13 22:53:14 +0000
committerChris Hall <chris.hall@highwayman.com>2011-02-13 22:53:14 +0000
commit64be6d766a65dc0749d17f5023d714678e9c96a6 (patch)
treef97e04068600d0851687b58d25ff198d1eb6cc73 /lib/vty_command.c
parent8443ca08672e0cf5b779f59db9d556dadf763de7 (diff)
downloadquagga-ex10p.tar.bz2
quagga-ex10p.tar.xz
Initial commit to seed the "pipework" branchex10p
This is a major revision of the command processing, in order to support new lexical level for command lines, plus all the necessary I/O redirection for the pipes. This is version 0.99.15ex10p. This supports: < filename <+ filename .... > filename .... >> filename .... >* Also contains all lexical level handling of '...', "...." and \x in order to allow use of '>' et al if required. Updated command line completion and help is a work in progress.
Diffstat (limited to 'lib/vty_command.c')
-rw-r--r--lib/vty_command.c863
1 files changed, 863 insertions, 0 deletions
diff --git a/lib/vty_command.c b/lib/vty_command.c
new file mode 100644
index 00000000..26a9ec6a
--- /dev/null
+++ b/lib/vty_command.c
@@ -0,0 +1,863 @@
+/* VTY for command execution
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: 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 "stdio.h"
+
+#include "vty_command.h"
+#include "command_local.h"
+#include "command_queue.h"
+#include "command_execute.h"
+#include "command_parse.h"
+#include "vty_local.h"
+#include "vty_io.h"
+#include "vty_cli.h"
+#include "vio_fifo.h"
+#include "vty_io_file.h"
+#include "list_util.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * Variables etc.
+ */
+
+/*------------------------------------------------------------------------------
+ * Prototypes
+ */
+static void uty_show_error_location(vio_vf vf) ;
+static void vty_cmd_failed(vty vty, cmd_return_code_t ret) ;
+
+/*==============================================================================
+ * There are two command loops -- cmd_read_config() and cq_process(). These
+ * functions support those command loops:
+ *
+ * * uty_cmd_prepare()
+ *
+ * After opening or closing a vin/vout object, the state of the execution
+ * context must be set to reflect the current top of the vin/vout stack.
+ *
+ * * uty_cmd_dispatch_line()
+ *
+ * This is for command lines which come from "push" sources, eg the
+ * Telnet VTY CLI or the VTYSH.
+ *
+ * Hands command line over to the vty_cli_nexus message queue.
+ *
+ * NB: from this point onwards, the vio is vio->cmd_running !
+ *
+ * * vty_cmd_fetch_line()
+ *
+ * This is for command lines which are "pulled" by one of the command
+ * execution loops, eg files and pipes. If not in cmd_read_config())
+ * can return CMD_WAITING.
+ *
+ * * vty_cmd_open_in_pipe_file()
+ * * vty_cmd_open_in_pipe_shell()
+ * * vty_cmd_open_out_pipe_file()
+ * * vty_cmd_open_out_pipe_shell()
+ *
+ * These do the I/O side of the required opens, pushing the new vty_vf
+ * onto the vin/vout stack.
+ *
+ * * vty_cmd_success()
+ *
+ * When a command returns success, this is called either to push all
+ * buffered output to the current vout, or to discard all buffered
+ * output (which is what cmd_read_config() does).
+ *
+ * If required, pops any vout(s).
+ *
+ * * vty_cmd_loop_exit()
+ *
+ * When a command has completed, successfully or otherwise, this is
+ * called to deal with any errors and return control to.
+ *
+ *
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * After opening or closing a vin/vout object, update the vty->exec context.
+ */
+extern void
+uty_cmd_prepare(vty_io vio)
+{
+ cmd_exec exec = vio->vty->exec ;
+
+ exec->parse_type = vio->vin->parse_type ;
+
+ exec->out_enabled = vio->vout->out_enabled ;
+ exec->reflect_enabled = vio->vin->reflect_enabled &&
+ vio->vout->out_enabled ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * This pushes the command line into the vty_cli_message queue, where it will
+ * be parsed and executed etc.
+ *
+ * NB: from this moment on, vio->cmd_running is true, so even if the vty is
+ * closed, it cannot be fully closed and then reaped until the flag
+ * is cleared.
+ *
+ * While vio->cmd_running the command side is responsible for the vty
+ * and the vty->exec stuff.
+ */
+extern cmd_return_code_t
+uty_cmd_dispatch(vty_io vio, cmd_do_t to_do, qstring line)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ vio->cmd_running = true ;
+ uty_cmd_prepare(vio) ; /* make sure */
+
+ cq_dispatch(vio->vty, to_do, line) ;
+
+ return CMD_WAITING ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Handle a "special" command -- anything not cmd_do_command
+ */
+extern cmd_return_code_t
+vty_cmd_special(vty vty)
+{
+ cmd_return_code_t ret ;
+
+ ret = CMD_SUCCESS ;
+ /* All other nodes... */
+ switch (vty->exec->to_do)
+ {
+ case cmd_do_nothing:
+ break ;
+
+ case cmd_do_eof:
+ ret = CMD_CLOSE ;
+ break ;
+
+ case cmd_do_command:
+ zabort("invalid cmd_do_command") ;
+ break ;
+
+ case cmd_do_ctrl_d:
+ zabort("invalid cmd_do_ctrl_d") ;
+ break ;
+
+ case cmd_do_ctrl_c:
+ case cmd_do_ctrl_z:
+ ret = cmd_end(vty) ;
+ break ;
+
+ default: /* must now be cmd_do_auth or cmd_do_auth_enable */
+ VTY_LOCK() ;
+ ret = uty_cli_auth(vty->vio->vin->cli) ;
+ VTY_UNLOCK() ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Fetch the next command line to be executed.
+ *
+ * If possible, get another command line to execute -- pops pipes as required
+ * and reflects command line when have one, if required.
+ *
+ * Action depends on the type of the current input:
+ *
+ * VIN_NONE invalid
+ *
+ * VIN_TERM ) return CMD_EOF
+ * VIN_VTYSH )
+ *
+ * VIN_FILE )
+ * VIN_PIPE ) fetch another line if can.
+ * VIN_CONFIG )
+ *
+ * VIN_DEV_NULL nothing: return CMD_EOF
+ *
+ * Returns: CMD_SUCCESS => OK -- have a line ready to be processed.
+ *
+ * vty->exec->line points at the line.
+ *
+ * or: CMD_EOF => nothing to fetch -- note that if pops back to the
+ * VIN_TERM or VIN_VTYSH level, then will return
+ * CMD_EOF.
+ *
+ * If was VIN_TERM or VIN_VTYSH, signals CMD_EOF to
+ * exit from the command loop.
+ *
+ * or: CMD_WAITING => OK -- but waiting for command line to arrive.
+ *
+ * or: ....
+ */
+extern cmd_return_code_t
+vty_cmd_fetch_line(vty vty)
+{
+ cmd_return_code_t ret ;
+ vty_io vio ;
+ qstring* p_line ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ; /* once locked */
+ p_line = &vty->exec->line ;
+
+ *p_line = NULL ;
+ vty->exec->to_do = cmd_do_nothing ;
+
+ while (1)
+ {
+ vio_vf vf ;
+
+ vf = vio->vin ;
+
+ if (vf->vin_state != vf_open)
+ {
+ switch (vf->vin_state)
+ {
+ case vf_closed:
+ case vf_eof:
+ ret = CMD_EOF ;
+ break ;
+
+ case vf_error:
+ ret = CMD_IO_ERROR ;
+ break ;
+
+ case vf_closing:
+ ret = CMD_CLOSE ;
+ break ;
+
+ default:
+ zabort("invalid vf->vin_state") ;
+ break ;
+ } ;
+ break ;
+ } ;
+
+ switch (vf->vin_type)
+ {
+ case VIN_NONE:
+ zabort("invalid VIN_NONE") ;
+ break ;
+
+ case VIN_TERM:
+ case VIN_VTYSH:
+ case VIN_DEV_NULL:
+ ret = CMD_EOF ;
+ break ;
+
+ /* fall through */
+
+ case VIN_FILE:
+ case VIN_PIPE:
+ case VIN_CONFIG:
+ ret = uty_file_fetch_command_line(vf, p_line) ;
+
+ if (ret == CMD_SUCCESS)
+ {
+ vty->exec->to_do = cmd_do_command ;
+ }
+ else if ((ret == CMD_EOF) && (vio->vin_depth > 0))
+ {
+ uty_vin_close(vio) ;
+ uty_cmd_prepare(vio) ;
+ continue ;
+ } ;
+ break ;
+
+ default:
+ zabort("unknown vin_type") ;
+ } ;
+
+ break ;
+ } ;
+
+ VTY_UNLOCK() ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reflect the command line to the current vio->obuf.
+ *
+ * Advances the end_mark past the reflected line, so that output (in particular
+ * error stuff) is separate.
+ */
+extern void
+vty_cmd_reflect_line(vty vty)
+{
+ vio_fifo obuf ;
+ qstring line ;
+
+ VTY_LOCK() ;
+
+ obuf = vty->vio->obuf ; /* once locked */
+ line = vty->exec->line ;
+
+ vio_fifo_put_bytes(obuf, qs_char_nn(line), qs_len_nn(line)) ;
+ vio_fifo_put_byte(obuf, '\n') ;
+
+ uty_out_accept(vty->vio) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open the given file as in in pipe, if possible.
+ *
+ * Puts error messages to vty if fails.
+ */
+extern cmd_return_code_t
+vty_cmd_open_in_pipe_file(vty vty, qstring name, bool reflect)
+{
+ cmd_return_code_t ret ;
+
+ VTY_LOCK() ;
+
+ ret = uty_file_read_open(vty->vio, name, reflect) ;
+
+ if (ret == CMD_SUCCESS)
+ uty_cmd_prepare(vty->vio) ;
+
+ VTY_UNLOCK() ;
+
+ return ret ;
+} ;
+
+extern cmd_return_code_t
+vty_cmd_open_in_pipe_shell(vty vty)
+{
+ return CMD_SUCCESS ;
+} ;
+
+extern cmd_return_code_t
+vty_cmd_open_out_pipe_file(vty vty, qstring name, bool append)
+{
+ cmd_return_code_t ret ;
+
+ VTY_LOCK() ;
+
+ ret = uty_file_write_open(vty->vio, name, append) ;
+
+ if (ret == CMD_SUCCESS)
+ uty_cmd_prepare(vty->vio) ;
+
+ VTY_UNLOCK() ;
+
+ return ret ;
+} ;
+
+extern cmd_return_code_t
+vty_cmd_open_out_dev_null(vty vty)
+{
+ vio_vf vf ;
+
+ VTY_LOCK() ;
+
+ vf = uty_vf_new(vty->vio, "dev_null", -1, vfd_none, vfd_io_none) ;
+ uty_vout_open(vty->vio, vf, VOUT_DEV_NULL, NULL, NULL, 0) ;
+
+ vf->out_enabled = false ;
+
+ VTY_UNLOCK() ;
+ return CMD_SUCCESS ;
+} ;
+
+extern cmd_return_code_t
+vty_cmd_open_out_pipe_shell(vty vty)
+{
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command has completed successfully.
+ *
+ * An output generated by the command is now pushed if exec->out_enabled,
+ * or discarded.
+ *
+ * If the vin_depth < vout_depth, then close vouts until the depth is equal.
+ */
+extern cmd_return_code_t
+vty_cmd_success(vty vty)
+{
+ vty_io vio ;
+
+ VTY_LOCK() ;
+ vio = vty->vio ; /* once locked */
+
+ if (!vio_fifo_empty_tail(vio->obuf))
+ {
+ if (vty->exec->out_enabled)
+ uty_cmd_out_push(vio) ;
+ else
+ uty_out_clear(vio) ;
+ } ;
+
+ /* Deal with closing out pipe on individual command */
+ while (vio->vout_depth > vio->vin_depth)
+ {
+ uty_vout_close(vio, false) ; /* ignore failures TODO */
+ uty_cmd_prepare(vio) ; /* set new state */
+ } ;
+
+ VTY_UNLOCK() ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is anything after the end_mark, push it to be written, now.
+ *
+ * See uty_cmd_out_push() below.
+ */
+extern void
+vty_cmd_out_push(vty vty)
+{
+ VTY_LOCK() ;
+ uty_cmd_out_push(vty->vio) ;
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is anything after the end_mark, push it to be written, now.
+ *
+ * For most outputs we kick the I/O side so that the pselect processing deals
+ * with the required write. For VOUT_STDERR and VOUT_STDOUT, output goes
+ * directly to standard I/O.
+ *
+ * Advances the end_mark past the stuff pushed.
+ *
+ * NB: takes no notice of vf->out_enabled... this is used when outputting
+ * any error messages when reading the configuration file.
+ *
+ * TODO: worry about closing state !
+ */
+extern void
+uty_cmd_out_push(vty_io vio)
+{
+ cmd_return_code_t ret ;
+ vio_vf vf ;
+
+ vf = vio->vout ;
+
+ assert(vio->obuf == vf->obuf) ;
+
+ uty_out_accept(vio) ; /* advance the end mark */
+
+ if (vf->vout_state != vf_open)
+ {
+ switch (vf->vout_state)
+ {
+ case vf_closed:
+ case vf_eof:
+ ret = CMD_EOF ; /* TODO what's to do ? */
+ break ;
+
+ case vf_error:
+ ret = CMD_IO_ERROR ;
+ break ;
+
+ case vf_closing:
+ ret = CMD_CLOSE ; /* TODO continue with output ? */
+ break ;
+
+ default:
+ zabort("invalid vf->vout_state") ;
+ break ;
+ } ;
+
+ return ; /* TODO errors ?? */
+ } ;
+
+ switch (vio->vout->vout_type)
+ {
+ case VOUT_NONE:
+ zabort("invalid vout_none") ;
+ break ;
+
+ case VOUT_TERM:
+ uty_cli_out_push(vf->cli) ;
+ break ;
+
+ case VOUT_VTYSH:
+ /* Kick the writer */
+ break ;
+
+ case VOUT_FILE:
+ uty_file_out_push(vf) ;
+ break ;
+
+ case VOUT_PIPE:
+ /* Kick the writer */
+ break ;
+
+ case VOUT_CONFIG:
+ /* Kick the writer */
+ break ;
+
+ case VOUT_DEV_NULL:
+ uty_out_clear(vio) ; /* clear fifo, keep end mark */
+ break ;
+
+ case VOUT_STDOUT:
+ vio_fifo_fwrite(vio->obuf, stdout) ;
+ break ;
+
+ case VOUT_STDERR:
+ vio_fifo_fwrite(vio->obuf, stderr) ;
+ break ;
+
+ default:
+ zabort("unknown vout_type") ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The command loop has exited, with the given return code.
+ *
+ * There are a number of return codes that are treated as success. On any
+ * of those, does vty_cmd_success() (which has no effect if already done).
+ *
+ * If not a success return code, then close down any pipe-work, and create
+ * suitable error message.
+ *
+ * If is any form of success, returns CMD_SUCCESS -- otherwise returns the
+ * return code as given.
+ */
+extern cmd_return_code_t
+vty_cmd_loop_exit(vty vty, cmd_return_code_t ret)
+{
+ bool close ;
+
+ /* Deal with the several names of success and the many names of failure.
+ */
+ close = false ;
+
+ switch(ret)
+ {
+ case CMD_CLOSE:
+ case CMD_EOF:
+ close = true ;
+ fall_through ;
+
+ case CMD_SUCCESS:
+ case CMD_EMPTY:
+ ret = vty_cmd_success(vty) ;
+ break ;
+
+ default:
+ /* If not CMD_SUCCESS, the treat as some sort of error:
+ *
+ * * close down all pipes -- leaving just the vin_base/vout_base.
+ * * create error messages in the vout_base.
+ */
+ vty_cmd_failed(vty, ret) ;
+ break ;
+ } ;
+
+ /* They think it's all over... */
+
+ VTY_LOCK() ;
+
+ vty->vio->cmd_running = false ;
+
+ if (vty->vio->vin->vin_type == VIN_TERM)
+ uty_cli_done_command(vty->vio->vin->cli, vty->node) ;
+
+ /* ... it is now. */
+
+ if (close)
+ uty_close(vty->vio, false, NULL) ;
+
+ VTY_UNLOCK() ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Dealing with error of some kind.
+ *
+ * Error is reported on the vout_base. Whatever the command has written
+ * so far will be appended to the reporting of the error location.
+ *
+ * If there is only one vout, then:
+ *
+ * - if is VOUT_TERM, then need to wipe the command line. The failing
+ * line will be the last output line on screen (unless monitor output,
+ * which can do nothing about).
+ *
+ * - if is VOUT_VTYSH, then the failing line will be the last output line
+ * on the screen.
+ *
+ * - if is VOUT_xxx, then nothing will have been reflected.
+ *
+ * In any event, need to separate out any output from the failing command,
+ * so that can report error location and type, before showing the error
+ * message(s).
+ *
+ * NB: does not expect to see all the possible CMD_XXX return codes (see
+ * below), but treats all as a form of error !
+ */
+static void
+vty_cmd_failed(vty vty, cmd_return_code_t ret)
+{
+ vty_io vio ;
+ vio_fifo tbuf ;
+ ulen eloc ;
+
+ VTY_LOCK() ;
+ vio = vty->vio ; /* once locked */
+
+ /* Copy command output to temporary, and remove from the obuf.
+ *
+ * Any command output should be messages explaining the error, and we want
+ * those (a) on the base vout, and (b) after description of where the error
+ * occurred.
+ */
+ tbuf = vio_fifo_copy_tail(NULL, vio->obuf) ;
+ vio_fifo_back_to_end_mark(vio->obuf, true) ;
+
+ /* Can now close everything in the vout stack, so all further output
+ * is to the vout_base.
+ */
+ uty_vout_close_stack(vio, false) ; /* not "final" */
+
+ /* Process the vin stack to generate the error location(s) */
+ uty_show_error_location(vio->vin) ;
+
+ if (vio->vin->vin_type == VIN_TERM)
+ eloc = uty_cli_prompt_len(vio->vin->cli) ;
+ else
+ eloc = 0 ;
+
+ /* Can now close everything in the vin stack */
+ uty_vin_close_stack(vio) ;
+
+ /* Now any additional error message if required */
+ switch (ret)
+ {
+ qstring qs ;
+
+ case CMD_WARNING:
+ if (vio_fifo_empty(tbuf))
+ uty_out(vio, "%% WARNING: non-specific warning\n") ;
+ break ;
+
+ case CMD_ERROR:
+ if (vio_fifo_empty(tbuf))
+ uty_out(vio, "%% ERROR: non-specific error\n") ;
+ break ;
+
+ case CMD_IO_ERROR:
+ break ;
+
+ case CMD_ERR_PARSING:
+ qs = qs_set_fill(NULL, eloc + vio->vty->exec->parsed->eloc, "....") ;
+ uty_out(vio, "%s^\n%% %s\n", qs_string(qs),
+ vio->vty->exec->parsed->emess) ;
+ qs_reset(qs, free_it) ;
+ break ;
+
+ case CMD_ERR_NO_MATCH:
+ uty_out(vio, "%% Unknown command.\n") ;
+ break;
+
+ case CMD_ERR_AMBIGUOUS:
+ uty_out(vio, "%% Ambiguous command.\n");
+ break;
+
+ case CMD_ERR_INCOMPLETE:
+ uty_out(vio, "%% Command incomplete.\n");
+ break;
+
+ case CMD_SUCCESS:
+ case CMD_SUCCESS_DAEMON:
+ case CMD_CLOSE:
+ case CMD_EMPTY:
+ case CMD_WAITING:
+ case CMD_EOF:
+ case CMD_COMPLETE_FULL_MATCH:
+ case CMD_COMPLETE_MATCH:
+ case CMD_COMPLETE_LIST_MATCH:
+ case CMD_COMPLETE_ALREADY:
+ default:
+ uty_out(vio, "%% Unexpected return code (%d).\n", (int)ret) ;
+ break ;
+ } ;
+
+ /* Now stick the tbuf onto the end of the output & discard tbuf. */
+ vio_fifo_copy(vio->obuf, tbuf) ;
+ vio_fifo_reset(tbuf, free_it) ;
+
+ uty_cmd_out_push(vio) ;
+
+ VTY_UNLOCK() ;
+} ;
+
+static void
+uty_show_error_location(vio_vf vf)
+{
+ vio_vf vf_next ;
+
+ /* Recurse until hit end of the vin stack */
+ vf_next = ssl_next(vf, vin_next) ;
+
+ if (vf_next != NULL)
+ uty_show_error_location(vf_next) ;
+ else
+ assert(vf == vf->vio->vin_base) ;
+
+ /* On the way back, add the error location for each vin entry */
+ switch (vf->vin_type)
+ {
+ case VIN_NONE:
+ zabort("invalid VIN_NONE") ;
+ break ;
+
+ case VIN_TERM:
+ /* Wipe the current command line */
+// uty_term_show_error_location(vf, loc) ;
+ break ;
+
+ case VIN_VTYSH:
+ break ;
+
+ case VIN_FILE:
+
+ case VIN_PIPE:
+
+ case VIN_CONFIG:
+ uty_out(vf->vio, "%% on line %d of %s:\n", vf->line_number, vf->name) ;
+ uty_out(vf->vio, "%s\n", qs_make_string(vf->cl)) ;
+ break ;
+
+ case VIN_DEV_NULL:
+ break ;
+
+ default:
+ zabort("unknown vin_type") ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Configuration node/state handling
+ *
+ * At most one VTY may hold the configuration symbol of power at any time.
+ */
+
+/*------------------------------------------------------------------------------
+ * Attempt to gain the configuration symbol of power
+ *
+ * If succeeds, set the given node.
+ *
+ * Returns: true <=> now own the symbol of power.
+ */
+extern bool
+vty_config_lock (struct vty *vty, enum node_type node)
+{
+ bool result;
+
+ VTY_LOCK() ;
+
+ if (!host.config)
+ {
+ vty->config = true ;
+ host.config = true ;
+ } ;
+
+ result = vty->config;
+
+ if (result)
+ vty->node = node ;
+
+ VTY_UNLOCK() ;
+
+ return result;
+}
+
+/*------------------------------------------------------------------------------
+ * Give back the configuration symbol of power -- if own it.
+ *
+ * Set the given node -- which must be <= MAX_NON_CONFIG_NODE
+ */
+extern void
+vty_config_unlock (struct vty *vty, enum node_type node)
+{
+ VTY_LOCK() ;
+ uty_config_unlock(vty, node);
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Give back the configuration symbol of power -- if own it.
+ *
+ * Set the given node -- which must be <= MAX_NON_CONFIG_NODE
+ */
+extern void
+uty_config_unlock (struct vty *vty, node_type_t node)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (host.config && vty->config)
+ {
+ vty->config = false ;
+ host.config = false ;
+ }
+
+ assert(node <= MAX_NON_CONFIG_NODE) ;
+ vty->node = node ;
+} ;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*------------------------------------------------------------------------------
+ * Result of command is to close the input.
+ *
+ * Posts the reason for the close.
+ *
+ * Returns: CMD_CLOSE
+ */
+extern cmd_return_code_t
+uty_cmd_close(vty_io vio, const char* reason)
+{
+ vio->close_reason = qs_set(NULL, reason) ;
+ return CMD_CLOSE ;
+} ;
+
+