summaryrefslogtreecommitdiffstats
path: root/lib/vty_io_term.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vty_io_term.c')
-rw-r--r--lib/vty_io_term.c1084
1 files changed, 568 insertions, 516 deletions
diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c
index 0f8e834d..2c9cb643 100644
--- a/lib/vty_io_term.c
+++ b/lib/vty_io_term.c
@@ -26,10 +26,13 @@
#include "vty_local.h"
#include "vty_io.h"
#include "vty_io_term.h"
+#include "vty_io_file.h"
#include "vty_cli.h"
#include "vty_command.h"
#include "vio_fifo.h"
+#include "log_local.h"
+
#include "qstring.h"
#include "keystroke.h"
@@ -75,12 +78,21 @@
* Opening and closing VTY_TERMINAL type
*/
-static void uty_term_ready(vio_vfd vfd, void* action_info) ;
-static vty_timer_time uty_term_read_timeout(vio_timer_t* timer,
+typedef enum {
+ utw_error = 0,
+ utw_done = BIT(0), /* all possible is done */
+ utw_stopped = BIT(1),
+ utw_blocked = BIT(2), /* I/O blocked */
+ utw_paused = utw_blocked | utw_stopped,
+} utw_ret_t ;
+
+static void uty_term_read_ready(vio_vfd vfd, void* action_info) ;
+static void uty_term_write_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_term_read_timeout(vio_timer timer,
void* action_info) ;
-static vty_timer_time uty_term_write_timeout(vio_timer_t* timer,
+static vty_timer_time uty_term_write_timeout(vio_timer timer,
void* action_info) ;
-static vty_readiness_t uty_term_write(vio_vf vf) ;
+static utw_ret_t uty_term_write(vio_vf vf) ;
static void uty_term_will_echo(vty_cli cli) ;
static void uty_term_will_suppress_go_ahead(vty_cli cli) ;
@@ -96,34 +108,38 @@ static void uty_term_dont_lflow_ahead(vty_cli cli) ;
static void
uty_term_open(int sock_fd, union sockunion *su)
{
+ node_type_t node ;
vty vty ;
vty_io vio ;
vio_vf vf ;
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- /* Allocate new vty structure and set up default values. */
- vty = uty_new(VTY_TERMINAL) ;
- vio = vty->vio ;
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
- /* The initial vty->node depends on a number of vty type things, so
- * we set that now.
+ /* The initial vty->node will be authentication, unless the host does not
+ * require that, in which case it may be a number of other things.
*
- * This completes the initialisation of the vty object, except that the
- * execution and vio objects are largely empty.
+ * Note that setting NULL_NODE at this point will cause the terminal to be
+ * closed very quickly, after issuing suitable message.
*/
+ node = (host.password != NULL) ? AUTH_NODE : NULL_NODE ;
+
if (host.no_password_check)
{
- if (host.restricted_mode)
- vio->vty->node = RESTRICTED_NODE;
- else if (host.advanced)
- vio->vty->node = ENABLE_NODE;
+ if (host.restricted_mode)
+ node = RESTRICTED_NODE;
+ else if (host.advanced && (host.enable == NULL))
+ node = ENABLE_NODE;
else
- vio->vty->node = VIEW_NODE;
- }
- else
- vio->vty->node = AUTH_NODE;
+ node = VIEW_NODE;
+ } ;
+
+ /* Allocate new vty structure and set up default values.
+ *
+ * This completes the initialisation of the vty object, except that the
+ * execution and vio objects are largely empty.
+ */
+ vty = uty_new(VTY_TERMINAL, node) ;
+ vio = vty->vio ;
/* Complete the initialisation of the vty_io object.
*
@@ -134,7 +150,6 @@ uty_term_open(int sock_fd, union sockunion *su)
*
* - parse_type -- default = cmd_parse_standard
* - reflect_enabled -- default = false
- * - out_enabled -- default = true iff vfd_io_write
*
* Are OK, except that we want the read_timeout set to the current EXEC
* timeout value.
@@ -143,10 +158,10 @@ uty_term_open(int sock_fd, union sockunion *su)
*/
vf = uty_vf_new(vio, sutoa(su).str, sock_fd, vfd_socket, vfd_io_read_write) ;
- uty_vin_open( vio, vf, VIN_TERM, uty_term_ready,
+ uty_vin_push( vio, vf, VIN_TERM, uty_term_read_ready,
uty_term_read_timeout,
0) ; /* no ibuf required */
- uty_vout_open(vio, vf, VOUT_TERM, uty_term_ready,
+ uty_vout_push(vio, vf, VOUT_TERM, uty_term_write_ready,
uty_term_write_timeout,
4096) ; /* obuf is required */
@@ -155,9 +170,6 @@ uty_term_open(int sock_fd, union sockunion *su)
/* Set up the CLI object & initialise */
vf->cli = uty_cli_new(vf) ;
- /* When we get here the VTY is set up and all ready to go. */
- uty_cmd_prepare(vio) ;
-
/* Issue Telnet commands/escapes to be a good telnet citizen -- not much
* real negotiating going on -- just a statement of intentions !
*/
@@ -171,90 +183,221 @@ uty_term_open(int sock_fd, union sockunion *su)
/* Say hello */
vty_hello(vty);
- /* If need password, issue prompt or give up if no password to check
- * against !
+ /* If about to authenticate, issue friendly message.
+ *
+ * If cannot authenticate, issue an error message.
+ */
+ if (vty->node == AUTH_NODE)
+ vty_out(vty, "User Access Verification\n") ;
+ else if (vty->node == NULL_NODE)
+ vty_out(vty, "%% Cannot continue because no password is set\n") ;
+
+ /* Enter the command loop. */
+ uty_cmd_loop_enter(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command line fetch from a VTY_TERMINAL.
+ *
+ * Fetching a command line <=> the previous command has completed.
+ *
+ * Returns: CMD_SUCCESS -- have another command line ready to go
+ * CMD_WAITING -- would not wait for input <=> non-blocking
+ * CMD_EOF -- ??????
+ *
+ * This can be called in any thread.
+ *
+ * NB: this does not signal CMD_EOF TODO ????
+ */
+extern cmd_return_code_t
+uty_term_fetch_command_line(vio_vf vf, cmd_action action, cmd_context context)
+{
+ return uty_cli_want_command(vf->cli, action, context) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Showing error context for the VTY_TERMINAL command line.
+ *
+ * If the stack is at level == 1, then the last full line displayed will be
+ * the line in which the error occurred (unless have monitor output, which
+ * there is little we can do about). So there is no further output required.
+ * The command line is indented by the current prompt.
+ *
+ * If the stack is at level > 1, then may or may not have had output separating
+ * the command line from the current position, so we output the command line
+ * to provide context.
+ *
+ * Returns: indent position of command line
+ */
+extern uint
+uty_term_show_error_context(vio_vf vf, vio_fifo ebuf, uint depth)
+{
+ if (depth == 1)
+ return uty_cli_prompt_len(vf->cli) ;
+
+ vio_fifo_printf(ebuf, "%% in command line:\n") ;
+ vio_fifo_printf(ebuf, " %s\n", qs_make_string(vf->cli->clx)) ;
+
+ return 2 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push output to the terminal.
+ *
+ * Returns: CMD_SUCCESS -- all buffers are empty, or final
+ * CMD_WAITING -- all buffers are not empty
+ * CMD_IO_ERROR -- failed -- final or not.
+ *
+ * This can be called in any thread. If "final" will not turn on any
+ * read/write ready stuff.
+ */
+extern cmd_return_code_t
+uty_term_out_push(vio_vf vf, bool final)
+{
+ vty_cli cli = vf->cli ;
+ utw_ret_t done ;
+
+ /* If have something in the obuf that needs to be written, then if not already
+ * out_active, make sure the command line is clear, and set out_active.
*/
- if (vty->node == AUTH_NODE)
+ if (!cli->out_active && !vio_fifo_empty(vf->obuf))
{
- if (host.password != NULL)
- vty_out(vty, "\nUser Access Verification\n\n");
- else
- uty_close(vio, false, qs_set(NULL, "vty password is not set."));
+ uty_cli_wipe(cli, 0) ;
+ cli->out_active = true ;
+ vio_lc_counter_reset(cli->olc) ;
+ } ;
+
+ /* Give the terminal writing a shove.
+ *
+ * If final, keep pushing while succeeds in writing without blocking.
+ */
+ if (final)
+ cli->flush = cli->out_active ; /* make sure empty everything */
+
+ do
+ {
+ if (final)
+ vio_lc_counter_reset(cli->olc) ;
+
+ done = uty_term_write(vf) ;
+ }
+ while (final && (done == utw_paused)) ;
+
+ if (!final)
+ {
+ if (done == utw_error)
+ return CMD_IO_ERROR ; /* TODO */
+
+ if ((done & utw_blocked) != 0)
+ {
+ confirm((utw_paused & utw_blocked) != 0) ;
+
+ uty_term_set_readiness(vf, write_ready) ;
+ return CMD_WAITING ;
+ } ;
} ;
- /* Push the output to date and start the CLI */
- uty_cmd_out_push(vio) ;
- uty_cli_start(vf->cli, vty->node) ;
+ return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
- * Close the reading side of VTY_TERMINAL, and close down CLI as far as
+ * The read side of the vfd has been closed. Close down CLI as far as
* possible, given that output may be continuing.
*
* Expects to be called once only for the VTY_TERMINAL.
+ *
+ * There is no difference between final and not-final close in this case.
+ *
+ * Note that this is only closed when the VTY_TERMINAL is forcibly closed, or
+ * when the user quits.
+ *
+ * Returns: CMD_SUCCESS -- all is quiet.
*/
-extern void
-uty_term_read_close(vio_vf vf)
+extern cmd_return_code_t
+uty_term_read_close(vio_vf vf, bool final)
{
vty_io vio ;
/* Get the vio and ensure that we are all straight */
vio = vf->vio ;
assert((vio->vin == vio->vin_base) && (vio->vin == vf)) ;
+ assert(vio->vin->vin_state == vf_closing) ;
- /* Do the file side of things
- *
- * Note that half closing the file sets a new timeout, sets read off
- * and write on.
+ /*
*/
uty_set_monitor(vio, 0) ;
+ /* Close the CLI as far as possible, leaving output side intact.
+ *
+ * Can generate some final output, which will be dealt with as the output
+ * side is closed.
+ */
uty_cli_close(vf->cli, false) ;
- /* Log closing of VTY_TERM */
+ /* Log closing of VTY_TERMINAL
+ */
assert(vio->vty->type == VTY_TERMINAL) ;
- uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio_vfd_fd(vf->vfd)) ;
+ zlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio_vfd_fd(vf->vfd)) ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Try to output the close reason to the given VOUT_TERM.
+ *
+ * If there is anything pending to be output, discard it, first. The obuf has
+ * already been cleared.
+ *
+ * This will be pushed out when the VOUT_TERM is finally closed.
+ */
+extern void
+uty_term_close_reason(vio_vf vf, const char* reason)
+{
+ vio_lc_clear(vf->cli->olc) ;
+ vio_fifo_clear(vf->cli->cbuf, true) ;
+
+ uty_cli_out(vf->cli, "%% %s%s", reason, uty_cli_newline) ;
} ;
/*------------------------------------------------------------------------------
* Close the writing side of VTY_TERMINAL.
*
- * Pushes any buffered stuff to output and
+ * Assumes that the read side has been closed already, and so this is the last
+ * thing to be closed.
+ *
+ * Kicks the output side:
*
+ * if final, will push as much as possible until would block.
+ *
+ * if not final, will push another tranche and let the uty_term_ready() keep
+ * pushing until buffers empty and can uty_cmd_signal().
+ *
+ * Returns: CMD_SUCCESS => all written (or final) and CLI closed.
+ * CMD_WAITING => write ready running to empty the buffers
+ * CMD_IO_ERROR =>
+ * others TODO
*/
-extern bool
-uty_term_write_close(vio_vf vf, bool final)
+extern cmd_return_code_t
+uty_term_write_close(vio_vf vf, bool final, bool base)
{
+ cmd_return_code_t ret ;
vty_io vio ;
- vty_readiness_t ready ;
-
- /* Get the vio and ensure that we are all straight */
- vio = vf->vio ;
- assert((vio->vin == vio->vin_base) && (vio->vin == vf)) ;
- /* Do the file side of things
+ /* Get the vio and ensure that we are all straight
*
- * Note that half closing the file sets a new timeout, sets read off
- * and write on.
+ * Can only be the vout_base and must also be the vin_base, and the vin_base
+ * must now be closed.
*/
- uty_set_monitor(vio, 0) ;
-
- vf->cli->out_active = true ; /* force the issue */
-
- do
- {
- vf->cli->out_done = false ;
- ready = uty_term_write(vf) ;
- } while ((ready != write_ready) && vf->cli->out_active) ;
-
- final = final || !vf->cli->out_active ;
+ vio = vf->vio ;
+ assert((vio->vout == vio->vout_base) && (vio->vout == vf)) ;
+ assert((vio->vin == vio->vin_base) && (vio->vin->vin_state == vf_closed)) ;
- if (!final)
- uty_term_set_readiness(vf, ready) ;
+ ret = uty_term_out_push(vf, final) ;
- vf->cli = uty_cli_close(vf->cli, final) ;
+ if (final)
+ vf->cli = uty_cli_close(vf->cli, final) ;
- return final ;
+ return ret ;
} ;
/*==============================================================================
@@ -272,6 +415,8 @@ uty_term_write_close(vio_vf vf, bool final)
* not set both at once.
*/
+static void uty_term_ready(vio_vf vf) ;
+
/*------------------------------------------------------------------------------
* Set read/write readiness -- for VIN_TERM/VOUT_TERM
*
@@ -282,12 +427,41 @@ uty_term_set_readiness(vio_vf vf, vty_readiness_t ready)
{
VTY_ASSERT_LOCKED() ;
- uty_vf_set_read(vf, (ready == read_ready)) ;
- uty_vf_set_write(vf, (ready >= write_ready)) ;
+ if ((ready & write_ready) != 0)
+ uty_vf_set_write(vf, on) ;
+ else if ((ready & read_ready) != 0)
+ uty_vf_set_read(vf, on) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Terminal read ready
+ */
+static void
+uty_term_read_ready(vio_vfd vfd, void* action_info)
+{
+ vio_vf vf = action_info ;
+
+ assert(vfd == vf->vfd) ;
+
+ vf->cli->paused = false ; /* read ready clears paused */
+ uty_term_ready(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Terminal write ready
+ */
+static void
+uty_term_write_ready(vio_vfd vfd, void* action_info)
+{
+ vio_vf vf = action_info ;
+
+ assert(vfd == vf->vfd) ;
+
+ uty_term_ready(vf) ;
} ;
/*------------------------------------------------------------------------------
- * So there is only one, common, uty_term_ready function, which:
+ * Terminal, something is ready -- read, write or no longer paused.
*
* 1. attempts to clear any output it can.
*
@@ -323,24 +497,142 @@ uty_term_set_readiness(vio_vf vf, vty_readiness_t ready)
* Resets the timer because something happened.
*/
static void
-uty_term_ready(vio_vfd vfd, void* action_info)
+uty_term_ready(vio_vf vf)
{
vty_readiness_t ready ;
+ utw_ret_t done ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Start by trying to write away any outstanding stuff, and then another
+ * tranche of any outstanding output.
+ */
+ ready = not_ready ;
+
+ if (!vf->cli->more_enabled)
+ vio_lc_counter_reset(vf->cli->olc) ; /* do one tranche */
+
+ done = uty_term_write(vf) ;
+
+ while (done != utw_error)
+ {
+ utw_ret_t done_before ;
+ done_before = done ;
+
+ /* Kick the CLI, which may advance either because there is more input,
+ * or because some output has now completed, or for any other reason.
+ *
+ * This may return write_ready, which is a proxy for CLI ready, and
+ * MUST be honoured, even (especially) if the output buffers are empty.
+ */
+ ready = uty_cli(vf->cli) ;
+
+ /* Now try to write away any new output which may have been generated
+ * by the CLI.
+ */
+ done = uty_term_write(vf) ;
+
+ if (done == done_before)
+ break ; /* quit if no change in response */
+ } ;
+
+ if (done == utw_error)
+ ; /* TODO !! */
+
+ if ((done & utw_blocked) != 0)
+ {
+ confirm((utw_paused & utw_blocked) != 0) ;
+ ready |= write_ready ;
+ } ;
+
+ if ((done != utw_blocked) && (done != utw_error))
+ {
+ /* Since is not output blocked, tell master that is now ready. */
+ if (vf->pr_master != NULL)
+ uty_pipe_return_slave_ready(vf) ;
+ } ;
+
+ uty_term_set_readiness(vf, ready) ;
+
+ /* Signal the command loop if out_active and the buffers empty out.
+ */
+ if (done == utw_done)
+ uty_cmd_signal(vf->vio, CMD_SUCCESS) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Read timer has expired.
+ *
+ * If closing, then this is curtains -- have waited long enough !
+ *
+ * TODO .... sort out the VTY_TERMINAL time-out & death-watch timeout
+ *
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
+ */
+static vty_timer_time
+uty_term_read_timeout(vio_timer timer, void* action_info)
+{
+ vio_vf vf = action_info ;
+
+ assert(timer == vf->vfd->read_timer) ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ vf->vin_state = vf_timed_out ;
+ keystroke_stream_set_eof(vf->cli->key_stream, true) ;
+
+ vf->cli->paused = false ;
+
+ uty_term_ready(vf) ;
+
+ return 0 ;
+ } ;
+/*------------------------------------------------------------------------------
+ * Write timer has expired.
+ *
+ * If closing, then this is curtains -- have waited long enough !
+ *
+ * TODO .... sort out the VTY_TERMINAL time-out & death-watch timeout
+ *
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
+ */
+static vty_timer_time
+uty_term_write_timeout(vio_timer timer, void* action_info)
+{
vio_vf vf = action_info ;
- assert(vfd == vf->vfd) ;
+ assert(timer == vf->vfd->read_timer) ;
VTY_ASSERT_LOCKED() ;
- uty_term_write(vf) ; /* try to clear outstanding stuff */
- do
+ vf->cli->paused = false ;
+
+//uty_close(vio, true, qs_set(NULL, "Timed out")) ; TODO
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Timeout on the cli->paused timer -- clear paused and treat as CLI ready.
+ */
+extern void
+vty_term_pause_timeout(qtimer qtr, void* timer_info, qtime_mono_t when)
+{
+ vty_cli cli ;
+
+ VTY_LOCK() ;
+
+ cli = timer_info ;
+ assert(cli->pause_timer == qtr) ;
+
+ if (cli->paused)
{
- ready = uty_cli(vf->cli) ; /* do any CLI work... */
- ready |= uty_term_write(vf) ; /* ...and any output that generates */
- } while (ready >= now_ready) ;
+ cli->paused = false ;
+ uty_term_ready(cli->vf) ;
+ } ;
- uty_term_set_readiness(vf, ready) ;
+ VTY_UNLOCK() ;
} ;
/*==============================================================================
@@ -362,7 +654,7 @@ uty_term_ready(vio_vfd vfd, void* action_info)
*
* Returns: 0 => nothing available
* > 0 => read at least one byte
- * -1 => EOF (or not open, or failed)
+ * -1 => EOF (or not open, or failed, or timed out, ...)
*/
extern int
uty_term_read(vio_vf vf, keystroke steal)
@@ -397,7 +689,7 @@ uty_term_read(vio_vf vf, keystroke steal)
* cli->cbuf -- command line -- reflects the status of the command line
*
* vf->obuf -- command output -- which is written to the file only while
- * out_active.
+ * out_active, and not blocked in more_wait.
*
* The cli output takes precedence.
*
@@ -405,351 +697,256 @@ uty_term_read(vio_vf vf, keystroke steal)
* "--more--" mechanism.
*/
-static int uty_write_lc(vio_vf vf, vio_fifo vff, vio_line_control lc) ;
-static int uty_write_fifo_lc(vio_vf vf, vio_fifo vff, vio_line_control lc) ;
+static utw_ret_t uty_term_write_lc(vio_line_control lc, vio_vf vf,
+ vio_fifo vff) ;
/*------------------------------------------------------------------------------
- * Write as much as possible of what there is.
- *
- * If not cmd_in_progress, clears cli_blocked if both FIFOs are, or become,
- * empty.
- *
- * Note that if !write_open, or becomes !write_open, then the FIFOs are empty
- * and all output instantly successful.
- *
- * Sets write on if prevented from writing everything available for output
- * by write() threatening to block.
+ * Have some (more) monitor output to send to the vty.
*
- * Returns: write_ready if should now set write on
- * now_ready if should loop back and try again
- * not_ready otherwise
+ * Make sure the command line is clear, then claim screen for monitor output
+ * and attempt to empty out buffers.
*/
-static vty_readiness_t
-uty_term_write(vio_vf vf)
+extern void
+uty_term_mon_write(vio_vf vf)
{
- vty_cli cli = vf->cli ;
- int ret ;
+ utw_ret_t done ;
- VTY_ASSERT_LOCKED() ;
+ uty_cli_pre_monitor(vf->cli) ; /* make sure in a fit state */
- ret = -1 ;
- while (vf->vout_state == vf_open)
- {
- /* Any outstanding line control output takes precedence */
- ret = uty_write_lc(vf, vf->obuf, cli->olc) ;
- if (ret != 0)
- break ;
+ done = uty_term_write(vf) ; /* TODO -- errors !! */
- /* Next: empty out the cli output */
- ret = vio_fifo_write_nb(cli->cbuf, vio_vfd_fd(vf->vfd), true) ;
- if (ret != 0)
- break ;
-
- /* Finished now if not allowed to progress the command stuff */
- if (!cli->out_active)
- return not_ready ; /* done all can do */
-
- /* If there is something in the command buffer, do that */
- if (!vio_fifo_empty(vf->obuf))
- {
-#if 0
- if (cli->out_done)
- break ; /* ...but not if done once */
-
- cli->out_done = true ; /* done this once */
-#endif
- assert(!cli->more_wait) ;
-
- ret = uty_write_fifo_lc(vf, vf->obuf, cli->olc) ;
- if (ret != 0)
- {
- if (ret < 0)
- break ; /* failed */
-
- if (!cli->more_wait)
- return write_ready ; /* done a tranche */
-
- /* Moved into "--more--" state
- *
- * * the "--more--" prompt is ready to be written, so do that
- * now
- *
- * * if that completes, then want to run the CLI *now* to
- * perform the first stage of the "--more--" process.
- */
- ret = vio_fifo_write_nb(cli->cbuf, vio_vfd_fd(vf->vfd), true) ;
- if (ret != 0)
- break ;
-
- return now_ready ;
- } ;
- } ;
-
- /* Exciting stuff: there is nothing left to output...
- *
- * ... watch out for half closed state.
- */
-#if 0
- if (vio->closing)
- {
- if (vio->close_reason != NULL)
- {
- if (cli->drawn || cli->dirty)
- uty_out(vio, "\n") ;
- uty_out(vio, "%% %s\n", vio->close_reason) ;
-
- vio->close_reason = NULL ; /* MUST discard now... */
- continue ; /* ... and write away */
- } ;
-
- if (!vio->closed) /* avoid recursion */
- uty_close(vio) ;
-
- return not_ready ; /* it's all over */
- } ;
-#endif
-
- if (uty_cli_draw_if_required(cli))
- continue ; /* do that now. */
-
- /* There really is nothing left to output */
- cli->out_active = false ;
+ if ((done & utw_blocked) != 0)
+ {
+ confirm((utw_paused & utw_blocked) != 0) ;
- return not_ready ;
+ uty_term_set_readiness(vf, write_ready) ;
} ;
-
- /* Arrives here if there is more to do, or failed (or was !write_open) */
-
- if (ret > 0)
- return write_ready ;
-
- if (ret == 0) /* just in case */
- return not_ready ;
-
- /* If is write_open, then report the error
- *
- * If still read_open, let the reader pick up and report the error, when it
- * has finished anything it has buffered.
- */
- if (vf->vout_state == vf_open)
- uty_vf_error(vf, "write", errno) ;
-
- /* For whatever reason, is no longer write_open -- clear all buffers.
- */
- vio_fifo_clear(vf->obuf, true) ; /* throw away cli stuff */
- uty_cli_out_clear(cli) ; /* throw away cmd stuff */
-
- cli->out_active = false ;
-
- return not_ready ; /* NB: NOT blocked by I/O */
} ;
/*------------------------------------------------------------------------------
- * Write as much as possible -- for "monitor" output.
+ * Write as much as possible of what there is.
*
- * Outputs only:
+ * Move to more_wait if required. TODO
*
- * a. outstanding line control stuff.
+ * If is cli->flush, then when all buffers are emptied out, clears itself and
+ * the out_active flag.
*
- * b. contents of CLI buffer
+ * Returns:
*
- * And:
+ * utw_error -- I/O error -- see errno (utw_error = -1)
*
- * a. does not report any errors.
+ * utw_blocked -- write blocked -- some write operation would block
*
- * b. does not change anything except the state of the buffers.
+ * utw_paused -- have written as much as line control allows in one go.
*
- * In particular, for the qpthreaded world, does not attempt to change
- * the state of the qfile or any other "thread private" structures.
+ * NB: this does NOT mean is now in more_wait, it means that
+ * to write more it is necessary to clear the line
+ * control pause state.
*
- * Returns: > 0 => blocked
- * 0 => all gone
- * < 0 => failed (or !write_open)
- */
-#if 0
-static int
-uty_write_monitor(vio_vf vf)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (!vf->write_open)
- return -1 ;
-
- if (vf->olc != NULL)
- {
- int ret ;
- ret = uty_write_lc(vf, vf->obuf, vf->olc) ;
-
- if (ret != 0)
- return ret ;
- } ;
-
- return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf), true) ;
-} ;
-#endif
-
-/*------------------------------------------------------------------------------
- * Write the given FIFO to output -- subject to line control.
+ * utw_stopped -- have done as much as can do -- no more output is possible
+ * until some external event changes things.
+ *
+ * This implies that any pending output has completed, in
+ * particular the line control iovec and the cli->cbuf are
+ * both empty.
*
- * Note that even if no "--more--" is set, will have set some height, so
- * that does not attempt to empty the FIFO completely all in one go.
+ * This state includes:
*
- * If the line control becomes "paused", it is time to enter "--more--" state
- * -- unless the FIFO is empty (or "--more--" is not enabled).
+ * * !out_active -- if there is something in the vf->obuf,
+ * we are not yet ready to output it.
*
- * NB: expects that the sock is write_open
+ * * more_wait -- waiting for user
*
- * Returns: > 0 => blocked or completed one tranche (more to go)
- * 0 => all gone
- * < 0 => failed
+ * utw_done -- have written everything can find, unless more output
+ * arrives, there is no more to do.
+ *
+ * If was cli->flush the all output really has gone, as
+ * well as any incomplete line. Also the out_active and
+ * the flush flags will have been cleared.
+ *
+ * If was not cli->flush, the out_active state persists,
+ * and there may be an incomplete line still pending.
*/
-static int
-uty_write_fifo_lc(vio_vf vf, vio_fifo vff, vio_line_control lc)
+static utw_ret_t
+uty_term_write(vio_vf vf)
{
- int ret ;
- char* src ;
- size_t have ;
- vty_cli cli ;
+ vty_cli cli = vf->cli ;
+ utw_ret_t ret ;
+ int did ;
+ size_t have, take ;
+ char* src ;
- cli = vf->cli ;
+ VTY_ASSERT_LOCKED() ;
- /* Collect another line_control height's worth of output.
- *
- * Expect the line control to be empty at this point, but it does not have
- * to be.
+ /* If the vout is neither vf_open, not vf_closing, discard all buffered
+ * output, and return all done.
*/
- vio_lc_set_pause(lc) ; /* clears lc->paused */
-
- vio_fifo_set_hold_mark(vff) ;
-
- src = vio_fifo_get(vff, &have) ;
- while ((src != NULL) && (!lc->paused))
+ if ((vf->vout_state != vf_open) && (vf->vout_state != vf_closing))
{
- size_t take ;
+ vio_fifo_clear(vf->obuf, false) ;
+ vio_fifo_clear(cli->cbuf, false) ;
+ vio_lc_clear(cli->olc) ;
- if (src == NULL)
- break ;
-
- take = vio_lc_append(lc, src, have) ;
- src = vio_fifo_step_get(vff, &have, take) ;
- } ;
+ cli->out_active = false ;
+ cli->flush = false ;
- cli->dirty = (lc->col != 0) ;
+ cli->more_wait = false ;
+ cli->more_enter = false ;
- /* Write the contents of the line control */
- ret = uty_write_lc(vf, vff, lc) ;
+ return utw_done ;
+ } ;
- if (ret < 0)
- return ret ; /* give up now if failed. */
+ /* Any outstanding line control output takes precedence */
+ ret = uty_term_write_lc(cli->olc, vf, vf->obuf) ;
+ if (ret != utw_done)
+ return ret ; /* utw_blocked or utw_error */
- if ((ret == 0) && vio_fifo_empty(vff))
- return 0 ; /* FIFO and line control empty */
+ /* Next: empty out the cli output */
+ did = vio_fifo_write_nb(cli->cbuf, vio_vfd_fd(vf->vfd), true) ;
+ if (did != 0)
+ return (did < 0) ? utw_error : utw_blocked ;
- /* If should now do "--more--", now is the time to prepare for that.
+ /* Next: if there is monitor output to deal with, deal with it.
*
- * Entering more state issues a new prompt in the CLI buffer, which can
- * be written once line control write completes.
+ * Note that the mon_active flag is set under VTY_LOCK(), so do not
+ * need to LOG_LOCK() to discover whether there is anything to do.
*
- * The "--more--" cli will not do anything until the CLI buffer has
- * cleared.
+ * But the vio->mbuf is filled under LOG_LOCK(), so need to write it
+ * under the same.
*/
- if (lc->paused && cli->more_enabled)
- uty_cli_enter_more_wait(cli) ;
+ if (cli->mon_active)
+ {
+ LOG_LOCK() ;
- return 1 ; /* FIFO or line control, not empty */
-} ;
+ did = vio_fifo_write_nb(vf->vio->mbuf, vio_vfd_fd(vf->vfd), true) ;
-/*------------------------------------------------------------------------------
- * Write contents of line control (if any).
- *
- * NB: expects that the sock is write_open
- *
- * NB: does nothing other than write() and buffer management.
- *
- * Returns: > 0 => blocked
- * 0 => all gone
- * < 0 => failed
- */
-static int
-uty_write_lc(vio_vf vf, vio_fifo vff, vio_line_control lc)
-{
- int ret ;
+ LOG_UNLOCK() ;
- ret = vio_lc_write_nb(vio_vfd_fd(vf->vfd), lc) ;
+ if (did != 0)
+ return (did < 0) ? utw_error : utw_blocked ;
- if (ret <= 0)
- vio_fifo_clear_hold_mark(vff) ; /* finished with FIFO contents */
+ uty_cli_post_monitor(vf->cli) ;
+ } ;
- return ret ;
-} ;
+ /* If not out_active, or in more_wait, then we are stopped, waiting for some
+ * external event to move things on.
+ */
+ if (cli->flush)
+ assert(cli->out_active) ; /* makes no sense, otherwise */
+ if (!cli->out_active || cli->more_wait)
+ return utw_stopped ;
-#if 0
+ /* Push the output fifo and any complete line fragments that may be buffered
+ * in hand in the line control -- this will stop if the line counter becomes
+ * exhausted.
+ *
+ * Note that this arranges for vio_lc_append() to be called at least once,
+ * even if the fifo is empty -- this deals with any parts of a complete
+ * line that may be held in the line control due to counter exhaustion.
+ *
+ * If the fifo is or becomes empty, then if is cli->flush, flush out any
+ * incomplete line which may be held in the line control -- in effect,
+ * cli->flush is a phantom '\n' at the end of the output fifo !
+ */
+ vio_fifo_set_hold_mark(vf->obuf) ; /* released in uty_term_write_lc() */
-/*------------------------------------------------------------------------------
- * Start command output -- clears down the line control.
- *
- * Requires that that current line is empty -- restarts the line control
- * on the basis that is at column 0.
- */
-extern void
-uty_cmd_output_start(vio_vf vf)
-{
- if (vf->olc != NULL)
- vio_lc_clear(vf->olc) ;
+ src = vio_fifo_get(vf->obuf, &have) ;
+ while (1)
+ {
+ take = vio_lc_append(cli->olc, src, have) ;
- vio_fifo_set_hold_mark(vf->obuf) ; /* mark to keep until all gone */
-} ;
+ if (take == 0)
+ break ;
-#endif
+ src = vio_fifo_step_get(vf->obuf, &have, take) ;
+ if (have == 0)
+ break ;
+ } ;
+ if ((have == 0) && (cli->flush))
+ vio_lc_flush(cli->olc) ;
+ ret = uty_term_write_lc(cli->olc, vf, vf->obuf) ;
+ if (ret != utw_done)
+ return ret ; /* utw_blocked or utw_error */
-/*==============================================================================
- * Timer actions for VTY_TERMINAL
- */
+ /* If arrive here, then:
+ *
+ * * no output is blocked and no errors have occurred.
+ *
+ * * the cli->cbuf is empty.
+ *
+ * * the line control iovec buffer is empty.
+ *
+ * If the fifo is not empty or there is a some part of a complete line in
+ * hand, then the line counter must be exhausted.
+ */
+ if ((have != 0) || vio_lc_have_complete_line_in_hand(cli->olc))
+ {
+ assert(vio_lc_counter_is_exhausted(cli->olc)) ;
-/*------------------------------------------------------------------------------
- * Read timer has expired.
- *
- * If closing, then this is curtains -- have waited long enough !
- *
- * TODO .... sort out the VTY_TERMINAL time-out & death-watch timeout
- *
- * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
- */
-static vty_timer_time
-uty_term_read_timeout(vio_timer_t* timer, void* action_info)
-{
- vty_io vio = action_info ;
+ if (cli->more_enabled)
+ {
+ uty_cli_enter_more_wait(cli) ;
+ return utw_stopped ;
+ }
+ else
+ return utw_paused ; /* artificial block */
+ } ;
- VTY_ASSERT_LOCKED() ;
+ /* Exciting stuff: there is nothing left to output...
+ *
+ * ...with the sole possible exception of an incomplete line buffered
+ * in the line control, which can do nothing about until there is more
+ * to be output, or the output is flushed...
+ *
+ * ...if not cli->flush, we are stopped, waiting for something else to
+ * happen.
+ */
+ assert(!cli->more_wait && !cli->more_enter) ;
- uty_close(vio, true, qs_set(NULL, "Timed out")) ;
+ if (cli->flush)
+ {
+ /* Even more exciting: is cli->flush !
+ *
+ * This means that any incomplete line must have been flushed, above.
+ * So all buffers MUST be empty.
+ */
+ assert(vio_fifo_empty(vf->obuf) && vio_lc_is_empty(cli->olc)) ;
- return 0 ;
- } ;
+ cli->out_active = false ;
+ cli->flush = false ;
+ } ;
+
+ return utw_done ;
+} ;
/*------------------------------------------------------------------------------
- * Write timer has expired.
+ * Write contents of line control iovec buffer (if any).
*
- * If closing, then this is curtains -- have waited long enough !
+ * NB: expects that the vf is write_open
*
- * TODO .... sort out the VTY_TERMINAL time-out & death-watch timeout
+ * NB: does nothing other than write() and buffer management.
*
- * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
+ * Returns: utw_blocked => blocked
+ * utw_done => all gone (may still have stuff "in hand")
+ * utw_error => failed
*/
-static vty_timer_time
-uty_term_write_timeout(vio_timer_t* timer, void* action_info)
+static utw_ret_t
+uty_term_write_lc(vio_line_control lc, vio_vf vf, vio_fifo vff)
{
- vty_io vio = action_info ;
+ int did ;
- VTY_ASSERT_LOCKED() ;
+ did = vio_lc_write_nb(vio_vfd_fd(vf->vfd), lc) ;
- uty_close(vio, true, qs_set(NULL, "Timed out")) ;
+ if (did > 0)
+ return utw_blocked ;
- return 0 ;
+ vio_fifo_clear_hold_mark(vff) ; /* finished with FIFO contents */
+
+ return (did == 0) ? utw_done : utw_error ;
} ;
/*==============================================================================
@@ -778,7 +975,7 @@ uty_term_open_listeners(const char *addr, unsigned short port)
n = uty_term_listen_simple(addr, port);
if (n == 0)
- uzlog(NULL, LOG_ERR, "could not open any VTY_TERMINAL listeners") ;
+ zlog(NULL, LOG_ERR, "could not open any VTY_TERMINAL listeners") ;
} ;
/*------------------------------------------------------------------------------
@@ -875,7 +1072,7 @@ uty_term_listen_simple(const char *addr, unsigned short port)
if (ret == 0)
sa = &su_addr.sa ;
else
- uzlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
+ zlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
} ;
/* Try for AF_INET */
@@ -896,7 +1093,7 @@ uty_term_listen_simple(const char *addr, unsigned short port)
/* If not used the address... something wrong */
if (sa != NULL)
- uzlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
+ zlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
/* Done */
return n ;
@@ -1003,7 +1200,7 @@ uty_term_accept(int sock_listen)
if (sock_fd < 0)
{
if (sock_fd == -1)
- uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
+ zlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
errtoa(errno, 0).str) ;
return ;
}
@@ -1046,7 +1243,7 @@ uty_term_accept(int sock_listen)
if (ret != 0)
{
- uzlog (NULL, LOG_INFO, "Vty connection refused from %s", sutoa(&su).str) ;
+ zlog (NULL, LOG_INFO, "Vty connection refused from %s", sutoa(&su).str) ;
close (sock_fd);
return ;
} ;
@@ -1056,14 +1253,14 @@ uty_term_accept(int sock_listen)
ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_NODELAY,
(void*)&on, sizeof (on));
if (ret < 0)
- uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
+ zlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
sock_fd, errtoa(errno, 0).str) ;
/* All set -- create the VTY_TERMINAL and set it going */
uty_term_open(sock_fd, &su);
/* Log new VTY */
- uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
+ zlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
sock_fd) ;
return ;
@@ -1315,7 +1512,7 @@ uty_telnet_command(vio_vf vf, keystroke stroke, bool callback)
case to_NAWS:
if (left != 4)
{
- uzlog(NULL, LOG_WARNING,
+ zlog(NULL, LOG_WARNING,
"RFC 1073 violation detected: telnet NAWS option "
"should send %d characters, but we received %d",
(3 + 4 + 2), (3 + left + 2)) ;
@@ -1352,148 +1549,3 @@ uty_telnet_command(vio_vf vf, keystroke stroke, bool callback)
return dealt_with ;
} ;
-/*==============================================================================
- * Output to vty which are set to "monitor".
- *
- * This is VERY TRICKY.
- *
- * If not running qpthreaded, then the objective is to get the message away
- * immediately -- do not wish it to be delayed in any way by the thread
- * system.
- *
- * So proceed as follows:
- *
- * a. wipe command line -- which adds output to the CLI buffer
- *
- * b. write the CLI buffer to the sock and any outstanding line control.
- *
- * c. write the monitor output.
- *
- * If that does not complete, put the tail end to the CLI buffer.
- *
- * d. restore any command line -- which adds output to the CLI buffer
- *
- * e. write the CLI buffer to the sock
- *
- * If that all succeeds, nothing has changed as far as the VTY stuff is
- * concerned -- except that possibly some CLI output was sent before it got
- * round to it.
- *
- * Note that step (b) will deal with any output hanging around from an
- * earlier step (e). If cannot complete that, then does not add fuel to the
- * fire -- but the message will be discarded.
- *
- * If that fails, or does not complete, then can set write on, to signal that
- * there is some output in the CLI buffer that needs to be sent, or some
- * error to be dealt with.
- *
- * The output should be tidy.
- *
- * To cut down the clutter, step (d) is performed only if the command line
- * is not empty (or if in cli_more_wait). Once a the user has started to enter
- * a command, the prompt and the command will remain visible.
- *
- * When logging an I/O error for a vty that happens to be a monitor, the
- * monitor-ness has already been turned off. The monitor output code does not
- * attempt to log any errors, sets write on so that the error will be picked
- * up that way.
- *
- * However, in the event of an assertion failure, it is possible that an
- * assertion will fail inside the monitor output. The monitor_busy flag
- * prevents disaster. It is also left set if I/O fails in monitor output, so
- * will not try to use the monitor again.
- *
- * Note that an assertion which is false for all vty monitors will recurse
- * through all the monitors, setting each one busy, in turn !
- *
-
-
- * TODO: sort out write on in the qpthreads world ??
- *
- * The problem is that the qpselect structure is designed to be accessed ONLY
- * within the thread to which it belongs. This makes it impossible for the
- * monitor output to set/clear read/write on the vty sock... so some way
- * around this is required.
- */
-
-/*------------------------------------------------------------------------------
- * Output logging information to all vty which are set to "monitor".
- */
-extern void
-uty_log(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va)
-{
- vty_io vio ;
-
- VTY_ASSERT_LOCKED() ;
-
- vio = sdl_head(vio_monitors_base) ;
-
- if (vio == NULL)
- return ; /* go no further if no "monitor" vtys */
-
- /* Prepare line for output. */
- uvzlog_line(ll, zl, priority, format, va, llt_crlf) ; /* with crlf */
-
- /* write to all known "monitor" vty
- *
- */
- while (vio != NULL)
- {
-#if 0
- if (!vio->monitor_busy)
- {
- int ret ;
-
- vio->monitor_busy = 1 ; /* close the door */
-
- uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
-
- ret = uty_write_monitor(vio) ;
- if (ret == 0)
- {
- ret = write_nb(vio_vfd_fd(vf->vfd), ll->line, ll->len) ;
-
- if (ret >= 0)
- {
- ret = uty_cli_post_monitor(vio, ll->line + ret,
- ll->len - ret) ;
- if (ret > 0)
- ret = uty_write_monitor(vio) ;
- } ;
- } ;
-
- if (ret != 0)
- /* need to prod */ ;
-
- if (ret >= 0)
- vio->monitor_busy = 0 ;
- } ;
-#endif
- vio = sdl_next(vio, mon_list) ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Async-signal-safe version of vty_log for fixed strings.
- *
- * This is last gasp operation.
- */
-void
-vty_log_fixed (const char *buf, size_t len)
-{
- vty_io vio ;
-
- /* Write to all known "monitor" vty
- *
- * Forget all the niceties -- about to die in any case.
- */
- vio = sdl_head(vio_monitors_base) ;
- while (vio != NULL)
- {
- write(vio_vfd_fd(vio->vout_base->vfd), buf, len) ;
- write(vio_vfd_fd(vio->vout_base->vfd), "\r\n", 2) ;
-
- vio = sdl_next(vio, mon_list) ;
- } ;
-} ;