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.c987
1 files changed, 599 insertions, 388 deletions
diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c
index 2053dcc5..0f8e834d 100644
--- a/lib/vty_io_term.c
+++ b/lib/vty_io_term.c
@@ -20,11 +20,15 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
+#include "zconfig.h"
+#include "misc.h"
-#include "zebra.h"
-
+#include "vty_local.h"
+#include "vty_io.h"
#include "vty_io_term.h"
#include "vty_cli.h"
+#include "vty_command.h"
+#include "vio_fifo.h"
#include "qstring.h"
#include "keystroke.h"
@@ -38,149 +42,159 @@
#include "network.h"
#include <arpa/telnet.h>
-#include <sys/un.h> /* for VTYSH */
#include <sys/socket.h>
+#include <errno.h>
-#define VTYSH_DEBUG 0
-
-/*------------------------------------------------------------------------------
- * Create new vty of type VTY_TERMINAL -- ie attached to a telnet session.
+/*==============================================================================
+ * The I/O side of Telnet VTY_TERMINAL. The CLI side is vty_cli.c.
+ *
+ * A VTY_TERMINAL comes into being when a telnet connection is accepted, and
+ * is closed either on command, or on timeout, or when the daemon is reset
+ * or terminated.
+ *
+ *
+ *
*
- * This is called by the accept action for the VTY_TERMINAL listener.
*/
-static void
-uty_term_accept_new(int sock_fd, union sockunion *su)
-{
- struct vty *vty ;
- vty_io vio ;
- enum vty_readiness ready ;
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
- /* Allocate new vty structure and set up default values. */
- vty = uty_new(VTY_TERMINAL, sock_fd) ;
- vio = vty->vio ;
- /* The text form of the address identifies the VTY */
- vty->vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
- /* Set the initial node */
- if (no_password_check)
- {
- if (restricted_mode)
- vty->node = RESTRICTED_NODE;
- else if (host.advanced)
- vty->node = ENABLE_NODE;
- else
- vty->node = VIEW_NODE;
- }
- else
- vty->node = AUTH_NODE;
- /* Pick up current timeout setting */
- vio->sock.v_timeout = vty_timeout_val;
- /* Use global 'lines' setting, as default. May be -1 => unset */
- vio->lines = host.lines ;
-
- /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
- vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
- uty_set_height(vio) ; /* set initial state */
-
- /* Initialise the CLI, ready for start-up messages etc. */
- uty_cli_init(vio) ;
+/*==============================================================================
+ * If possible, will use getaddrinfo() to find all the things to listen on.
+ */
+#if defined(HAVE_IPV6) && !defined(NRL)
+# define VTY_USE_ADDRINFO 1
+#else
+# define VTY_USE_ADDRINFO 0
+#endif
- /* Reject connection if password isn't set, and not "no password" */
- if ((host.password == NULL) && (host.password_encrypt == NULL)
- && ! no_password_check)
- {
- uty_close(vio, "Vty password is not set.");
- vty = NULL;
- }
- else
- {
- /* Say hello to the world. */
- vty_hello (vty);
+/*==============================================================================
+ * Opening and closing VTY_TERMINAL type
+ */
- if (! no_password_check)
- uty_output (vty, "\nUser Access Verification\n\n");
- } ;
+static void uty_term_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_term_read_timeout(vio_timer_t* timer,
+ void* action_info) ;
+static vty_timer_time uty_term_write_timeout(vio_timer_t* timer,
+ void* action_info) ;
+static vty_readiness_t uty_term_write(vio_vf vf) ;
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
-} ;
+static void uty_term_will_echo(vty_cli cli) ;
+static void uty_term_will_suppress_go_ahead(vty_cli cli) ;
+static void uty_term_dont_linemode(vty_cli cli) ;
+static void uty_term_do_window_size(vty_cli cli) ;
+static void uty_term_dont_lflow_ahead(vty_cli cli) ;
/*------------------------------------------------------------------------------
- * Construct vio_vf structure for new VTY_TERMINAL type VTY, and add same.
- *
+ * Create new vty of type VTY_TERMINAL -- ie attached to a telnet session.
*
+ * This is called by the accept action for the VTY_TERMINAL listener.
*/
-
static void
-uty_term_new(vty_io vio, int sock_fd)
+uty_term_open(int sock_fd, union sockunion *su)
{
- vio_vf vf ;
-
- enum vty_readiness ready ;
+ vty vty ;
+ vty_io vio ;
+ vio_vf vf ;
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
- /* May only be the first vio_vf ! */
- assert((vio->vin_base == NULL) && (vio->vout_base == NULL)) ;
-
- /* Construct and add to the vio */
- vf = uty_vf_new(vio, sock_fd, vfd_socket, vfd_io_read_write) ;
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new(VTY_TERMINAL) ;
+ vio = vty->vio ;
- uty_vin_add(vio, vf, VIN_TERM, uty_term_ready, uty_term_read_timeout) ;
- uty_vout_add(vio, vf, VIN_TERM, uty_term_ready, uty_term_write_timeout) ;
+ /* The initial vty->node depends on a number of vty type things, so
+ * we set that now.
+ *
+ * This completes the initialisation of the vty object, except that the
+ * execution and vio objects are largely empty.
+ */
+ if (host.no_password_check)
+ {
+ if (host.restricted_mode)
+ vio->vty->node = RESTRICTED_NODE;
+ else if (host.advanced)
+ vio->vty->node = ENABLE_NODE;
+ else
+ vio->vty->node = VIEW_NODE;
+ }
+ else
+ vio->vty->node = AUTH_NODE;
- /* Allocate and initialise a keystroke stream TODO: CSI ?? */
- vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
+ /* Complete the initialisation of the vty_io object.
+ *
+ * Note that the defaults for:
+ *
+ * - read_timeout -- default = 0 => no timeout
+ * - write_timeout -- default = 0 => no timeout
+ *
+ * - 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.
+ *
+ * The text form of the address identifies the VTY.
+ */
+ vf = uty_vf_new(vio, sutoa(su).str, sock_fd, vfd_socket, vfd_io_read_write) ;
- /* Pick up current timeout setting */
- vf->read_timeout = vty_timeout_val;
+ uty_vin_open( vio, vf, VIN_TERM, uty_term_ready,
+ uty_term_read_timeout,
+ 0) ; /* no ibuf required */
+ uty_vout_open(vio, vf, VOUT_TERM, uty_term_ready,
+ uty_term_write_timeout,
+ 4096) ; /* obuf is required */
- /* Use global 'lines' setting, as default. May be -1 => unset */
- vio->lines = host.lines ;
+ vf->read_timeout = host.vty_timeout_val ; /* current EXEC timeout */
- /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
- vf->olc = vio_lc_init_new(NULL, 0, 0) ;
- uty_set_height(vio) ; /* set initial state */
+ /* Set up the CLI object & initialise */
+ vf->cli = uty_cli_new(vf) ;
- /* Initialise the CLI, ready for start-up messages etc. */
- uty_cli_init(vio) ;
+ /* When we get here the VTY is set up and all ready to go. */
+ uty_cmd_prepare(vio) ;
- /* Reject connection if password isn't set, and not "no password" */
- if ((host.password == NULL) && (host.password_encrypt == NULL)
- && ! no_password_check)
- {
- uty_close(vio, "Vty password is not set.");
- vty = NULL;
- }
- else
+ /* Issue Telnet commands/escapes to be a good telnet citizen -- not much
+ * real negotiating going on -- just a statement of intentions !
+ */
+ uty_term_will_echo (vf->cli);
+ uty_term_will_suppress_go_ahead (vf->cli);
+ uty_term_dont_linemode (vf->cli);
+ uty_term_do_window_size (vf->cli);
+ if (0)
+ uty_term_dont_lflow_ahead (vf->cli) ;
+
+ /* Say hello */
+ vty_hello(vty);
+
+ /* If need password, issue prompt or give up if no password to check
+ * against !
+ */
+ if (vty->node == AUTH_NODE)
{
- /* Say hello to the world. */
- vty_hello (vty);
-
- if (! no_password_check)
- uty_output (vty, "\nUser Access Verification\n\n");
+ 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."));
} ;
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
+ /* Push the output to date and start the CLI */
+ uty_cmd_out_push(vio) ;
+ uty_cli_start(vf->cli, vty->node) ;
} ;
-
-
/*------------------------------------------------------------------------------
+ * Close the reading side of VTY_TERMINAL, and close down CLI as far as
+ * possible, given that output may be continuing.
*
+ * Expects to be called once only for the VTY_TERMINAL.
*/
extern void
-uty_term_half_close(vio_vf vf)
+uty_term_read_close(vio_vf vf)
{
vty_io vio ;
@@ -193,93 +207,60 @@ uty_term_half_close(vio_vf vf)
* Note that half closing the file sets a new timeout, sets read off
* and write on.
*/
-
-
-
uty_set_monitor(vio, 0) ;
- /* Discard everything in the keystroke stream and force it to EOF */
- if (vio->key_stream != NULL)
- keystroke_stream_set_eof(vio->key_stream) ;
-
- /* Turn off "--more--" so that all output clears without interruption.
- *
- * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
- */
- vio->cli_more_enabled = 0 ;
-
- if (vio->cli_more_wait)
- uty_cli_exit_more_wait(vio) ;
-
- /* If a command is not in progress, enable output, which will clear
- * the output buffer if there is anything there, plus any close reason,
- * and then close.
- *
- * If command is in progress, then this process will start when it
- * completes.
- */
- if (!vio->cmd_in_progress)
- vio->cmd_out_enabled = 1 ;
+ uty_cli_close(vf->cli, false) ;
/* Log closing of VTY_TERM */
assert(vio->vty->type == VTY_TERMINAL) ;
- uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", uty_vf_fd(vf)) ;
+ uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio_vfd_fd(vf->vfd)) ;
} ;
/*------------------------------------------------------------------------------
- * vprintf to VTY_TERM
- *
- * All output goes to output fifo until command completes.
+ * Close the writing side of VTY_TERMINAL.
*
- * NB: MUST be cmd_in_progress
+ * Pushes any buffered stuff to output and
*
- * Discards output if the socket is not open for whatever reason.
*/
-extern int
-uty_term_vprintf(vio_vf vf, const char *format, va_list args)
+extern bool
+uty_term_write_close(vio_vf vf, bool final)
{
- VTY_ASSERT_LOCKED() ;
-
- if (!vf->write_open)
- return 0 ; /* discard output if not open ! */
+ vty_io vio ;
+ vty_readiness_t ready ;
- assert(vf->vio->cmd_in_progress) ;
+ /* Get the vio and ensure that we are all straight */
+ vio = vf->vio ;
+ assert((vio->vin == vio->vin_base) && (vio->vin == vf)) ;
- return vio_fifo_vprintf(vf->obuf, format, args) ;
-} ;
+ /* 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) ;
-/*------------------------------------------------------------------------------
- * Set/Clear "monitor" state:
- *
- * set: if VTY_TERM and not already "monitor" (and write_open !)
- * clear: if is "monitor"
- */
-extern void
-uty_set_monitor(vty_io vio, bool on)
-{
- VTY_ASSERT_LOCKED() ;
+ vf->cli->out_active = true ; /* force the issue */
- if (on && !vio->monitor)
- {
- if ((vio->type == VTY_TERM) && vio->sock.write_open)
- {
- vio->monitor = 1 ;
- sdl_push(vio_monitors_base, vio, mon_list) ;
- } ;
- }
- else if (!on && vio->monitor)
+ do
{
- vio->monitor = 0 ;
- sdl_del(vio_monitors_base, vio, mon_list) ;
- }
+ vf->cli->out_done = false ;
+ ready = uty_term_write(vf) ;
+ } while ((ready != write_ready) && vf->cli->out_active) ;
+
+ final = final || !vf->cli->out_active ;
+
+ if (!final)
+ uty_term_set_readiness(vf, ready) ;
+
+ vf->cli = uty_cli_close(vf->cli, final) ;
+
+ return final ;
} ;
/*==============================================================================
* Action routines for VIN_TERM/VOUT_TERM type vin/vout objects
*/
-static enum vty_readiness uty_term_write(vio_vf vf) ;
-
/*==============================================================================
* Readiness and the VIN_TERM type vin.
*
@@ -289,7 +270,23 @@ static enum vty_readiness uty_term_write(vio_vf vf) ;
*
* The VIN_TERM uses read ready only when it doesn't set write ready. Does
* not set both at once.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set read/write readiness -- for VIN_TERM/VOUT_TERM
*
+ * Note that sets only one of read or write, and sets write for preference.
+ */
+extern void
+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)) ;
+} ;
+
+/*------------------------------------------------------------------------------
* So there is only one, common, uty_term_ready function, which:
*
* 1. attempts to clear any output it can.
@@ -326,39 +323,41 @@ static enum vty_readiness uty_term_write(vio_vf vf) ;
* Resets the timer because something happened.
*/
static void
-uty_term_ready(vio_fd vfd, void* action_info)
+uty_term_ready(vio_vfd vfd, void* action_info)
{
- enum vty_readiness ready ;
+ vty_readiness_t ready ;
vio_vf vf = action_info ;
- vty_io vio = vf->vio ;
- VTY_ASSERT_LOCKED() ;
+ assert(vfd == vf->vfd) ;
- vio->cmd_out_done = 0 ; /* not done any command output yet */
+ VTY_ASSERT_LOCKED() ;
uty_term_write(vf) ; /* try to clear outstanding stuff */
do
{
- ready = uty_cli(vio) ; /* do any CLI work... */
+ ready = uty_cli(vf->cli) ; /* do any CLI work... */
ready |= uty_term_write(vf) ; /* ...and any output that generates */
} while (ready >= now_ready) ;
- uty_file_set_readiness(vf, ready) ;
+ uty_term_set_readiness(vf, ready) ;
} ;
/*==============================================================================
- * Reading from VTY_TERM.
- *
- * The select/pselect call-back ends up in uty_read_ready().
+ * Reading from VTY_TERMINAL.
*
- * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
- * current CLI.
+ * The select/pselect call-back ends up in uty_term_ready().
*/
/*------------------------------------------------------------------------------
* Read a lump of bytes and shovel into the keystroke stream
*
+ * This function is called from the vty_cli to top up the keystroke buffer,
+ * or in the stealing of a keystroke to end "--more--" state.
+ *
+ * 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.
+ *
* Steal keystroke if required -- see keystroke_input()
*
* Returns: 0 => nothing available
@@ -366,25 +365,24 @@ uty_term_ready(vio_fd vfd, void* action_info)
* -1 => EOF (or not open, or failed)
*/
extern int
-uty_read (vty_io vio, keystroke steal)
+uty_term_read(vio_vf vf, keystroke steal)
{
unsigned char buf[500] ;
int get ;
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open */
+ if (vf->vin_state != vf_open)
+ return -1 ; /* at EOF if not open */
- get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
+ get = read_nb(vio_vfd_fd(vf->vfd), buf, sizeof(buf)) ;
if (get >= 0)
- keystroke_input(vio->key_stream, buf, get, steal) ;
+ keystroke_input(vf->cli->key_stream, buf, get, steal) ;
else if (get < 0)
{
if (get == -1)
- uty_file_error(vio, "read") ;
-
- vio->sock.read_open = 0 ;
- keystroke_input(vio->key_stream, NULL, 0, steal) ;
+ uty_vf_error(vf, "read", errno) ;
+ keystroke_input(vf->cli->key_stream, NULL, 0, steal) ;
+ /* Tell keystroke stream that EOF met */
get = -1 ;
} ;
@@ -396,10 +394,10 @@ uty_read (vty_io vio, keystroke steal)
*
* There are two sets of buffering:
*
- * cli -- command line -- which reflects the status of the command line
+ * cli->cbuf -- command line -- reflects the status of the command line
*
- * cmd -- command output -- which is written to the file only while
- * cmd_out_enabled.
+ * vf->obuf -- command output -- which is written to the file only while
+ * out_active.
*
* The cli output takes precedence.
*
@@ -407,8 +405,8 @@ uty_read (vty_io vio, keystroke steal)
* "--more--" mechanism.
*/
-static int uty_write_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
-static int uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
+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) ;
/*------------------------------------------------------------------------------
* Write as much as possible of what there is.
@@ -426,83 +424,79 @@ static int uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
* now_ready if should loop back and try again
* not_ready otherwise
*/
-static enum vty_readiness
+static vty_readiness_t
uty_term_write(vio_vf vf)
{
- vty_io vio = vf->vio ;
+ vty_cli cli = vf->cli ;
int ret ;
VTY_ASSERT_LOCKED() ;
ret = -1 ;
- while (vf->write_open)
+ while (vf->vout_state == vf_open)
{
/* Any outstanding line control output takes precedence */
- if (vf->olc != NULL)
- {
- ret = uty_write_lc(vf, vf->obuf, vf->olc) ;
- if (ret != 0)
- break ;
- }
+ ret = uty_write_lc(vf, vf->obuf, cli->olc) ;
+ if (ret != 0)
+ break ;
/* Next: empty out the cli output */
- ret = vio_fifo_write_nb(&vf->cli_obuf, vio->sock.fd, true) ;
+ 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 (!vio->cmd_out_enabled)
+ if (!cli->out_active)
return not_ready ; /* done all can do */
- /* Last: if there is something in the command buffer, do that */
+ /* If there is something in the command buffer, do that */
if (!vio_fifo_empty(vf->obuf))
{
- if (vio->cmd_out_done)
+#if 0
+ if (cli->out_done)
break ; /* ...but not if done once */
- vio->cmd_out_done = 1 ; /* done this once */
-
- assert(!vio->cli_more_wait) ;
+ cli->out_done = true ; /* done this once */
+#endif
+ assert(!cli->more_wait) ;
- if (vio->cmd_lc != NULL)
- ret = uty_write_fifo_lc(vf, vf->obuf, vf->olc) ;
- else
- ret = vio_fifo_write_nb(vf->obuf, vio->sock.fd, true) ;
-
- /* If 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.
- */
- if (vio->cli_more_wait)
+ ret = uty_write_fifo_lc(vf, vf->obuf, cli->olc) ;
+ if (ret != 0)
{
- ret = vio_fifo_write_nb(vf->obuf, vio->sock.fd, true) ;
- if (ret == 0)
- return now_ready ;
- } ;
+ 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 ;
- if (ret != 0)
- break ;
- }
+ return now_ready ;
+ } ;
+ } ;
/* Exciting stuff: there is nothing left to output...
*
* ... watch out for half closed state.
*/
- if (vio->half_closed)
+#if 0
+ if (vio->closing)
{
if (vio->close_reason != NULL)
{
- vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
-
- struct vty* vty = vio->vty ;
- if (vio->cli_drawn || vio->cli_dirty)
- vty_out(vty, VTY_NEWLINE) ;
- vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
-
- vio->cmd_in_progress = 0 ;
+ 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 */
@@ -513,42 +507,39 @@ uty_term_write(vio_vf vf)
return not_ready ; /* it's all over */
} ;
+#endif
- /* For VTY_TERM: if the command line is not drawn, now is a good
- * time to do that.
- */
- if (vio->vty_type == VTY_TERM)
- if (uty_cli_draw_if_required(vio))
- continue ; /* do that now. */
+ if (uty_cli_draw_if_required(cli))
+ continue ; /* do that now. */
/* There really is nothing left to output */
+ cli->out_active = false ;
+
return not_ready ;
} ;
/* Arrives here if there is more to do, or failed (or was !write_open) */
- if (ret >= 0)
+ 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->write_open)
- {
- if (!vf->read_open)
- uty_file_error(vio, "write") ;
-
- vf->write_open = false ; /* crash close write */
- } ;
+ 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) ; /* throw away cli stuff */
- uty_out_clear(vio) ; /* throw away cmd stuff */
+ vio_fifo_clear(vf->obuf, true) ; /* throw away cli stuff */
+ uty_cli_out_clear(cli) ; /* throw away cmd stuff */
- vio->close_reason = NULL ; /* too late for this */
+ cli->out_active = false ;
return not_ready ; /* NB: NOT blocked by I/O */
} ;
@@ -575,6 +566,7 @@ uty_term_write(vio_vf vf)
* 0 => all gone
* < 0 => failed (or !write_open)
*/
+#if 0
static int
uty_write_monitor(vio_vf vf)
{
@@ -592,11 +584,12 @@ uty_write_monitor(vio_vf vf)
return ret ;
} ;
- return vio_fifo_write_nb(vf->obuf, uty_vf_fd(vf), true) ;
+ return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf), true) ;
} ;
+#endif
/*------------------------------------------------------------------------------
- * Write the given FIFO to output -- subject to possible line control.
+ * Write the given FIFO to output -- subject to line control.
*
* 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.
@@ -606,16 +599,19 @@ uty_write_monitor(vio_vf vf)
*
* NB: expects that the sock is write_open
*
- * Returns: > 0 => blocked or completed one tranche
+ * Returns: > 0 => blocked or completed one tranche (more to go)
* 0 => all gone
* < 0 => failed
*/
static int
-uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
+uty_write_fifo_lc(vio_vf vf, vio_fifo vff, vio_line_control lc)
{
int ret ;
char* src ;
size_t have ;
+ vty_cli cli ;
+
+ cli = vf->cli ;
/* Collect another line_control height's worth of output.
*
@@ -624,24 +620,29 @@ uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
*/
vio_lc_set_pause(lc) ; /* clears lc->paused */
- src = vio_fifo_get_rdr(vfifo, &have) ;
+ vio_fifo_set_hold_mark(vff) ;
+ src = vio_fifo_get(vff, &have) ;
while ((src != NULL) && (!lc->paused))
{
size_t take ;
+
+ if (src == NULL)
+ break ;
+
take = vio_lc_append(lc, src, have) ;
- src = vio_fifo_step_rdr(vfifo, &have, take) ;
+ src = vio_fifo_step_get(vff, &have, take) ;
} ;
- vf->vio->cli_dirty = (lc->col != 0) ;
+ cli->dirty = (lc->col != 0) ;
/* Write the contents of the line control */
- ret = uty_write_lc(vf, vfifo, lc) ;
+ ret = uty_write_lc(vf, vff, lc) ;
if (ret < 0)
return ret ; /* give up now if failed. */
- if ((ret == 0) && vio_fifo_empty(vfifo))
+ if ((ret == 0) && vio_fifo_empty(vff))
return 0 ; /* FIFO and line control empty */
/* If should now do "--more--", now is the time to prepare for that.
@@ -652,8 +653,8 @@ uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
* The "--more--" cli will not do anything until the CLI buffer has
* cleared.
*/
- if (lc->paused && vf->vio->cli_more_enabled)
- uty_cli_enter_more_wait(vf->vio) ;
+ if (lc->paused && cli->more_enabled)
+ uty_cli_enter_more_wait(cli) ;
return 1 ; /* FIFO or line control, not empty */
} ;
@@ -670,18 +671,21 @@ uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
* < 0 => failed
*/
static int
-uty_write_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
+uty_write_lc(vio_vf vf, vio_fifo vff, vio_line_control lc)
{
int ret ;
- ret = vio_lc_write_nb(uty_vf_fd(vf), lc) ;
+ ret = vio_lc_write_nb(vio_vfd_fd(vf->vfd), lc) ;
if (ret <= 0)
- vio_fifo_sync_rdr(vfifo) ; /* finished with FIFO contents */
+ vio_fifo_clear_hold_mark(vff) ; /* finished with FIFO contents */
return ret ;
} ;
+
+#if 0
+
/*------------------------------------------------------------------------------
* Start command output -- clears down the line control.
*
@@ -693,137 +697,60 @@ uty_cmd_output_start(vio_vf vf)
{
if (vf->olc != NULL)
vio_lc_clear(vf->olc) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set the effective height for line control (if any)
- *
- * If using line_control, may enable the "--more--" output handling.
- *
- * If not, want some limit on the amount of stuff output at a time.
- *
- * Sets the line control window width and height.
- * Sets cli_more_enabled if "--more--" is enabled.
- */
-extern void
-uty_set_height(vio_vf vf)
-{
- vty_io vio = vf->vio ;
- bool on ;
-
- on = 0 ; /* default state */
- if ((vf->olc != NULL) && !vio->half_closed)
- {
- int height ;
-
- height = 0 ; /* default state */
+ vio_fifo_set_hold_mark(vf->obuf) ; /* mark to keep until all gone */
+} ;
- if ((vio->width) != 0)
- {
- /* If window size is known, use lines or given height */
- if (vio->lines >= 0)
- height = vio->lines ;
- else
- {
- /* Window height, leaving one line from previous "page"
- * and one line for the "--more--" -- if at all possible
- */
- height = vio->height - 2 ;
- if (height < 1)
- height = 1 ;
- } ;
- }
- else
- {
- /* If window size not known, use lines if that has been set
- * explicitly for this terminal.
- */
- if (vio->lines_set)
- height = vio->lines ;
- } ;
+#endif
- if (height > 0)
- on = 1 ; /* have a defined height */
- else
- height = 200 ; /* but no "--more--" */
- vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
- } ;
- vio->cli_more_enabled = on ;
-} ;
/*==============================================================================
- * Timer for VTY_TERM (and VTY_SHELL_SERV).
+ * Timer actions for VTY_TERMINAL
*/
/*------------------------------------------------------------------------------
- * Timer has expired.
+ * Read timer has expired.
+ *
+ * If closing, then this is curtains -- have waited long enough !
*
- * If half_closed, 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 void
-uty_timer_expired (vty_io vio)
+static vty_timer_time
+uty_term_read_timeout(vio_timer_t* timer, void* action_info)
{
+ vty_io vio = action_info ;
+
VTY_ASSERT_LOCKED() ;
- if (vio->half_closed)
- return uty_close(vio) ; /* curtains */
+ uty_close(vio, true, qs_set(NULL, "Timed out")) ;
- uty_close(vio, "Timed out") ; /* bring input side to a halt */
+ return 0 ;
} ;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*------------------------------------------------------------------------------
- * Set read/write readiness -- for VTY_TERM
+ * 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
*
- * Note that for VTY_TERM, set only one of read or write, and sets write for
- * preference.
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
*/
-extern void
-uty_file_set_readiness(vio_vf vf, enum vty_readiness ready)
+static vty_timer_time
+uty_term_write_timeout(vio_timer_t* timer, void* action_info)
{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- vio_fd_set_read(vf, (ready == read_ready)) ;
- vio_fd_set_write(vf, (ready >= write_ready)) ;
-} ;
+ vty_io vio = action_info ;
-/*------------------------------------------------------------------------------
- * Set a new timer value.
- */
-extern void
-uty_file_set_timer(vio_vf vf, unsigned long timeout)
-{
VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- vf->v_timeout = timeout ;
-} ;
-
-
-
+ uty_close(vio, true, qs_set(NULL, "Timed out")) ;
+ return 0 ;
+} ;
/*==============================================================================
* VTY Listener(s) for VTY_TERMINAL
@@ -1055,7 +982,7 @@ uty_term_listen_open(sa_family_t family, int type, int protocol,
} ;
/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM
+ * Accept action -- create and dispatch VTY_TERMINAL
*/
static void
uty_term_accept(int sock_listen)
@@ -1093,24 +1020,24 @@ uty_term_accept(int sock_listen)
p = sockunion2hostprefix (&su);
ret = 0 ; /* so far, so good */
- if ((p->family == AF_INET) && vty_accesslist_name)
+ if ((p->family == AF_INET) && host.vty_accesslist_name)
{
/* VTY's accesslist apply. */
struct access_list* acl ;
- if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
+ if ((acl = access_list_lookup (AFI_IP, host.vty_accesslist_name))
+ && (access_list_apply (acl, p) == FILTER_DENY))
ret = -1 ;
}
#ifdef HAVE_IPV6
- if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
+ if ((p->family == AF_INET6) && host.vty_ipv6_accesslist_name)
{
/* VTY's ipv6 accesslist apply. */
struct access_list* acl ;
- if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
+ if ((acl = access_list_lookup (AFI_IP6, host.vty_ipv6_accesslist_name))
+ && (access_list_apply (acl, p) == FILTER_DENY))
ret = -1 ;
}
#endif /* HAVE_IPV6 */
@@ -1132,8 +1059,8 @@ uty_term_accept(int sock_listen)
uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
sock_fd, errtoa(errno, 0).str) ;
- /* All set -- create the VTY_TERM */
- uty_term_accept_new(sock_fd, &su);
+ /* 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,
@@ -1143,6 +1070,289 @@ uty_term_accept(int sock_listen)
} ;
/*==============================================================================
+ * VTY telnet stuff
+ *
+ * Note that all telnet commands (escapes) and any debug stuff is treated as
+ * CLI output.
+ */
+
+#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
+
+static const char* telnet_commands[256] =
+{
+ [tn_IAC ] = "IAC",
+ [tn_DONT ] = "DONT",
+ [tn_DO ] = "DO",
+ [tn_WONT ] = "WONT",
+ [tn_WILL ] = "WILL",
+ [tn_SB ] = "SB",
+ [tn_GA ] = "GA",
+ [tn_EL ] = "EL",
+ [tn_EC ] = "EC",
+ [tn_AYT ] = "AYT",
+ [tn_AO ] = "AO",
+ [tn_IP ] = "IP",
+ [tn_BREAK] = "BREAK",
+ [tn_DM ] = "DM",
+ [tn_NOP ] = "NOP",
+ [tn_SE ] = "SE",
+ [tn_EOR ] = "EOR",
+ [tn_ABORT] = "ABORT",
+ [tn_SUSP ] = "SUSP",
+ [tn_EOF ] = "EOF",
+} ;
+
+static const char* telnet_options[256] =
+{
+ [to_BINARY] = "BINARY", /* 8-bit data path */
+ [to_ECHO] = "ECHO", /* echo */
+ [to_RCP] = "RCP", /* prepare to reconnect */
+ [to_SGA] = "SGA", /* suppress go ahead */
+ [to_NAMS] = "NAMS", /* approximate message size */
+ [to_STATUS] = "STATUS", /* give status */
+ [to_TM] = "TM", /* timing mark */
+ [to_RCTE] = "RCTE", /* remote controlled tx and echo */
+ [to_NAOL] = "NAOL", /* neg. about output line width */
+ [to_NAOP] = "NAOP", /* neg. about output page size */
+ [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
+ [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
+ [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
+ [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
+ [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
+ [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
+ [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
+ [to_XASCII] = "XASCII", /* extended ascii character set */
+ [to_LOGOUT] = "LOGOUT", /* force logout */
+ [to_BM] = "BM", /* byte macro */
+ [to_DET] = "DET", /* data entry terminal */
+ [to_SUPDUP] = "SUPDUP", /* supdup protocol */
+ [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
+ [to_SNDLOC] = "SNDLOC", /* send location */
+ [to_TTYPE] = "TTYPE", /* terminal type */
+ [to_EOR] = "EOR", /* end or record */
+ [to_TUID] = "TUID", /* TACACS user identification */
+ [to_OUTMRK] = "OUTMRK", /* output marking */
+ [to_TTYLOC] = "TTYLOC", /* terminal location number */
+ [to_3270REGIME] = "3270REGIME", /* 3270 regime */
+ [to_X3PAD] = "X3PAD", /* X.3 PAD */
+ [to_NAWS] = "NAWS", /* window size */
+ [to_TSPEED] = "TSPEED", /* terminal speed */
+ [to_LFLOW] = "LFLOW", /* remote flow control */
+ [to_LINEMODE] = "LINEMODE", /* Linemode option */
+ [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
+ [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
+ [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
+ [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
+ [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
+ [to_EXOPL] = "EXOPL", /* extended-options-list */
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as decimal.
+ */
+static void
+uty_cli_out_dec(vty_cli cli, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(cli, "%s ", str) ;
+ else
+ uty_cli_out(cli, "%d ", (int)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as hex.
+ */
+static void
+uty_cli_out_hex(vty_cli cli, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(cli, "%s ", str) ;
+ else
+ uty_cli_out(cli, "0x%02x ", (unsigned)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "WILL TELOPT_ECHO"
+ */
+static void
+uty_term_will_echo (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "suppress Go-Ahead"
+ */
+static void
+uty_term_will_suppress_go_ahead (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use linemode"
+ */
+static void
+uty_term_dont_linemode (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "Use window size"
+ */
+static void
+uty_term_do_window_size (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use lflow" -- not currently used
+ */
+static void
+uty_term_dont_lflow_ahead (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Process incoming Telnet Option(s)
+ *
+ * May be called during keystroke iac callback, or when processing CLI
+ * keystrokes.
+ *
+ * In particular: get telnet window size.
+ *
+ * Returns: true <=> dealt with, for:
+ *
+ * * telnet window size.
+ */
+extern bool
+uty_telnet_command(vio_vf vf, keystroke stroke, bool callback)
+{
+ uint8_t* p ;
+ uint8_t o ;
+ int left ;
+ bool dealt_with ;
+
+ vty_cli cli = vf->cli ;
+
+ /* Echo to the other end if required */
+ if (TELNET_OPTION_DEBUG)
+ {
+ uty_cli_wipe(cli, 0) ;
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ uty_cli_out_hex(cli, telnet_commands[tn_IAC], tn_IAC) ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(cli, telnet_commands[*p], *p) ;
+ ++p ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(cli, telnet_options[*p], *p) ;
+ ++p ;
+
+ if (left > 0)
+ {
+ while(left-- > 0)
+ uty_cli_out_hex(cli, NULL, *p++) ;
+
+ if (stroke->flags & kf_truncated)
+ uty_cli_out(cli, "... ") ;
+
+ if (!(stroke->flags & kf_broken))
+ {
+ uty_cli_out_hex(cli, telnet_commands[tn_IAC], tn_IAC) ;
+ uty_cli_out_hex(cli, telnet_commands[tn_SE], tn_SE) ;
+ }
+ } ;
+
+ if (stroke->flags & kf_broken)
+ uty_cli_out (cli, "BROKEN") ;
+
+ uty_cli_out_newline(cli) ;
+ } ;
+
+ /* Process the telnet command */
+ dealt_with = false ;
+
+ if (stroke->flags != 0)
+ return dealt_with ; /* go no further if broken */
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ passert(left >= 1) ; /* must be if not broken ! */
+ passert(stroke->value == *p) ; /* or something is wrong */
+
+ ++p ; /* step past X of IAC X */
+ --left ;
+
+ /* Decode the one command that is interesting -- "NAWS" */
+ switch (stroke->value)
+ {
+ case tn_SB:
+ passert(left > 0) ; /* or parser failed */
+
+ o = *p++ ; /* the option byte */
+ --left ;
+ switch(o)
+ {
+ case to_NAWS:
+ if (left != 4)
+ {
+ uzlog(NULL, LOG_WARNING,
+ "RFC 1073 violation detected: telnet NAWS option "
+ "should send %d characters, but we received %d",
+ (3 + 4 + 2), (3 + left + 2)) ;
+ }
+ else
+ {
+ int width, height ;
+
+ width = *p++ << 8 ; width += *p++ ;
+ height = *p++ << 8 ; height += *p ;
+
+ if (TELNET_OPTION_DEBUG)
+ {
+ uty_cli_out(cli, "TELNET NAWS window size received: "
+ "width %d, height %d", width, height) ;
+ uty_cli_out_newline(cli) ;
+ } ;
+
+ uty_cli_set_window(cli, width, height) ;
+
+ dealt_with = true ;
+ } ;
+ break ;
+
+ default: /* no other IAC SB <option> */
+ break ;
+ } ;
+ break ;
+
+ default: /* no other IAC X */
+ break ;
+ } ;
+
+ return dealt_with ;
+} ;
+
+/*==============================================================================
* Output to vty which are set to "monitor".
*
* This is VERY TRICKY.
@@ -1230,6 +1440,7 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
*/
while (vio != NULL)
{
+#if 0
if (!vio->monitor_busy)
{
int ret ;
@@ -1241,7 +1452,7 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
ret = uty_write_monitor(vio) ;
if (ret == 0)
{
- ret = write_nb(uty_vf_fd(vf), ll->line, ll->len) ;
+ ret = write_nb(vio_vfd_fd(vf->vfd), ll->line, ll->len) ;
if (ret >= 0)
{
@@ -1258,7 +1469,7 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
if (ret >= 0)
vio->monitor_busy = 0 ;
} ;
-
+#endif
vio = sdl_next(vio, mon_list) ;
} ;
} ;
@@ -1280,8 +1491,8 @@ vty_log_fixed (const char *buf, size_t len)
vio = sdl_head(vio_monitors_base) ;
while (vio != NULL)
{
- write(uty_vf_fd(vf), buf, len) ;
- write(uty_vf_fd(vf), "\r\n", 2) ;
+ 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) ;
} ;