summaryrefslogtreecommitdiffstats
path: root/lib/vty_io.c
diff options
context:
space:
mode:
authorChris Hall <GMCH@hestia.halldom.com>2010-04-06 02:10:30 +0100
committerChris Hall <GMCH@hestia.halldom.com>2010-04-06 02:10:30 +0100
commit8fea5ca7104c0d95108947661a4991b61b2ee06e (patch)
tree7ad44a658a61d4a8dfb43ca5b6122c5626f68ea0 /lib/vty_io.c
parentc933cf7233f51f677ab01689f175ceb3dc5361f6 (diff)
downloadquagga-8fea5ca7104c0d95108947661a4991b61b2ee06e.tar.bz2
quagga-8fea5ca7104c0d95108947661a4991b61b2ee06e.tar.xz
First beta release
Various bug fixes and improvements. Running with a fair amount of debug/assert code, which must be removed at some date.
Diffstat (limited to 'lib/vty_io.c')
-rw-r--r--lib/vty_io.c1785
1 files changed, 1163 insertions, 622 deletions
diff --git a/lib/vty_io.c b/lib/vty_io.c
index 15f90219..7137fbdc 100644
--- a/lib/vty_io.c
+++ b/lib/vty_io.c
@@ -46,21 +46,12 @@
/*==============================================================================
* VTY Command Output -- base functions
*
- * ALL vty command output ends up here.
- *
- * vty == NULL => vprintf(stdout, ...)
- * VTY_SHELL => vprintf(stdout, ...)
- * VTY_STDOUT => vprintf(stdout, ...)
- *
- * VTY_FILE => write(fd, ....)
- *
- * VTY_TERM => command FIFO
- * VTY_SHELL_SERV => command FIFO
- *
* During command processing the output sent here is held until the command
* completes.
*/
+static int uty_config_write(vty_io vio, bool all) ;
+
/*------------------------------------------------------------------------------
* VTY output function -- cf fprintf
*
@@ -84,71 +75,111 @@ uty_out (struct vty *vty, const char *format, ...)
*
* Returns: >= 0 => OK
* < 0 => failed (see errno)
+ *
+ * NB: for VTY_TERM and for VTY_SHELL_SERV -- this is command output:
+ *
+ * * MAY NOT do any command output if !cmd_enabled
+ *
+ * * first, the life of a vty is not guaranteed unless cmd_in_progress,
+ * so should not attempt to use a vty anywhere other than command
+ * execution.
+ *
+ * * second, cmd_out_enabled is false most of the time, and is only
+ * set true when a command completes, and it is time to write away
+ * the results.
+ *
+ * * all output is placed in the vio->cmd_obuf. When the command completes,
+ * the contents of the cmd_obuf will be written away -- subject to line
+ * control.
+ *
+ * * output is discarded if the vty is no longer write_open
*/
extern int
uty_vout(struct vty *vty, const char *format, va_list args)
{
- enum vty_type type ;
vty_io vio ;
- int len ;
+ int ret ;
VTY_ASSERT_LOCKED() ;
- /* Establish type of vty -- if any */
- if (vty != NULL)
- {
- vio = vty->vio ;
- if (!vio->file.write_open)
- return 0 ; /* discard output if not open ! */
-
- type = vio->type ;
- }
- else
- {
- vio = NULL ;
- type = VTY_STDOUT ;
- } ;
+ vio = vty->vio ;
- /* Output -- process depends on type */
- switch (type)
+ switch (vio->type)
{
case VTY_STDOUT:
case VTY_SHELL:
- len = vprintf (format, args);
+ ret = vprintf (format, args) ;
+ break ;
+
+ case VTY_STDERR:
+ ret = vfprintf (stderr, format, args) ;
+ break ;
+
+ case VTY_CONFIG_WRITE:
+ ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
+ if ((ret > 0) && vio_fifo_full_lump(&vio->cmd_obuf))
+ ret = uty_config_write(vio, false) ;
break ;
- case VTY_FILE:
case VTY_TERM:
case VTY_SHELL_SERV:
+ assert(vio->cmd_in_progress) ;
- len = qs_vprintf(&vio->cmd_vbuf, format, args) ;
- if (len > 0)
- {
- if (type == VTY_FILE)
- len = write(vio->file.fd, vio->cmd_vbuf.body, len) ;
- else
- vio_fifo_put(&vio->cmd_obuf, vio->cmd_vbuf.body, len) ;
- } ;
+ if (!vio->sock.write_open)
+ return 0 ; /* discard output if not open ! */
+
+ /* fall through.... */
+
+ case VTY_CONFIG_READ:
+ ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
break ;
default:
zabort("impossible VTY type") ;
} ;
- return len;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
- * Discard the current contents of the command FIFO
+ * Clear the contents of the command output FIFO etc.
*
- * TODO: worry about line control ??
+ * NB: does not change any of the cli_blocked/cmd_in_progress/cli_wait_more/etc
+ * flags -- competent parties must deal with those
*/
extern void
-uty_out_discard(vty_io vio)
+uty_out_clear(vty_io vio)
{
VTY_ASSERT_LOCKED() ;
- vio_fifo_set_empty(&vio->cmd_obuf) ;
+ vio_fifo_clear(&vio->cmd_obuf) ;
+
+ if (vio->cmd_lc != NULL)
+ vio_lc_clear(vio->cmd_lc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Flush the contents of the command output FIFO to the given file.
+ *
+ * Takes no notice of any errors !
+ */
+extern void
+uty_out_fflush(vty_io vio, FILE* file)
+{
+ char* src ;
+ size_t have ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ fflush(file) ;
+
+ while ((src = vio_fifo_get_lump(&vio->cmd_obuf, &have)) != NULL)
+ {
+ fwrite(src, 1, have, file) ;
+ vio_fifo_got_upto(&vio->cmd_obuf, src + have) ;
+ } ;
+
+ fflush(file) ;
} ;
/*==============================================================================
@@ -167,30 +198,13 @@ enum { vty_watch_dog_interval = 5 } ;
static void vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when) ;
static int vty_watch_dog_thread(struct thread *thread) ;
+static void uty_watch_dog_bark(void) ;
+static bool uty_death_watch_scan(void) ;
+
/*------------------------------------------------------------------------------
- * Watch dog action
+ * Start watch dog -- the first time a VTY is created.
*/
-static void
-uty_watch_dog_bark(void)
-{
- uty_check_host_name() ; /* check for host name change */
-
- /* TODO: death watch scan */
-
- /* Set timer to go off again later */
- if (vty_cli_nexus)
- qtimer_set(vty_watch_dog.qnexus, qt_add_monotonic(vty_watch_dog_interval),
- vty_watch_dog_qnexus) ;
- else
- {
- if (vty_watch_dog.thread != NULL)
- thread_cancel (vty_watch_dog.thread);
- vty_watch_dog.thread = thread_add_timer (vty_master,
- vty_watch_dog_thread, NULL, vty_watch_dog_interval) ;
- } ;
-} ;
-
-static void
+extern void
uty_watch_dog_start()
{
if (vty_cli_nexus)
@@ -200,6 +214,12 @@ uty_watch_dog_start()
uty_watch_dog_bark() ; /* start up by barking the first time */
}
+/*------------------------------------------------------------------------------
+ * Stop watch dog timer -- at close down.
+ *
+ * Final run along the death-watch
+ *
+ */
extern void
uty_watch_dog_stop(void)
{
@@ -210,6 +230,8 @@ uty_watch_dog_stop(void)
else
thread_cancel(vty_watch_dog.thread) ;
} ;
+
+ uty_death_watch_scan() ; /* scan the death-watch list */
}
/*------------------------------------------------------------------------------
@@ -219,7 +241,9 @@ static void
vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when)
{
VTY_LOCK() ;
+
uty_watch_dog_bark() ;
+
VTY_UNLOCK() ;
} ;
@@ -230,17 +254,77 @@ static int
vty_watch_dog_thread(struct thread *thread)
{
VTY_LOCK() ;
+
+ vty_watch_dog.thread = NULL ;
uty_watch_dog_bark() ;
+
VTY_UNLOCK() ;
return 0 ;
} ;
+/*------------------------------------------------------------------------------
+ * Watch dog action
+ */
+static void
+uty_watch_dog_bark(void)
+{
+ uty_check_host_name() ; /* check for host name change */
+
+ uty_death_watch_scan() ; /* scan the death-watch list */
+
+ /* Set timer to go off again later */
+ if (vty_cli_nexus)
+ qtimer_set(vty_watch_dog.qnexus,
+ qt_add_monotonic(QTIME(vty_watch_dog_interval)),
+ vty_watch_dog_qnexus) ;
+ else
+ {
+ if (vty_watch_dog.thread != NULL)
+ thread_cancel (vty_watch_dog.thread);
+ vty_watch_dog.thread = thread_add_timer (vty_master,
+ vty_watch_dog_thread, NULL, vty_watch_dog_interval) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Scan the death watch list.
+ *
+ * A vty may finally be freed if it is closed and there is no command in
+ * progress.
+ */
+static bool
+uty_death_watch_scan(void)
+{
+ vty_io vio ;
+ vty_io next ;
+
+ next = vio_death_watch ;
+ while (next != NULL)
+ {
+ vio = next ;
+ next = sdl_next(vio, vio_list) ;
+
+ if (vio->closed && !vio->cmd_in_progress)
+ {
+ uty_close(vio) ; /* closes again to ensure that all buffers
+ are released. */
+
+ sdl_del(vio_death_watch, vio, vio_list) ;
+
+ XFREE(MTYPE_VTY, vio->vty) ;
+ XFREE(MTYPE_VTY, vio) ;
+ } ;
+ } ;
+
+ return (vio_death_watch == NULL) ;
+} ;
+
/*==============================================================================
* Prototypes.
*/
-static void uty_file_init_new(vio_file file, int fd, void* info) ;
-static void uty_file_half_close(vio_file file) ;
-static void uty_file_close(vio_file file) ;
+static void uty_sock_init_new(vio_sock sock, int fd, void* info) ;
+static void uty_sock_half_close(vio_sock sock) ;
+static void uty_sock_close(vio_sock sock) ;
static void vty_read_qnexus (qps_file qf, void* file_info) ;
static void vty_write_qnexus (qps_file qf, void* file_info) ;
@@ -253,6 +337,8 @@ static int vty_timer_thread (struct thread *thread) ;
static void vtysh_read_qnexus (qps_file qf, void* file_info) ;
static int vtysh_read_thread (struct thread *thread) ;
+static enum vty_readiness uty_write(vty_io vio) ;
+
/*==============================================================================
* Creation and destruction of VTY objects
*/
@@ -260,109 +346,151 @@ static int vtysh_read_thread (struct thread *thread) ;
/*------------------------------------------------------------------------------
* Allocate new vty struct
*
- * Allocates and initialises vty_io structure, complete with:
+ * Allocates and initialises basic vty and vty_io structures, setting the
+ * given type.
*
- * Output buffer
- * Input buffer
- * qpselect file -- added to CLI nexus ) if running CLI nexus
- * qtimer )
+ * Note that where is not setting up a vty_sock, this *may* be called from
+ * any thread.
*
- * Adds to the known vty's -- which locks/unlocks momentarily.
+ * NB: may not create a VTY_CONFIG_WRITE type vty directly
+ *
+ * see: vty_open_config_write() and vty_close_config_write()
+ *
+ * NB: the sock_fd *must* be valid for VTY_TERM and VTY_SHELL_SERV.
+ * (So MUST be in the CLI thread to set those up !)
+ *
+ * the sock_fd is ignored for everything else.
*
* Returns: new vty
*/
extern struct vty *
-uty_new (int fd, enum vty_type type)
+uty_new(enum vty_type type, int sock_fd)
{
struct vty *vty ;
struct vty_io* vio ;
VTY_ASSERT_LOCKED() ;
- if (vty_watch_dog.anon == NULL)
- uty_watch_dog_start() ;
+ /* If this is a VTY_TERM or a VTY_SHELL, place */
+ switch (type)
+ {
+ case VTY_TERM: /* Require fd -- Telnet session */
+ case VTY_SHELL_SERV: /* Require fd -- Unix socket */
+ assert(sock_fd >= 0) ;
+ break ;
+
+ case VTY_CONFIG_WRITE:
+ zabort("may not make a new VTY_CONFIG_WRITE VTY") ;
+ break ;
+ case VTY_CONFIG_READ:
+ case VTY_STDOUT:
+ case VTY_STDERR:
+ case VTY_SHELL:
+ sock_fd = -1 ; /* No fd -- output to stdout/stderr */
+ break ;
+
+ default:
+ zabort("unknown VTY type") ;
+ } ;
+
+ /* Basic allocation */
vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
vio = XCALLOC (MTYPE_VTY, sizeof (struct vty_io)) ;
vty->vio = vio ;
vio->vty = vty ;
- /* Zeroising the vty structure has set:
- *
- * node = 0 TODO: something better for node value ????
- * buf = NULL -- no command line, yet
- * index = NULL -- nothing, yet
- * index_sub = NULL -- nothing, yet
- */
- confirm(AUTH_NODE == 0) ; /* default node type */
-
- if (type == VTY_TERM)
- vty->newline = "\r\n" ;
- else
- vty->newline = "\n" ;
-
/* Zeroising the vty_io structure has set:
*
+ * name = NULL -- no name, yet
+ *
* vio_list both pointers NULL
+ * mon_list both pointers NULL
*
- * half_closed = 0 -- NOT half closed (important !)
- * timed_out = 0 -- NOT timed out
+ * half_closed = 0 -- NOT half closed (important !)
+ * closed = 0 -- NOT closed (important !)
+ * close_reason = NULL -- no reason, yet
*
- * mon_list both pointers NULL
+ * real_type = 0 -- not material
+ * file_fd = 0 -- not material
+ * file_error = 0 -- not material
*
- * name = NULL -- no name, yet
+ * key_stream = NULL -- no key stream (always empty, at EOF)
*
- * cli_drawn = 0 -- not drawn
+ * cli_drawn = 0 -- not drawn
+ * cli_dirty = 0 -- not dirty
* cli_prompt_len = 0 )
- * cli_extra_len = 0 ) not material
+ * cli_extra_len = 0 ) not material
* cli_echo_suppress = 0 )
*
- * cli_prompt_node = 0 -- not material
- * cli_prompt_set = 0 -- so prompt needs to be constructed
+ * cli_prompt_node = 0 -- not material
+ * cli_prompt_set = 0 -- so prompt needs to be constructed
+ *
+ * cli_blocked = 0 -- not blocked
+ * cmd_in_progress = 0 -- no command in progress
+ * cmd_out_enabled = 0 -- command output is disabled
+ * cli_wait_more = 0 -- not waiting for response to "--more--"
+ *
+ * cli_more_enabled = 0 -- not enabled for "--more--"
*
- * cli_blocked = 0 -- not blocked
- * cmd_in_progress = 0 -- no command in progress
+ * cmd_out_done = 0 -- not material
*
- * cli_do = 0 = cli_do_nothing
+ * cli_do = 0 == cli_do_nothing
*
- * cmd_wait_more = 0 -- not waiting for response to "--more--"
+ * cmd_lc = NULL -- no line control
*
- * fail = 0 -- no login failures yet
+ * fail = 0 -- no login failures yet
*
* hist = empty vector
- * hp = 0 -- at the beginning
- * hindex = 0 -- the beginning
+ * hp = 0 -- at the beginning
+ * hindex = 0 -- the beginning
*
- * width = 0 -- unknown console width
- * height = 0 -- unknown console height
+ * width = 0 -- unknown console width
+ * height = 0 -- unknown console height
*
- * lines = 0 -- unset
+ * lines = 0 -- no limit
+ * lines_set = 0 -- no explicit setting
*
- * monitor = 0 -- not a monitor
+ * monitor = 0 -- not a monitor
+ * monitor_busy = 0 -- not a busy monitor
*
- * config = 0 -- not holder of "config" mode
+ * config = 0 -- not holder of "config" mode
*/
confirm(cli_do_nothing == 0) ;
+ confirm(AUTH_NODE == 0) ; /* default node type */
vio->type = type ;
- uty_file_init_new(&vio->file, fd, vio) ;
-
- vio->key_stream = keystroke_stream_new('\0') ; /* TODO: CSI ?? */
+ /* Zeroising the vty structure has set:
+ *
+ * node = 0 TODO: something better for node value ????
+ * buf = NULL -- no command line, yet
+ * parsed = NULL -- no parsed command, yet
+ * lineno = 0 -- nothing read, yet
+ * index = NULL -- nothing, yet
+ * index_sub = NULL -- nothing, yet
+ */
+ if (type == VTY_TERM)
+ vty->newline = "\n" ; /* line control looks after "\r\n" */
+ else
+ vty->newline = "\n" ;
- qs_init_new(&vio->ibuf, 0) ;
+ /* Initialise the vio_sock, */
+ uty_sock_init_new(&vio->sock, sock_fd, vio) ;
+ /* Make sure all buffers etc. are initialised clean and empty.
+ *
+ * Note that no buffers are actually allocated at this stage.
+ */
qs_init_new(&vio->cli_prompt_for_node, 0) ;
qs_init_new(&vio->cl, 0) ;
qs_init_new(&vio->clx, 0) ;
- qs_init_new(&vio->cli_vbuf, 0) ;
- vio_fifo_init_new(&vio->cli_obuf, 4 * 1024) ; /* allocate in 4K lumps */
+ vio_fifo_init_new(&vio->cli_obuf, 2 * 1024) ; /* allocate in 2K lumps */
- qs_init_new(&vio->cmd_vbuf, 0) ;
- vio_fifo_init_new(&vio->cmd_obuf, 16 * 1024) ;
+ vio_fifo_init_new(&vio->cmd_obuf, 8 * 1024) ;
/* Place on list of known vio/vty */
sdl_push(vio_list_base, vio, vio_list) ;
@@ -376,29 +504,34 @@ uty_new (int fd, enum vty_type type)
* Returns: new vty
*/
static struct vty *
-uty_new_term(int vty_sock, union sockunion *su)
+uty_new_term(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_sock, VTY_TERM) ;
+ vty = uty_new (VTY_TERM, sock_fd) ;
vio = vty->vio ;
- /* Set the action functions */
+ /* Allocate and initialise a keystroke stream TODO: CSI ?? */
+ vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
+
+ /* Set the socket action functions */
if (vty_cli_nexus)
{
- vio->file.action.read.qnexus = vty_read_qnexus ;
- vio->file.action.write.qnexus = vty_write_qnexus ;
- vio->file.action.timer.qnexus = vty_timer_qnexus ;
+ vio->sock.action.read.qnexus = vty_read_qnexus ;
+ vio->sock.action.write.qnexus = vty_write_qnexus ;
+ vio->sock.action.timer.qnexus = vty_timer_qnexus ;
}
else
{
- vio->file.action.read.thread = vty_read_thread ;
- vio->file.action.write.thread = vty_write_thread ;
- vio->file.action.timer.thread = vty_timer_thread ;
+ vio->sock.action.read.thread = vty_read_thread ;
+ vio->sock.action.write.thread = vty_write_thread ;
+ vio->sock.action.timer.thread = vty_timer_thread ;
} ;
/* The text form of the address identifies the VTY */
@@ -418,41 +551,38 @@ uty_new_term(int vty_sock, union sockunion *su)
vty->node = AUTH_NODE;
/* Pick up current timeout setting */
- vio->file.v_timeout = vty_timeout_val;
+ vio->sock.v_timeout = vty_timeout_val;
- /* Use global 'lines' setting, otherwise is unset */
- if (host.lines >= 0)
- vio->lines = host.lines;
- else
- vio->lines = -1;
+ /* Use global 'lines' setting, as default. May be -1 => unset */
+ vio->lines = host.lines ;
- /* Setting up terminal. */
- uty_will_echo (vio);
- uty_will_suppress_go_ahead (vio);
- uty_dont_linemode (vio);
- uty_do_window_size (vio);
- if (0)
- uty_dont_lflow_ahead (vio) ;
+ /* 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 */
- /* Set CLI into state waiting for output to complete. */
- vio->cli_blocked = 1 ;
- uty_file_set_write(&vio->file, on) ;
+ /* Initialise the CLI, ready for start-up messages etc. */
+ uty_cli_init(vio) ;
/* Reject connection if password isn't set, and not "no password" */
if ((host.password == NULL) && (host.password_encrypt == NULL)
&& ! no_password_check)
{
- uty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
- uty_half_close (vio);
- return NULL;
+ uty_half_close (vio, "Vty password is not set.");
+ vty = NULL;
}
+ else
+ {
+ /* Say hello to the world. */
+ vty_hello (vty);
- /* Say hello to the world. */
- vty_hello (vty);
-
- if (! no_password_check)
- uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
+ if (! no_password_check)
+ uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
VTY_NEWLINE, VTY_NEWLINE);
+ } ;
+
+ /* Now start the CLI and set a suitable state of readiness */
+ ready = uty_cli_start(vio) ;
+ uty_sock_set_readiness(&vio->sock, ready) ;
return vty;
} ;
@@ -463,7 +593,7 @@ uty_new_term(int vty_sock, union sockunion *su)
* Returns: new vty
*/
static struct vty *
-uty_new_shell_serv(int vty_sock)
+uty_new_shell_serv(int sock_fd)
{
struct vty *vty ;
vty_io vio ;
@@ -471,30 +601,27 @@ uty_new_shell_serv(int vty_sock)
VTY_ASSERT_LOCKED() ;
/* Allocate new vty structure and set up default values. */
- vty = uty_new (vty_sock, VTY_SHELL_SERV) ;
+ vty = uty_new (VTY_SHELL_SERV, sock_fd) ;
vio = vty->vio ;
/* Set the action functions */
if (vty_cli_nexus)
{
- vio->file.action.read.qnexus = vtysh_read_qnexus ;
- vio->file.action.write.qnexus = vty_write_qnexus ;
- vio->file.action.timer.qnexus = NULL ;
+ vio->sock.action.read.qnexus = vtysh_read_qnexus ;
+ vio->sock.action.write.qnexus = vty_write_qnexus ;
+ vio->sock.action.timer.qnexus = NULL ;
}
else
{
- vio->file.action.read.thread = vtysh_read_thread ;
- vio->file.action.write.thread = vty_write_thread ;
- vio->file.action.timer.thread = NULL ;
+ vio->sock.action.read.thread = vtysh_read_thread ;
+ vio->sock.action.write.thread = vty_write_thread ;
+ vio->sock.action.timer.thread = NULL ;
} ;
vty->node = VIEW_NODE;
- /* Enable the command output to clear the output to date, and set cli
- * state to blocked waiting for that output to complete.
- */
- vio->cli_blocked = 1 ;
- uty_file_set_write(&vio->file, on) ;
+ /* Kick start the CLI etc. */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
return vty;
} ;
@@ -512,7 +639,7 @@ uty_set_monitor(vty_io vio, bool on)
if (on && !vio->monitor)
{
- if ((vio->type == VTY_TERM) && vio->file.write_open)
+ if ((vio->type == VTY_TERM) && vio->sock.write_open)
{
vio->monitor = 1 ;
sdl_push(vio_monitors_base, vio, mon_list) ;
@@ -539,50 +666,83 @@ uty_get_name(vty_io vio)
/*------------------------------------------------------------------------------
* Closing down VTY for reading.
*
- * Shuts the read side and discards any buffered input.
+ * For VTY_TERM (must be in CLI thread):
+ *
+ * * shut the socket for reading
+ * * discard all buffered input, setting it to "EOF"
+ * * turns off any monitor status !
+ * * drop down to RESTRICTED_NODE
+ *
+ * For VTY_SHELL_SERV (must be in CLI thread):
+ *
+ * * shut the socket for reading
+ * * discard all buffered input
+ * * drop down to RESTRICTED_NODE
*
- * Leaves the output running, but places the VTY on "death watch". When
- * all output completes and there is no queued command or anything else
- * active, the VTY is finally put to sleep.
+ * In all cases:
+ *
+ * * place on death watch
+ * * set the vty half_closed
+ * * sets the reason for closing (if any given)
+ *
+ * For VTY_TERM and VTY_SHELL_SERV, when the output side has emptied out all
+ * the buffers, the VTY is closed.
+ *
+ * May already have set the vio->close_reason, or can set it now. (Passing a
+ * NULL reason has no effect on any existing posted reason.)
*/
extern void
-uty_half_close (vty_io vio)
+uty_half_close (vty_io vio, const char* reason)
{
- char* line ;
-
VTY_ASSERT_LOCKED() ;
if (vio->half_closed)
- return ; /* cannot do it again */
-
- vio->half_closed = 1 ;
+ return ;
- uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio->file.fd) ;
+ if (reason != NULL)
+ vio->close_reason = reason ;
- uty_file_half_close(&vio->file) ;
+ /* Do the file side of things
+ *
+ * Note that half closing the file sets a new timeout, sets read off
+ * and write on.
+ */
+ uty_sock_half_close(&vio->sock) ;
uty_set_monitor(vio, 0) ;
- keystroke_stream_free(vio->key_stream) ;
- qs_free_body(&vio->ibuf) ;
+ /* Discard everything in the keystroke stream and force it to EOF */
+ if (vio->key_stream != NULL)
+ keystroke_stream_set_eof(vio->key_stream) ;
- uty_cli_wipe(vio) ;
-
- while ((line = vector_ream_keep(&vio->hist)) != NULL)
- XFREE(MTYPE_VTY_HIST, line) ;
+ /* Turn off "--more--" so that all output clears without interruption.
+ *
+ * Note that if is waiting for "--more--", then shutting the read side
+ * causes it to be readable, but EOF -- so that will flush through.
+ */
+ vio->cli_more_enabled = 0 ;
- /* Hit the width, height and lines so that all output clears without
- * interruption.
+ /* 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.
*/
- vio->width = 0 ;
- vio->height = 0 ;
- vio->lines = 0 ;
+ if (!vio->cmd_in_progress)
+ vio->cmd_out_enabled = 1 ;
/* Make sure no longer holding the config symbol of power */
- uty_config_unlock(vio->vty, AUTH_NODE) ;
+ uty_config_unlock(vio->vty, RESTRICTED_NODE) ;
+
+ /* Log closing of VTY_TERM */
+ if (vio->type == VTY_TERM)
+ uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio->sock.fd) ;
/* Move to the death watch list */
sdl_del(vio_list_base, vio, vio_list) ;
sdl_push(vio_death_watch, vio, vio_list) ;
+
+ vio->half_closed = 1 ;
} ;
/*------------------------------------------------------------------------------
@@ -590,118 +750,185 @@ uty_half_close (vty_io vio)
*
* Shuts down everything and discards all buffers etc. etc.
*
- * If not already on it, places the VTY on "death watch". When there is no
- * queued command or anything else active, the VTY is finally put to sleep.
+ * If cmd_in_progress, cannot complete the process -- but sets the closed
+ * flag.
+ *
+ * Can call vty_close() any number of times.
+ *
+ * The vty structure is placed on death watch, which will finally free the
+ * structure once no longer cmd_in_progress.
*/
extern void
uty_close (vty_io vio)
{
VTY_ASSERT_LOCKED() ;
- uty_file_close(&vio->file) ; /* bring the file to a complete stop */
+ /* Empty all the output buffers */
+ vio_fifo_reset_keep(&vio->cli_obuf) ;
+ vio_fifo_reset_keep(&vio->cmd_obuf) ;
+ vio->cmd_lc = vio_lc_reset_free(vio->cmd_lc) ;
+
+ /* If not already closed, close. */
+ if (!vio->closed)
+ {
+ uty_half_close(vio, NULL) ; /* place on death watch -- if not
+ already done */
+ uty_cli_close(vio) ; /* tell the CLI to stop */
+
+ vio->closed = 1 ; /* now closed (stop uty_write()
+ from recursing) */
+
+ if (vio->sock.write_open)
+ uty_write(vio) ; /* last gasp attempt */
- uty_half_close(vio) ; /* deal with the input side, and place on
- death watch -- if not already done */
+ uty_sock_close(&vio->sock) ;
+
+ } ;
+
+ /* Nothing more should happen, so can now release almost everything,
+ * the exceptions being the things that are related to a cmd_in_progress.
+ *
+ * All writing to buffers is suppressed, and as the sock has been closed,
+ * there will be no more read_ready or write_ready events.
+ */
+ if (vio->name != NULL)
+ XFREE(MTYPE_VTY_NAME, vio->name) ;
+
+ vio->key_stream = keystroke_stream_free(vio->key_stream) ;
qs_free_body(&vio->cli_prompt_for_node) ;
qs_free_body(&vio->cl) ;
- qs_free_body(&vio->clx) ;
- qs_free_body(&vio->cli_vbuf) ;
- qs_free_body(&vio->cmd_vbuf) ;
- vio_fifo_reset_keep(&vio->cli_obuf) ;
- vio_fifo_reset_keep(&vio->cmd_obuf) ;
+ {
+ qstring line ;
+ while ((line = vector_ream_keep(&vio->hist)) != NULL)
+ qs_reset_free(line) ;
+ } ;
- vio->vty->buf = NULL ;
+ /* The final stage cannot be completed if cmd_in_progress.
+ *
+ * The clx is pointed at by vty->buf -- containing the current command.
+ *
+ * Once everything is released, can take the vty off death watch, and
+ * release the vio and the vty.
+ */
+ if (!vio->cmd_in_progress)
+ {
+ qs_free_body(&vio->clx) ;
+ vio->vty->buf = NULL ;
+ } ;
} ;
+/*==============================================================================
+ * For writing configuration file by command, temporarily redirect output to
+ * an actual file.
+ */
+
/*------------------------------------------------------------------------------
- * Closing down VTY completely.
- *
- * Shuts down everything and discards all buffers etc. etc.
- *
- * If not already on it, places the VTY on "death watch". When there is no
- * queued command or anything else active, the VTY is finally put to sleep.
+ * Set the given fd as the VTY_FILE output.
*/
extern void
-uty_full_close (vty_io vio)
+vty_open_config_write(struct vty* vty, int fd)
{
- VTY_ASSERT_LOCKED() ;
+ vty_io vio ;
- uty_file_close(&vio->file) ; /* bring the file to a complete stop */
+ VTY_LOCK() ;
- uty_half_close(vio) ; /* deal with the input side, and place on
- death watch -- if not already done */
+ vio = vty->vio ;
- qs_free_body(&vio->cli_prompt_for_node) ;
- qs_free_body(&vio->cl) ;
- qs_free_body(&vio->clx) ;
- qs_free_body(&vio->cli_vbuf) ;
- qs_free_body(&vio->cmd_vbuf) ;
+ assert((vio->type != VTY_CONFIG_WRITE) && (vio->type != VTY_NONE)) ;
- vio_fifo_reset_keep(&vio->cli_obuf) ;
- vio_fifo_reset_keep(&vio->cmd_obuf) ;
+ vio->real_type = vio->type ;
+
+ vio->type = VTY_CONFIG_WRITE ;
+ vio->file_fd = fd ;
+ vio->file_error = 0 ;
- vio->vty->buf = NULL ;
+ VTY_UNLOCK() ;
} ;
-/*==============================================================================
- * Dealing with am I/O error on VTY
- *
- * If this is the first error for this VTY, produce suitable log message.
+/*------------------------------------------------------------------------------
+ * Write away configuration file stuff -- all or just the full lump(s).
*
- * If is a "monitor", turn that off, *before* issuing log message.
+ * Returns: > 0 => blocked
+ * 0 => all gone (up to last lump if !all)
+ * < 0 => failed -- see vio->file_error
*/
static int
-uty_io_error(vty_io vio, const char* what)
+uty_config_write(vty_io vio, bool all)
{
- /* can no longer be a monitor ! */
- uty_set_monitor(vio, 0) ;
+ int ret ;
- /* if this is the first error, log it */
- if (vio->file.error_seen == 0)
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->file_error == 0)
{
- const char* type ;
- switch (vio->type)
- {
- case VTY_TERM:
- type = "VTY Terminal" ;
- break ;
- case VTY_SHELL_SERV:
- type = "VTY Shell Server" ;
- break ;
- default:
- zabort("unknown VTY type for uty_io_error()") ;
- } ;
+ ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->file_fd, all) ;
- vio->file.error_seen = errno ;
- uzlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
- type, what, vio->file.fd, safe_strerror(vio->file.error_seen)) ;
- } ;
+ if (ret < 0)
+ vio->file_error = errno ;
+ }
+ else
+ ret = -1 ;
- return -1 ;
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write away any pending stuff, and return the VTY to normal.
+ */
+extern int
+vty_close_config_write(struct vty* vty)
+{
+ vty_io vio ;
+ int err ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
+ assert((vio->type == VTY_CONFIG_WRITE) && (vio->real_type != VTY_NONE)) ;
+
+ uty_config_write(vio, true) ; /* write all that is left */
+
+ err = vio->file_error ;
+
+ vio->type = vio->real_type ;
+ vio->file_fd = -1 ;
+ vio->file_error = 0 ;
+
+ VTY_UNLOCK() ;
+
+ return err ;
} ;
/*==============================================================================
- * vio_file level operations
+ * vio_sock level operations
*/
/*------------------------------------------------------------------------------
- * Initialise a new vio_file structure.
+ * Initialise a new vio_sock structure.
*
- * Requires that: the vio_file structure is not currently in use.
+ * Requires that: the vio_sock structure is not currently in use.
*
- * if fd >= 0 then: file is open and ready read and write
- * otherwise: file is not open
+ * if fd >= 0 then: sock is open and ready read and write
+ * otherwise: sock is not open
*
* there are no errors, yet.
*
* Sets timeout to no timeout at all -- timeout is optional.
+ *
+ * NB: MUST be in the CLI thread if the fd is >= 0 !
*/
static void
-uty_file_init_new(vio_file file, int fd, void* info)
+uty_sock_init_new(vio_sock sock, int fd, void* info)
{
- memset(file, 0, sizeof(struct vio_file)) ;
+ VTY_ASSERT_LOCKED() ;
+
+ if (fd >= 0)
+ VTY_ASSERT_CLI_THREAD() ;
+
+ memset(sock, 0, sizeof(struct vio_sock)) ;
/* Zeroising the structure has set:
*
@@ -718,17 +945,16 @@ uty_file_init_new(vio_file file, int fd, void* info)
* t_timer = NULL -- no timer thread, yet
* qtr = NULL -- no qtimer, yet
*/
- file->fd = fd ;
- file->info = info ;
+ sock->fd = fd ;
+ sock->info = info ;
- file->read_open = (fd >= 0) ;
- file->write_open = (fd >= 0) ;
+ sock->read_open = (fd >= 0) ;
+ sock->write_open = (fd >= 0) ;
- if (vty_cli_nexus)
+ if ((fd >= 0) && vty_cli_nexus)
{
- file->qf = qps_file_init_new(NULL, NULL);
- if (fd >= 0)
- qps_add_file(vty_cli_nexus->selection, file->qf, file->fd, file->info);
+ sock->qf = qps_file_init_new(NULL, NULL);
+ qps_add_file(vty_cli_nexus->selection, sock->qf, sock->fd, sock->info);
} ;
} ;
@@ -740,250 +966,368 @@ uty_file_init_new(vio_file file, int fd, void* info)
* If no timeout time is set, and the timer is running, unset it.
*/
static void
-uty_file_restart_timer(vio_file file)
+uty_sock_restart_timer(vio_sock sock)
{
- if (file->v_timeout != 0)
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->v_timeout != 0)
{
- assert(file->action.timer.anon != NULL) ;
+ assert(sock->action.timer.anon != NULL) ;
if (vty_cli_nexus)
{
- if (file->qtr == NULL) /* allocate qtr if required */
- file->qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
- NULL, file->info) ;
- qtimer_set(file->qtr, qt_add_monotonic(QTIME(file->v_timeout)),
- file->action.timer.qnexus) ;
+ if (sock->qtr == NULL) /* allocate qtr if required */
+ sock->qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ NULL, sock->info) ;
+ qtimer_set(sock->qtr, qt_add_monotonic(QTIME(sock->v_timeout)),
+ sock->action.timer.qnexus) ;
}
else
{
- if (file->t_timer != NULL)
- thread_cancel (file->t_timer);
- file->t_timer = thread_add_timer (vty_master,
- file->action.timer.thread, file->info, file->v_timeout) ;
+ if (sock->t_timer != NULL)
+ thread_cancel (sock->t_timer);
+ sock->t_timer = thread_add_timer (vty_master,
+ sock->action.timer.thread, sock->info, sock->v_timeout) ;
} ;
- file->timer_running = 1 ;
+ sock->timer_running = 1 ;
}
- else if (file->timer_running)
+ else if (sock->timer_running)
{
if (vty_cli_nexus)
{
- if (file->qtr != NULL)
- qtimer_unset(file->qtr) ;
+ if (sock->qtr != NULL)
+ qtimer_unset(sock->qtr) ;
}
else
{
- if (file->t_timer != NULL)
- thread_cancel (file->t_timer) ;
+ if (sock->t_timer != NULL)
+ thread_cancel (sock->t_timer) ;
} ;
- file->timer_running = 0 ;
+ sock->timer_running = 0 ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Set a new timer value.
+ * Set read on/off
+ *
+ * Returns: the on/off state set
*/
-extern void
-uty_file_set_timer(vio_file file, unsigned long timeout)
+static bool
+uty_sock_set_read(vio_sock sock, bool on)
{
- file->v_timeout = timeout ;
- if (file->timer_running)
- uty_file_restart_timer(file) ;
-} ;
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
-/*------------------------------------------------------------------------------
- * Set read on/off -- restart timer.
- */
-extern void
-uty_file_set_read(vio_file file, bool on)
-{
- if (file->fd < 0)
- return ;
+ if (sock->fd < 0)
+ return 0 ;
if (on)
{
- assert(file->action.read.anon != NULL) ;
+ assert(sock->action.read.anon != NULL) ;
if (vty_cli_nexus)
- {
- qps_enable_mode(file->qf, qps_read_mnum, file->action.read.qnexus) ;
- }
+ qps_enable_mode(sock->qf, qps_read_mnum, sock->action.read.qnexus) ;
else
{
- if (file->t_read != NULL)
- thread_cancel(file->t_read) ;
+ if (sock->t_read != NULL)
+ thread_cancel(sock->t_read) ;
- file->t_read = thread_add_read(vty_master,
- file->action.read.thread, file->info, file->fd) ;
+ sock->t_read = thread_add_read(vty_master,
+ sock->action.read.thread, sock->info, sock->fd) ;
} ;
}
else
{
if (vty_cli_nexus)
- {
- qps_disable_modes(file->qf, qps_read_mbit) ;
- }
+ qps_disable_modes(sock->qf, qps_read_mbit) ;
else
{
- if (file->t_read != NULL)
- thread_cancel (file->t_read) ;
+ if (sock->t_read != NULL)
+ thread_cancel (sock->t_read) ;
} ;
} ;
- uty_file_restart_timer(file) ;
+ return on ;
} ;
/*------------------------------------------------------------------------------
- * Set write on/off -- restart timer.
+ * Set write on/off
+ *
+ * Returns: the on/off state set
*/
-extern void
-uty_file_set_write(vio_file file, bool on)
+static bool
+uty_sock_set_write(vio_sock sock, bool on)
{
- if (file->fd < 0)
- return ;
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->fd < 0)
+ return 0 ;
if (on)
{
- assert(file->action.write.anon != NULL) ;
+ assert(sock->action.write.anon != NULL) ;
if (vty_cli_nexus)
- {
- qps_enable_mode(file->qf, qps_write_mnum, file->action.write.qnexus) ;
- }
+ qps_enable_mode(sock->qf, qps_write_mnum, sock->action.write.qnexus) ;
else
{
- if (file->t_write != NULL)
- thread_cancel(file->t_write) ;
+ if (sock->t_write != NULL)
+ thread_cancel(sock->t_write) ;
- file->t_write = thread_add_write(vty_master,
- file->action.write.thread, file->info, file->fd) ;
+ sock->t_write = thread_add_write(vty_master,
+ sock->action.write.thread, sock->info, sock->fd) ;
} ;
}
else
{
if (vty_cli_nexus)
- {
- qps_disable_modes(file->qf, qps_write_mbit) ;
- }
+ qps_disable_modes(sock->qf, qps_write_mbit) ;
else
{
- if (file->t_write != NULL)
- thread_cancel (file->t_write) ;
+ if (sock->t_write != NULL)
+ thread_cancel (sock->t_write) ;
} ;
} ;
- uty_file_restart_timer(file) ;
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set read/write readiness -- for VTY_TERM
+ *
+ * Note that for VTY_TERM, set only one of read or write, and sets write for
+ * preference.
+ */
+extern void
+uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ uty_sock_set_read(sock, (ready == read_ready)) ;
+ uty_sock_set_write(sock, (ready >= write_ready)) ;
} ;
/*------------------------------------------------------------------------------
- * Close given vty file for reading.
+ * Set a new timer value.
+ */
+extern void
+uty_sock_set_timer(vio_sock sock, unsigned long timeout)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ sock->v_timeout = timeout ;
+ if (sock->timer_running)
+ uty_sock_restart_timer(sock) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close given vty sock for reading.
*
* Sets timer to timeout for clearing any pending output.
+ *
+ * NB: if there is a socket, MUST be in the CLI thread
*/
static void
-uty_file_half_close(vio_file file)
+uty_sock_half_close(vio_sock sock)
{
VTY_ASSERT_LOCKED() ;
- if (file->fd >= 0)
- {
- shutdown(file->fd, SHUT_RD) ; /* actual half close */
+ sock->read_open = 0 ; /* make sure */
- file->v_timeout = 30 ; /* for output to clear */
- uty_file_set_read(file, off) ;
- } ;
+ if (sock->fd < 0)
+ return ; /* nothing more if no socket */
+
+ VTY_ASSERT_CLI_THREAD() ;
- file->read_open = 0 ;
+ shutdown(sock->fd, SHUT_RD) ; /* actual half close */
+
+ uty_sock_set_read(sock, off) ;
+ uty_sock_set_write(sock, on) ;
+ sock->v_timeout = 30 ; /* for output to clear */
+ uty_sock_restart_timer(sock) ;
} ;
/*------------------------------------------------------------------------------
- * Close given vio_file, completely -- shut down any timer.
+ * Close given vio_sock, completely -- shut down any timer.
*
* Structure is cleared of everything except the last error !
+ *
+ * NB: if there is a socket, MUST be in the CLI thread
*/
static void
-uty_file_close(vio_file file)
+uty_sock_close(vio_sock sock)
{
VTY_ASSERT_LOCKED() ;
- if (file->fd >= 0)
- close(file->fd) ;
+ sock->read_open = 0 ; /* make sure */
+ sock->write_open = 0 ;
- if (vty_cli_nexus && (file->fd >= 0))
- qps_remove_file(file->qf) ;
+ if (sock->fd < 0)
+ {
+ assert( (sock->qf == NULL)
+ && (sock->qtr == NULL)
+ && (sock->t_read == NULL)
+ && (sock->t_write == NULL)
+ && (sock->t_timer == NULL) ) ;
+ return ; /* no more to be done here */
+ } ;
- if (file->qf != NULL)
- qps_file_free(file->qf) ;
+ VTY_ASSERT_CLI_THREAD() ;
+ close(sock->fd) ;
- if (file->t_read != NULL)
- thread_cancel(file->t_write) ;
- if (file->t_write != NULL)
- thread_cancel(file->t_write) ;
+ if (vty_cli_nexus)
+ {
+ assert((sock->qf != NULL) && (sock->fd == qps_file_fd(sock->qf))) ;
+ qps_remove_file(sock->qf) ;
+ qps_file_free(sock->qf) ;
+ sock->qf = NULL ;
+ } ;
- file->fd = -1 ;
- file->qf = NULL ;
- file->t_read = NULL ;
- file->t_write = NULL ;
+ sock->fd = -1 ;
- file->info = NULL ;
- file->action.read.anon = NULL ;
- file->action.write.anon = NULL ;
- file->action.timer.anon = NULL ;
+ if (sock->t_read != NULL)
+ thread_cancel(sock->t_write) ;
+ if (sock->t_write != NULL)
+ thread_cancel(sock->t_write) ;
- file->read_open = 0 ;
- file->write_open = 0 ;
+ sock->t_read = NULL ;
+ sock->t_write = NULL ;
- if (file->qtr != NULL)
- qtimer_free(file->qtr) ;
- if (file->t_timer != NULL)
- thread_cancel(file->t_timer) ;
+ sock->info = NULL ;
+ sock->action.read.anon = NULL ;
+ sock->action.write.anon = NULL ;
+ sock->action.timer.anon = NULL ;
- file->v_timeout = 0 ;
- file->qtr = NULL ;
- file->t_timer = NULL ;
+ if (sock->qtr != NULL)
+ qtimer_free(sock->qtr) ;
+ if (sock->t_timer != NULL)
+ thread_cancel(sock->t_timer) ;
+
+ sock->v_timeout = 0 ;
+ sock->qtr = NULL ;
+ sock->t_timer = NULL ;
} ;
-/*==============================================================================
- * Reading from the VTY_TERM type file.
+/*------------------------------------------------------------------------------
+ * Dealing with an I/O error on VTY socket
*
- * The select/pselect call-back ends up in uty_read_ready().
+ * If this is the first error for this VTY, produce suitable log message.
*
- * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
- * current CLI.
+ * If is a "monitor", turn that off, *before* issuing log message.
*/
+static int
+uty_sock_error(vty_io vio, const char* what)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
-/*------------------------------------------------------------------------------
- * Ready to read -> kicking CLI
+ /* can no longer be a monitor ! *before* any logging ! */
+ uty_set_monitor(vio, 0) ;
+
+ /* if this is the first error, log it */
+ if (vio->sock.error_seen == 0)
+ {
+ const char* type ;
+ switch (vio->type)
+ {
+ case VTY_TERM:
+ type = "VTY Terminal" ;
+ break ;
+ case VTY_SHELL_SERV:
+ type = "VTY Shell Server" ;
+ break ;
+ default:
+ zabort("unknown VTY type for uty_sock_error()") ;
+ } ;
+
+ vio->sock.error_seen = errno ;
+ uzlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
+ type, what, vio->sock.fd, safe_strerror(vio->sock.error_seen)) ;
+ } ;
+
+ return -1 ;
+} ;
+
+/*==============================================================================
+ * Readiness and the VTY_TERM type VTY.
*
- * Have two CLI: one (trivial one) when waiting on "--more--",
- * and the standard one.
+ * For VTY_TERM the driving force is write ready. This is used to prompt the
+ * VTY_TERM when there is outstanding output (obviously), but also if there
+ * is buffered input in the keystroke stream.
*
- * End up here when there is something ready to be read.
+ * The VTY_TERM uses read ready only when it doesn't set write ready. Does
+ * not set both at once.
*
- * Also ends up here when was write_ready and did not block in uty_write.
+ * So there is only one, common, uty_ready function, which:
*
- * Will also end up here if an error has occurred, the other end has closed,
- * this end has half closed, etc. This fact is used to kick the CLI even when
- * there is no data to be read.
+ * 1. attempts to clear any output it can.
*
- * Note that nothing is actually read here -- reading is done in the CLI itself,
- * if required.
+ * The state of the output affects the CLI, so must always do this before
+ * before invoking the CLI.
*
- * The CLI decides whether to re-enable read, or enable write, or both.
+ * If this write enters the "--more--" state, then will have tried to
+ * write away the prompt.
+ *
+ * 2. invokes the CLI
+ *
+ * Which will do either the standard CLI stuff or the special "--more--"
+ * stuff.
+ *
+ * 3. attempts to write any output there now is.
+ *
+ * If the CLI generated new output, as much as possible is written away
+ * now.
+ *
+ * If this write enters the "--more--" state, then it returns now_ready,
+ * if the prompt was written away, which loops back to the CLI.
+ *
+ * Note that this is arranging:
+ *
+ * a. to write away the "--more--" prompt as soon as the tranche of output to
+ * which it refers, completes
+ *
+ * b. to enter the cli_more_wait CLI for the first time immediately after the
+ * "--more--" prompt is written away.
+ *
+ * The loop limits itself to one trache of command output each time.
+ *
+ * Resets the timer because something happened.
*/
static void
-uty_read_ready(vty_io vio)
+uty_ready(vty_io vio)
{
- uty_file_set_read(&vio->file, off) ; /* restarts timer */
+ enum vty_readiness ready ;
- /* Execute the required command processor */
- if (vio->cmd_wait_more)
- uty_cli_wait_more(vio) ; /* run "--more--" CLI */
- else
- uty_cli(vio) ; /* run standard CLI */
+ VTY_ASSERT_LOCKED() ;
+
+ vio->cmd_out_done = 0 ; /* not done any command output yet */
+
+ uty_write(vio) ; /* try to clear outstanding stuff */
+ do
+ {
+ ready = uty_cli(vio) ; /* do any CLI work... */
+ ready |= uty_write(vio) ; /* ...and any output that generates */
+ } while (ready >= now_ready) ;
+
+ uty_sock_set_readiness(&vio->sock, ready) ;
+ uty_sock_restart_timer(&vio->sock) ;
} ;
+/*==============================================================================
+ * Reading from VTY_TERM.
+ *
+ * The select/pselect call-back ends up in uty_read_ready().
+ *
+ * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
+ * current CLI.
+ */
+
/*------------------------------------------------------------------------------
* Callback -- qnexus: ready to read -> kicking CLI
*/
@@ -994,9 +1338,9 @@ vty_read_qnexus(qps_file qf, void* file_info)
VTY_LOCK() ;
- assert((vio->file.fd == qf->fd) && (vio == vio->file.info)) ;
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
- uty_read_ready(vio) ;
+ uty_ready(vio) ;
VTY_UNLOCK() ;
}
@@ -1011,10 +1355,10 @@ vty_read_thread(struct thread *thread)
VTY_LOCK() ;
- assert(vio->file.fd == THREAD_FD (thread) && (vio == vio->file.info)) ;
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
- vio->file.t_read = NULL ; /* implicitly */
- uty_read_ready(vio);
+ vio->sock.t_read = NULL ; /* implicitly */
+ uty_ready(vio);
VTY_UNLOCK() ;
return 0 ;
@@ -1035,18 +1379,18 @@ uty_read (vty_io vio, keystroke steal)
unsigned char buf[500] ;
int get ;
- if (!vio->file.read_open)
+ if (!vio->sock.read_open)
return -1 ; /* at EOF if not open */
- get = read_nb(vio->file.fd, buf, sizeof(buf)) ;
- if (get > 0)
+ get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
+ if (get >= 0)
keystroke_input(vio->key_stream, buf, get, steal) ;
else if (get < 0)
{
if (get == -1)
- uty_io_error(vio, "read") ;
+ uty_sock_error(vio, "read") ;
- vio->file.read_open = 0 ;
+ vio->sock.read_open = 0 ;
keystroke_input(vio->key_stream, NULL, 0, steal) ;
get = -1 ;
@@ -1056,14 +1400,14 @@ uty_read (vty_io vio, keystroke steal)
} ;
/*==============================================================================
- * The write file action for VTY_TERM type VTY
+ * The write sock action for VTY_TERM type VTY
*
* There are two sets of buffering:
*
* cli -- command line -- which reflects the status of the command line
*
- * cmd -- command output -- which is not written to the file while
- * cmd_in_progress.
+ * cmd -- command output -- which is written to the file only while
+ * cmd_out_enabled.
*
* The cli output takes precedence.
*
@@ -1071,41 +1415,8 @@ uty_read (vty_io vio, keystroke steal)
* "--more--" mechanism.
*/
-static bool uty_write(vty_io vio) ;
-static int uty_flush_fifo(vty_io vio, vio_fifo vf,
- struct vty_line_control* line_control) ;
-static void uty_empty_out_fifos(vty_io vio) ;
-
-/*------------------------------------------------------------------------------
- * Flush as much as possible of what there is.
- *
- * May end up with:
- *
- * * something in the buffers waiting to go, but output is currently
- * threatening to block.
- *
- * in this case will have set write on, and things will progress when next
- * write_ready.
- *
- * * otherwise:
- *
- * will be set write off, so does a read_ready in order t kick the CLI,
- * which may wish to set either read or write on.
- */
-static void
-uty_write_ready(vty_io vio)
-{
- bool blocked ;
-
- VTY_ASSERT_LOCKED() ;
-
- uty_file_set_write(&vio->file, off) ; /* restarts timer, too */
-
- blocked = uty_write(vio) ;
-
- if (!blocked)
- uty_read_ready(vio) ;
-} ;
+static int uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
+static int uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
/*------------------------------------------------------------------------------
* Callback -- qnexus: ready to write -> try to empty buffers
@@ -1117,9 +1428,9 @@ vty_write_qnexus(qps_file qf, void* file_info)
VTY_LOCK() ;
- assert((vio->file.fd == qf->fd) && (vio == vio->file.info)) ;
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
- uty_write_ready(vio) ;
+ uty_ready(vio) ;
VTY_UNLOCK() ;
}
@@ -1134,10 +1445,10 @@ vty_write_thread(struct thread *thread)
VTY_LOCK() ;
- assert(vio->file.fd == THREAD_FD (thread) && (vio == vio->file.info)) ;
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
- vio->file.t_write = NULL; /* implicitly */
- uty_write_ready(vio) ;
+ vio->sock.t_write = NULL; /* implicitly */
+ uty_ready(vio) ;
VTY_UNLOCK() ;
return 0 ;
@@ -1152,154 +1463,337 @@ vty_write_thread(struct thread *thread)
* 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 outputting everything available for output
+ * Sets write on if prevented from writing everything available for output
* by write() threatening to block.
*
- * Sets read on if enters cmd_wait_more state.
- *
- * Returns: true <=> blocked by I/O
- *
- * Note that this means that returns true iff sets write on.
+ * Returns: write_ready if should now set write on
+ * now_ready if should loop back and try again
+ * not_ready otherwise
*/
-static bool
+static enum vty_readiness
uty_write(vty_io vio)
{
int ret ;
VTY_ASSERT_LOCKED() ;
- /* empty the CLI FIFO
- *
- * NB: if the file is !write_open, or if it fails during output here
- * and becomes !write_open, then ret == 0 -- as if everything
- * has been written.
- */
- ret = uty_flush_fifo(vio, &vio->cli_obuf, NULL) ;
- if (ret != 0)
- return 1 ; /* blocked by I/O */
+ ret = -1 ;
+ while (vio->sock.write_open)
+ {
+ /* Any outstanding line control output takes precedence */
+ if (vio->cmd_lc != NULL)
+ {
+ ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+ if (ret != 0)
+ break ;
+ }
- if ((vio->cmd_in_progress) || (vio->cmd_wait_more))
- return 0 ; /* not blocked by I/O */
+ /* Next: empty out the cli output */
+ ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ if (ret != 0)
+ break ;
- /* write from the command FIFO
- *
- * NB: if the file is !write_open, or if it fails during output here
- * and becomes !write_open, then ret == 0 -- as if everything
- * has been written.
- */
- ret = uty_flush_fifo(vio, &vio->cmd_obuf, &vio->line_control) ;
- if (ret == 1)
- return 1 ; /* blocked by I/O */
+ /* Finished now if not allowed to progress the command stuff */
+ if (!vio->cmd_out_enabled)
+ return not_ready ; /* done all can do */
- if (ret == 2)
- {
- /* Want now to wait for "--more--"
+ /* Last: if there is something in the command buffer, do that */
+ if (!vio_fifo_empty(&vio->cmd_obuf))
+ {
+ if (vio->cmd_out_done)
+ break ; /* ...but not if done once */
+
+ vio->cmd_out_done = 1 ; /* done this once */
+
+ assert(!vio->cli_more_wait) ;
+
+ if (vio->cmd_lc != NULL)
+ ret = uty_write_fifo_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+ else
+ ret = vio_fifo_write_nb(&vio->cmd_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 = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ if (ret == 0)
+ return now_ready ;
+ } ;
+
+ if (ret != 0)
+ break ;
+ }
+
+ /* Exciting stuff: there is nothing left to output...
*
- * Note that this produces CLI output, which must deal with here.
+ * ... watch out for half closed state.
*/
- uty_cli_want_more(vio) ; /* NB: sets cmd_wait_more */
+ if (vio->half_closed)
+ {
+ 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) ;
- ret = uty_flush_fifo(vio, &vio->cli_obuf, NULL) ;
- if (ret == 1)
- return 1 ; /* blocked by I/O */
+ vio->cmd_in_progress = 0 ;
+
+ 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 */
+ } ;
- if (vio->file.write_open)
- return 0 ; /* not blocked by I/O */
+ /* For VTY_TERM: if the command line is not drawn, now is a good
+ * time to do that.
+ */
+ if (vio->type == VTY_TERM)
+ if (uty_cli_draw_if_required(vio))
+ continue ; /* do that now. */
+
+ /* There really is nothing left to output */
+ return not_ready ;
} ;
- /* Reach here iff both CLI and command FIFOs are empty and is not
- * cmd_in_progress
+ /* Arrives here if there is more to do, or failed (or was !write_open) */
+
+ if (ret >= 0)
+ return write_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.
*/
- vio->cli_blocked = 0 ;
+ if (vio->sock.write_open)
+ {
+ if (!vio->sock.read_open)
+ uty_sock_error(vio, "write") ;
- return 0 ;
+ vio->sock.write_open = 0 ; /* crash close write */
+ } ;
+
+ /* For whatever reason, is no longer write_open -- clear all buffers.
+ */
+ vio_fifo_clear(&vio->cli_obuf) ; /* throw away cli stuff */
+ uty_out_clear(vio) ; /* throw away cmd stuff */
+
+ vio->close_reason = NULL ; /* too late for this */
+
+ return not_ready ; /* NB: NOT blocked by I/O */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible -- for "monitor" output.
+ *
+ * Outputs only:
+ *
+ * a. outstanding line control stuff.
+ *
+ * b. contents of CLI buffer
+ *
+ * And:
+ *
+ * a. does not report any errors.
+ *
+ * b. does not change anything except the state of the buffers.
+ *
+ * In particular, for the qpthreaded world, does not attempt to change
+ * the state of the qfile or any other "thread private" structures.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone
+ * < 0 => failed (or !write_open)
+ */
+static int
+uty_write_monitor(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (!vio->sock.write_open)
+ return -1 ;
+
+ if (vio->cmd_lc != NULL)
+ {
+ int ret ;
+ ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+
+ if (ret != 0)
+ return ret ;
+ } ;
+
+ return vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
} ;
/*------------------------------------------------------------------------------
- * Flush the given FIFO to output -- subject to possible line control.
+ * Write the given FIFO to output -- subject to possible 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.
+ *
+ * If the line control becomes "paused", it is time to enter "--more--" state
+ * -- unless the FIFO is empty (or "--more--" is not enabled).
*
- * If ends up needing to write more, sets write on.
+ * NB: expects that the sock is write_open
*
- * Returns: 0 => written everything there is -- or not (now) write_open
- * 1 => written everything that could -- needs to write more
- * 2 => written everything that could -- needs a "--more--"
+ * Returns: > 0 => blocked or completed one tranche
+ * 0 => all gone
+ * < 0 => failed
*/
static int
-uty_flush_fifo(vty_io vio, vio_fifo vf, struct vty_line_control* line_control)
+uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
{
+ int ret ;
char* src ;
size_t have ;
- int done ;
- bool wait_more ;
- if (!vio->file.write_open)
+ /* 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.
+ */
+ vio_lc_set_pause(lc) ; /* clears lc->paused */
+
+ src = vio_fifo_get_rdr(vf, &have) ;
+
+ while ((src != NULL) && (!lc->paused))
{
- uty_empty_out_fifos(vio) ;
- return 0 ;
+ size_t take ;
+ take = vio_lc_append(lc, src, have) ;
+ src = vio_fifo_step_rdr(vf, &have, take) ;
} ;
- wait_more = 0 ;
+ vio->cli_dirty = (lc->col != 0) ;
- while ((src = vio_fifo_get_lump(vf, &have)) != NULL)
- {
- if (line_control != NULL) /* TODO: line control */
- {
- /* Account for what happens if output have bytes from src...
- * ... and if necessary reduce "have".
- *
- * set wait_more if now need to wait
- */
- } ;
+ /* Write the contents of the line control */
+ ret = uty_write_lc(vio, vf, lc) ;
- done = write_nb(vio->file.fd, src, have) ;
+ if (ret < 0)
+ return ret ; /* give up now if failed. */
- if (done < 0)
- {
- uty_io_error(vio, "write") ;
+ if ((ret == 0) && vio_fifo_empty(vf))
+ return 0 ; /* FIFO and line control empty */
- vio->file.write_open = 0 ;
- uty_empty_out_fifos(vio) ;
- return 0 ; /* no longer open */
- }
+ /* If should now do "--more--", now is the time to prepare for that.
+ *
+ * Entering more state issues a new prompt in the CLI buffer, which can
+ * be written once line control write completes.
+ *
+ * The "--more--" cli will not do anything until the CLI buffer has
+ * cleared.
+ */
+ if (lc->paused && vio->cli_more_enabled)
+ uty_cli_go_more_wait(vio) ;
- vio_fifo_got_upto(vf, src + done) ;
+ return 1 ; /* FIFO or line control, not empty */
+} ;
- if (done < (int)have)
- {
- if (line_control != NULL)
- {
- /* "put back" have - done bytes for next time */
- } ;
+/*------------------------------------------------------------------------------
+ * 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(vty_io vio, vio_fifo vf, vio_line_control lc)
+{
+ int ret ;
- uty_file_set_write(&vio->file, on) ;
- return 1 ; /* output is full */
- } ;
+ ret = vio_lc_write_nb(vio->sock.fd, lc) ;
- /* If now wants to wait for a "--more--", then exit
- *
- * Note that the line_control cannot tell if the place it wants to
- * stop is, in fact, the end of the FIFO -- can only tell that
- * now...
- */
- if (wait_more)
- return vio_fifo_empty(vf) ? 0 : 2 ;
- } ;
+ if (ret <= 0)
+ vio_fifo_sync_rdr(vf) ; /* finished with FIFO contents */
- return 0 ; /* all gone */
+ return ret ;
} ;
/*------------------------------------------------------------------------------
- * Empty the output FIFOs
+ * Start command output -- clears down the line control.
*
- * This is for use when the output has failed or is closed.
+ * Requires that that current line is empty -- restarts the line control
+ * on the basis that is at column 0.
*/
-static void
-uty_empty_out_fifos(vty_io vio)
+extern void
+uty_cmd_output_start(vty_io vio)
{
- vio_fifo_set_empty(&vio->cli_obuf) ;
- vio_fifo_set_empty(&vio->cmd_obuf) ;
+ if (vio->cmd_lc != NULL)
+ vio_lc_clear(vio->cmd_lc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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(vty_io vio)
+{
+ bool on ;
+
+ on = 0 ; /* default state */
+
+ if ((vio->cmd_lc != NULL) && !vio->half_closed)
+ {
+ int height ;
+
+ height = 0 ; /* default state */
- vio->cmd_wait_more = 0 ;
+ 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 ;
+ } ;
+
+ 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 ;
} ;
/*==============================================================================
@@ -1321,10 +1815,8 @@ uty_timer_expired (vty_io vio)
if (vio->half_closed)
return uty_close(vio) ; /* curtains */
- uty_half_close(vio) ; /* bring input side to a halt */
-
- vio->timed_out = 1 ; /* why stopped */
-} ;
+ uty_half_close(vio, "Timed out") ; /* bring input side to a halt */
+ } ;
/*------------------------------------------------------------------------------
* Callback -- qnexus: deal with timer timeout.
@@ -1351,7 +1843,7 @@ vty_timer_thread (struct thread *thread)
VTY_LOCK() ;
- vio->file.t_timer = NULL ; /* implicitly */
+ vio->sock.t_timer = NULL ; /* implicitly */
uty_timer_expired(vio) ;
@@ -1373,7 +1865,7 @@ struct vty_listener
enum vty_type type ;
- struct vio_file file ;
+ struct vio_sock sock ;
};
/* List of listeners so can tidy up. */
@@ -1450,7 +1942,7 @@ uty_close_listeners(void)
while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
{
- uty_file_close(&listener->file) ; /* no ceremony, no flowers */
+ uty_sock_close(&listener->sock) ; /* no ceremony, no flowers */
XFREE(MTYPE_VTY, listener) ;
} ;
} ;
@@ -1537,6 +2029,8 @@ uty_serv_sock(const char* addr, unsigned short port)
VTY_ASSERT_LOCKED() ;
+ n = 0 ; /* nothing opened yet */
+
/* If have an address, see what kind and whether valid */
sa = NULL ;
@@ -1620,6 +2114,19 @@ uty_serv_sock_open(sa_family_t family, int type, int protocol,
if (ret >= 0)
ret = set_nonblocking(sock);
+#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)
+ /* Want only IPV6 on ipv6 socket (not mapped addresses)
+ *
+ * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the
+ * attempt to bind to :: after binding to 0.0.0.0.
+ */
+ if ((ret >= 0) && (sa->sa_family == AF_INET6))
+ {
+ int on = 1;
+ ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
+ }
+#endif
+
if (ret >= 0)
ret = sockunion_bind (sock, &su, port, sa) ;
@@ -1673,7 +2180,6 @@ uty_serv_vtysh(const char *path)
{
uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
safe_strerror(errno));
- umask (old_mask);
return -1 ;
}
@@ -1711,10 +2217,8 @@ uty_serv_vtysh(const char *path)
{
/* set group of socket */
if ( chown (path, -1, ids.gid_vty) )
- {
- uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
+ uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
safe_strerror (errno) );
- }
}
umask (old_mask);
@@ -1745,16 +2249,16 @@ uty_serv_start_listener(int fd, enum vty_type type)
listener = XCALLOC(MTYPE_VTY, sizeof (struct vty_listener));
ssl_push(vty_listeners_list, listener, next) ;
- uty_file_init_new(&listener->file, fd, listener) ;
+ uty_sock_init_new(&listener->sock, fd, listener) ;
listener->type = type ;
if (vty_cli_nexus)
- listener->file.action.read.qnexus = vty_accept_qnexus ;
+ listener->sock.action.read.qnexus = vty_accept_qnexus ;
else
- listener->file.action.read.thread = vty_accept_thread ;
+ listener->sock.action.read.thread = vty_accept_thread ;
- uty_file_set_read(&listener->file, on) ;
+ uty_sock_set_read(&listener->sock, on) ;
} ;
/*------------------------------------------------------------------------------
@@ -1770,7 +2274,7 @@ vty_accept_thread(struct thread *thread)
result = uty_accept(listener, THREAD_FD(thread));
- uty_file_set_read(&listener->file, on) ;
+ uty_sock_set_read(&listener->sock, on) ;
VTY_UNLOCK() ;
return result ;
@@ -1797,7 +2301,7 @@ uty_accept(vty_listener listener, int listen_sock)
{
VTY_ASSERT_LOCKED() ;
- assert(listener->file.fd == listen_sock) ;
+ assert(listener->sock.fd == listen_sock) ;
switch (listener->type)
{
@@ -1828,9 +2332,9 @@ uty_accept_term(vty_listener listener)
VTY_ASSERT_LOCKED() ;
/* We can handle IPv4 or IPv6 socket. */
- sockunion_init_new(&su, 0) ;
+ sockunion_init_new(&su, AF_UNSPEC) ;
- sock = sockunion_accept (listener->file.fd, &su);
+ sock = sockunion_accept (listener->sock.fd, &su);
if (sock < 0)
{
@@ -1918,7 +2422,7 @@ uty_accept_shell_serv (vty_listener listener)
client_len = sizeof(client);
memset (&client, 0, client_len);
- sock = accept(listener->file.fd, (struct sockaddr *) &client,
+ sock = accept(listener->sock.fd, (struct sockaddr *) &client,
(socklen_t *) &client_len) ;
if (sock < 0)
@@ -1948,7 +2452,7 @@ uty_accept_shell_serv (vty_listener listener)
}
/*==============================================================================
- * Reading from the VTY_SHELL_SERV type file.
+ * Reading from the VTY_SHELL_SERV type sock.
*
* The select/pselect call-back ends up in utysh_read_ready().
*/
@@ -1970,7 +2474,7 @@ uty_accept_shell_serv (vty_listener listener)
static void
utysh_read_ready(vty_io vio)
{
- uty_file_set_read(&vio->file, off) ;
+ uty_sock_set_read(&vio->sock, off) ;
/* TODO: need minimal "CLI" for VTY_SHELL_SERV
* NB: when output from command is flushed out, must append the
@@ -1989,7 +2493,7 @@ vtysh_read_qnexus(qps_file qf, void* file_info)
VTY_LOCK() ;
- assert((vio->file.fd == qf->fd) && (vio == vio->file.info)) ;
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
utysh_read_ready(vio) ;
@@ -2006,9 +2510,9 @@ vtysh_read_thread(struct thread *thread)
VTY_LOCK() ;
- assert(vio->file.fd == THREAD_FD (thread) && (vio == vio->file.info)) ;
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
- vio->file.t_read = NULL ; /* implicitly */
+ vio->sock.t_read = NULL ; /* implicitly */
utysh_read_ready(vio);
VTY_UNLOCK() ;
@@ -2025,7 +2529,7 @@ vtysh_read_thread(struct thread *thread)
* Moves stuff from the "buf" qstring and appends to "cl" qstring, stopping
* when get '\0' or empties the "buf".
*
- * When empties "buf", reads a lump from the file.
+ * When empties "buf", reads a lump from the sock.
*
* Returns: 0 => command line is incomplete
* 1 => have a complete command line
@@ -2070,13 +2574,13 @@ utysh_read (vty_io vio, qstring cl, qstring buf)
/* buffer is empty -- try and get some more stuff */
assert(buf->len == buf->cp) ;
- if (!vio->file.read_open)
+ if (!vio->sock.read_open)
return -1 ; /* at EOF if not open <<<<<<<<<<<<< */
qs_need(buf, 500) ; /* need a reasonable lump */
- qs_set_empty(buf) ; /* set cp = len = 0 */
+ qs_clear(buf) ; /* set cp = len = 0 */
- get = read_nb(vio->file.fd, buf->body, buf->size) ;
+ get = read_nb(vio->sock.fd, buf->body, buf->size) ;
if (get > 0)
buf->len = get ;
else if (get == 0)
@@ -2084,9 +2588,9 @@ utysh_read (vty_io vio, qstring cl, qstring buf)
else
{
if (get == -1)
- uty_io_error(vio, "read") ;
+ uty_sock_error(vio, "read") ;
- vio->file.read_open = 0 ;
+ vio->sock.read_open = 0 ;
return -1 ; /* at EOF or failed <<<<<<<<<<<<< */
} ;
@@ -2096,13 +2600,65 @@ utysh_read (vty_io vio, qstring cl, qstring buf)
/*==============================================================================
* Output to vty which are set to "monitor".
*
- * If there is something in the command FIFO and command is not in progress,
- * then throw logging away -- console is busy dealing with the output from
- * some command.
+ * 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.
*
- * Wipes the command line and flushes the output. If the CLI FIFO is now
- * empty, add the logging line to it and flush. Enable read, so that the
- * CLI will be reentered, and the command line restored in due course.
+ * 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.
*/
/*------------------------------------------------------------------------------
@@ -2113,67 +2669,52 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
const char *format, va_list va)
{
vty_io vio ;
- vty_io next ;
VTY_ASSERT_LOCKED() ;
- next = sdl_head(vio_monitors_base) ;
+ vio = sdl_head(vio_monitors_base) ;
- if (next == NULL)
+ if (vio == NULL)
return ; /* go no further if no "monitor" vtys */
/* Prepare line for output. */
- uvzlog_line(ll, zl, priority, format, va, 1) ; /* with crlf */
+ uvzlog_line(ll, zl, priority, format, va, llt_crlf) ; /* with crlf */
/* write to all known "monitor" vty
*
- * While writing to a given "monitor" the monitor flag is cleared. This
- * means that if the write fails, and logs a message, then will recurse
- * through here -- but won't log to the monitor that has failed.
- *
- * If one of the other monitors fails during this process, will recurse
- * again, now with two monitors with their monitor flags cleared.
- *
- * Once the output (and any recursed output) has completed, then the
- * monitor flag is restored -- but only if the vty is still write_open.
- *
- * A monitor that is not write_open at the end of this, is removed from the
- * monitors list. The current vio *cannot* be the current vio at a higher
- * level in any recursion stack, because... if anything higher up the stack
- * will have their monitor flag cleared, and therefore have been stepped
- * over at the current level.
*/
- while (next != NULL)
+ while (vio != NULL)
{
- vio = next ;
-
- if ( vio->monitor /* may be temporarily not a monitor */
- && (vio->cmd_in_progress || vio_fifo_empty(&vio->cmd_obuf)) )
+ if (!vio->monitor_busy)
{
- vio->monitor = 0 ; /* avoid recursion */
+ int ret ;
- uty_cli_wipe(vio) ;
- uty_write(vio) ;
+ vio->monitor_busy = 1 ; /* close the door */
- if (vio_fifo_empty(&vio->cli_obuf) && vio->file.write_open)
+ uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
+
+ ret = uty_write_monitor(vio) ;
+ if (ret == 0)
{
- vio_fifo_put(&vio->cli_obuf, ll->line, ll->len) ;
- uty_write(vio) ;
+ ret = write_nb(vio->sock.fd, 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) ;
+ } ;
} ;
- uty_file_set_read(&vio->file, on) ;
+ if (ret != 0)
+ /* need to prod */ ;
- /* It is possible that something failed, so that is no longer
- * write_open, and should no longer be a monitor.
- */
- vio->monitor = vio->file.write_open ;
+ if (ret >= 0)
+ vio->monitor_busy = 0 ;
} ;
- next = sdl_next(vio, mon_list) ;
-
- /* take self off list if no onger a monitor */
- if (!vio->monitor)
- sdl_del(vio_monitors_base, vio, mon_list) ;
+ vio = sdl_next(vio, mon_list) ;
} ;
} ;
@@ -2194,8 +2735,8 @@ vty_log_fixed (const char *buf, size_t len)
vio = sdl_head(vio_monitors_base) ;
while (vio != NULL)
{
- write(vio->file.fd, buf, len) ;
- write(vio->file.fd, "\r\n", 2) ;
+ write(vio->sock.fd, buf, len) ;
+ write(vio->sock.fd, "\r\n", 2) ;
vio = sdl_next(vio, mon_list) ;
} ;