summaryrefslogtreecommitdiffstats
path: root/lib/vty.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vty.c')
-rw-r--r--lib/vty.c246
1 files changed, 121 insertions, 125 deletions
diff --git a/lib/vty.c b/lib/vty.c
index e6dcd6a8..831ce721 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -57,8 +57,8 @@
* vty -- level visible from outside the vty/command/log family
* and within those families.
*
- * vty_common.h -- definitions ...
- * vty_local.h
+ * vty_common.h -- definitions used by both external and internal users
+ * vty_local.h -- definitions used within the family only
*
* vty_io -- top level of the vio handling
*
@@ -76,12 +76,10 @@
* thread/select worlds.
*
* vio_lines -- for terminal: handles width, CRLF, line counting etc.
- * vio_fifo --
- * qiovec
- *
+ * vio_fifo -- for all vty types, indefinite size buffer
+ * qiovec -- using writev() to output contents of buffers
*/
-
/*==============================================================================
* Variables etc. (see vty_local.h)
*/
@@ -119,9 +117,6 @@ vty_io vio_live_list = NULL ;
/* List of all vty which are in monitor state. */
vty_io vio_monitor_list = NULL ;
-/* List of all vty which are on death watch */
-vty_io vio_death_watch = NULL ;
-
/* List of child processes in our care */
vio_child vio_childer_list = NULL ;
@@ -139,7 +134,7 @@ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ;
/*------------------------------------------------------------------------------
* Prototypes
*/
-static void uty_reset (bool final, const char* why) ;
+static void uty_reset (const char* why, bool curtains) ;
static void uty_init_commands (void) ;
//static bool vty_terminal (struct vty *);
@@ -186,7 +181,6 @@ vty_init (struct thread_master *master_thread)
vty_master = master_thread; /* Local pointer to the master thread */
vio_live_list = NULL ; /* no VTYs yet */
- vio_death_watch = NULL ;
vio_childer_list = NULL ;
vty_nexus = false ; /* not running qnexus-wise */
@@ -196,8 +190,6 @@ vty_init (struct thread_master *master_thread)
vty_child_signal_nexus = NULL ; /* none, yet */
- uty_watch_dog_init() ; /* empty watch dog */
-
uty_init_monitor() ;
uty_init_commands() ; /* install nodes */
@@ -290,22 +282,26 @@ extern void
vty_reset()
{
vty_reset_because("Reset") ;
-}
+} ;
/*------------------------------------------------------------------------------
- * Reset all VTY status
- *
- * This is done in response to SIGHUP/SIGINT/SIGTERM -- and runs in the
- * CLI thread (if there is one).
+ * Reset all VTY.
*
- * Closes all VTY, leaving the death watch to tidy up once all output and any
- * command in progress have completed.
+ * This is done in response to SIGHUP/SIGINT -- and runs in the CLI pthread
+ * (if there is one).
*
- * Closes all listening sockets.
+ * Stops any command loop and closes all VTY, "final". Issues the "why"
+ * message just before closing the base output.
*
- * TODO: revoke ?
+ * Note that if a command loop is currently executing a command in the
+ * Routing Engine pthread, then when that completes the command loop will stop
+ * and close the VTY. So completion of the close depends on the CLI pthread
+ * continuing to run and consume messages. The Routing Engine will act on
+ * the SIGHUP/SIGINT/SIGTERM when it consumes a message sent to it, so any
+ * currently executing command will complete, before any further action is
+ * taken.
*
- * NB: old code discarded all output and hard closed all the VTY...
+ * Closes all listening sockets.
*/
extern void
vty_reset_because(const char* why)
@@ -317,15 +313,16 @@ vty_reset_because(const char* why)
{
assert(vty_init_state == vty_init_started) ;
- uty_reset(false, why) ; /* not final ! */
+ uty_reset(why, false) ; /* not curtains */
vty_init_state = vty_init_reset ;
} ;
+
VTY_UNLOCK() ;
-}
+} ;
/*------------------------------------------------------------------------------
- * Restart the VTY, following a vty_reset().
+ * Restart the VTY, following a vty_reset()/vty_reset_because().
*
* This starts the listeners for VTY_TERMINAL and VTY_SHELL_SERVER, again.
*
@@ -444,7 +441,7 @@ vty_terminate (void)
assert( (vty_init_state > vty_init_pending)
&& (vty_init_state < vty_init_terminated) ) ;
- uty_reset(true, "Shut down") ; /* final reset */
+ uty_reset("Shut down", true) ; /* curtains */
vty_child_close_register() ;
@@ -457,35 +454,71 @@ vty_terminate (void)
}
/*------------------------------------------------------------------------------
- * Reset -- for SIGHUP or at final curtain.
+ * Reset -- for SIGHUP/SIGINT or at final curtain.
+ *
+ * Closes listeners and resets the vty timeout and access lists.
+ *
+ * Closes all vty "final" -- see uty_close(). Before can close the vty must
+ * stop any related command loop -- see uty_cmd_loop_stop():
+ *
+ * * at "not-curtains" this is called in the *CLI* pthread, with all message
+ * and event handling is still running.
+ *
+ * This is called *before* a message is sent to the Routing Engine pthread
+ * to inform it that a SIGHUP/SIGINT/SIGTERM has been seen.
+ *
+ * It is possible that the command loop for a given vty cannot be stopped
+ * immediately and must be left to continue, until it sees the CMD_STOP
+ * vio->signal which will be set.
+ *
+ * * at "curtains" the system is being terminated, all threads have been
+ * joined, so is implicity in the CLI thread, but all message and event
+ * handling has stopped
+ *
+ * At "curtains" it is always possible to stop any command loop.
*
- * For SIGHUP is called via vty_reset_because(), and is sitting in the
- * vty_cli_nexus (if pthreaded) with the message queues still running.
+ * This is part of vty_terminate -- see above.
*
- * For final curtain will
+ * Close down is then a two step process:
*
+ * 1. vty_reset()/vty_reset_because() -- which immediately closes everything
+ * that does not have to wait for a command loop to stop, and anything
+ * that remains will be closed, automatically, as soon as possible.
*
- * is called by vty_terminate(), by which time all qnexus
- * have been shut down, so no message queues and no timers etc, are running.
+ * 2. vty_terminate() -- which sweeps up and closes anything that remains
+ * to be closed, and which the automatic mechanism has not got to yet.
*
+ * So, for each vty, if uty_cmd_loop_stop() does stop the command loop, then:
*
- * Closes listeners.
+ * * the close of the vty proceeds immediately.
*
- * Revokes any outstanding commands and close (SIGHUP) or close_final
- * (curtains) all VTY.
+ * Otherwise:
*
+ * * the CMD_STOP vio->signal has been set and the vty is left open.
*
+ * Note that the command loop *must* be running in the vty_cmd_nexus
+ * -- see uty_cmd_loop_stop().
*
- * Resets the vty timeout and access lists.
+ * * nothing further will happen until the Routing Engine collects the
+ * CMD_STOP vio->signal, and sends a message to pass the command loop
+ * back to the CLI pthread, to deal with the CMD_STOP.
*
- * When reach final reset it should not be possible for there to be any
- * commands still in progress. If there are, they are simply left on the
- * death-watch list... there is no pressing need to do anything more radical,
- * and the presence of anything on the death watch is grounds for some debug
- * activity !
+ * Before passing the command loop back, the Routing Engine will release
+ * the config symbol of power. So, if the Routing Engine goes on to
+ * re-read the configuration, it does not have to wait for the CLI to get
+ * round to stopping the vty.
+ *
+ * * when the CLI pthread receives the command loop back, it will collect the
+ * CMD_STOP (again) stop the command loop and close the vty.
+ *
+ * * when the Routing Engine picks up any SIGHUP/SIGTERM/etc message, it
+ * will either re-read the configuration or vty_terminate().
+ *
+ * The Routing Engine will not do this until after it has come out
+ * of the command loop, after dealing with the vio->signal, as above.
*/
static void
-uty_reset (bool curtains, const char* why)
+uty_reset (const char* why, bool curtains)
{
vty_io vio ;
vty_io next ;
@@ -500,7 +533,16 @@ uty_reset (bool curtains, const char* why)
vio = next ;
next = sdl_next(vio, vio_list) ;
- uty_close(vio, why, curtains) ;
+ /* Save the close reason for later, unless one is already set. */
+ if ((why != NULL) && (vio->close_reason == NULL))
+ vio->close_reason = XSTRDUP(MTYPE_TMP, why) ;
+
+ /* Stop the command loop -- if not already stopped.
+ *
+ * If command loop is running, it will be signalled to stop, soonest.
+ */
+ if (uty_cmd_loop_stop(vio, curtains))
+ uty_close(vio) ;
} ;
host.vty_timeout_val = VTY_TIMEOUT_DEFAULT;
@@ -512,7 +554,7 @@ uty_reset (bool curtains, const char* why)
/* sets host.vty_ipv6_accesslist_name = NULL */
if (curtains)
- uty_watch_dog_stop() ; /* and final death watch run */
+ uty_watch_dog_stop() ;
} ;
/*==============================================================================
@@ -680,6 +722,8 @@ vty_hello (struct vty *vty)
/*------------------------------------------------------------------------------
* "cat" file to vty
+ *
+ *
*/
extern cmd_return_code_t
vty_cat_file(vty vty, qpath path, const char* desc)
@@ -687,7 +731,7 @@ vty_cat_file(vty vty, qpath path, const char* desc)
int fd ;
void* buf ;
- fd = uty_vfd_file_open(qpath_string(path), vfd_io_read | vfd_io_blocking) ;
+ fd = uty_fd_file_open(qpath_string(path), vfd_io_read | vfd_io_blocking, 0) ;
if (fd < 0)
{
@@ -697,7 +741,7 @@ vty_cat_file(vty vty, qpath path, const char* desc)
return CMD_WARNING;
} ;
- enum { buffer_size = 64 * 1024 } ;
+ enum { buffer_size = 32 * 1024 } ;
buf = XMALLOC(MTYPE_TMP, buffer_size) ;
while (1)
@@ -717,6 +761,7 @@ vty_cat_file(vty vty, qpath path, const char* desc)
} ;
} ;
+ XFREE(MTYPE_TMP, buf) ;
close(fd) ;
return CMD_SUCCESS;
@@ -849,7 +894,7 @@ vty_read_config_first_cmd_special(const char *config_file,
name = qpath_string(path) ;
/* try to open the configuration file */
- conf_fd = uty_vfd_file_open(name, vfd_io_read) ;
+ conf_fd = uty_fd_file_open(name, vfd_io_read, 0) ;
if (conf_fd < 0)
{
@@ -908,8 +953,8 @@ vty_use_backup_config (qpath path)
sav_fd = -1 ;
tmp_fd = -1 ;
- sav_fd = uty_vfd_file_open(qpath_string(temp),
- vfd_io_read | vfd_io_blocking) ;
+ sav_fd = uty_fd_file_open(qpath_string(temp),
+ vfd_io_read | vfd_io_blocking, 0) ;
/* construct a temporary file and copy "<fullpath.sav>" to it. */
qpath_extend_str(temp, ".XXXXXX") ;
@@ -950,7 +995,7 @@ vty_use_backup_config (qpath path)
qpath_free(temp) ; /* done with the qpath */
if (ok)
- return uty_vfd_file_open(qpath_string(path), vfd_io_read) ;
+ return uty_fd_file_open(qpath_string(path), vfd_io_read, 0) ;
errno = err ;
return -1 ;
@@ -977,95 +1022,42 @@ vty_use_backup_config (qpath path)
* so all commands are executed directly.
*/
static void
-vty_read_config_file (int fd, const char* name, cmd_command first_cmd,
+vty_read_config_file(int fd, const char* name, cmd_command first_cmd,
bool ignore_warnings, bool full_lex)
{
cmd_return_code_t ret ;
vty vty ;
qtime_t taking ;
- /* Set up configuration file reader VTY -- which buffers all output */
- vty = vty_open(VTY_CONFIG_READ);
- vty->node = CONFIG_NODE;
-
- /* Make sure we have a suitable buffer, and set vty->buf to point at
- * it -- same like other command execution.
- */
- qs_need(&vty->vio->clx, VTY_BUFSIZ) ;
- vty->buf = qs_chars(&vty->vio->clx) ;
-
- taking = qt_get_monotonic() ;
-
- /* Execute configuration file */
- ret = config_from_file (vty, confp, first_cmd, &vty->vio->clx,
- ignore_warnings) ;
-
- taking = (qt_get_monotonic() - taking) / (QTIME_SECOND / 1000) ;
-
- zlog_info("Finished reading configuration in %d.%dsecs%s",
- (int)(taking / 1000), (int)(taking % 1000),
- (ret == CMD_SUCCESS) ? "." : " -- FAILED") ;
-
- VTY_LOCK() ;
-
vty = vty_config_read_open(fd, name, full_lex) ;
- vty_cmd_loop_prepare(vty) ;
-
- taking = qt_get_monotonic() ;
+ if (vty_cmd_config_loop_prepare(vty))
+ {
+ taking = qt_get_monotonic() ;
- ret = cmd_read_config(vty, first_cmd, ignore_warnings) ;
+ ret = cmd_read_config(vty, first_cmd, ignore_warnings) ;
- taking = (qt_get_monotonic() - taking) / (QTIME_SECOND / 1000) ;
+ taking = (qt_get_monotonic() - taking) / (QTIME_SECOND / 1000) ;
- zlog_info("Finished reading configuration '%s' in %d.%d secs%s",
+ zlog_info("Finished reading configuration '%s' in %d.%d secs%s",
name, (int)(taking / 1000), (int)(taking % 1000),
(ret == CMD_SUCCESS) ? "." : " -- FAILED") ;
+ }
+ else
+ {
+ /* This should be impossible -- before reading the configuration,
+ * all other vty are closed.
+ */
+ zlog_err("%s: Failed to gain config capability !", __func__) ;
+ ret = CMD_ERROR ;
+ } ;
- vty_cmd_loop_exit(vty) ;
+ vty_cmd_loop_exit(vty) ; /* the vty is released */
if (ret != CMD_SUCCESS)
exit(1) ;
} ;
-/*------------------------------------------------------------------------------
- * Push the given fd as the VOUT_CONFIG.
- *
- * Note that this is a "blocking" vf, so can open and close in the cmd thread.
- */
-extern void
-vty_open_config_write(vty vty, int fd)
-{
- vty_io vio ;
- vio_vf vf ;
-
- VTY_LOCK() ;
-
- vio = vty->vio ;
-
- vf = uty_vf_new(vio, "config write", fd, vfd_file,
- vfd_io_read | vfd_io_blocking) ;
- uty_vout_push(vio, vf, VOUT_CONFIG, NULL, NULL, 32 * 1024) ;
-
- VTY_UNLOCK() ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Write away any pending stuff, and pop the VOUT_CONFIG.
- */
-extern cmd_return_code_t
-vty_close_config_write(struct vty* vty, bool final)
-{
- cmd_return_code_t ret ;
- VTY_LOCK() ;
-
- ret = uty_vout_pop(vty->vio, final) ;
-
- VTY_UNLOCK() ;
-
- return ret ;
-} ;
-
/*==============================================================================
* Commands
*
@@ -1391,7 +1383,7 @@ vty_config_write (struct vty *vty)
{
vty_io vio ;
- VTY_LOCK() ; /* while accessing the host.xxx */
+ VTY_LOCK() ; /* while accessing the host.xx */
vio = vty->vio ;
@@ -1483,8 +1475,11 @@ vty_is_shell_client (struct vty *vty)
#endif
-void
-vty_set_lines(struct vty *vty, int lines)
+/*------------------------------------------------------------------------------
+ * When terminal length is set, change the current CLI setting, if required.
+ */
+extern void
+vty_set_lines(vty vty, int lines)
{
VTY_LOCK() ;
@@ -1592,7 +1587,8 @@ DEFUN (delay_secs,
a = (rand() % 4) + 1 ;
} ;
- *q++ = '\n' ;
+ if (n != 0)
+ *q++ = '\n' ;
*q++ = '\0' ;
vty_out(vty, buf) ;