summaryrefslogtreecommitdiffstats
path: root/lib/command_queue.c
diff options
context:
space:
mode:
authorChris Hall <chris.hall@highwayman.com>2011-07-21 19:53:02 +0100
committerChris Hall <chris.hall@highwayman.com>2011-07-21 19:53:02 +0100
commit56da2a1c9b6361e302b7a39fe2740561a9012d88 (patch)
tree6b6543532133a0c618d0f4ec70a87cf3f96caf30 /lib/command_queue.c
parente535bc959729262480a9702e71334002edee3f8c (diff)
downloadquagga-56da2a1c9b6361e302b7a39fe2740561a9012d88.tar.bz2
quagga-56da2a1c9b6361e302b7a39fe2740561a9012d88.tar.xz
Update pipework and improve memory reporting.
Improve error handling for all new pipework inputs and outputs. Change behaviour of ^C from VTY Terminal, so that will interrupt output and terminate all running pipes -- including running shell commands. In pipe commands, recognise "~/..." and "~user/..." home directory forms. Changed "~/" to mean the usual home for the current user. "~~/" now means the configuration file directory. Introduced "shdir DIR" command to show what is (currently) what. Changed "<|" so that if the command has a path, it is expanded using Quagga's rules (including "~~/" and "~./") and the "here" directory is set to that path. Fixed collection of stderr output from all pipes so that is separate from stdout output, and is always sent to the base output (eg VTY Terminal). Increase amount of information about the heap that "show mem" shows -- particularly if the "memory_tracker" is enabled. Tested and applied resulting fixes.
Diffstat (limited to 'lib/command_queue.c')
-rw-r--r--lib/command_queue.c186
1 files changed, 110 insertions, 76 deletions
diff --git a/lib/command_queue.c b/lib/command_queue.c
index 7946d0e8..b80b2c9f 100644
--- a/lib/command_queue.c
+++ b/lib/command_queue.c
@@ -33,78 +33,77 @@
/*==============================================================================
* This command loop processes commands for VTY_TERMINAL and VTY_SHELL_SERVER.
*
- * Commands appear from those sources, driven by socket I/O. Once a command
- * line is ready it is passed out of the I/O pselect/select driven code
- * as a message, and enters the command loop.
+ * The command loop is a form of co-routine, which is "called" by sending a
+ * message (or by legacy threads events). In the multi-pthread world, the
+ * command loop may run in either the vty_cli_nexus or the vty_cmd_nexus, and
+ * may be passed from one to the other by sending a message.
*
- * If pipes are involved that is all dealt with in the command loop, which
- * runs until the vin/vout stacks return to 0 -- the VTY_TERMINAL and
- * VTY_SHELL_SERVER.
+ * The command loop is usually in one of three states:
*
- * There are further issues: fix to current state TODO
+ * vc_running -- the command loop is running, doing things.
*
- * 1) in the qpthreads world, commands are parsed in the CLI thread, but most
- * are executed in the Routing thread. So parsed commands are passed, by
- * message between threads.
+ * In this state the vty and the vty->exec are in the hands
+ * of the command loop.
*
- * 2) input is expected to be non-blocking -- so the command loop will
- * exit if a command line cannot be delivered immediately, and will be
- * returned to later.
+ * The vty->vio is always accessed under VTY_LOCK().
*
- * 3) while a VTY is in the command loop it is marked vio->cmd_running. TODO
+ * Note that one of the things the command loop may be
+ * doing is waiting for a message to be picked up (or
+ * a legacy thread event to be dispatched).
*
- * While that is true, the vty and the vty->exec are in the hands
- * of the command loop. The vty->vio is always accessed under VTY_LOCK().
+ * vc_waiting -- the command loop is waiting for something to happen
+ * (typically some I/O), and will stay there until a
+ * message is sent (or a legacy threads event).
*
- * 4) opening pipes is done in the CLI thread, in case of any possible
- * blocking.
+ * To be waiting the command loop must have entered "hiatus"
+ * and then exited.
*
- * 5) all output is to fifo buffers -- when output is pushed the CLI side
- * is kicked to manage all output via pselect/select.
+ * When a command loop is or has been stopped, to goes to vc_stopped state.
*
- * In vty_io_basic() it is possible to set read/write ready and associated
- * timeouts while running in the CMD thread, but that too depends on the
- * passing of messages to the CLI thread.
+ * There are further issues:
*
- * The smooth running of the command handling depends on the continued
- * running of the CLI thread.
+ * * In the mult-pthreaded world, commands may be parsed in either pthread,
+ * but most are executed in the Routing thread. So may switch pthreads
+ * after parsing a command -- by sending a message.
*
- * To close a VTY must (eventually) arrange for vio->cmd_running to be cleared.
- * While a vty is vio->cmd_running, it must be in one of these states: TODO
+ * * opening pipes is done in the vty_cli_nexus, in case of any possible
+ * blocking and because the pselect() stuff is all done in the
+ * vty_cli_nexus.
*
- * - on the vty_cli_nexus queue (or the combined queue)
- * - executing in the vty_cli_nexus (or the single "both" nexus)
- * - on the vty_cmd_nexus queue (if different)
- * - executing in the vty_cmd_nexus (if different)
- *
- * Or, in the legacy threads world:
+ * * all output is via fifo buffers -- when output is pushed, as much as
+ * possible is done immediately, but if necessary the pselect() process
+ * (in the vty_cli_nexus) is left to keep things moving.
*
- * - on the event queue
- * - executing
+ * In vty_io_basic() it is possible to set read/write ready and associated
+ * timeouts while running in the vty_cmd_nexus, but note that this is done
+ * by sending messages to the CLI thread (if multi-pthreaded).
*
- * Where there is only one pthread (and in the legacy threads world) things are
- * easy.
+ * The smooth running of the command handling depends on the continued
+ * running of the CLI thread.
*
+ * To close a VTY must (eventually) arrange for the state to not be vc_running.
+ * While a vty is vc_running, the command loop may be in either nexus, and
+ * may be:
*
- * This revoke runs in the CLI thread, and will catch the message if it is
- * on either queue, and vty_revoke() will deal with it -- still in the CLI
- * thread.
+ * - on the vty_cli_nexus queue (or the combined queue)
+ * - executing cq_process() in the vty_cli_nexus (or the single "both" nexus)
+ * - on the vty_cmd_nexus queue (if different)
+ * - executing cq_process() in the vty_cmd_nexus (if different)
*
- * The command cannot be running in the CLI thread, since that is where we
- * are !
+ * where being on the queue means that there is a message waiting to be
+ * picked up on that queue, which will end up calling cq_process().
*
- * That leaves the command running in the CMD thread. That will run to
- * completion... the VTY may be closed in the meantime, which will shut down
- * the reading side, so the command loop will come to a halt quite quickly.
- * Note, however, that the smooth running of this process requires the CLI
- * thread and its messages to be
+ * Or, in the legacy threads world:
*
+ * - on the event queue -- waiting for event handler to call cq_process()
+ * - executing cq_process()
*
+ * To bring the command loop to a stop will call cq_revoke to revoke any
+ * pending message or pending legacy event. If that succeeds, then the
+ * command loop can be stopped immediately. If not, then vc_running implies
+ * it is executing in cq_process() in one of the nexuses.
*/
-
-
-
/*------------------------------------------------------------------------------
* Prototypes
*/
@@ -127,7 +126,7 @@ cq_loop_enter(vty vty, cmd_return_code_t ret)
{
VTY_ASSERT_CLI_THREAD() ;
- assert(vty->exec->state == exec_null) ;
+ qassert(vty->exec->state == exec_null) ;
cq_enqueue(vty, vty_cli_nexus, exec_done_cmd, ret) ;
} ;
@@ -140,7 +139,7 @@ cq_continue(vty vty, cmd_return_code_t ret)
{
VTY_ASSERT_CLI_THREAD() ;
- assert(vty->exec->state == exec_hiatus) ;
+ qassert(vty->exec->state == exec_hiatus) ;
cq_enqueue(vty, vty_cli_nexus, exec_hiatus, ret) ;
} ;
@@ -150,6 +149,8 @@ cq_continue(vty vty, cmd_return_code_t ret)
*
* Will execute in the current exec->state, passing in the given return
* code.
+ *
+ * Note that the vio->state *must* be vc_running.
*/
static void
cq_enqueue(vty vty, qpn_nexus dst, cmd_exec_state_t state,
@@ -157,7 +158,7 @@ cq_enqueue(vty vty, qpn_nexus dst, cmd_exec_state_t state,
{
cmd_exec exec = vty->exec ;
- assert((dst == vty_cli_nexus) || (dst == vty_cmd_nexus)) ;
+ qassert((dst == vty_cli_nexus) || (dst == vty_cmd_nexus)) ;
exec->locus = dst ;
exec->state = state ;
@@ -175,7 +176,7 @@ cq_enqueue(vty vty, qpn_nexus dst, cmd_exec_state_t state,
}
else
{
- assert(vty_cli_nexus == vty_cmd_nexus) ;
+ qassert(vty_cli_nexus == vty_cmd_nexus) ;
exec->cq.thread = thread_add_event(vty_master, cq_thread, vty, 0) ;
} ;
} ;
@@ -183,7 +184,11 @@ cq_enqueue(vty vty, qpn_nexus dst, cmd_exec_state_t state,
/*------------------------------------------------------------------------------
* Deal with command message -- in the qpthreads world.
*
- * Note that if the command is revoked.... TODO
+ * Note that if the message is revoked, then the state is set vc_stopped.
+ * We revoke when stopping a command loop -- see cq_revoke, below. If the
+ * message is revoked at any other time then the command loop is, indeed,
+ * stopped -- the I/O side may plow on until buffers empty, but the
+ * command loop will not cq_continue(). At
*/
static void
cq_action(mqueue_block mqb, mqb_flag_t flag)
@@ -194,33 +199,35 @@ cq_action(mqueue_block mqb, mqb_flag_t flag)
vty = mqb_get_arg0(mqb);
- assert(vty->exec->cq.mqb == mqb) ;
+ qassert(vty->exec->cq.mqb == mqb) ;
if (flag == mqb_action)
- return cq_process(vty, vty->exec->state, vty->exec->ret) ;
+ cq_process(vty, vty->exec->state, vty->exec->ret) ;
/* do not touch vty on way out */
+ else
+ {
+ /* Revoke action. */
+ mqb_free(mqb) ;
+ vty->exec->cq.mqb = NULL ;
- /* Revoke action. */
- mqb_free(mqb) ;
- vty->exec->cq.mqb = NULL ;
+ vty_cmd_set_stopped(vty) ; /* enforced stop of loop */
+ } ;
} ;
/*------------------------------------------------------------------------------
* Deal with command message -- in the legacy threads world.
- *
- * Note that if the command is revoked.... TODO
*/
static int
cq_thread(struct thread* thread)
{
vty vty = THREAD_ARG(thread) ;
- assert(vty->exec->cq.thread == thread) ;
+ qassert(vty->exec->cq.thread == thread) ;
vty->exec->cq.thread = NULL ;
cq_process(vty, vty->exec->state, vty->exec->ret) ;
- /* do not touch vty on way out */
+ /* do not touch vty on way out */
return 0 ;
} ;
@@ -248,7 +255,7 @@ cq_thread(struct thread* thread)
*
* CMD_SUCCESS => can proceed to fetch the next command
* CMD_WAITING => must leave the command loop, waiting for I/O
- * CMD_EOF => close the vty and leave command loop
+ * CMD_STOP => close the vty and leave command loop
* CMD_IO_ERROR => loop back and deal with the error
*
* The cq_loop_enter() and cq_continue() operations send a message (to the
@@ -261,7 +268,11 @@ cq_thread(struct thread* thread)
* life simple, switches back to the cli thread if ends up waiting for I/O.)
*
* The vty_io_basic stuff allows the multi-pthread vty_cmd_nexus to set read
- * and/or write ready state -- which may generate messages to the vty_cli_nexus.
+ * and/or write ready state -- which may generate messages to the
+ * vty_cli_nexus.
+ *
+ * NB: on exit from cq_process the VTY may have been closed down and released,
+ * so do NOT depend on its existence.
*/
static void
cq_process(vty vty, cmd_exec_state_t state, cmd_return_code_t ret)
@@ -341,7 +352,7 @@ cq_process(vty vty, cmd_exec_state_t state, cmd_return_code_t ret)
ret = vty_cmd_reflect_line(vty) ;
if ((ret != CMD_SUCCESS) && (ret != CMD_WAITING))
- break ;
+ break ; /* CMD_IO_ERROR or CMD_HIATUS */
} ;
fall_through ;
@@ -447,20 +458,28 @@ cq_process(vty vty, cmd_exec_state_t state, cmd_return_code_t ret)
/*--------------------------------------------------------------------
* Hiatus state -- some return code to be dealt with !
+ *
+ * If we are not in the vty_cli_nexus, then must pass the problem
+ * to the vty_cli_nexus. If the return code is CMD_STOP, or there
+ * is a CMD_STOP signal, then drop the config symbol of power -- see
+ * uty_close().
*/
case exec_hiatus:
if (exec->locus != vty_cli_nexus)
- return cq_enqueue(vty, vty_cli_nexus, exec_hiatus, ret) ;
+ {
+ vty_cmd_check_stop(vty, ret) ;
+ return cq_enqueue(vty, vty_cli_nexus, exec_hiatus, ret) ;
+ } ;
while (1)
{
- /* Let vty_cmd_hiatus() deal with the return code, and adjust
- * the stack as required.
+ /* Let vty_cmd_hiatus() deal with the return code and/or any
+ * stop/error/etc trap, and adjust the stack as required.
*/
ret = vty_cmd_hiatus(vty, ret) ;
if (ret == CMD_SUCCESS)
- break ;
+ break ; /* back to exec_fetch */
if (ret == CMD_WAITING)
{
@@ -468,7 +487,10 @@ cq_process(vty vty, cmd_exec_state_t state, cmd_return_code_t ret)
return ; /* <<< DONE, pro tem */
} ;
- if (ret == CMD_EOF)
+ if (ret == CMD_IO_ERROR)
+ continue ; /* give error back to hiatus */
+
+ if (ret == CMD_STOP)
{
exec->state = exec_stopped ;
vty_cmd_loop_exit(vty) ;
@@ -476,9 +498,6 @@ cq_process(vty vty, cmd_exec_state_t state, cmd_return_code_t ret)
return ; /* <<< DONE, permanently */
} ;
- if (ret == CMD_IO_ERROR)
- continue ;
-
zabort("invalid return from vty_cmd_hiatus()") ;
} ;
@@ -524,7 +543,13 @@ cq_process(vty vty, cmd_exec_state_t state, cmd_return_code_t ret)
* Note that the revoke does not affect any vty_cli_nexus messages associated
* with the vty_io_basic operations.
*
+ * If succeeds in revoking, then will have set the state to vc_stopped, and
+ * will have released the message block or legacy thread object.
+ *
* Returns: true <=> have revoked a pending message/event
+ *
+ * NB: if no command loop has ever been started for this vty, then will not
+ * find anything to revoke.
*/
extern bool
cq_revoke(vty vty)
@@ -541,7 +566,16 @@ cq_revoke(vty vty)
ret = mqueue_revoke(vty_cmd_nexus->queue, vty, 1) ;
}
else
- ret = thread_cancel_event(vty_master, vty) ;
+ {
+ ret = thread_cancel_event(vty_master, vty) ;
+
+ if (ret != 0)
+ {
+ /* Make sure in same state as after mqueue_revoke. */
+ vty_cmd_set_stopped(vty) ;
+ vty->exec->cq.thread = NULL ;
+ } ;
+ } ;
/* If revoked message/event, the command loop is stopped. */
if (ret != 0)