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.c267
1 files changed, 180 insertions, 87 deletions
diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c
index 0660afb6..42ab941a 100644
--- a/lib/vty_io_term.c
+++ b/lib/vty_io_term.c
@@ -181,6 +181,11 @@ uty_term_open(int sock_fd, union sockunion *su)
if (0)
uty_term_dont_lflow_ahead (vf->cli) ;
+ /* Hoover up any currently available input -- possibly Telnet commands/
+ * escapes from the other end.
+ */
+ uty_term_read(vf) ;
+
/* Say hello */
vty_hello(vty);
@@ -215,13 +220,53 @@ uty_term_open(int sock_fd, union sockunion *su)
* CMD_IO_ERROR -- any errors are dealt with by signalling the command loop.
*/
extern cmd_return_code_t
-uty_term_fetch_command_line(vio_vf vf, cmd_action action, cmd_context context)
+uty_term_fetch_command_line(vio_vf vf, cmd_action action)
{
VTY_ASSERT_LOCKED() ;
qassert(vf->vin_state == vf_open) ;
- return uty_cli_want_command(vf->cli, action, context) ;
+ return uty_cli_want_command(vf->cli, action) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command has completed.
+ *
+ * If is cst_in_progress:
+ *
+ * -- set cst_complete
+ *
+ * -- collect current context
+ *
+ * -- if is cos_idle then force cos_active -- the obuf etc. will be
+ * empty, but we want to make sure that all other buffers empty out,
+ * and that the output side will signal when that's so.
+ *
+ * -- kick write_ready to make sure that things move along.
+ */
+extern void
+uty_term_cmd_complete(vio_vf vf, cmd_context context)
+{
+ vty_cli cli = vf->cli ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (cli->state == cst_in_progress)
+ {
+ cli->state = cst_complete ;
+
+ if (cli->out_state == cos_idle)
+ {
+ vio_fifo_clear(vf->obuf, false) ; /* keep end mark */
+ cli->out_state = cos_active ;
+ } ;
+
+ *cli->context = *context ;
+ cli->auth_context = ( (cli->context->node == AUTH_NODE)
+ || (cli->context->node == AUTH_ENABLE_NODE) ) ;
+
+ uty_term_set_readiness(vf, write_ready) ;
+ } ;
} ;
/*------------------------------------------------------------------------------
@@ -269,28 +314,36 @@ uty_term_out_push(vio_vf vf, bool final)
{
vty_cli cli = vf->cli ;
utw_ret_t done ;
+ cli_out_state_t out_state ;
qassert(vf->vout_state == vf_open) ;
qassert(!vf->blocking) ;
- /* If squelching, dump anything we have in the obuf.
+ out_state = cli->out_state & cos_mask ;
+
+ /* If squelching, discard anything we have in the obuf.
*/
if (vf->vio->cancel)
vio_fifo_clear(vf->obuf, false) ; /* keep end mark */
- /* 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 we have something in the obuf that needs to be written, then if
+ * is cos_idle, make sure the command line is clear, and set cos_active.
+ *
+ * Note that may be cos_idle | cos_monitor, for which we need to keep the
+ * monitor flag but update to cos_active -- the cli will already be wiped.
*/
- if (!cli->out_active && !vio_fifo_empty(vf->obuf))
+ if (!vio_fifo_empty(vf->obuf) && (out_state == cos_idle))
{
uty_cli_wipe(cli, 0) ;
- cli->out_active = true ;
+ cli->out_state ^= (cos_active ^ cos_idle) ;
vio_lc_counter_reset(cli->olc) ;
} ;
/* Give the terminal writing a shove.
*
+ * Note that there may be stuff pending, and stuff in the cbuf, etc. We
+ * depend on the uty_term_write() to know all about what can be done.
+ *
* If final, keep pushing while succeeds in writing without blocking.
*
* Note that is only called "final" when closing the vout, by which time
@@ -310,21 +363,52 @@ uty_term_out_push(vio_vf vf, bool final)
/* Deal with the result
*
- * If required and if not final make sure that write ready is set, so
- * that the pselect() process can pursue the issue.
+ * If not failed and not final, set write ready if:
+ *
+ * not utw_done => output blocked.
+ *
+ * cli->out_state == cos_idle => CLI may run
*
* Return code depends on utw_xxx
*/
- if (done == utw_done)
- return CMD_SUCCESS ; /* don't set write ready */
-
if (done == utw_error)
return CMD_IO_ERROR ; /* don't set write ready */
if (!final)
- uty_term_set_readiness(vf, write_ready) ;
+ {
+ if ((done != utw_done) || (cli->out_state == cos_idle))
+ uty_term_set_readiness(vf, write_ready) ;
+ } ;
- return CMD_WAITING ; /* waiting for write ready */
+ return (done == utw_done) ? CMD_SUCCESS : CMD_WAITING ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Completed the cancelling of command(s) and output.
+ *
+ * Caller is about to output "^C" to signal completion of the cancellation
+ * process -- sets the cli->state to cos_active, to allow final output to
+ * proceed, and the command loop continue.
+ *
+ * Clears any cos_paused !
+ *
+ * If the current line is "drawn", make sure we are at the end of it, and then
+ * clear the "drawn" state, because "^C\n" is about to follow.
+ *
+ * Clear the line control, to make sure that cannot now interfere.
+ */
+extern void
+uty_term_out_cancelled(vio_vf vf)
+{
+ vty_cli cli = vf->cli ;
+
+ qassert((cli->out_state & cos_mask) == cos_cancel) ;
+
+ cli->out_state = cos_active | (cli->out_state & cos_monitor) ;
+
+ vio_lc_counter_reset(cli->olc) ;
+
+ uty_cli_cancel(cli, false) ;
} ;
/*------------------------------------------------------------------------------
@@ -377,8 +461,8 @@ uty_term_close_reason(vio_vf vf, const char* reason)
{
vio_lc_clear(vf->cli->olc) ;
- vf->cli->mon_active = false ; /* stamp on any monitor output */
-
+ vf->cli->out_state = cos_idle ; /* stamp on monitor or other output
+ * clear cancel and paused */
uty_cli_out(vf->cli, "%% %s%s", reason, uty_cli_newline) ;
} ;
@@ -487,7 +571,7 @@ uty_term_read_ready(vio_vfd vfd, void* action_info)
qassert(vfd == vf->vfd) ;
- vf->cli->paused = false ; /* read ready clears paused */
+ vf->cli->out_state &= ~cos_paused ; /* read ready clears paused */
uty_term_read(vf) ;
uty_term_ready(vf) ;
@@ -547,6 +631,7 @@ uty_term_ready(vio_vf vf)
{
vty_readiness_t ready ;
utw_ret_t done, done_before ;
+ cli_out_state_t out_state ;
VTY_ASSERT_LOCKED() ;
@@ -562,6 +647,7 @@ uty_term_ready(vio_vf vf)
* This is because the CLI may generate more to write, and writing stuff
* away may release the CLI.
*/
+ out_state = vf->cli->out_state ;
done = utw_null ;
do
{
@@ -595,9 +681,9 @@ uty_term_ready(vio_vf vf)
uty_term_set_readiness(vf, ready) ;
- /* Signal the command loop if not waiting for write any more.
+ /* Signal the command loop if the cli->out_state has changed significantly
*/
- if ((ready & write_ready) == 0)
+ if ((vf->cli->out_state == cos_idle) && (out_state != cos_idle))
uty_cmd_signal(vf->vio, CMD_SUCCESS) ;
} ;
@@ -618,7 +704,7 @@ uty_term_read_timeout(vio_timer timer, void* action_info)
keystroke_stream_set_eof(vf->cli->key_stream, true) ; /* timed out */
- vf->cli->paused = false ;
+ vf->cli->out_state &= ~cos_paused ;
uty_term_ready(vf) ;
@@ -639,7 +725,7 @@ uty_term_write_timeout(vio_timer timer, void* action_info)
VTY_ASSERT_LOCKED() ;
- vf->cli->paused = false ;
+ vf->cli->out_state &= ~cos_paused ;
uty_vf_error(vf, verr_to_vout, 0) ;
@@ -659,9 +745,9 @@ vty_term_pause_timeout(qtimer qtr, void* timer_info, qtime_mono_t when)
cli = timer_info ;
assert(cli->pause_timer == qtr) ;
- if (cli->paused)
+ if (cli->out_state & cos_paused)
{
- cli->paused = false ;
+ cli->out_state ^= cos_paused ;
uty_term_ready(cli->vf) ;
} ;
@@ -675,15 +761,13 @@ vty_term_pause_timeout(qtimer qtr, void* timer_info, qtime_mono_t when)
*/
/*------------------------------------------------------------------------------
- * Read a lump of bytes and shovel into the keystroke stream
+ * Hoover up input and shovel into the keystroke stream
*
* NB: need not be in the term_ready path. Indeed, when the VTY_TERMINAL is
* initialised, this is called to suck up any telnet preamble.
*
- * NB: the terminal is permanently read-ready, so will keep calling this
- * until all input is hoovered up. For real terminals it is assumed that
- * reading a lump of bytes this small will immediately empty the input
- * buffer.
+ * NB: the terminal is permanently read-ready, so the keystroke stream
+ * should be permanently topped up.
*
* When reaches EOF on the input eof is set in the keystroke stream, and the
* vfd is read closed. Read closing the vfd turns off any timeout and prevents
@@ -691,23 +775,16 @@ vty_term_pause_timeout(qtimer qtr, void* timer_info, qtime_mono_t when)
* NOT set vf->vin_state to vf_end, because that is not true until the
* keystroke stream is empty. Once eof is set in the keystroke stream, this
* code will not attempt any further input from the vfd.
- *
- * Returns: 0 => nothing available
- * > 0 => read at least one byte
- * == -1 => I/O error
- * == -2 => hit EOF (without reading anything else)
*/
-extern int
+extern void
uty_term_read(vio_vf vf)
{
keystroke_stream stream ;
- unsigned char buf[500] ;
- int get ;
stream = vf->cli->key_stream ;
if (keystroke_stream_met_eof(stream))
- return -2 ; /* already seen EOF */
+ return ; /* already seen EOF */
if (vf->vin_state != vf_open)
{
@@ -716,33 +793,51 @@ uty_term_read(vio_vf vf)
* force it to EOF.
*/
keystroke_stream_set_eof(vf->cli->key_stream, false) ;
- return -2 ;
+ return ;
} ;
/* OK: read from input and pass result to keystroke stream.
+ *
+ * Uses a relatively small buffer, which should be fine for actual
+ * terminals !
*/
- get = read_nb(vio_vfd_fd(vf->vfd), buf, sizeof(buf)) ;
- /* -1 <=> error, -2 <=> EOF */
- if (get != 0)
+ while (1)
{
- if (get == -1)
+ unsigned char buf[500] ;
+ int get ;
+
+ get = read_nb(vio_vfd_fd(vf->vfd), buf, sizeof(buf)) ;
+ /* -1 <=> error, -2 <=> EOF */
+ if (get == 0)
+ return ;
+
+ if (get != -1)
{
- /* Error: signal to command loop and force key_stream empty */
- uty_vf_error(vf, verr_io_vin, errno) ;
- keystroke_stream_set_eof(vf->cli->key_stream, false) ;
- /* not timed-out */
+ /* Not error. get < 0 => EOF and that is set in key_stream.
+ */
+ keystroke_input(vf->cli->key_stream, buf, get) ;
}
else
{
- /* Not error. get < 0 => EOF and that is set in key_stream. */
- keystroke_input(vf->cli->key_stream, buf, get) ;
+ /* Error: signal to command loop and force key_stream empty
+ */
+ uty_vf_error(vf, verr_io_vin, errno) ;
+ keystroke_stream_set_eof(vf->cli->key_stream, false) ;
+ /* not timed-out */
} ;
- if (get < 0)
- vio_vfd_read_close(vf->vfd) ;
- } ;
+ if (get > 0)
+ continue ; /* hoover up all there is. */
+
+ /* Error or EOF -- close the input half of the socket, and we're done.
+ *
+ * NB: this does not affect the vin_state, which (if not error) continues
+ * vf_open until hits EOF on the keystroke stream.
+ */
+ vio_vfd_read_close(vf->vfd) ;
- return get ;
+ return ;
+ } ;
} ;
/*==============================================================================
@@ -752,8 +847,8 @@ uty_term_read(vio_vf vf)
*
* 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, and not blocked in more_wait.
+ * vf->obuf -- command output -- which is written to the terminal only while
+ * cos_active.
*
* The cli output takes precedence.
*
@@ -786,10 +881,14 @@ uty_term_mon_write(vio_vf vf)
/*------------------------------------------------------------------------------
* Write as much as possible of what there is.
*
- * Move to more_wait if required.
+ * If there is anything in the CLI buffer, try to empty that.
+ *
+ * If is cos_monitor, try to empty the mbuf.
*
- * If is cli->flush, then when all buffers are emptied out, clears itself and
- * the out_active flag.
+ * If cos_active, try to empty the obuf. Move to more_wait if required.
+ *
+ * If is not "in_progress", then when all buffers are emptied out, clears
+ * itself and the cos_active state.
*
* Returns:
*
@@ -833,10 +932,7 @@ uty_term_write(vio_vf vf)
vio_fifo_clear(cli->cbuf, false) ;
vio_lc_clear(cli->olc) ;
- cli->out_active = false ;
-
- cli->more_wait = false ;
- cli->more_enter = false ;
+ cli->out_state = cos_idle ; /* stamps on everything */
return utw_error ;
} ;
@@ -863,13 +959,13 @@ uty_term_write(vio_vf vf)
/* Next: if there is monitor output to deal with, deal with it.
*
- * Note that the mon_active flag is set under VTY_LOCK(), so do not
+ * Note that the cos_monitor state is set under VTY_LOCK(), so do not
* need to LOG_LOCK() to discover whether there is anything to do.
*
* But the vio->mbuf is filled under LOG_LOCK(), so need to write it
* under the same.
*/
- if (cli->mon_active)
+ if (cli->out_state & cos_monitor)
{
LOG_LOCK() ;
@@ -886,13 +982,13 @@ uty_term_write(vio_vf vf)
return utw_error ;
} ;
- uty_cli_post_monitor(vf->cli) ;
+ uty_cli_post_monitor(vf->cli) ; /* clears monitor output state */
} ;
- /* If not out_active, or in more_wait, then we are done, waiting for some
- * external event to move things on.
+ /* If not cos_active we are done, waiting for some external event to move
+ * things on.
*/
- if ((!cli->out_active) || (cli->more_wait))
+ if (cli->out_state != cos_active)
return utw_done ; /* can do no more */
/* Push the output fifo and any complete line fragments that may be buffered
@@ -903,9 +999,8 @@ uty_term_write(vio_vf vf)
* 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 !
+ * If the fifo is or becomes empty, then if the command has completed, flush
+ * out any incomplete line which may be held in the line control.
*/
vio_fifo_set_hold_mark(vf->obuf) ; /* released in uty_term_write_lc() */
@@ -923,7 +1018,7 @@ uty_term_write(vio_vf vf)
break ;
} ;
- if ((have == 0) && !cli->in_progress)
+ if ((have == 0) && (cli->state == cst_complete))
vio_lc_flush(cli->olc) ;
ret = uty_term_write_lc(cli->olc, vf, vf->obuf) ;
@@ -957,25 +1052,23 @@ uty_term_write(vio_vf vf)
/* 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...
+ * in the line control -- if not cst_complete.
*
- * ...if not cli->flush, we are stopped, waiting for something else to
- * happen.
+ * ...if is cst_complete, all output has gone, so can fall back to cos_idle.
*/
- qassert(!cli->more_wait && !cli->more_enter) ;
-
- if (cli->in_progress)
- return utw_done ; /* can do no more */
+ qassert(cli->out_state = cos_active) ;
- /* Even more exciting: is !cli->in_progress !
- *
- * This means that any incomplete line must have been flushed, above.
- * So all buffers MUST be empty.
- */
- qassert(vio_fifo_empty(vf->obuf) && vio_lc_is_empty(cli->olc)) ;
+ if (cli->state == cst_complete)
+ {
+ /* Even more exciting: is cst_complete
+ *
+ * This means that any incomplete line must have been flushed, above.
+ * So all buffers MUST be empty.
+ */
+ qassert(vio_fifo_empty(vf->obuf) && vio_lc_is_empty(cli->olc)) ;
- cli->out_active = false ;
+ cli->out_state = cos_idle ;
+ } ;
return utw_done ; /* absolutely all done */
} ;