summaryrefslogtreecommitdiffstats
path: root/lib/vty.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vty.c')
-rw-r--r--lib/vty.c816
1 files changed, 598 insertions, 218 deletions
diff --git a/lib/vty.c b/lib/vty.c
index 8acd1403..76f62e01 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -23,6 +23,7 @@
#include "zebra.h"
#include <stdbool.h>
+#include "version.h"
#include "vty_io.h"
#include "vty.h"
@@ -32,15 +33,13 @@
#include "list_util.h"
#include "command.h"
+#include "command_queue.h"
#include "memory.h"
#include "log.h"
+#include "mqueue.h"
/*==============================================================================
- * Variables etc.
- */
-
-/*------------------------------------------------------------------------------
- * Static and Global (see uty.h) Variables
+ * Variables etc. (see uty.h)
*/
/* The mutex and related debug counters */
@@ -48,8 +47,8 @@ qpt_mutex_t vty_mutex ;
#if VTY_DEBUG
-int vty_lock_count = 0 ;
-int vty_lock_assert_fail = 0 ;
+int vty_lock_count = 0 ;
+int vty_assert_fail = 0 ;
#endif
@@ -107,10 +106,25 @@ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ;
/*------------------------------------------------------------------------------
* Prototypes
*/
-static void uty_reset (bool final) ;
+static void uty_reset (bool final, const char* why) ;
static void uty_init_commands (void) ;
static void vty_save_cwd (void) ;
+/*------------------------------------------------------------------------------
+ * Tracking the initialisation state.
+ */
+enum vty_init_state
+{
+ vty_init_pending = 0, /* first and lowest numbered state */
+ vty_init_1st_stage,
+ vty_init_2nd_stage,
+ vty_init_started,
+ vty_init_reset,
+ vty_init_terminated /* final and highest numbered state */
+};
+
+static enum vty_init_state vty_init_state ;
+
/*==============================================================================
* Public Interface
*/
@@ -119,11 +133,19 @@ static void vty_save_cwd (void) ;
* Initialise vty handling (threads and pthreads)
*
* Install vty's own commands like `who' command.
+ *
+ * This runs before any pthreads or nexus stuff starts -- so is, implicitly,
+ * in the CLI thread.
+ *
+ * NB: may be called once and once only.
*/
extern void
vty_init (struct thread_master *master_thread)
{
- VTY_LOCK() ;
+ VTY_LOCK() ; /* Does nothing if !qpthreads_enabled */
+ VTY_ASSERT_CLI_THREAD() ; /* True if !qpthreads_enabled */
+
+ assert(vty_init_state == vty_init_pending) ;
vty_master = master_thread; /* Local pointer to the master thread */
@@ -140,6 +162,8 @@ vty_init (struct thread_master *master_thread)
uty_init_commands() ; /* install nodes */
+ vty_init_state = vty_init_1st_stage ;
+
VTY_UNLOCK() ;
}
@@ -149,30 +173,28 @@ vty_init (struct thread_master *master_thread)
* This is done during "second stage" initialisation, when all nexuses have
* been set up and the qpthread_enabled state established.
*
+ * This is before any threads have been started, so is, implicitly, in the
+ * CLI thread.
+ *
* Need to know where the CLI nexus and the Routeing Engine nexus are.
*
* Initialise mutex.
+ *
+ * Cannot lock or assert in CLI thread while initialising those things !
+ *
+ * NB: may be called once and once only.
*/
extern void
vty_init_r (qpn_nexus cli, qpn_nexus cmd)
{
+ assert(vty_init_state == vty_init_1st_stage) ;
+
vty_cli_nexus = cli ;
vty_cmd_nexus = cmd ;
qpt_mutex_init(&vty_mutex, qpt_mutex_recursive);
-} ;
-/*------------------------------------------------------------------------------
- * Initialise the listeners for VTY_TERM and VTY_SHELL_SERV VTY
- *
- * This is done after the configuration file has been read.
- */
-extern void
-vty_serv_sock(const char *addr, unsigned short port, const char *path)
-{
- VTY_LOCK() ;
- uty_open_listeners(addr, port, path) ;
- VTY_UNLOCK() ;
+ vty_init_state = vty_init_2nd_stage ;
} ;
/*------------------------------------------------------------------------------
@@ -189,86 +211,243 @@ vty_init_vtysh (void)
} ;
/*------------------------------------------------------------------------------
- * Create a new VTY of the given type
+ * Start the VTY going.
+ *
+ * This starts the listeners for VTY_TERM and VTY_SHELL_SERV.
+ *
+ * Also starts the watch dog.
+ *
+ * This is run during early morning start, after any daemonisation, but before
+ * any threads are started -- so is, implicitly, in the CLI thread.
+ *
+ * NB: may be called once and once only.
+ *
+ * NB: MUST be in the CLI thread (if any).
*/
-extern struct vty *
-vty_new (int fd, enum vty_type type)
+extern void
+vty_start(const char *addr, unsigned short port, const char *path)
{
- struct vty* vty ;
-
VTY_LOCK() ;
- vty = uty_new(fd, type);
- VTY_UNLOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
- return vty ;
+ assert( (vty_init_state == vty_init_1st_stage)
+ || (vty_init_state == vty_init_2nd_stage) ) ;
+
+ uty_watch_dog_start() ;
+
+ uty_open_listeners(addr, port, path) ;
+
+ vty_init_state = vty_init_started ;
+ VTY_UNLOCK() ;
} ;
/*------------------------------------------------------------------------------
- * Close the given VTY completely
+ * Reset all VTY status for reasons unknown -- probably SIGHUP
*/
extern void
-vty_close (struct vty *vty)
+vty_reset()
{
- VTY_LOCK() ;
- uty_close(vty->vio);
- VTY_UNLOCK() ;
+ vty_reset_because("Reset") ;
}
/*------------------------------------------------------------------------------
* Reset all VTY status
*
- * This is done just before the configuration file is re-read (SIGHUP).
+ * This is done in response to SIGHUP -- and runs in the CLI thread.
*
- * Half closes all VTY, leaving the death watch to tidy up once all output has
- * completed.
+ * Half closes all VTY, leaving the death watch to tidy up once all output
+ * and any command in progress have completed.
*
- * NB: old code discarded all output and hard closed all the VTY...
+ * Closes all listening sockets.
*
- * TODO: ...SIGHUP while a command is queued ?
+ * TODO: revoke ?
*
- * Closes all listening sockets.
+ * NB: old code discarded all output and hard closed all the VTY...
*/
extern void
-vty_reset(void)
+vty_reset_because(const char* why)
{
VTY_LOCK() ;
- uty_reset(0) ; /* not final ! */
+ VTY_ASSERT_CLI_THREAD() ;
+
+ assert(vty_init_state == vty_init_started) ;
+
+ uty_reset(0, why) ; /* not final ! */
+
+ vty_init_state = vty_init_reset ;
VTY_UNLOCK() ;
}
/*------------------------------------------------------------------------------
+ * Restart the VTY, following a vty_reset().
+ *
+ * This starts the listeners for VTY_TERM and VTY_SHELL_SERV, again.
+ *
+ * NB: may be called once, and once only, *after* a vty_reset().
+ *
+ * NB: need not be in the CLI thread (if any).
+ */
+struct vty_restart_args
+{
+ char* addr ;
+ unsigned short port ;
+ char* path ;
+} ;
+MQB_ARGS_SIZE_OK(vty_restart_args) ;
+
+static void uty_restart_action(mqueue_block mqb, mqb_flag_t flag) ;
+static void uty_restart(const char *addr, unsigned short port,
+ const char *path) ;
+extern void
+vty_restart(const char *addr, unsigned short port, const char *path)
+{
+ VTY_LOCK() ;
+
+ /* If not running qnexus-wise, call uty_restart directly.
+ *
+ * Otherwise, construct and dispatch message to do a uty_restart.
+ */
+ if (!vty_cli_nexus)
+ uty_restart(addr, port, path) ;
+ else
+ {
+ mqueue_block mqb ;
+ struct vty_restart_args* args ;
+
+ mqb = mqb_init_new(NULL, uty_restart_action, vty_cli_nexus) ;
+ args = mqb_get_args(mqb) ;
+
+ if (addr != NULL)
+ args->addr = XSTRDUP(MTYPE_TMP, addr) ;
+ else
+ args->addr = NULL ;
+
+ args->port = port ;
+
+ if (path != NULL)
+ args->path = XSTRDUP(MTYPE_TMP, path) ;
+ else
+ args->path = NULL ;
+
+ mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
+ } ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/* Deal with the uty_restart message */
+static void
+uty_restart_action(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct vty_restart_args* args ;
+ args = mqb_get_args(mqb) ;
+
+ if (flag == mqb_action)
+ {
+ VTY_LOCK() ;
+
+ uty_restart(args->addr, args->port, args->path) ;
+
+ VTY_UNLOCK() ;
+ } ;
+
+ if (args->addr != NULL)
+ XFREE(MTYPE_TMP, args->addr) ;
+ if (args->path != NULL)
+ XFREE(MTYPE_TMP, args->path) ;
+} ;
+
+/* Do the actual restart */
+static void
+uty_restart(const char *addr, unsigned short port, const char *path)
+{
+ VTY_ASSERT_LOCKED() ;
+ assert(vty_init_state == vty_init_reset) ;
+
+ uty_open_listeners(addr, port, path) ;
+
+ vty_init_state = vty_init_started ;
+} ;
+
+/*------------------------------------------------------------------------------
* System shut-down
*
- * Reset all known vty and release all memory.
+ * In the pthreads world, all threads other than the main (CLI) thread have
+ * been joined -- so this is, implicitly, in the CLI thread.
+ *
+ * Close all known vty and release all memory -- discard all pending output.
+ *
+ * NB: this may be done in any initialisation state.
+ *
+ * Note that all the locking stuff does nothing if not qpthreads_enabled, so
+ * these may be done in any state of initialisation. (It is assumed that the
+ * switch into qpthreads_enabled is an atomic action... so all second stage
+ * initialisation completes together.)
*/
extern void
vty_terminate (void)
{
+ if ( (vty_init_state == vty_init_pending)
+ || (vty_init_state == vty_init_terminated) )
+ return ; /* nothing to do ! */
+
VTY_LOCK() ;
- uty_reset(1) ; /* final reset */
+ VTY_ASSERT_CLI_THREAD() ;
+
+ assert( (vty_init_state > vty_init_pending)
+ && (vty_init_state < vty_init_terminated) ) ;
+
+ uty_reset(1, "Shut down") ; /* final reset */
+
VTY_UNLOCK() ;
qpt_mutex_destroy(&vty_mutex, 0);
+
+ vty_init_state = vty_init_terminated ;
}
/*------------------------------------------------------------------------------
* Reset -- final or for SIGHUP
+ *
+ * Closes listeners.
+ *
+ * Closes (final) or half closes (SIGHUP) all VTY, and revokes any outstanding
+ * commands.
+ *
+ * Resets the vty timeout and access lists.
+ *
+ * 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 !
*/
static void
-uty_reset (bool curtains)
+uty_reset (bool curtains, const char* why)
{
vty_io vio ;
+ vty_io next ;
VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
uty_close_listeners() ;
- while ((vio = sdl_pop(&vio, vio_list_base, vio_list)) != NULL)
+ next = sdl_head(vio_list_base) ;
+ while (next != NULL)
{
- uty_half_close(vio) ; /* TODO: reason for close */
+ vio = next ;
+ next = sdl_next(vio, vio_list) ;
+
+ cq_revoke(vio->vty) ;
+
+ if (why != NULL)
+ vio->close_reason = why ;
if (curtains)
- uty_full_close(vio) ;
+ uty_close(vio) ;
+ else
+ uty_half_close(vio, why) ;
} ;
vty_timeout_val = VTY_TIMEOUT_DEFAULT;
@@ -287,9 +466,48 @@ uty_reset (bool curtains)
if (curtains && vty_cwd)
XFREE (MTYPE_TMP, vty_cwd);
+
+ if (curtains)
+ uty_watch_dog_stop() ; /* and final death watch run */
} ;
/*==============================================================================
+ * Opening and closing VTY.
+ *
+ * VTY without a socket may be opened and closed at will.
+ *
+ * TODO: sort out the relationship between the non-socket VTY and vty_reset()
+ */
+
+/*------------------------------------------------------------------------------
+ * Create a new VTY of the given type
+ *
+ * The type may NOT be: VTY_TERM or VTY_SHELL_SERV
+ */
+extern struct vty *
+vty_open(enum vty_type type)
+{
+ struct vty* vty ;
+
+ VTY_LOCK() ;
+ vty = uty_new(type, -1) ; /* fails for VTY_TERM or VTY_SHELL_SERV */
+ VTY_UNLOCK() ;
+
+ return vty ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the given VTY
+ */
+extern void
+vty_close (struct vty *vty)
+{
+ VTY_LOCK() ;
+ uty_close(vty->vio) ;
+ VTY_UNLOCK() ;
+}
+
+/*==============================================================================
* General VTY output.
*
* This is mostly used during command execution, to output the results of the
@@ -324,17 +542,14 @@ vty_out (struct vty *vty, const char *format, ...)
const char vty_spaces_string[] = " " ;
CONFIRM(VTY_MAX_SPACES == (sizeof(vty_spaces_string) - 1)) ;
-extern int
+extern void
vty_out_indent(struct vty *vty, int indent)
{
- while (indent > VTY_MAX_SPACES)
+ while (indent > 0)
{
- int ret = vty_out(vty, VTY_SPACES(indent)) ;
- if (ret < 0)
- return ret ;
+ vty_out(vty, VTY_SPACES(indent)) ;
indent -= VTY_MAX_SPACES ;
}
- return vty_out(vty, VTY_SPACES(indent)) ;
} ;
/*------------------------------------------------------------------------------
@@ -394,81 +609,91 @@ vty_hello (struct vty *vty)
VTY_UNLOCK() ;
}
+/*------------------------------------------------------------------------------
+ * Clear the contents of the command output FIFO etc.
+ */
+extern void
+vty_out_clear(struct vty* vty)
+{
+ VTY_LOCK() ;
+ uty_out_clear(vty->vio) ;
+ VTY_UNLOCK() ;
+} ;
+
/*==============================================================================
* Command Execution
*/
/*------------------------------------------------------------------------------
- * Execute command, adding it to the history if not empty or comment
+ * Execute command -- adding to history is not empty or just comment
+ *
+ * This is for VTY_TERM type VTY.
*
* Outputs diagnostics if fails to parse.
*
- * Returns: CMD_xxxx result.
+ * Returns: command return code
*/
-extern int
-uty_command(struct vty *vty, const char *buf)
+extern enum cmd_return_code
+uty_command(struct vty *vty)
{
- int ret;
- vector vline;
- const char *protocolname;
+ enum cmd_return_code ret;
VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
- /* Split readline string up into the vector */
- vline = cmd_make_strvec (buf);
+ assert(vty->vio->type == VTY_TERM) ;
- if (vline == NULL)
- return CMD_SUCCESS; /* quit if empty or comment */
-
- uty_cli_hist_add (vty->vio, buf) ;
+ /* Parse the command and add to history (if not empty) */
+ ret = cmd_parse_command(vty,
+ cmd_parse_completion + cmd_parse_do + cmd_parse_tree) ;
+ if (ret != CMD_EMPTY)
+ uty_cli_hist_add (vty->vio, vty->buf) ;
+ /* If parsed and not empty, dispatch */
+ if (ret == CMD_SUCCESS)
+ {
#ifdef CONSUMED_TIME_CHECK
- {
- RUSAGE_T before;
- RUSAGE_T after;
- unsigned long realtime, cputime;
+ RUSAGE_T before;
+ RUSAGE_T after;
+ unsigned long realtime, cputime;
- GETRUSAGE(&before);
+ GETRUSAGE(&before);
#endif /* CONSUMED_TIME_CHECK */
-//VTY_UNLOCK() ;
- ret = cmd_execute_command (vline, vty, NULL, vty_cmd_nexus, vty_cli_nexus, 0);
-//VTY_LOCK() ;
-
- /* Get the name of the protocol if any */
- protocolname = uzlog_get_proto_name(NULL);
+ ret = cmd_dispatch(vty, cmd_may_queue) ;
#ifdef CONSUMED_TIME_CHECK
- GETRUSAGE(&after);
- if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
- CONSUMED_TIME_CHECK)
- /* Warn about CPU hog that must be fixed. */
- uzlog(NULL, LOG_WARNING, "SLOW COMMAND: command took %lums (cpu time %lums): %s",
- realtime/1000, cputime/1000, buf);
- }
+ GETRUSAGE(&after);
+ if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
+ CONSUMED_TIME_CHECK)
+ /* Warn about CPU hog that must be fixed. */
+ uzlog(NULL, LOG_WARNING,
+ "SLOW COMMAND: command took %lums (cpu time %lums): %s",
+ realtime/1000, cputime/1000, vty->buf) ;
#endif /* CONSUMED_TIME_CHECK */
+ } ;
- if (ret != CMD_SUCCESS)
- switch (ret)
- {
- case CMD_WARNING:
- if (vty->vio->type == VTY_FILE)
- uty_out (vty, "Warning...%s", VTY_NEWLINE);
- break;
- case CMD_ERR_AMBIGUOUS:
- uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
- break;
- case CMD_ERR_NO_MATCH:
- uty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
- break;
- case CMD_ERR_INCOMPLETE:
- uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
- break;
- }
- cmd_free_strvec (vline);
-
- return ret;
-}
+ /* Deal with the return code */
+ switch (ret)
+ {
+ case CMD_ERR_AMBIGUOUS:
+ uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ break;
+
+ case CMD_ERR_NO_MATCH:
+ uty_out (vty, "%% Unknown command.%s", VTY_NEWLINE) ;
+ break;
+
+ case CMD_ERR_INCOMPLETE:
+ uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
+ break;
+
+ default:
+ break ;
+ } ;
+
+ return ret ;
+} ;
/*------------------------------------------------------------------------------
* Authentication of vty
@@ -479,19 +704,21 @@ uty_command(struct vty *vty, const char *buf)
* Note that if the AUTH_NODE password fails too many times, the terminal is
* closed.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
{
char *passwd = NULL;
enum node_type next_node = 0;
int fail;
char *crypt (const char *, const char *);
+ enum cmd_return_code ret ;
vty_io vio = vty->vio ;
VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
/* What to do ?
*
@@ -502,20 +729,23 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
{
case cli_do_nothing:
case cli_do_ctrl_c:
- case cli_do_ctrl_d:
case cli_do_ctrl_z:
- return 0 ;
+ return CMD_SUCCESS ;
case cli_do_command:
break ;
+ case cli_do_ctrl_d:
+ case cli_do_eof:
+ return uty_cmd_close(vty, "End") ;
+
default:
zabort("unknown or invalid cli_do") ;
} ;
/* Ordinary command dispatch -- see if password is OK. */
switch (vty->node)
- {
+ {
case AUTH_NODE:
if (host.encrypt)
passwd = host.password_encrypt;
@@ -526,6 +756,7 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
else
next_node = VIEW_NODE;
break;
+
case AUTH_ENABLE_NODE:
if (host.encrypt)
passwd = host.enable_encrypt;
@@ -533,7 +764,10 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
passwd = host.enable;
next_node = ENABLE_NODE;
break;
- }
+
+ default:
+ zabort("unknown node type") ;
+ }
if (passwd)
{
@@ -545,6 +779,8 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
else
fail = 1;
+ ret = CMD_SUCCESS ;
+
if (! fail)
{
vio->fail = 0;
@@ -557,9 +793,7 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
{
if (vty->node == AUTH_NODE)
{
- uty_out (vty, "%% Bad passwords, too many failures!%s",
- VTY_NEWLINE);
- uty_half_close(vio) ;
+ ret = uty_cmd_close(vty, "Bad passwords, too many failures!%s") ;
}
else
{
@@ -568,11 +802,13 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
uty_out (vty, "%% Bad enable passwords, too many failures!%s",
VTY_NEWLINE);
vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
+
+ ret = CMD_WARNING ;
}
}
}
- return 0 ;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
@@ -580,13 +816,16 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
*
* Falls back one NODE level.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
vty_cmd_exit(struct vty* vty)
{
- VTY_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ enum cmd_return_code ret ;
+
+ VTY_LOCK() ;
+ ret = CMD_SUCCESS ;
switch (vty->node)
{
case VIEW_NODE:
@@ -594,8 +833,8 @@ vty_cmd_exit(struct vty* vty)
case RESTRICTED_NODE:
if (vty_shell (vty))
exit (0);
-// else
-// vty_set_status(vty, VTY_CLOSE);
+ else
+ ret = uty_cmd_close(vty, "Exit") ;
break;
case CONFIG_NODE:
uty_config_unlock (vty, ENABLE_NODE);
@@ -628,8 +867,8 @@ vty_cmd_exit(struct vty* vty)
break;
}
- VTY_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
- return 0 ;
+ VTY_UNLOCK() ;
+ return ret ;
}
/*------------------------------------------------------------------------------
@@ -637,12 +876,12 @@ vty_cmd_exit(struct vty* vty)
*
* Falls back to ENABLE_NODE.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
vty_cmd_end(struct vty* vty)
{
- VTY_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ VTY_LOCK() ;
switch (vty->node)
{
@@ -676,8 +915,22 @@ vty_cmd_end(struct vty* vty)
break;
}
- VTY_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
- return 0 ;
+ VTY_UNLOCK() ;
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Result of command is to close the input.
+ *
+ * Posts the reason for the close.
+ *
+ * Returns: CMD_CLOSE
+ */
+extern enum cmd_return_code
+uty_cmd_close(struct vty *vty, const char* reason)
+{
+ vty->vio->close_reason = reason ;
+ return CMD_CLOSE ;
} ;
/*------------------------------------------------------------------------------
@@ -689,9 +942,9 @@ vty_cmd_end(struct vty* vty)
*
* Resets the history pointer.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
uty_stop_input(struct vty *vty)
{
vty_io vio = vty->vio ;
@@ -724,7 +977,7 @@ uty_stop_input(struct vty *vty)
/* Set history pointer to the latest one. */
vio->hp = vio->hindex;
- return 0 ;
+ return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
@@ -734,9 +987,9 @@ uty_stop_input(struct vty *vty)
*
* Fall back to ENABLE_NODE if in any one of a number of nodes.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
uty_end_config (struct vty *vty)
{
VTY_ASSERT_LOCKED() ;
@@ -774,7 +1027,7 @@ uty_end_config (struct vty *vty)
break;
}
- return 0 ;
+ return CMD_SUCCESS ;
}
/*------------------------------------------------------------------------------
@@ -782,9 +1035,9 @@ uty_end_config (struct vty *vty)
*
* Same as "exit" command.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
uty_down_level (struct vty *vty)
{
return vty_cmd_exit(vty) ;
@@ -792,10 +1045,28 @@ uty_down_level (struct vty *vty)
/*==============================================================================
* Reading of configuration file
+ *
+ * The reading of the configuration file occurs at two times:
+ *
+ * 1. early in the morning, before daemonisation, and before any threads
+ * or nexuses have been set up.
+ *
+ * In the qpthreads world, this means that it is running in the main (CLI)
+ * and only thread and nexus.
+ *
+ * 2. at SIGHUP time.
+ *
+ * In the qpthreads world, this is running in whatever thread is executing
+ * commands.
+ *
+ * Sets up a VTY_CONFIG_READ in which to execute commands. This has no CLI
+ * and no socket. All output is buffered in the cmd_obuf. All commands are
+ * run directly in the thread -- no commands are queued.
*/
static FILE * vty_use_backup_config (char *fullpath) ;
-static void vty_read_file (FILE *confp, void (*after_first_cmd)(void)) ;
+static void vty_read_file (FILE *confp, struct cmd_element* first_cmd,
+ bool ignore_warnings) ;
/*------------------------------------------------------------------------------
* Read the given configuration file.
@@ -804,7 +1075,7 @@ extern void
vty_read_config (char *config_file,
char *config_default)
{
- vty_read_config_first_cmd_special(config_file, config_default, NULL);
+ vty_read_config_first_cmd_special(config_file, config_default, NULL, 1);
}
/*------------------------------------------------------------------------------
@@ -827,7 +1098,8 @@ vty_read_config (char *config_file,
extern void
vty_read_config_first_cmd_special(char *config_file,
char *config_default,
- void (*after_first_cmd)(void))
+ struct cmd_element* first_cmd,
+ bool ignore_warnings)
{
char cwd[MAXPATHLEN];
FILE *confp = NULL;
@@ -902,7 +1174,7 @@ vty_read_config_first_cmd_special(char *config_file,
fprintf(stderr, "Reading config file: %s\n", fullpath);
#endif
- vty_read_file (confp, after_first_cmd);
+ vty_read_file (confp, first_cmd, ignore_warnings);
fclose (confp);
host_config_set (fullpath);
@@ -998,15 +1270,28 @@ vty_use_backup_config (char *fullpath)
* May have a function to call after the first actual command is processed.
* This mechanism supports the setting of qpthreads-ness by configuration file
* command.
+ *
+ * In the qpthreads world:
+ *
+ * * when the configuration is first read, this runs in the CLI thread
+ * (the main and only thread).
+ *
+ * * when the configuration is reread, this runs in the command processor
+ * thread.
+ *
+ * All consoles are shut down, so there can be no interference from that
+ * quarter.
+ *
+ * so all commands are executed directly.
*/
static void
-vty_read_file (FILE *confp, void (*after_first_cmd)(void))
+vty_read_file (FILE *confp, struct cmd_element* first_cmd, bool ignore_warnings)
{
- int ret;
- struct vty *vty;
+ enum cmd_return_code ret ;
+ struct vty *vty ;
- /* TODO: sort out what VTY Type should use for reading config file */
- vty = vty_new (0, VTY_TERM); /* stdout */
+ /* 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
@@ -1016,60 +1301,51 @@ vty_read_file (FILE *confp, void (*after_first_cmd)(void))
vty->buf = qs_chars(&vty->vio->clx) ;
/* Execute configuration file */
- ret = config_from_file (vty, confp, after_first_cmd, &vty->vio->clx) ;
+ ret = config_from_file (vty, confp, first_cmd, &vty->vio->clx,
+ ignore_warnings) ;
VTY_LOCK() ;
- if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
+ if (ret != CMD_SUCCESS)
{
+ fprintf (stderr, "%% while processing line %u of the configuration:\n"
+ "%s", vty->lineno, vty->buf) ;
+
switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- fprintf (stderr, "Ambiguous command.\n");
- break;
- case CMD_ERR_NO_MATCH:
- fprintf (stderr, "There is no such command.\n");
- break;
- }
- fprintf (stderr, "Error occurred while processing:\n%s\n", vty->buf);
+ {
+ case CMD_WARNING:
+ fprintf (stderr, "%% Warning...\n");
+ break;
- exit (1);
- }
+ case CMD_ERROR:
+ fprintf (stderr, "%% Error...\n");
+ break;
- uty_half_close (vty->vio);
- VTY_UNLOCK() ;
-}
+ case CMD_ERR_AMBIGUOUS:
+ fprintf (stderr, "%% Ambiguous command.\n");
+ break;
-#ifdef QDEBUG
-/* Tell all terminals that we are shutting down */
-void
-vty_goodbye (void)
-{
- unsigned int i;
- struct vty *vty;
+ case CMD_ERR_NO_MATCH:
+ fprintf (stderr, "%% There is no such command.\n");
+ break;
- VTY_LOCK() ;
+ case CMD_ERR_INCOMPLETE:
+ fprintf (stderr, "%% Incomplete command.\n");
+ break;
- if (vtyvec)
- {
- for (i = 0; i < vector_active (vtyvec); i++)
- {
- if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->vio->type == VTY_TERM)
- {
- uty_cout(vty, QUAGGA_PROGNAME " is shutting down%s", VTY_NEWLINE);
+ default:
+ fprintf(stderr, "%% (unknown cause %d)\n", ret) ;
+ break ;
+ } ;
- /* Wake up */
- if (vty_cli_nexus)
- vty_event (VTY_WRITE, vty->vio->fd, vty);
- }
- }
- if (qpthreads_enabled)
- qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
- }
+ uty_out_fflush(vty->vio, stderr) ; /* flush command output buffer */
- VTY_UNLOCK() ;
-}
-#endif
+ exit (1);
+ } ;
+
+ uty_close(vty->vio) ;
+ VTY_UNLOCK() ;
+} ;
/*==============================================================================
* Configuration node/state handling
@@ -1197,8 +1473,9 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
timeout += strtol (sec_str, NULL, 10);
vty_timeout_val = timeout;
- vty->vio->file.v_timeout = timeout;
-// vty_event (VTY_TIMEOUT_RESET, 0, vty);
+
+ if (vty_term(vty) || vty_shell_serv(vty))
+ uty_sock_set_timer(&vty->vio->sock, timeout) ;
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1445,7 +1722,7 @@ DEFUN_CALL (show_history,
for (index = vty->vio->hindex + 1; index != vty->vio->hindex;)
{
- const char* line ;
+ qstring line ;
if (index == VTY_MAXHIST)
{
@@ -1455,7 +1732,7 @@ DEFUN_CALL (show_history,
line = vector_get_item(&vty->vio->hist, index) ;
if (line != NULL)
- uty_out (vty, " %s%s", line, VTY_NEWLINE);
+ uty_out (vty, " %s%s", line->char_body, VTY_NEWLINE);
index++;
}
@@ -1545,27 +1822,37 @@ vty_get_cwd ()
* Access functions for VTY values, where locking is or might be required.
*/
-int
+bool
vty_shell (struct vty *vty)
{
+ bool result;
VTY_LOCK() ;
- int result;
- result = (vty->vio->type == VTY_SHELL) ? 1 : 0 ;
+ result = (vty->vio->type == VTY_SHELL) ;
VTY_UNLOCK() ;
return result;
}
-int
+bool
+vty_term(struct vty *vty)
+{
+ bool result;
+ VTY_LOCK() ;
+ result = (vty->vio->type == VTY_TERM);
+ VTY_UNLOCK() ;
+ return result;
+}
+
+bool
vty_shell_serv (struct vty *vty)
{
+ bool result;
VTY_LOCK() ;
- int result;
- result = ((vty->vio->type == VTY_SHELL_SERV) ? 1 : 0);
+ result = (vty->vio->type == VTY_SHELL_SERV);
VTY_UNLOCK() ;
return result;
}
-int
+enum node_type
vty_get_node(struct vty *vty)
{
int result;
@@ -1576,39 +1863,129 @@ vty_get_node(struct vty *vty)
}
void
-vty_set_node(struct vty *vty, int node)
+vty_set_node(struct vty *vty, enum node_type node)
{
VTY_LOCK() ;
vty->node = node;
VTY_UNLOCK() ;
}
-int
-vty_get_type(struct vty *vty)
+void
+vty_set_lines(struct vty *vty, int lines)
{
- int result;
VTY_LOCK() ;
- result = vty->vio->type;
+ vty->vio->lines = lines;
+ vty->vio->lines_set = 1 ;
+ uty_set_height(vty->vio) ;
VTY_UNLOCK() ;
- return result;
}
-int
-vty_get_lines(struct vty *vty)
+/*==============================================================================
+ *
+ */
+const char* wordlist[] =
+ {
+ "Lorem",
+ "ipsum",
+ "dolor",
+ "magna",
+ "vita",
+ "brevis",
+ "Aliquot",
+ "in",
+ "tempura",
+ "mores",
+ "ad",
+ "Astronomica",
+ "per",
+ "impedimenta",
+ "quod",
+ "et",
+ "sed",
+ "semper",
+ "ut",
+ "Elisium",
+ "est",
+ };
+
+
+DEFUN (delay_secs,
+ delay_secs_cmd,
+ "delay <0-600> secs <0-10000> lines",
+ "Delay for a number of seconds and spit out a number of lines\n"
+ "Delay time\n"
+ "Delay time units\n"
+ "How much to output\n"
+ "Output units\n")
{
- int result;
- VTY_LOCK() ;
- result = vty->vio->lines;
- VTY_UNLOCK() ;
- return result;
-}
+ unsigned long delay ;
+ unsigned long lines ;
-void
-vty_set_lines(struct vty *vty, int lines)
-{
- VTY_LOCK() ;
- vty->vio->lines = lines;
- VTY_UNLOCK() ;
+ unsigned long unit ;
+
+ delay = strtol(argv[0], NULL, 10) ;
+ lines = strtol(argv[1], NULL, 10) ;
+
+ vty_out(vty, "delay %d secs %d lines\n", (int)delay, (int)lines) ;
+
+ unit = (lines * 100) / delay ;
+
+ while (delay--)
+ {
+ char buf[200] ;
+ char* e ;
+ int n ;
+ int w = sizeof(wordlist) / sizeof(char*) ;
+
+ sleep(1) ;
+
+ n = ((rand() % (unit + 1)) + (unit / 2)) / 100 ;
+
+ if ((n > (int)lines) || (delay == 0))
+ n = lines ;
+
+ lines -= n ;
+
+ while (n--)
+ {
+ char* q ;
+ const char* p ;
+ int a ;
+
+ q = buf ;
+ e = buf + (rand() % 120) + 30 ;
+
+ if ((rand() % 6) == 0)
+ e = buf ;
+
+ a = (rand() % 4) == 1 ;
+ while (q < e)
+ {
+ int s ;
+ s = 0 ;
+ if (a == 1)
+ s = (rand() % 5) + 1 ;
+ else if (a > 1)
+ s = 1 ;
+
+ while (s--)
+ *q++ = ' ' ;
+
+ p = wordlist[rand() % w] ;
+ while (*p != '\0')
+ *q++ = *p++ ;
+
+ a = (rand() % 4) + 1 ;
+ } ;
+
+ *q++ = '\n' ;
+ *q++ = '\0' ;
+
+ vty_out(vty, buf) ;
+ } ;
+ } ;
+
+ return CMD_SUCCESS;
}
/*==============================================================================
@@ -1632,6 +2009,9 @@ uty_init_commands (void)
install_node (&vty_node, vty_config_write);
+ install_element (VIEW_NODE, &delay_secs_cmd);
+ install_element (ENABLE_NODE, &delay_secs_cmd);
+
install_element (RESTRICTED_NODE, &config_who_cmd);
install_element (RESTRICTED_NODE, &show_history_cmd);
install_element (VIEW_NODE, &config_who_cmd);