summaryrefslogtreecommitdiffstats
path: root/lib/vty.c
diff options
context:
space:
mode:
authorChris Hall <chris.hall@highwayman.com>2011-07-21 19:53:02 +0100
committerChris Hall <chris.hall@highwayman.com>2011-07-21 19:53:02 +0100
commit56da2a1c9b6361e302b7a39fe2740561a9012d88 (patch)
tree6b6543532133a0c618d0f4ec70a87cf3f96caf30 /lib/vty.c
parente535bc959729262480a9702e71334002edee3f8c (diff)
downloadquagga-56da2a1c9b6361e302b7a39fe2740561a9012d88.tar.bz2
quagga-56da2a1c9b6361e302b7a39fe2740561a9012d88.tar.xz
Update pipework and improve memory reporting.
Improve error handling for all new pipework inputs and outputs. Change behaviour of ^C from VTY Terminal, so that will interrupt output and terminate all running pipes -- including running shell commands. In pipe commands, recognise "~/..." and "~user/..." home directory forms. Changed "~/" to mean the usual home for the current user. "~~/" now means the configuration file directory. Introduced "shdir DIR" command to show what is (currently) what. Changed "<|" so that if the command has a path, it is expanded using Quagga's rules (including "~~/" and "~./") and the "here" directory is set to that path. Fixed collection of stderr output from all pipes so that is separate from stdout output, and is always sent to the base output (eg VTY Terminal). Increase amount of information about the heap that "show mem" shows -- particularly if the "memory_tracker" is enabled. Tested and applied resulting fixes.
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) ;