summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorChris Hall <chris.hall@highwayman.com>2011-02-13 23:11:45 +0000
committerChris Hall <chris.hall@highwayman.com>2011-02-13 23:11:45 +0000
commit5cae7eea451f2b7d65b5892e2c1dafc70f8b836e (patch)
tree0fbd9679e9ae28e7d061b5bdda08756077415ecb /lib
parent64be6d766a65dc0749d17f5023d714678e9c96a6 (diff)
downloadquagga-5cae7eea451f2b7d65b5892e2c1dafc70f8b836e.tar.bz2
quagga-5cae7eea451f2b7d65b5892e2c1dafc70f8b836e.tar.xz
Second tranche of updates for pipework branch.
modified: bgpd/bgp_connection.c modified: bgpd/bgp_debug.c modified: bgpd/bgp_engine.h modified: bgpd/bgp_main.c modified: bgpd/bgp_packet.c modified: bgpd/bgp_peer.c modified: bgpd/bgp_route.c modified: bgpd/bgp_routemap.c modified: bgpd/bgp_session.c modified: bgpd/bgp_vty.c modified: bgpd/bgpd.c modified: bgpd/bgpd.h modified: configure.ac modified: isisd/dict.h modified: isisd/isis_misc.c modified: isisd/isis_routemap.c modified: isisd/isis_spf.c modified: lib/Makefile.am modified: lib/command.c modified: lib/command.h modified: lib/command_execute.h modified: lib/command_parse.c modified: lib/command_parse.h modified: lib/command_queue.c modified: lib/command_queue.h modified: lib/elstring.h modified: lib/heap.c modified: lib/if.c modified: lib/if.h modified: lib/keychain.c modified: lib/keystroke.c modified: lib/keystroke.h modified: lib/list_util.c modified: lib/list_util.h modified: lib/log.c modified: lib/log.h modified: lib/memory.c modified: lib/memory.h modified: lib/memtypes.c modified: lib/misc.h modified: lib/mqueue.c modified: lib/mqueue.h deleted: lib/node_type.h modified: lib/pthread_safe.c modified: lib/qfstring.c modified: lib/qiovec.c modified: lib/qiovec.h modified: lib/qpath.c modified: lib/qpnexus.c modified: lib/qpnexus.h modified: lib/qpselect.c modified: lib/qpthreads.h modified: lib/qstring.c modified: lib/qstring.h modified: lib/qtime.c modified: lib/qtime.h modified: lib/qtimers.c modified: lib/qtimers.h modified: lib/routemap.c modified: lib/symtab.h modified: lib/thread.h deleted: lib/uty.h modified: lib/vector.c modified: lib/vector.h modified: lib/version.h.in modified: lib/vio_fifo.c modified: lib/vio_fifo.h modified: lib/vio_lines.c modified: lib/vio_lines.h modified: lib/vty.c modified: lib/vty.h modified: lib/vty_cli.c modified: lib/vty_cli.h modified: lib/vty_io.c modified: lib/vty_io.h modified: lib/vty_io_basic.c modified: lib/vty_io_basic.h modified: lib/vty_io_file.c modified: lib/vty_io_file.h modified: lib/vty_io_shell.c modified: lib/vty_io_term.c modified: lib/vty_io_term.h modified: lib/vty_local.h modified: lib/vty_pipe.c modified: lib/workqueue.h modified: lib/zebra.h modified: ospf6d/ospf6_lsa.c modified: ripngd/ripngd.c modified: tests/test-list_util.c modified: tests/test-vector.c modified: vtysh/vtysh.c modified: vtysh/vtysh_config.c
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am19
-rw-r--r--lib/command.c2919
-rw-r--r--lib/command.h263
-rw-r--r--lib/command_execute.h135
-rw-r--r--lib/command_parse.c4260
-rw-r--r--lib/command_parse.h806
-rw-r--r--lib/command_queue.c580
-rw-r--r--lib/command_queue.h8
-rw-r--r--lib/elstring.h259
-rw-r--r--lib/heap.c4
-rw-r--r--lib/if.c2
-rw-r--r--lib/if.h14
-rw-r--r--lib/keychain.c22
-rw-r--r--lib/keystroke.c41
-rw-r--r--lib/keystroke.h6
-rw-r--r--lib/list_util.c12
-rw-r--r--lib/list_util.h10
-rw-r--r--lib/log.c3
-rw-r--r--lib/log.h8
-rw-r--r--lib/memory.c26
-rw-r--r--lib/memory.h16
-rw-r--r--lib/memtypes.c7
-rw-r--r--lib/misc.h28
-rw-r--r--lib/mqueue.c79
-rw-r--r--lib/mqueue.h101
-rw-r--r--lib/node_type.h82
-rw-r--r--lib/pthread_safe.c3
-rw-r--r--lib/qfstring.c20
-rw-r--r--lib/qiovec.c18
-rw-r--r--lib/qiovec.h3
-rw-r--r--lib/qpath.c6
-rw-r--r--lib/qpnexus.c14
-rw-r--r--lib/qpnexus.h5
-rw-r--r--lib/qpselect.c25
-rw-r--r--lib/qpthreads.h24
-rw-r--r--lib/qstring.c861
-rw-r--r--lib/qstring.h592
-rw-r--r--lib/qtime.c48
-rw-r--r--lib/qtime.h137
-rw-r--r--lib/qtimers.c18
-rw-r--r--lib/qtimers.h16
-rw-r--r--lib/routemap.c2
-rw-r--r--lib/symtab.h2
-rw-r--r--lib/thread.h2
-rw-r--r--lib/uty.h232
-rw-r--r--lib/vector.c10
-rw-r--r--lib/vector.h14
-rw-r--r--lib/version.h.in2
-rw-r--r--lib/vio_fifo.c1823
-rw-r--r--lib/vio_fifo.h196
-rw-r--r--lib/vio_lines.c2
-rw-r--r--lib/vio_lines.h18
-rw-r--r--lib/vty.c1078
-rw-r--r--lib/vty.h152
-rw-r--r--lib/vty_cli.c2542
-rw-r--r--lib/vty_cli.h166
-rw-r--r--lib/vty_io.c1196
-rw-r--r--lib/vty_io.h485
-rw-r--r--lib/vty_io_basic.c387
-rw-r--r--lib/vty_io_basic.h65
-rw-r--r--lib/vty_io_file.c2650
-rw-r--r--lib/vty_io_file.h25
-rw-r--r--lib/vty_io_shell.c5
-rw-r--r--lib/vty_io_term.c987
-rw-r--r--lib/vty_io_term.h15
-rw-r--r--lib/vty_local.h25
-rw-r--r--lib/vty_pipe.c59
-rw-r--r--lib/workqueue.h2
-rw-r--r--lib/zebra.h4
69 files changed, 12109 insertions, 11537 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 39b6b03e..cc8fbd3a 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -14,9 +14,10 @@ libzebra_la_SOURCES = \
zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c privs.c \
sigevent.c pqueue.c jhash.c memtypes.c workqueue.c symtab.c heap.c \
qtime.c qpthreads.c mqueue.c qpselect.c qtimers.c qpnexus.c \
- command_parse.c command_queue.c qlib_init.c pthread_safe.c list_util.c \
- vty_io.c vty_io_file.c vty_io_shell.c vty_io_term.c vty_cli.c \
- vty_io_basic.c keystroke.c qstring.c vio_fifo.c vio_lines.c \
+ command_parse.c command_execute.c command_queue.c vty_command.c \
+ vty_io.c vty_io_file.c vty_io_shell.c vty_io_vsh.c vty_io_term.c vty_cli.c \
+ vty_io_basic.c keystroke.c qstring.c elstring.c vio_fifo.c vio_lines.c \
+ qlib_init.c pthread_safe.c list_util.c \
qiovec.c qfstring.c errno_names.c
BUILT_SOURCES = memtypes.h route_types.h
@@ -26,19 +27,19 @@ libzebra_la_DEPENDENCIES = @LIB_REGEX@
libzebra_la_LIBADD = @LIB_REGEX@
pkginclude_HEADERS = \
- buffer.h checksum.h command.h filter.h getopt.h hash.h \
+ zebra.h zconfig.h buffer.h checksum.h command.h filter.h getopt.h hash.h \
if.h linklist.h log.h \
memory.h network.h prefix.h routemap.h distribute.h sockunion.h \
- str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \
+ str.h stream.h table.h thread.h vector.h version.h vty.h \
plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \
privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
workqueue.h route_types.h symtab.h heap.h \
qtime.h qpthreads.h mqueue.h qpselect.h qtimers.h qpnexus.h \
- command_parse.h command_queue.h qlib_init.h qafi_safi.h \
+ command_parse.h command_queue.h vty_command.h qlib_init.h qafi_safi.h \
confirm.h misc.h vargs.h miyagi.h pthread_safe.h list_util.h \
- tstring.h node_type.h uty.h \
- vty_io.h vty_io_file.h vty_io_shell.h vty_io_term.h vty_cli.h \
- vty_io_basic.h keystroke.h qstring.h vio_fifo.h vio_lines.h \
+ tstring.h command_common.h command_local.h vty_common.h vty_local.h \
+ vty_io.h vty_io_file.h vty_io_shell.h vty_io_vsh.h vty_io_term.h vty_cli.h \
+ vty_io_basic.h keystroke.h qstring.h elstring.h vio_fifo.h vio_lines.h \
qiovec.h qfstring.h errno_names.h \
route_types.h command_execute.h
diff --git a/lib/command.c b/lib/command.c
index 3c4f056a..adea80e4 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -21,86 +21,279 @@ along with GNU Zebra; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
-#include <zebra.h>
+#include "zconfig.h"
+#include "version.h"
+#include "misc.h"
+
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
#include "memory.h"
#include "log.h"
-#include <lib/version.h>
#include "thread.h"
#include "vector.h"
-#include "vty.h"
-#include "uty.h"
#include "qstring.h"
+#include "qtime.h"
+#include "workqueue.h"
#include "command.h"
+#include "command_local.h"
+#include "command_parse.h"
#include "command_execute.h"
-#include "workqueue.h"
#include "command_queue.h"
-#include "command_parse.h"
+#include "vty_local.h"
+#include "vty_command.h"
+#include "vty_io.h"
+
+/* Vector of cmd_node, one for each known node, built during daemon
+ * initialisation.
+ *
+ * Declared extern in command_local.h, so it can get at it.
+ */
+vector node_vector = NULL ;
+
+/*==============================================================================
+ * Default motd string.
+ */
+#define DEFAULT_MOTD \
+"\n" \
+"Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ")\n" \
+ QUAGGA_COPYRIGHT "\n" \
+"\n"
+
+#ifdef QDEBUG
+const char *debug_banner =
+ QUAGGA_PROGNAME " version " QUAGGA_VERSION " QDEBUG=" QDEBUG " "
+ __DATE__ " " __TIME__;
+#endif
+
+/*==============================================================================
+ * Host information structure -- shared across command/vty
+ */
+struct host host =
+{
+ /* Host name of this router. */
+ .name_set = false,
+ .name = NULL, /* set by cmd_init */
+ .name_gen = 0, /* set by cmd_init */
+
+ /* Password for vty interface. */
+ .password = NULL,
+ .password_encrypted = false,
+
+ /* Enable password */
+ .enable = NULL,
+ .enable_encrypted = false,
+
+ /* System wide terminal lines. */
+ .lines = -1, /* unset */
+
+ /* Log filename. */
+ .logfile = NULL,
+
+ /* config file name of this host */
+ .config_file = NULL,
+
+ /* Flags for services */
+ .advanced = false,
+ .encrypt = false,
+
+ /* Banner configuration. */
+ .motd = DEFAULT_MOTD,
+ .motdfile = NULL,
+
+ /* allow VTY to start without password */
+ .no_password_check = false,
+
+ /* if VTY starts without password, use RESTRICTED_NODE */
+ .restricted_mode = false,
+
+ /* Vty timeout value -- see "exec timeout" command */
+ .vty_timeout_val = VTY_TIMEOUT_DEFAULT,
+
+ /* Vty access-class command */
+ .vty_accesslist_name = NULL,
+
+ /* Vty access-class for IPv6. */
+ .vty_ipv6_accesslist_name = NULL,
+
+ /* Current directory -- initialised in vty_init() */
+ .vty_cwd = NULL,
+} ;
+
+/*==============================================================================
+ * host.name handling.
+ *
+ * If the host.name is set explicitly by command then host.name_set is true,
+ * and things are simple.
+ *
+ * Otherwise, need to ask the system. Does that once at start up, and if the
+ * host.name is unset by command -- so there should always be a valid host.name.
+ *
+ * However, it is conceivable that the host name changes (!). So, when asking
+ * for cmd_host_name(), can ask for the system to be asked afresh (if the name
+ * is not explicitly set).
+ *
+ * The VTY watch-dog timer refreshes the host.name on a regular basis,
+ * cmd_check_host_name(), so need not ask for a fresh host.name, unless wish
+ * to guarantee to be absolutely up to date.
+ *
+ * The VTY prompts need the current host name, but that is debounced using the
+ * host.name_gen value. host.name_gen is incremented each time the host.name
+ * actually changes. It is thought unlikely that this will ever wrap round,
+ * but it is guaranteed not to be zero.
+ *
+ * The fact that the host.name can change is reflected in the need to hold
+ * the VTY_LOCK while have the host.name in hand.
+ */
+
+static void cmd_get_sys_host_name(void) ;
+static void cmd_new_host_name(const char* name) ;
+
+/*------------------------------------------------------------------------------
+ * Get the host name: (a) from an explicit host name command
+ * or: (b) from the last time the system was asked.
+ *
+ * Note that the system is asked regularly by the watch dog.
+ *
+ * NB: needs to be VTY_LOCK() *or* not running qpthreads.
+ */
+extern const char*
+cmd_host_name(bool fresh)
+{
+ VTY_ASSERT_LOCKED() ;
-/* Command vector which includes some level of command lists. Normally
- each daemon maintains each own cmdvec. */
-vector cmdvec = NULL;
+ if (!host.name_set && fresh)
+ cmd_get_sys_host_name() ;
+
+ return host.name ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set (or unset) the host name from an explicit host name command.
+ *
+ * If unsets, immediately asks the system for the system host name (which may
+ * be the same !).
+ */
+static cmd_return_code_t
+cmd_set_host_name(const char* name)
+{
+ VTY_LOCK() ;
+
+ host.name_set = (name != NULL) ;
+ if (host.name_set)
+ cmd_new_host_name(name) ;
+ else
+ cmd_get_sys_host_name() ;
+
+ VTY_UNLOCK() ;
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get the host name from the system and set host.name to that.
+ *
+ * NB: result will not be NULL, whatever happens will get at least an empty
+ * '\0' terminated string.
+ *
+ * NB: called early in the morning to initialise host.name and to set
+ * host.name_gen != 0.
+ */
+static void
+cmd_get_sys_host_name(void)
+{
+ struct utsname info ;
-struct desc desc_cr;
-char *command_cr = NULL;
+ VTY_ASSERT_LOCKED() ;
-/* Host information structure. */
-struct host host;
+ uname (&info) ;
+ cmd_new_host_name(info.nodename) ;
+} ;
-/* Store of qstrings, used for parsing. */
-token_vector_t spare_token_strings ;
+/*------------------------------------------------------------------------------
+ * Set host.name to (possibly) new value.
+ *
+ * Does nothing if new name == old name, otherwise increments name_gen.
+ */
+static void
+cmd_new_host_name(const char* name)
+{
+ if ((host.name == NULL) || (strcmp(host.name, name) != 0))
+ {
+ XFREE(MTYPE_HOST, host.name) ;
+ host.name = XSTRDUP(MTYPE_HOST, name) ;
+ do ++host.name_gen ; while (host.name_gen == 0) ;
+ } ;
+} ;
-/* Standard command node structures. */
+/*==============================================================================
+ * node structures for the basic nodes.
+ *
+ * Covers everything up to and including the CONFIG_NODE.
+ */
static struct cmd_node auth_node =
{
- .node = AUTH_NODE,
- "Password: ",
+ .node = AUTH_NODE,
+ .prompt = "Password: ",
+
+ .parent = AUTH_NODE, /* self => no parent */
+ .exit_to = AUTH_NODE, /* self => no exit */
+ .end_to = AUTH_NODE, /* self => no end */
};
static struct cmd_node view_node =
{
- VIEW_NODE,
- "%s> ",
+ .node = VIEW_NODE,
+ .prompt = "%s> ",
+
+ .parent = AUTH_NODE, /* self => no parent */
+ .exit_to = NULL_NODE, /* close ! */
+ .end_to = VIEW_NODE, /* self => no end */
};
static struct cmd_node restricted_node =
{
- RESTRICTED_NODE,
- "%s$ ",
+ .node = RESTRICTED_NODE,
+ .prompt = "%s$ ",
+
+ .parent = AUTH_NODE, /* self => no parent */
+ .exit_to = NULL_NODE, /* close ! */
+ .end_to = RESTRICTED_NODE, /* self => no end */
};
static struct cmd_node auth_enable_node =
{
- AUTH_ENABLE_NODE,
- "Password: ",
+ .node = AUTH_ENABLE_NODE,
+ .prompt = "Password: ",
+
+ .parent = AUTH_ENABLE_NODE, /* self => no parent */
+ .exit_to = NULL_NODE, /* close ! */
+ .end_to = AUTH_ENABLE_NODE, /* self => no end */
};
static struct cmd_node enable_node =
{
- ENABLE_NODE,
- "%s# ",
+ .node = ENABLE_NODE,
+ .prompt = "%s# ",
+
+ .parent = ENABLE_NODE, /* self => no parent */
+ .exit_to = NULL_NODE, /* close ! */
+ .end_to = ENABLE_NODE, /* self => no end */
};
static struct cmd_node config_node =
{
- CONFIG_NODE,
- "%s(config)# ",
- 1
-};
+ .node = CONFIG_NODE,
+ .prompt = "%s(config)# ",
-/* Default motd string. */
-static const char *default_motd =
-"\r\n\
-Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
-" QUAGGA_COPYRIGHT "\r\n\
-\r\n";
+ .parent = CONFIG_NODE, /* self => no parent */
+ .exit_to = ENABLE_NODE, /* exit == end for CONFIG_NODE */
+ .end_to = ENABLE_NODE, /* standard end action */
-#ifdef QDEBUG
-const char *debug_banner =
- QUAGGA_PROGNAME " version " QUAGGA_VERSION " QDEBUG=" QDEBUG " "
- __DATE__ " " __TIME__;
-#endif
+ .config_to_vtysh = true
+};
static const struct facility_map {
int facility;
@@ -132,18 +325,29 @@ static const struct facility_map {
{ 0, NULL, 0 },
};
-static struct cmd_element cmd_pipe =
+#if 0
+static enum cmd_return_code
+cmd_pipe_func(cmd_command self DEFUN_CMD_ARG_UNUSED,
+ struct vty* vty DEFUN_CMD_ARG_UNUSED,
+ int argc DEFUN_CMD_ARG_UNUSED,
+ argv_t argv DEFUN_CMD_ARG_UNUSED)
+{
+ return CMD_SUCCESS ;
+} ;
+
+static struct cmd_command cmd_pipe_element =
{
.string = "< or <|", /* Dummy */
.func = cmd_pipe_func,
.doc = "Pipe input to command processor",
.daemon = 0,
- .strvec = NULL,
+ .items = NULL,
.cmdsize = 0,
.config = NULL,
.subconfig = NULL,
.attr = CMD_ATTR_SIMPLE,
} ;
+#endif
static const char *
@@ -188,9 +392,12 @@ print_version (const char *progname)
}
-/* Utility function to concatenate argv argument into a single string
- with inserting ' ' character between each argument. */
-char *
+/*------------------------------------------------------------------------------
+ * Concatenate argv argument into a single string, inserting ' ' between each
+ * argument.
+ *
+ */
+extern char *
argv_concat (const char* const* argv, int argc, int shift)
{
int i;
@@ -201,8 +408,10 @@ argv_concat (const char* const* argv, int argc, int shift)
len = 0;
for (i = shift; i < argc; i++)
len += strlen(argv[i])+1;
+
if (!len)
return NULL;
+
p = str = XMALLOC(MTYPE_TMP, len);
for (i = shift; i < argc; i++)
{
@@ -220,8 +429,8 @@ argv_concat (const char* const* argv, int argc, int shift)
static int
cmp_node (const void *p, const void *q)
{
- const struct cmd_element *a = *(struct cmd_element * const *)p;
- const struct cmd_element *b = *(struct cmd_element * const *)q;
+ const struct cmd_command *a = *(struct cmd_command * const *)p;
+ const struct cmd_command *b = *(struct cmd_command * const *)q;
return strcmp (a->string, b->string);
}
@@ -237,42 +446,146 @@ cmp_desc (const void *p, const void *q)
#endif //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-/* Install top node of command vector. */
-void
+/*------------------------------------------------------------------------------
+ * Install top node of command vector.
+ *
+ * Initialised as follows:
+ *
+ * .node -- must be set before node is installed
+ * .prompt -- must be set before node is installed
+ * .config_to_vtysh -- must be set true/false before node is installed
+ *
+ * .parent -- may be set, or if 0 (node_null) will be set to default
+ * .exit_to -- may be set, or if 0 (node_null) will be set to default
+ * .end_to -- may be set, or if 0 (node_null) will be set to default
+ *
+ * .config_write -- is set from parameter
+ *
+ * .cmd_vector -- initialised empty
+ *
+ * Default parent node:
+ *
+ * * all nodes >= NODE_CONFIG have NODE_CONFIG as a parent
+ * * all nodes < NODE_CONFIG are their own parents
+ *
+ * Default exit_to:
+ *
+ * * all nodes > NODE_CONFIG exit_to their parent
+ * * node == NODE_CONFIG exit_to ENABLE_NODE (same as end_to !)
+ * * all nodes < NODE_CONFIG exit_to close
+ *
+ * Default end_to:
+ *
+ * * all nodes >= NODE_CONFIG end_to ENABLE_NODE
+ * * all nodes < NODE_CONFIG end_to themselves
+ *
+ */
+extern void
install_node (struct cmd_node *node,
- int (*func) (struct vty *))
+ int (*config_write) (struct vty *))
{
- vector_set_index (cmdvec, node->node, node);
- node->func = func;
- node->cmd_vector = vector_init (0);
-}
+ confirm(NULL_NODE == 0) ; /* unset value for .parent, .end_to & .exit_to */
-/* Compare two command's string. Used in sort_node (). */
-static int
-cmp_node (const struct cmd_element **a, const struct cmd_element **b)
+ if (node->parent == NULL_NODE)
+ {
+ if (node->node >= CONFIG_NODE)
+ node->parent = CONFIG_NODE ;
+ else
+ node->parent = node->node ;
+ } ;
+
+ if (node->end_to == NULL_NODE)
+ {
+ if (node->node >= CONFIG_NODE)
+ node->end_to = ENABLE_NODE ;
+ else
+ node->end_to = node->node ;
+ } ;
+
+ if (node->exit_to == NULL_NODE)
+ {
+ if (node->node > CONFIG_NODE)
+ node->exit_to = node->parent ;
+ else if (node->node == CONFIG_NODE)
+ node->exit_to = ENABLE_NODE ;
+ else
+ node->exit_to = NULL_NODE ;
+ } ;
+
+ node->config_write = config_write ;
+
+ vector_init_new(node->cmd_vector, 0) ; /* embedded */
+
+ vector_set_index (node_vector, node->node, node);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return address of cmd_node -- asserts is not NULL !
+ */
+static cmd_node
+cmd_cmd_node(node_type_t node)
{
- return strcmp ((*a)->string, (*b)->string);
-}
+ cmd_node cn ;
+
+ cn = vector_get_item(node_vector, node) ;
+ if (cn != NULL)
+ return cn ;
+
+ zabort("invalid node") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return parent node
+ */
+extern node_type_t
+cmd_node_parent(node_type_t node)
+{
+ return (cmd_cmd_node(node))->parent ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return exit_to node
+ */
+extern node_type_t
+cmd_node_exit_to(node_type_t node)
+{
+ return (cmd_cmd_node(node))->exit_to ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return parent node
+ */
+extern node_type_t
+cmd_node_end_to(node_type_t node)
+{
+ return (cmd_cmd_node(node))->end_to ;
+} ;
+
+
+/*------------------------------------------------------------------------------
+ * Sorting of all node cmd_vectors.
+ */
+
+/* Compare two command's string. Used in sort_node (). */
static int
-cmp_desc (const struct desc **a, const struct desc **b)
+cmp_node (const struct cmd_command **a, const struct cmd_command **b)
{
- return strcmp ((*a)->cmd, (*b)->cmd);
+ return strcmp ((*a)->string, (*b)->string);
}
/* Sort each node's command element according to command string. */
-void
+extern void
sort_node ()
{
unsigned int i ;
- for (i = 0; i < vector_length(cmdvec); i++)
+ for (i = 0; i < vector_length(node_vector); i++)
{
struct cmd_node *cnode;
vector cmd_vector ;
- unsigned int j;
- cnode = vector_get_item(cmdvec, i) ;
+ cnode = vector_get_item(node_vector, i) ;
if (cnode == NULL)
continue ;
@@ -282,25 +595,10 @@ sort_node ()
continue ;
vector_sort(cmd_vector, (vector_sort_cmp*)cmp_node) ;
-
- for (j = 0; j < vector_length(cmd_vector); j++)
- {
- struct cmd_element *cmd_element ;
- vector descvec ;
-
- cmd_element = vector_get_item (cmd_vector, j);
- if (cmd_element == NULL)
- continue ;
-
- descvec = vector_get_last_item(cmd_element->strvec) ;
- if (descvec == NULL)
- continue ;
-
- vector_sort(descvec, (vector_sort_cmp*)cmp_desc) ;
- } ;
} ;
} ;
+#if 0
/*------------------------------------------------------------------------------
* Take string and break it into tokens -- see cmd_make_tokens().
*
@@ -310,8 +608,11 @@ sort_node ()
extern vector
cmd_make_strvec (const char *string)
{
+#if 0
return cmd_tokenise(NULL, string) ;
#error sort this one out
+#endif
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
@@ -320,204 +621,47 @@ cmd_make_strvec (const char *string)
* Create vector if required.
*/
extern vector
-cmd_add_to_strvec (vector strvec, const char* str)
+cmd_add_to_strvec (vector items, const char* str)
{
- if (strvec == NULL)
- strvec = vector_init(1) ;
+ if (items == NULL)
+ items = vector_init(1) ;
- vector_push_item(strvec, XSTRDUP(MTYPE_STRVEC, str));
+ vector_push_item(items, XSTRDUP(MTYPE_STRVEC, str));
- return strvec ;
+ return items ;
} ;
/*------------------------------------------------------------------------------
* Free allocated string vector (if any) and all its contents.
*
- * Note that this is perfectly happy with strvec == NULL.
+ * Note that this is perfectly happy with items == NULL.
*/
extern void
-cmd_free_strvec (vector strvec)
+cmd_free_strvec (vector items)
{
char *cp;
- /* Note that vector_ream_free() returns NULL if strvec == NULL */
- while((cp = vector_ream(strvec, free_it)) != NULL)
+ /* Note that vector_ream_free() returns NULL if items == NULL */
+ while((cp = vector_ream(items, free_it)) != NULL)
XFREE (MTYPE_STRVEC, cp);
} ;
-/*----------------------------------------------------------------------------*/
-
-/* Fetch next description. Used in cmd_make_descvec(). */
-static char *
-cmd_desc_str (const char **string)
-{
- const char *cp, *start;
- char *token;
- int strlen;
-
- cp = *string;
-
- if (cp == NULL)
- return NULL;
-
- /* Skip white spaces. */
- while (isspace ((int) *cp) && *cp != '\0')
- cp++;
-
- /* Return if there is only white spaces */
- if (*cp == '\0')
- return NULL;
-
- start = cp;
-
- while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
- cp++;
-
- strlen = cp - start;
- token = XMALLOC (MTYPE_STRVEC, strlen + 1);
- memcpy (token, start, strlen);
- *(token + strlen) = '\0';
-
- *string = cp;
-
- return token;
-}
-
-/* New string vector. */
-static vector
-cmd_make_descvec (const char *string, const char *descstr)
-{
- int multiple = 0;
- const char *sp;
- char *token;
- int len;
- const char *cp;
- const char *dp;
- vector allvec;
- vector strvec = NULL;
- struct desc *desc;
-
- cp = string;
- dp = descstr;
-
- if (cp == NULL)
- return NULL;
-
- allvec = vector_init (0);
-
- while (1)
- {
- while (isspace ((int) *cp) && *cp != '\0')
- cp++;
-
- if (*cp == '(')
- {
- multiple = 1;
- cp++;
- }
- if (*cp == ')')
- {
- multiple = 0;
- cp++;
- }
- if (*cp == '|')
- {
- if (! multiple)
- {
- fprintf (stderr, "Command parse error!: %s\n", string);
- exit (1);
- }
- cp++;
- }
-
- while (isspace ((int) *cp) && *cp != '\0')
- cp++;
-
- if (*cp == '(')
- {
- multiple = 1;
- cp++;
- }
-
- if (*cp == '\0')
- return allvec;
-
- sp = cp;
-
- while (! (isspace ((int) *cp) || *cp == ')' || *cp == '|') && *cp != '\0')
- cp++;
-
- len = cp - sp;
-
- token = XMALLOC (MTYPE_STRVEC, len + 1);
- memcpy (token, sp, len);
- *(token + len) = '\0';
-
- desc = XCALLOC (MTYPE_DESC, sizeof (struct desc));
- desc->cmd = token;
- desc->str = cmd_desc_str (&dp);
-
- if (multiple)
- {
- if (multiple == 1)
- {
- strvec = vector_init (0);
- vector_set (allvec, strvec);
- }
- multiple++;
- }
- else
- {
- strvec = vector_init (0);
- vector_set (allvec, strvec);
- }
- vector_set (strvec, desc);
- }
-}
-
-/* Count mandantory string vector size. This is to determine inputed
- command has enough command length. */
-static int
-cmd_cmdsize (vector strvec)
-{
- unsigned int i;
- int size = 0;
-
- for (i = 0; i < vector_length(strvec); i++)
- {
- vector descvec;
-
- descvec = vector_get_item (strvec, i) ;
- if (descvec == NULL)
- continue ;
-
- if (vector_length(descvec) == 1)
- {
- struct desc *desc;
-
- desc = vector_get_item(descvec, 0) ;
- if (desc != NULL)
- if (desc->cmd == NULL || CMD_OPTION (desc->cmd))
- break ;
- }
- size++;
- } ;
-
- return size;
-}
+#endif
-/* Return prompt character of specified node. */
-const char *
-cmd_prompt (enum node_type node)
+/*------------------------------------------------------------------------------
+ * Return prompt string for the specified node.
+ */
+extern const char *
+cmd_prompt(node_type_t node)
{
struct cmd_node *cnode;
- assert(cmdvec != NULL) ;
- assert(cmdvec->p_items != NULL) ;
+ assert(node_vector != NULL) ;
+ assert(node_vector->p_items != NULL) ;
cnode = NULL ;
- if (node < cmdvec->limit)
- cnode = vector_get_item (cmdvec, node);
+ if (node < node_vector->limit)
+ cnode = vector_get_item (node_vector, node);
if (cnode == NULL)
{
@@ -528,17 +672,16 @@ cmd_prompt (enum node_type node)
return cnode->prompt;
}
-/* Install a command into a node. */
-void
-install_element (enum node_type ntype, struct cmd_element *cmd)
+/*------------------------------------------------------------------------------
+ * Install a command into a node.
+ *
+ */
+extern void
+install_element(node_type_t ntype, cmd_command cmd)
{
- struct cmd_node *cnode;
+ cmd_node cnode;
- /* cmd_init hasn't been called */
- if (!cmdvec)
- return;
-
- cnode = vector_get_item (cmdvec, ntype);
+ cnode = vector_get_item (node_vector, ntype);
if (cnode == NULL)
{
@@ -549,65 +692,75 @@ install_element (enum node_type ntype, struct cmd_element *cmd)
vector_set (cnode->cmd_vector, cmd);
- if (cmd->strvec == NULL)
- cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc);
+ /* A cmd_command may appear in a number of cmd_vectors, but the cmd->items
+ * etc. need only be set up once.
+ *
+ * It is assumed that once a cmd_command has been installed it will never be
+ * changed !
+ *
+ * Need now to "compile" the command if not already compiled.
+ */
+ if (cmd->items == NULL)
+ cmd_compile(cmd);
- cmd->cmdsize = cmd_cmdsize (cmd->strvec);
-}
+ /* Post compilation check for reasonable cmd_command ! */
+ cmd_compile_check(cmd) ;
+} ;
+/*==============================================================================
+ * Password encryption
+ */
static const unsigned char itoa64[] =
-"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-static void
-to64(char *s, long v, int n)
-{
- while (--n >= 0)
- {
- *s++ = itoa64[v&0x3f];
- v >>= 6;
- }
-}
-
-static char *
+/* Uses the usual crypt() function.
+ *
+ * Note that crypt() is not thread safe !
+ */
+static const char *
zencrypt (const char *passwd)
{
- char salt[6];
- struct timeval tv;
- char *crypt (const char *, const char *);
+ uint32_t r ;
+ char salt[3];
+
+ extern char *crypt (const char *, const char *) ;
- gettimeofday(&tv,0);
+ r = qt_random(*passwd) ;
- to64(&salt[0], random(), 3);
- to64(&salt[3], tv.tv_usec, 3);
- salt[5] = '\0';
+ salt[0] = itoa64[(r >> (32 - 5)) & 0x3F] ; /* ms 5 */
+ salt[1] = itoa64[(r >> (32 - 10)) & 0x3F] ; /* next ms 5 */
+ salt[2] = '\0';
- return crypt (passwd, salt);
+ return crypt(passwd, salt) ;
}
/* This function write configuration of this host. */
static int
config_write_host (struct vty *vty)
{
+ VTY_LOCK() ;
+
if (qpthreads_enabled)
vty_out (vty, "threaded%s", VTY_NEWLINE);
- if (host.name)
+ if (host.name_set)
vty_out (vty, "hostname %s%s", host.name, VTY_NEWLINE);
- if (host.encrypt)
+ if (host.password != NULL)
{
- if (host.password_encrypt)
- vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE);
- if (host.enable_encrypt)
- vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE);
- }
- else
+ if (host.password_encrypted)
+ vty_out (vty, "password 8 %s\n", host.password);
+ else
+ vty_out (vty, "password %s\n", host.password);
+ } ;
+
+ if (host.enable != NULL)
{
- if (host.password)
- vty_out (vty, "password %s%s", host.password, VTY_NEWLINE);
- if (host.enable)
- vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
- }
+ if (host.enable_encrypted)
+ vty_out (vty, "enable password 8 %s\n", host.enable);
+ else
+ vty_out (vty, "enable password %s\n", host.enable);
+ } ;
if (zlog_get_default_lvl(NULL) != LOG_DEBUG)
{
@@ -671,1674 +824,16 @@ config_write_host (struct vty *vty)
vty_out (vty, "service terminal-length %d%s", host.lines,
VTY_NEWLINE);
- if (host.motdfile)
+ if (host.motdfile)
vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE);
else if (! host.motd)
vty_out (vty, "no banner motd%s", VTY_NEWLINE);
- return 1;
-}
-
-/* Utility function for getting command vector. */
-static vector
-cmd_node_vector (vector v, enum node_type ntype)
-{
- struct cmd_node *cnode = vector_get_item (v, ntype);
- return cnode->cmd_vector;
-}
-
-#if 0
-/* Filter command vector by symbol. This function is not actually used;
- * should it be deleted? */
-static int
-cmd_filter_by_symbol (char *command, char *symbol)
-{
- int i, lim;
-
- if (strcmp (symbol, "IPV4_ADDRESS") == 0)
- {
- i = 0;
- lim = strlen (command);
- while (i < lim)
- {
- if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/'))
- return 1;
- i++;
- }
- return 0;
- }
- if (strcmp (symbol, "STRING") == 0)
- {
- i = 0;
- lim = strlen (command);
- while (i < lim)
- {
- if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-'))
- return 1;
- i++;
- }
- return 0;
- }
- if (strcmp (symbol, "IFNAME") == 0)
- {
- i = 0;
- lim = strlen (command);
- while (i < lim)
- {
- if (! isalnum ((int) command[i]))
- return 1;
- i++;
- }
- return 0;
- }
- return 0;
-}
-#endif
-
-/*==============================================================================
- * Command "filtering".
- *
- * The command parsing process starts with a (shallow) copy of the cmd_vector
- * entry for the current "node".
- *
- * So cmd_v contains pointers to struct cmd_element values. When match fails,
- * the pointer is set NULL -- so parsing is a process of reducing the cmd_v
- * down to just the entries that match.
- *
- * Each cmd_element has a vector "strvec", which contains an entry for each
- * "token" position. That entry is a vector containing the possible values at
- * that position.
- *
- *
- */
-
-/*------------------------------------------------------------------------------
- * Make strict or completion match and return match type flag.
- *
- * Takes: command -- address of candidate token
- * cmd_v -- vector of commands that is being reduced/filtered
- * index -- index of token (position in line -- 0 == first)
- * min_match -- any_match => allow partial matching
- * exact_match => must match completely
- *
- * Returns: any of the enum match_type values:
- *
- * no_match => no match of any kind
- *
- * extend_match => saw an optional token
- * ipv4_prefix_match )
- * ipv4_match )
- * ipv6_prefix_match ) saw full or partial match for this
- * ipv6_match )
- * range_match )
- * vararg_match )
- *
- * partly_match => saw partial match for a keyword
- * exact_match => saw exact match for a keyword
- *
- * Note that these return values are in ascending order of preference. So,
- * if there are multiple possibilities at this position, will return the one
- * furthest down this list.
- */
-static enum match_type
-cmd_filter(const char *command, vector cmd_v, unsigned int index,
- match_type_t min_match)
-{
- unsigned int i;
- unsigned int k;
- enum match_type best_match;
- size_t c_len ;
-
- best_match = no_match ;
- c_len = strlen(command) ;
-
- /* If command and cmd_element string do match, keep in vector */
- k = 0 ;
- for (i = 0; i < vector_length (cmd_v); i++)
- {
- const char *str;
- struct cmd_element *cmd_element;
- vector descvec;
- struct desc *desc;
- unsigned int j;
- bool matched ;
-
- cmd_element = vector_get_item(cmd_v, i) ;
-
- /* Skip past NULL cmd_v entries (just in case) */
- if (cmd_element == NULL)
- continue ;
-
- /* Discard cmd_v entry that has no token at the current position */
- descvec = vector_get_item(cmd_element->strvec, index) ;
- if (descvec == NULL)
- continue ;
-
- /* See if get any sort of match at current position */
- matched = 0 ;
- for (j = 0; j < vector_length (descvec); j++)
- {
- desc = vector_get_item(descvec, j) ;
- if (desc == NULL)
- continue ;
-
- str = desc->cmd;
-
- if (CMD_VARARG (str))
- {
- if (best_match < vararg_match)
- best_match = vararg_match;
- matched = true ;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (best_match < range_match)
- best_match = range_match;
- matched = true ;
- }
- }
-#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command) >= min_match)
- {
- if (best_match < ipv6_match)
- best_match = ipv6_match;
- matched = true ;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command) >= min_match)
- {
- if (best_match < ipv6_prefix_match)
- best_match = ipv6_prefix_match;
- matched = true ;
- }
- }
-#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command) >= min_match)
- {
- if (best_match < ipv4_match)
- best_match = ipv4_match;
- matched = true ;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command) >= min_match)
- {
- if (best_match < ipv4_prefix_match)
- best_match = ipv4_prefix_match;
- matched = true ;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (best_match < extend_match)
- best_match = extend_match;
- matched = true ;
- }
- else
- {
- if (strcmp (command, str) == 0)
- {
- best_match = exact_match ;
- matched = true ;
- }
- else if (min_match <= partly_match)
- {
- if (strncmp (command, str, c_len) == 0)
- {
- if (best_match < partly_match)
- best_match = partly_match ;
- matched = true ;
- } ;
- } ;
- } ;
- } ;
-
- /* Keep cmd_element if have a match */
- if (matched)
- vector_set_item(cmd_v, k++, cmd_element) ;
- } ;
-
- vector_set_length(cmd_v, k) ; /* discard what did not keep */
-
- return best_match;
-} ;
-
-/*------------------------------------------------------------------------------
- * Check for ambiguous match
- *
- * Given the best that cmd_filter_by_completion() or cmd_filter_by_string()
- * found, check as follows:
- *
- * 1. discard all commands for which do not have the type of match selected.
- *
- * See above for the ranking of matches.
- *
- * 2. for "partial match", look out for matching more than one keyword, and
- * return 1 if finds that.
- *
- * 3. for "range match", look out for matching more than one range, and
- * return 1 if finds that.
- *
- * 4. for ipv4_prefix_match and ipv6_prefix_match, if get a "partial match",
- * return 2.
- *
- * This appears to catch things which are supposed to be prefixes, but
- * do not have a '/' or do not have any digits after the '/'.
- *
- * Takes: command -- address of candidate token
- * cmd_v -- vector of commands that is being reduced/filtered
- * index -- index of token (position in line -- 0 == first)
- * type -- as returned by cmd_filter_by_completion()
- * or cmd_filter_by_string()
- *
- * Returns: 0 => not ambiguous
- * 1 => ambiguous -- the candidate token matches more than one
- * keyword, or the candidate number matches more
- * than one number range.
- * 2 => partial match for ipv4_prefix or ipv6_prefix
- * (missing '/' or no digits after '/').
- *
- * NB: it is assumed that cannot find both 1 and 2 states. But in any case,
- * returns 1 in preference.
- */
-static int
-is_cmd_ambiguous (const char *command, vector cmd_v, int index,
- enum match_type type)
-{
- unsigned int i;
- unsigned int k;
- int ret ;
-
- ret = 0 ; /* all's well so far */
- k = 0 ; /* nothing kept, yet */
-
- for (i = 0; i < vector_length (cmd_v); i++)
- {
- unsigned int j;
- struct cmd_element *cmd_element;
- const char *str_matched ;
- vector descvec;
- struct desc *desc;
- bool matched ;
- enum match_type mt ;
-
- cmd_element = vector_get_item (cmd_v, i) ;
-
- /* Skip past NULL cmd_v entries (just in case) */
- if (cmd_element == NULL)
- continue ;
-
- /* The cmd_v entry MUST have a token at the current position */
- descvec = vector_get_item (cmd_element->strvec, index) ;
- assert(descvec != NULL) ;
-
- /* See if have a match against any of the current possibilities
- *
- * str_matched is set the first time get a partial string match,
- * or the first time get a number range match.
- *
- * If get a second partial string match or number range match, then
- * unless
- */
- str_matched = NULL ;
- matched = 0;
- for (j = 0; j < vector_length (descvec); j++)
- {
- enum match_type ret;
- const char *str ;
-
- desc = vector_get_item (descvec, j) ;
- if (desc == NULL)
- continue ;
-
- str = desc->cmd;
-
- switch (type)
- {
- case exact_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
- && strcmp (command, str) == 0)
- matched = true ;
- break;
-
- case partly_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
- && strncmp (command, str, strlen (command)) == 0)
- {
- if (str_matched && (strcmp (str_matched, str) != 0))
- ret = 1; /* There is ambiguous match. */
- else
- str_matched = str;
- matched = true ;
- }
- break;
-
- case range_match:
- if (cmd_range_match (str, command))
- {
- if (str_matched && strcmp (str_matched, str) != 0)
- ret = 1;
- else
- str_matched = str;
- matched = true ;
- }
- break;
-
-#ifdef HAVE_IPV6
- case ipv6_match:
- if (CMD_IPV6 (str))
- matched = true ;
- break;
-
- case ipv6_prefix_match:
- if ((mt = cmd_ipv6_prefix_match (command)) != no_match)
- {
- if ((mt == partly_match) && (ret != 1))
- ret = 2; /* There is incomplete match. */
- matched = true ;
- }
- break;
-#endif /* HAVE_IPV6 */
-
- case ipv4_match:
- if (CMD_IPV4 (str))
- matched = true ;
- break;
-
- case ipv4_prefix_match:
- if ((mt = cmd_ipv4_prefix_match (command)) != no_match)
- {
- if ((mt == partly_match) && (ret != 1))
- ret = 2; /* There is incomplete match. */
- matched = true ;
- }
- break;
-
- case extend_match:
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
- matched = true ;
- break;
-
- case no_match:
- default:
- break;
- } ;
- } ;
-
- /* Keep cmd_element if have a match */
- if (matched)
- vector_set_item(cmd_v, k++, cmd_element) ;
- } ;
-
- vector_set_length(cmd_v, k) ; /* discard what did not keep */
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * If src matches dst return dst string, otherwise return NULL
- *
- * Returns NULL if dst is an option, variable of vararg.
- *
- * NULL or empty src are deemed to match.
- */
-static const char *
-cmd_entry_function (const char *src, const char *dst)
-{
- if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst))
- return NULL;
-
- if ((src == NULL) || (*src == '\0'))
- return dst;
-
- if (strncmp (src, dst, strlen (src)) == 0)
- return dst;
-
- return NULL;
-}
-
-/* If src matches dst return dst string, otherwise return NULL */
-/* This version will return the dst string always if it is
- CMD_VARIABLE for '?' key processing */
-static const char *
-cmd_entry_function_desc (const char *src, const char *dst)
-{
- if (CMD_VARARG (dst))
- return dst;
-
- if (CMD_RANGE (dst))
- {
- if (cmd_range_match (dst, src))
- return dst;
- else
- return NULL;
- }
-
-#ifdef HAVE_IPV6
- if (CMD_IPV6 (dst))
- {
- if (cmd_ipv6_match (src))
- return dst;
- else
- return NULL;
- }
-
- if (CMD_IPV6_PREFIX (dst))
- {
- if (cmd_ipv6_prefix_match (src))
- return dst;
- else
- return NULL;
- }
-#endif /* HAVE_IPV6 */
-
- if (CMD_IPV4 (dst))
- {
- if (cmd_ipv4_match (src))
- return dst;
- else
- return NULL;
- }
-
- if (CMD_IPV4_PREFIX (dst))
- {
- if (cmd_ipv4_prefix_match (src))
- return dst;
- else
- return NULL;
- }
-
- /* Optional or variable commands always match on '?' */
- if (CMD_OPTION (dst) || CMD_VARIABLE (dst))
- return dst;
-
- /* In case of 'command \t', given src is NULL string. */
- if (src == NULL)
- return dst;
-
- if (strncmp (src, dst, strlen (src)) == 0)
- return dst;
- else
- return NULL;
-}
-
-/*------------------------------------------------------------------------------
- * Check same string element existence.
- *
- * Returns: 0 => found same string in the vector
- * 1 => NOT found same string in the vector
- */
-static bool
-cmd_unique_string (vector v, const char *str)
-{
- unsigned int i;
- char *match;
+ VTY_UNLOCK() ;
- for (i = 0; i < vector_length (v); i++)
- if ((match = vector_get_item (v, i)) != NULL)
- if (strcmp (match, str) == 0)
- return 0;
return 1;
}
-/* Compare string to description vector. If there is same string
- return 1 else return 0. */
-static bool
-desc_unique_string (vector v, const char *str)
-{
- unsigned int i;
- struct desc *desc;
-
- for (i = 0; i < vector_length (v); i++)
- if ((desc = vector_get_item (v, i)) != NULL)
- if (strcmp (desc->cmd, str) == 0)
- return 1;
- return 0;
-}
-
-/*------------------------------------------------------------------------------
- * Special parsing for leading 'do', if current mode allows it.
- *
- * If finds a valid "do", sets current node and do_shortcut flag, and discards
- * the "do" token.
- *
- * Returns: true <=> dealt with the "do"
- * false => no do, or no do allowed.
- */
-static bool
-cmd_try_do_shortcut(cmd_parsed parsed)
-{
- const char* ts ;
-
- if (parsed->cnode < MIN_DO_SHORTCUT_NODE)
- return false ;
-
- ts = cmd_token_string(cmd_token_get(&parsed->tokens, 0)) ;
- if (strcmp("do", ts) != 0)
- return false ;
-
- parsed->cnode = ENABLE_NODE ;
- parsed->do_shortcut = true ;
- cmd_token_discard(cmd_token_shift(&parsed->tokens)) ;
-
- return true ;
-} ;
-
-/*==============================================================================
- * '?' describe command support.
- */
-
-/*------------------------------------------------------------------------------
- * Get description of current (partial) command
- *
- * Returns: NULL => no description available
- *
- * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS
- *
- * or: address of vector of "struct desc" values available.
- *
- * NB: when a vector is returned it is the caller's responsibility to
- * vector_free() it. (The contents are all effectively const, so do not
- * themselves need to be freed.)
- */
-extern vector
-cmd_describe_command (const char* line, node_type_t node,
- cmd_return_code_t* status)
-{
- vector ret ;
- struct cmd_parsed parsed_s ;
- cmd_parsed parsed ;
- cmd_token_type_t tok_total ;
-
- /* Set up a parser object and tokenise the command line */
- parsed = cmd_parse_init_new(&parsed_s) ;
- tok_total = cmd_tokenise(parsed, line, node) ;
-
-
-
-
-
-
-
- /* Stop immediately if line is empty apart from comment */
- if ((tok_total & ~cmd_tok_comment) == cmd_tok_null)
- return CMD_EMPTY ; /* NB: parsed->cmd == NULL */
-
- /* Level 1 parsing
- *
- * Strip quotes and escapes from all the tokens.
- */
- if (tok_total != cmd_tok_simple)
- {
- ret = cmd_parse_phase_one(parsed) ;
- if (ret != CMD_SUCCESS)
- return ret ;
- } ;
-
- /* If allowed to 'do', see if there.
- *
- * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
- */
- if (type & cmd_parse_do)
- cmd_try_do_shortcut(parsed) ;
-
-
-
-
- return cmd_describe_command_real (tokens, node, status);
-
-
-
- static vector
- cmd_describe_command_real (vector tokens, int node, int *status)
- {
- unsigned int i;
- vector cmd_vector;
- #define INIT_MATCHVEC_SIZE 10
- vector matchvec;
- struct cmd_element *cmd_element;
- unsigned int index;
- int ret;
- enum match_type match;
- char *command;
-
- /* Set index. */
- if (vector_length (tokens) == 0)
- {
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
- else
- index = vector_length (tokens) - 1;
-
- /* Make copy vector of current node's command vector. */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
-
- /* Prepare match vector */
- matchvec = vector_init (INIT_MATCHVEC_SIZE);
-
- /* Filter commands. */
- /* Only words precedes current word will be checked in this loop. */
- for (i = 0; i < index; i++)
- if ((command = vector_get_item (tokens, i)))
- {
- match = cmd_filter(command, cmd_vector, i, any_match) ;
-
- if (match == vararg_match)
- {
- struct cmd_element *cmd_element;
- vector descvec;
- unsigned int j, k;
-
- for (j = 0; j < vector_length (cmd_vector); j++)
- if ((cmd_element = vector_get_item (cmd_vector, j)) != NULL
- && (vector_length (cmd_element->strvec)))
- {
- descvec = vector_get_item (cmd_element->strvec,
- vector_length (cmd_element->strvec) - 1);
- for (k = 0; k < vector_length (descvec); k++)
- {
- struct desc *desc = vector_get_item (descvec, k);
- vector_set (matchvec, desc);
- }
- }
-
- vector_set (matchvec, &desc_cr);
- vector_free (cmd_vector);
-
- return matchvec;
- } ;
-
- ret = is_cmd_ambiguous (command, cmd_vector, i, match) ;
- if (ret != 0)
- {
- vector_free (cmd_vector);
- vector_free (matchvec);
- *status = (ret == 1) ? CMD_ERR_AMBIGUOUS
- : CMD_ERR_NO_MATCH ;
- return NULL ;
- } ;
- }
-
- /* Prepare match vector */
- /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
-
- /* Make sure that cmd_vector is filtered based on current word */
- command = vector_get_item (tokens, index);
- if (command)
- match = cmd_filter(command, cmd_vector, index, any_match);
-
- /* Make description vector. */
- for (i = 0; i < vector_length (cmd_vector); i++)
- {
- vector strvec ;
-
- cmd_element = vector_get_item (cmd_vector, i) ;
- if (cmd_element == NULL)
- continue ;
-
- /* Ignore cmd_element if no tokens at index position.
- *
- * Deal with special case of possible <cr> completion.
- */
- strvec = cmd_element->strvec;
- if (index >= vector_length (strvec))
- {
- if (command == NULL && index == vector_length (strvec))
- {
- if (!desc_unique_string (matchvec, command_cr))
- vector_push_item(matchvec, &desc_cr);
- }
- continue ;
- } ;
-
- /* Check if command is completed. */
- unsigned int j;
- vector descvec = vector_get_item (strvec, index);
- struct desc *desc;
-
- for (j = 0; j < vector_length (descvec); j++)
- if ((desc = vector_get_item (descvec, j)))
- {
- const char *string;
-
- string = cmd_entry_function_desc (command, desc->cmd);
- if (string)
- {
- /* Uniqueness check */
- if (!desc_unique_string (matchvec, string))
- vector_push_item(matchvec, desc);
- }
- } ;
- } ;
-
- vector_free (cmd_vector);
-
- if (vector_length(matchvec) == 0)
- {
- vector_free (matchvec);
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
-
- *status = CMD_SUCCESS;
- return matchvec;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- cmd_parse_reset(parsed, false) ;
-
- return
-} ;
-
-/*------------------------------------------------------------------------------
- * Check LCD of matched command.
- *
- * Scan list of matched keywords, and by comparing them pair-wise, find the
- * longest common leading substring.
- *
- * Returns: 0 if zero or one matched keywords
- * length of longest common leading substring, otherwise.
- */
-static int
-cmd_lcd (vector matchvec)
-{
- int n ;
- int i ;
- int lcd ;
- char *sp, *sq, *ss ;
-
- n = vector_end(matchvec) ;
- if (n < 2)
- return 0 ;
-
- ss = vector_get_item(matchvec, 0) ;
- lcd = strlen(ss) ;
-
- for (i = 1 ; i < n ; i++)
- {
- sq = ss ;
- ss = vector_get_item(matchvec, i) ;
- sp = ss ;
-
- while ((*sp == *sq) && (*sp != '\0'))
- {
- ++sp ;
- ++sq ;
- } ;
-
- if (lcd > (sp - ss))
- lcd = (sp - ss) ;
- }
- return lcd;
-}
-
-/*------------------------------------------------------------------------------
- * Command line completion support.
- */
-static vector
-cmd_complete_command_real (vector tokens, int node, int *status)
-{
- unsigned int i;
- unsigned int ivl ;
- unsigned int last_ivl ;
- vector cmd_v ;
-#define INIT_MATCHVEC_SIZE 10
- vector matchvec;
- struct cmd_element *cmd_element;
- unsigned int index;
- struct desc *desc;
- vector descvec;
- char *token;
- int n ;
-
- /* Stop immediately if the tokens is empty. */
- if (vector_length (tokens) == 0)
- {
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
-
- /* Take (shallow) copy of cmdvec for given node. */
- cmd_v = vector_copy (cmd_node_vector (cmdvec, node));
-
- /* First, filter upto, but excluding last token */
- last_ivl = vector_length (tokens) - 1;
-
- for (ivl = 0; ivl < last_ivl; ivl++)
- {
- enum match_type match;
- int ret;
-
- /* TODO: does this test make any sense ? */
- if ((token = vector_get_item (tokens, ivl)) == NULL)
- continue ;
-
- /* First try completion match, return best kind of match */
- index = ivl ;
- match = cmd_filter_by_completion (token, cmd_v, index) ;
-
- /* Eliminate all but the selected kind of match */
- ret = is_cmd_ambiguous (token, cmd_v, index, match) ;
-
- if (ret == 1)
- {
- /* ret == 1 => either token matches more than one keyword
- * or token matches more than one number range
- */
- vector_free (cmd_v);
- *status = CMD_ERR_AMBIGUOUS;
- return NULL;
- }
-#if 0
- /* For command completion purposes do not appear to care about
- * incomplete ipv4 or ipv6 prefixes (missing '/' or digits after).
- */
- else if (ret == 2)
- {
- vector_free (cmd_v);
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
-#endif
- }
-
- /* Prepare match vector. */
- matchvec = vector_init (INIT_MATCHVEC_SIZE);
-
- /* Now we got into completion */
- index = last_ivl ;
- token = vector_get_item(tokens, last_ivl) ; /* is now the last token */
-
- for (i = 0; i < vector_length (cmd_v); i++)
- {
- unsigned int j;
- const char *string;
-
- if ((cmd_element = vector_get_item (cmd_v, i)) == NULL)
- continue ;
-
- descvec = vector_get_item (cmd_element->strvec, index);
- if (descvec == NULL)
- continue ;
-
- for (j = 0; j < vector_length (descvec); j++)
- {
- desc = vector_get_item (descvec, j) ;
- if (desc == NULL)
- continue ;
-
- string = cmd_entry_function(token, desc->cmd) ;
- if ((string != NULL) && cmd_unique_string(matchvec, string))
- cmd_add_to_strvec (matchvec, string) ;
- } ;
- } ;
-
- n = vector_length(matchvec) ; /* number of entries in the matchvec */
-
- /* We don't need cmd_v any more. */
- vector_free (cmd_v);
-
- /* No matched command */
- if (n == 0)
- {
- vector_free (matchvec);
-
- /* In case of 'command \t' pattern. Do you need '?' command at
- the end of the line. */
- if (*token == '\0')
- *status = CMD_COMPLETE_ALREADY;
- else
- *status = CMD_ERR_NO_MATCH;
- return NULL;
- }
-
- /* Only one matched */
- if (n == 1)
- {
- *status = CMD_COMPLETE_FULL_MATCH;
- return matchvec ;
- }
-
- /* Check LCD of matched strings. */
- if (token != NULL)
- {
- unsigned lcd = cmd_lcd (matchvec) ;
-
- if (lcd != 0)
- {
- if (strlen(token) < lcd)
- {
- char *lcdstr;
-
- lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1);
- memcpy (lcdstr, vector_get_item(matchvec, 0), lcd) ;
- lcdstr[lcd] = '\0';
-
- cmd_free_strvec(matchvec) ; /* discard the match vector */
-
- matchvec = vector_init (1);
- vector_push_item(matchvec, lcdstr) ;
-
- *status = CMD_COMPLETE_MATCH;
- return matchvec ;
- }
- }
- }
-
- *status = CMD_COMPLETE_LIST_MATCH;
- return matchvec ;
-}
-
-/*------------------------------------------------------------------------------
- * Can the current command be completed ?
- */
-extern vector
-cmd_complete_command (vector tokens, int node, int *status)
-{
- vector ret;
-
- if ( cmd_try_do_shortcut(node, vector_get_item(tokens, 0) ) )
- {
- vector shifted_tokens;
- unsigned int index;
-
- /* We can try it on enable node, cos' the vty is authenticated */
-
- shifted_tokens = vector_init (vector_count(tokens));
- /* use memcpy? */
- for (index = 1; index < vector_length (tokens); index++)
- {
- vector_set_index (shifted_tokens, index-1,
- vector_lookup(tokens, index)) ;
- }
-
- ret = cmd_complete_command_real (shifted_tokens, ENABLE_NODE, status);
-
- vector_free(shifted_tokens);
- return ret;
- }
-
- return cmd_complete_command_real (tokens, node, status);
-}
-
-/*------------------------------------------------------------------------------
- * Return parent node
- *
- * All nodes > CONFIG_NODE are descended from CONFIG_NODE
- */
-enum node_type
-node_parent ( enum node_type node )
-{
- assert (node > CONFIG_NODE);
-
- switch (node)
- {
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- return BGP_NODE;
-
- case KEYCHAIN_KEY_NODE:
- return KEYCHAIN_NODE;
-
- default:
- return CONFIG_NODE;
- } ;
-} ;
-
-/*==============================================================================
- * Parsing of command lines
- */
-
-/*------------------------------------------------------------------------------
- * Parse a command in the given "node", if possible, ready for execution.
- *
- * If 'strict': use cmd_filter_by_string()
- * otherwise: use cmd_filter_by_completion()
- *
- * If 'do': see if there is a 'do' at the front and proceed accordingly.
- *
- * If 'tree': move up the node tree to find command if not found in the
- * current node.
- */
-
-static enum cmd_return_code cmd_parse_phase_one(cmd_parsed parsed) ;
-static enum cmd_return_code cmd_parse_phase_two(struct cmd_parsed* parsed,
- bool strict) ;
-
-/*------------------------------------------------------------------------------
- * Parse a command in the given "node", or (if required) any of its ancestors.
- *
- * Returns: CMD_SUCCESS => successfully parsed command, and the result is
- * in the given parsed structure, ready for execution.
- *
- * NB: parsed->cnode may have changed.
- *
- * NB: parsed->cmd->daemon => daemon
- *
- * CMD_EMPTY => empty or comment line
- *
- * NB: parsed->cmd == NULL
- *
- * CMD_SUCCESS_DAEMON => parsed successfully. Something for vtysh ??
- *
- * CMD_ERR_NO_MATCH )
- * CMD_ERR_AMBIGUOUS ) failed to parse
- * CMD_ERR_INCOMPLETE )
- *
- * NB: if has failed to parse in the current node
- * and in any ancestor nodes, returns the error
- * from the attempt to parse in the current node
- * (parsed->cnode which is returned unchanged).
- *
- * NB: the command line MUST be preserved until the parsed command is no
- * longer required -- no copy is made.
- *
- * NB: expects to have free run of everything in the vty structure (except
- * the contents of the vty_io sub-structure) until the command completes.
- *
- * See elsewhere for description of parsed structure.
- */
-extern enum cmd_return_code
-cmd_parse_command(struct vty* vty, cmd_parse_type_t type)
-{
- enum cmd_return_code ret ;
- enum cmd_return_code first_ret ;
- cmd_parsed parsed ;
- cmd_token_type_t tok_total ;
- bool varflag ;
- unsigned int i, ivl ;
-
- /* Initialise the parsed structure -- assuming no 'do' */
- if (vty->parsed == NULL)
- parsed = vty->parsed = cmd_parse_init_new(NULL) ;
- else
- {
- parsed = vty->parsed ;
-
- parsed->cmd = NULL ;
- parsed->do_shortcut = false ;
-
- if (parsed->pipes != cmd_pipe_none)
- {
- parsed->pipes = cmd_pipe_none ;
- cmd_empty_parsed_tokens(parsed) ;
- } ;
- } ;
-
- /* Parse the line into tokens, set parsed->line, ->cnode & ->onode */
- tok_total = cmd_tokenise(parsed, vty->buf, vty->node) ;
-
- /* Stop immediately if line is empty apart from comment */
- if ((tok_total & ~cmd_tok_comment) == cmd_tok_null)
- return CMD_EMPTY ; /* NB: parsed->cmd == NULL */
-
- /* Level 1 parsing
- *
- * Strip quotes and escapes from all the tokens.
- */
- if (tok_total != cmd_tok_simple)
- {
- ret = cmd_parse_phase_one(parsed) ;
- if (ret != CMD_SUCCESS)
- return ret ;
- } ;
-
- /* If allowed to 'do', see if there.
- *
- * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
- */
- if (type & cmd_parse_do)
- cmd_try_do_shortcut(parsed) ;
-
- /* Level 2 parsing
- * Try in the current node
- */
- ret = cmd_parse_phase_two(parsed, type) ;
-
- if (ret != CMD_SUCCESS)
- {
- if (((type & cmd_parse_tree) == 0) || parsed->do_shortcut)
- return ret ; /* done if not allowed to walk tree
- or just tried to parse a 'do' */
-
- /* Try in parent node(s) */
- first_ret = ret ;
-
- while (ret != CMD_SUCCESS)
- {
- if (parsed->cnode <= CONFIG_NODE)
- {
- parsed->cnode = parsed->onode ; /* restore node state */
- return first_ret ; /* return original result */
- } ;
-
- parsed->cnode = node_parent(parsed->cnode) ;
- ret = cmd_parse_phase_two(parsed, type) ;
- } ;
- } ;
-
- /* Parsed successfully -- construct the arg_vector */
-
- varflag = false ;
- ivl = vector_length(parsed->cmd->strvec) ;
-
- cmd_arg_vector_empty(parsed) ;
- for (i = 0; i < ivl ; i++)
- {
- bool take = varflag ;
-
- if (!varflag)
- {
- vector descvec = vector_get_item (parsed->cmd->strvec, i);
-
- if (vector_length (descvec) == 1)
- {
- struct desc *desc = vector_get_item (descvec, 0);
-
- if (CMD_VARARG (desc->cmd))
- take = varflag = true ;
- else
- take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ;
- }
- else
- take = true ;
- }
-
- if (take)
- cmd_arg_vector_push(parsed,
- cmd_token_value(cmd_token_get(&parsed->tokens, i))) ;
- } ;
-
- /* Return appropriate form of success */
- return parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
- : CMD_SUCCESS ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Phase 1 of command parsing
- *
- * * At start of line look for:
- *
- * * '<' -- pipe-in command of some sort
- *
- * Sets the pipe type and the read_pipe_tokens -- all tokens up to
- * '>', '|', '!' or '#'..
- *
- * Scan for '>', '|', '!' or '#'
- *
- * * '>' or '|' -- pipe-out command of some sort
- *
- * Collect type of pipe, and then all tokens up to '!' or '#'
- *
- *
- *
- * * '!', '#', comment -- discards
- *
- * Returns: CMD_SUCCESS -- parsed successfully
- * CMD_ERR_NO_MATCH )
- * CMD_ERR_AMBIGUOUS ) failed to parse
- * CMD_ERR_INCOMPLETE )
- */
-static enum cmd_return_code
-cmd_parse_phase_one(cmd_parsed parsed)
-{
-
-
-
-
-
-
-} ;
-
-/*------------------------------------------------------------------------------
- * Phase 2 of command parsing
- *
- * Takes a parsed structure, with the:
- *
- * cnode -- node to parse in
- * tokens -- the line broken into words
- * do_shortcut -- true if first word is 'do' (to be ignored)
- *
- * and parses either strictly or with command completion.
- *
- * Returns: CMD_SUCCESS -- parsed successfully
- * CMD_ERR_NO_MATCH )
- * CMD_ERR_AMBIGUOUS ) failed to parse
- * CMD_ERR_INCOMPLETE )
- */
-static enum cmd_return_code
-cmd_parse_phase_two(cmd_parsed parsed, cmd_parse_type_t type)
-{
- unsigned int i ;
- unsigned int ivl ;
- unsigned index ;
- vector cmd_v;
- struct cmd_element *cmd_element;
- struct cmd_element *matched_element;
- unsigned int matched_count, incomplete_count;
- enum match_type match ;
- enum match_type filter_level ;
- const char *command;
-
- /* Need number of tokens */
- ivl = cmd_token_count(&parsed->tokens) ;
-
- /* Make copy of command elements. */
- cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode));
-
- /* Look for an unambiguous result */
- filter_level = (type & cmd_parse_strict) ? exact_match : any_match ;
- match = no_match ; /* in case of emptiness */
- for (index = 0 ; index < ivl; index++)
- {
- int ret ;
-
- command = cmd_token_string(cmd_token_get(&parsed->tokens, index)) ;
-
- match = cmd_filter(command, cmd_v, index, filter_level) ;
-
- if (match == vararg_match)
- break;
-
- ret = is_cmd_ambiguous (command, cmd_v, index, match);
-
- if (ret != 0)
- {
- assert((ret == 1) || (ret == 2)) ;
- vector_free (cmd_v);
- return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ;
- }
- } ;
-
- /* Check matched count. */
- matched_element = NULL;
- matched_count = 0;
- incomplete_count = 0;
-
- for (i = 0; i < vector_length(cmd_v); i++)
- {
- cmd_element = vector_get_item(cmd_v, i) ;
- if (cmd_element == NULL)
- continue ;
-
- if (match == vararg_match || index >= cmd_element->cmdsize)
- {
- matched_element = cmd_element;
-#if 0
- printf ("DEBUG: %s\n", cmd_element->string);
-#endif
- matched_count++;
- }
- else
- {
- incomplete_count++;
- }
- } ;
-
- /* Finished with cmd_v. */
- vector_free (cmd_v);
-
- /* To execute command, matched_count must be 1. */
- if (matched_count != 1)
- {
- if (matched_count == 0)
- return (incomplete_count) ? CMD_ERR_INCOMPLETE : CMD_ERR_NO_MATCH ;
- else
- return CMD_ERR_AMBIGUOUS ;
- } ;
-
- /* Everything checks out... ready to execute command */
- parsed->cmd = matched_element ;
-
- return CMD_SUCCESS ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Dispatch a parsed command.
- *
- * Returns: command return code. NB: may be CMD_QUEUED (unless no_queue).
- *
- * NB: expects to have free run of everything in the vty structure (except
- * the contents of the vty_io sub-structure) until the command completes.
- */
-extern enum cmd_return_code
-cmd_dispatch(struct vty* vty, bool no_queue)
-{
- cmd_parsed parsed = vty->parsed ;
- enum cmd_return_code ret ;
-
- if (parsed->cmd == NULL)
- return CMD_SUCCESS ; /* NULL commands are easy */
-
- vty->node = parsed->cnode ;
-
- if (no_queue || !vty_cli_nexus)
- {
- ret = cmd_dispatch_call(vty) ;
- cmd_post_command(vty, ret) ;
- }
- else
- {
- /* Don't do it now, but send to bgp qpthread */
- if (parsed->cmd->attr & CMD_ATTR_CALL)
- cq_enqueue(vty, vty_cli_nexus) ;
- else
- cq_enqueue(vty, vty_cmd_nexus) ;
-
- ret = CMD_QUEUED ;
- } ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Tidy up after executing command.
- *
- * This is separated out so that can be called when queued command completes.
- *
- * If have just processed a "do" shortcut command, and it has not set the
- * vty->node to something other than ENABLE_NODE, then restore to the original
- * state.
- *
- * Arguments: ret = CMD_XXXX -- NB: CMD_QUEUED => command revoked
- */
-extern void
-cmd_post_command(struct vty* vty, int ret)
-{
- if (vty->parsed->do_shortcut)
- {
- if (vty->node == ENABLE_NODE)
- vty->node = vty->parsed->onode ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Parse and execute a command.
- *
- * The command is given by vty->buf and vty->node.
- *
- * Uses vty->parsed.
- *
- * -- use strict/completion parsing, as required.
- *
- * -- parse in current node and in ancestors, as required
- *
- * If does not find in any ancestor, return error from current node.
- *
- * -- implement the "do" shortcut, as required
- *
- * If qpthreads_enabled, then may queue the command rather than execute it
- * here.
- *
- * The vty->node may be changed during the execution of the command, and may
- * be returned changed once the command has completed.
- *
- * NB: expects to have free run of everything in the vty structure (except
- * the contents of the vty_io sub-structure) until the command completes.
- */
-extern enum cmd_return_code
-cmd_execute_command(struct vty *vty,
- enum cmd_parse_type type, struct cmd_element **cmd)
-{
- enum cmd_return_code ret ;
-
- /* Try to parse in vty->node or, if required, ancestors thereof. */
- ret = cmd_parse_command(vty, type) ;
-
- if (cmd != NULL)
- *cmd = vty->parsed->cmd ; /* for vtysh */
-
- if (ret == CMD_SUCCESS)
- ret = cmd_dispatch(vty, cmd_may_queue) ;
- else if (ret == CMD_EMPTY)
- ret = CMD_SUCCESS ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Read configuration from file.
- *
- * In the qpthreads world this assumes that it is running with the vty
- * locked, and that all commands are to be executed directly.
- *
- * If the 'first_cmd' argument is not NULL it is the address of the first
- * command that is expected to appear. If the first command is not this, then
- * the 'first_cmd' is called with argv == NULL (and argc == 0) to signal the
- * command is being invoked by default.
- *
- * Command processing continues while CMD_SUCCESS is returned by the command
- * parser and command execution.
- *
- * If 'ignore_warning' is set, then any CMD_WARNING returned by command
- * execution is converted to CMD_SUCCESS. Note that any CMD_WARNING returned
- * by command parsing (or in execution of any default 'first_cmd').
- *
- * Returns: cmd_return_code for last command
- * vty->buf is last line processed
- * vty->lineno is number of last line processed (1 is first)
- *
- * If the file is empty, will return CMD_SUCCESS.
- *
- * Never returns CMD_EMPTY -- that counts as CMD_SUCCESS.
- *
- * If
- *
- * If return code is not CMD_SUCCESS, the the output buffering contains the
- * output from the last command attempted.
- */
-extern enum cmd_return_code
-config_from_file (struct vty *vty, FILE *fp, struct cmd_element* first_cmd,
- qstring buf, bool ignore_warning)
-{
- enum cmd_return_code ret;
-
- vty->buf = buf->body ;
- vty->lineno = 0 ;
-
- ret = CMD_SUCCESS ; /* in case file is empty */
- vty_out_clear(vty) ;
-
- while (fgets (buf->body, buf->size, fp))
- {
- ++vty->lineno ;
-
- /* Execute configuration command : this is strict match */
- ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ;
-
- if (ret == CMD_EMPTY)
- continue ; /* skip empty/comment */
-
- if (ret != CMD_SUCCESS)
- break ; /* stop on *any* parsing issue */
-
- /* special handling before of first command */
- if (first_cmd != NULL)
- {
- if (first_cmd != vty->parsed->cmd)
- {
- ret = (*first_cmd->func)(first_cmd, vty, 0, NULL) ;
- if (ret != CMD_SUCCESS)
- break ; /* stop on *any* issue with "default" */
- } ;
- first_cmd = NULL ;
- }
-
- /* Standard command handling */
- ret = cmd_dispatch(vty, cmd_no_queue) ;
-
- if (ret != CMD_SUCCESS)
- {
- /* Ignore CMD_WARNING if required
- *
- * Ignore CMD_CLOSE at all times.
- */
- if ( ((ret == CMD_WARNING) && ignore_warning)
- || (ret == CMD_CLOSE) )
- ret = CMD_SUCCESS ; /* in case at EOF */
- else
- break ; /* stop */
- } ;
-
- vty_out_clear(vty) ;
- } ;
-
- if (ret == CMD_EMPTY)
- ret = CMD_SUCCESS ; /* OK if end on empty line */
-
- return ret ;
-} ;
-
-/*==============================================================================
- */
-
-static cmd_return_code_t cmd_fetch_command(struct vty* vty) ;
-
-/*------------------------------------------------------------------------------
- * Command Loop
- *
- * Read and dispatch commands until can no longer do so for whatever reason:
- *
- * - reached end of command stream -- CMD_CLOSE
- * - encounter error of some kind -- CMD_WARNING, CMD_ERROR, etc
- * - waiting for input to arrive -- CMD_WAIT_INPUT
- * - waiting for command to complete -- CMD_QUEUED
- * - waiting for output to complete -- ??
- *
- */
-extern cmd_return_code_t
-cmd_command_loop(struct vty *vty, struct cmd_element* first_cmd,
- qstring buf, bool ignore_warning)
-{
- cmd_return_code_t ret ;
-
- vty->buf = buf->body ;
- vty->lineno = 0 ;
-
-
- /*
- *
- */
-
- vty_out_clear(vty) ;
-
- while (1) {
-
- /* Fetch a command line */
-
- ret = cmd_fetch_command(vty) ;
-
- if (ret != CMD_SUCCESS)
- break ;
-
- ++vty->lineno ;
-
- /* Parse the command line we now have */
-
- ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ;
-
- if (ret == CMD_EMPTY)
- continue ; /* skip empty/comment */
-
- if (ret != CMD_SUCCESS)
- break ; /* stop on *any* parsing issue */
-
- /* special handling before of first command */
- if (first_cmd != NULL)
- {
- if (first_cmd != vty->parsed->cmd)
- {
- ret = (*first_cmd->func)(first_cmd, vty, 0, NULL) ;
- if (ret != CMD_SUCCESS)
- break ; /* stop on *any* issue with "default" */
- } ;
- first_cmd = NULL ;
- } ;
-
- /* Reflect command line if required */
-
- /* Standard command handling */
-
- ret = cmd_dispatch(vty, cmd_no_queue) ;
-
- if (ret == CMD_QUEUED)
- break ;
-
- /* Output Handling..... */
-
-
- /* Return code handling.... */
-
- if (ret != CMD_SUCCESS)
- {
- if ((ret == CMD_WARNING) && !ignore_warning)
- break ;
- if (ret != CMD_CLOSE)
- break ;
- } ;
-
- vty_out_clear(vty) ;
- } ;
-
- return ret ;
-} ;
-
-
-/*------------------------------------------------------------------------------
- * Fetch the next command.
- *
- *
- *
- *
- */
-static cmd_return_code_t
-cmd_fetch_command(struct vty* vty)
-{
-
-
-
-
-} ;
-
-
-
-
-
-
-
-
-
-
-
/*============================================================================*/
/*----------------------------------------------------------------------------*/
@@ -2364,11 +859,10 @@ DEFUN_CALL (enable,
"Turn on privileged mode command\n")
{
/* If enable password is NULL, change to ENABLE_NODE */
- if ((host.enable == NULL && host.enable_encrypt == NULL) ||
- vty_shell_server(vty))
- vty_set_node(vty, ENABLE_NODE);
+ if ((host.enable == NULL) || (vty->type == VTY_SHELL_SERVER))
+ vty->node = ENABLE_NODE ;
else
- vty_set_node(vty, AUTH_ENABLE_NODE);
+ vty->node = AUTH_ENABLE_NODE ;
return CMD_SUCCESS;
}
@@ -2379,8 +873,8 @@ DEFUN_CALL (disable,
"disable",
"Turn off privileged mode command\n")
{
- if (vty_get_node(vty) == ENABLE_NODE)
- vty_set_node(vty, VIEW_NODE);
+ if (vty->node == ENABLE_NODE)
+ vty->node = VIEW_NODE;
return CMD_SUCCESS;
}
@@ -2390,7 +884,7 @@ DEFUN_CALL (config_exit,
"exit",
"Exit current mode and down to previous mode\n")
{
- return vty_cmd_exit(vty) ;
+ return cmd_exit(vty) ;
}
/* quit is alias of exit. */
@@ -2403,9 +897,9 @@ ALIAS_CALL (config_exit,
DEFUN_CALL (config_end,
config_end_cmd,
"end",
- "End current mode and change to enable mode.")
+ "End current mode and change to enable mode\n")
{
- return vty_cmd_end(vty) ;
+ return cmd_end(vty) ;
}
/* Show version. */
@@ -2453,8 +947,8 @@ DEFUN_CALL (config_list,
"Print command list\n")
{
unsigned int i;
- struct cmd_node *cnode = vector_get_item (cmdvec, vty_get_node(vty));
- struct cmd_element *cmd;
+ struct cmd_node *cnode = vector_get_item (node_vector, vty->node);
+ struct cmd_command *cmd;
for (i = 0; i < vector_length (cnode->cmd_vector); i++)
if ((cmd = vector_get_item (cnode->cmd_vector, i)) != NULL
@@ -2481,7 +975,7 @@ DEFUN (config_write_file,
int ret = CMD_WARNING;
/* Check and see if we are operating under vtysh configuration */
- if (host.config == NULL)
+ if (host.config_file == NULL)
{
vty_out (vty, "Can't save to configuration file, using vtysh.%s",
VTY_NEWLINE);
@@ -2489,7 +983,7 @@ DEFUN (config_write_file,
}
/* Get filename. */
- config_file = host.config;
+ config_file = host.config_file;
config_file_sav =
XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
@@ -2504,8 +998,7 @@ DEFUN (config_write_file,
fd = mkstemp (config_file_tmp);
if (fd < 0)
{
- vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
- VTY_NEWLINE);
+ vty_out (vty, "Can't open configuration file %s.\n", config_file_tmp) ;
goto finished;
}
@@ -2517,15 +1010,18 @@ DEFUN (config_write_file,
vty_time_print (vty, 1);
vty_out (vty, "!\n");
- for (i = 0; i < vector_length (cmdvec); i++)
- if ((node = vector_get_item (cmdvec, i)) && node->func)
- {
- if ((*node->func) (vty))
- vty_out (vty, "!\n");
- }
+ for (i = 0; i < vector_length (node_vector); i++)
+ {
+ if ((node = vector_get_item (node_vector, i)) && node->config_write)
+ {
+ if ((*node->config_write) (vty))
+ vty_out (vty, "!\n");
+
+ vty_cmd_out_push(vty) ; /* Push stuff so far */
+ }
+ } ;
err = vty_close_config_write(vty) ;
- close(fd) ;
if (err != 0)
{
@@ -2611,31 +1107,34 @@ DEFUN (config_write_terminal,
"Write to terminal\n")
{
unsigned int i;
- struct cmd_node *node;
+ bool vtysh_config ;
- if (vty_shell_serv(vty))
- {
- for (i = 0; i < vector_length (cmdvec); i++)
- if ((node = vector_get_item (cmdvec, i)) && node->func && node->vtysh)
- {
- if ((*node->func) (vty))
- vty_out (vty, "!%s", VTY_NEWLINE);
- }
- }
- else
+ vtysh_config = (vty->type == VTY_SHELL_SERVER) ;
+
+ if (!vtysh_config)
+ vty_out (vty, "\n"
+ "Current configuration:\n"
+ "!\n") ;
+
+ for (i = 0 ; i < vector_length(node_vector) ; i++)
{
- vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
- VTY_NEWLINE);
- vty_out (vty, "!%s", VTY_NEWLINE);
-
- for (i = 0; i < vector_length (cmdvec); i++)
- if ((node = vector_get_item (cmdvec, i)) && node->func)
- {
- if ((*node->func) (vty))
- vty_out (vty, "!%s", VTY_NEWLINE);
- }
- vty_out (vty, "end%s",VTY_NEWLINE);
- }
+ cmd_node node ;
+
+ node = vector_get_item(node_vector, i) ;
+
+ if (node == NULL)
+ continue ;
+
+ if (vtysh_config && !node->config_to_vtysh)
+ continue ;
+
+ if ((*node->config_write != NULL) && ((*node->config_write)(vty) != 0))
+ vty_out (vty, "!\n") ;
+ } ;
+
+ if (!vtysh_config)
+ vty_out (vty, "end\n") ;
+
return CMD_SUCCESS;
}
@@ -2656,11 +1155,11 @@ DEFUN (show_startup_config,
char buf[BUFSIZ];
FILE *confp;
- confp = fopen (host.config, "r");
+ confp = fopen (host.config_file, "r");
if (confp == NULL)
{
vty_out (vty, "Can't open configuration file [%s]%s",
- host.config, VTY_NEWLINE);
+ host.config_file, VTY_NEWLINE);
return CMD_WARNING;
}
@@ -2689,21 +1188,11 @@ DEFUN_CALL (config_hostname,
{
if (!isalpha((int) *argv[0]))
{
- vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
+ vty_out (vty, "Please specify string starting with alphabet\n");
return CMD_WARNING;
}
- VTY_LOCK() ;
-
- if (host.name)
- XFREE (MTYPE_HOST, host.name);
-
- host.name = XSTRDUP (MTYPE_HOST, argv[0]);
- uty_set_host_name(host.name) ;
-
- VTY_UNLOCK() ;
-
- return CMD_SUCCESS;
+ return cmd_set_host_name(argv[0]) ;
}
DEFUN_CALL (config_no_hostname,
@@ -2713,26 +1202,18 @@ DEFUN_CALL (config_no_hostname,
"Reset system's network name\n"
"Host name of this router\n")
{
- VTY_LOCK() ;
-
- if (host.name)
- XFREE (MTYPE_HOST, host.name);
- host.name = NULL;
- uty_set_host_name(host.name) ;
-
- VTY_UNLOCK() ;
-
- return CMD_SUCCESS;
+ return cmd_set_host_name(NULL) ;
}
-/* VTY interface password set. */
-DEFUN_CALL (config_password, password_cmd,
- "password (8|) WORD",
- "Assign the terminal connection password\n"
- "Specifies a HIDDEN password will follow\n"
- "dummy string \n"
- "The HIDDEN line password string\n")
+/*------------------------------------------------------------------------------
+ * Password setting function -- common for password and enable password.
+ */
+static cmd_return_code_t
+do_set_password(vty vty, int argc, argv_t argv, char** p_password,
+ bool* p_encrypted)
{
+ cmd_return_code_t ret ;
+
/* Argument check. */
if (argc == 0)
{
@@ -2740,47 +1221,63 @@ DEFUN_CALL (config_password, password_cmd,
return CMD_WARNING;
}
+ VTY_LOCK() ;
+ ret = CMD_SUCCESS ;
+
if (argc == 2)
{
+ /* Encrypted password argument */
+
if (*argv[0] == '8')
- {
- if (host.password)
- XFREE (MTYPE_HOST, host.password);
- host.password = NULL;
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
- return CMD_SUCCESS;
- }
+ {
+ XFREE(MTYPE_HOST, *p_password);
+ *p_encrypted = true ;
+ *p_password = XSTRDUP(MTYPE_HOST, argv[1]);
+ }
else
- {
- vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ {
+ vty_out(vty, "Unknown encryption type.\n");
+ ret = CMD_WARNING;
+ }
}
-
- if (!isalnum ((int) *argv[0]))
+ else
{
- vty_out (vty,
- "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
+ /* Plaintext password argument */
+
+ if (!isalnum ((int) *argv[0]))
+ {
+ vty_out(vty, "Please specify string starting with alphanumeric\n");
+ ret = CMD_WARNING ;
+ }
+ else
+ {
+ /* If host.encrypt, only keeps the encrypted password. */
- if (host.password)
- XFREE (MTYPE_HOST, host.password);
- host.password = NULL;
+ XFREE (MTYPE_HOST, *p_password);
- if (host.encrypt)
- {
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
- }
- else
- host.password = XSTRDUP (MTYPE_HOST, argv[0]);
+ *p_encrypted = host.encrypt ;
+ if (*p_encrypted)
+ *p_password = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
+ else
+ *p_password = XSTRDUP (MTYPE_HOST, argv[0]);
+ } ;
+ } ;
- return CMD_SUCCESS;
-}
+ VTY_UNLOCK() ;
+ return ret ;
+} ;
+
+/* VTY interface password set. */
+DEFUN_CALL (config_password, password_cmd,
+ "password (8) WORD",
+ "Assign the terminal connection password\n"
+ "Specifies a HIDDEN password will follow\n"
+ "dummy string \n"
+ "The HIDDEN line password string\n")
+{
+ return do_set_password(vty, argc, argv, &host.password,
+ &host.password_encrypted) ;
+} ;
ALIAS_CALL (config_password, password_text_cmd,
"password LINE",
@@ -2789,65 +1286,16 @@ ALIAS_CALL (config_password, password_text_cmd,
/* VTY enable password set. */
DEFUN_CALL (config_enable_password, enable_password_cmd,
- "enable password (8|) WORD",
+ "enable password (8) WORD",
"Modify enable password parameters\n"
"Assign the privileged level password\n"
"Specifies a HIDDEN password will follow\n"
"dummy string \n"
"The HIDDEN 'enable' password string\n")
{
- /* Argument check. */
- if (argc == 0)
- {
- vty_out (vty, "Please specify password.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- /* Crypt type is specified. */
- if (argc == 2)
- {
- if (*argv[0] == '8')
- {
- if (host.enable)
- XFREE (MTYPE_HOST, host.enable);
- host.enable = NULL;
-
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]);
-
- return CMD_SUCCESS;
- }
- else
- {
- vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- }
-
- if (!isalnum ((int) *argv[0]))
- {
- vty_out (vty,
- "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (host.enable)
- XFREE (MTYPE_HOST, host.enable);
- host.enable = NULL;
-
- /* Plain password input. */
- if (host.encrypt)
- {
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0]));
- }
- else
- host.enable = XSTRDUP (MTYPE_HOST, argv[0]);
-
- return CMD_SUCCESS;
-}
+ return do_set_password(vty, argc, argv, &host.enable,
+ &host.enable_encrypted) ;
+} ;
ALIAS_CALL (config_enable_password,
enable_password_text_cmd,
@@ -2863,14 +1311,12 @@ DEFUN_CALL (no_config_enable_password, no_enable_password_cmd,
"Modify enable password parameters\n"
"Assign the privileged level password\n")
{
- if (host.enable)
- XFREE (MTYPE_HOST, host.enable);
- host.enable = NULL;
+ VTY_LOCK() ;
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = NULL;
+ host.enable_encrypted = false ;
+ XFREE (MTYPE_HOST, host.enable);
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -2880,24 +1326,30 @@ DEFUN_CALL (service_password_encrypt,
"Set up miscellaneous service\n"
"Enable encrypted passwords\n")
{
- if (host.encrypt)
- return CMD_SUCCESS;
+ VTY_LOCK() ;
- host.encrypt = 1;
+ host.encrypt = true ;
- if (host.password)
+ /* If we have an unencrypted password in hand, convert that now.
+ * If not, retain any already encrypted password.
+ */
+ if (!host.password_encrypted && (host.password != NULL))
{
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
- }
- if (host.enable)
+ char* plain = host.password ;
+ host.password = XSTRDUP(MTYPE_HOST, zencrypt (plain)) ;
+ XFREE(MTYPE_HOST, plain) ;
+ host.password_encrypted = true ;
+ } ;
+
+ if (!host.enable_encrypted && (host.enable != NULL))
{
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
- }
+ char* plain = host.enable ;
+ host.enable = XSTRDUP(MTYPE_HOST, zencrypt (plain)) ;
+ XFREE(MTYPE_HOST, plain) ;
+ host.enable_encrypted = true ;
+ } ;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -2908,19 +1360,13 @@ DEFUN_CALL (no_service_password_encrypt,
"Set up miscellaneous service\n"
"Enable encrypted passwords\n")
{
- if (! host.encrypt)
- return CMD_SUCCESS;
-
- host.encrypt = 0;
+ VTY_LOCK() ;
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- host.password_encrypt = NULL;
+ /* Keep any existing passwords, encrypted or not. */
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- host.enable_encrypt = NULL;
+ host.encrypt = false ;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -2954,6 +1400,15 @@ DEFUN_CALL (config_terminal_no_length, config_terminal_no_length_cmd,
return CMD_SUCCESS;
}
+static cmd_return_code_t
+set_host_lines(int lines)
+{
+ VTY_LOCK() ;
+ host.lines = lines ;
+ VTY_UNLOCK() ;
+ return CMD_SUCCESS ;
+} ;
+
DEFUN_CALL (service_terminal_length, service_terminal_length_cmd,
"service terminal-length <0-512>",
"Set up miscellaneous service\n"
@@ -2969,10 +1424,9 @@ DEFUN_CALL (service_terminal_length, service_terminal_length_cmd,
vty_out (vty, "length is malformed%s", VTY_NEWLINE);
return CMD_WARNING;
}
- host.lines = lines;
- return CMD_SUCCESS;
-}
+ return set_host_lines(lines) ;
+} ;
DEFUN_CALL (no_service_terminal_length, no_service_terminal_length_cmd,
"no service terminal-length [<0-512>]",
@@ -2981,8 +1435,7 @@ DEFUN_CALL (no_service_terminal_length, no_service_terminal_length_cmd,
"System wide terminal length configuration\n"
"Number of lines of VTY (0 means no line control)\n")
{
- host.lines = -1;
- return CMD_SUCCESS;
+ return set_host_lines(-1) ;
}
DEFUN_HID_CALL (do_echo,
@@ -2993,10 +1446,10 @@ DEFUN_HID_CALL (do_echo,
{
char *message;
- vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""),
- VTY_NEWLINE);
- if (message)
- XFREE(MTYPE_TMP, message);
+ message = argv_concat(argv, argc, 0) ;
+ vty_out (vty, "%s\n", message ? message : "") ;
+ XFREE(MTYPE_TMP, message);
+
return CMD_SUCCESS;
}
@@ -3015,8 +1468,7 @@ DEFUN_CALL (config_logmsg,
message = argv_concat(argv, argc, 1);
zlog(NULL, level, "%s", (message ? message : ""));
- if (message)
- XFREE(MTYPE_TMP, message);
+ XFREE(MTYPE_TMP, message);
return CMD_SUCCESS;
}
@@ -3436,6 +1888,7 @@ DEFUN_CALL (banner_motd_file,
{
if (host.motdfile)
XFREE (MTYPE_HOST, host.motdfile);
+
host.motdfile = XSTRDUP (MTYPE_HOST, argv[0]);
return CMD_SUCCESS;
@@ -3448,7 +1901,7 @@ DEFUN_CALL (banner_motd_default,
"Strings for motd\n"
"Default string\n")
{
- host.motd = default_motd;
+ host.motd = DEFAULT_MOTD ;
return CMD_SUCCESS;
}
@@ -3467,12 +1920,12 @@ DEFUN_CALL (no_banner_motd,
}
/* Set config filename. Called from vty.c */
-void
-host_config_set (char *filename)
+extern void
+host_config_set (const char* file_name)
{
- if (host.config)
- XFREE (MTYPE_HOST, host.config);
- host.config = XSTRDUP (MTYPE_HOST, filename);
+ if (host.config_file)
+ XFREE (MTYPE_HOST, host.config_file);
+ host.config_file = XSTRDUP (MTYPE_HOST, file_name);
}
void
@@ -3495,25 +1948,12 @@ install_default (enum node_type node)
void
cmd_init (int terminal)
{
- command_cr = XSTRDUP(MTYPE_STRVEC, "<cr>");
- desc_cr.cmd = command_cr;
- desc_cr.str = XSTRDUP(MTYPE_STRVEC, "");
+ /* Allocate initial top vector of commands. */
+ node_vector = vector_init(0);
- /* Allocate initial top vector of commands. */
- cmdvec = vector_init(0);
+ /* Default host value settings are already set, see above */
- /* Allocate vector of spare qstrings for tokens */
- cmd_spare_tokens_init() ;
-
- /* Default host value settings. */
- host.name = NULL;
- host.password = NULL;
- host.enable = NULL;
- host.logfile = NULL;
- host.config = NULL;
- host.lines = -1;
- host.motd = default_motd;
- host.motdfile = NULL;
+ cmd_get_sys_host_name() ; /* start with system name & name_gen == 1 */
/* Install top nodes. */
install_node (&view_node, NULL);
@@ -3622,85 +2062,46 @@ cmd_init (int terminal)
void
cmd_terminate ()
{
- unsigned int i, j, k, l;
- struct cmd_node *cmd_node;
- struct cmd_element *cmd_element;
- struct desc *desc;
- vector cmd_node_v, cmd_element_v, desc_v;
+ cmd_node cmd_node;
+ cmd_command cmd ;
- cmd_spare_tokens_free() ;
-
- if (cmdvec)
+ while ((cmd_node = vector_ream(node_vector, free_it)) != NULL)
{
- for (i = 0; i < vector_length (cmdvec); i++)
+ while ((cmd = vector_ream(cmd_node->cmd_vector, free_it)) != NULL)
{
- cmd_node = vector_get_item (cmdvec, i) ;
- if (cmd_node == NULL)
- continue ;
-
- cmd_node_v = cmd_node->cmd_vector;
+ /* Note that each cmd is a static structure, which may appear in
+ * more than one cmd_vector.
+ */
+ cmd_item next_item ;
- for (j = 0; j < vector_length (cmd_node_v); j++)
+ while ((next_item = vector_ream(cmd->items, free_it)) != NULL)
{
- cmd_element = vector_get_item (cmd_node_v, j) ;
- if (cmd_element == NULL)
- continue ;
-
- cmd_element_v = cmd_element->strvec ;
- if (cmd_element_v == NULL)
- continue ;
-
- for (k = 0; k < vector_length (cmd_element_v); k++)
+ do
{
- desc_v = vector_get_item (cmd_element_v, k) ;
- if (desc_v == NULL)
- continue ;
-
- for (l = 0; l < vector_length (desc_v); l++)
- {
- desc = vector_get_item (desc_v, l) ;
- if (desc == NULL)
- continue ;
-
- if (desc->cmd)
- XFREE (MTYPE_STRVEC, desc->cmd);
- if (desc->str)
- XFREE (MTYPE_STRVEC, desc->str);
-
- XFREE (MTYPE_DESC, desc);
- } ;
- vector_free (desc_v);
- } ;
-
- cmd_element->strvec = NULL;
- vector_free (cmd_element_v);
+ cmd_item item ;
+ item = next_item ;
+ next_item = item->next ;
+ XFREE(MTYPE_CMD_ITEM, item);
+ }
+ while (next_item != NULL) ;
} ;
- vector_free (cmd_node_v);
- } ;
+ cmd->items = NULL ; /* gone */
- vector_free (cmdvec);
- cmdvec = NULL;
+ XFREE (MTYPE_CMD_STRING, cmd->r_string) ; /* sets NULL */
+ XFREE (MTYPE_CMD_STRING, cmd->r_doc) ;
+ } ;
}
- if (command_cr)
- XFREE(MTYPE_STRVEC, command_cr);
- if (desc_cr.str)
- XFREE(MTYPE_STRVEC, desc_cr.str);
- if (host.name)
- XFREE (MTYPE_HOST, host.name);
- if (host.password)
- XFREE (MTYPE_HOST, host.password);
- if (host.password_encrypt)
- XFREE (MTYPE_HOST, host.password_encrypt);
- if (host.enable)
- XFREE (MTYPE_HOST, host.enable);
- if (host.enable_encrypt)
- XFREE (MTYPE_HOST, host.enable_encrypt);
- if (host.logfile)
- XFREE (MTYPE_HOST, host.logfile);
- if (host.motdfile)
- XFREE (MTYPE_HOST, host.motdfile);
- if (host.config)
- XFREE (MTYPE_HOST, host.config);
-}
+ node_vector = NULL ;
+
+ XFREE(MTYPE_HOST, host.name);
+ XFREE(MTYPE_HOST, host.password);
+ XFREE(MTYPE_HOST, host.enable);
+ XFREE(MTYPE_HOST, host.logfile);
+ XFREE(MTYPE_HOST, host.motdfile);
+ XFREE(MTYPE_HOST, host.config_file);
+ XFREE(MTYPE_HOST, host.vty_accesslist_name);
+ XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name);
+ XFREE(MTYPE_HOST, host.vty_cwd);
+} ;
diff --git a/lib/command.h b/lib/command.h
index fe4f0a56..89644ce7 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -25,168 +25,39 @@
#include "misc.h"
-#include "node_type.h"
+#include "command_common.h"
+#include "vty.h"
+
#include "vector.h"
#include "qstring.h"
-struct vty ; /* in case command.h expanded first */
-
-/* Host configuration variable */
-struct host
-{
- /* Host name of this router. */
- char *name;
-
- /* Password for vty interface. */
- char *password;
- char *password_encrypt;
-
- /* Enable password */
- char *enable;
- char *enable_encrypt;
-
- /* System wide terminal lines. */
- int lines;
-
- /* Log filename. */
- char *logfile;
-
- /* config file name of this host */
- char *config;
-
- /* Flags for services */
- int advanced;
- int encrypt;
-
- /* Banner configuration. */
- const char *motd;
- char *motdfile;
-};
-
-/* Node which has some commands and prompt string and configuration
- function pointer . */
-struct cmd_node
-{
- /* Node index. */
- enum node_type node;
-
- /* Prompt character at vty interface. */
- const char *prompt;
-
- /* Is this node's configuration goes to vtysh ? */
- int vtysh;
-
- /* Node's configuration write function */
- int (*func) (struct vty *);
-
- /* Vector of this node's command list. */
- vector cmd_vector;
-};
-
-enum
-{
- CMD_ATTR_SIMPLE = 0x00,
- /* bit significant */
- CMD_ATTR_DEPRECATED = 0x01,
- CMD_ATTR_HIDDEN = 0x02,
- CMD_ATTR_CALL = 0x04,
-};
-
-/* Return values for command handling.
- *
- * NB: when a command is executed it may return CMD_SUCCESS or CMD_WARNING.
- *
- * In both cases any output required (including any warning or error
- * messages) must already have been output.
- *
- * All other return codes are for use within the command handler.
+/*==============================================================================
+ * This contains everything required for command handling from outside the
+ * library.
*/
-enum cmd_return_code
-{
- CMD_SUCCESS = 0,
- CMD_WARNING = 1,
- CMD_ERROR,
-
- CMD_EMPTY,
- CMD_SUCCESS_DAEMON,
-
- CMD_WAIT_INPUT,
- CMD_CLOSE,
- CMD_QUEUED,
-
- CMD_ERR_NO_MATCH,
- CMD_ERR_AMBIGUOUS,
- CMD_ERR_INCOMPLETE,
-
- CMD_COMPLETE_FULL_MATCH,
- CMD_COMPLETE_MATCH,
- CMD_COMPLETE_LIST_MATCH,
- CMD_COMPLETE_ALREADY
-} ;
-
-typedef enum cmd_return_code cmd_return_code_t ;
-
-#define MSG_CMD_ERR_AMBIGUOUS "Ambiguous command"
-#define MSG_CMD_ERR_NO_MATCH "Unrecognised command"
-#define MSG_CMD_ERR_NO_MATCH_old "There is no matched command"
-
-/* Structure of command element. */
-
-struct cmd_element ;
-typedef struct cmd_element* cmd_element ;
-
-typedef const char* const argv_t[] ;
-
-#define DEFUN_CMD_ARG_UNUSED __attribute__ ((unused))
-#define DEFUN_CMD_FUNCTION(name) \
- enum cmd_return_code name (cmd_element self DEFUN_CMD_ARG_UNUSED, \
- struct vty* vty DEFUN_CMD_ARG_UNUSED, \
- int argc DEFUN_CMD_ARG_UNUSED, \
- argv_t argv DEFUN_CMD_ARG_UNUSED)
-
-typedef DEFUN_CMD_FUNCTION((cmd_function)) ;
-
-struct cmd_element
-{
- const char* string ; /* Command specification by string. */
- cmd_function* func ;
- const char* doc ; /* Documentation of this command. */
- int daemon ; /* Daemon to which this command belong. */
- vector strvec ; /* Vector of vectors of struct desc */
- unsigned int cmdsize ; /* Command index count. */
- char* config ; /* Configuration string */
- vector subconfig ; /* Sub configuration string */
- u_char attr ; /* Command attributes */
-};
-
-/* Command description structure. */
-struct desc
-{
- char* cmd; /* Command string. */
- char* str; /* Command's description. */
-};
-
/*------------------------------------------------------------------------------
- * Can now include these
+ * Turn off these macros when using cpp with extract.pl
*/
-
-#include "vty.h"
-#include "uty.h"
-
-/*----------------------------------------------------------------------------*/
-/* Turn off these macros when uisng cpp with extract.pl */
#ifndef VTYSH_EXTRACT_PL
-/* helper defines for end-user DEFUN* macros */
-#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
- struct cmd_element cmdname = \
+/* helper defines for end-user DEFUN* macros */
+#define DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+ struct cmd_command cmdname = \
{ \
- .string = cmdstr, \
- .func = funcname, \
- .doc = helpstr, \
- .attr = attrs, \
- .daemon = dnum, \
- };
+ .string = cmdstr, \
+ .func = funcname, \
+ .doc = helpstr, \
+ .attr = attrs, \
+ .daemon = dnum, \
+ \
+ .items = NULL, \
+ .nt_min = 0, \
+ .nt = 0, \
+ .nt_max = 0, \
+ .vararg = NULL, \
+ .r_string = NULL, \
+ .r_doc = NULL, \
+ } ;
#define DEFUN_CMD_FUNC_DECL(funcname) \
static cmd_function funcname;
@@ -194,50 +65,52 @@ struct desc
#define DEFUN_CMD_FUNC_TEXT(funcname) \
static DEFUN_CMD_FUNCTION(funcname)
-/* DEFUN for vty command interafce. Little bit hacky ;-). */
+/* DEFUN for vty command interface. Little bit hacky ;-). */
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, 0, 0) \
DEFUN_CMD_FUNC_TEXT(funcname)
#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, attr, 0) \
DEFUN_CMD_FUNC_TEXT(funcname)
+#define DEFUN_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DIRECT)
+
#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
#define DEFUN_HID_CALL(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, (CMD_ATTR_CALL | CMD_ATTR_HIDDEN))
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, \
+ (CMD_ATTR_DIRECT | CMD_ATTR_HIDDEN))
#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
#define DEFUN_DEP_CALL(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, (CMD_ATTR_CALL | CMD_ATTR_DEPRECATED))
-
-#define DEFUN_CALL(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_CALL)
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, \
+ (CMD_ATTR_DIRECT | CMD_ATTR_DEPRECATED))
-/* DEFUN_NOSH for commands that vtysh should ignore */
+/* DEFUN_NOSH for commands that vtysh should ignore */
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
DEFUN(funcname, cmdname, cmdstr, helpstr)
-/* DEFSH for vtysh. */
+/* DEFSH for vtysh. */
#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon)
+ DEFUN_CMD_COMMAND(NULL, cmdname, cmdstr, helpstr, 0, daemon)
-/* DEFUN + DEFSH */
+/* DEFUN + DEFSH */
#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
DEFUN_CMD_FUNC_TEXT(funcname)
-/* DEFUN + DEFSH with attributes */
+/* DEFUN + DEFSH with attributes */
#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
DEFUN_CMD_FUNC_DECL(funcname) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
DEFUN_CMD_FUNC_TEXT(funcname)
#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
@@ -246,44 +119,34 @@ struct desc
#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
-/* ALIAS macro which define existing command's alias. */
+/* ALIAS macro which define existing command's alias. */
#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, 0, 0)
#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, attr, 0)
+
+#define ALIAS_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DIRECT, 0)
#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
-
-#define ALIAS_CALL(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_CALL, 0)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, 0, daemon)
#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon)
+ DEFUN_CMD_COMMAND(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED,\
+ daemon)
#endif /* VTYSH_EXTRACT_PL */
-/* Some macroes */
-#define CMD_OPTION(S) ((S[0]) == '[')
-#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<'))
-#define CMD_VARARG(S) ((S[0]) == '.')
-#define CMD_RANGE(S) ((S[0] == '<'))
-
-#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
-#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
-#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
-#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
-
/* Common descriptions. */
#define SHOW_STR "Show running system information\n"
#define IP_STR "IP information\n"
@@ -347,17 +210,29 @@ extern void cmd_terminate (void);
extern void print_version (const char *);
extern void install_node (struct cmd_node *, int (*) (struct vty *));
-extern void install_default (enum node_type);
-extern void install_element (enum node_type, struct cmd_element *);
+extern void install_default (node_type_t);
+extern void install_element (node_type_t, struct cmd_command *);
extern void sort_node (void);
+extern const char* cmd_host_name(bool fresh) ;
+
+extern node_type_t cmd_node_parent(node_type_t node) ;
+extern node_type_t cmd_node_exit_to(node_type_t node) ;
+extern node_type_t cmd_node_end_to(node_type_t node) ;
+
/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
string with a space between each element (allocated using
XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */
extern char *argv_concat (const char* const* argv, int argc, int shift);
-/* struct host global, ick */
-extern struct host host;
+/* Export typical functions. */
+extern struct cmd_command config_end_cmd;
+extern struct cmd_command config_exit_cmd;
+extern struct cmd_command config_quit_cmd;
+extern struct cmd_command config_help_cmd;
+extern struct cmd_command config_list_cmd;
+extern char *host_config_file (void);
+extern void host_config_set (const char *);
#ifdef QDEBUG
extern const char *debug_banner ;
diff --git a/lib/command_execute.h b/lib/command_execute.h
index 6a7e2e4b..c1a2bdd0 100644
--- a/lib/command_execute.h
+++ b/lib/command_execute.h
@@ -23,59 +23,104 @@
#ifndef _ZEBRA_COMMAND_EXECUTE_H
#define _ZEBRA_COMMAND_EXECUTE_H
-#include "command.h"
+#include "command_local.h"
#include "command_parse.h"
-#include "node_type.h"
-
-extern vector cmd_make_strvec (const char *);
-extern vector cmd_add_to_strvec (vector v, const char* str) ;
-extern void cmd_free_strvec (vector);
-extern vector cmd_describe_command (const char* line, node_type_t node,
- cmd_return_code_t* status) ;
-extern vector cmd_complete_command (vector, int, int *status);
-extern const char *cmd_prompt (enum node_type);
-extern enum cmd_return_code
-config_from_file (struct vty* vty, FILE *fp, struct cmd_element* first_cmd,
- qstring buf, bool stop_on_warning) ;
-extern enum node_type node_parent (enum node_type);
-extern enum cmd_return_code cmd_execute_command (struct vty *vty,
- enum cmd_parse_type type, struct cmd_element **cmd) ;
-extern enum cmd_return_code cmd_execute_command_strict (struct vty *vty,
- enum cmd_parse_type type, struct cmd_element **cmd) ;
-
-extern cmd_parsed cmd_parse_init_new(cmd_parsed parsed) ;
-extern cmd_parsed cmd_parse_reset(cmd_parsed parsed, bool free_structure) ;
-extern enum cmd_return_code cmd_parse_command(struct vty* vty,
- enum cmd_parse_type type) ;
-extern enum cmd_return_code cmd_dispatch(struct vty* vty, bool no_queue) ;
-
-Inline enum cmd_return_code
-cmd_dispatch_call(struct vty* vty)
+#include "vty_common.h"
+#include "qstring.h"
+#include "mqueue.h"
+#include "qpnexus.h"
+#include "thread.h"
+
+/*==============================================================================
+ * This is stuff which is used to parse and then execute commands.
+ */
+
+/* State of the execution loop
+ */
+enum cmd_exec_state
{
- cmd_parsed parsed = vty->parsed ;
- return (*(parsed->cmd->func))(parsed->cmd, vty, cmd_arg_vector_argc(parsed),
- cmd_arg_vector_argv(parsed)) ;
+ exec_null = 0,
+
+ exec_special, /* not a simple command */
+
+ exec_fetch,
+ exec_parse,
+ exec_open_pipes,
+ exec_execute,
+ exec_success,
+ exec_complete,
} ;
+typedef enum cmd_exec_state cmd_exec_state_t ;
+
+typedef struct cmd_exec* cmd_exec ;
+
+struct cmd_exec
+{
+ vty vty ; /* parent */
-#define cmd_parse_reset_keep(parsed) cmd_parse_reset(parsed, 0)
-#define cmd_parse_reset_free(parsed) cmd_parse_reset(parsed, 1)
+ qstring line ; /* pointer to qstring in vf */
+ cmd_do_t to_do ; /* for cli driven stuff */
-extern void config_replace_string (struct cmd_element *, char *, ...);
+ cmd_parse_type_t parse_type ; /* how should parse */
+ bool out_enabled ; /* as required */
+ bool reflect_enabled ; /* as required */
+
+ cmd_parsed_t parsed ; /* embedded */
+
+ cmd_exec_state_t state ; /* for cq_process */
+ qpn_nexus locus ; /* for cq_process */
+
+ cmd_return_code_t ret ; /* for cq_process */
+
+ union
+ {
+ mqueue_block mqb ; /* for cq_process */
+ struct thread* thread ;
+ } cq ;
+} ;
+
+/*==============================================================================
+ * Functions
+ *
+ */
-/* Export typical functions. */
-extern struct cmd_element config_end_cmd;
-extern struct cmd_element config_exit_cmd;
-extern struct cmd_element config_quit_cmd;
-extern struct cmd_element config_help_cmd;
-extern struct cmd_element config_list_cmd;
-extern char *host_config_file (void);
-extern void host_config_set (char *);
+extern cmd_exec cmd_exec_new(vty vty) ;
+extern cmd_exec cmd_exec_free(cmd_exec exec) ;
-/* "<cr>" global */
-extern char *command_cr;
+extern cmd_return_code_t cmd_read_config(vty vty, cmd_command first_cmd,
+ bool ignore_warning) ;
+extern cmd_return_code_t cmd_end(vty vty) ;
+extern cmd_return_code_t cmd_exit(vty vty) ;
+
+extern cmd_return_code_t cmd_open_pipes(vty vty) ;
+
+extern cmd_return_code_t cmd_execute(vty vty) ;
+
+
+
+
+
+
+#if 0
+
+extern enum cmd_return_code cmd_execute_command (vty vty,
+ cmd_parse_type_t type, struct cmd_command **cmd) ;
+extern enum cmd_return_code cmd_execute_command_strict (vty vty,
+ enum cmd_parse_type type, struct cmd_command **cmd) ;
+
+
+extern void config_replace_string (cmd_command, char *, ...);
-#ifdef QDEBUG
-extern const char *debug_banner;
#endif
+/*==============================================================================
+ * Inlines
+ */
+
+Inline bool
+cmd_is_direct(cmd_parsed parsed)
+{
+ return ((parsed->cmd->attr & CMD_ATTR_DIRECT) != 0) ;
+} ;
+
#endif /* _ZEBRA_COMMAND_EXECUTE_H */
diff --git a/lib/command_parse.c b/lib/command_parse.c
index d8960b8d..c64ee3ba 100644
--- a/lib/command_parse.c
+++ b/lib/command_parse.c
@@ -1,4 +1,4 @@
-/* Quagga command line parsing -- header
+/* Quagga command line parsing
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
* Recast and extended: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
@@ -21,58 +21,1155 @@
* Boston, MA 02111-1307, USA.
*/
-#include <zebra.h>
+#include "misc.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include "command_local.h"
#include "command_parse.h"
#include "memory.h"
/*==============================================================================
- * Token handling
+ * Command Description objects.
+ *
+ */
+static void cmd_fail_item(cmd_command cmd, const char* msg) ;
+static char* cmd_item_brackets(cmd_command cmd, char* cp) ;
+static cmd_item cmd_make_item(cmd_command cmd, char* cp, char* dp) ;
+static void cmd_make_item_inner(cmd_command cmd, cmd_item n, char* cp) ;
+static char* cmd_make_item_numeric(cmd_command cmd, cmd_item n, char* cp) ;
+static long cmd_make_item_number(cmd_command cmd, cmd_item n, char** p_cp) ;
+static int cmd_cmp_item(const cmd_item* a, const cmd_item* b) ;
+static int cmd_cmp_range_items(const cmd_item a, const cmd_item b) ;
+static bool cmd_item_is_option(cmd_item_type_t it) ;
+static bool cmd_item_is_vararg(cmd_item_type_t it) ;
+
+/*------------------------------------------------------------------------------
+ * Dummy eol_item
*/
+static struct cmd_item eol_item =
+{
+ .str = "<cr>",
+ .doc = "",
+
+ .next = NULL,
-/* Store of qstrings, used for parsing. */
-token_vector_t spare_tokens ;
+ .type = item_eol,
+ .arg = false,
-static char cmd_token_escape(char e) ;
+ .range_sign_allowed = false,
+ .range_sign_required = false,
+ .range_min = 0,
+ .range_max = 0
+} ;
/*------------------------------------------------------------------------------
- * Initialise a brand new token vector -- empty.
+ * Parse cmd_command string and doc to create the items for the cmd_command,
+ * and fill in:
+ *
+ * cmd->items -- vector of cmd_item(s).
+ *
+ * Where a given item may have more than one possible
+ * value, thet are arranged as a list.
+ *
+ * cmd->nt_min -- count of items up to first [option]
+ * where (...) counts as 1
+ * and .vararg counts as 1
+ *
+ * Is the minimum number of tokens required to match to
+ * this command.
+ *
+ * cmd->nt -- count of all items
+ * where (...) counts as 1
+ * and [option] counts as 1
+ * and .vararg counts as 1
+ *
+ * cmd->nt_max -- count of all items as nt_var,
+ * except .vararg forces to UINT_MAX
+ *
+ * Is the maximum number of tokens which can be matched
+ * to this command.
+ *
+ * cmd->r_string -- copy of cmd->string, chopped up and referred to by
+ * the cmd_items.
+ *
+ * cmd->d_string -- copy of the cmd->doc, chopped up and referred to by
+ * the cmd_items.
+ *
+ * Note that the cmd_items point into the r_string and the d_string, and
+ * do not have further copies of their fragments of the original.
+ *
+ * Note that the t_string and d_string have all extraneous spaces, tabs and
+ * control characters removed.
+ *
+ * Stops dead if not valid !
+ *
+ * Accepts: items separated by one or more spaces. Early on in the process,
+ * will discard spaces within a bracketed item (and checks for balanced
+ * brackets). Rejects any control character other than '\t', which is
+ * converted to ' '. Multiple spaces are reduced to single spaces.
+ *
+ * - single item is one of:
+ *
+ * - keyword -- anything starting a-z or 0-9, followed by any
+ * alphanumeric, '-', '_' or ':'.
+ * or *
+ * - <0-9> -- decimal range
+ * - WORD -- anything at least starting A-Z, followed by any
+ * alphanumeric, '-', '_' or ':'.
+ * - A.B.C.D -- ipv4 address
+ * - A.B.C.D/M -- ipv4 prefix
+ * - X:X::X:X -- ipv6 address
+ * - X:X::X:X/M -- ipv6 prefix
+ * - .vararg -- anything starting '.', followed by any alphanumeric,
+ * '-', '_' or ':'.
+ * - [item] -- optional item any of the above TODO
+ *
+ * - multiple item is: '(' item '|' item .... ')'
+ *
+ * where spaces around items are discarded. The items may be any of the
+ * above, except:
+ *
+ * - must all be different types of item, or for keywords and ranges,
+ * different values.
+ *
+ * - cannot have [item]
+ *
+ * - cannot have .var
+ *
+ * may have a single item -- whose value is sent out as an argument.
+ *
+ * An [item] may only be followed by other [item](s). An [item] matches a
+ * token or end of line.
+ *
+ * A .vararg must be the last item. A .vararg matches one or more tokens.
+ *
+ *
+ *
*/
-static inline void
-cmd_token_vector_init(token_vector tokens)
+extern void
+cmd_compile(cmd_command cmd)
{
- vector_init_new(tokens->body, 0) ;
+ vector multvec ;
+ char* cp ;
+ char* qp ;
+ char* dp ;
+ bool opt ;
+ bool vararg ;
+
+ /* Initialise the compiled version of the command */
+
+ assert((cmd->r_doc == NULL) && (cmd->r_string == NULL)) ;
+
+ cmd->items = vector_init_new(NULL, 10) ; /* plenty ! */
+ cmd->nt_min = 0 ;
+ cmd->nt = 0 ;
+ cmd->nt_max = 0 ;
+ cmd->vararg = NULL ;
+ cmd->r_doc = XSTRDUP(MTYPE_CMD_STRING, cmd->doc) ; /* NULL => "" */
+ cmd->r_string = XSTRDUP(MTYPE_CMD_STRING, cmd->string) ;
+
+ /* Simplify the command line string by replacing TABs by spaces, and barfing
+ * on control characters. Strip leading and trailing spaces and any spaces
+ * between brackets... checking for matching brackets.
+ */
+ cp = cmd->r_string ;
+ while (*cp != '\0')
+ {
+ if (!iscntrl(*cp))
+ ++cp ;
+ else if (*cp == '\t')
+ *cp++ = ' ' ;
+ else
+ cmd_fail_item(cmd, "improper control character in string") ;
+ } ;
+
+ cp = cmd->r_string ;
+ while (*cp == ' ')
+ ++cp ;
+
+ qp = cmd->r_string ;
+ while (*cp != '\0')
+ {
+ if (*cp != ' ')
+ {
+ if ((*cp == '(') || (*cp == '[') || (*cp == '<') || (*cp == '{'))
+ {
+ /* Check for balanced brackets and remove any spaces between.
+ *
+ * Checks for enclosed brackets being balanced as well.
+ *
+ * Leaves cp pointing at the trailing bracket.
+ */
+ char* sp = cp ;
+ cp = cmd_item_brackets(cmd, cp) ;
+ while (sp < cp)
+ {
+ if (*sp != ' ')
+ *qp++ = *sp++ ;
+ else
+ ++sp ;
+ } ;
+ } ;
+ }
+ else
+ {
+ while (*(cp + 1) == ' ')
+ ++cp ;
+ if (*(cp + 1) == '\0')
+ break ;
+ } ;
+
+ *qp++ = *cp++ ;
+ } ;
+
+ *qp++ = '\0' ; /* terminate reduced string */
+
+ /* Simplify the documentation string by replacing TABs by spaces, and barfing
+ * on control characters other than '\n'.
+ *
+ * Strips leading spaces and any spaces before or after '\n'.
+ */
+
+ qp = dp = cmd->r_doc ;
+ while (*dp != '\0')
+ {
+ /* Strip leading */
+ while (*dp == ' ')
+ ++dp ;
+
+ /* Eat documentation section. */
+ while ((*dp != '\n') && (*dp != '\0'))
+ {
+ if (!iscntrl(*dp))
+ *qp++ = *dp++ ;
+ else if (*dp == '\t')
+ {
+ *qp++ = ' ' ;
+ ++dp ;
+ }
+ else
+ cmd_fail_item(cmd, "improper control character in documentation") ;
+ } ;
+
+ /* Get here with *dp == '\n' or '\0'
+ *
+ * Strip trailing spaces (any before '\n' or '\0'
+ */
+ while ((qp != cmd->r_doc) && (*(qp - 1) == ' '))
+ --qp ;
+
+ /* copy '\n', if required. */
+ if (*dp == '\n')
+ *qp++ = *dp++ ;
+ } ;
+
+ *qp++ = '\0' ; /* terminate reduced string */
+
+ /* Processing loop */
+
+ cp = cmd->r_string ;
+ dp = cmd->r_doc ;
+
+ opt = false ;
+ vararg = false ;
+
+ multvec = NULL ;
+
+ while (*cp != '\0')
+ {
+ uint multiple ;
+
+ /* Deal with single or multiple item. */
+ multiple = 0 ;
+ do
+ {
+ cmd_item n ;
+ char* c_sp ;
+ char* d_sp ;
+
+ /* step to the next documentation section */
+
+ d_sp = dp ; /* start of documentation */
+
+ while (*dp != '\0')
+ {
+ if (*dp == '\n')
+ {
+ *dp++ = '\0' ;
+ break ;
+ } ;
+ ++dp ;
+ } ;
+
+ /* Deal with '(' if we have one. */
+
+ if (*cp == '(') /* change up to multiple */
+ {
+ if (multiple != 0)
+ cmd_fail_item(cmd, "unexpected '('") ;
+
+ multiple = 1 ; /* seen '(' */
+ ++cp ; /* step past it */
+
+ multvec = vector_re_init(multvec, 10) ; /* plenty ! */
+ } ;
+
+ /* Find end of current item & '\0' terminate it. */
+ c_sp = cp ;
+ while (1)
+ {
+ if (*cp == '|') /* eat '|' */
+ {
+ if ((c_sp == cp) || (multiple < 1))
+ cmd_fail_item(cmd, "unexpected '|'") ;
+ *cp++ = '\0' ;
+ break ;
+ } ;
+
+ if (*cp == ')') /* eat ')' */
+ {
+ if ((c_sp == cp) || (multiple < 1))
+ cmd_fail_item(cmd, "unexpected ')'") ;
+ *cp++ = '\0' ;
+ multiple = 2 ;
+
+ if ((*cp != ' ') && (*cp != '\0'))
+ cmd_fail_item(cmd, "expect ' ' or nothing after ')'") ;
+ } ;
+
+ if (*cp == ' ')
+ {
+ *cp++ = '\0' ;
+ break ;
+ } ;
+
+ if (*cp == '\0')
+ break ;
+
+ ++cp ;
+ } ;
+
+ /* Create the next item and push */
+
+ n = cmd_make_item(cmd, c_sp, d_sp) ;
+
+ if (multiple == 0)
+ vector_push_item(cmd->items, n) ;
+ else
+ vector_push_item(multvec, n) ;
+
+ /* Extra checks for multiple item. */
+ if (multiple > 0)
+ {
+ n->arg = true ; /* always */
+
+ if (cmd_item_is_option(n->type))
+ cmd_fail_item(cmd, "cannot have [option] inside (..)") ;
+
+ /* could lift this restriction, but need to check that
+ * do not have a WORD|.VAR together, because that is tautologous.
+ */
+
+ if (cmd_item_is_vararg(n->type))
+ cmd_fail_item(cmd, "cannot have .vararg inside (..)") ;
+ } ;
+
+ /* Check optional item state -- can only be trailing */
+ if (cmd_item_is_option(n->type))
+ opt = true ;
+ else if (opt)
+ cmd_fail_item(cmd, "can only have [option] after [option]") ;
+
+ /* Check vararg item state -- can only be trailing */
+ if (vararg)
+ cmd_fail_item(cmd, "cannot have anything after .vararg") ;
+ else if (cmd_item_is_vararg(n->type))
+ {
+ vararg = true ;
+ cmd->vararg = n ; /* remember for parsing */
+ } ;
+
+ } while (multiple == 1) ;
+
+ /* count the item */
+ if (!opt)
+ ++cmd->nt_min ;
+ ++cmd->nt ;
+ if (!vararg)
+ ++cmd->nt_max ;
+ else
+ cmd->nt_max = UINT_MAX ;
+
+ /* Complete the multiple item.
+ *
+ * Sort the items so that are always used and presented in the same
+ * order. Check that the items are unique.
+ *
+ * We must have at least one item.
+ */
+ if (multiple == 2)
+ {
+ cmd_item n, p ;
+ uint i ;
+
+ assert(vector_length(multvec) >= 1) ;
+
+ vector_sort(multvec, (vector_sort_cmp*)cmd_cmp_item) ;
+
+ n = vector_get_item(multvec, 0) ;
+ vector_push_item(cmd->items, n) ;
+
+ for (i = 1 ; i < vector_length(multvec) ; ++i)
+ {
+ p = n ;
+ n = vector_get_item(multvec, i) ;
+
+ p->next = n ;
+ n->next = NULL ;
+
+ if (p->type == n->type)
+ {
+ bool repeat ;
+
+ if (n->type == item_keyword)
+ repeat = strcmp(n->str, p->str) == 0 ;
+ else if (n->type == item_range)
+ repeat = cmd_cmp_range_items(n, p) == 0 ;
+ else
+ repeat = true ;
+
+ if (repeat)
+ cmd_fail_item(cmd, "repeated items in (...)") ;
+ } ;
+ } ;
+ }
+ else
+ assert(multiple == 0) ;
+ } ;
+
+ vector_reset(multvec, free_it) ;
+
+ /* Reduce the vector to the minimum size required */
+ vector_decant(cmd->items) ;
} ;
/*------------------------------------------------------------------------------
- * Initialise empty spare tokens vector
+ * Validate a compiled item
+ *
+ * Checks that the contents of the cmd_command are consistent with the
+ * contents of the srcvec.
+ *
*/
extern void
-cmd_spare_tokens_init(void)
+cmd_compile_check(cmd_command cmd)
{
- cmd_token_vector_init(spare_tokens) ;
+ bool ok ;
+
+ uint nt_min = 0 ;
+ uint nt = 0 ;
+ uint nt_max = 0 ;
+ cmd_item vararg = NULL ;
+
+ ok = true ;
+
+ /* Require the following to be set to something */
+ if ( (cmd->string == NULL)
+ || (cmd->func == NULL)
+ || (cmd->items == NULL)
+ || (cmd->r_string == NULL)
+ || (cmd->r_doc == NULL) )
+ ok = false ;
+
+ /* cmd->nt must match the vector_length and be non-zero. */
+ ok = ok && ((nt = vector_length(cmd->items)) == cmd->nt) ;
+ ok = ok && (nt != 0) ;
+
+ /* Walk the vector of items, and check that those are OK. */
+ if (ok)
+ {
+ uint ii = 0 ;
+ bool opt = false ;
+
+ for (ii = 0 ; ok && (ii < nt) ; ++ii)
+ {
+ cmd_item item ;
+ cmd_item first_item ;
+
+ item = vector_get_item(cmd->items, ii) ;
+ if (item == NULL)
+ {
+ ok = false ;
+ break ;
+ } ;
+
+ if (vararg != NULL) /* nothing after vararg */
+ {
+ ok = false ;
+ break ;
+ } ;
+
+ first_item = item ;
+ while (ok && (item != NULL))
+ {
+ /* If this is an option, may only be a single item
+ *
+ * Otherwise, after an option must all be options.
+ */
+ if (cmd_item_is_option(item->type))
+ {
+ /* option must be a single item. */
+ opt = true ;
+ if ((item != first_item) || (item->next != NULL))
+ {
+ ok = false ;
+ break ;
+ } ;
+ }
+ else if (opt)
+ {
+ /* once we have an option, must all be options */
+ ok = false ;
+ break ;
+ } ;
+
+ /* If this is a vararg, must be the last of this item.
+ *
+ * Note that allow for [.varg] and (...., .varg) -- the second
+ * should be sorted to the back !
+ */
+ if (cmd_item_is_vararg(item->type))
+ {
+ /* vararg must be last item & only vararg */
+ if ((item->next != NULL) || (vararg != NULL))
+ {
+ ok = false ;
+ break ;
+ } ;
+
+ vararg = item ;
+ } ;
+
+
+ /* If there is a next, this and the next MUST be arg */
+ if (item->next != NULL)
+ {
+ if (!((item->arg) && (item->next->arg)))
+ {
+ ok = false ;
+ break ;
+ } ;
+ } ;
+
+ item = item->next ;
+ } ;
+
+ /* Advance the nt_min and nt_max as required. */
+ if (!opt)
+ ++nt_min ;
+
+ if (vararg == NULL)
+ ++nt_max ;
+ else
+ nt_max = UINT_MAX ;
+ } ;
+ } ;
+
+ /* Final checks */
+
+ ok = ok && (cmd->nt_min == nt_min)
+ && (cmd->nt == nt)
+ && (cmd->nt_max == nt_max)
+ && (cmd->vararg == vararg) ;
+
+ if (!ok)
+ cmd_fail_item(cmd, "some compile error") ;
} ;
/*------------------------------------------------------------------------------
- * Empty out the spare_tokens vector and release all memory
+ * Reject the cmd_item string or doc.
*/
-extern void
-cmd_spare_tokens_free(void)
+static void
+cmd_fail_item(cmd_command cmd, const char* msg)
+{
+ fprintf (stderr, "Command parse error!: %s\n", msg) ;
+ fprintf (stderr, " in command: '%s'\n", cmd->string) ;
+ exit(2) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Advance to matching bracket -- fail if not found. Recurse as required.
+ *
+ * Returns: address of matching bracket.
+ */
+static char*
+cmd_item_brackets(cmd_command cmd, char* cp)
+{
+ char seek ;
+
+ switch (*cp)
+ {
+ case '(':
+ seek = ')' ;
+ break ;
+
+ case '[':
+ seek = ']' ;
+ break ;
+
+ case '<':
+ seek = '>' ;
+ break ;
+
+ case '{':
+ seek = '}' ;
+ break ;
+
+ default:
+ return cp ;
+ } ;
+
+ do
+ {
+ ++cp ;
+
+ if (*cp == seek)
+ return cp ;
+ else if ((*cp == '(') || (*cp == '[') || (*cp == '<') || (*cp == '{'))
+ cp = cmd_item_brackets(cmd, cp) ;
+ }
+ while (*cp != '\0') ;
+
+ cmd_fail_item(cmd, "unbalanced brackets of some sort") ;
+
+ return cp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make descriptor for current item.
+ *
+ * cp points at start of '\0' terminated item, which has no spaces and no
+ * control characters in or around it. Also, if there are brackets, they are
+ * balanced.
+ *
+ * Returns: new descriptor, filled in as required.
+ */
+
+static cmd_item
+cmd_make_item(cmd_command cmd, char* cp, char* dp)
+{
+ char* inner ;
+ cmd_item n ;
+
+ n = XCALLOC(MTYPE_CMD_ITEM, sizeof(struct cmd_item)) ;
+
+ /* Zeroising has set:
+ *
+ * * cmd = NULL -- set below
+ * * doc = NULL -- set below
+ *
+ * * next = NULL -- set elsewhere if multiple.
+ *
+ * * type = item_null
+ * * arg = false -- set if required, below
+ *
+ * * range_sign_allowed )
+ * * range_sign_required ) -- set elsewhere if required
+ * * range_min )
+ * * range_max )
+ */
+ confirm(item_null == 0) ;
+
+ n->str = cp ;
+ n->doc = dp ;
+
+ /* Worry about option state */
+ inner = NULL ;
+ if (*cp == '[')
+ {
+ n->arg = true ; /* always true for option */
+
+ inner = XSTRDUP(MTYPE_TMP, cp) ;
+ cp = inner + 1 ; /* strip leading '[' */
+ *(cp + strlen(cp) - 1) = '\0' ; /* strip trailing ']' */
+
+ if (*cp == '\0')
+ cmd_fail_item(cmd, "empty [option]") ;
+ } ;
+
+ /* Deal with the inner item */
+ cmd_make_item_inner(cmd, n, cp) ;
+
+ /* Worry about the option state, again. */
+ if (inner != NULL)
+ {
+ XFREE(MTYPE_TMP, inner) ;
+
+ if (n->type == item_vararg)
+ cmd_fail_item(cmd, "cannot have [.vararg]") ;
+
+ n->type = item_option_word ; /* TODO other option types ? */
+ } ;
+
+ /* return newly minted cmd_item item */
+ assert(n->type != item_null) ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Make inner part of cmd_item -- so can have [...] anything (in principle).
+ *
+ * Require '\0' terminated inner part.
+ */
+static void
+cmd_make_item_inner(cmd_command cmd, cmd_item n, char* cp)
+{
+ bool eat_name_chars ; /* alphanumeric + '-', '_', '.' and ':' */
+
+ eat_name_chars = false ;
+
+ if (islower(*cp) /* 'a'..'z' */
+ || isdigit(*cp)) /* '0'..'9' */
+ {
+ /* item_keyword -- lowercase alpha numeric + '_' and '-' */
+ n->type = item_keyword ;
+ eat_name_chars = true ;
+ }
+ else if (*cp == '*') /* '*' */
+ {
+ /* special item_keyword '*' */
+ n->type = item_keyword ;
+ ++cp ;
+ }
+ else if (isupper(*cp)) /* 'A'..'Z' */
+ {
+ n->arg = true ;
+
+ /* WORD or other variable */
+ if (strcmp(cp, "A.B.C.D") == 0)
+ n->type = item_ipv4_address ;
+ else if (strcmp(cp, "A.B.C.D/M") == 0)
+ n->type = item_ipv4_prefix ;
+ else if (strcmp(cp, "X:X::X:X") == 0)
+ n->type = item_ipv6_address ;
+ else if (strcmp(cp, "X:X::X:X/M") == 0)
+ n->type = item_ipv6_prefix ;
+ else
+ {
+ n->type = item_word ;
+ eat_name_chars = true ;
+ } ;
+
+ if (n->type != item_word)
+ cp += strlen(cp) ; /* step past "A.B.C.D" et al */
+ }
+ else if (*cp == '.') /* '.' */
+ {
+ n->arg = true ;
+ n->type = item_vararg ;
+ eat_name_chars = true ;
+ }
+ else if (*cp == '<') /* '<' */
+ {
+ n->arg = true ;
+
+ cp = cmd_make_item_numeric(cmd, n, ++cp) ;
+
+ if (*cp != '>')
+ cmd_fail_item(cmd, "badly formed <...>") ;
+ else
+ ++cp ;
+ }
+ else if (*cp == '\0')
+ cmd_fail_item(cmd, "cannot have an empty item") ;
+
+ if (eat_name_chars)
+ {
+ do ++cp ; while ( isalnum(*cp)
+ || (*cp == '-')
+ || (*cp == '_')
+ || (*cp == ':')
+ || (*cp == '.') ) ;
+
+ } ;
+
+ if (*cp != '\0')
+ cmd_fail_item(cmd, "invalid item") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Make <...> types
+ *
+ * Require '\0' terminated <...>, pointing after the '<'.
+ *
+ * Assumes the '>' is present.
+ *
+ * Returns: where processed up to -- pointing at '>' iff OK.
+ *
+ * Supports ranges:
+ *
+ * 9-10 => unsigned values in 32b range -- NO sign
+ *
+ * +9-10 => unsigned value, where '+' is optional
+ * 9-+10 => unsigned value, where '+' is *required*
+ * +9-+10 => same as above
+ *
+ * -9-10 => signed value, where '+' is optional
+ * -9-+10 => signed value, where '+' is *required*
+ *
+ * -9--8 => signed value, where '-' is required (!)
+ *
+ *
+ * +/-9 => -9-+9 -- sign is required.
+ *
+ * In place of decimal number can use 9b -- giving 2^9 - 1.
+ */
+static char*
+cmd_make_item_numeric(cmd_command cmd, cmd_item n, char* cp)
+{
+ if (isdigit(*cp) || (*cp == '+') || (*cp == '-'))
+ {
+ long m ;
+ bool pm ;
+
+ confirm((LONG_MAX > item_max_number) && (LONG_MIN < -item_max_number)) ;
+
+ n->type = item_range ;
+ n->range_sign_allowed = false ;
+ n->range_sign_required = false ;
+
+ if (strncmp(cp, "+/-", 3) == 0)
+ {
+ pm = true ;
+ n->range_sign_required = true ;
+ cp += 2 ; /* step to '-' to get -ve range_min. */
+ }
+ else
+ {
+ pm = false ;
+ n->range_sign_allowed = (*cp == '+') ;
+ } ;
+
+ m = cmd_make_item_number(cmd, n, &cp) ;
+ n->range_min = m ;
+
+ if (pm)
+ m = -m ; /* for range_max */
+
+ else if (*cp == '-')
+ {
+ ++cp ; /* past the '-' */
+
+ n->range_sign_required = (*cp == '+') ;
+
+ m = cmd_make_item_number(cmd, n, &cp) ;
+ }
+
+ else
+ cmd_fail_item(cmd, "badly formed <0-1>") ;
+
+ n->range_max = m ;
+
+ if (n->range_min > n->range_max)
+ cmd_fail_item(cmd, "badly formed <0-1> min > max !") ;
+
+ if ((n->range_sign_required) || (n->range_min < 0))
+ n->range_sign_allowed = true ; /* allowed if required ! */
+ } ;
+
+ return cp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get signed or unsigned value -- process the '9b' form.
+ *
+ */
+static long
+cmd_make_item_number(cmd_command cmd, cmd_item n, char** p_cp)
+{
+ long m ;
+ char* cp ;
+
+ cp = *p_cp ;
+ m = strtol(cp, p_cp, 10) ;
+
+ if ((*p_cp == cp) || (m > item_max_number))
+ cmd_fail_item(cmd, "badly formed or out of range number in <...>") ;
+
+ if (**p_cp == 'b')
+ {
+ long s ;
+
+ ++(*p_cp) ; /* step past 'b' */
+ s = m ;
+ m = labs(m) ;
+ if ((m == 0) || (m > 32))
+ cmd_fail_item(cmd, "out of range number in 9b form in <...>") ;
+
+ m = ((long)1 << m) - 1 ;
+ if (s < 0)
+ m = -m ;
+ } ;
+
+ return m ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare cmd_item items
+ *
+ * Note that command types sort with the larger type value before the smaller.
+ */
+static int
+cmd_cmp_item(const cmd_item* a, const cmd_item* b)
+{
+ if ((*a)->type != (*b)->type)
+ return ((*a)->type > (*b)->type) ? -1 : +1 ; /* descending order */
+ else
+ {
+ if ((*a)->type == item_range)
+ return cmd_cmp_range_items(*a, *b) ;
+ else
+ return strcmp ((*a)->str, (*b)->str);
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare cmd_item item_range items
+ */
+static int
+cmd_cmp_range_items(const cmd_item a, const cmd_item b)
+{
+ int as, bs ;
+
+ if (a->range_min != b->range_min)
+ return (a->range_min < b->range_min) ? -1 : +1 ;
+
+ if (a->range_max != b->range_max)
+ return (a->range_max < b->range_max) ? -1 : +1 ;
+
+ as = a->range_sign_required ? 2 : (a->range_sign_allowed ? 1 : 0) ;
+ bs = b->range_sign_required ? 2 : (b->range_sign_allowed ? 1 : 0) ;
+
+ if (as != bs)
+ return (as < bs) ? -1 : +1 ;
+
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Token objects
+ */
+
+/*------------------------------------------------------------------------------
+ * Make a brand new token object
+ */
+Private cmd_token
+cmd_token_new(void)
+{
+ return XCALLOC(MTYPE_TOKEN, sizeof(struct cmd_token)) ;
+
+ /* Zeroising the new structure sets:
+ *
+ * type = 0 -- cmd_tok_eol
+ * qs = zeroised qstring -- empty string
+ * complete = 0 -- false
+ *
+ * tp = 0
+ * lp = zeroised elstring -- empty string
+ */
+ confirm(cmd_tok_eol == 0) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+ confirm(ELSTRING_INIT_ALL_ZEROS) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty token object and free it.
+ */
+static void
+cmd_token_free(cmd_token t)
+{
+ qs_reset(t->qs, keep_it) ; /* discard body of qstring */
+ XFREE(MTYPE_TOKEN, t) ;
+} ;
+
+/*==============================================================================
+ * Parser object
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a new cmd_parsed object, allocating if required
+ */
+extern cmd_parsed
+cmd_parsed_init_new(cmd_parsed parsed)
{
- token tok ;
+ if (parsed == NULL)
+ parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ;
+ else
+ memset(parsed, 0, sizeof(*parsed)) ;
+
+ /* Zeroising the structure has set:
+ *
+ * parts = 0 -- cleared by cmd_tokenise()
+ * tok_total = 0 -- set by cmd_tokenise()
+ *
+ * elen = 0 -- set by cmd_tokenise()
+ * tsp = 0 -- set by cmd_tokenise()
+ *
+ * cmd = NULL -- no command yet
+ * cnode = 0 -- not set
+ *
+ * num_tokens = 0 -- set by cmd_tokenise()
+ * tokens = all zeros -- empty token vector
+ *
+ * args = all zeros -- empty vector of arguments
+ *
+ * emess = NULL -- no error yet
+ * eloc = 0 -- no error location
+ *
+ * in_pipe = cmd_pipe_none
+ * out_pipe = cmd_pipe_none
+ *
+ * first_in_pipe )
+ * num_in_pipe )
+ * first_do )
+ * num_do )
+ * first_command ) none
+ * num_command )
+ * first_out_pipe )
+ * num_out_pipe )
+ * first_comment )
+ * num_comment )
+ *
+ * cti ) set by cmd_token_position()
+ * rp )
+ *
+ * cmd_v = all zeros -- empty vector of filtered commands
+ * item_v = all zeros -- empty vector of filtered items
+ *
+ * strongest )
+ * best_complete ) set by cmd_filter_prepare()
+ * min_strength )
+ * strict )
+ */
+ confirm(cmd_pipe_none == 0) ;
+ confirm(TOKEN_VECTOR_INIT_ALL_ZEROS) ;
+ confirm(ARG_VECTOR_INIT_ALL_ZEROS) ;
- while ((tok = vector_ream(spare_tokens->body, keep_it)) != NULL)
- qs_reset(tok->qs, keep_it) ;
+ return parsed ;
} ;
/*------------------------------------------------------------------------------
- * Take string and break it into tokens.
+ * Empty out and (if required) free a cmd_parsed object
+ */
+extern cmd_parsed
+cmd_parsed_reset(cmd_parsed parsed, free_keep_b free_structure)
+{
+ if (parsed != NULL)
+ {
+ cmd_token t ;
+
+ /* Give back all the token objects and release vector body */
+ while ((t = vector_ream(parsed->tokens->body, keep_it)) != NULL)
+ cmd_token_free(t) ;
+
+ vector_reset(parsed->args->body, keep_it) ; /* embedded */
+ vector_reset(parsed->cmd_v, keep_it) ; /* embedded */
+ vector_reset(parsed->item_v, keep_it) ; /* embedded */
+
+ if (free_structure)
+ XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */
+ else
+ cmd_parsed_init_new(parsed) ;
+ } ;
+
+ return parsed ;
+} ;
+
+/*==============================================================================
+ * Parsing error handling.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Register a parsing error.
+ *
+ * Takes token in which parsing error was detected, and an offset from the
+ * start of that, for the location of the error. If the offset is not zero,
+ * it must be an offset in the original token (!).
+ *
+ * The message is a simple constant string (!).
+ *
+ * The message will be output as: ..........^ pointing to the location
+ * followed by: % <mess>\n
+ *
+ * (The mess does not need to include the '%' or the '\n'.)
+ *
+ * Sets: parsed->emess
+ * parsed->eloc
+ *
+ * Returns: CMD_ERR_PARSING -- which MUST only be returned if p
+ */
+static cmd_return_code_t
+cmd_parse_error(cmd_parsed parsed, cmd_token t, usize off, const char* mess)
+{
+ parsed->emess = mess ;
+ parsed->eloc = t->tp + off ;
+
+ return CMD_ERR_PARSING ;
+} ;
+
+/*==============================================================================
+ * Lexical level stuff
+ */
+
+/*------------------------------------------------------------------------------
+ * Take elstring and see if it is empty -- only whitespace and/or comment
+ */
+extern bool
+cmd_is_empty(elstring line)
+{
+ cpp_t lp ;
+
+ els_cpp(lp, line) ; /* NULL -> NULL */
+
+ while (lp->p < lp->e)
+ {
+ if ((*lp->p == ' ') || (*lp->p == '\t'))
+ ++lp->p ;
+ else
+ return ((*lp->p == '!') || (*lp->p == '#')) ;
+ } ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reserved characters in a pipe token.
+ *
+ * The characters reserved allow for a large number of possible options to
+ * be attached to the pipe token.
+ *
+ * Wish to allow the pipe token to be followed by file name or command name
+ * without requiring whitespace separation, also do not want to intrude into
+ * quoted or escaped stuff. So limit the characters that are reserved.
+ */
+static inline bool
+cmd_pipe_reserved_char(char ch)
+{
+ return strchr("<|>%&*+-=?", ch) != NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Take elstring and break it into tokens.
*
* Discards leading and trailing ' ' or '\t'.
*
* Expects string to have been preprocessed, if required, to ensure that any
* unwanted control characters have been removed. This code only recognises
- * '\t'.
+ * '\t' and treats it as whitespace.
*
* Anything between '....' is ignored by the tokenizer. NB: this follows the
* shell convention, so '\' is also ignored and there is no way to include "'"
@@ -90,60 +1187,88 @@ cmd_spare_tokens_free(void)
* * tokens are separated by whitespace -- one ' ' or '\t' characters
* The whitespace is discarded.
*
- * * tokens are separated by "separators", which start with any of:
+ * * tokens which start with any of:
*
- * '!', '#', '<', '>' and '|'
+ * '!', '#', '<' and '>'
*
- * which may be followed by one or more characters to form a separator
- * token.
+ * terminate themselves, as follows:
*
* - from '!' or '#' to end of line is a comment token.
*
- * - '<' opt and '<|' opt are separators, where opt is any combination
- * of '+', '*' and '-'.
+ * - '<' followed by pipe_reserved_chars is a token (in_pipe)
*
- * - '>', '>>' and '|' are separators.
+ * - '>' followed by pipe_reserved_chars is a token (out_pipe).
*
- * NB: the tokenization mimics the standard shell which makes the piping stuff
- * straightforward. It's also well known. Apart from the "'" rule, it
- * also seems fine !
+ * See above for pipe_reserved_
*
- * NB: any control characters other than those spotted by isspace() are accepted
- * as part of the current token !
+ * NB: this means that for '!', '#', '<' and '>' to be significant, they
+ * MUST be preceeded by whitespace (or start of line). This ever so
+ * slightly reduces the impact of the new lexical conventions.
*
- * The tokens returned contain all the original characters of the line, except
- * for the removal of '\t' between tokens.
+ * NB: the tokenization roughly mimics the (POSIX) standard shell. The
+ * differences are:
*
- * Returns: the types of all tokens or'd together.
- * returns cmd_tok_null if the line is empty (apart from ' ' and '\t')
- * or if the pointer was NULL.
+ * '|' is *not* a pipe ('>|' is), because '|' is a character in the
+ * regex repertoire.
+ *
+ * '<' and '>' do not terminate a token -- so are only significant
+ * at the start of a token.
+ *
+ * '!' is not a comment for the shell.
+ *
+ * The requirement for whitespace (or start of line) before '#' is
+ * consistent with the shell.
+ *
+ * The handling of '...' follows the standard shell.
+ *
+ * The tokenization does not remove any " ' or \ characters, that is left
+ * for a later stage, where context may affect the handling.
+ *
+ * NB: any control characters other than '\t' are accepted as part of the
+ * current token !
+ *
+ * The tokens returned contain all the original characters of the line, except
+ * for the removal of ' ' and '\t' between tokens and at the end of the line.
*
* Note: all the tokens in the vector have at least one character, and no
* entries are NULL.
*
* NB: it is the callers responsibility to release the token objects in due
* course.
+ *
+ * NB: the elstring containing the line to be tokenised MUST NOT change
+ * until the parsed object is finished with.
+ *
+ * Returns: the types of all tokens or'd together.
+ * returns cmd_tok_null if the line is empty (apart from ' ' and '\t')
+ * or if the elstring was or contained NULL.
+ *
+ * Initialises the parsed object, ready for further parsing:
+ *
+ * Sets: parsed->parts = cmd_parts_none
+ * parsed->num_tokens )
+ * parsed->elen ) per the results
+ * parsed->tsp )
+ * parsed->tokens )
+ *
+ * Note that the num_tokens does not include the cmd_tok_eol on the end.
*/
-extern cmd_token_type_t
-cmd_tokenise(cmd_parsed parsed, const char *line, node_type_t node)
+extern void
+cmd_tokenise(cmd_parsed parsed, qstring line)
{
- const char *cp, *ep ;
+ cpp_t lp ;
+ const char *cp, *tp ;
cmd_token_type_t total ;
+ uint nt ;
- cmd_empty_token_vector(parsed->tokens) ; /* Empty the token vector */
-
- parsed->line = line ;
- parsed->onode = parsed->cnode = node ;
-
- total = cmd_tok_null ; /* nothing yet */
+ total = 0 ; /* nothing yet */
+ nt = 0 ;
- if (line == NULL) /* tolerate NULL */
- return total ;
+ qs_cpp(lp, line) ; /* NULL -> NULL */
- cp = line ;
- ep = cp + strlen(cp) ;
-
- while (cp < ep) /* process to end */
+ cp = lp->p ;
+ tp = cp ;
+ while (cp < lp->e) /* process to end */
{
const char* sp ;
bool end ;
@@ -152,18 +1277,17 @@ cmd_tokenise(cmd_parsed parsed, const char *line, node_type_t node)
if ((*cp == ' ') || (*cp == '\t'))
{
/* skip white-space */
- do { ++cp ; } while ((*cp == ' ') || (*cp == '\t')) ;
-
- if (cp == ep)
+ do
{
- if (total != cmd_tok_null)
- total |= cmd_tok_trailing ;
- break ;
- } ;
+ end = (++cp == lp->e) ;
+ } while (!end && ((*cp == ' ') || (*cp == '\t'))) ;
+
+ if (end)
+ break ;
} ;
- sp = cp ;
end = false ;
+ sp = cp ;
type = cmd_tok_simple ;
do
{
@@ -174,10 +1298,10 @@ cmd_tokenise(cmd_parsed parsed, const char *line, node_type_t node)
end = true ;
break ;
- case '\'': /* proceed to matching '\'' or end */
- type |= cmd_tok_sq ;
+ case '\'': /* proceed to matching ' or end */
++cp ;
- while (cp < ep)
+ type |= cmd_tok_sq ;
+ while (cp < lp->e)
{
if (*cp++ == '\'')
break ;
@@ -185,719 +1309,2785 @@ cmd_tokenise(cmd_parsed parsed, const char *line, node_type_t node)
break ;
case '\\': /* step past escaped character, if any */
- type |= cmd_tok_esc ;
++cp ;
- if (cp < ep)
+ type |= cmd_tok_esc ;
+ if (cp < lp->e)
++cp ;
break ;
- case '"': /* proceed to matching '"' or end... */
- type |= cmd_tok_dq ;
+ case '"': /* proceed to matching " or end... */
++cp ;
- while (cp < ep) /* NB: do not register '\\' separately */
+ type |= cmd_tok_dq ;
+ while (cp < lp->e) /* NB: do not register \ separately */
{
if (*cp++ == '"')
- if (*(cp - 2) != '\\') /* ignore escaped '"' */
+ if (*(cp - 2) != '\\') /* ignore escaped " */
break ;
} ;
break ;
- case '>': /* '>' or '>>' separators. */
- end = true ;
- if (cp == sp) /* if at start of token */
+ case '>': /* '>' special at start */
+ end = (cp == sp) ;
+ ++cp ;
+ if (end) /* if special */
{
- type = cmd_tok_pipe_out ;
- ++cp ;
- if ((cp < ep) && (*cp == '>'))
+ type = cmd_tok_out_pipe ;
+ while ((cp < lp->e) && cmd_pipe_reserved_char(*cp))
++cp ;
} ;
break ;
- case '|': /* '|' separator. */
- end = true ;
- if (cp == sp)
- type = cmd_tok_pipe_out ;
- ++cp ;
- break ;
-
- case '<': /* '<' or '<|' separators. */
- end = true ;
- if (cp == sp)
+ case '<': /* '<' special at start */
+ end = (cp == sp) ;
+ ++cp ;
+ if (end) /* if special */
{
- type = cmd_tok_pipe_in ;
- ++cp ;
- if ((cp < ep) && (*cp == '|'))
- ++cp ;
- if ( (cp < ep) &&
- ((*cp == '+') || (*cp == '-') || (*cp == '*')) )
+ type = cmd_tok_in_pipe ;
+ while ((cp < lp->e) && cmd_pipe_reserved_char(*cp))
++cp ;
} ;
break ;
- case '!': /* '!' and '#' separators. */
+ case '!': /* '!' and '#' special at start */
case '#':
- end = true ;
- if (cp == sp)
+ if ((cp == sp) && (nt == 0))
{
+ end = true ;
type = cmd_tok_comment ;
- cp = ep ;
- } ;
+ cp = lp->e ;
+ }
+ else
+ ++cp ;
break ;
default:
++cp ;
break ;
} ;
- } while (!end && (cp < ep)) ;
+ } while (!end && (cp < lp->e)) ;
- cmd_token_push(parsed->tokens,
- cmd_token_new(type, sp, cp - sp, sp - line)) ;
+ cmd_token_set(parsed->tokens, nt, type, sp, cp - sp, sp - lp->p) ;
+ ++nt ;
+ total |= type ;
+
+ tp = cp ;
} ;
- return total ;
+ /* When we get here, tp points just after last character of last token,
+ * or at start of line if the line is blank (other than whitespace).
+ *
+ * If line is empty (apart from whitespace):
+ *
+ * - the effective length is zero.
+ * - the trailing space count is the number of (leading) spaces.
+ *
+ * If line is not empty:
+ *
+ * - the start position of the first token is the number of leading spaces.
+ * - the trailing space count is the number of spaces after the last
+ * token. (If the last token is comment, this will be zero.)
+ */
+ parsed->parts = cmd_parts_none ;
+ parsed->tok_total = total ;
+ parsed->num_tokens = nt ;
+ parsed->elen = tp - lp->p ;
+ parsed->tsp = lp->e - tp ;
+
+ /* Append an empty end of line token. */
+ cmd_token_set(parsed->tokens, parsed->num_tokens, cmd_tok_eol,
+ lp->e, 0, lp->e - lp->p) ;
} ;
/*------------------------------------------------------------------------------
- * Process token to remove quotes and escapes (if any).
+ * Process in-pipe token and set the required bits in the pipe type word
*
- * Returns: true <=> OK
- * false => invalid escape or incomplete quotes
+ * Known tokens are: < <| <+ <|+
+ */
+static cmd_return_code_t
+cmd_parse_in_pipe(cmd_parsed parsed, cmd_token t)
+{
+ cpp_t p ;
+ bool ok ;
+
+ els_cpp(p, t->ot) ;
+
+ ok = ((p->p < p->e) && (*p->p++ == '<')) ;
+
+ if (ok)
+ {
+ /* First character after '<' may qualify the type of the pipe */
+ parsed->in_pipe = cmd_pipe_file ;
+
+ if (p->p < p->e)
+ {
+ switch (*p->p++)
+ {
+ case '|':
+ parsed->in_pipe = cmd_pipe_shell ;
+ break ;
+
+ default:
+ --p->p ; /* put back */
+ break ;
+ }
+ } ;
+
+ /* Deal with option characters */
+ while (ok && (p->p < p->e))
+ {
+ /* Eat option character, if recognise it. */
+ switch (*p->p++)
+ {
+ case '+': /* reflect command lines */
+ parsed->in_pipe |= cmd_pipe_reflect ;
+ break ;
+
+ default:
+ --p->p ;
+ ok = false ;
+ break ;
+ } ;
+ } ;
+ } ;
+
+ if (!ok)
+ return cmd_parse_error(parsed, t, 0, "invalid 'pipe in'") ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process out-pipe token and set the required bits in the pipe type word
+ *
+ * Known tokens are: > >> >| >*
+ */
+static cmd_return_code_t
+cmd_parse_out_pipe(cmd_parsed parsed, cmd_token t)
+{
+ cpp_t p ;
+ bool ok ;
+
+ els_cpp(p, t->ot) ;
+
+ ok = ((p->p < p->e) && (*p->p++ == '>')) ;
+
+ if (ok)
+ {
+ /* First character after '>' may qualify the type of the pipe */
+ parsed->out_pipe = cmd_pipe_file ;
+
+ if (p->p < p->e)
+ {
+ switch (*p->p++)
+ {
+ case '>':
+ parsed->out_pipe |= cmd_pipe_append ;
+ break ;
+
+ case '|':
+ parsed->out_pipe = cmd_pipe_shell ;
+ break ;
+
+ case '*':
+ parsed->out_pipe = cmd_pipe_dev_null ;
+ break ;
+
+ default:
+ --p->p ; /* put back */
+ break ;
+ }
+ } ;
+
+ /* Could now have options, but presently do not */
+ if (p->p < p->e)
+ {
+ ok = false ;
+ } ;
+ } ;
+
+ if (!ok)
+ return cmd_parse_error(parsed, t, 0, "invalid 'pipe out'") ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If token is incomplete make a copy of it and '\0' terminate.
+ *
+ * If if contains quotes or escapes process those down.
+ *
+ * For Quagga purposes, the following is done:
+ *
+ * inside '...': all characters stand for themselves, except '\t' -> ' ',
+ * and, of course, the terminating '.
+ *
+ * This is just like the shell.
+ *
+ * inside "...": all characters stand for themselves, except '\t' -> ' ',
+ * and, of course, the terminating ", plus the following
+ * \x escapes are processed:
+ *
+ * \" -> " -- so can have " in "...."
+ * \\ -> \ -- so can have \ in "...."
+ * \$ -> $ -- so can have $ in "...."
+ *
+ * outside quotes: all characters stand for themselves, except:
+ *
+ * \sp -> sp -- so can escape the odd space (cf shell)
+ * \tab -> sp -- ditto of tab, but map to space
+ * \? -> ? )
+ * \' -> ' )
+ * \" -> " ) so can escape the odd meta character
+ * \< -> < ) where required or to taste.
+ * \> -> > )
+ * \! -> ! )
+ * \# -> \# )
+ *
+ * NB: with the exception of $ inside of "..." the carefully avoids the
+ * regex meta characters: .*+?^$_ and (|)[-]
+ * and the regex escaped forms of those and the back reference \1 etc.
+ *
+ * $ in "..." is reserved for future use as a variable or other
+ * substitution start.
+ *
+ * Returns: CMD_SUCCESS <=> OK
+ * CMD_ERR_PARSE <=> invalid escape or incomplete quotes
*
* NB: if fails, returns token completed as far as possible.
*/
-extern bool
-cmd_token_do_complete(token t)
+static cmd_return_code_t
+cmd_token_complete(cmd_parsed parsed, cmd_token t)
{
- char *s, *p, *q ;
- char ch ;
- bool ok = true ;
- bool dq = false ;
+ cpp_t p ;
+ pp_t q ;
+ bool dq ;
+ cmd_return_code_t ret ;
+
+ if ((t->type & cmd_tok_incomplete) == 0)
+ return CMD_SUCCESS ; /* Quick out if nothing to do */
- p = s = cmd_token_value(t) ;
- q = p ;
- while (*p != '\0')
+ /* To process quotes etc works from the elstring that points to
+ * the original line, to the qstring.
+ *
+ * For quotes and escapes, the result is always no longer than the
+ * original.
+ */
+ els_cpp_nn(p, t->ot) ; /* original token */
+
+ qs_new_size(t->qs, p->e - p->p) ; /* discard alias & set qs to be
+ big enough for original */
+ qs_pp_nn(q, t->qs) ; /* where to complete token to */
+
+ ret = CMD_SUCCESS ;
+ dq = false ;
+ while (p->p < p->e)
{
- switch (*p)
+ switch (*p->p)
{
+ case '\t':
+ *q->p++ = ' ' ; /* '\t' -> ' ' */
+ ++p->p ;
+ break ;
+
case '\'':
- ++p ; /* skip leading '\'' */
+ ++p->p ; /* skip leading ' */
while (1)
{
- if (*p == '\0')
+ if (p->p == p->e)
{
- ok = false ; /* broken '...' */
+ ret = cmd_parse_error(parsed, t, 0, "missing closing '") ;
break ;
} ;
- if (*p == '\'')
+ if (*p->p == '\'')
{
- ++p ; /* skip trailing '\'' */
+ ++p->p ; /* skip trailing ' */
break ; /* done '....' */
} ;
- *q++ = *p++ ;
+ if (*p->p == '\t')
+ {
+ *q->p++ = ' ' ; /* '\t' -> ' ' */
+ ++p->p ;
+ }
+ else
+ *q->p++ = *p->p++ ; /* rest as is */
} ;
break ;
case '"':
- ++p ; /* skip '"' */
- dq = !dq ;
+ ++p->p ; /* skip " */
+ dq = !dq ; /* switch state */
break ;
case '\\':
- ++p ; /* step past '\\' */
- ch = cmd_token_escape(*p) ;
- if (ch == '\0')
+ *q->p++ = *p->p++ ; /* copy the \ */
+ if (p->p == p->e)
+ ret = cmd_parse_error(parsed, t, 0, "trailing \\") ;
+ else
{
- ok = false ;
- ch = *p ;
- if (ch == '\0')
- ch = '\\' ; /* \ at end is kept */
+ if (dq)
+ {
+ /* inside "...": \", \$ and \\ only */
+ if ((*p->p == '"') || (*p->p == '$') || (*p->p == '\\'))
+ --q->p ; /* strip the \ */
+ }
+ else
+ {
+ /* outside quotes: \sp \tab \< \> \! \# */
+ if ( (*p->p == '\t') || (*p->p == ' ') ||
+ (*p->p == '\'') || (*p->p == '"') ||
+ (*p->p == '<') || (*p->p == '>') ||
+ (*p->p == '!') || (*p->p == '#') )
+ --q->p ; /* strip the \ */
+ } ;
+
+ if (*p->p != '\t')
+ *q->p++ = *p->p++ ;
else
- *q++ = '\\' ; /* otherwise keep \x */
+ {
+ *q->p++ = ' ' ;
+ ++p->p ;
+ } ;
} ;
- *q++ = ch ;
- break ;
+ break ;
default:
- *q++ = *p++ ;
+ *q->p++ = *p->p++ ;
+ break ;
} ;
} ;
- qs_term_here(t->qs, q) ;
+ if (dq)
+ ret = cmd_parse_error(parsed, t, 0, "missing closing \"") ;
- return ok && !dq ;
+ if (ret == CMD_SUCCESS)
+ {
+ *q->p = '\0' ; /* '\0' terminate */
+ qs_set_len_nn(t->qs, q->p - qs_char_nn(t->qs)) ;
+ t->term = true ;
+ }
+ else
+ {
+ qs_set_alias_els(t->qs, t->ot) ;
+ } ;
+
+ return ret ;
}
/*------------------------------------------------------------------------------
- * Return escaped value of \e.
+ * Tokenise the given line and work out where cursor is wrt tokens.
+ *
+ * Looks for first token whose end is at or beyond the cursor position. Note
+ * that:
+ *
+ * * where the cursor is just after the last character of a token, it is at
+ * the end of that token.
+ *
+ * * where there is more than one space between tokens, and the cursor is
+ * after the first space, then it is deemed to be "on" the second token.
+ *
+ * - if there are spaces at the start of the line and the cursor is on
+ * one of those spaces, the it is "on" the first token.
*
- * Everything except '0'..'9', 'A'..'Z', 'a'..'z' can be escaped -- these are
- * reserved for future actual escapes !
+ * - if the line is blank, with zero or more spaces, the cursor is "on"
+ * the eol token.
*
- * Returns '\0' if e == '\0' or if \e is an invalid escape.
+ * Note that where a line ends in a comment, there are no trailing spaces.
+ *
+ * Returns: true <=> "in a special place"
+ *
+ * Is "in a special place" if the cursor is:
+ *
+ * a. in a quoted string of any type
+ * b. in an escape (so immediately after a '\')
+ * c. after the '!' or '#' on a line which consists only of a comment
+ * d. after the first '<' or '>' of the first pipe token on the line
+ *
+ * If NOT "in a special place", will set:
+ *
+ * * parsed->cti -- cursor token index
+ * * parsed->rp -- cursor position relative to the start of token
+ *
+ * Note that the cursor token may be a comment or pipe token, or the eol token
+ * at the end of the line.
*/
-static char
-cmd_token_escape(char e)
+extern bool
+cmd_token_position(cmd_parsed parsed, qstring line)
{
- if ((e < '0') || (e > 'z'))
- return e ;
- return isalpha(e) ? '\0' : e ;
+ cmd_token t ;
+ uint ti ;
+ uint cp, ep ;
+
+ cpp_t p ;
+ const char* q ;
+ const char* e ;
+ bool sq, dq, bs ;
+
+ /* Re-initialise parsed object and tokenise the given line */
+ cmd_tokenise(parsed, line) ;
+
+ /* Get the cursor position */
+ cp = qs_cp_nn(line) ;
+
+ /* Look for the last token whose end is <= cp
+ *
+ * Will position on last, "eol" token -- which is not counted in
+ * parsed->num_tokens -- if is beyond the last real token on the line.
+ */
+ t = NULL ;
+ ti = 0 ;
+ while (1)
+ {
+ t = cmd_token_get(parsed->tokens, ti) ;
+
+ if (cp > t->tp)
+ {
+ /* As soon as we are past '<', '>', '!' or '#' -- return "special" */
+ if ((t->type & ( cmd_tok_in_pipe
+ | cmd_tok_out_pipe | cmd_tok_comment) ) != 0)
+ return true ;
+ } ;
+
+ ep = t->tp + els_len_nn(t->ot) ;
+
+ if ((cp <= ep) || (ti == parsed->num_tokens))
+ break ; /* stop when found token (or at eol) */
+
+ ++ti ;
+ } ;
+
+ parsed->cti = ti ;
+ parsed->ctl = els_len_nn(t->ot) ;
+ parsed->rp = (int)cp - (int)t->tp ;
+
+ /* Arrive with t = token in which cp belongs
+ *
+ * If the token is incomplete, then need to check for "ins" -- unless is
+ * already "ins".
+ */
+ if ((t->type & cmd_tok_incomplete) == 0)
+ return false ;
+
+ /* Scan to see if in '...' or "..." or after '\'. */
+
+ els_cpp_nn(p, t->ot) ; /* original token */
+
+ q = p->p + (cp - t->tp) ; /* position interested in */
+ assert(q > p->p) ;
+
+ dq = false ;
+ sq = false ;
+ bs = false ;
+
+ e = (q <= p->e) ? q : p->e ; /* stop at q or end of token */
+
+ while (p->p < e)
+ {
+ switch (*p->p)
+ {
+ case '\'':
+ if (!dq && !bs) /* ignore ' inside "..." & after \ */
+ sq = !sq ;
+ break ;
+
+ case '\"': /* ignore " inside '...' & after \ */
+ if (!sq && !bs)
+ dq = !dq ;
+ break ;
+
+ case '\\':
+ if (!sq) /* ignore \ inside '...' */
+ bs = !bs ; /* cope with \\ */
+ break ;
+
+ default:
+ break ;
+ } ;
+
+ ++p->p ;
+ } ;
+
+ /* Have scanned to but excluding current character or end of token, whichever
+ * came first.
+ *
+ * If in '...' or "...", then is "ins"
+ * If is immediately after \, then is "ins".
+ */
+ return sq || dq || (bs && (p->e <= q)) ;
} ;
/*==============================================================================
- * Parser object
+ * Match functions.
+ *
+ * Is the given string a, possibly incomplete, value of the required kind ?
*/
+static match_strength_t cmd_ipv4_match(const char* cp, uint prefix) ;
+static match_strength_t cmd_prefix_match(const char* cp, uint prefix) ;
+
/*------------------------------------------------------------------------------
- * Initialise a new cmd_parsed object, allocating if required
+ * Is this an IPv4 Address
+ *
+ * 999.999.999.999 -- each 0..255, no leading zeros, decimal only.
+ *
+ * Returns: mt_no_match -- improperly formed
+ * mt_ipv4_address_partial -- OK as far as it goes (or empty)
+ * mt_ipv4_address_complete -- syntactically complete
*/
-extern cmd_parsed
-cmd_parse_init_new(cmd_parsed parsed)
+static match_type_t
+cmd_ipv4_address_match(cmd_token t)
{
- if (parsed == NULL)
- parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ;
- else
- memset(parsed, 0, sizeof(*parsed)) ;
+ match_strength_t ms ;
- /* Zeroising the structure has set:
- *
- * cmd = NULL -- no command parsed, yet
- * cnode -- no node set, yet
- *
- * do_shortcut -- false
- * onode -- not material (do_shortcut is false)
- *
- * pipes = 0 -- cmd_pipe_none
- */
- confirm(cmd_pipe_none == 0) ;
+ ms = cmd_ipv4_match(cmd_token_make_string(t), 0) ;
- cmd_token_vector_init(parsed->tokens) ;
- cmd_token_vector_init(parsed->read_pipe_tokens) ;
- cmd_token_vector_init(parsed->write_pipe_tokens) ;
- cmd_arg_vector_init(parsed) ;
+ if (ms == ms_var_complete)
+ return mt_ipv4_address_complete ;
+ if (ms == ms_partial)
+ return mt_ipv4_address_partial ;
- return parsed ;
+ return mt_no_match ;
} ;
/*------------------------------------------------------------------------------
- * Empty out and (if required) free a cmd_parsed object
+ * Is this an IPv4 Prefix
+ *
+ * 999.999.999.999/99 -- each 0..255, no leading zeros, decimal only.
+ * and prefix length must be <= 32
+ *
+ * Returns: mt_no_match -- improperly formed
+ * mt_ipv4_prefix_partial -- OK as far as it goes (or empty)
+ * mt_ipv4_prefix_complete -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
*/
-extern cmd_parsed
-cmd_parse_reset(cmd_parsed parsed, bool free_structure)
+static match_type_t
+cmd_ipv4_prefix_match(cmd_token t)
{
- if (parsed != NULL)
+ match_strength_t ms ;
+
+ ms = cmd_ipv4_match(cmd_token_make_string(t), 32) ;
+
+ if (ms == ms_var_complete)
+ return mt_ipv4_prefix_complete ;
+ if (ms == ms_partial)
+ return mt_ipv4_prefix_partial ;
+
+ return mt_no_match ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Address or Prefix:
+ *
+ * 999.999.999.999[/99] -- each 0..255, no leading zeros, decimal only.
+ * and prefix length must be <= n
+ *
+ * Returns: ms_no_match -- improperly formed
+ * ms_partial -- OK as far as it goes (or empty)
+ * ms_var_complete -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
+static match_strength_t
+cmd_ipv4_match(const char* cp, uint prefix)
+{
+ uint nums ;
+
+ for (nums = 0 ; nums < 4 ; ++nums)
{
- cmd_empty_parsed_tokens(parsed) ; /* give back tokens */
- cmd_arg_vector_free(parsed) ; /* give back vector body */
+ if (*cp == '.') /* need a '.' except at start */
+ {
+ if (nums == 0)
+ return ms_no_match ;
- if (free_structure)
- XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */
+ ++cp ; /* step past '.' */
+ }
+ else
+ {
+ if (nums != 0)
+ return (*cp == '\0') ? ms_partial : ms_no_match ;
+ } ;
+
+ /* Collect a decimal number 0..255, no leading zeros.
+ *
+ * Rejects anything other than digits -- including '/' and '.'.
+ *
+ * Accepts '\0' as partial -- which accepts empty strings.
+ */
+ if (*cp == '0')
+ {
+ ++cp ;
+ if (isdigit(*cp))
+ return ms_no_match ; /* reject leading zeros */
+ }
+ else
+ {
+ char* ep ;
+
+ if (isdigit(*cp))
+ {
+ if (strtoul(cp, &ep, 10) <= 255)
+ cp = ep ;
+ else
+ return ms_no_match ; /* reject invalid number */
+ }
+ else
+ return (*cp == '\0') ? ms_partial : ms_no_match ;
+ } ;
+ } ;
+
+ /* Arrive here with 4 numbers */
+
+ if (prefix == 0)
+ return (*cp == '\0') ? ms_var_complete : ms_no_match ;
+ else
+ return cmd_prefix_match(cp, prefix) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Is this a Prefix:
+ *
+ * /99 no leading zeros, decimal only, value must be <= n.
+ *
+ * Arrives here with *cp pointing at where there should be a '/'.
+ *
+ * Returns: ms_no_match -- improperly formed
+ * ms_partial -- OK as far as it goes (or empty).
+ * ms_var_complete -- syntactically complete
+ */
+static match_strength_t
+cmd_prefix_match(const char* cp, uint prefix)
+{
+ if (*cp != '/')
+ return (*cp == '\0') ? ms_partial : ms_no_match ;
+
+ /* OK have '/' and a prefix is now expected. */
+
+ ++cp ; /* step past '/' */
+
+ if (*cp == '\0')
+ return ms_partial ; /* if nothing after '/' */
+
+ if (*cp == '0')
+ ++cp ;
+ else
+ {
+ char* ep ;
+ if (isdigit(*cp) && (strtoul(cp, &ep, 10) <= prefix))
+ cp = ep ;
else
- cmd_parse_init_new(parsed) ;
+ return ms_no_match ; /* reject invalid number */
} ;
- return parsed ;
+ if (*cp != '\0')
+ return ms_no_match ; /* something other than digits after the '/',
+ or leading zero, or number too big */
+
+ return ms_var_complete ;
} ;
-/*==============================================================================
- * Match functions.
+/*------------------------------------------------------------------------------
+ * IPv6 Address and Prefix matching.
+ */
+
+#ifdef HAVE_IPV6
+
+static match_strength_t cmd_ipv6_match(const char* cp, uint prefix) ;
+
+
+
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Address
*
- * Is the given string a, possibly incomplete, value of the required kind ?
+ * h:h:... -- RFC 4291 rules & prefix length must be <= n
+ *
+ * Returns: mt_no_match -- improperly formed
+ * mt_ipv6_address_partial -- OK as far as it goes (or empty)
+ * mt_ipv6_address_complete -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
*/
+static match_type_t
+cmd_ipv6_address_match (cmd_token t)
+{
+ match_strength_t ms ;
+
+ ms = cmd_ipv6_match(cmd_token_make_string(t), 0) ;
+ if (ms == ms_var_complete)
+ return mt_ipv6_address_complete ;
+ if (ms == ms_partial)
+ return mt_ipv6_address_partial ;
+
+ return mt_no_match ;
+} ;
/*------------------------------------------------------------------------------
- * Is this an IPv4 Address:
+ * Is this an IPv6 Prefix
*
- * 999.999.999.999 -- where no part may be > 255
+ * h:h:...[/99] -- RFC 4291 rules & prefix length must be <= 128
*
- * TODO: cmd_ipv4_match() seems to accept leading '.' ?
- * TODO: cmd_ipv4_match() seems to accept leading zeros ?
+ * Returns: mt_no_match -- improperly formed
+ * mt_ipv6_prefix_partial -- OK as far as it goes (or empty)
+ * mt_ipv6_prefix_complete -- syntactically complete
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
*/
-extern match_type_t
-cmd_ipv4_match (const char *str)
+static match_type_t
+cmd_ipv6_prefix_match(cmd_token t)
{
- const char *sp;
- int dots = 0, nums = 0;
- char buf[4];
+ match_strength_t ms ;
+
+ ms = cmd_ipv6_match(cmd_token_make_string(t), 128) ;
+
+ if (ms == ms_var_complete)
+ return mt_ipv6_prefix_complete ;
+ if (ms == ms_partial)
+ return mt_ipv6_prefix_partial ;
- if (str == NULL)
- return partly_match;
+ return mt_no_match ;
+} ;
- for (;;)
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Address or Prefix:
+ *
+ * h:h:...[/99] -- RFC 4291 rules & prefix length must be <= n
+ *
+ * Returns: ms_no_match -- improperly formed
+ * ms_partial -- OK as far as it goes (or empty)
+ * ms_var_complete -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
+static match_strength_t
+cmd_ipv6_match(const char* cp, uint prefix)
+{
+ bool double_colon ;
+ uint nums ;
+
+ double_colon = false ;
+ nums = 0 ;
+
+ /* At start, the first time around the loop... */
+
+ for (nums = 0 ; nums < 8 ; ++nums)
{
- memset (buf, 0, sizeof (buf));
- sp = str;
- while (*str != '\0')
+ const char* sp, * ep ;
+
+ /* After number (nums != 0), or at start.
+ *
+ * Deal with (a) ':', '::' and '::/'.
+ * (b) '/' -- valid only if had '::'.
+ * (c) '\0' -- partial unless have had '::'
+ * (d) if not at start, must have one of the above.
+ */
+ if (*cp == ':')
{
- if (*str == '.')
+ /* (a) ':', '::' and '::/'.
+ *
+ * At start can accept '::', but not ':' (unless '\0' follows).
+ *
+ * If not at start, accept ':' and accept '::' if not already seen.
+ *
+ * After '::' can have the full complement of numbers, or '/' or
+ * '\0' which bring the number part to an end.
+ *
+ * After ':' we accept '\0' but explicitly reject '/' (and we
+ * reject a ':' at the start if not followed by '\0').
+ */
+ ++cp ; /* step past ':' */
+
+ if (*cp == ':')
{
- if (dots >= 3)
- return no_match;
+ /* '::' -- counts as number, can be followed by '/' */
- if (*(str + 1) == '.')
- return no_match;
+ if (double_colon)
+ return ms_no_match ; /* at most one */
- if (*(str + 1) == '\0')
- return partly_match;
+ ++cp ; /* step past '::' */
- dots++;
- break;
+ double_colon = true ;
+ ++nums ; /* counts as a number */
+
+ if ((nums == 8) || (*cp == '/') || (*cp == '\0'))
+ break ; /* no more numbers */
}
- if (!isdigit ((int) *str))
- return no_match;
+ else if (*cp == '\0')
+ return ms_partial ; /* accepts bare ':', inter alia */
- str++;
+ else if ((*cp == '/') || (nums == 0))
+ return ms_no_match ;
+ }
+ else if (*cp == '/')
+ {
+ /* (b) '/' -- valid only if had '::'. */
+ if (double_colon)
+ break ;
+ else
+ return ms_no_match ;
+ }
+ else if (*cp == '\0')
+ {
+ /* (c) '\0' -- partial unless have had '::' */
+ if (double_colon)
+ break ;
+ else
+ return ms_partial ; /* accept empty string, inter alia */
}
+ else if (nums != 0)
+ /* (d) if not at start, must have one of the above. */
+ return ms_no_match ;
+
+ assert(*cp != '\0') ;
+
+ /* Is now at start, or after ':' and is not '\0'.
+ *
+ * Require 1..4 hex digits -- will also accept 1..3 decimals !
+ *
+ * Rejects anything else, including '/' at this stage.
+ */
+ sp = cp ;
+ ep = cp + 4 ;
+ do
+ {
+ if (((*cp >= '0') && (*cp <= '9')) || ((*cp >= 'A') && (*cp <= 'F'))
+ || ((*cp >= 'a') && (*cp <= 'f')))
+ ++cp ;
+ else
+ {
+ if (cp > sp)
+ break ;
+ else
+ return ms_no_match ; /* no digits */
+ }
+ }
+ while (cp < ep) ;
- if (str - sp > 3)
- return no_match;
+ /* Watch out for '.' ! */
- strncpy (buf, sp, str - sp);
- if (atoi (buf) > 255)
- return no_match;
+ if (*cp == '.')
+ {
+ /* Can have IPv4 trailing part, if that would account for the
+ * last two number parts of the IPv6.
+ *
+ * Note that a '.' after something which is not simple decimal
+ * 0..255 will be rejected by cmd_ipv4_match().
+ *
+ * Note also that we pass through the prefix requirement.
+ */
+ if ((nums == 6) || (double_colon && (nums < 6)))
+ return cmd_ipv4_match(sp, prefix) ;
+ else
+ return ms_no_match ;
+ } ;
+ } ;
- nums++;
+ /* Arrives here either because nums == 8, or met '/' or '\0' after '::
+ *
+ * So only get here if have a valid end of the digits part of the IPv6
+ */
+
+ assert((nums == 8) || double_colon) ;
+
+ if (prefix == 0)
+ return (*cp == '\0') ? ms_var_complete : ms_no_match ;
+ else
+ return cmd_prefix_match(cp, prefix) ;
+} ;
+
+
+#endif /* HAVE_IPV6 */
+
+/*------------------------------------------------------------------------------
+ * Is this a decimal number in the allowed range:
+ *
+ * Returns: mt_no_match -- improperly formed or empty
+ * mt_range_partial -- OK as far as it went (or empty string)
+ * mt_range_complete -- syntactically complete
+ */
+static match_type_t
+cmd_range_match (cmd_item item, cmd_token t)
+{
+ const char* cp, * dp ;
+ char *ep ;
+ int base ;
+ long val;
+
+ confirm((LONG_MAX > item_max_number) && (LONG_MIN < -item_max_number)) ;
+
+ cp = cmd_token_make_string(t) ;
+
+ /* Worry about any sign */
+
+ dp = cp ;
+
+ if ((*cp == '-') || (*cp == '+'))
+ {
+ if (!item->range_sign_allowed)
+ return mt_no_match ; /* reject '-' or '+' */
+
+ ++dp ; /* step to digit */
+ }
+ else
+ {
+ if (item->range_sign_required)
+ return mt_no_match ;
+ } ;
+
+ /* Worry about leading digits and hex, and no digits at all */
+
+ base = 10 ; /* by default. */
+
+ if (*dp == '\0')
+ return mt_range_partial ; /* accepts empty string, inter alia */
- if (*str == '\0')
- break;
+ else if (*dp == '0')
+ {
+ ++dp ; /* step past zero */
+
+ if (*dp != '\0')
+ {
+ /* No leading zeros and no stinking octal -- but allow hex */
+ if ((*dp != 'x') && (*dp != 'X'))
+ return mt_no_match ;
- str++;
+ ++dp ; /* step past 'x' or 'X' */
+ base = 16 ;
+
+ if (*dp == '\0')
+ return mt_range_partial ;
+ } ;
}
- if (nums < 4)
- return partly_match;
+ else if (!isdigit(*dp))
+ return mt_no_match ;
- return exact_match;
-}
+ /* The string starts with digit, possibly preceded by sign, and possibly
+ * an 'x' or 'X' with at least 1 further character.
+ */
+ val = strtol(cp, &ep, base) ;
+ if (*ep != '\0')
+ return mt_no_match ;
+
+ /* Is the result in range ? */
+
+ if (val >= item->range_min && val <= item->range_max)
+ return mt_range_complete ; /* on the money */
+
+ /* Want to return mt_range_partial iff adding digits might make
+ * an in range value.
+ *
+ * If val is < 0, then adding digits makes it smaller.
+ * If val is == 0, not allowed to add digits.
+ * If val is > 0, then adding digits makes it bigger.
+ */
+ if (val < item->range_min)
+ {
+ /* Is less than minimum, so partial match if can get bigger. */
+ return (val > 0) ? mt_range_partial : mt_no_match ;
+ }
+ else
+ {
+ /* Is more than maximum, so partial match if can get smaller. */
+ return (val < 0) ? mt_range_partial : mt_no_match ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Command "filtering".
+ *
+ * The command parsing process starts with a (shallow) copy of the cmd_vector
+ * entry for the current "node".
+ *
+ * So cmd_v contains pointers to struct cmd_command values. When match fails,
+ * the pointer is set NULL -- so parsing is a process of reducing the cmd_v
+ * down to just the entries that match.
+ *
+ * Each cmd_command has a vector "items", which contains an entry for each
+ * "token" position. That entry is a vector containing the possible values at
+ * that position.
+ */
+
+static int cmd_item_filter(cmd_parsed parsed, cmd_item item, cmd_token t) ;
/*------------------------------------------------------------------------------
- * Is this an IPv4 Prefix:
+ * Prepare to filter commands in the node being parsed in.
*
- * 999.999.999.999/99 -- where no part may be > 255,
- * and prefix length may not be > 32
+ * The execute option turns off all partial matching -- so will not match, say,
+ * 222 as a possible IP address ! This means that the result of the filter
+ * operation will be executable command(s), only.
*
- * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ?
- * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ?
+ * The execute option also pre-filters the command vector to discard all
+ * commands which are too short or too long to match the current line.
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
+ * The strict option turns off partial matching of keywords, so only complete
+ * keywords will do. This is used for the configuration file, so that new
+ * commands can be added !
*
- * NB: partly_match is returned for anything valid before the '/', but which
- * has no '/' or no number after the '/'.
+ * Returns: number of commands which may match to.
+ */
+static uint
+cmd_filter_prepare(cmd_parsed parsed, cmd_parse_type_t type)
+{
+ vector src_v ;
+ uint ci ;
+ uint nct ;
+
+ bool execution = (type & cmd_parse_execution) != 0 ;
+ bool strict = (type & cmd_parse_strict) != 0 ;
+
+ /* get commands for the current node */
+ src_v = ((cmd_node)vector_get_item(node_vector, parsed->cnode))
+ ->cmd_vector ;
+
+ assert(src_v != NULL) ; /* invalid parsed->cnode ?? */
+
+ /* empty the working commands vector, making sure big enough for the current
+ * node's commands.
+ *
+ * Note that the cmd_v lives for as long as the parsed object, so will
+ * grow over time to accommodate what is required.
+ */
+ vector_re_init(parsed->cmd_v, vector_length(src_v)) ;
+
+ /* Filter out commands which are too short.
+ *
+ * For execution, can filter out commands which are too long.
+ */
+ nct = parsed->num_command ;
+
+ for (ci = 0 ; ci < vector_length(src_v) ; ++ci)
+ {
+ cmd_command cmd ;
+
+ cmd = vector_get_item(src_v, ci) ;
+
+ if ( cmd->nt_max < nct)
+ continue ; /* ignore if too short */
+
+ if ((cmd->nt_min > nct) && execution)
+ continue ; /* ignore if too long */
+
+ vector_push_item(parsed->cmd_v, cmd) ;
+ } ;
+
+ /* Other preparation for filtering
+ *
+ * The min_strength is set according to whether is for execution. The
+ * strongest match is set from that. This means that any match which does
+ * not meet the minimum criterion is automatically excluded.
+ */
+
+ parsed->min_strength = execution ? ms_min_execute : ms_min_parse ;
+ parsed->strict = strict ;
+
+ parsed->strongest = parsed->min_strength ;
+ parsed->best_complete = mt_no_match ;
+
+ return vector_length(parsed->cmd_v) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Filter set commands by matching items against the given token.
+ *
+ * Takes: parsed -- cmd_parsed object, previously prepared by
+ * cmd_prepare_fiter().
+ * ii -- item index (0 == first)
+ * keep_items -- how to filter
+ *
+ * The item index is the index wrt the start of the commands being matched,
+ * not the index of the token in the command line.
+ *
+ * Keywords must match strictly or partly, depending on the filtering state
+ * in the parsed object.
+ *
+ * Variables must match completely if filtering for execution, otherwise may
+ * match partially.
+ *
+ * Returns: the number of items matched.
+ *
+ * NB: when filtering for execution, will not accept any partial matches for
+ * variables (but may accept partial matches for keywords). So, once the
+ * last token has been filtered against, the number of items matched gives
+ * the number of unambiguous commands, with complete values, that the line
+ * matches.
*/
-extern match_type_t
-cmd_ipv4_prefix_match (const char *str)
+static uint
+cmd_filter(cmd_parsed parsed, uint ii, bool keep_items)
{
- const char *sp;
- int dots = 0;
- char buf[4];
+ uint ci ; /* command index -- in cmd_v */
+ uint ti ; /* token index in command line */
+ uint c_keep ;
+ uint i_keep ;
+ cmd_token t ;
+
+ /* Reset the filtering state */
+
+ parsed->strongest = parsed->min_strength ;
+ parsed->best_complete = mt_no_match ;
+
+ /* If we are keeping the items which are matched, set the item_v
+ * empty and guess that may get one item per command.
+ *
+ * Note that the item_v lives for as long as the parsed object, so will
+ * grow over time to accommodate what is required.
+ */
+ if (keep_items)
+ vector_re_init(parsed->item_v, vector_length(parsed->cmd_v)) ;
- if (str == NULL)
- return partly_match;
+ /* Work down the cmd_v, attempting to match cmd_items against cmd_tokens.
+ *
+ * Keep in the cmd_v the commands for which we get a match.
+ *
+ * At the same time, if required, keep in the item_v all the items which have
+ * matched.
+ */
+ if (ii < parsed->num_command)
+ ti = parsed->first_command + ii ; /* map to token index */
+ else
+ {
+ /* special case of filtering against the empty token at the end of
+ * the command line -- this is for command line help stuff.
+ *
+ * Note that there may be out_pipe or even comment tokens in between,
+ * so we here set the ti to the trailing eol token.
+ */
+ assert(ii == parsed->num_command) ;
+ ti = parsed->num_tokens ;
+ }
+ t = cmd_token_get(parsed->tokens, ti) ;
- for (;;)
+ c_keep = 0 ;
+ i_keep = 0 ;
+
+ if (keep_items)
+ vector_re_init(parsed->item_v, vector_length(parsed->cmd_v)) ;
+
+ for (ci = 0; ci < vector_length(parsed->cmd_v); ci++)
{
- memset (buf, 0, sizeof (buf));
- sp = str;
- while (*str != '\0' && *str != '/')
+ cmd_command cmd ;
+ cmd_item item ;
+ int best ;
+
+ cmd = vector_get_item(parsed->cmd_v, ci) ;
+
+ if (ii < cmd->nt)
+ item = vector_get_item(cmd->items, ii) ;
+
+ else if (ii == cmd->nt_max)
+ item = &eol_item ; /* match at end of line */
+
+ else if (ii > cmd->nt_max)
+ continue ; /* discard commands we are beyond, now */
+
+ else /* cmd->nt < cmd->nt_max <=> vararg. */
+ {
+ /* It is elsewhere arranged that a vararg is always the last
+ * item for a given command.
+ *
+ * We cope with all tokens from the first vararg onwards by matching
+ * them all against the vararg. Inter alia this allows for a
+ * vararg to check each argument in turn -- iff feel like doing
+ * that.
+ */
+ item = cmd->vararg ;
+
+ /* Must have something and must now only check against the varag.
+ * (Not anything else in the (...) if vararg was in one !)
+ */
+ assert((item != NULL) && (item->next == NULL)) ;
+ } ;
+
+ /* See if get any sort of match at current position */
+ best = -1 ;
+ while (item != NULL)
{
- if (*str == '.')
+ int ret ;
+ ret = cmd_item_filter(parsed, item, t) ;
+
+ if (ret >= 0)
{
- if (dots == 3)
- return no_match;
+ if (ret > 0)
+ i_keep = 0 ;
- if (*(str + 1) == '.' || *(str + 1) == '/')
- return no_match;
+ if (keep_items)
+ vector_set_item(parsed->item_v, i_keep++, item) ;
+ else
+ ++i_keep ;
+ } ;
- if (*(str + 1) == '\0')
- return partly_match;
+ if (ret > best)
+ best = ret ;
- dots++;
- break;
- }
+ item = item->next ;
+ } ;
- if (!isdigit ((int) *str))
- return no_match;
+ /* Keep if had a match */
+ if (best >= 0)
+ {
+ if (best > 0)
+ c_keep = 0 ; /* better than all the rest */
- str++;
- }
+ vector_set_item(parsed->cmd_v, c_keep++, cmd) ;
+ } ;
+ } ;
+
+ if (keep_items)
+ vector_set_length(parsed->item_v, i_keep) ; /* discard what did not keep */
- if (str - sp > 3)
- return no_match;
+ vector_set_length(parsed->cmd_v, c_keep) ; /* discard what did not keep */
- strncpy (buf, sp, str - sp);
- if (atoi (buf) > 255)
- return no_match;
+ return i_keep ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Filter given item, in given parsed state, attempting to match given token.
+ *
+ * Update the parsed state if get a better match.
+ *
+ * Returns: < 0 => is not as good )
+ * == 0 => equally good ) compared to best match to date.
+ * > 0 => better than )
+ *
+ * NB: the matching functions will partially match an empty string.
+ *
+ * The ms_anything types of item, will match an empty string.
+ */
+static int
+cmd_item_filter(cmd_parsed parsed, cmd_item item, cmd_token t)
+{
+ match_strength_t ms ;
+ match_type_t mt ;
+ int cw ;
- if (dots == 3)
+ /* Ignore item if the best we can hope for is not as strong as what we
+ * have already.
+ */
+ mt = item_best_match(item->type) ;
+ ms = match_match_strength(mt) ;
+
+ if ((mt < parsed->best_complete) || (ms < parsed->strongest))
+ return -1 ; /* cannot even be as good */
+
+ /* Bang the rocks together to get match type
+ *
+ * TODO: do we need mt_xxx_partial etc ?
+ */
+ if (t->type == cmd_tok_eol)
+ {
+// mt = (item->type == item_eol) ? mt_eol : mt_eol_partial ;
+ mt = mt_eol_partial ;
+ }
+ else
+ {
+ switch (item->type)
+ {
+ case item_null:
+ zabort("invalid item_null") ;
+ break ;
+
+ case item_eol:
+ break ;
+
+ case item_keyword:
+ cw = els_cmp_word(t->ot, item->str) ;
+
+ if (cw > 0) /* nope */
+ mt = mt_no_match ;
+ else if (cw == 0) /* exact match */
+ mt = mt_keyword_complete ;
+ else /* partial match */
+ mt = parsed->strict ? mt_no_match
+ : mt_keyword_incomplete ;
+ break ;
+
+ case item_range:
+ mt = cmd_range_match(item, t) ;
+ break ;
+
+ case item_ipv4_address:
+ mt = cmd_ipv4_address_match(t) ;
+ break ;
+
+ case item_ipv4_prefix:
+ mt = cmd_ipv4_prefix_match(t) ;
+ break ;
+
+ case item_ipv6_address:
+ #ifdef HAVE_IPV6
+ mt = cmd_ipv6_address_match(t) ;
+ #endif /* HAVE_IPV6 */
+ break ;
+
+ case item_ipv6_prefix:
+ #ifdef HAVE_IPV6
+ mt = cmd_ipv6_prefix_match(t) ;
+ #endif /* HAVE_IPV6 */
+ break ;
+
+ case item_word:
+ mt = mt_word_match ;
+ break ;
+
+ case item_vararg:
+ mt = mt_vararg_match ;
+ break ;
+
+ case item_option_word:
+ mt = mt_option_word_match ;
+ break ;
+
+ default:
+ zabort("unknown item type") ;
+ } ;
+ } ;
+
+ /* Easy if did not match at all */
+ if (mt == mt_no_match)
+ return -1 ;
+
+ /* Is what we got worse, as good or better ?
+ *
+ * Update parsed to suit and return the news.
+ *
+ * Note that parsed->best_complete will be ms_no_match until parsed->strongest
+ * is set to ms_var_complete.
+ */
+ ms = match_match_strength(mt) ;
+
+ if (ms < parsed->strongest)
+ return -1 ;
+
+ if (ms == parsed->strongest)
+ return 0 ;
+
+ parsed->strongest = ms ;
+ return +1 ;
+} ;
+
+/*==============================================================================
+ * Parsing of command lines
+ */
+
+/*------------------------------------------------------------------------------
+ * Parse a command in the given "node", if possible, ready for execution.
+ *
+ * If 'exact': use cmd_filter_by_string()
+ * otherwise: use cmd_filter_by_completion()
+ *
+ * If 'do': see if there is a 'do' at the front and proceed accordingly.
+ *
+ * If 'tree': move up the node tree to find command if not found in the
+ * current node.
+ */
+
+static cmd_return_code_t cmd_parse_phase_one(cmd_parsed parsed,
+ cmd_parse_type_t type, node_type_t node) ;
+static cmd_return_code_t cmd_parse_phase_one_b(cmd_parsed parsed, uint nt) ;
+static cmd_return_code_t cmd_parse_phase_two(cmd_parsed parsed,
+ cmd_parse_type_t type) ;
+
+/*------------------------------------------------------------------------------
+ * Parse a command in the given "node", or (if required) any of its ancestors.
+ *
+ * Requires command line to have been tokenised.
+ *
+ * Returns: CMD_SUCCESS => successfully parsed command, and the result is
+ * in the given parsed structure, ready for execution.
+ *
+ * NB: parsed->cnode may not be the incoming node.
+ *
+ * NB: parsed->parts is what was found
+ *
+ * NB: parsed->cmd->daemon => daemon
+ *
+ * CMD_EMPTY => line is empty, except perhaps for comment
+ * (iff parsing for execution)
+ *
+ * CMD_ERR_INCOMPLETE => "do" and nothing more
+ * (iff parsing for execution)
+ *
+ * CMD_SUCCESS_DAEMON => parsed successfully. Something for vtysh ??
+ *
+ * CMD_ERR_NO_MATCH => could find nothing to match the command
+ * line.
+ *
+ * CMD_ERR_AMBIGUOUS => found 2 or more possible matches.
+ * Or, if not parsing for execution, there
+ * were no command tokens.
+ *
+ * CMD_ERR_PARSING => something wrong ! See parsed->emess.
+ *
+ * NB: unless cmd_parse_no_tree, may have tried any ancestor nodes. Returns
+ * with parsed->cnode with node last tried.
+ *
+ * NB: unless cmd_parse_no_do, will have taken a leading "do", and pushed the
+ * parsed->cnode to ENABLE_NODE (if in MIN_DO_SHORTCUT_NODE or higher).
+ *
+ * NB: the command line MUST be preserved until the parsed command is no
+ * longer required -- no copy is made.
+ *
+ * NB: expects to have free run of everything in the vty structure (except
+ * the contents of the vty_io sub-structure) until the command completes.
+ *
+ * See elsewhere for description of parsed structure.
+ */
+extern cmd_return_code_t
+cmd_parse_command(cmd_parsed parsed, node_type_t node, cmd_parse_type_t type)
+{
+ cmd_return_code_t ret ;
+
+ /* Level 1 parsing
+ *
+ * Break down the tokens into:
+ *
+ * 1) in-pipe or command (with or without "do")
+ * 2) possible out-pipe
+ * 3) possible comment
+ *
+ * Complete any tokens which contain quotes and/or escapes.
+ *
+ * If there is a command then:
+ *
+ * - sort out any "do" and cut from start of command.
+ * - set parsed->cnode (from given node or according to "do")
+ * - set parsed->cmd = NULL
+ * - empty the argv vector
+ *
+ * Note that cmd_parse_phase_one only returns CMD_SUCCESS or CMD_ERR_PARSING.
+ */
+ ret = cmd_parse_phase_one(parsed, type, node) ;
+ if (ret != CMD_SUCCESS)
+ {
+ assert(ret == CMD_ERR_PARSING) ; /* no other error at this point */
+ return ret ;
+ } ;
+
+ /* If no command tokens, and is parsing for execution, then we are done...
+ * but watch out for bare "do"
+ */
+ if (((parsed->parts & cmd_part_command) == 0) &&
+ ((type & cmd_parse_execution) != 0))
+ {
+ if ((parsed->parts & ~cmd_part_comment) == cmd_parts_none)
+ return CMD_EMPTY ; /* accept empty */
+
+ if ((parsed->parts & cmd_part_do) != 0)
+ return CMD_ERR_INCOMPLETE ; /* reject "do" alone */
+
+ return CMD_SUCCESS ; /* accept pipes */
+ } ;
+
+ /* Level 2 parsing
+ *
+ * Try in the current node and then in parent nodes, if can.
+ *
+ * Cannot move up the node tree if is already at CONFIG_NODE or below.
+ * Note that "do" pushes us to the ENABLE_NODE -- which is below the
+ * CONFIG_NODE.
+ *
+ * Note that when not parsing for execution, may get here with no command
+ * tokens at all -- in which case cmd_parse_phase_two() will return
+ * CMD_ERR_AMBIGUOUS.
+ *
+ * Note that cmd_parse_phase_two only returns CMD_SUCCESS, CMD_ERR_NO_MATCH
+ * or CMD_ERR_AMBIGUOUS.
+ */
+ while (1)
+ {
+ node_type_t pnode ;
+
+ ret = cmd_parse_phase_two(parsed, type) ;
+
+ if (ret == CMD_SUCCESS)
+ break ;
+
+ if (ret != CMD_ERR_NO_MATCH)
+ return ret ;
+
+ confirm(ENABLE_NODE < CONFIG_NODE) ;
+
+ if (((type & cmd_parse_no_tree) != 0) || (parsed->cnode <= CONFIG_NODE))
+ return CMD_ERR_NO_MATCH ;
+
+ pnode = cmd_node_parent(parsed->cnode) ;
+
+ if (pnode == parsed->cnode)
+ return CMD_ERR_NO_MATCH ; /* done if no parent node. */
+
+ parsed->cnode = pnode ;
+ } ;
+
+ /* Parsed successfully.
+ *
+ * If for execution, fill the arg_vector
+ *
+ * The arg_vector is an array of pointers to '\0' terminated strings, which
+ * are pointers to the relevant tokens' qstring bodies.
+ */
+
+ if ((type & cmd_parse_execution) != 0)
+ {
+ uint ti ;
+
+ cmd_arg_vector_empty(parsed) ;
+
+ for (ti = 0; ti < parsed->num_command ; ti++)
{
- if (*str == '/')
+ bool take ;
+
+ if (ti < parsed->cmd->nt_min)
{
- if (*(str + 1) == '\0')
- return partly_match;
+ cmd_item item = vector_get_item (parsed->cmd->items, ti);
- str++;
- break;
+ take = item->arg ; /* follow item */
}
- else if (*str == '\0')
- return partly_match;
- }
+ else
+ take = true ; /* option or vararg token */
- if (*str == '\0')
- return partly_match;
+ if (take)
+ {
+ cmd_token t ;
+ t = cmd_token_get(parsed->tokens, parsed->first_command + ti) ;
+ cmd_arg_vector_push(parsed, cmd_token_make_string(t)) ;
+ } ;
+ } ;
+ } ;
- str++;
- }
+ /* Return appropriate form of success */
+ return parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
+ : CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Phase 1 of command parsing
+ *
+ * Scan the tokens to break them up into the sections:
+ *
+ * - in-pipe -- '<' etc up to '>' or comment
+ * - do -- leading 'do' on the command (empty if in-pipe)
+ * - command -- actual command (empty if in-pipe)
+ * - out-pipe -- '>' etc up to comment
+ * - comment -- '!' or '#' onwards
+ *
+ * Requires line to have been tokenised -- cmd_tokenise().
+ *
+ * Returns: CMD_SUCCESS -- all is well
+ * CMD_ERR_PARSING -- parsing error -- malformed or misplaced pipe
+ * -- malformed quotes/escapes
+ */
+static cmd_return_code_t
+cmd_parse_phase_one(cmd_parsed parsed, cmd_parse_type_t type, node_type_t node)
+{
+ uint nt = parsed->num_tokens ;
- sp = str;
- while (*str != '\0')
+ /* Set command and parsing entries */
+ parsed->cnode = node ;
+ parsed->cmd = NULL ;
+
+ /* pick off any comment */
+ if ((parsed->tok_total & cmd_tok_comment) != 0)
{
- if (!isdigit ((int) *str))
- return no_match;
+ parsed->num_comment = 1 ;
+ parsed->first_comment = --nt ; /* implicitly the last */
+ parsed->tok_total ^= cmd_tok_comment ;
+ parsed->parts |= cmd_part_comment ;
+ } ;
- str++;
+ /* If this is not a simple line need to do some extra work:
+ *
+ * - identify and check any pipe items
+ * -
+ */
+ if (parsed->tok_total == cmd_tok_simple)
+ {
+ /* All tokens are simple and there is at least one */
+ parsed->first_command = 0 ;
+ parsed->num_command = nt ;
+ parsed->parts |= cmd_part_command ;
}
+ else if (parsed->tok_total != 0)
+ {
+ /* There is at least one not-simple cmd_token. */
+ cmd_return_code_t ret ;
+ ret = cmd_parse_phase_one_b(parsed, nt) ;
- if (atoi (sp) > 32)
- return no_match;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
- return exact_match;
-}
+ /* If have a have a 'do' at the front, account for it */
+ if ((parsed->parts & cmd_part_command) != 0)
+ {
+ /* Have a command -- worry about "do" if allowed */
+ if (((type & cmd_parse_no_do) == 0) && (node >= MIN_DO_SHORTCUT_NODE))
+ {
+ cmd_token t ;
+ t = cmd_token_get(parsed->tokens, parsed->first_command) ;
+ if (els_len_nn(t->ot) == 2)
+ {
+ const char* p = els_body_nn(t->ot) ;
+ if ((*p == 'd') && (*(p+1) == 'o'))
+ {
+ node = ENABLE_NODE ; /* change to this node */
+
+ parsed->num_do = 1 ;
+ parsed->first_do = parsed->first_command ;
+ --parsed->num_command ;
+ ++parsed->first_command ;
+ parsed->parts |= cmd_part_do ;
+ /* If have *only* the "do", we don't have a command */
+ if (parsed->num_command == 0)
+ parsed->parts ^= cmd_part_command ;
+ } ;
+ } ;
+ } ;
+ } ;
+
+ return CMD_SUCCESS ;
+} ;
/*------------------------------------------------------------------------------
- * Is this an IPv6 Address:
+ * Phase 1b of command parsing
+ *
+ * Tokeniser found at least one of:
+ *
+ * - in pipe token
+ * - out pipe token
+ * - token with quotes or escapes
*
- * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ?
+ * Deal with all of those, verifying the syntax of any pipes and completing
+ * any tokens with quotes etc.
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
+ * Update the "parts" and the relevant first/num values.
+ *
+ * Returns: CMD_SUCCESS -- all is well
+ * CMD_ERR_PARSING -- parsing error -- malformed or misplaced pipe
+ * -- malformed quotes/escapes
*/
+static cmd_return_code_t
+cmd_parse_phase_one_b(cmd_parsed parsed, uint nt)
+{
+ cmd_return_code_t ret ;
+ cmd_token t ;
+ cmd_parts_t parts ;
+ uint i, n ;
+ uint* pn ;
-#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
-#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
-#define STATE_START 1
-#define STATE_COLON 2
-#define STATE_DOUBLE 3
-#define STATE_ADDR 4
-#define STATE_DOT 5
-#define STATE_SLASH 6
-#define STATE_MASK 7
+ parts = cmd_parts_none ; /* no parts yet */
+ n = 0 ; /* no tokens in current part */
+ pn = NULL ; /* no current part */
-#ifdef HAVE_IPV6
+ for (i = 0 ; i < nt ; ++i)
+ {
+ t = cmd_token_get(parsed->tokens, i) ;
-extern match_type_t
-cmd_ipv6_match (const char *str)
-{
- int state = STATE_START;
- int colons = 0, nums = 0, double_colon = 0;
- const char *sp = NULL;
- struct sockaddr_in6 sin6_dummy;
- int ret;
+ if ((t->type & cmd_tok_simple) != 0)
+ {
+ if (parts == cmd_parts_none)
+ {
+ parts = cmd_part_command ;
+ parsed->first_command = i ;
+ pn = &parsed->num_command ;
+ n = 0 ;
+ } ;
+ } ;
- if (str == NULL)
- return partly_match;
+ if ((t->type & cmd_tok_in_pipe) != 0)
+ {
+ if (parts != cmd_parts_none)
+ return cmd_parse_error(parsed, t, 0, "unexpected 'pipe in'") ;
- if (strspn (str, IPV6_ADDR_STR) != strlen (str))
- return no_match;
+ ret = cmd_parse_in_pipe(parsed, t) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
- /* use inet_pton that has a better support,
- * for example inet_pton can support the automatic addresses:
- * ::1.2.3.4
- */
- ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
+ parts = cmd_part_in_pipe ;
+ parsed->first_in_pipe = i ;
+ pn = &parsed->num_in_pipe ;
+ n = 0 ;
+ } ;
+
+ if ((t->type & cmd_tok_out_pipe) != 0)
+ {
+ if ((parts == cmd_parts_none) || ((parts & cmd_part_out_pipe) != 0))
+ return cmd_parse_error(parsed, t, 0, "unexpected 'pipe out'") ;
+
+ ret = cmd_parse_out_pipe(parsed, t) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+
+ *pn = n ; /* set number of in-pipe/cmd */
+
+ parts |= cmd_part_out_pipe ;
+ parsed->first_out_pipe = i ;
+ pn = &parsed->num_out_pipe ;
+ n = 0 ;
+ } ;
+
+ assert(parts != cmd_parts_none) ; /* dealt with all token types */
+
+ if ((t->type & cmd_tok_incomplete) != 0)
+ {
+ ret = cmd_token_complete(parsed, t) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
+
+ ++n ; /* count up tokens */
+ } ;
- if (ret == 1)
- return exact_match;
+ if (pn != NULL)
+ *pn = n ; /* number in last phase */
- while (*str != '\0')
+ /* If have an in-pipe or an out-pipe, worry about the number of
+ * arguments
+ */
+ if ((parts & cmd_parts_pipe) != 0)
{
- switch (state)
+ const char* msg = NULL ;
+ uint i ;
+ bool e ;
+
+ /* If there is an in_pipe part, check number of arguments */
+ if ((msg == NULL) && ((parts & cmd_part_in_pipe) != 0))
{
- case STATE_START:
- if (*str == ':')
+ assert(parsed->num_in_pipe > 0) ;
+
+ if (((parsed->in_pipe & cmd_pipe_file) != 0)
+ && (parsed->num_in_pipe != 2))
{
- if (*(str + 1) != ':' && *(str + 1) != '\0')
- return no_match;
- colons--;
- state = STATE_COLON;
- }
- else
+ if (parsed->num_in_pipe == 1)
+ {
+ i = parsed->first_in_pipe ;
+ e = true ;
+ msg = "requires file" ;
+ }
+ else
+ {
+ i = parsed->first_in_pipe + 2 ;
+ e = false ;
+ msg = "expects file only" ;
+ } ;
+ } ;
+
+ if (((parsed->in_pipe & cmd_pipe_shell) != 0)
+ && (parsed->num_in_pipe < 2))
{
- sp = str;
- state = STATE_ADDR;
- }
+ i = parsed->first_in_pipe ;
+ e = true ;
+ msg = "requires shell command" ;
+ } ;
+ } ;
- continue;
- case STATE_COLON:
- colons++;
- if (*(str + 1) == ':')
- state = STATE_DOUBLE;
- else
+ /* If there is an out_pipe part, check the number of arguments */
+ if ((msg == NULL) && ((parts & cmd_part_out_pipe) != 0))
+ {
+ assert(parsed->num_out_pipe > 0) ;
+
+ if (((parsed->out_pipe & cmd_pipe_file) != 0)
+ && (parsed->num_out_pipe != 2))
{
- sp = str + 1;
- state = STATE_ADDR;
- }
- break;
- case STATE_DOUBLE:
- if (double_colon)
- return no_match;
+ if (parsed->num_out_pipe == 1)
+ {
+ i = parsed->first_out_pipe ;
+ e = true ;
+ msg = "requires file" ;
+ }
+ else
+ {
+ i = parsed->first_out_pipe + 2 ;
+ e = false ;
+ msg = "expects file only" ;
+ } ;
+ } ;
- if (*(str + 1) == ':')
- return no_match;
- else
+ if (((parsed->out_pipe & cmd_pipe_shell) != 0)
+ && (parsed->num_out_pipe < 2))
{
- if (*(str + 1) != '\0')
- colons++;
- sp = str + 1;
- state = STATE_ADDR;
- }
+ assert(parsed->num_out_pipe > 0) ;
+
+ i = parsed->first_out_pipe ;
+ e = true ;
+ msg = "requires shell command" ;
+ } ;
+ } ;
+
+ if (msg != NULL)
+ {
+ t = cmd_token_get(parsed->tokens, i) ;
+ return cmd_parse_error(parsed, t, e ? els_len_nn(t->ot) : 0, msg) ;
+ } ;
+ } ;
+
+ /* It's OK -- so record the parts found and return success */
+ parsed->parts |= parts ;
+
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Phase 2 of command parsing -- parsing for execution.
+ *
+ * Assumes phase 1 completed successfully.
+ *
+ * Note that if parsed->num_command == 0, will have constructed a cmd_v, with
+ * all possible commands in it (depending on cmd_parse_execution).
+ *
+ * Returns: CMD_SUCCESS -- parsed successfully
+ * CMD_ERR_NO_MATCH -- could find nothing that matches
+ * CMD_ERR_AMBIGUOUS -- found more than one match
+ * or parsed->num_command == 0
+ */
+static cmd_return_code_t
+cmd_parse_phase_two(cmd_parsed parsed, cmd_parse_type_t type)
+{
+ uint ii ;
+ uint match ;
+
+ /* Prepare to filter commands */
+
+ cmd_filter_prepare(parsed, type) ;
+
+ match = 2 ; /* in case parsed->num_command == 0 ! */
+
+ for (ii = 0 ; ii < parsed->num_command ; ii++)
+ match = cmd_filter(parsed, ii, false) ;
+
+ /* Should end up with one command to execute. */
+ if (match == 0)
+ return CMD_ERR_NO_MATCH ;
+
+ if (match > 1)
+ return CMD_ERR_AMBIGUOUS ;
+
+ parsed->cmd = vector_get_item(parsed->cmd_v, 0) ;
+
+ return CMD_SUCCESS ;
+} ;
+
+#if 0
+/*------------------------------------------------------------------------------
+ * If src matches dst return dst string, otherwise return NULL
+ *
+ * Returns NULL if dst is an option, variable of vararg.
+ *
+ * NULL or empty src are deemed to match.
+ */
+static const char *
+cmd_entry_function (const char *src, const char *dst)
+{
+ if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst))
+ return NULL;
+
+ if ((src == NULL) || (*src == '\0'))
+ return dst;
+
+ if (strncmp (src, dst, strlen (src)) == 0)
+ return dst;
+
+ return NULL;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+/* This version will return the dst string always if it is
+ CMD_VARIABLE for '?' key processing */
+static const char *
+cmd_entry_function_item (const char *src, const char *dst)
+{
+ if (CMD_VARARG (dst))
+ return dst;
+
+ if (CMD_RANGE (dst))
+ {
+ if (cmd_range_match (dst, src))
+ return dst;
+ else
+ return NULL;
+ }
+
+#ifdef HAVE_IPV6
+ if (CMD_IPV6 (dst))
+ {
+ if (cmd_ipv6_match (src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV6_PREFIX (dst))
+ {
+ if (cmd_ipv6_prefix_match (src))
+ return dst;
+ else
+ return NULL;
+ }
+#endif /* HAVE_IPV6 */
+
+ if (CMD_IPV4 (dst))
+ {
+ if (cmd_ipv4_match (src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV4_PREFIX (dst))
+ {
+ if (cmd_ipv4_prefix_match (src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ /* Optional or variable commands always match on '?' */
+ if (CMD_OPTION (dst) || CMD_VARIABLE (dst))
+ return dst;
+
+ /* In case of 'command \t', given src is NULL string. */
+ if (src == NULL)
+ return dst;
+
+ if (strncmp (src, dst, strlen (src)) == 0)
+ return dst;
+ else
+ return NULL;
+}
+
+/*------------------------------------------------------------------------------
+ * Check same string element existence.
+ *
+ * Returns: 0 => found same string in the vector
+ * 1 => NOT found same string in the vector
+ */
+static bool
+cmd_unique_string (vector v, const char *str)
+{
+ unsigned int i;
+ char *match;
+
+ for (i = 0; i < vector_length (v); i++)
+ if ((match = vector_get_item (v, i)) != NULL)
+ if (strcmp (match, str) == 0)
+ return 0;
+ return 1;
+}
+
+/* Compare string to description vector. If there is same string
+ return 1 else return 0. */
+static bool
+item_unique_string (vector v, const char *str)
+{
+ unsigned int i;
+ struct cmd_item *item;
+
+ for (i = 0; i < vector_length (v); i++)
+ if ((item = vector_get_item (v, i)) != NULL)
+ if (strcmp (item->cmd, str) == 0)
+ return 1;
+ return 0;
+}
+#endif
+/*==============================================================================
+ * '?' describe command support.
+ */
+
+/*------------------------------------------------------------------------------
+ * Get description of current (partial) command
+ *
+ * Returns: NULL => no description available
+ *
+ * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS
+ *
+ * or: address of vector of "struct cmd_item" values available.
+ *
+ * NB: when a vector is returned it is the caller's responsibility to
+ * vector_free() it. (The contents are all effectively const, so do not
+ * themselves need to be freed.)
+ */
+extern vector
+cmd_describe_command (const char* line, node_type_t node,
+ cmd_return_code_t* status)
+{
+#if 0
+ vector ret ;
+ struct cmd_parsed parsed_s ;
+ cmd_parsed parsed ;
+ cmd_token_type_t tok_total ;
+
+ /* Set up a parser object and tokenise the command line */
+ parsed = cmd_parse_init_new(&parsed_s) ;
+ tok_total = cmd_tokenise(parsed, line, node) ;
+
- double_colon++;
- nums++;
- break;
- case STATE_ADDR:
- if (*(str + 1) == ':' || *(str + 1) == '\0')
+
+
+
+
+
+ /* Level 1 parsing
+ *
+ * Strip quotes and escapes from all the tokens.
+ */
+ if (tok_total != cmd_tok_simple)
+ {
+ ret = cmd_parse_phase_one(parsed) ;
+ if (ret != CMD_SUCCESS)
+ return ret ;
+ } ;
+
+ /* If allowed to 'do', see if there.
+ *
+ * 'do' forces command to be parsed in ENABLE_NODE (if allowed)
+ */
+ if ((type & cmd_parse_no_do) == 0)
+ cmd_try_do_shortcut(parsed) ;
+
+
+
+
+ return cmd_describe_command_real (tokens, node, status);
+
+
+
+ static vector
+ cmd_describe_command_real (vector tokens, int node, int *status)
+ {
+ unsigned int i;
+ vector cmd_vector;
+ #define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_command *cmd_command;
+ unsigned int index;
+ int ret;
+ enum match_type match;
+ char *command;
+
+ /* Set index. */
+ if (vector_length (tokens) == 0)
+ {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ else
+ index = vector_length (tokens) - 1;
+
+ /* Make copy vector of current node's command vector. */
+ cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
+
+ /* Prepare match vector */
+ matchvec = vector_init (INIT_MATCHVEC_SIZE);
+
+ /* Filter commands. */
+ /* Only words precedes current word will be checked in this loop. */
+ for (i = 0; i < index; i++)
+ if ((command = vector_get_item (tokens, i)))
+ {
+ match = cmd_filter(command, cmd_vector, i, any_match) ;
+
+ if (match == vararg_match)
{
- if (str - sp > 3)
- return no_match;
+ struct cmd_command *cmd_command;
+ vector descvec;
+ unsigned int j, k;
+
+ for (j = 0; j < vector_length (cmd_vector); j++)
+ if ((cmd_command = vector_get_item (cmd_vector, j)) != NULL
+ && (vector_length (cmd_command->items)))
+ {
+ descvec = vector_get_item (cmd_command->items,
+ vector_length (cmd_command->items) - 1);
+ for (k = 0; k < vector_length (descvec); k++)
+ {
+ struct cmd_item *item = vector_get_item (descvec, k);
+ vector_set (matchvec, item);
+ }
+ }
+
+ vector_set (matchvec, &item_cr);
+ vector_free (cmd_vector);
+
+ return matchvec;
+ } ;
- nums++;
- state = STATE_COLON;
- }
- if (*(str + 1) == '.')
- state = STATE_DOT;
- break;
- case STATE_DOT:
- state = STATE_ADDR;
- break;
- default:
- break;
+ ret = is_cmd_ambiguous (command, cmd_vector, i, match) ;
+ if (ret != 0)
+ {
+ vector_free (cmd_vector);
+ vector_free (matchvec);
+ *status = (ret == 1) ? CMD_ERR_AMBIGUOUS
+ : CMD_ERR_NO_MATCH ;
+ return NULL ;
+ } ;
}
- if (nums > 8)
- return no_match;
+ /* Prepare match vector */
+ /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
- if (colons > 7)
- return no_match;
+ /* Make sure that cmd_vector is filtered based on current word */
+ command = vector_get_item (tokens, index);
+ if (command)
+ match = cmd_filter(command, cmd_vector, index, any_match);
- str++;
- }
+ /* Make description vector. */
+ for (i = 0; i < vector_length (cmd_vector); i++)
+ {
+ vector items ;
+
+ cmd_command = vector_get_item (cmd_vector, i) ;
+ if (cmd_command == NULL)
+ continue ;
+
+ /* Ignore cmd_command if no tokens at index position.
+ *
+ * Deal with special case of possible <cr> completion.
+ */
+ items = cmd_command->items;
+ if (index >= vector_length (items))
+ {
+ if (command == NULL && index == vector_length (items))
+ {
+ if (!item_unique_string (matchvec, cr_string))
+ vector_push_item(matchvec, &item_cr);
+ }
+ continue ;
+ } ;
+
+ /* Check if command is completed. */
+ unsigned int j;
+ vector descvec = vector_get_item (items, index);
+ struct cmd_item *item;
+
+ for (j = 0; j < vector_length (descvec); j++)
+ if ((item = vector_get_item (descvec, j)))
+ {
+ const char *string;
+
+ string = cmd_entry_function_desc (command, item->cmd);
+ if (string)
+ {
+ /* Uniqueness check */
+ if (!item_unique_string (matchvec, string))
+ vector_push_item(matchvec, item);
+ }
+ } ;
+ } ;
+
+ vector_free (cmd_vector);
+
+ if (vector_length(matchvec) == 0)
+ {
+ vector_free (matchvec);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+
+ *status = CMD_SUCCESS;
+ return matchvec;
+ }
+
+ cmd_parse_reset(parsed, false) ;
+
+ return
+#endif
+
+ return NULL ;
+} ;
#if 0
- if (nums < 11)
- return partly_match;
-#endif /* 0 */
+/*------------------------------------------------------------------------------
+ * Check LCD of matched command.
+ *
+ * Scan list of matched keywords, and by comparing them pair-wise, find the
+ * longest common leading substring.
+ *
+ * Returns: 0 if zero or one matched keywords
+ * length of longest common leading substring, otherwise.
+ */
+static int
+cmd_lcd (vector matchvec)
+{
+ int n ;
+ int i ;
+ int lcd ;
+ char *sp, *sq, *ss ;
- return exact_match;
+ n = vector_end(matchvec) ;
+ if (n < 2)
+ return 0 ;
+
+ ss = vector_get_item(matchvec, 0) ;
+ lcd = strlen(ss) ;
+
+ for (i = 1 ; i < n ; i++)
+ {
+ sq = ss ;
+ ss = vector_get_item(matchvec, i) ;
+ sp = ss ;
+
+ while ((*sp == *sq) && (*sp != '\0'))
+ {
+ ++sp ;
+ ++sq ;
+ } ;
+
+ if (lcd > (sp - ss))
+ lcd = (sp - ss) ;
+ }
+ return lcd;
}
+#endif
+
+/*==============================================================================
+ * Command line help support.
+ *
+ *
+ */
+
+static bool cmd_post_last(cmd_parsed parsed) ;
+
+
/*------------------------------------------------------------------------------
- * Is this an IPv6 Prefix:
+ * Look out for the following special cases:
*
- * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ?
- * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ?
+ * a) before or on '#' or '!'
*
- * Returns: no_match -- improperly formed
- * partly_match -- accepts empty string
- * exact_match -- syntactically complete
+ * '#' and '!' are only recognised as the start of a comment at the
+ * start of a line... So can insert something in front, and what
+ * currently looks like a comment, suddenly needs to be reconsidered
+ * as tokens... the first of which happens to start with '!' or '#'.
*
- * NB: partly_match is returned for anything valid before the '/', but which
- * has no '/' or no number after the '/'.
+ * To save work, and leave open the possibility of trailing comments,
+ * we treat this case as an "error".
+ *
+ * b) before or on '<'
+ *
+ * currently, pipe-in must be the first token on the command line.
+ *
+ * So if there is anything prior to the '<', treat that as an error.
+ * If is just spaces before the '<', say that nothing may be placed
+ * before the '<' (cf nothing may be placed before '!' or '#').
+ *
+ * in these cases no help is available -- return message explaining.
+ *
+ * NB: assumes that have already done cmd_token_position(), and therefore that
+ * if there is a '#' or '<' that cannot be positioned *after* it, or
+ * indeed that can be positioned after a '>'.
*/
-extern match_type_t
-cmd_ipv6_prefix_match (const char *str)
+extern const char*
+cmd_help_preflight(cmd_parsed parsed)
{
- int state = STATE_START;
- int colons = 0, nums = 0, double_colon = 0;
- int mask;
- const char *sp = NULL;
- char *endptr = NULL;
+ if ((parsed->tok_total & cmd_tok_comment) != 0)
+ {
+ return "cannot have a command on a comment line";
+ } ;
+
+ if ((parsed->tok_total & cmd_tok_in_pipe) != 0)
+ {
+ return "cannot have a command and an '<' pipe together" ;
+ } ;
- if (str == NULL)
- return partly_match;
+ return NULL ; /* OK ! */
+} ;
- if (strspn (str, IPV6_PREFIX_STR) != strlen (str))
- return no_match;
- while (*str != '\0' && state != STATE_MASK)
+
+
+/*------------------------------------------------------------------------------
+ * See if current command line can be completed, and if so, how.
+ *
+ * NB: must already have done cmd_token_position().
+ *
+ * Must not be called if cmd_token_position() reported a "special".
+ *
+ * Returns: CMD_ERR_PARSING -- the parser could not make sense of the line.
+ *
+ */
+extern cmd_return_code_t
+cmd_completion(cmd_parsed parsed, node_type_t node)
+{
+ cmd_return_code_t ret ;
+
+ /* Parse the line -- allow completion, allow do, allow backing up the tree,
+ * but do not parse for execution.
+ */
+ ret = cmd_parse_command(parsed, node, cmd_parse_standard) ;
+
+ if (ret == CMD_ERR_PARSING)
+ return ret ; /* nothing more possible */
+
+ /* Expect now to have a cmd_v set with the result of the filtering,
+ * with 0, 1 or more possible commands in it.
+ *
+ * Now establish what the alternatives are at the current token position.
+ *
+ * We do this by filtering again, on the current token position, and
+ * asking the filter to collect all the possible items. Note that if the
+ * current token position is beyond the last actual command token, then
+ * will be filtering on the empty eol token -- which we arrange to match
+ * anything, including eol -- which gives the dummy item_cr of type
+ * item_eol.
+ */
+ assert(parsed->cti >= parsed->first_command) ;
+ assert(parsed->cti <= parsed->first_command + parsed->num_command) ;
+
+ cmd_filter(parsed, parsed->cti - parsed->first_command, true) ;
+
+ /* Reduce the list of items available at the current position to
+ * eliminate duplicates and have the possibilities sorted by type and
+ * by value.
+ */
+ if (vector_length(parsed->item_v) > 1)
+ {
+ cmd_item prev ;
+ uint ii ;
+ uint i_keep ;
+
+ vector_sort(parsed->item_v, (vector_sort_cmp*)cmd_cmp_item) ;
+
+ i_keep = 1 ;
+
+ prev = vector_get_item(parsed->item_v, 0) ;
+ for (ii = 1 ; ii < vector_length(parsed->item_v) ; ++ii)
+ {
+ cmd_item item ;
+
+ item = vector_get_item(parsed->item_v, ii) ;
+ if (cmd_cmp_item(&item, &prev) != 0)
+ {
+ vector_set_item(parsed->item_v, i_keep++, item) ;
+ prev = item ;
+ } ;
+ } ;
+
+ vector_set_length(parsed->item_v, i_keep) ;
+ /* discard what did not keep */
+ } ;
+
+ return CMD_SUCCESS ;
+} ;
+
+
+
+
+/*------------------------------------------------------------------------------
+ * How to insert a newly completed keyword ?
+ *
+ * There are a number of cases:
+ *
+ * 1) the cti is for the token to be completed.
+ *
+ * May be positioned one or more spaces in front of the keyword.
+ *
+ * Will replace any leading spaces and the keyword by the new keyword.
+ *
+ * 2) the cti is for the empty eol token
+ *
+ * Will replace any leading spaces to the eol keyword, by the new
+ * keyword.
+ *
+ * 3) the cti is for a '>' token, or any other !cmd_tok_simple.
+ *
+ * If cti is not exactly on the token, then insert the keyword exactly
+ * where it is -- no spaces need to be removed or added.
+ *
+ * If the cti is on the token, insert the keyword and one space.
+ *
+ * The second issue is where to leave the cursor... if the command is now
+ * complete, wish to leave the cursor at the end of the newly completed
+ * keyword. Otherwise:
+ *
+ * 1) if the next token is a cmd_tok_simple, move to it.
+ *
+ * 2) otherwise if there is already a space after the newly completed
+ * keyword, move past it.
+ *
+ * Otherwise, insert a space.
+ *
+ * Returns: n = number of characters to move the cursor before
+ * starting to replace characters (may be -ve)
+ * *rep = number of characters to replace
+ * *ins = number of spaces to insert
+ * *mov = number of spaces to move afterwards (may be -ve)
+ */
+extern void
+cmd_complete_keyword(cmd_parsed parsed, int* pre, int* rep, int* ins, int* mov)
+{
+ cmd_token t, nt ;
+ bool last ;
+ int gap ;
+
+ /* Is this the last token possible on this command line ? */
+ last = cmd_post_last(parsed) ;
+
+ /* Get the token we are operating and the following one (if any).
+ *
+ * Calculate gap between end of token to be replaced and start of next,
+ * if any.
+ *
+ * gap == 0 => is at eol or on !cmd_tok_simple.
+ */
+ t = cmd_token_get(parsed->tokens, parsed->cti) ;
+ if (parsed->cti < parsed->num_tokens)
+ {
+ nt = cmd_token_get(parsed->tokens, parsed->cti + 1) ;
+ gap = nt->tp - (t->tp + els_len_nn(t->ot)) ;
+ }
+ else
+ {
+ nt = NULL ;
+ gap = (parsed->rp < 0) ? -parsed->rp : 0 ;
+ } ;
+
+ /* Now work out what we need to do. */
+
+ *pre = 0 ; /* preset values */
+ *rep = 0 ;
+ *ins = 0 ;
+ *mov = 0 ;
+
+ if ((t->type & cmd_tok_simple) != 0)
{
- switch (state)
+ /* Replacing an existing simple token ------------------------------
+ *
+ * Move to start of token and replace it.
+ */
+ *pre = -parsed->rp ;
+ *rep = parsed->ctl ;
+
+ /* now what do we do after the token ? */
+ assert(nt != NULL) ;
+
+ if ((nt->type & cmd_tok_simple) != 0)
{
- case STATE_START:
- if (*str == ':')
+ /* Next token is simple -- step to it */
+ assert(!last) ;
+
+ *mov = gap ;
+ }
+ else if (nt->type == cmd_tok_eol)
+ {
+ /* Next token is eol
+ *
+ * gap is the number of spaces there will be after the
+ * newly replaced token.
+ */
+ if (!last)
{
- if (*(str + 1) != ':' && *(str + 1) != '\0')
- return no_match;
- colons--;
- state = STATE_COLON;
+ if (gap == 0)
+ *ins = 1 ; /* need a trailing space */
+ else
+ *mov = 1 ; /* step over existing space */
+ } ;
+ }
+ else
+ {
+ /* Next token is something special.
+ *
+ * gap is the number of spaces there will be after the
+ * newly replaced token.
+ */
+ if (gap == 0)
+ {
+ *ins = last ? 1 : 2 ;
+ *mov = -1 ;
}
- else
+ else if (gap == 1)
{
- sp = str;
- state = STATE_ADDR;
+ *ins = last ? 0 : 1 ;
}
-
- continue;
- case STATE_COLON:
- colons++;
- if (*(str + 1) == '/')
- return no_match;
- else if (*(str + 1) == ':')
- state = STATE_DOUBLE;
else
{
- sp = str + 1;
- state = STATE_ADDR;
+ *mov = last ? 0 : 1 ;
}
- break;
- case STATE_DOUBLE:
- if (double_colon)
- return no_match;
+ } ;
+ }
+
+ else if (t->type == cmd_tok_eol)
+ {
+ /* Inserting at or before eol --------------------------------------
+ *
+ * Insert exactly where we are -- *pre == 0
+ */
+
+ /* If last, replace any spaces between cursor and end of line,
+ * otherwise, keep one of them.
+ */
+ *rep = last ? gap : (gap > 0) ? gap - 1 : gap ;
- if (*(str + 1) == ':')
- return no_match;
+ /* now what do we do after the token ? */
+ assert(nt == NULL) ;
+
+ if (!last)
+ {
+ if (gap == 0)
+ *ins = 1 ; /* need space after */
else
- {
- if (*(str + 1) != '\0' && *(str + 1) != '/')
- colons++;
- sp = str + 1;
+ *mov = 1 ; /* step over one */
+ } ;
+ }
+ else
+ {
+ /* Inserting at or before something special ------------------------
+ *
+ * Insert exactly where we are -- *pre == 0
+ * replace nothing -- *rep == 0
+ */
+ if (gap == 0)
+ {
+ *ins = last ? 1 : 2 ;
+ *mov = -1 ;
+ }
+ else if (gap == 1)
+ {
+ *ins = last ? 0 : 1 ;
+ }
+ else
+ {
+ *mov = last ? 0 : 1 ;
+ }
+ } ;
+} ;
- if (*(str + 1) == '/')
- state = STATE_SLASH;
- else
- state = STATE_ADDR;
- }
+/*------------------------------------------------------------------------------
+ * Do we have just one command left, and have we just filtered on the last
+ * possible command item & token ?
+ *
+ * If so, then there is no point filtering any further, and there is nothing
+ * more that could be added to this command line.
+ */
+static bool
+cmd_post_last(cmd_parsed parsed)
+{
+ if (vector_length(parsed->cmd_v) == 1)
+ {
+ cmd_command cmd ;
+ cmd = vector_get_item(parsed->cmd_v, 0) ;
- double_colon++;
- nums += 1;
- break;
- case STATE_ADDR:
- if (*(str + 1) == ':' || *(str + 1) == '.'
- || *(str + 1) == '\0' || *(str + 1) == '/')
- {
- if (str - sp > 3)
- return no_match;
+ return ((parsed->cti - parsed->first_command + 1) == cmd->nt_max) ;
+ } ;
+
+ return false ;
+} ;
- for (; sp <= str; sp++)
- if (*sp == '/')
- return no_match;
- nums++;
- if (*(str + 1) == ':')
- state = STATE_COLON;
- else if (*(str + 1) == '.')
- state = STATE_DOT;
- else if (*(str + 1) == '/')
- state = STATE_SLASH;
- }
- break;
- case STATE_DOT:
- state = STATE_ADDR;
- break;
- case STATE_SLASH:
- if (*(str + 1) == '\0')
- return partly_match;
-
- state = STATE_MASK;
- break;
- default:
- break;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#if 0
+static vector
+cmd_complete_command_real (vector tokens, int node, int *status)
+{
+ unsigned int i;
+ unsigned int ivl ;
+ unsigned int last_ivl ;
+ vector cmd_v ;
+#define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_command *cmd_command;
+ unsigned int index;
+ struct cmd_item *cmd_item;
+ vector descvec;
+ char *token;
+ int n ;
+
+ /* Stop immediately if the tokens is empty. */
+ if (vector_length (tokens) == 0)
+ {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+
+ /* Take (shallow) copy of cmdvec for given node. */
+ cmd_v = vector_copy (cmd_node_vector (cmdvec, node));
+
+ /* First, filter upto, but excluding last token */
+ last_ivl = vector_length (tokens) - 1;
+
+ for (ivl = 0; ivl < last_ivl; ivl++)
+ {
+ enum match_type match;
+ int ret;
+
+ /* TODO: does this test make any sense ? */
+ if ((token = vector_get_item (tokens, ivl)) == NULL)
+ continue ;
+
+ /* First try completion match, return best kind of match */
+ index = ivl ;
+ match = cmd_filter_by_completion (token, cmd_v, index) ;
+
+ /* Eliminate all but the selected kind of match */
+ ret = is_cmd_ambiguous (token, cmd_v, index, match) ;
+
+ if (ret == 1)
+ {
+ /* ret == 1 => either token matches more than one keyword
+ * or token matches more than one number range
+ */
+ vector_free (cmd_v);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
}
+#if 0
+ /* For command completion purposes do not appear to care about
+ * incomplete ipv4 or ipv6 prefixes (missing '/' or digits after).
+ */
+ else if (ret == 2)
+ {
+ vector_free (cmd_v);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+#endif
+ }
+
+ /* Prepare match vector. */
+ matchvec = vector_init (INIT_MATCHVEC_SIZE);
+
+ /* Now we got into completion */
+ index = last_ivl ;
+ token = vector_get_item(tokens, last_ivl) ; /* is now the last token */
+
+ for (i = 0; i < vector_length (cmd_v); i++)
+ {
+ unsigned int j;
+ const char *string;
- if (nums > 11)
- return no_match;
+ if ((cmd_command = vector_get_item (cmd_v, i)) == NULL)
+ continue ;
- if (colons > 7)
- return no_match;
+ descvec = vector_get_item (cmd_command->items, index);
+ if (descvec == NULL)
+ continue ;
- str++;
+ for (j = 0; j < vector_length (descvec); j++)
+ {
+ item = vector_get_item (descvec, j) ;
+ if (item == NULL)
+ continue ;
+
+ string = cmd_entry_function(token, item->str) ;
+ if ((string != NULL) && cmd_unique_string(matchvec, string))
+ cmd_add_to_strvec (matchvec, string) ;
+ } ;
+ } ;
+
+ n = vector_length(matchvec) ; /* number of entries in the matchvec */
+
+ /* We don't need cmd_v any more. */
+ vector_free (cmd_v);
+
+ /* No matched command */
+ if (n == 0)
+ {
+ vector_free (matchvec);
+
+ /* In case of 'command \t' pattern. Do you need '?' command at
+ the end of the line. */
+ if (*token == '\0')
+ *status = CMD_COMPLETE_ALREADY;
+ else
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
}
- if (state < STATE_MASK)
- return partly_match;
+ /* Only one matched */
+ if (n == 1)
+ {
+ *status = CMD_COMPLETE_FULL_MATCH;
+ return matchvec ;
+ }
- mask = strtol (str, &endptr, 10);
- if (*endptr != '\0')
- return no_match;
+ /* Check LCD of matched strings. */
+ if (token != NULL)
+ {
+ unsigned lcd = cmd_lcd (matchvec) ;
+
+ if (lcd != 0)
+ {
+ if (strlen(token) < lcd)
+ {
+ char *lcdstr;
- if (mask < 0 || mask > 128)
- return no_match;
+ lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1);
+ memcpy (lcdstr, vector_get_item(matchvec, 0), lcd) ;
+ lcdstr[lcd] = '\0';
-/* I don't know why mask < 13 makes command match partly.
- Forgive me to make this comments. I Want to set static default route
- because of lack of function to originate default in ospf6d; sorry
- yasu
- if (mask < 13)
- return partly_match;
-*/
+ cmd_free_strvec(matchvec) ; /* discard the match vector */
- return exact_match;
-}
+ matchvec = vector_init (1);
+ vector_push_item(matchvec, lcdstr) ;
-#endif /* HAVE_IPV6 */
+ *status = CMD_COMPLETE_MATCH;
+ return matchvec ;
+ }
+ }
+ }
+
+ *status = CMD_COMPLETE_LIST_MATCH;
+ return matchvec ;
+}
+#endif
/*------------------------------------------------------------------------------
- * Is this a decimal number in the allowed range:
- *
- * Returns: true <=> OK -- *including* empty string
- * false => not a valid number, or not in required range
- * (or invalid range !!)
+ * Can the current command be completed ?
*/
+extern vector
+cmd_complete_command (vector tokens, int node, int *status)
+{
+#if 0
+ vector ret;
-#define DECIMAL_STRLEN_MAX 10
+ if ( cmd_try_do_shortcut(node, vector_get_item(tokens, 0) ) )
+ {
+ vector shifted_tokens;
+ unsigned int index;
-extern bool
-cmd_range_match (const char *range, const char *str)
-{
- char *p;
- char buf[DECIMAL_STRLEN_MAX + 1];
- char *endptr = NULL;
- unsigned long min, max, val;
+ /* We can try it on enable node, cos' the vty is authenticated */
- if (str == NULL)
- return true ;
+ shifted_tokens = vector_init (vector_count(tokens));
+ /* use memcpy? */
+ for (index = 1; index < vector_length (tokens); index++)
+ {
+ vector_set_index (shifted_tokens, index-1,
+ vector_lookup(tokens, index)) ;
+ }
- val = strtoul (str, &endptr, 10);
- if (*endptr != '\0')
- return false ;
+ ret = cmd_complete_command_real (shifted_tokens, ENABLE_NODE, status);
+
+ vector_free(shifted_tokens);
+ return ret;
+ }
+
+ return cmd_complete_command_real (tokens, node, status);
+#endif
+
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+} ;
- range++;
- p = strchr (range, '-');
- if (p == NULL)
- return false ;
- if (p - range > DECIMAL_STRLEN_MAX)
- return false ;
- strncpy (buf, range, p - range);
- buf[p - range] = '\0';
- min = strtoul (buf, &endptr, 10);
- if (*endptr != '\0')
- return false ;
- range = p + 1;
- p = strchr (range, '>');
- if (p == NULL)
- return false ;
- if (p - range > DECIMAL_STRLEN_MAX)
- return false ;
- strncpy (buf, range, p - range);
- buf[p - range] = '\0';
- max = strtoul (buf, &endptr, 10);
- if (*endptr != '\0')
- return false ;
- if (val < min || val > max)
- return false ;
- return true ;
-}
diff --git a/lib/command_parse.h b/lib/command_parse.h
index ab91e7f4..789fe601 100644
--- a/lib/command_parse.h
+++ b/lib/command_parse.h
@@ -24,13 +24,15 @@
#ifndef _ZEBRA_COMMAND_PARSE_H
#define _ZEBRA_COMMAND_PARSE_H
-#include <zebra.h>
+#include "zconfig.h" /* HAVE_IPV6 */
#include "misc.h"
-#include "node_type.h"
+#include "command_common.h"
#include "vector.h"
#include "qstring.h"
+#include "elstring.h"
+#if 0
/*==============================================================================
* Parsing of tokens
*/
@@ -51,42 +53,397 @@ enum cmd_token_spec
cmd_ts_ipv6_prefix,
cmd_ts_vararg,
+} ;
+typedef enum cmd_token_spec cmd_token_spec_t ;
+#endif
+
+/*==============================================================================
+ * Sexing of token types in command descriptions
+ */
+#define CMD_OPTION(S) ((S[0]) == '[')
+#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<'))
+#define CMD_VARARG(S) ((S[0]) == '.')
+#define CMD_RANGE(S) ((S[0] == '<'))
+
+#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
+#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
+#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
+#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
+
+/*==============================================================================
+ * Command Items.
+ *
+ * A command is compiled into a vector of lists of command items -- the
+ * cmd_command->items (so called).
+ *
+ */
+
+/* Command item types
+ *
+ * NB: the command items sort according to this -- highest first.
+ *
+ * NB: this is in the same order as the match_type -- and should remain so.
+ * See match_type below for further discussion of the hierarchy.
+ */
+enum cmd_item_type
+{
+ item_null,
+
+ item_eol,
+ item_option_word,
+ item_vararg, /* rest of the line */
+ item_word,
+ item_ipv6_prefix,
+ item_ipv6_address,
+ item_ipv4_prefix,
+ item_ipv4_address,
+
+ item_range,
+
+ item_keyword,
+
+ item_type_count, /* number of types */
+} ;
+typedef enum cmd_item_type cmd_item_type_t ;
+
+enum {
+ item_max_number = 0xFFFFFFFF /* can be +/- this */
+} ;
+
+CONFIRM(LONG_MAX >= item_max_number) ;
+
+/*------------------------------------------------------------------------------
+ * Sex cmd_item_type and return whether it is an "option" type, or not.
+ *
+ * NB: tolerates item_null -- others may well not.
+ */
+Inline bool
+cmd_item_is_option(cmd_item_type_t itt)
+{
+ const static bool is_option[item_type_count] =
+ {
+ [item_null] = false,
+
+ [item_eol] = false,
+
+ [item_option_word] = true,
+
+ [item_vararg] = false,
+ [item_word] = false,
+
+ [item_ipv6_prefix] = false,
+ [item_ipv6_address] = false,
+ [item_ipv4_prefix] = false,
+ [item_ipv4_address] = false,
+
+ [item_range] = false,
+
+ [item_keyword] = false,
+ } ;
+
+ assert((itt >= 0) && (itt < item_type_count)) ;
+
+ return is_option[itt] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Sex cmd_item_type and return whether it is an "vararg" type, or not.
+ *
+ * NB: tolerates item_null -- others may well not.
+ */
+Inline bool
+cmd_item_is_vararg(cmd_item_type_t itt)
+{
+ const static bool is_vararg[item_type_count] =
+ {
+ [item_null] = false,
+
+ [item_eol] = false,
+
+ [item_option_word] = false,
+
+ [item_vararg] = true,
+ [item_word] = false,
+
+ [item_ipv6_prefix] = false,
+ [item_ipv6_address] = false,
+ [item_ipv4_prefix] = false,
+ [item_ipv4_address] = false,
+
+ [item_range] = false,
+
+ [item_keyword] = false,
+ } ;
+
+ assert((itt >= 0) && (itt < item_type_count)) ;
+
+ return is_vararg[itt] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The command item structure.
+ */
+typedef struct cmd_item* cmd_item ;
+struct cmd_item
+{
+ const char* str ; /* in r_string -- original string form */
+ const char* doc ; /* in r_doc -- description text */
+
+ cmd_item next ; /* Next possibility (if any) */
+
+ cmd_item_type_t type ;
+ bool arg ; /* include in argv */
+
+ /* For item_range values */
+ bool range_sign_allowed ;
+ bool range_sign_required ;
+ long range_min ;
+ long range_max ;
} ;
-typedef enum cmd_token_spec cmd_token_spec_t ;
/*==============================================================================
- * Completion match types.
+ * Match strengths types and filter settings.
+ *
+ * When matching a token, may have a number of competing items, possibly of
+ * different types.
+ *
+ * For execution a token must match completely -- or, for keyword items,
+ * match partially at most one possible keyword.
+ */
+
+/* Match strength
+ *
+ * When matching a token against an item, the following are the possible
+ * results.
+ */
+enum match_strength
+{
+ ms_no_match = 0, /* match failed: token is definitely NOT
+ * the item in question.
+ */
+
+ ms_min_parse = ms_no_match + 1,
+ /* for parsing must match somehow ! */
+
+ ms_partial, /* match OK up to the end of the token, but
+ * more is required to complete it.
+ * This is used by the variable matches.
+ */
+
+ ms_min_execute = ms_partial + 1,
+ /* for execution must be at least this */
+
+ ms_anything, /* matches because will match anything.
+ * This is used for WORD and such like items.
+ */
+
+ ms_kwd_incomplete, /* keyword match OK, but not complete.
+ */
+
+ ms_var_complete, /* match succeeded: token is a complete and
+ * valid instance of the item in question
+ */
+
+ ms_kwd_complete /* match succeeded: token is a complete and
+ * valid instance of the item in question
+ */
+} ;
+typedef enum match_strength match_strength_t ;
+
+
+/* The match type indicates what has been matched and the strength of the
+ * match.
+ *
+ * NB: the order of these is significant, higher numbered match types are
+ * preferred over lower numbered ones.
+ *
+ * NB: this is in the same order as the cmd_item_type -- and should remain so.
+ *
+ * During the command filtering, when a token and an item match, and the
+ * match type is better than the match type to date, then all previous matches
+ * are discarded.
+ *
+ * The hierarchy means that, for example, '22' will match an IPv4 prefix
+ * partially, but that will be forgotten if it later matches a WORD, and that
+ * will be forgotten if it later matches a number range.
+ *
+ * The IPv6 items have a lower priority so that a simple decimal will prefer
+ * the simpler IPv4 address form. As soon as there is an 'a'..'f' or a ':',
+ * the IPv4 will no longer match.
+ *
+ * The partial keyword match is preferred over a partial anything else, but
+ * cannot mask a complete value !
+ *
+ * The relative ranking of: mt_option_word_match, mt_vararg_match, and
+ * mt_word_match, is more or less arbitrary -- if these are ever candidates
+ * at the same time, the commands are ambiguous.
*
- * NB: the order of these is significant -- in particular as confirmed below.
*/
enum match_type
{
- no_match = 0, /* nope */
- any_match = 1,
+ mt_no_match,
- extend_match = any_match,
+ mt_eol_partial, /* a partial match ! */
- ipv4_prefix_match,
- ipv4_match,
- ipv6_prefix_match,
- ipv6_match,
- range_match,
- vararg_match,
+ mt_ipv6_address_partial,
+ mt_ipv6_prefix_partial,
+ mt_ipv4_address_partial,
+ mt_ipv4_prefix_partial,
- partly_match, /* OK as far as it went */
- exact_match, /* Syntactically complete -- greatest match */
+ mt_range_partial,
+
+ mt_eol, /* an ms_anything match */
+
+ mt_option_word_match, /* anything can match a [WORD] */
+
+ mt_vararg_match, /* anything can match a .vararg */
+ mt_word_match, /* anything can match a WORD */
+
+ mt_keyword_incomplete,
+
+ mt_ipv6_prefix_complete,
+ mt_ipv6_address_complete,
+ mt_ipv4_prefix_complete,
+ mt_ipv4_address_complete,
+
+ mt_range_complete,
+
+ mt_keyword_complete,
match_type_count /* Number of match types */
} ;
typedef enum match_type match_type_t ;
-CONFIRM(no_match == false) ;
-CONFIRM(extend_match == (no_match + 1)) ;
-CONFIRM(partly_match == (exact_match - 1)) ;
-CONFIRM(exact_match == (match_type_count - 1)) ;
+/*------------------------------------------------------------------------------
+ * Map match_type -> cmd_item_type
+ *
+ * From a match type extract the type of item which has been match, partially
+ * or completely.
+ */
+Inline cmd_item_type_t
+match_item_type(match_type_t mt)
+{
+ static cmd_item_type_t match_item_type[match_type_count] =
+ {
+ [mt_no_match] = item_null,
+
+ [mt_eol_partial] = item_eol,
+
+ [mt_ipv4_prefix_partial] = item_ipv4_prefix,
+ [mt_ipv4_address_partial] = item_ipv4_address,
+ [mt_ipv6_prefix_partial] = item_ipv6_prefix,
+ [mt_ipv6_address_partial] = item_ipv6_address,
+
+ [mt_range_partial] = item_range,
+
+ [mt_eol] = item_eol,
+
+ [mt_option_word_match] = item_option_word,
+
+ [mt_vararg_match] = item_vararg,
+ [mt_word_match] = item_word,
+
+ [mt_keyword_incomplete] = item_keyword,
+
+ [mt_ipv6_prefix_complete] = item_ipv6_prefix,
+ [mt_ipv6_address_complete] = item_ipv6_address,
+ [mt_ipv4_prefix_complete] = item_ipv4_prefix,
+ [mt_ipv4_address_complete] = item_ipv4_address,
+
+ [mt_range_complete] = item_range,
+
+ [mt_keyword_complete] = item_keyword,
+ } ;
+
+ assert((mt >= 0) && (mt < match_type_count)) ;
+
+ return match_item_type[mt] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Map match_type -> match_strength.
+ *
+ * From a match type extract the strength of the match.
+ */
+Inline match_strength_t
+match_match_strength(match_type_t mt)
+{
+ const static match_strength_t match_match_strength[match_type_count] =
+ {
+ [mt_no_match] = ms_no_match,
+
+ [mt_eol_partial] = ms_partial,
+
+ [mt_ipv6_prefix_partial] = ms_partial,
+ [mt_ipv6_address_partial] = ms_partial,
+ [mt_ipv4_prefix_partial] = ms_partial,
+ [mt_ipv4_address_partial] = ms_partial,
+
+ [mt_range_partial] = ms_partial,
+
+ [mt_eol] = ms_anything,
+
+ [mt_option_word_match] = ms_anything,
+
+ [mt_vararg_match] = ms_anything,
+ [mt_word_match] = ms_anything,
+
+ [mt_keyword_incomplete] = ms_kwd_incomplete,
+
+ [mt_ipv6_prefix_complete] = ms_var_complete,
+ [mt_ipv6_address_complete] = ms_var_complete,
+ [mt_ipv4_prefix_complete] = ms_var_complete,
+ [mt_ipv4_address_complete] = ms_var_complete,
+
+ [mt_range_complete] = ms_var_complete,
+
+ [mt_keyword_complete] = ms_kwd_complete,
+ } ;
+
+ assert((mt >= 0) && (mt < match_type_count)) ;
+
+ return match_match_strength[mt] ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Map cmd_item_type -> best possible match type.
+ *
+ * This gives the most optimistic outcome of an attempt to match to the given
+ * type of item.
+ *
+ * NB: tolerates item_null -- others may not
+ */
+Inline match_type_t
+item_best_match(cmd_item_type_t it)
+{
+ const static match_type_t item_best_match[item_type_count] =
+ {
+ [item_null] = mt_no_match,
+
+ [item_eol] = mt_eol,
+
+ [item_option_word] = mt_option_word_match,
+
+ [item_vararg] = mt_vararg_match,
+ [item_word] = mt_word_match,
+
+ [item_ipv6_prefix] = mt_ipv6_prefix_complete,
+ [item_ipv6_address] = mt_ipv6_address_complete,
+ [item_ipv4_prefix] = mt_ipv4_prefix_complete,
+ [item_ipv4_address] = mt_ipv4_address_complete,
+
+ [item_range] = mt_range_complete,
+
+ [item_keyword] = mt_keyword_complete,
+ } ;
+
+ assert((it >= 0) && (it < item_type_count)) ;
+
+ return item_best_match[it] ;
+} ;
/*==============================================================================
*
@@ -95,11 +452,13 @@ CONFIRM(exact_match == (match_type_count - 1)) ;
/* Command parsing options */
enum cmd_parse_type /* bit significant */
{
- cmd_parse_completion = 0,
+ cmd_parse_standard = 0, /* accept short command parts */
+
cmd_parse_strict = BIT(0),
+ cmd_parse_no_do = BIT(1),
+ cmd_parse_no_tree = BIT(2),
- cmd_parse_do = BIT(1),
- cmd_parse_tree = BIT(2),
+ cmd_parse_execution = BIT(3), /* wish to execute command */
} ;
typedef enum cmd_parse_type cmd_parse_type_t ;
@@ -108,28 +467,44 @@ enum cmd_pipe_type /* bit significant */
{
cmd_pipe_none = 0,
- cmd_pipe_in_file = BIT(0),
- cmd_pipe_in_shell = BIT(1),
+ cmd_pipe_file = BIT(0),
+ cmd_pipe_shell = BIT(1),
+ cmd_pipe_dev_null = BIT(2), /* out pipe only -- black hole */
- cmd_pipe_reflect = BIT(4),
- cmd_pipe_output = BIT(5),
- cmd_pipe_more = BIT(6),
+ /* For in pipes */
+ cmd_pipe_reflect = BIT(4), /* + option */
- cmd_pipe_out_file = BIT( 8),
- cmd_pipe_out_file_append = BIT( 9),
- cmd_pipe_out_shell = BIT(10),
+ /* For out file pipes */
+ cmd_pipe_append = BIT(4), /* >> */
} ;
typedef enum cmd_pipe_type cmd_pipe_type_t ;
+/* Parsed parts */
+enum cmd_parts /* bit significant */
+{
+ cmd_parts_none = 0,
+
+ cmd_part_do = BIT(0),
+ cmd_part_command = BIT(1),
+
+ cmd_part_in_pipe = BIT(2),
+ cmd_part_out_pipe = BIT(3),
+
+ cmd_parts_pipe = (cmd_part_in_pipe | cmd_part_out_pipe),
+
+ cmd_part_comment = BIT(4),
+} ;
+typedef enum cmd_parts cmd_parts_t ;
+
+
/*------------------------------------------------------------------------------
* Token object -- a qstring and some other properties.
*/
enum cmd_token_type /* *bit* significant */
{
- cmd_tok_null = 0, /* used for empty lines */
+ cmd_tok_eol = 0, /* all lines have one */
cmd_tok_simple = BIT( 0),
- cmd_tok_trailing = BIT( 1),
cmd_tok_sq = BIT( 8),
cmd_tok_dq = BIT( 9), /* '\\' within "..." are not
@@ -138,20 +513,24 @@ enum cmd_token_type /* *bit* significant */
cmd_tok_incomplete = (cmd_tok_sq | cmd_tok_dq | cmd_tok_esc),
- cmd_tok_pipe_in = BIT(12),
- cmd_tok_pipe_out = BIT(13),
- cmd_tok_comment = BIT(14),
+ cmd_tok_in_pipe = BIT(12), /* token starting '<' */
+ cmd_tok_out_pipe = BIT(13), /* token starting '>' */
+ cmd_tok_comment = BIT(14), /* token starting '!' or '#" */
} ;
typedef enum cmd_token_type cmd_token_type_t ;
-struct token
+struct cmd_token
{
cmd_token_type_t type ;
- qstring_t qs ;
- size_t tp ;
+ qstring_t qs ; /* token string */
+
+ bool term ; /* token has been '\0' terminated */
+
+ usize tp ; /* location of token in the line */
+ elstring_t ot ; /* original token in the line */
} ;
-typedef struct token token_t[1] ;
-typedef struct token* token ;
+
+typedef struct cmd_token* cmd_token ;
/*------------------------------------------------------------------------------
* Token vector -- a vector of token objects
@@ -164,6 +543,10 @@ struct token_vector
typedef struct token_vector token_vector_t[1] ;
typedef struct token_vector* token_vector ;
+enum {
+ TOKEN_VECTOR_INIT_ALL_ZEROS = VECTOR_INIT_ALL_ZEROS
+} ;
+
/*------------------------------------------------------------------------------
* Argument vector -- a vector of const char*
*/
@@ -174,6 +557,10 @@ struct arg_vector
typedef struct arg_vector arg_vector_t[1] ;
typedef struct arg_vector* arg_vector ;
+enum {
+ ARG_VECTOR_INIT_ALL_ZEROS = VECTOR_INIT_ALL_ZEROS
+} ;
+
/*------------------------------------------------------------------------------
* Parsed command line
*
@@ -191,252 +578,209 @@ typedef struct arg_vector* arg_vector ;
* emptied -- see cmd_empty_parsed_tokens() -- but the next command to be
* parsed will tidy up before proceeding.
*/
-typedef struct cmd_parsed* cmd_parsed ;
struct cmd_parsed
{
- struct cmd_element *cmd ; /* NULL if empty command
- or fails to parse */
+ cmd_parts_t parts ; /* What parts are present */
- const char* line ; /* the current line */
+ cmd_token_type_t tok_total ; /* What token types are present */
- enum node_type cnode ; /* node command is in */
- enum node_type onode ; /* node the parser started in */
+ usize elen ; /* effective length (less trailing spaces) */
+ usize tsp ; /* number of trailing spaces */
- bool do_shortcut ; /* true => is "do" command */
+ uint num_tokens ; /* number of tokens parsed */
- token_vector_t tokens ; /* vector of token objects */
- arg_vector_t args ; /* vector of arguments */
+ token_vector_t tokens ; /* vector of token objects */
- cmd_pipe_type_t pipes ; /* if any */
+ /* NB: the following are significant only if there is a command part
+ * or a do part
+ */
+ struct cmd_command *cmd ; /* NULL if empty command
+ or fails to parse */
+ node_type_t cnode ; /* node command is in */
- token_vector_t read_pipe_tokens ;
- token_vector_t write_pipe_tokens ;
-} ;
+ arg_vector_t args ; /* vector of arguments -- embedded */
-/* Command dispatch options */
-enum {
- cmd_no_queue = true,
- cmd_may_queue = false,
-} ;
+ /* NB: the following are significant only if an error is returned */
-/*------------------------------------------------------------------------------
- * Vector of spare token objects -- declared here to allow inlines, defined
- * in command_parse.c
- */
-extern token_vector_t spare_tokens ;
+ const char* emess ; /* parse error */
+ usize eloc ; /* error location */
-/*==============================================================================
- * Prototypes
- */
-extern void cmd_spare_tokens_init(void) ;
-extern void cmd_spare_tokens_free(void) ;
-
-extern cmd_parsed cmd_parse_init_new(cmd_parsed parsed) ;
-extern cmd_parsed cmd_parse_reset(cmd_parsed parsed, bool free_structure) ;
-
-extern cmd_token_type_t cmd_tokenise(cmd_parsed parsed, const char *line,
- node_type_t node) ;
-Inline const char* cmd_token_string(token t) ;
-Inline int cmd_token_count(token_vector tv) ;
-Inline token cmd_token_get(token_vector tv, vector_index_t i) ;
-Inline token cmd_token_pop(token_vector tv) ;
-Inline void cmd_token_push(token_vector tv, token t) ;
-Inline token cmd_token_shift(token_vector tv) ;
-Inline void cmd_token_unshift(token_vector tv, token t) ;
-Inline token cmd_token_make(void) ;
-Inline token cmd_token_new(cmd_token_type_t type, const char* p,
- size_t len, size_t tp) ;
-
-Inline void cmd_empty_token_vector(token_vector tv) ;
-Inline void cmd_empty_parsed_tokens(cmd_parsed parsed) ;
-
-Inline bool cmd_token_complete(token t) ;
-extern bool cmd_token_do_complete(token t) ;
-
-extern match_type_t cmd_ipv4_match (const char *str) ;
-extern match_type_t cmd_ipv4_prefix_match (const char *str) ;
-#if HAVE_IPV6
-extern match_type_t cmd_ipv6_match (const char *str) ;
-extern match_type_t cmd_ipv6_prefix_match (const char *str) ;
-#endif
-extern bool cmd_range_match (const char *range, const char *str) ;
+ /* NB: the following are significant only if respective part is
+ * present.
+ */
+ cmd_pipe_type_t in_pipe ; /* if any */
+ cmd_pipe_type_t out_pipe ; /* if any */
-/*==============================================================================
- * Inline Functions
- */
+ uint first_in_pipe ;
+ uint num_in_pipe ;
-/*------------------------------------------------------------------------------
- * Get pointer to token value.
- *
- * Returns NULL if token NULL or no string.
- */
-Inline char*
-cmd_token_value(token t)
-{
- return (t == NULL) ? NULL : qs_chars(t->qs) ;
-} ;
+ uint first_do ;
+ uint num_do ;
-/*------------------------------------------------------------------------------
- * Get string value of given token.
- *
- * Returns an empty (not NULL) string if token NULL or no string.
- */
-Inline const char*
-cmd_token_string(token t)
-{
- const char* s = cmd_token_value(t) ;
- return (s == NULL) ? "" : s ;
-} ;
+ uint first_command ;
+ uint num_command ;
-/*------------------------------------------------------------------------------
- * Get number of tokens in the given token vector
- */
-Inline int
-cmd_token_count(token_vector tv)
-{
- return vector_length(tv->body) ;
-} ;
+ uint first_out_pipe ;
+ uint num_out_pipe ;
-/*------------------------------------------------------------------------------
- * Get i'th token from given token vector -- zero origin
- */
-Inline token
-cmd_token_get(token_vector tv, vector_index_t i)
-{
- return vector_get_item(tv->body, i) ;
-} ;
+ uint first_comment ;
+ uint num_comment ;
-/*------------------------------------------------------------------------------
- * Pop token from end of given token vector -- if any.
- */
-Inline token
-cmd_token_pop(token_vector tv)
-{
- return vector_pop_item(tv->body) ;
-} ;
+ /* The following are significant after cmd_token_position() */
-/*------------------------------------------------------------------------------
- * Push token onto end of given token vector.
- */
-Inline void
-cmd_token_push(token_vector tv, token t)
-{
- vector_push_item(tv->body, t) ;
+ uint cti ; /* cursor token index -- may be eol token */
+ uint ctl ; /* cursor token length -- 0 <=> eol token */
+ int rp ; /* cursor relative to start of cursor token */
+
+ /* The following are used while filtering commands */
+
+ vector_t cmd_v ; /* working vector -- embedded */
+ vector_t item_v ; /* working vector -- embedded */
+
+ match_strength_t strongest ;
+ match_type_t best_complete ;
+
+ match_strength_t min_strength ; /* for execution */
+ bool strict ; /* for strict keyword match */
} ;
-/*------------------------------------------------------------------------------
- * Shift first token off front of given token vector -- if any.
- */
-Inline token
-cmd_token_shift(token_vector tv)
+enum
{
- return vector_shift_item(tv->body) ;
+ CMD_PARSED_INIT_ALL_ZEROS = (cmd_pipe_none == 0)
+ && TOKEN_VECTOR_INIT_ALL_ZEROS
+ && ARG_VECTOR_INIT_ALL_ZEROS
} ;
-/*------------------------------------------------------------------------------
- * Unshift token onto front of given token vector.
- */
-Inline void
-cmd_token_unshift(token_vector tv, token t)
+typedef struct cmd_parsed cmd_parsed_t[1] ;
+typedef struct cmd_parsed* cmd_parsed ;
+
+/* Command dispatch options */
+enum cmd_queue_b
{
- vector_unshift_item(tv->body, t) ;
+ cmd_no_queue = false,
+ cmd_queue_it = true,
} ;
+typedef enum cmd_queue_b cmd_queue_b ;
-/*------------------------------------------------------------------------------
- * Make a brand new token object
+/*==============================================================================
+ * Prototypes
*/
-Inline token
-cmd_token_make(void)
-{
- return XCALLOC(MTYPE_TOKEN, sizeof(struct token)) ;
+extern void cmd_compile(cmd_command cmd) ;
+extern void cmd_compile_check(cmd_command cmd) ;
- /* Zeroising the new structure sets:
- *
- * type = 0 -- cmd_tok_null
- * qs = zeroised qstring -- empty string
- */
- confirm(cmd_tok_null == 0) ;
-} ;
+extern cmd_parsed cmd_parsed_init_new(cmd_parsed parsed) ;
+extern cmd_parsed cmd_parsed_reset(cmd_parsed parsed,
+ free_keep_b free_structure) ;
+extern bool cmd_is_empty(elstring line) ;
-/*------------------------------------------------------------------------------
- * Create new 'cmd_tok_simple' token from given characters + length
- */
-Inline token
-cmd_token_new(cmd_token_type_t type, const char* p, size_t len, size_t tp)
-{
- token t ;
+extern cmd_return_code_t cmd_parse_command(cmd_parsed parsed,
+ node_type_t node, cmd_parse_type_t type) ;
- t = cmd_token_pop(spare_tokens) ;
- if (t == NULL)
- t = cmd_token_make() ;
+extern bool cmd_token_position(cmd_parsed parsed, qstring line) ;
+extern const char* cmd_help_preflight(cmd_parsed parsed) ;
+extern cmd_return_code_t cmd_completion(cmd_parsed parsed, node_type_t node) ;
- t->type = type ;
- qs_set_n(&t->qs, p, len) ;
- t->tp = tp ;
+extern void cmd_complete_keyword(cmd_parsed parsed,
+ int* pre, int* rep, int* ins, int* mov) ;
- return t ;
-} ;
-/*------------------------------------------------------------------------------
- * Discard given token -- give back to spare tokens list
- */
-Inline void
-cmd_token_discard(token t)
-{
- if (t != NULL)
- vector_push_item(spare_tokens->body, t) ;
-} ;
-/*------------------------------------------------------------------------------
- * Release contents of token vector -- move to the spare_token_strings.
+
+
+
+//extern vector cmd_make_strvec (const char *);
+//extern vector cmd_add_to_strvec (vector v, const char* str) ;
+//extern void cmd_free_strvec (vector);
+extern vector cmd_describe_command (const char* line, node_type_t node,
+ cmd_return_code_t* status) ;
+extern vector cmd_complete_command (vector, int, int *status);
+
+
+
+extern void cmd_tokenise(cmd_parsed parsed, qstring line) ;
+//extern cmd_return_code_t cmd_parse_error(cmd_parsed parsed, cmd_token t,
+// usize off, const char* mess) ;
+
+//Inline const char* cmd_token_string(cmd_token t) ;
+//Inline char* cmd_token_make_string(cmd_token t) ;
+//Inline int cmd_token_count(token_vector tv) ;
+//Inline cmd_token cmd_token_get(token_vector tv, vector_index_t i) ;
+//Inline void cmd_token_set(token_vector tv, vector_index_t i,
+// cmd_token_type_t type, const char* p, usize len, usize tp) ;
+
+//extern cmd_return_code_t cmd_token_complete(cmd_parsed parsed, cmd_token t) ;
+//Inline const char* cmd_token_string(cmd_token t) ;
+//extern cmd_return_code_t cmd_parse_in_pipe(cmd_parsed parsed, cmd_token t) ;
+//extern cmd_return_code_t cmd_parse_out_pipe(cmd_parsed parsed, cmd_token t) ;
+
+//extern match_type_t cmd_ipv4_match (const char *str) ;
+//extern match_type_t cmd_ipv4_prefix_match (const char *str) ;
+#if HAVE_IPV6
+//extern match_type_t cmd_ipv6_match (const char *str) ;
+//extern match_type_t cmd_ipv6_prefix_match (const char *str) ;
+#endif
+//extern bool cmd_range_match (const char *range, const char *str) ;
+
+/*==============================================================================
+ * Inline Functions
*/
-Inline void
-cmd_empty_token_vector(token_vector tv)
-{
- if (cmd_token_count(tv) != 0)
- vector_move_append(spare_tokens->body, tv->body) ;
-} ;
+
+Private cmd_token cmd_token_new(void) ;
/*------------------------------------------------------------------------------
- * Release contents of all token vectors in given parsed object.
+ * Get pointer to token value.
+ *
+ * Returns NULL if token NULL or no string.
*/
-Inline void
-cmd_empty_parsed_tokens(cmd_parsed parsed)
+Inline char*
+cmd_token_value(cmd_token t)
{
- cmd_empty_token_vector(parsed->tokens) ;
- cmd_empty_token_vector(parsed->read_pipe_tokens) ;
- cmd_empty_token_vector(parsed->write_pipe_tokens) ;
+ return (t == NULL) ? NULL : qs_char_nn(t->qs) ;
} ;
/*------------------------------------------------------------------------------
- * If token is incomplete (contains quotes or escapes) process those down.
+ * Get string value of given token.
*
- * Returns: true <=> OK
- * false => invalid escape
+ * Returns an empty (not NULL) string if token NULL or no string.
*/
-Inline bool
-cmd_token_complete(token t)
+Inline char*
+cmd_token_make_string(cmd_token t)
{
- return ((t->type & cmd_tok_incomplete) == 0) ? true
- : cmd_token_do_complete(t) ;
+ if (t->term)
+ return qs_char_nn(t->qs) ;
+ else
+ {
+ t->term = true ;
+ return qs_make_string(t->qs) ;
+ }
} ;
/*------------------------------------------------------------------------------
- * Initialise arg_vector object in cmd_parsed.
+ * Get i'th token from given token vector -- zero origin
*/
-Inline void
-cmd_arg_vector_init(cmd_parsed parsed)
+Inline cmd_token
+cmd_token_get(token_vector tv, vector_index_t i)
{
- vector_init_new(parsed->args->body, 0) ;
+ return vector_get_item(tv->body, i) ;
} ;
/*------------------------------------------------------------------------------
- * Free the body of the arg_vector object in cmd_parsed.
+ * Set i'th token from given token vector -- zero origin
*/
Inline void
-cmd_arg_vector_free(cmd_parsed parsed)
+cmd_token_set(token_vector tv, vector_index_t i,
+ cmd_token_type_t type, const char* p, usize len, usize tp)
{
- vector_reset(parsed->args->body, keep_it) ;
+ cmd_token t = cmd_token_get(tv, i) ;
+
+ if (t == NULL)
+ vector_set_item(tv->body, i, (t = cmd_token_new())) ;
+
+ t->type = type ;
+ t->term = false ;
+ t->tp = tp ;
+ qs_set_alias_n(t->qs, p, len) ;
+ qs_els_copy_nn(t->ot, t->qs) ;
} ;
/*------------------------------------------------------------------------------
diff --git a/lib/command_queue.c b/lib/command_queue.c
index 5f14abae..0a09d950 100644
--- a/lib/command_queue.c
+++ b/lib/command_queue.c
@@ -19,136 +19,564 @@
* Boston, MA 02111-1307, USA.
*/
-#include <zebra.h>
+#include "zconfig.h" /* for CONSUMED_TIME_CHECK */
+#include "misc.h"
-#include "mqueue.h"
#include "qpnexus.h"
-#include "memory.h"
+#include "mqueue.h"
+#include "thread.h"
+
#include "command_queue.h"
#include "command_execute.h"
-#include "vty.h"
-#include "uty.h"
-#include "vector.h"
-#include "qstring.h"
+#include "vty_command.h"
+#include "vty_io.h"
+#include "log.h"
+
+/*==============================================================================
+ * This command loop processes commands for VTY_TERMINAL and VTY_SHELL_SERVER.
+ *
+ * Commands appear from those sources, driven by socket I/O. Once a command
+ * line is ready it is passed out of the I/O pselect/select driven code
+ * as a message, and enters the command loop.
+ *
+ * If pipes are involved that is all dealt with in the command loop, which
+ * runs until the vin/vout stacks return to 0 -- the VTY_TERMINAL and
+ * VTY_SHELL_SERVER.
+ *
+ * There are further issues:
+ *
+ * 1) in the qpthreads world, commands are parsed in the CLI thread, but most
+ * are executed in the Routing thread. So parsed commands are passed, by
+ * message between threads.
+ *
+ * 2) input is expected to be non-blocking -- so the command loop will
+ * exit if a command line cannot be delivered immediately, and will be
+ * returned to later.
+ *
+ * 3) while a VTY is in the command loop it is marked vio->cmd_running.
+ *
+ * While that is true, the vty and the vty->exec are in the hands
+ * of the command loop. The vty->vio is always accessed under VTY_LOCK().
+ *
+ * 4) opening pipes is done in the CLI thread, in case of any possible
+ * blocking.
+ *
+ * 5) all output is to fifo buffers -- when output is pushed the CLI side
+ * is kicked to manage all output via pselect/select.
+ *
+ * In vty_io_basic() it is possible to set read/write ready and associated
+ * timeouts while running in the CMD thread, but that too depends on the
+ * passing of messages to the CLI thread.
+ *
+ * The smooth running of the command handling depends on the continued
+ * running of the CLI thread.
+ *
+ * To close a VTY must (eventually) arrange for vio->cmd_running to be cleared.
+ * While a vty is vio->cmd_running, it must be in one of these states:
+ *
+ * - on the vty_cli_nexus queue (or the combined queue)
+ * - executing in the vty_cli_nexus (or the single "both" nexus)
+ * - on the vty_cmd_nexus queue (if different)
+ * - executing in the vty_cmd_nexus (if different)
+ *
+ * Or, in the legacy threads world:
+ *
+ * - on the event queue
+ * - executing
+ *
+ * Where there is only one pthread (and in the legacy threads world) things are
+ * easy.
+ *
+ *
+ * This revoke runs in the CLI thread, and will catch the message if it is
+ * on either queue, and vty_revoke() will deal with it -- still in the CLI
+ * thread.
+ *
+ * The command cannot be running in the CLI thread, since that is where we
+ * are !
+ *
+ * That leaves the command running in the CMD thread. That will run to
+ * completion... the VTY may be closed in the meantime, which will shut down
+ * the reading side, so the command loop will come to a halt quite quickly.
+ * Note, however, that the smooth running of this process requires the CLI
+ * thread and its messages to be
+ *
+ *
+ */
+
+
+
/*------------------------------------------------------------------------------
- * Form of message passed with command to be executed
+ * Prototypes
*/
+static void cq_enqueue(struct vty *vty, qpn_nexus dst, cmd_exec_state_t state,
+ cmd_return_code_t ret) ;
+static void cq_action(mqueue_block mqb, mqb_flag_t flag);
+static int cq_thread(struct thread* thread) ;
+static void cq_process(vty vty) ;
-struct cq_command_args
+/*------------------------------------------------------------------------------
+ * Enqueue vty for parse and execution of a command.
+ *
+ * Sets the vio->cmd_running flag, which will be cleared only when the
+ * command is completed (including any nested pipes etc.) or when the vty
+ * is blocked on input or it is revoked.
+ *
+ * Note that from now on, exec->ret reflects the state of the return
+ * code when the vty was last enqueued.
+ *
+ * While vio_cmd_running is set, the CLI side MUST NOT fiddle with the main
+ * part of the VTY or with the vty->exec state.
+ */
+extern void
+cq_dispatch(vty vty, cmd_do_t to_do, qstring line)
{
- enum cmd_return_code ret ; /* return code from command */
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vty->exec->to_do = to_do ;
+ vty->exec->line = line ;
+ vty->vio->cmd_running = true ;
+ cq_enqueue(vty, vty_cli_nexus,
+ (to_do == cmd_do_command) ? exec_parse
+ : exec_special, CMD_SUCCESS) ;
} ;
-MQB_ARGS_SIZE_OK(cq_command_args) ;
/*------------------------------------------------------------------------------
- * Prototypes
+ * Enqueue vty for fetching a command line from an in_pipe which has just
+ * received more input.
+ *
+ * Sets the vio->cmd_running flag, which will be cleared only when the
+ * command is completed (including any nested pipes etc.) or when the vty
+ * is blocked on input or it is revoked.
+ *
+ * While vio_cmd_running is set, the CLI side MUST NOT fiddle with the main
+ * part of the VTY or with the vty->exec state.
*/
-static void cq_action(mqueue_block mqb, mqb_flag_t flag);
-static void cq_return(mqueue_block mqb, mqb_flag_t flag);
+extern void
+cq_go_fetch(vty vty)
+{
+ VTY_ASSERT_CLI_THREAD() ;
+
+ vty->vio->cmd_running = true ;
+ cq_enqueue(vty, vty_cli_nexus, exec_fetch, CMD_SUCCESS) ;
+} ;
/*------------------------------------------------------------------------------
- * Enqueue vty and argv[] for execution in given nexus.
+ * Enqueue vty for execution in given nexus or issue thread event.
+ *
+ * Note that preserves the return code state.
*/
-void
-cq_enqueue(struct vty *vty, qpn_nexus dst)
+static void
+cq_enqueue(vty vty, qpn_nexus dst, cmd_exec_state_t state,
+ cmd_return_code_t ret)
{
- struct cq_command_args* args ;
- mqueue_block mqb ;
+ cmd_exec exec = vty->exec ;
+
+ assert(vty->vio->cmd_running) ;
+ assert((dst == vty_cli_nexus) || (dst == vty_cmd_nexus)) ;
- assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+ exec->locus = dst ;
+ exec->state = state ;
+ exec->ret = ret ;
- mqb = mqb_init_new(NULL, cq_action, vty) ;
- args = mqb_get_args(mqb) ;
+ if (vty_nexus)
+ {
+ mqueue_block mqb ;
- args->ret = CMD_QUEUED ;
+ if ((mqb = exec->cq.mqb) == NULL)
+ mqb = exec->cq.mqb = mqb_init_new(NULL, cq_action, vty) ;
- mqueue_enqueue(dst->queue, mqb, 0) ;
-}
+ mqueue_enqueue(dst->queue, mqb, (dst == vty_cmd_nexus) ? mqb_priority
+ : mqb_ordinary) ;
+ }
+ else
+ {
+ assert(vty_cli_nexus == vty_cmd_nexus) ;
+ exec->cq.thread = thread_add_event(vty_master, cq_thread, vty, 0) ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
- * Dispatch a command from the message queue block
+ * Deal with command message -- in the qpthreads world.
*
- * When done (or revoked/deleted) return the message, so that the sender knows
- * that the command has been dealt with (one way or another).
- *
- * Note that if the command is revoked the return is set to CMD_QUEUED.
+ * Note that if the command is revoked....
*/
static void
cq_action(mqueue_block mqb, mqb_flag_t flag)
{
- struct vty *vty;
- struct cq_command_args* args ;
+ vty vty;
- assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+ assert(vty_nexus) ; /* must be running qnexus-wise */
vty = mqb_get_arg0(mqb);
- args = mqb_get_args(mqb) ;
+
+ assert(vty->exec->cq.mqb == mqb) ;
+ assert(vty->vio->cmd_running) ;
if (flag == mqb_action)
- {
- args->ret = cmd_dispatch_call(vty) ;
- assert(args->ret != CMD_QUEUED) ; /* avoid confusion ! */
- }
- else
- args->ret = CMD_QUEUED ;
+ return cq_process(vty) ; /* do not touch vty on way out */
+
+ mqb_free(mqb) ;
+ vty->exec->cq.mqb = NULL ;
+} ;
- mqb_set_action(mqb, cq_return) ;
- mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
+/*------------------------------------------------------------------------------
+ * Deal with command message -- in the legacy threads world.
+ *
+ * Note that if the command is revoked....
+ */
+static int
+cq_thread(struct thread* thread)
+{
+ vty vty = THREAD_ARG(thread) ;
+
+ assert(vty->exec->cq.thread == thread) ;
+ assert(vty->vio->cmd_running) ;
+
+ vty->exec->cq.thread = NULL ;
+
+ cq_process(vty) ; /* do not touch vty on way out */
+
+ return 0 ;
} ;
/*------------------------------------------------------------------------------
- * Accept return from command which has completed.
+ * Process command(s) queued from VTY_TERMINAL or from VTY_SHELL_SERVER.
*
- * The command line processing for the vty may be stalled (with read mode
- * disabled) waiting for the return from the command.
+ * To get into the process loop, or to get back to it when more input has
+ * arrived a message is sent:
*
- * Do not care whether the message is being revoked or not... the command
- * has completed and that must be signalled to the CLI. Any pending output
- * is released.
+ * cli -> cli -- exec_parse -- when a command line has been gathered
+ * from input and is ready to be processed.
*
- * The command itself may have been revoked before it was executed. That
- * makes no difference either... the output buffers will simply be empty.
- * However, the return code is CMD_QUEUED, to signal the fact that the command
- * was never executed.
+ * cli -> cli -- exec_fetch -- when was waiting for more input from an
+ * in_pipe of some sort.
+ *
+ * In the single-pthread world, where the vty_cli_nexus is the same as the
+ * vty_cmd_nexus (either because not running qpthreaded, or because are
+ * running in the legacy threads world), things are reasonably straightforward.
+ * The process runs to completion or until an in_pipe would block, and the
+ * above are the only messages in the system.
+ *
+ * In the multi-pthread world, things are more complicated... The above
+ * messages are sent, and in addition the following are sent back and forth to
+ * transfer between threads in the following states:
+ *
+ * cmd -> cli -- exec_open_pipes
+ * cli -> cmd -- exec_execute
+ * cmd -> cli -- exec_complete
+ *
+ * Note that this means that in the multi-pthread world, only one sort of
+ * message is sent to the vty_cmd_nexus.
+ *
+ * The vty_io_basic stuff allows the multi-pthread vty_cmd_nexus to set read
+ * and/or write ready state -- which may generate messages to the vty_cli_nexus.
+ *
+ * NB: if blocks in exec_fetch, then vty_cmd_fetch_line() will have cleared
+ * vio->cmd_running -- so on return from cq_process the vty MAY HAVE BEEN
+ * DELETED.
*/
static void
-cq_return(mqueue_block mqb, mqb_flag_t flag)
+cq_process(vty vty)
{
- struct vty *vty ;
- struct cq_command_args* args ;
+ cmd_exec exec = vty->exec ;
+ cmd_parsed parsed = exec->parsed ;
+ cmd_return_code_t ret = exec->ret ;
+
+ /* Have switch wrapped in a while(1) so that can change state by setting
+ * exec->state and doing "continue".
+ *
+ * Breaking out of the switch forces the exec state to exec_complete,
+ * with the current ret, in the CLI thread.
+ *
+ * The exec locus is either vty_cli_nexus or vty_cmd_nexus. If these
+ * are equal (including both NULL, in the legacy threads world), then is
+ * running single threaded -- otherwise is running multi-threaded.
+ */
+ while (1)
+ {
+ switch(exec->state)
+ {
+ /*--------------------------------------------------------------------
+ * Should not get here in exec_null state.
+ */
+ case exec_null:
+ zabort("exec->state == exec_null") ;
+ break ;
+
+ /*--------------------------------------------------------------------
+ * Deal with the "spacial" commands
+ */
+ case exec_special:
+ ret = vty_cmd_special(vty) ;
+ if (ret != CMD_SUCCESS)
+ break ;
+
+ exec->state = exec_fetch ;
+ /* continue by falling through */
+
+ /*--------------------------------------------------------------------
+ * Need another command to execute => in_pipe !
+ *
+ * Note that at vin_depth == 0 this will return CMD_EOF, and will
+ * drop out of the loop exec_complete.
+ *
+ * Will also receive CMD_EOF if the VTY has been closed.
+ *
+ * If multi-threaded: may be in either thread:
+ *
+ * vty_cmd_fetch_line() may set read and/or write ready -- so in
+ * vty_cmd_nexus may generate message to vty_cli_nexus.
+ */
+ case exec_fetch:
+ ret = vty_cmd_fetch_line(vty) ;
+
+ if (ret != CMD_SUCCESS)
+ {
+ /* If is CMD_WAITING, then the vty_cmd_fetch_line() will
+ * have prepared for command to be re-queued when there is more
+ * to be read. NB: vio->cmd_running has been cleared, so
+ * vty MAY HAVE BEEN DELETED !
+ *
+ * If is CMD_EOF then is "closing" or reached EOF on top-most
+ * pipe.
+ */
+ if (ret == CMD_WAITING)
+ return ; /* <<<< DONE, pro tem */
+
+ break ; /* => exec_complete */
+ } ;
+
+ if (exec->to_do != cmd_do_command)
+ {
+ exec->state = exec_special ;
+ continue ;
+ } ;
+
+ exec->state = exec_parse ;
+ /* continue by falling through */
+
+ /*--------------------------------------------------------------------
+ * Parse command in hand
+ *
+ * If multi-threaded: may be in either thread:
+ *
+ * vty_cmd_reflect_line() may set read and/or write ready -- so in
+ * vty_cmd_nexus may generate message to vty_cli_nexus.
+ */
+ case exec_parse:
+ cmd_tokenise(parsed, exec->line) ;
+ ret = cmd_parse_command(parsed, vty->node,
+ exec->parse_type | cmd_parse_execution) ;
+ if (ret != CMD_SUCCESS)
+ {
+ if (ret != CMD_EMPTY)
+ break ; /* stop on *any* parsing issue */
+
+ /* Empty lines from in_pipes we simply ignore.
+ *
+ * Don't expect to see them otherwise, but if we do then need
+ * to complete the command execution process.
+ */
+ ret = CMD_SUCCESS ;
+
+ exec->state = exec_success ;
+ continue ;
+ } ;
+
+ /* reflection now -- output always succeeds */
+ if (exec->reflect_enabled)
+ vty_cmd_reflect_line(vty) ;
+
+ /* continue by falling through */
+
+ /*--------------------------------------------------------------------
+ * Pipe work if any
+ *
+ * Will receive CMD_EOF if the VTY has been closed.
+ *
+ * If multi-threaded: must be in vty_cli_nexus to proceed -- so may
+ * generate message to transfer to vty_cli_nexus.
+ */
+ case exec_open_pipes:
+ if ((parsed->parts & cmd_parts_pipe) != 0)
+ {
+ exec->state = exec_open_pipes ;
+
+ /* If running pthreaded, do open pipes in vty_cli_nexus */
+ if (exec->locus != vty_cli_nexus)
+ return cq_enqueue(vty, vty_cli_nexus, exec_open_pipes, ret) ;
+
+ /* Now in vty_cli_nexus */
+ ret = cmd_open_pipes(vty) ;
+ if (ret != CMD_SUCCESS)
+ break ; /* quit if open fails */
+ } ;
- assert(vty_cli_nexus) ; /* must be running qnexus-wise */
+ exec->state = exec_execute ;
+ /* continue by falling through */
- vty = mqb_get_arg0(mqb) ;
- args = mqb_get_args(mqb) ;
+ /*--------------------------------------------------------------------
+ * Execute command in hand
+ *
+ * If multi-threaded: some commands can run in either thread, most must
+ * run in the vty_cmd_nexus -- so may generate message to transfer to
+ * the vty_cmd_nexus.
+ */
+ case exec_execute:
+ if ((parsed->parts & cmd_part_command) != 0)
+ {
+ /* If running pthreaded, do most commands in vty_cmd_nexus */
+ if ((exec->locus != vty_cmd_nexus) &&
+ (!cmd_is_direct(parsed)))
+ return cq_enqueue(vty, vty_cmd_nexus, exec_execute, ret) ;
- /* signal end of command */
- cmd_post_command(vty, args->ret) ;
- vty_queued_result(vty, args->ret) ;
+ /* Standard command handling */
+#ifdef CONSUMED_TIME_CHECK
+ {
+ RUSAGE_T before;
+ RUSAGE_T after;
+ unsigned long realtime, cputime;
-//if (qpthreads_enabled)
-// qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
+ GETRUSAGE(&before);
+#endif /* CONSUMED_TIME_CHECK */
- mqb_free(mqb);
-}
+ ret = cmd_execute(vty) ;
+
+#ifdef CONSUMED_TIME_CHECK
+ GETRUSAGE(&after);
+ realtime = thread_consumed_time(&after, &before, &cputime) ;
+ if (realtime > 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,
+ qs_make_string(exec->line)) ;
+ } ;
+#endif /* CONSUMED_TIME_CHECK */
+
+ if (ret != CMD_SUCCESS)
+ break ; /* stop */
+ } ;
+
+ exec->state = exec_success ;
+ /* continue by falling through */
+
+ /*--------------------------------------------------------------------
+ * Command has completed successfully -- so push the output.
+ *
+ * If the vout_depth > vin_depth, pops the vout's -- deals with single
+ * command lines with a pipe output.
+ *
+ * Output cannot block, so this always succeeds.
+ *
+ * Then loop back to fetch another command line, if can.
+ *
+ * If multi-threaded: may be in either thread:
+ *
+ * vty_cmd_success() may set write ready -- so in vty_cmd_nexus may
+ * generate message to vty_cli_nexus.
+ */
+ case exec_success:
+ assert(ret == CMD_SUCCESS) ;
+
+ vty_cmd_success(vty) ;
+
+ exec->state = exec_fetch ;
+ continue ;
+
+ /*--------------------------------------------------------------------
+ * End of the command loop !
+ *
+ * If multi-threaded: must return to the vty_cli_nexus.
+ */
+ case exec_complete:
+ if (exec->locus != vty_cli_nexus)
+ break ; /* Will send back to the cli */
+
+ /* Now in the vty_cli_nexus */
+
+ exec->state = exec_null ; /* all done ! */
+
+ vty_cmd_loop_exit(vty, ret) ; /* clears vio->cmd_running */
+
+ return ; /* <<<< Finally finished ! */
+
+ /*----------------------------------------------------------------------
+ * Unknown exec->state !
+ */
+ default:
+ zabort("unknown exec->state") ;
+ break ;
+ } ;
+
+ /* Have broken out of the switch(). This means that for good or ill,
+ * the command is complete. If we are not in the vty_cli_nexus, need to
+ * send back to the vty_cli_nexus for handling.
+ *
+ * At all times we treat CMD_EOF and CMD_SUCCESS.
+ *
+ * Otherwise, can continue in exec_complete state.
+ */
+ if (ret == CMD_EOF)
+ ret = CMD_SUCCESS ;
+
+ if (exec->locus != vty_cli_nexus)
+ return cq_enqueue(vty, vty_cli_nexus, exec_complete, ret) ;
+
+ exec->state = exec_complete ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
- * Revoke any messages related to the given VTY -- if running qnexus-wise.
+ * Revoke any message associated with the given vty from the command queue.
+ *
+ * This is used when a VTY_TERMINAL or a VTY_SHELL_SERVER is being closed.
+ *
+ * See cq_process above for discussion of what messages there may be. At any
+ * time there is at most one message in flight.
+ *
+ * If we find a message in flight, then we vty_cmd_loop_exit() to bring things
+ * to a stop tidily.
*
- * Revokes in vty_cmd_nexus -- so before command is started
- * and in vty_cli_nexus -- so after command has completed
+ * In the single-threaded world, expect that a command, once started will run
+ * to conclusion, or until blocked on an in_pipe. So after this revoke the
+ * vty should not be vio->cmd_running.
*
- * Can do nothing about any command actually being executed in the
- * vty_cmd_nexus.
+ * In the multi-threaded world, this revoke will catch any vty which is on
+ * either the vty_cli_nexus or vty_cmd_nexus queues, and force completion.
+ * After this revoke, vio->cmd_running will be true iff the command is
+ * currently being executed in the vty_cmd_nexus -- we expect that to run to
+ * conclusion or block on an in_pipe, shortly, which will be collected when
+ * the vty_cli_nexus message queue is next processed.
+ *
+ * Note that the revoke does not affect any vty_cli_nexus messages associated
+ * with the vty_io_basic operations.
*/
-void
-cq_revoke(struct vty *vty)
+extern void
+cq_revoke(vty vty)
{
- if (vty_cli_nexus)
+ int ret ;
+
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (vty_nexus)
{
- mqueue_revoke(vty_cmd_nexus->queue, vty) ;
- mqueue_revoke(vty_cli_nexus->queue, vty) ;
+ ret = mqueue_revoke(vty_cmd_nexus->queue, vty, 1) ;
+
+ if ((ret == 0) && (vty_cli_nexus != vty_cmd_nexus))
+ ret = mqueue_revoke(vty_cli_nexus->queue, vty, 1) ;
+ }
+ else
+ ret = thread_cancel_event(vty_master, vty) ;
+
+ if (ret != 0)
+ {
+ vty->exec->state = exec_null ;
+ vty_cmd_loop_exit(vty, vty->exec->ret) ;
} ;
-}
+} ;
+
+
diff --git a/lib/command_queue.h b/lib/command_queue.h
index 804dfa1f..0966bd68 100644
--- a/lib/command_queue.h
+++ b/lib/command_queue.h
@@ -22,10 +22,12 @@
#ifndef COMMAND_QUEUE_H_
#define COMMAND_QUEUE_H_
-#include "command.h"
+#include "vty_local.h"
+#include "command_execute.h"
#include "qpnexus.h"
-extern void cq_enqueue(struct vty *vty, qpn_nexus dst) ;
-extern void cq_revoke(struct vty* vty) ;
+extern void cq_dispatch(vty vty, cmd_do_t to_do, qstring line) ;
+extern void cq_go_fetch(vty vty) ;
+extern void cq_revoke(vty vty) ;
#endif /* COMMAND_QUEUE_H_ */
diff --git a/lib/elstring.h b/lib/elstring.h
index 051e5962..8ab1c58d 100644
--- a/lib/elstring.h
+++ b/lib/elstring.h
@@ -23,8 +23,6 @@
#define _ZEBRA_ELSTRING_H
#include "misc.h"
-#include "zassert.h"
-#include "memory.h"
/*==============================================================================
* This is some very simple support for strings which are Length/Body
@@ -34,8 +32,8 @@
* is the simplest possible encapsulation of strings which are NOT '\0'
* terminated.
*
- *
- *
+ * NB: this object knows NOTHING about whether there is a '\0' beyond the
+ * 'len' of the string.
*/
struct elstring
{
@@ -46,7 +44,6 @@ struct elstring
} body ;
ulen len ;
- bool term ; /* true <=> body is '\0' terminated */
} ;
typedef struct elstring elstring_t[1] ;
@@ -60,15 +57,48 @@ enum
ELSTRING_INIT_ALL_ZEROS = true
} ;
-/*------------------------------------------------------------------------------
- * Various forms of body -- NB:
+/*==============================================================================
+ * Pointer pair and unsigned pointer pair and const versions.
*/
+struct pp
+{
+ char* p ;
+ char* e ;
+} ;
+typedef struct pp pp_t[1] ;
+typedef struct pp* pp ;
-Inline void*
-els_body_nn(elstring els)
+struct cpp
{
- return els->body.v ;
+ const char* p ;
+ const char* e ;
} ;
+typedef struct cpp cpp_t[1] ;
+typedef struct cpp* cpp ;
+
+/*==============================================================================
+ * NULLs for all types of pp and for els
+ */
+Inline void pp_null(pp p) { p->p = p->e = NULL ; } ;
+Inline void cpp_null(cpp p) { p->p = p->e = NULL ; } ;
+
+Inline void els_null(elstring els) { els->body.v = NULL ; els->len = 0 ; } ;
+
+/*==============================================================================
+ * Access functions.
+ */
+
+Inline void* els_body(elstring els) ;
+Inline void* els_body_nn(elstring els) ;
+Inline ulen els_len(elstring els) ;
+Inline ulen els_len_nn(elstring els) ;
+Inline void* els_end(elstring els) ;
+Inline void* els_end_nn(elstring els) ;
+
+Inline void els_pp(pp p, elstring els) ;
+Inline void els_pp_nn(pp p, elstring els) ;
+Inline void els_cpp(cpp p, elstring els) ;
+Inline void els_cpp_nn(cpp p, elstring els) ;
Inline void*
els_body(elstring els)
@@ -76,10 +106,10 @@ els_body(elstring els)
return (els != NULL) ? els_body_nn(els) : NULL ;
} ;
-Inline ulen
-els_len_nn(elstring els)
+Inline void*
+els_body_nn(elstring els)
{
- return els->len ;
+ return els->body.v ;
} ;
Inline ulen
@@ -88,38 +118,70 @@ els_len(elstring els)
return (els != NULL) ? els_len_nn(els) : 0 ;
} ;
-Inline bool
-els_term_nn(elstring els)
+Inline ulen
+els_len_nn(elstring els)
{
- return els->term ;
+ return els->len ;
} ;
-Inline bool
-els_term(elstring els)
+Inline void*
+els_end(elstring els)
{
- return (els != NULL) ? els_term_nn(els) : false ;
+ return (els != NULL) ? els_end_nn(els) : NULL ;
} ;
-/*==============================================================================
- * All so simple that everything is implemented as Inline
- */
+Inline void*
+els_end_nn(elstring els)
+{
+ return (void*)((char*)els->body.v + els->len);
+} ;
-/*------------------------------------------------------------------------------
- * Initialise or create a new elstring
- */
-Inline elstring
-els_init_new(elstring els)
+Inline void
+els_pp(pp p, elstring els)
{
- if (els == NULL)
- els = XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+ if (els != NULL)
+ els_pp_nn(p, els) ;
else
- memset(els, 0, sizeof(elstring_t)) ;
+ pp_null(p) ;
+} ;
- confirm(ELSTRING_INIT_ALL_ZEROS) ;
+Inline void
+els_pp_nn(pp p, elstring els)
+{
+ p->p = els->body.v ;
+ p->e = p->p + els->len ;
+} ;
- return els ;
+Inline void
+els_cpp(cpp p, elstring els)
+{
+ if (els != NULL)
+ els_cpp_nn(p, els) ;
+ else
+ cpp_null(p) ;
+} ;
+
+Inline void
+els_cpp_nn(cpp p, elstring els)
+{
+ p->p = els->body.cv ;
+ p->e = p->p + els->len ;
} ;
+/*==============================================================================
+ * All so simple that most is implemented as Inline
+ */
+
+extern elstring els_init_new(elstring els) ;
+extern elstring els_new(void) ;
+extern elstring els_free(elstring els) ;
+
+extern int els_cmp(elstring a, elstring b) ;
+extern int els_cmp_word(elstring a, const char* w) ;
+extern int els_cmp_sig(elstring a, elstring b) ;
+extern bool els_equal(elstring a, elstring b) ;
+extern bool els_substring(elstring a, elstring b) ;
+
/*------------------------------------------------------------------------------
* Set elstring value from ordinary string.
*
@@ -134,7 +196,6 @@ els_set_nn(elstring els, const void* str)
{
els->body.cv = str ;
els->len = (str != NULL) ? strlen(str) : 0 ;
- els->term = (str != NULL) ;
} ;
/*------------------------------------------------------------------------------
@@ -148,7 +209,7 @@ Inline elstring
els_set(elstring els, const void* str)
{
if (els == NULL)
- els = XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+ els = els_new() ;
els_set_nn(els, str) ;
@@ -169,7 +230,6 @@ els_set_n_nn(elstring els, const void* body, ulen len)
{
els->body.cv = body ;
els->len = len ;
- els->term = false ;
} ;
/*------------------------------------------------------------------------------
@@ -183,7 +243,7 @@ Inline elstring
els_set_n(elstring els, const void* body, ulen len)
{
if (els == NULL)
- els = XCALLOC(MTYPE_TMP, sizeof(elstring_t)) ;
+ els = els_new() ;
els_set_n_nn(els, body, len) ;
@@ -203,31 +263,13 @@ els_clear(elstring els)
{
els->body.v = NULL ;
els->len = 0 ;
- els->term = false ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Release dynamically allocated elstring.
- *
- * Returns NULL.
- *
- * NB: it is the callers responsibility to free the contents of the elstring.
- * if that is required, before freeing the elstring itself.
- */
-Inline elstring
-els_free(elstring els)
-{
- if (els != NULL)
- XFREE(MTYPE_TMP, els) ;
-
- return NULL ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set elstring length. And set term false.
+ * Set elstring 'len'.
*
- * NB: it is the caller's responsibility to set a valid length !!
+ * NB: it is the caller's responsibility to set a valid body !!
*
* NB: elstring MUST NOT be NULL.
*/
@@ -235,120 +277,21 @@ Inline void
els_set_len_nn(elstring els, ulen len)
{
els->len = len ;
- els->term = false ;
} ;
/*------------------------------------------------------------------------------
- * Set elstring body. And set term false.
+ * Set elstring body.
*
* NB: it is the caller's responsibility to set a valid body !!
*
+ * NB: it is the caller's responsibility to set a valid 'len'.
+ *
* NB: elstring MUST NOT be NULL.
*/
Inline void
els_set_body_nn(elstring els, const void* body)
{
els->body.cv = body ;
- els->term = false ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set elstring terminated.
- *
- * NB: it is the caller's responsibility to set a valid body !!
- *
- * NB: elstring MUST NOT be NULL.
- */
-Inline void
-els_set_term_nn(elstring els, bool term)
-{
- els->term = term ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Compare two elstrings -- returns the usual -ve, 0, +ve cmp result.
- */
-Inline int
-els_cmp(elstring a, elstring b)
-{
- const uchar* ap ;
- const uchar* bp ;
- ulen al, bl ;
- ulen n ;
-
- ap = els_body(a) ;
- bp = els_body(b) ;
- al = els_len(a) ;
- bl = els_len(b) ;
-
- n = (al <= bl) ? al : bl ;
-
- while (n)
- {
- int d = *ap++ - *bp++ ;
- if (d != 0)
- return d ;
- --n ;
- } ;
-
- return al < bl ? -1 : (al == bl) ? 0 : +1 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Are two elstrings equal ? -- returns true if strings equal.
- */
-Inline bool
-els_equal(elstring a, elstring b)
-{
- const uchar* ap ;
- const uchar* bp ;
- ulen n ;
-
- n = els_len(b) ;
- if (n != els_len(a))
- return false ;
-
- ap = els_body(a) ;
- bp = els_body(b) ;
-
- while (n)
- {
- if (*ap++ != *bp++)
- return false ;
- --n ;
- } ;
-
- return true ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Is 'b' a leading substring of 'a' ? -- returns true if it is.
- *
- * If 'b' is empty it is always a leading substring.
- */
-Inline int
-els_substring(elstring a, elstring b)
-{
- const uchar* ap ;
- const uchar* bp ;
- ulen n ;
-
- n = els_len(b) ;
- if (n > els_len(a))
- return false ;
-
- ap = els_body(a) ;
- bp = els_body(b) ;
-
- while (n)
- {
- if (*ap++ != *bp++)
- return false ;
- --n ;
- } ;
-
- return true ;
} ;
#endif /* _ZEBRA_ELSTRING_H */
-
diff --git a/lib/heap.c b/lib/heap.c
index 7821a9c9..708e88d0 100644
--- a/lib/heap.c
+++ b/lib/heap.c
@@ -19,9 +19,7 @@
* 02111-1307, USA.
*/
-#include <zebra.h>
-
-#include "zassert.h"
+#include "misc.h"
#include "heap.h"
#include "memory.h"
diff --git a/lib/if.c b/lib/if.c
index b1d0d49a..66a8bfef 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -556,7 +556,7 @@ DEFUN (interface,
#endif /* SUNOS_5 */
vty->index = ifp;
- vty_set_node(vty, INTERFACE_NODE) ;
+ vty->node = INTERFACE_NODE ;
return CMD_SUCCESS;
}
diff --git a/lib/if.h b/lib/if.h
index c99ab81b..b4b83cd5 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -300,12 +300,12 @@ extern char *if_indextoname (unsigned int, char *);
/* Exported variables. */
extern struct list *iflist;
-extern struct cmd_element interface_desc_cmd;
-extern struct cmd_element no_interface_desc_cmd;
-extern struct cmd_element interface_cmd;
-extern struct cmd_element no_interface_cmd;
-extern struct cmd_element interface_pseudo_cmd;
-extern struct cmd_element no_interface_pseudo_cmd;
-extern struct cmd_element show_address_cmd;
+extern struct cmd_command interface_desc_cmd;
+extern struct cmd_command no_interface_desc_cmd;
+extern struct cmd_command interface_cmd;
+extern struct cmd_command no_interface_cmd;
+extern struct cmd_command interface_pseudo_cmd;
+extern struct cmd_command no_interface_pseudo_cmd;
+extern struct cmd_command show_address_cmd;
#endif /* _ZEBRA_IF_H */
diff --git a/lib/keychain.c b/lib/keychain.c
index 2f8a0b77..57ccf7cc 100644
--- a/lib/keychain.c
+++ b/lib/keychain.c
@@ -238,7 +238,7 @@ DEFUN (key_chain,
keychain = keychain_get (argv[0]);
vty->index = keychain;
- vty_set_node(vty, KEYCHAIN_NODE) ;
+ vty->node = KEYCHAIN_NODE ;
return CMD_SUCCESS;
}
@@ -281,7 +281,7 @@ DEFUN (key,
VTY_GET_INTEGER ("key identifier", index, argv[0]);
key = key_get (keychain, index);
vty->index_sub = key;
- vty_set_node(vty, KEYCHAIN_KEY_NODE) ;
+ vty->node = KEYCHAIN_KEY_NODE ;
return CMD_SUCCESS;
}
@@ -309,7 +309,7 @@ DEFUN (no_key,
key_delete (keychain, key);
- vty_set_node(vty, KEYCHAIN_NODE) ;
+ vty->node = KEYCHAIN_NODE ;
return CMD_SUCCESS;
}
@@ -850,16 +850,20 @@ DEFUN (send_lifetime_duration_month_day,
static struct cmd_node keychain_node =
{
- KEYCHAIN_NODE,
- "%s(config-keychain)# ",
- 1
+ .node = KEYCHAIN_NODE,
+ .prompt ="%s(config-keychain)# ",
+
+ .config_to_vtysh = true
};
static struct cmd_node keychain_key_node =
{
- KEYCHAIN_KEY_NODE,
- "%s(config-keychain-key)# ",
- 1
+ .node = KEYCHAIN_KEY_NODE,
+ .prompt = "%s(config-keychain-key)# ",
+
+ .parent = KEYCHAIN_NODE,
+
+ .config_to_vtysh = true
};
static int
diff --git a/lib/keystroke.c b/lib/keystroke.c
index c6eb811e..13b79e39 100644
--- a/lib/keystroke.c
+++ b/lib/keystroke.c
@@ -272,7 +272,7 @@ struct keystroke_state
struct keystroke_stream
{
- vio_fifo_t fifo ; /* the keystrokes */
+ vio_fifo_t fifo ; /* the keystrokes -- embedded */
keystroke_callback* iac_callback ;
void* iac_callback_context ;
@@ -301,7 +301,8 @@ enum { keystroke_buffer_len = 2000 } ; /* should be plenty ! */
static void keystroke_in_push(keystroke_stream stream, uint8_t u) ;
static void keystroke_in_pop(keystroke_stream stream) ;
-inline static int keystroke_set_null(keystroke_stream stream, keystroke stroke);
+inline static bool keystroke_set_null(keystroke_stream stream,
+ keystroke stroke) ;
inline static uint8_t keystroke_get_byte(keystroke_stream stream) ;
inline static void keystroke_add_raw(keystroke_stream stream, uint8_t u) ;
static void keystroke_put_char(keystroke_stream stream, uint32_t u) ;
@@ -368,7 +369,7 @@ keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
stream->iac_callback = iac_callback ;
stream->iac_callback_context = iac_callback_context ;
- vio_fifo_init_new(&stream->fifo, keystroke_buffer_len) ;
+ vio_fifo_init_new(stream->fifo, keystroke_buffer_len) ;
stream->CSI = (csi_char != '\0') ? csi_char : 0x1B ;
@@ -385,7 +386,7 @@ keystroke_stream_free(keystroke_stream stream)
{
if (stream != NULL)
{
- vio_fifo_reset_keep(&stream->fifo) ;
+ vio_fifo_reset(stream->fifo, keep_it) ;
XFREE(MTYPE_KEY_STREAM, stream) ;
} ;
@@ -407,7 +408,7 @@ keystroke_stream_free(keystroke_stream stream)
extern bool
keystroke_stream_empty(keystroke_stream stream)
{
- return (stream == NULL) || vio_fifo_empty(&stream->fifo) ;
+ return (stream == NULL) || vio_fifo_empty(stream->fifo) ;
} ;
/*------------------------------------------------------------------------------
@@ -428,7 +429,7 @@ keystroke_stream_eof(keystroke_stream stream)
* is converted to a broken keystroke and placed in the stream.
* (So eof_met => no partial keystroke.)
*/
- return (stream == NULL) || (vio_fifo_empty(&stream->fifo) && stream->eof_met);
+ return (stream == NULL) || (vio_fifo_empty(stream->fifo) && stream->eof_met);
} ;
/*------------------------------------------------------------------------------
@@ -441,7 +442,7 @@ keystroke_stream_eof(keystroke_stream stream)
extern void
keystroke_stream_set_eof(keystroke_stream stream)
{
- vio_fifo_reset_keep(&stream->fifo) ;
+ vio_fifo_reset(stream->fifo, keep_it) ;
stream->eof_met = true ; /* essential information */
@@ -858,10 +859,10 @@ keystroke_in_pop(keystroke_stream stream)
/*==============================================================================
* Fetch next keystroke from keystroke stream
*
- * Returns: 1 => have a stroke type != ks_null
- * 0 => stroke type is ks_null (may be EOF).
+ * Returns: true => have a stroke type != ks_null
+ * false => stroke type is ks_null (may be EOF).
*/
-extern int
+extern bool
keystroke_get(keystroke_stream stream, keystroke stroke)
{
int b ;
@@ -869,7 +870,7 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
uint8_t* e ;
/* Get first byte and deal with FIFO empty response */
- b = vio_fifo_get_byte(&stream->fifo) ;
+ b = vio_fifo_get_byte(stream->fifo) ;
if (b < 0)
return keystroke_set_null(stream, stroke) ;
@@ -884,7 +885,7 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
stroke->len = 1 ;
stroke->buf[0] = b ;
- return 1 ;
+ return true ;
} ;
/* Sex the compound keystroke */
@@ -954,7 +955,7 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
zabort("unknown keystroke type") ;
} ;
- return 1 ;
+ return true ;
} ;
/*------------------------------------------------------------------------------
@@ -962,7 +963,7 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
*
* Returns: 0
*/
-inline static int
+inline static bool
keystroke_set_null(keystroke_stream stream, keystroke stroke)
{
stroke->type = ks_null ;
@@ -971,7 +972,7 @@ keystroke_set_null(keystroke_stream stream, keystroke stroke)
stroke->flags = 0 ;
stroke->len = 0 ;
- return 0 ;
+ return false ;
} ;
/*------------------------------------------------------------------------------
@@ -983,7 +984,7 @@ keystroke_set_null(keystroke_stream stream, keystroke stroke)
inline static uint8_t
keystroke_get_byte(keystroke_stream stream)
{
- int b = vio_fifo_get_byte(&stream->fifo) ;
+ int b = vio_fifo_get_byte(stream->fifo) ;
passert(b >= 0) ;
@@ -1013,7 +1014,7 @@ static void
keystroke_put_char(keystroke_stream stream, uint32_t u)
{
if (u < 0x80)
- vio_fifo_put_byte(&stream->fifo, (uint8_t)u) ;
+ vio_fifo_put_byte(stream->fifo, (uint8_t)u) ;
else
{
uint8_t buf[4] ;
@@ -1140,12 +1141,12 @@ keystroke_put(keystroke_stream stream, enum keystroke_type type, bool broken,
type |= kf_truncated ;
} ;
- vio_fifo_put_byte(&stream->fifo,
+ vio_fifo_put_byte(stream->fifo,
kf_compound | (broken ? kf_broken : 0) | type) ;
- vio_fifo_put_byte(&stream->fifo, len) ;
+ vio_fifo_put_byte(stream->fifo, len) ;
if (len > 0)
- vio_fifo_put(&stream->fifo, (void*)bytes, len) ;
+ vio_fifo_put_bytes(stream->fifo, (void*)bytes, len) ;
} ;
/*------------------------------------------------------------------------------
diff --git a/lib/keystroke.h b/lib/keystroke.h
index 10494df0..67c6cd0f 100644
--- a/lib/keystroke.h
+++ b/lib/keystroke.h
@@ -47,6 +47,7 @@ enum keystroke_type
ks_type_reserved = 0x0F,
} ;
+typedef enum keystroke_type keystroke_type ;
CONFIRM(ks_type_count <= ks_type_reserved) ;
enum keystroke_null
@@ -68,8 +69,9 @@ enum keystroke_flags
kf_type_mask = 0x0F, /* extraction of type */
} ;
+typedef enum keystroke_type keystroke_flags ;
-CONFIRM(ks_type_reserved == kf_type_mask) ;
+CONFIRM(ks_type_reserved == (keystroke_type)kf_type_mask) ;
typedef struct keystroke* keystroke ;
typedef struct keystroke_stream* keystroke_stream ;
@@ -182,7 +184,7 @@ keystroke_stream_eof(keystroke_stream stream) ;
extern void
keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
keystroke steal) ;
-extern int
+extern bool
keystroke_get(keystroke_stream stream, keystroke stroke) ;
#endif /* _ZEBRA_KEYSTROKE_H */
diff --git a/lib/list_util.c b/lib/list_util.c
index 8ce01c03..f73244a4 100644
--- a/lib/list_util.c
+++ b/lib/list_util.c
@@ -49,28 +49,28 @@
*
* Note again the cast to (void**).
*
- * Returns: 0 => OK -- removed item from list (OR item == NULL)
- * -1 => item not found on list
+ * Returns: true => removed item from list
+ * false => item not found on list (or item == NULL)
*/
-extern int
+extern bool
ssl_del_func(void* p_this, void* item, size_t link_offset)
{
void* this ;
if (item == NULL)
- return 0 ;
+ return false ;
while ((this = *(void**)p_this) != item)
{
if (this == NULL)
- return -1 ;
+ return false ;
p_this = _sl_p_next(this, link_offset) ;
} ;
*(void**)p_this = _sl_next(item, link_offset) ;
- return 0 ;
+ return true ;
} ;
/*==============================================================================
diff --git a/lib/list_util.h b/lib/list_util.h
index bd796779..f6a01d93 100644
--- a/lib/list_util.h
+++ b/lib/list_util.h
@@ -133,10 +133,10 @@ struct dl_void_base_pair base_pair(void*) ;
*
* ssl_del(base, item, next) -- delete from list
*
- * Treat as function returning int. Does nothing if the item is NULL.
+ * Treat as function returning bool. Does nothing if the item is NULL.
*
- * Returns: 0 => OK -- removed item from list (OR item == NULL)
- * -1 => item not found on list
+ * Returns: true => removed item from list
+ * false => item not found on list (or item was NULL)
*
* ssl_del_head(base, next) -- delete head of list
*
@@ -237,7 +237,7 @@ struct dl_void_base_pair base_pair(void*) ;
(base) = item ; \
} while (0)
-extern int ssl_del_func(void* p_this, void* obj, size_t link_offset)
+extern bool ssl_del_func(void* p_this, void* obj, size_t link_offset)
__attribute__((noinline)) ;
#define ssl_del(base, item, next) \
@@ -506,7 +506,7 @@ extern int ssl_del_func(void* p_this, void* obj, size_t link_offset)
*
* Treat as function returning void*.
*
- * Returns old head in dst and as return from "function".
+ * Returns old tail in dst and as return from "function".
*
* Returns NULL and sets dst == NULL if list is empty.
*
diff --git a/lib/log.c b/lib/log.c
index 72f967f6..4da1e5e8 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -26,9 +26,8 @@
#include "log.h"
#include "vty.h"
-#include "uty.h"
#include "memory.h"
-#include "command.h"
+#include "command_local.h"
#ifndef SUNOS_5
#include <sys/un.h>
#endif
diff --git a/lib/log.h b/lib/log.h
index 256d09a3..6f4c8265 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -26,6 +26,7 @@
#define _ZEBRA_LOG_H
#include <syslog.h>
+#include <stdio.h>
#include "vargs.h"
#include "pthread_safe.h"
@@ -109,13 +110,6 @@ extern struct zlog *openzlog (const char *progname, zlog_proto_t protocol,
/* Close zlog function. */
extern void closezlog (struct zlog *zl);
-/* GCC have printf type attribute check. */
-#ifdef __GNUC__
-#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
-#else
-#define PRINTF_ATTRIBUTE(a,b)
-#endif /* __GNUC__ */
-
/* Generic function for zlog. */
extern void zlog (struct zlog *zl, int priority, const char *format, ...)
PRINTF_ATTRIBUTE(3, 4);
diff --git a/lib/memory.c b/lib/memory.c
index c13404d2..e15492c9 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -72,7 +72,7 @@ mem_tracker_zeroise(struct mem_tracker* mem)
memset(mem, 0, sizeof(struct mem_tracker)) ;
} ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
#include "mem_tracker.c"
#endif
@@ -130,7 +130,7 @@ zmalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
else
{
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_malloc(mtype, memory, size, name) ;
#endif
UNLOCK ;
@@ -159,7 +159,7 @@ zcalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
else
{
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_malloc(mtype, memory, size, name) ;
#endif
UNLOCK ;
@@ -188,7 +188,7 @@ zrealloc (enum MTYPE mtype, void *ptr, size_t size MEMORY_TRACKER_NAME)
{
if (ptr == NULL)
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_realloc(mtype, ptr, memory, size, name) ;
#endif
UNLOCK ;
@@ -210,7 +210,7 @@ zfree (enum MTYPE mtype, void *ptr)
assert(mstat.mt[mtype].alloc > 0) ;
mstat.mt[mtype].alloc--;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_free(mtype, ptr) ;
#endif
@@ -230,7 +230,7 @@ zstrdup (enum MTYPE mtype, const char *str MEMORY_TRACKER_NAME)
LOCK ;
- dup = strdup (str);
+ dup = strdup (str ? str : "");
if (dup == NULL)
{
UNLOCK ;
@@ -239,7 +239,7 @@ zstrdup (enum MTYPE mtype, const char *str MEMORY_TRACKER_NAME)
else
{
mstat.mt[mtype].alloc++;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_md_malloc(mtype, dup, strlen(str)+1, name) ;
#endif
UNLOCK ;
@@ -443,7 +443,7 @@ show_memory_type_vty (struct vty *vty, const char* name,
vty_out (vty, "-----------------------------%s", VTY_NEWLINE) ;
vty_out (vty, "%-30s:", name) ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
show_memory_tracker_detail(vty, mt, alloc) ;
#else
vty_out (vty, " %10ld", alloc) ;
@@ -464,13 +464,13 @@ show_memory_vty (struct vty *vty, struct memory_list *m, struct mlist* ml,
struct mem_tracker mem_one ;
struct mem_tracker* mt ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
struct mem_type_tracker mem_tt ;
#endif
LOCK ;
mst = mstat ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mem_tt = mem_type_tracker ;
#endif
UNLOCK ;
@@ -499,7 +499,7 @@ show_memory_vty (struct vty *vty, struct memory_list *m, struct mlist* ml,
else
{
alloc = mst.mt[m->index].alloc ;
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
mt = &(mem_tt.mt[m->index]) ;
#else
mt = &mem_one ;
@@ -580,7 +580,7 @@ DEFUN_CALL (show_memory_summary,
"Memory statistics\n"
"Summary memory statistics\n")
{
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
show_memory_tracker_summary(vty) ;
#else
long alloc = 0 ;
@@ -613,7 +613,7 @@ DEFUN_CALL (show_memory_all,
#ifdef HAVE_MALLINFO
needsep |= show_memory_mallinfo (vty);
#endif /* HAVE_MALLINFO */
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
needsep |= show_memory_tracker_summary(vty) ;
#endif
diff --git a/lib/memory.h b/lib/memory.h
index 6cc95a5b..4fccb079 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -58,11 +58,21 @@ typedef enum MTYPE mtype_t ;
mtype_zstrdup (__FILE__, __LINE__, (mtype), (str))
#else
-#ifdef QDEBUG
-#define MEMORY_TRACKER 1
+#ifdef MEMORY_TRACKER /* Can be forced from outside */
+# if MEMORY_TRACKER
+# define MEMORY_TRACKER 1 /* Force 1 or 0 */
+#else
+# define MEMORY_TRACKER 0
+# endif
+#else
+# ifdef QDEBUG
+# define MEMORY_TRACKER 1 /* Follow QDEBUG */
+#else
+# define MEMORY_TRACKER 0
+# endif
#endif
-#ifdef MEMORY_TRACKER
+#if MEMORY_TRACKER
#define MEMORY_TRACKER_NAME , const char* name
#define MEMORY_TRACKER_FUNC , __func__
#else
diff --git a/lib/memtypes.c b/lib/memtypes.c
index e5a591d6..7e73e4c1 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -16,7 +16,8 @@ struct memory_list memory_list_lib[] =
{
{ MTYPE_TMP, "Temporary memory" },
{ MTYPE_STRING, "String (general)" },
- { MTYPE_STRVEC, "String vector" },
+ { MTYPE_CMD_ITEM, "Command item" },
+ { MTYPE_CMD_STRING, "Command string" },
{ MTYPE_VECTOR, "Vector structure" },
{ MTYPE_VECTOR_BODY, "Vector body" },
{ MTYPE_SYMBOL_TABLE, "Symbol Table structure" },
@@ -44,13 +45,14 @@ struct memory_list memory_list_lib[] =
{ MTYPE_QPN_NEXUS, "qtn nexus" },
{ MTYPE_TSD, "Thread specific data" },
{ MTYPE_VTY, "VTY" },
+ { MTYPE_CMD_EXEC, "Command exec context" },
{ MTYPE_CMD_PARSED, "Parsed command" },
{ MTYPE_TOKEN, "Command token" },
- { MTYPE_MARSHAL, "marshalled commands" },
{ MTYPE_VTY_OUT_BUF, "VTY output buffer" },
{ MTYPE_VTY_HIST, "VTY history" },
{ MTYPE_VTY_NAME, "VTY name" },
{ MTYPE_KEY_STREAM, "Keystroke Stream" },
+ { MTYPE_VTY_CLI, "VTY CLI" },
{ MTYPE_VIO_FIFO, "VTY IO FIFO" },
{ MTYPE_VIO_FIFO_LUMP, "VTY IO FIFO Lump" },
{ MTYPE_VIO_LC, "VTY IO Line Control" },
@@ -87,7 +89,6 @@ struct memory_list memory_list_lib[] =
{ MTYPE_ROUTE_MAP_RULE, "Route map rule" },
{ MTYPE_ROUTE_MAP_RULE_STR, "Route map rule str" },
{ MTYPE_ROUTE_MAP_COMPILED, "Route map compiled" },
- { MTYPE_DESC, "Command desc" },
{ MTYPE_KEY, "Key" },
{ MTYPE_KEYCHAIN, "Key chain" },
{ MTYPE_IF_RMAP, "Interface route map" },
diff --git a/lib/misc.h b/lib/misc.h
index 9437b617..1bd0680b 100644
--- a/lib/misc.h
+++ b/lib/misc.h
@@ -22,13 +22,16 @@
#ifndef _ZEBRA_MISC_H
#define _ZEBRA_MISC_H
+#include "zconfig.h"
+
/* Stuff which we generally expect to have */
-#include <stddef.h>
-#include <stdint.h>
#include <limits.h>
+#include <string.h>
+
#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
-#include <string.h>
#include "zassert.h"
@@ -43,6 +46,9 @@
*/
#define Private extern
+/* For use in switch/case */
+#define fall_through
+
/* Other names of true/false */
enum on_off
{
@@ -67,14 +73,26 @@ enum free_keep
} ;
typedef enum free_keep free_keep_b ;
-/* We really want to be able to assume that an int is at least 32 bits */
-CONFIRM(UINT_MAX >= 0xFFFFFFFF) ;
+/* We really want to be able to assume that an int is at least 32 bits
+ * and that a long is at least 64 bits !
+ */
+CONFIRM(UINT_MAX >= 0xFFFFFFFF) ;
+CONFIRM(ULONG_MAX >= 0xFFFFFFFFFFFFFFFF) ;
/* Some useful shorthand */
typedef unsigned char byte ;
typedef unsigned char uchar ;
+
typedef unsigned int uint ;
typedef unsigned int usize ;
typedef unsigned int ulen ;
+typedef unsigned long ulong ;
+
+/* cvp == const void* -- ptr to constant void
+ * cvp* == void * const* -- ptr to ptr to constant void
+ * const cvp* == void const* const* -- ptr to constant ptr to constant void
+ */
+typedef const void* cvp ;
+
#endif /* _ZEBRA_MISC_H */
diff --git a/lib/mqueue.c b/lib/mqueue.c
index 8fa9fbd5..c7037a64 100644
--- a/lib/mqueue.c
+++ b/lib/mqueue.c
@@ -254,7 +254,7 @@ mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type)
extern void
mqueue_empty(mqueue_queue mq)
{
- mqueue_revoke(mq, NULL) ;
+ mqueue_revoke(mq, NULL, 0) ;
assert((mq->head == NULL) && (mq->count == 0)) ;
} ;
@@ -268,8 +268,11 @@ mqueue_empty(mqueue_queue mq)
* NB: there MUST NOT be ANY waiters !
*/
extern mqueue_queue
-mqueue_reset(mqueue_queue mq, int free_structure)
+mqueue_reset(mqueue_queue mq, free_keep_b free_structure)
{
+ if (mq == NULL)
+ return NULL ;
+
mqueue_empty(mq) ;
passert(mq->waiters == 0) ;
@@ -327,13 +330,10 @@ mqueue_local_init_new(mqueue_local_queue lmq)
*
* Dequeues entries and dispatches them "mqb_destroy", to empty the queue.
*
- * See: mqueue_local_reset_keep(lmq)
- * mqueue_local_reset_free(lmq)
- *
* Returns address of Local Message Queue
*/
extern mqueue_local_queue
-mqueue_local_reset(mqueue_local_queue lmq, int free_structure)
+mqueue_local_reset(mqueue_local_queue lmq, free_keep_b free_structure)
{
mqueue_block mqb ;
@@ -533,7 +533,7 @@ static void mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig) ;
* never be any waiters... so no kicking is ever done.
*/
extern void
-mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, enum mqb_rank priority)
+mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, mqb_rank_b priority)
{
if (mq == NULL)
return mqb_dispatch_destroy(mqb) ;
@@ -755,35 +755,41 @@ done:
*
* Revokes by calling mqb_dispatch_destroy().
*
- * During a revoke() operation more items may be enqueued, but no other mqueue
- * operations may be performed. Enqueued items may promptly be revoked, except
- * for priority items if the revoke operation has already moved past the last
- * priority item.
+ * NB: for safety, holds the queue locked for the duration of the revoke
+ * operation.
+ *
+ * If the destroy code can handle it, this means that can revoke stuff
+ * from one thread even though it is usually only dequeued by another.
+ *
+ * The danger is that if queues get very long, and many revokes happen,
+ * may (a) spend a lot of time scanning the message queue, which stops
+ * other threads as soon as they try to enqueue anything, and (b) if this
+ * happens a lot, could end up in an O(n^2) thing scanning the message
+ * queue once for each revoked object type.
*
* If mq is NULL, does nothing.
+ *
+ * If num > 0, stops after revoking that many messages.
+ *
+ * Returns: number of messages revoked.
*/
-extern void
-mqueue_revoke(mqueue_queue mq, void* arg0)
+extern int
+mqueue_revoke(mqueue_queue mq, void* arg0, int num)
{
mqueue_block mqb ;
mqueue_block prev ;
+ int did ;
if (mq == NULL)
- return ;
+ return 0 ;
qpt_mutex_lock(&mq->mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ did = 0 ;
prev = NULL ;
- while (1)
+ mqb = mq->head ;
+ while (mqb != NULL)
{
- if (prev == NULL)
- mqb = mq->head ;
- else
- mqb = prev->next ;
-
- if (mqb == NULL)
- break ;
-
if ((arg0 == NULL) || (arg0 == mqb->arg0))
{
assert(mq->count > 0) ;
@@ -800,16 +806,30 @@ mqueue_revoke(mqueue_queue mq, void* arg0)
mq->tail_priority = prev ;
--mq->count ;
+ ++did ;
+
+ mqb_dispatch_destroy(mqb) ;
- qpt_mutex_unlock(&mq->mutex) ;
- mqb_dispatch_destroy(mqb) ;
- qpt_mutex_lock(&mq->mutex) ;
+ if (num == 1)
+ break ;
+ else if (num > 1)
+ --num ;
+
+ if (prev == NULL)
+ mqb = mq->head ;
+ else
+ mqb = prev->next ;
}
else
- prev = mqb ;
+ {
+ prev = mqb ;
+ mqb = mqb->next ;
+ } ;
} ;
qpt_mutex_unlock(&mq->mutex) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+
+ return did ;
} ;
/*------------------------------------------------------------------------------
@@ -930,8 +950,11 @@ mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
* Otherwise zeroises the structure, and returns address of same.
*/
extern mqueue_thread_signal
-mqueue_thread_signal_reset(mqueue_thread_signal mqt, int free_structure)
+mqueue_thread_signal_reset(mqueue_thread_signal mqt, free_keep_b free_structure)
{
+ if (mqt == NULL)
+ return NULL ;
+
passert(mqt->prev == NULL) ;
if (free_structure)
diff --git a/lib/mqueue.h b/lib/mqueue.h
index 68dceb15..f33af564 100644
--- a/lib/mqueue.h
+++ b/lib/mqueue.h
@@ -191,83 +191,48 @@ struct mqueue_local_queue
* Functions
*/
-extern void
-mqueue_initialise(void) ;
-
-extern void
-mqueue_finish(void) ;
-
-extern mqueue_queue
-mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type) ;
-
-extern void
-mqueue_empty(mqueue_queue mq) ;
-
-extern mqueue_queue
-mqueue_reset(mqueue_queue mq, int free_structure) ;
-
-#define mqueue_reset_keep(mq) mqueue_reset(mq, 0)
-#define mqueue_reset_free(mq) mqueue_reset(mq, 1)
-
-extern mqueue_local_queue
-mqueue_local_init_new(mqueue_local_queue lmq) ;
-
-extern mqueue_local_queue
-mqueue_local_reset(mqueue_local_queue lmq, int free_structure) ;
-
-#define mqueue_local_reset_keep(lmq) mqueue_local_reset(lmq, 0)
-#define mqueue_local_reset_free(lmq) mqueue_local_reset(lmq, 1)
-
-extern void
-mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval) ;
-
-extern mqueue_thread_signal
-mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
- int signum) ;
-mqueue_thread_signal
-mqueue_thread_signal_reset(mqueue_thread_signal mqt, int free_structure) ;
-
-#define mqueue_thread_signal_reset_keep(mqt) mqueue_thread_signal_reset(mqt, 0)
-#define mqueue_thread_signal_reset_free(mqt) mqueue_thread_signal_reset(mqt, 1)
-
-extern mqueue_block
-mqb_init_new(mqueue_block mqb, mqueue_action action, void* arg0) ;
-
-extern mqueue_block
-mqb_re_init(mqueue_block mqb, mqueue_action action, void* arg0) ;
-
-extern void
-mqb_free(mqueue_block mqb) ;
+extern void mqueue_initialise(void) ;
+extern void mqueue_finish(void) ;
+
+extern mqueue_queue mqueue_init_new(mqueue_queue mq,
+ enum mqueue_queue_type type) ;
+extern void mqueue_empty(mqueue_queue mq) ;
+extern mqueue_queue mqueue_reset(mqueue_queue mq, free_keep_b free_structure) ;
+
+extern mqueue_local_queue mqueue_local_init_new(mqueue_local_queue lmq) ;
+extern mqueue_local_queue mqueue_local_reset(mqueue_local_queue lmq,
+ free_keep_b free_structure) ;
+
+extern void mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval) ;
+extern mqueue_thread_signal mqueue_thread_signal_init(mqueue_thread_signal mqt,
+ qpt_thread_t thread, int signum) ;
+mqueue_thread_signal mqueue_thread_signal_reset(mqueue_thread_signal mqt,
+ free_keep_b free_structure) ;
+
+extern mqueue_block mqb_init_new(mqueue_block mqb, mqueue_action action,
+ void* arg0) ;
+extern mqueue_block mqb_re_init(mqueue_block mqb, mqueue_action action,
+ void* arg0) ;
+extern void mqb_free(mqueue_block mqb) ;
enum mqb_rank
{
mqb_priority = true,
mqb_ordinary = false
} ;
+typedef enum mqb_rank mqb_rank_b ;
-extern void
-mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, enum mqb_rank priority) ;
-
-extern mqueue_block
-mqueue_dequeue(mqueue_queue mq, int wait, void* arg) ;
+extern void mqueue_enqueue(mqueue_queue mq, mqueue_block mqb,
+ mqb_rank_b priority) ;
+extern mqueue_block mqueue_dequeue(mqueue_queue mq, int wait, void* arg) ;
+extern int mqueue_revoke(mqueue_queue mq, void* arg0, int num) ;
-extern void
-mqueue_revoke(mqueue_queue mq, void* arg0) ;
-
-extern int
-mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig) ;
-
-extern void
-mqueue_local_enqueue(mqueue_local_queue lmq, mqueue_block mqb) ;
-
-extern void
-mqueue_local_enqueue_head(mqueue_local_queue lmq, mqueue_block mqb) ;
-
-Inline mqueue_block
-mqueue_local_head(mqueue_local_queue lmq) ;
+extern int mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig) ;
-extern mqueue_block
-mqueue_local_dequeue(mqueue_local_queue lmq) ;
+extern void mqueue_local_enqueue(mqueue_local_queue lmq, mqueue_block mqb) ;
+extern void mqueue_local_enqueue_head(mqueue_local_queue lmq, mqueue_block mqb) ;
+Inline mqueue_block mqueue_local_head(mqueue_local_queue lmq) ;
+extern mqueue_block mqueue_local_dequeue(mqueue_local_queue lmq) ;
/*==============================================================================
* Access functions for mqueue_block fields -- mqb_set_xxx/mqb_get_xxx
diff --git a/lib/node_type.h b/lib/node_type.h
deleted file mode 100644
index b1f26187..00000000
--- a/lib/node_type.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* Command handler node_type stuff -- header
- * Copyright (C) 1997, 98 Kunihiro Ishiguro
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Zebra; see the file COPYING. If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef _ZEBRA_NODE_TYPE_H
-#define _ZEBRA_NODE_TYPE_H
-
-/* There are some command levels which called from command node. */
-enum node_type
-{
- AUTH_NODE, /* Authentication mode of vty interface. */
- RESTRICTED_NODE, /* Restricted view mode */
- VIEW_NODE, /* View node. Default mode of vty interface. */
- AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
- ENABLE_NODE, /* Enable node. */
-
- MIN_DO_SHORTCUT_NODE = ENABLE_NODE,
- /* May not "do xxx" at any node lower */
- MAX_NON_CONFIG_NODE = ENABLE_NODE,
- /* May not be higher than this without owning
- * the configuration symbol of power */
-
- CONFIG_NODE, /* Config node. Default mode of config file. */
-
- MIN_CONTEXT_NODE = CONFIG_NODE,
- /* May not change context to any node lower */
-
- SERVICE_NODE, /* Service node. */
- DEBUG_NODE, /* Debug node. */
- AAA_NODE, /* AAA node. */
- KEYCHAIN_NODE, /* Key-chain node. */
- KEYCHAIN_KEY_NODE, /* Key-chain key node. */
- INTERFACE_NODE, /* Interface mode node. */
- ZEBRA_NODE, /* zebra connection node. */
- TABLE_NODE, /* rtm_table selection node. */
- RIP_NODE, /* RIP protocol mode node. */
- RIPNG_NODE, /* RIPng protocol mode node. */
- BGP_NODE, /* BGP protocol mode which includes BGP4+ */
- BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */
- BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */
- BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */
- BGP_IPV6_NODE, /* BGP IPv6 address family */
- BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */
- OSPF_NODE, /* OSPF protocol mode */
- OSPF6_NODE, /* OSPF protocol for IPv6 mode */
- ISIS_NODE, /* ISIS protocol mode */
- MASC_NODE, /* MASC for multicast. */
- IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
- IP_NODE, /* Static ip route node. */
- ACCESS_NODE, /* Access list node. */
- PREFIX_NODE, /* Prefix list node. */
- ACCESS_IPV6_NODE, /* Access list node. */
- PREFIX_IPV6_NODE, /* Prefix list node. */
- AS_LIST_NODE, /* AS list node. */
- COMMUNITY_LIST_NODE, /* Community list node. */
- RMAP_NODE, /* Route map node. */
- SMUX_NODE, /* SNMP configuration node. */
- DUMP_NODE, /* Packet dump node. */
- FORWARDING_NODE, /* IP forwarding node. */
- PROTOCOL_NODE, /* protocol filtering node */
- VTY_NODE, /* Vty node. */
-} ;
-typedef enum node_type node_type_t ;
-
-#endif /* _ZEBRA_NODE_TYPE_H */
diff --git a/lib/pthread_safe.c b/lib/pthread_safe.c
index c4ab9679..e2c7cdc0 100644
--- a/lib/pthread_safe.c
+++ b/lib/pthread_safe.c
@@ -299,7 +299,8 @@ errtox(strerror_t* st, int err, int len, int want)
if ((len + ql) <= qfs_left(&qfs)) /* accounting for "quotes" */
qfs_printf(&qfs, "%s%s%s", q, errm, q) ;
else
- qfs_printf(&qfs, "%s%.*s...%s", q, qfs_left(&qfs) - ql - 3, errm, q) ;
+ qfs_printf(&qfs, "%s%.*s...%s",
+ q, (int)(qfs_left(&qfs) - ql - 3), errm, q) ;
/* -ve precision is ignored ! */
} ;
diff --git a/lib/qfstring.c b/lib/qfstring.c
index 3db3fedf..f40f1417 100644
--- a/lib/qfstring.c
+++ b/lib/qfstring.c
@@ -26,10 +26,10 @@
*/
/*------------------------------------------------------------------------------
- * Initialise qf_str -- to given size (which includes the '\n')
+ * Initialise qf_str -- to given size (which includes the '\0')
*
* Sets pointers and terminates an empty string with one byte reserved for the
- * terminating '\n'.
+ * terminating '\0'.
*
* This operation is async-signal-safe.
*/
@@ -47,12 +47,12 @@ qfs_init(qf_str qfs, char* str, size_t size)
/*------------------------------------------------------------------------------
* Initialise qf_str which already contains string -- to given size (which
- * includes the '\n')
+ * includes the '\0')
*
* This may be used to prepare for appending to a buffer which already contains
* something.
*
- * Sets pointers, setting the write pointer to the existing terminating '\n'.
+ * Sets pointers, setting the write pointer to the existing terminating '\0'.
*
* This operation is async-signal-safe.
*/
@@ -331,7 +331,7 @@ qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags,
*
* If the precision is < 0 it is ignored (unless pf_hex, see below).
*
- * If the precision is 0 it is ignored unless bf_precision is set.
+ * If the precision is 0 it is ignored unless pf_precision is set.
*
* Precedence issues:
*
@@ -776,7 +776,7 @@ qfs_vprintf(qf_str qfs, const char *format, va_list args)
if (precision < 0)
{
precision = 0 ;
- flags &= ~pf_precision ;
+ flags &= ~pf_precision ; /* completely ignore */
} ;
}
else
@@ -885,7 +885,7 @@ qfs_vprintf(qf_str qfs, const char *format, va_list args)
* %s handler -- tolerates NULL pointer
*
* Accepts: width
- * precision
+ * precision -- ignored if < 0
* pf_precision -- explicit precision
*
* Rejects: pf_commas -- "'" seen
@@ -913,6 +913,12 @@ qfs_arg_string(qf_str qfs, va_list args, enum pf_flags flags,
if (flags != (flags & pf_precision))
return pfp_failed ;
+ if (precision < 0) /* make sure */
+ {
+ precision = 0 ;
+ flags &= ~pf_precision ;
+ } ;
+
len = (src != NULL) ? strlen(src) : 0 ;
if (((precision > 0) || (flags & pf_precision)) && (len > precision))
len = precision ;
diff --git a/lib/qiovec.c b/lib/qiovec.c
index 546dfcb0..38ea0dbb 100644
--- a/lib/qiovec.c
+++ b/lib/qiovec.c
@@ -18,16 +18,30 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+#include "zconfig.h" /* Otherwise IOV_MAX is not defined ! */
-#include "zebra.h"
+#include <errno.h>
+#include "misc.h"
#include "memory.h"
-#include "zassert.h"
#include "miyagi.h"
#include "qiovec.h"
/*==============================================================================
+ * IOV_MAX is defined by POSIX to appear in <limits.h>.
+ *
+ * This does not appear unless "zconfig.h" is included, first...
+ * ...but although it compiles OK, Eclipse cannot see where the value has
+ * come from.
+ */
+#ifdef IOV_MAX /* Stops Eclipse whinging */
+#if IOV_MAX < 64 /* check for a reasonable value */
+#error IOV_MAX < 64
+#endif
+#endif
+
+/*==============================================================================
* Initialise, allocate and reset qiovec
*/
diff --git a/lib/qiovec.h b/lib/qiovec.h
index f1498146..230198f3 100644
--- a/lib/qiovec.h
+++ b/lib/qiovec.h
@@ -22,9 +22,8 @@
#ifndef _ZEBRA_QIOVEC_H
#define _ZEBRA_QIOVEC_H
-#include "zebra.h"
-
#include "misc.h"
+#include <sys/uio.h> /* iovec stuff */
/*==============================================================================
* Flexible size "struct iovec"
diff --git a/lib/qpath.c b/lib/qpath.c
index f6157b89..07c294de 100644
--- a/lib/qpath.c
+++ b/lib/qpath.c
@@ -68,7 +68,7 @@ qpath_init_new(qpath qp, const char* path)
/* Worry about fields other than the path */
- qs_init_new(&qp->path, 0) ;
+ qs_init_new(qp->path, 0) ;
if (path != NULL)
qpath_set(qp, path) ;
@@ -119,7 +119,7 @@ qpath_set(qpath qp, const char* path)
if (path != NULL)
qs_set(&qp->path, path) ;
else
- qs_clear(&qp->path) ;
+ qs_clear(qp->path) ;
/* Worry about fields other than the path */
} ;
@@ -521,7 +521,7 @@ qpath_push_str(qpath qp, const char* path)
len = 0 ;
/* Worry about whether need to add a '/' to the path before pushing */
- sp = qs_chars(qs) ;
+ sp = qs_char(qs) ;
ep = qs_ep_char(qs) ; /* points at trailing '\0' */
if (sp == NULL)
diff --git a/lib/qpnexus.c b/lib/qpnexus.c
index 3b014be2..001d5049 100644
--- a/lib/qpnexus.c
+++ b/lib/qpnexus.c
@@ -18,9 +18,8 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
-
-#include <zebra.h>
-#include <stdbool.h>
+#include "misc.h"
+#include <signal.h>
#include "qpnexus.h"
#include "memory.h"
@@ -91,7 +90,7 @@ qpn_add_hook_function(qpn_hook_list list, void* hook)
* object is empty or otherwise out of action.
*/
extern qpn_nexus
-qpn_reset(qpn_nexus qpn, bool free_structure)
+qpn_reset(qpn_nexus qpn, free_keep_b free_structure)
{
qps_file qf;
qtimer qtr;
@@ -115,11 +114,8 @@ qpn_reset(qpn_nexus qpn, bool free_structure)
qpn->selection = NULL ;
}
- if (qpn->queue != NULL)
- qpn->queue = mqueue_reset(qpn->queue, 1);
-
- if (qpn->mts != NULL)
- qpn->mts = mqueue_thread_signal_reset(qpn->mts, 1);
+ qpn->queue = mqueue_reset(qpn->queue, free_it);
+ qpn->mts = mqueue_thread_signal_reset(qpn->mts, free_it);
if (free_structure)
XFREE(MTYPE_QPN_NEXUS, qpn) ; /* sets qpn = NULL */
diff --git a/lib/qpnexus.h b/lib/qpnexus.h
index 03b52876..a6c8bf44 100644
--- a/lib/qpnexus.h
+++ b/lib/qpnexus.h
@@ -146,9 +146,6 @@ extern qpn_nexus qpn_init_new(qpn_nexus qpn, bool main_thread);
extern void qpn_add_hook_function(qpn_hook_list list, void* hook) ;
extern void qpn_exec(qpn_nexus qpn);
extern void qpn_terminate(qpn_nexus qpn);
-extern qpn_nexus qpn_reset(qpn_nexus qpn, bool free_structure);
-
-#define qpn_reset_free(qpn) qpn_reset(qpn, 1)
-#define qpn_reset_keep(qpn) qpn_reset(qpn, 0)
+extern qpn_nexus qpn_reset(qpn_nexus qpn, free_keep_b free_structure);
#endif /* _ZEBRA_QPNEXUS_H */
diff --git a/lib/qpselect.c b/lib/qpselect.c
index c15f112e..bcd2e2c9 100644
--- a/lib/qpselect.c
+++ b/lib/qpselect.c
@@ -29,13 +29,24 @@
#include "memory.h"
#include "vector.h"
-enum { qdebug =
-#ifdef QDEBUG
- 1
+/*------------------------------------------------------------------------------
+ * Sort out any debug setting
+ */
+#ifdef QPSELECT_DEBUG /* Can be forced from outside */
+# if QPSELECT_DEBUG
+# define QPSELECT_DEBUG 1 /* Force 1 or 0 */
+#else
+# define QPSELECT_DEBUG 0
+# endif
#else
- 0
+# ifdef QDEBUG
+# define QPSELECT_DEBUG 1 /* Follow QDEBUG */
+#else
+# define QPSELECT_DEBUG 0
+# endif
#endif
-};
+
+enum { qpselect_debug = QPSELECT_DEBUG } ;
/*==============================================================================
* Quagga pselect -- qps_xxxx
@@ -319,7 +330,7 @@ qps_pselect(qps_selection qps, qtime_t max_wait)
fd_set* p_fds[qps_mnum_count] ;
int n ;
- if (qdebug)
+ if (qpselect_debug)
qps_selection_validate(qps) ;
/* If there is stuff still pending, tidy up by zeroising the result */
@@ -415,7 +426,7 @@ qps_dispatch_next(qps_selection qps)
qps_file qf ;
qps_mnum_t mnum ;
- if (qdebug)
+ if (qpselect_debug)
qps_selection_validate(qps) ;
if (qps->pend_count == 0)
diff --git a/lib/qpthreads.h b/lib/qpthreads.h
index 959194ad..da6b2319 100644
--- a/lib/qpthreads.h
+++ b/lib/qpthreads.h
@@ -52,6 +52,22 @@
#error Require _POSIX_THREADS
#endif
+#ifdef QPTHREADS_DEBUG /* Can be forced from outside */
+# if QPTHREADS_DEBUG
+# define QPTHREADS_DEBUG 1 /* Force 1 or 0 */
+#else
+# define QPTHREADS_DEBUG 0
+# endif
+#else
+# ifdef QDEBUG
+# define QPTHREADS_DEBUG 1 /* Follow QDEBUG */
+#else
+# define QPTHREADS_DEBUG 0
+# endif
+#endif
+
+enum { qpthreads_debug = QPTHREADS_DEBUG } ;
+
/*==============================================================================
* Global Switch -- this allows the library to be run WITHOUT pthreads !
*
@@ -165,7 +181,7 @@ Inline bool qpt_thread_is_self(qpt_thread_t id)
*
* Quagga's default mutex type is:
*
- * * PTHREAD_MUTEX_ERRORCHECK unless NDEBUG && NDEBUG_QPTHREADS
+ * * PTHREAD_MUTEX_ERRORCHECK if QPTHREADS_DEBUG
* * QPT_MUTEX_TYPE_DEFAULT
*
* QPT_MUTEX_TYPE_DEFAULT may be set elsewhere. If it is not set then it is
@@ -200,10 +216,10 @@ enum qpt_mutex_options
# define QPT_MUTEX_TYPE_DEFAULT PTHREAD_MUTEX_NORMAL
#endif
-#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
-# define QPT_MUTEX_TYPE QPT_MUTEX_TYPE_DEFAULT
-#else
+#if QPTHREADS_DEBUG
# define QPT_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK
+#else
+# define QPT_MUTEX_TYPE QPT_MUTEX_TYPE_DEFAULT
#endif
extern qpt_mutex /* freezes qpthreads_enabled */
diff --git a/lib/qstring.c b/lib/qstring.c
index c7e5b81c..19f74e8c 100644
--- a/lib/qstring.c
+++ b/lib/qstring.c
@@ -19,13 +19,10 @@
* Boston, MA 02111-1307, USA.
*/
-#include <stdio.h>
-#include <ctype.h>
+#include "stdio.h"
#include "qstring.h"
-
#include "memory.h"
-#include "zassert.h"
/*==============================================================================
*/
@@ -38,15 +35,53 @@ qs_new(void)
{
/* zeroising sets a completely empty qstring -- see qs_init_new() */
return XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
+
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create a new body or extend existing one to accommodate at least slen + 1.
+ *
+ * Sets size to multiple of 8 (minimum 16), with at least 9 bytes free beyond
+ * the requested slen.
+ *
+ * Does not affect 'len' or 'cp'.
+ *
+ * If 'keep_alias', ONLY sets 'b_size' & 'b_body'.
+ *
+ * If !'keep_alias', sets the elstring body & size & clears 'alias'.
+ */
+static inline void
+qs_new_body(qstring qs, usize slen, bool keep_alias)
+{
+ qs->b_size = (slen + 0x10) & ~(usize)(0x08 - 1) ;
+ qs->b_body = XREALLOC(MTYPE_STRING, qs->b_body, qs->b_size) ;
+
+ if (!keep_alias)
+ qs_set_real_body_nn(qs) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create a new, empty qs with body
+ */
+extern qstring
+qs_new_with_body(usize slen)
+{
+ qstring qs ;
+
+ qs = qs_new() ;
+ qs_new_body(qs, slen, false) ;
+
+ return qs ;
} ;
/*------------------------------------------------------------------------------
* Initialise qstring -- allocate if required.
*
- * If non-zero slen is given, a body is allocated (for at least slen + 1).
+ * If non-zero slen is given, a body is allocated (size = slen + 1).
* If zero slen is given, no body is allocated.
*
- * Sets qs->len = qs->cp = 0. '\0' terminates body if allocates one.
+ * Sets 'len' = 'cp' = 0.
*
* Returns: address of qstring
*
@@ -65,18 +100,20 @@ qs_init_new(qstring qs, usize slen)
/* Zeroising has set:
*
- * body = NULL -- no body
- * size = 0 -- no body
+ * empty elstring -- no body, 'len' = 0
+ *
+ * size = 0 -- no elstring body
*
- * len = 0
* cp = 0
*
- * b_body = NULL -- no body buffer
- * b_size = 0 -- no body buffer
+ * b_body = NULL -- no body buffer
+ * b_size = 0 -- no body buffer
+ *
+ * alias = false -- not an alias qstring
*/
if (slen != 0)
- qs_make_to_size(qs, slen, false) ;
+ qs_new_body(qs, slen, false) ;
return qs ;
} ;
@@ -104,90 +141,75 @@ qs_reset(qstring qs, free_keep_b free_structure)
XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */
else
memset(qs, 0, sizeof(qstring_t)) ; /* see qs_init_new */
+
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
} ;
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Allocate or reallocate body so that is big enough for the given "slen".
+ * Allocate or reallocate body so that is big enough for the given 'slen'.
*
- * If the qstring is currently an alias, copies all of the alias to a new
- * body -- so always returns a non-alias qstring.
+ * qstring may NOT be NULL.
*
- * Returns with a body with size > 0. Allocates to 16 byte boundaries with
- * space for '\0' beyond given length.
+ * Returns with a body with size > 0 (even if 'slen' == 0).
*
- * Does NOT affect qs->len or qs->cp. Does NOT re-terminate.
+ * Expects, but does not require, that 'slen' > 'size'.
*
- * NB: will allocate a new body even if the slen == 0.
+ * Does not change 'cp' or 'len' (even if 'len' > 'slen').
*
- * NB: always copies all of any aliased string (even if the slen == 0).
- *
- * NB: sets terminated false
+ * If is an alias, copies min('len', 'alen', 'slen') characters to the body.
*/
Private void
-qs_make_to_size(qstring qs, usize slen, bool keep)
+qs_make_to_size(qstring qs, usize slen, usize alen)
{
- usize size ;
- usize alen ;
+ /* If body needs to be extended, do that now */
+ if (slen >= qs->b_size)
+ qs_new_body(qs, slen, (qs->alias && (alen != 0))) ;
+ /* keeps alias if required */
/* Worry about alias. If we keep it, we keep all of it. */
- if (keep && (qs->size == 0))
+ if (qs->alias)
{
- alen = qs_len_nn(qs) ; /* alias stuff to keep */
- if (slen <= alen)
- slen = alen + 1 ; /* making sure can do that. */
- }
- else
- alen = 0 ; /* no alias stuff to keep */
+ /* alias or empty */
+ usize len = qs_len_nn(qs) ;
- /* Calculate the new size -- multiple of 16, >= 16. */
- size = (slen + 0x10) & ~(usize)(0x10 - 1) ;
- dassert(size != 0) ;
+ if (alen >= len)
+ alen = len ; /* keep min(alen, len) */
- /* If that requires the body to be extended, do that now */
- if (size > qs->b_size)
- {
- /* Need to allocate or extend the buffer.
- *
- * If current size is not zero, extend by doubling size or making at
- * least the multiple of 16 calculated for new len.
- */
- qs->b_size *= 2 ;
- if (qs->b_size < size)
- qs->b_size = size ;
- qs->b_body = XREALLOC(MTYPE_STRING, qs->b_body, qs->b_size) ;
- } ;
-
- /* If this is a non-empty alias, copy all or part of it. */
- if (alen != 0)
- memcpy(qs->b_body, qs_body_nn(qs), alen) ;
-
- /* Update body and size, and no longer known to be terminated */
- qs_set_body_nn(qs, qs->b_body) ;
- qs->size = qs->b_size ;
+ if (alen > slen)
+ alen = slen ; /* keep only to new len. */
+ if (alen != 0)
+ memcpy(qs->b_body, qs_body_nn(qs), alen) ;
+ qs_set_real_body_nn(qs) ;
+ } ;
} ;
/*==============================================================================
* Setting value of qstring
+ *
+ * Copy the given string to the qstring, allocating qstring and/or extending
+ * it as required.
+ *
+ * Any alias is simply discarded.
+ *
+ * Sets 'len' to new length
+ * Sets 'cp' = 0
+ *
+ * Returns: address of the qstring (allocated if required).
*/
/*------------------------------------------------------------------------------
* Set qstring to be copy of the given string.
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
- *
- * Allocates a body and copies src to it, adding '\0'. Treats src == NULL as
- * an empty string.
- *
- * Sets qs->len to the length of the string (excluding trailing '\0').
- * Sets qs->cp == 0.
+ * Treats src == NULL as an empty string. Otherwise src must be a '\0'
+ * terminated string.
*
- * Returns: address of the qstring copied to.
+ * Does not copy or count the '\0' terminator.
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * See notes above.
*/
extern qstring
qs_set(qstring qs, const char* src)
@@ -196,157 +218,202 @@ qs_set(qstring qs, const char* src)
} ;
/*------------------------------------------------------------------------------
- * Set qstring to be leading 'n' bytes of given string.
- *
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
- *
- * Allocates a body and copies 'n' bytes from src to it, adding '\0'. The
- * src pointer is ignored if n == 0.
+ * Set qstring to be copy of leading 'n' bytes of given string.
*
- * Sets qs->len to the length of the string (excluding trailing '\0').
- * Sets qs->cp == 0.
+ * If n == 0, src is ignored (and may be NULL)
+ * If n > 0, src string MUST be at least 'n' bytes long.
*
- * Returns: address of the qstring copied to.
- *
- * NB: if n == 0, src may be NULL
- *
- * NB: if n > 0, src string MUST be at least 'n' bytes long.
- *
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * See notes above.
*/
extern qstring
qs_set_n(qstring qs, const char* src, usize len)
{
- char* p ;
-
- qs = qs_new_len(qs, len) ; /* ensures have body > n */
-
- p = qs_char_nn(qs) ;
+ qs = qs_new_size(qs, len) ; /* ensures have body > len */
if (len != 0)
- memcpy(p, src, len) ;
-
- *(p + len) = '\0' ;
+ memcpy(qs_char_nn(qs), src, len) ;
- qs_set_term_nn(qs, true) ;
- qs->cp = 0 ;
+ qs_set_len_nn(qs, len) ;
+ qs_set_cp_nn(qs, 0) ;
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Append given string to a qstring -- adding at qs->len position.
- *
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
- *
- * Allocates or extends the body and copies bytes from src to it, adding '\0'.
- * Treats src == NULL as an empty string.
- *
- * Sets qs->len to the length of the result (excluding trailing '\0')
- * Does not change qs->cp.
+ * Set qstring to be copy of given qstring contents.
*
- * Returns: address of the qstring appended to.
+ * If the given qstring is an alias, then the contents of the alias are copied
+ * (so the result is not an alias). See qs_copy() for the alternative.
*
- * NB: if appending to a dummy qstring, the old body is copied first.
+ * See notes above -- and note that 'cp' is set to 0.
*/
-extern qstring qs_append(qstring qs, const char* src)
+extern qstring
+qs_set_qs(qstring qs, qstring src)
{
- return qs_append_n(qs, src, (src != NULL) ? strlen(src) : 0) ;
+ return qs_set_n(qs, qs_body(src), qs_len(src)) ;
} ;
/*------------------------------------------------------------------------------
- * Append leading 'n' bytes of given string to a qstring -- adding at qs->len
- * position.
- *
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * Set qstring to be copy of given elstring contents.
*
- * Allocates or extends the body and copies 'n' bytes from src to it,
- * adding '\0'. The src pointer is ignored if n == 0.
+ * See notes above.
+ */
+extern qstring
+qs_set_els(qstring qs, elstring src)
+{
+ return qs_set_n(qs, els_body(src), els_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring with given pattern to given length.
*
- * Sets qs->len to the length of the result (excluding trailing '\0')
- * Does not change qs->cp.
+ * Repeats the given pattern as many times as necessary to get to the given
+ * length -- using a final partial piece of the pattern as required.
*
- * Returns: address of the qstring appended to.
+ * If the pattern is zero length, fills with spaces !
*
- * NB: if n == 0, src may be NULL
+ * See notes above.
+ */
+extern qstring
+qs_set_fill(qstring qs, usize len, const char* src)
+{
+ return qs_set_fill_n(qs, len, src, (src != NULL ? strlen(src) : 0)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring with given pattern to given length.
*
- * NB: if n > 0, src string MUST be at least 'n' bytes long.
+ * Repeats the given pattern as many times as necessary to get to the given
+ * length -- using a final partial piece of the pattern as required.
*
- * NB: if appending to a dummy qstring, the old body is copied first.
+ * See notes above.
*/
extern qstring
-qs_append_n(qstring qs, const char* src, usize n)
+qs_set_fill_n(qstring qs, usize len, const char* src, usize flen)
{
- char* ep ;
+ char* p ;
+ char* q ;
+ usize left ;
- qs = qs_add_len(qs, n, &ep) ;
+ qs = qs_new_size(qs, len) ; /* ensures have body > len */
- if (n != 0)
- memcpy(ep, src, n) ;
+ if (len != 0)
+ {
+ if (flen == 0)
+ {
+ src = " " ;
+ flen = strlen(src) ;
+ } ;
+
+ if (len < flen)
+ flen = len ;
+
+ q = p = qs_char_nn(qs) ;
+ memcpy(p, src, flen) ;
+ p += flen ;
+ left = len - flen ;
+
+ while (left > 0)
+ {
+ if (left < flen)
+ flen = left ;
+
+ memcpy(p, q, flen) ;
+ p += flen ;
+ left -= flen ;
+
+ flen += flen ;
+ } ;
+ } ;
+
+ qs_set_len_nn(qs, len) ;
+ qs_set_cp_nn(qs, 0) ;
return qs ;
} ;
-/*------------------------------------------------------------------------------
- * Copy one qstring to another
+/*==============================================================================
+ * Appending to a qstring
*
- * If both are NULL, returns NULL.
+ * Copy the given string to the end of the given qstring (at 'len'),
+ * allocating qstring and/or extending it as required.
*
- * Otherwise if dst is NULL, creates a new qstring.
+ * If this is an alias, it is copied to before being appended to (even if
+ * appending nothing).
*
- * Sets dst: body = copy of src->len bytes of src->body -- '\0' terminated.
- * cp = src->cp
- * len = src->len
+ * Can append to NULL or empty qstring.
*
- * Where a NULL src has zero cp and len.
+ * Sets 'len' to new length.
+ * Does not affect 'cp'.
+ *
+ * Returns: address of the qstring (allocated if required).
+ */
+
+/*------------------------------------------------------------------------------
+ * Append given string to a qstring.
*
- * If not NULL, the destination is guaranteed to have a body, and that will be
- * '\0' terminated.
+ * Treats src == NULL as an empty string. Otherwise src must be a '\0'
+ * terminated string.
*
- * Returns: the destination qstring
+ * Does not copy or count the '\0' terminator.
*
- * NB: if copying to a dummy qstring, the old body is simply discarded.
+ * See notes above.
+ */
+extern qstring qs_append(qstring qs, const char* src)
+{
+ return qs_append_n(qs, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append leading 'n' bytes of given string to a qstring.
+ *
+ * If n == 0, src may be NULL
+ * If n > 0, src string MUST be at least 'n' bytes long.
+ *
+ * See notes above.
*/
extern qstring
-qs_copy(qstring dst, qstring src)
+qs_append_n(qstring qs, const char* src, usize n)
{
- usize n ;
+ qs = qs_extend(qs, n) ; /* allocate, copy any alias, extend body,
+ set new length, etc */
- if (src == NULL)
- {
- if (dst == NULL)
- return dst ;
+ if (n != 0)
+ memcpy(qs_ep_char_nn(qs) - n, src, n) ;
- n = 0 ;
- dst->cp = 0 ;
- }
- else
- {
- if (dst == NULL)
- dst = qs_new() ;
+ return qs ;
+} ;
- n = src->len ;
- dst->cp = src->cp ;
- } ;
+/*------------------------------------------------------------------------------
+ * Append given qstring to a qstring.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_append_qs(qstring qs, qstring src)
+{
+ return qs_append_n(qs, qs_body(src), qs_len(src)) ;
+} ;
- qs_set_len(dst, n) ; /* TODO: Copies alias !! */
+/*------------------------------------------------------------------------------
+ * Append given elstring to a qstring.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_append_els(qstring qs, elstring src)
+{
+ return qs_append_n(qs, els_body(src), els_len(src)) ;
+} ;
- if (n > 0)
- memcpy(dst->s.body, src->s.body, n) ;
- *(dst->s.char_body + n) = '\0' ;
- return dst ;
-} ;
-/*------------------------------------------------------------------------------
- * Construct a qstring which is an alias for the given string.
- *
- * Allocates a qstring if required.
- *
- * Given string must be '\0' terminated.
+/*==============================================================================
+ * Setting of alias.
*
* Does NOT copy the given string, but sets the qstring to be a pointer to it.
+ * This means that:
*
* NB: it is the caller's responsibility to ensure that the original string
* stays put for however long the qstring is an alias for it.
@@ -361,150 +428,147 @@ qs_copy(qstring dst, qstring src)
* use, the qstring must not be released, so that the alias is not
* released.
*
- * Returns: the address of the qstring.
+ * Preserves any existing qstring body.
+ *
+ * Returns: address of the qstring (allocated if required).
+ */
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be an alias for the given string.
+ *
+ * Treats src == NULL as an empty string. Otherwise src must be a '\0'
+ * terminated string.
+ *
+ * Does not count the '\0' terminator.
+ *
+ * See notes above.
*/
extern qstring
qs_set_alias(qstring qs, const char* src)
{
+ return qs_set_alias_n(qs, src, (src != NULL) ? strlen(src) : 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be an alias for the leading 'n' bytes of given string.
+ *
+ * If n == 0, src may be NULL
+ * If n > 0, src string MUST be at least 'n' bytes long.
+ *
+ * See notes above.
+ */
+extern qstring
+qs_set_alias_n(qstring qs, const char* src, usize n)
+{
if (qs == NULL)
- qs = qs_init_new(NULL, 0) ;
+ qs = qs_new() ;
/* Make the alias. Note that any existing b_body and b_size are preserved,
* so that any current body can be reused at a later date.
*/
- qs->s.const_body = (src != NULL) ? src : "" ;
- qs->len = strlen(src) ;
- qs->cp = 0 ;
- qs->size = 0 ; /* <=> this is an alias ! */
- qs->terminated = true ;
+ qs_set_body_nn(qs, (n != 0) ? src : "") ;
+ qs_set_len_nn(qs, n) ;
+ qs_set_cp_nn(qs, 0) ;
+ qs->alias = true ;
+ qs->size = 0 ; /* => empty or alias */
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Construct a qstring which is an alias for the 'n' given characters.
- *
- * Allocates a qstring if required.
- *
- * Given characters are assumed not to be '\0' terminated.
+ * Set qstring to be an alias for the given qstring.
*
- * Does NOT copy the given characters, but sets the qstring to be a pointer to
- * them.
+ * If the src is not an alias, then the qstring is an alias for the body of
+ * src -- so must be careful not to disturb that !
*
- * NB: it is the caller's responsibility to ensure that the original characters
- * stays put for however long the qstring is an alias for them.
+ * If the src is an alias, then the qstring is another alias.
*
- * It is also the caller's responsibility to see that the original
- * characters are discarded as required (once the alias is no longer
- * required.)
- *
- * NB: if the qstring is changed in any way, a copy of the aliased characters
- * will be made first.
+ * See notes above.
+ */
+extern qstring
+qs_set_alias_qs(qstring qs, qstring src)
+{
+ return qs_set_alias_n(qs, qs_body(src), qs_len(src)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Construct a qstring which is an alias for the given elstring.
*
- * NB: if a pointer to the body of the qstring is taken, then while that is in
- * use, the qstring must not be released, so that the alias is not
- * released.
+ * If n == 0, src may be NULL
+ * If n > 0, src string MUST be at least 'n' bytes long.
*
- * Returns: the address of the qstring.
+ * See notes above.
*/
extern qstring
-qs_set_alias_n(qstring qs, const char* src, usize len)
+qs_set_alias_els(qstring qs, elstring src)
{
- if (qs == NULL)
- qs = qs_init_new(NULL, 0) ;
-
- if (len == 0)
- src = "" ;
- else
- assert(src != NULL) ;
-
- /* Make the alias. Note that any existing b_body and b_size are preserved,
- * so that any current body can be reused at a later date.
- */
- qs->s.const_body = src ;
- qs->len = len ;
- qs->cp = 0 ;
- qs->size = 0 ; /* <=> this is an alias ! */
- qs->terminated = false ;
-
- return qs ;
+ return qs_set_alias_n(qs, els_body(src), els_len(src)) ;
} ;
-
-
-
-
-
-
-
-
-
-
-
-
-
+/*==============================================================================
+ * Copying of qstring
+ */
/*------------------------------------------------------------------------------
- * Add 'n' to the current string length, allocating or extending the body.
+ * Copy one qstring to another -- allocating/extending as required.
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * If both are NULL, returns NULL.
+ * Otherwise if dst is NULL, creates a new qstring.
*
- * Returns with a body with size > 0. Allocates to 16 byte boundaries with
- * space for '\0' beyond given length.
+ * If src is NULL it is treated as zero length, with 'cp' == 0.
*
- * Does NOT affect qs->cp.
- * Does set the new qs->len -- qs->len += n
- * Does NOT reterminate.
+ * If src is not an alias, a copy is made to dst.
+ * If src is an alias, dst becomes another alias for the same thing.
*
- * Returns: address of qstring
+ * If dst is an alias, that is discarded.
*
- * also: sets char** p_ep to point at the *end* of the old len.
+ * Copies the src 'cp' to the dst.
*
- * NB: will allocate a new body even if the new len == 0.
- *
- * NB: always copies all of any aliased string (even if the slen == 0).
+ * Returns: the destination qstring (allocated if required).
*/
extern qstring
-qs_add_len(qstring qs, usize n, char** p_ep)
+qs_copy(qstring dst, qstring src)
{
- usize slen ;
- usize len ;
-
- len = qs_len(qs) ;
- slen = len + n ;
+ if (src == NULL)
+ {
+ qs_clear(dst) ; /* if dst not NULL, clear it */
+ return dst ;
+ } ;
- /* Set the new length -- creating if required.
- *
- * Will always return with a body and no longer an alias (if was one).
- */
- qs = qs_set_len(qs, slen) ;
+ if (src->alias)
+ dst = qs_set_alias_qs(dst, src) ;
+ else
+ dst = qs_set_qs(dst, src) ;
- /* Set pointer to old end (len) position. */
- *p_ep = ((char*)qs_body_nn(qs)) + len ;
+ qs_set_cp_nn(dst, qs_cp_nn(src)) ; /* copy in the src cp. */
- return qs ;
+ return dst ;
} ;
/*==============================================================================
* printf() and vprintf() type functions
- */
-
-/*------------------------------------------------------------------------------
- * Formatted print to qstring -- cf printf()
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * Allocate and/or extend qstring as required.
*
- * If OK:
+ * Any alias is discarded.
*
- * Sets qs->len to the length of the null terminated result.
- * Does NOT affect qs->cp.
+ * Sets 'len' = length of the '\0' terminated result (less the '\0').
+ * Sets 'cp' = 0
*
- * If fails:
+ * If fails and qs != NULL:
*
- * Sets qs->len = qs->cp = 0 and terminates to zero length.
+ * Sets 'len' = 0
+ * Sets 'cp' = 0
+ * But retains any existing body.
*
* Returns: address of qstring if OK
- * NULL if failed (unlikely though that is) -- qstring set empty.
+ * NULL if failed (unlikely though that is)
+ */
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qstring -- cf printf()
+ *
+ * See notes above.
*/
extern qstring
qs_printf(qstring qs, const char* format, ...)
@@ -521,19 +585,7 @@ qs_printf(qstring qs, const char* format, ...)
/*------------------------------------------------------------------------------
* Formatted print to qstring -- cf vprintf()
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
- *
- * If OK:
- *
- * Sets qs->len to the length of the null terminated result.
- * Does NOT affect qs->cp.
- *
- * If fails:
- *
- * Sets qs->len = qs->cp = 0 and terminates to zero length.
- *
- * Returns: address of qstring if OK
- * NULL if failed (unlikely though that is)
+ * See notes above.
*/
extern qstring
qs_vprintf(qstring qs, const char *format, va_list args)
@@ -542,11 +594,12 @@ qs_vprintf(qstring qs, const char *format, va_list args)
int slen ;
qstring qqs ;
- qqs = qs ;
+ qqs = qs ; /* NULL => need to make qs */
if (qs == NULL)
- qs = qs_new() ; /* sets size == 0 */
+ qqs = qs_new() ; /* Sets size, cp & len = 0 */
else
- qs_set_len_nn(qs, 0) ; /* Forget current contents */
+ qs_clear(qqs) ; /* Sets cp & len = 0, discard any alias, but
+ keep existing body */
while (1)
{
@@ -558,25 +611,24 @@ qs_vprintf(qstring qs, const char *format, va_list args)
* the result is still the length required.
*/
va_copy(ac, args);
- slen = vsnprintf (qs_body_nn(qs), qs->size, format, ac) ;
+ slen = vsnprintf(qs_body_nn(qqs), qqs->size, format, ac) ;
va_end(ac);
if (slen < 0)
- break ; /* Quit if failed */
+ break ; /* failed */
- if ((usize)slen < qs->size)
+ if ((usize)slen < qqs->size)
{
- qs_set_len_nn(qs, slen) ;
- return qs ; /* Exit if succeeded */
+ qs_set_len_nn(qqs, slen) ;
+ return qqs ; /* succeeded */
} ;
- qs_make_to_size(qs, slen) ; /* Extend body to required len */
+ qs_make_to_size(qqs, slen, 0) ; /* need space for slen */
} ;
- if (qqs == NULL)
- qs_reset(qs, free_it) ; /* discard what was allocated */
- else
- qs_clear(qs) ;
+ /* Failed... discard anything that has been allocated. */
+ if (qs == NULL)
+ qs_reset(qqs, free_it) ;
return NULL ;
} ;
@@ -586,233 +638,64 @@ qs_vprintf(qstring qs, const char *format, va_list args)
*/
/*------------------------------------------------------------------------------
- * Compare significant parts of two qstrings.
+ * Replace 'r' bytes at 'cp' by 'n' bytes -- extending if required.
*
- * By significant, mean excluding leading/trailing isspace() and treating
- * multiple isspace() as single isspace().
+ * May increase or decrease 'len'. but does not affect 'cp'.
*
- * Compares the 'len' portions of the strings.
+ * Returns: number of bytes beyond 'cp' that now exist.
*
- * If either is NULL, it is deemed to be an empty string.
+ * qstring MUST NOT be NULL
*
- * Returns: -1 => a < b
- * 0 => a == b
- * +1 => a > b
- */
-extern int
-qs_cmp_sig(qstring a, qstring b)
-{
- const unsigned char* p_a ;
- const unsigned char* e_a ;
- const unsigned char* p_b ;
- const unsigned char* e_b ;
-
- /* Set up pointers and dispense with leading and trailing isspace()
- *
- * Dummy up if NULL
- */
- if (a != NULL)
- {
- p_a = a->s.uchar_body ;
- e_a = p_a + a->len ;
-
- while ((p_a < e_a) && isspace(*p_a))
- ++p_a ;
- while ((p_a < e_a) && isspace(*(e_a - 1)))
- --e_a ;
- }
- else
- {
- p_a = NULL ;
- e_a = NULL ;
- }
-
- if (b != NULL)
- {
- p_b = b->s.uchar_body ;
- e_b = p_b + b->len ;
-
- while ((p_b < e_b) && isspace(*p_b))
- ++p_b ;
- while ((p_b < e_b) && isspace(*(e_b - 1)))
- --e_b ;
- }
- else
- {
- p_b = NULL ;
- e_b = NULL ;
- } ;
-
- /* Now set about finding the first difference */
- while ((p_a != e_a) && (p_b != e_b))
- {
- if (isspace(*p_a) && isspace(*p_b))
- {
- do { ++p_a ; } while (isspace(*p_a)) ;
- do { ++p_b ; } while (isspace(*p_b)) ;
- } ;
-
- if (*p_a != *p_b)
- return (*p_a < *p_b) ? -1 : +1 ;
-
- ++p_a ;
- ++p_b ;
- } ;
-
- /* No difference before ran out of one or both */
- if (p_a != e_a)
- return +1 ;
- else if (p_b != e_b)
- return -1 ;
- else
- return 0 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Insert 'n' bytes at 'cp' -- moves anything cp..len up.
- *
- * Increases 'len'. but does not affect 'cp'.
- *
- * Returns: number of bytes beyond 'cp' that were moved before insert.
- *
- * NB: qstring MUST NOT be NULL
- *
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
- *
- * NB: the string is NOT re-terminated.
- *
- * NB: if this is a aliased qstring, a copy is made of the original body.
- */
-extern usize
-qs_insert(qstring qs, const void* src, usize n)
-{
- usize after ;
- usize len ;
- char* p ;
-
- qs->terminated = false ; /* NB: require qs != NULL ! */
-
- len = qs_len_nn(qs) ;
- if (len < qs->cp) /* make len = max(len, cp) ! */
- len = qs->cp ;
-
- after = len - qs->cp ;
-
- qs_set_len(qs, len + n) ; /* set len and ensure have space
- Makes copy of any aliased string. */
- p = qs_cp_char(qs) ;
- if (after > 0)
- memmove (p + n, p, after) ;
-
- if (n > 0)
- memmove(p, src, n) ;
-
- return after ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Replace 'n' bytes at 'cp' -- extending if required.
- *
- * May increase 'len'. but does not affect 'cp'.
+ * If 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
*
- * NB: qstring MUST NOT be NULL
- *
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
- *
- * NB: the string is NOT re-terminated.
- *
- * NB: if this is a aliased qstring, a copy is made of the original body.
- */
-extern void
-qs_replace(qstring qs, const void* src, usize n)
-{
- usize len ;
-
- qs->terminated = false ; /* NB: require qs != NULL ! */
-
- len = qs_len_nn(qs) ;
- if (len < (qs->cp + n)) /* make len = max(len, cp + n) */
- len = qs->cp + n ;
-
- qs_set_len(qs, len) ; /* set len and ensure have space.
- Makes copy of any aliased string. */
-
- if (n > 0)
- memmove(qs_cp_char(qs), src, n) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Remove 'n' bytes at 'cp' -- extending if required.
- *
- * May change 'len'. but does not affect 'cp'.
- *
- * Returns: number of bytes beyond 'cp' that were moved before insert.
- *
- * NB: qstring MUST NOT be NULL
- *
- * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
- * one or more undefined bytes.
- *
- * NB: the string is NOT re-terminated.
- *
- * NB: if this is a aliased qstring, a copy is made of the original body.
+ * If this is a aliased qstring, a copy is made, so is no longer an alias.
*/
extern usize
-qs_delete(qstring qs, usize n)
+qs_replace(qstring qs, usize r, const void* src, usize n)
{
- usize after ;
- char* p ;
- usize len ;
+ usize cp, len, nlen, after ;
+ const char* ap ;
+ char* np ;
- qs->terminated = false ; /* NB: require qs != NULL ! */
+ len = qs_len_nn(qs) ;
+ cp = qs_cp_nn(qs) ;
- len = qs_len_nn(qs) ;
-
- /* If deleting to or beyond 'len', force len to cp */
- if ((qs->cp + n) >= len)
- {
- len = qs->cp ;
- qs_set_len_nn(qs, len) ; /* truncate now, so that if this is an
- aliased string, only copy what is
- going to be kept. */
- after = 0 ; /* nothing to move */
- }
+ if ((cp + r) >= len)
+ /* Replacing up to or beyond the end of the string */
+ after = 0 ;
else
- after = len - (qs->cp + n) ;
+ /* Replacing section short of the end of the string */
+ after = len - (cp + r) ;
- qs_set_len(qs, len) ; /* set len and ensure have space.
- Makes copy of any aliased string. */
+ nlen = cp + n + after ;
+ if (nlen >= qs->b_size)
+ qs_new_body(qs, nlen, qs->alias) ; /* keeping any alias */
+ ap = np = qs->b_body ;
+ if (qs->alias)
+ {
+ ap = qs_body_nn(qs) ; /* copy from the alias */
- /* Watch out for "dummy" */
- if (qs->size == 0)
- qs_make_to_size(qs, len) ;
+ uint before = cp ;
+ if (before > len)
+ before = len ;
- /* If deleting up to or beyond len, then simply set len == cp
- * note that this may reduce or increase len !
- */
- if ((qs->cp + n) >= len)
- {
- if (qs->cp < len)
- qs_set_len_nn(qs, qs->cp) ; /* discard stuff after qs->cp */
+ if (before > 0)
+ memmove(np, ap, before) ;
- qs_set_len(qs, qs->cp) ; /* set len */
- return 0 ; /* nothing after */
- }
+ qs_set_real_body_nn(qs) ;
+ } ;
- /* There is at least one byte after cp (so body must exist) */
- after = len - (qs->cp + n) ;
+ if (after > 0) /* move the after part before inserting */
+ memmove(np + cp + n, ap + cp + r, after) ;
- if (n > 0)
- {
- p = qs_cp_char(qs) ;
- memmove (p, p + n, after) ;
+ if (n > 0) /* insert */
+ memmove(np + cp, src, n) ;
- qs_set_len_nn(qs, len - n) ;
- } ;
+ /* Set new 'len' */
+ qs_set_len_nn(qs, nlen) ;
- return after ;
+ return n + after ;
} ;
diff --git a/lib/qstring.h b/lib/qstring.h
index 5b1d4932..48124a2d 100644
--- a/lib/qstring.h
+++ b/lib/qstring.h
@@ -38,8 +38,21 @@
* The caller does, however, have to explicitly release the contents of a
* qstring when it is done with.
*
- *
- *
+ * The mechanics of a qstring work on a length/pointer basis. The body of a
+ * qstring is not guaranteed to be '\0' terminated, but when memory is
+ * allocated provision is always made for a terminator beyond the 'len'.
+ * The qs_string() function will add a '\0' at the 'len' position.
+ *
+ * The body of a qstring is allocated and extended automatically as the length
+ * or the size is set. The address of the body can, therefore, change -- so
+ * should be careful when handling pointers to the body. The qstring handling
+ * tends to hold on to any body that has been allocated -- only qs_reset() will
+ * free the body.
+ *
+ * The qstring supports an "alias" state. A qstring can be set to be an
+ * "alias" for another length/pointer string, and at some later date that
+ * can be copied to the qstring body -- to be changed or for any other
+ * reason.
*/
struct qstring
{
@@ -47,10 +60,12 @@ struct qstring
usize size ; /* of the els body */
- usize cp ;
+ ulen cp ;
- usize b_size ;
void* b_body ;
+ usize b_size ;
+
+ bool alias ;
} ;
typedef struct qstring qstring_t[1] ;
@@ -68,111 +83,295 @@ enum
/*------------------------------------------------------------------------------
* Access functions for body of qstring -- to take care of casting pointers
*
- * NB: if the body has not yet been allocated, these functions will return
- * NULL or NULL + the offset.
+ * There are generally two versions of each function:
+ *
+ * xxxx_nn -- where the argument may NOT be NULL
+ *
+ * xxxx -- where a NULL or zero value is returned if the argument
+ * is NULL
+ *
+ * NB: the various 'cp', 'len' and 'at' functions do not guarantee that these
+ * positions exist within the current body of the string.
+ *
*/
+
+Inline elstring qs_els(qstring qs) ;
+Inline elstring qs_els_nn(qstring qs) ;
+Inline void qs_els_copy(elstring els, qstring qs) ;
+Inline void qs_els_copy_nn(elstring els, qstring qs) ;
+
+Inline char* qs_char(qstring qs) ;
+Inline char* qs_char_nn(qstring qs) ;
+
+Inline char* qs_cp_char(qstring qs) ;
+Inline char* qs_cp_char_nn(qstring qs) ;
+
+Inline char* qs_ep_char(qstring qs) ;
+Inline char* qs_ep_char_nn(qstring qs) ;
+
+Inline char* qs_char_at(qstring qs, usize off) ;
+Inline char* qs_char_at_nn(qstring qs, usize off) ;
+
+Inline void* qs_body(qstring qs) ;
+Inline void* qs_body_nn(qstring qs) ;
+Inline void qs_set_body_nn(qstring qs, const void* body) ;
+
+Inline ulen qs_len(qstring qs) ;
+Inline ulen qs_len_nn(qstring qs) ;
+Inline void qs_set_len_nn(qstring qs, ulen len) ;
+
+Inline ulen qs_cp(qstring qs) ;
+Inline ulen qs_cp_nn(qstring qs) ;
+Inline void qs_set_cp_nn(qstring qs, usize cp) ;
+Inline void qs_move_cp_nn(qstring qs, int delta) ;
+
+Inline ulen qs_after_cp(qstring qs) ;
+Inline ulen qs_after_cp_nn(qstring qs) ;
+
+Inline void qs_pp(pp p, qstring qs) ;
+Inline void qs_pp_nn(pp p, qstring qs) ;
+
+Inline void qs_cpp(cpp p, qstring qs) ;
+Inline void qs_cpp_nn(cpp p, qstring qs) ;
+
+/*------------------------------------------------------------------------------
+ * Functions to fetch the elstring body of the qstring.
+ */
+
+/* Pointer to elstring of qstring -- returns NULL if qstring is NULL */
Inline elstring
-qs_els_nn(qstring qs)
+qs_els(qstring qs)
{
- return qs->els ;
+ return (qs != NULL) ? qs->els : NULL ;
} ;
+/* Pointer to elstring of qstring (not NULL) */
Inline elstring
-qs_els(qstring qs)
+qs_els_nn(qstring qs)
{
return qs->els ;
} ;
-Inline void*
-qs_body_nn(qstring qs) /* pointer to body of qstring (not NULL) */
+/* Copy elstring of qstring to another elstring (elstring not NULL) */
+Inline void
+qs_els_copy(elstring els, qstring qs)
{
- return els_body_nn(qs->els) ;
+ if (qs != NULL)
+ qs_els_copy_nn(els, qs) ;
+ else
+ els_null(els) ;
+} ;
+
+/* Copy elstring of qstring to another elstring (neither NULL) */
+Inline void
+qs_els_copy_nn(elstring els, qstring qs)
+{
+ *els = *(qs->els) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Functions to fetch pointers to or into the string body.
+ *
+ * NB: these pointers must be treated with care -- and change to the string
+ * may invalidate the pointer. Where the qstring is an alias, the pointer
+ * returned is the alias -- which could disappear !
+ */
+
+/* Start of qstring -- returns NULL if qstring is NULL, or body is. */
+Inline char*
+qs_char(qstring qs)
+{
+ return qs_body(qs) ;
} ;
-Inline void* /* pointer to body of qstring */
+/* Start of qstring (not NULL)-- returns NULL if body is NULL. */
+Inline char*
+qs_char_nn(qstring qs)
+{
+ return qs_body_nn(qs) ;
+} ;
+
+/* Offset in qstring -- returns NULL if qstring is NULL
+ * returns *nonsense if body is NULL */
+Inline char*
+qs_char_at(qstring qs, usize off)
+{
+ return (qs != NULL) ? qs_char_at_nn(qs, off) : NULL ;
+} ;
+
+/* Offset in qstring (not NULL) -- returns *nonsense if body is NULL */
+Inline char*
+qs_char_at_nn(qstring qs, usize off)
+{
+ return qs_char_nn(qs) + off ;
+} ;
+
+/* 'cp' in qstring -- returns NULL if qstring is NULL
+ * returns *nonsense if body is NULL */
+Inline char*
+qs_cp_char(qstring qs)
+{
+ return (qs != NULL) ? qs_cp_char_nn(qs) : NULL ;
+} ;
+
+/* 'cp' in qstring (not NULL) -- returns *nonsense if body is NULL */
+Inline char*
+qs_cp_char_nn(qstring qs)
+{
+ return qs_char_at_nn(qs, qs_cp_nn(qs)) ;
+} ;
+
+/* 'len' in qstring -- returns NULL if qstring is NULL
+ * returns *nonsense if body is NULL */
+Inline char*
+qs_ep_char(qstring qs)
+{
+ return (qs != NULL) ? qs_ep_char_nn(qs) : NULL ;
+} ;
+
+/* 'len' in qstring (not NULL) -- returns *nonsense if body is NULL */
+Inline char*
+qs_ep_char_nn(qstring qs)
+{
+ return qs_char_at_nn(qs, qs_len_nn(qs)) ;
+} ;
+
+/* Start of qstring -- returns NULL if qstring is NULL, or body is. */
+Inline void*
qs_body(qstring qs)
{
return (qs != NULL) ? qs_body_nn(qs) : NULL ;
} ;
-Inline void /* set pointer to body of qstring (not NULL) */
-qs_set_body_nn(qstring qs, const void* body)
+/* Start of qstring (not NULL)-- returns NULL if body is NULL. */
+Inline void*
+qs_body_nn(qstring qs)
{
- els_set_body_nn(qs->els, body) ; /* sets term = fase */
+ return els_body_nn(qs->els) ;
} ;
-Inline ulen /* length of qstring (not NULL) */
-qs_len_nn(qstring qs)
+/* Set new body of qstring (not NULL).
+ *
+ * Caller must ensure that 'size', 'len' & 'cp' are all valid ! */
+Inline void
+qs_set_body_nn(qstring qs, const void* body)
{
- return els_len_nn(qs->els) ;
+ els_set_body_nn(qs->els, body) ;
} ;
-Inline ulen /* length of qstring */
+/*----------------------------------------------------------------------------*/
+
+/* 'len' of qstring -- returns 0 if qstring is NULL */
+Inline ulen
qs_len(qstring qs)
{
return (qs != NULL) ? qs_len_nn(qs) : 0 ;
} ;
-Inline void /* set length of qstring (not NULL) */
-qs_do_set_len_nn(qstring qs, ulen len)
+/* 'len' of qstring (not NULL) */
+Inline ulen
+qs_len_nn(qstring qs)
{
- els_set_len_nn(qs->els, len) ; /* sets term = false */
+ return els_len_nn(qs->els) ;
} ;
-Inline ulen /* cp of qstring (not NULL) */
-qs_cp_nn(qstring qs)
+/* set 'len' of qstring (not NULL) -- caller responsible for validity */
+Inline void
+qs_set_len_nn(qstring qs, ulen len)
{
- return qs->cp ;
+ els_set_len_nn(qs->els, len) ;
} ;
-Inline ulen /* cp of qstring */
+/*----------------------------------------------------------------------------*/
+
+/* 'cp' of qstring -- returns 0 if qstring is NULL */
+Inline ulen
qs_cp(qstring qs)
{
return (qs != NULL) ? qs_cp_nn(qs) : 0 ;
} ;
-Inline void /* set cp of qstring (not NULL) */
-qs_do_set_cp_nn(qstring qs, ulen cp)
+/* 'cp' of qstring (not NULL) */
+Inline ulen
+qs_cp_nn(qstring qs)
+{
+ return qs->cp ;
+} ;
+
+/* set 'cp' of qstring (not NULL) -- caller responsible for validity */
+Inline void
+qs_set_cp_nn(qstring qs, usize cp)
{
qs->cp = cp ;
} ;
-Inline char* /* pointer to given offset in qstring */
-qs_char_at_nn(qstring qs, usize off)
+/* move 'cp' of qstring (not NULL) -- caller responsible for validity */
+Inline void
+qs_move_cp_nn(qstring qs, int delta)
{
- char* p ;
- p = qs_body_nn(qs) ;
- return (p != NULL) ? p + off : NULL ;
+ qs->cp += delta ;
} ;
-Inline char* /* pointer to given offset in qstring */
-qs_char_at(qstring qs, usize off)
+/*----------------------------------------------------------------------------*/
+
+/* 'len' - 'cp' of qstring (not NULL) -- zero if 'len' < 'cp' */
+Inline ulen
+qs_after_cp_nn(qstring qs)
{
- return (qs != NULL) ? qs_char_at_nn(qs, off) : NULL ;
+ return (qs_len_nn(qs) > qs_cp_nn(qs)) ? qs_len_nn(qs) - qs_cp_nn(qs) : 0 ;
} ;
-Inline char* /* pointer to 'cp' offset in qstring */
-qs_cp_char(qstring qs)
+/* 'len' - 'cp' of qstring -- zero if NULL or 'len' < 'cp' */
+Inline ulen
+qs_after_cp(qstring qs)
{
- return (qs != NULL) ? qs_char_at_nn(qs, qs_cp_nn(qs)) : NULL ;
+ return (qs != NULL) ? qs_after_cp_nn(qs) : 0 ;
} ;
-Inline char* /* pointer to 'len' offset in qstring */
-qs_ep_char(qstring qs)
+/*------------------------------------------------------------------------------
+ * Functions to fetch various pointer pairs.
+ */
+
+Inline void
+qs_pp(pp p, qstring qs)
+{
+ if (qs != NULL)
+ qs_pp_nn(p, qs) ;
+ else
+ pp_null(p) ;
+} ;
+
+Inline void
+qs_pp_nn(pp p, qstring qs)
{
- return (qs != NULL) ? qs_char_at_nn(qs, qs_len_nn(qs)) : NULL ;
+ els_pp_nn(p, qs->els) ;
+} ;
+
+Inline void
+qs_cpp(cpp p, qstring qs)
+{
+ if (qs != NULL)
+ qs_cpp_nn(p, qs) ;
+ else
+ cpp_null(p) ;
} ;
-Inline bool /* whether qstring is known to be terminated */
-qs_term_nn(qstring qs)
+Inline void
+qs_cpp_nn(cpp p, qstring qs)
{
- return els_term_nn(qs->els) ;
+ els_cpp_nn(p, qs->els) ;
} ;
-Inline void /* set qstring is known to be terminated */
-qs_set_term_nn(qstring qs, bool how)
+/*------------------------------------------------------------------------------
+ * Set real body -- discarding any alias.
+ *
+ * NB: does not affect 'len' or 'cp'
+ */
+Inline void qs_set_real_body_nn(qstring qs)
{
- return els_set_term_nn(qs->els, how) ;
+ qs->size = qs->b_size ;
+ qs_set_body_nn(qs, qs->b_body) ;
+ qs->alias = false ;
} ;
/*==============================================================================
@@ -180,42 +379,140 @@ qs_set_term_nn(qstring qs, bool how)
*/
extern qstring qs_new(void) ;
+extern qstring qs_new_with_body(usize slen) ;
extern qstring qs_init_new(qstring qs, usize len) ;
extern qstring qs_reset(qstring qs, free_keep_b free_structure) ;
-Private void qs_make_to_size(qstring qs, usize len, free_keep_b free) ;
+Inline char* qs_make_string(qstring qs) ;
+Inline const char* qs_string(qstring qs) ;
+Inline void qs_clear(qstring qs) ;
+Inline qstring qs_new_size(qstring qs, usize slen) ;
+Inline qstring qs_extend(qstring qs, usize elen) ;
+Inline void qs_chop(qstring qs, usize clen) ;
+
+Private void qs_make_to_size(qstring qs, usize len, usize alen) ;
extern qstring qs_set(qstring qs, const char* src) ;
extern qstring qs_set_n(qstring qs, const char* src, usize n) ;
+extern qstring qs_set_qs(qstring qs, qstring src) ;
+extern qstring qs_set_els(qstring qs, elstring src) ;
+extern qstring qs_set_fill(qstring qs, usize len, const char* src) ;
+extern qstring qs_set_fill_n(qstring qs, usize len, const char* src,
+ usize flen) ;
extern qstring qs_append(qstring qs, const char* src) ;
extern qstring qs_append_n(qstring qs, const char* src, usize n) ;
-
-extern qstring qs_copy(qstring dst, qstring src) ;
+extern qstring qs_append_qs(qstring qs, qstring src) ;
+extern qstring qs_append_els(qstring qs, elstring src) ;
extern qstring qs_set_alias(qstring qs, const char* src) ;
extern qstring qs_set_alias_n(qstring qs, const char* src, usize len) ;
+extern qstring qs_set_alias_qs(qstring qs, qstring src) ;
+extern qstring qs_set_alias_els(qstring qs, elstring src) ;
+
+extern qstring qs_copy(qstring dst, qstring src) ;
extern qstring qs_printf(qstring qs, const char* format, ...)
PRINTF_ATTRIBUTE(2, 3) ;
extern qstring qs_vprintf(qstring qs, const char *format, va_list args) ;
-extern usize qs_insert(qstring qs, const void* src, usize n) ;
-extern void qs_replace(qstring qs, const void* src, usize n) ;
-extern usize qs_delete(qstring qs, usize n) ;
+extern usize qs_replace(qstring qs, usize r, const void* src, usize n) ;
+Inline usize qs_insert(qstring qs, const void* src, usize n)
+{
+ return qs_replace(qs, 0, src, n) ;
+} ;
+Inline usize qs_delete(qstring qs, usize n)
+{
+ return qs_replace(qs, n, NULL, 0) ;
+} ;
-extern int qs_cmp_sig(qstring a, qstring b) ;
+Inline int qs_cmp(qstring a, qstring b) ;
+Inline int qs_cmp_word(qstring a, const char* w) ;
+Inline int qs_cmp_sig(qstring a, qstring b) ;
+Inline bool qs_equal(qstring a, qstring b) ;
+Inline bool qs_substring(qstring a, qstring b) ;
/*==============================================================================
* The Inline functions.
*/
/*------------------------------------------------------------------------------
+ * Return pointer to string value -- ensure not alias and '\0' terminated.
+ *
+ * If is alias, copies that before adding '\0' terminator.
+ *
+ * Sets the '\0' terminator at the 'len' position, extending string if that
+ * is required.
+ *
+ * If qs == NULL or 'len' == 0 returns pointer to constant empty '\0'
+ * terminated string (ie "").
+ *
+ * NB: The qstring should not be changed or reset until this pointer has been
+ * discarded !
+ */
+Inline char*
+qs_make_string(qstring qs)
+{
+ usize len ;
+ char* p ;
+
+ if (qs == NULL)
+ {
+ len = 0 ;
+ qs = qs_new_with_body(len) ;
+ }
+ else
+ {
+ len = qs_len_nn(qs) ;
+ if (len >= qs->size) /* for alias, qs_size == 0 */
+ qs_make_to_size(qs, len, len) ; /* extend and/or copy any alias */
+ } ;
+
+ p = qs_char_nn(qs) ;
+ *(p + len) = '\0' ;
+
+ return p ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return pointer to string value.
+ *
+ * If possible, writes '\0' at 'len' in order to return a terminated string.
+ *
+ * If qs == NULL or body == NULL, or 'len' == 0 returns pointer to constant
+ * empty '\0' terminated string (ie "").
+ *
+ * NB: if 'len' is beyond the current 'size' of the of the qstring, then
+ * does NOT write '\0' (does NOT extend the string).
+ *
+ * NB: if string is an alias, this returns its address, whether it is
+ * terminated or not.
+ *
+ * NB: In any event, the string should not be changed or reset until this
+ * pointer has been discarded !
+ */
+Inline const char*
+qs_string(qstring qs)
+{
+ char* p ;
+ usize len ;
+
+ if ( (qs == NULL) || ((len = qs_len_nn(qs) ) == 0)
+ || ((p = qs_char_nn(qs)) == NULL) )
+ return "" ;
+
+ if (len < qs->size) /* for alias, qs_size == 0 */
+ *(p + len) = '\0' ;
+
+ return p ;
+} ;
+
+/*------------------------------------------------------------------------------
* Clear contents of qstring -- preserves any qstring body, but sets len = 0.
*
* Does nothing if qstring is NULL
*
- * Sets 'cp' = 'len' = 0.
+ * Sets 'cp' = 'len' = 0
*
* If is an alias qstring, discard the alias.
*
@@ -226,68 +523,64 @@ qs_clear(qstring qs)
{
if (qs != NULL)
{
- qs_do_set_len_nn(qs, 0) ; /* sets term == false */
- if (qs->size == 0)
- {
- qs_set_body_nn(qs, qs->b_body) ;
- qs->size = qs->b_size ;
- } ;
- qs->cp = 0 ;
+ if (qs->alias)
+ qs_set_real_body_nn(qs) ;
+ qs_set_len_nn(qs, 0) ;
+ qs_set_cp_nn(qs, 0) ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Need space for a string of 'slen' characters (plus possible '\0').
+ * Ensure have space for a string of 'slen' characters (plus possible '\0'),
+ * and discard any alias.
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * Allocate qstring if required (setting 'len' = 'cp' = 0).
*
* Returns: address of qstring -- with body that can be written upto and
* including 'slen' + 1.
*
- * NB: has no effect on 'len' -- even if 'len' > 'slen'.
+ * Has no effect on 'len' -- even if 'len' > 'slen'.
*
- * NB: has no effect on 'cp' -- even if 'cp' > 'len' or 'cp' > 'slen'.
+ * Has no effect on 'cp' -- even if 'cp' > 'len' or 'cp' > 'slen'.
*
- * NB: if this is a aliased qstring, the alias is discarded and term = false.
+ * If this is a aliased qstring, the alias is discarded.
*/
Inline qstring
-qs_need(qstring qs, usize slen)
+qs_new_size(qstring qs, usize slen)
{
- if (qs == NULL)
- qs = qs_init_new(NULL, slen) ; /* Make the qstring if required */
- else
- if (slen >= qs->size) /* for alias qs->size == 0 ! */
- qs_make_to_size(qs, slen, free_it) ;
+ if (qs == NULL)
+ qs = qs_new_with_body(slen) ;
+ else if (slen >= qs->size) /* for alias qs->size == 0 ! */
+ qs_make_to_size(qs, slen, 0) ; /* extend and/or make body */
return qs ;
} ;
/*------------------------------------------------------------------------------
- * Set 'len' -- allocate or extend body as required.
+ * Extend to 'len' + 'elen' -- allocate or extend body as required.
*
- * Allocate qstring if required (setting qs->len = qs->cp = 0).
+ * Allocate qstring if required (setting 'cp' = 0).
*
- * Returns: address of qstring -- with body that can be written upto and
- * including 'len' + 1.
+ * Returns: address of qstring -- with body that can be written up to and
+ * including 'len' + 'elen' + 1.
*
- * Sets 'cp' to the (new) 'len' if 'cp' > 'len'.
+ * Has no effect on 'cp' -- even if 'cp' > 'len'.
*
- * NB: if this is a aliased qstring, a copy is made of all of the original body,
- * even if that is longer than the required 'slen'. (And term = false.)
+ * If this is a aliased qstring, a copy is made.
*/
Inline qstring
-qs_set_len(qstring qs, usize len)
+qs_extend(qstring qs, usize elen)
{
if (qs == NULL)
- qs = qs_init_new(NULL, len) ; /* Make the qstring if required */
+ qs = qs_new_with_body(elen) ;
else
- if (len >= qs->size) /* for alias qs->size == 0 ! */
- qs_make_to_size(qs, len, keep_it) ;
-
- qs_do_set_len_nn(qs, len) ;
+ {
+ elen = qs_len_nn(qs) + elen ;
+ if (elen >= qs->size) /* for alias qs->size == 0 ! */
+ qs_make_to_size(qs, elen, elen) ; /* extend and/or copy any alias */
+ } ;
- if (qs->cp > len)
- qs->cp = len ;
+ qs_set_len_nn(qs, elen) ;
return qs ;
} ;
@@ -299,125 +592,74 @@ qs_set_len(qstring qs, usize len)
*
* Does not change the 'len' if it is <= length to chop to.
*
- * Sets 'cp' to the (new) 'len' if 'cp' > 'len'.
+ * NB: has no effect on 'cp' -- even if 'cp' > (new) 'len'.
*
* NB: if this is a aliased qstring, then it remains an aliased string, but
- * shorter and term = false (unless no change made to the length).
+ * shorter (unless no change made to the length).
*/
Inline void
qs_chop(qstring qs, usize clen)
{
if (qs != NULL)
{
- usize len = qs_len_nn(qs) ;
- if (len > clen)
- qs_do_set_len_nn(qs, (len = clen)) ; /* sets term = false */
- if (qs->cp > len)
- qs->cp = len ;
+ if (clen < qs_len_nn(qs))
+ qs_set_len_nn(qs, clen) ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Set 'cp' -- allocate or extend body as required.
- *
- * Allocates the qstring, if required.
- *
- * Returns: address of qstring
+ * Compare two qstrings -- returns the usual -ve, 0, +ve cmp result.
*
- * NB: if there was no body, allocates a body for the string, even if 'cp' == 0.
- *
- * NB: if new 'cp' > 'len', extends body (or allocates one), and sets 'len' to
- * 'cp'. If this is an alias qstring, a copy of the string is made.
+ * NULL qstring is treated as empty.
*/
-Inline qstring
-qs_set_cp(qstring qs, usize cp)
+Inline int
+qs_cmp(qstring a, qstring b)
{
- if (qs == NULL)
- qs = qs_new(qs) ;
-
- if (qs->size <= cp)
-
-
- if ((qs == NULL) || (cp >(cp > qs_len_nn(qs)))
- qs = qs_set_len(qs, cp) ;
- qs->cp = cp ;
- return qs ;
+ return els_cmp(qs_els(a), qs_els(b)) ;
} ;
/*------------------------------------------------------------------------------
- * If not "term": set '\0' at qs->len -- extending body as required.
- *
- * Does NOT affect qs->cp or qs->len.
- *
- * Returns address of body -- NULL if the qstring is NULL
- *
- * NB: if this is an alias, and it is not terminated, make a copy before adding
- * terminating '\0'.
+ * Compare qstrings to given word -- see els_cmp_word
*/
-Inline void
-qs_terminate_nn(qstring qs)
+Inline int
+qs_cmp_word(qstring a, const char* w)
{
- if (!qs_term_nn(qs))
- {
- usize len ;
-
- len = qs_len_nn(qs) ;
-
- if (len >= qs->size) /* alias has size == 0 */
- qs_make_to_size(qs, len) ; /* make sure can insert '\0' */
-
- *qs_char_at_nn(qs, len) = '\0' ;
-
- qs_set_term_nn(qs, true) ;
- } ;
+ return els_cmp_word(qs_els(a), w) ;
} ;
/*------------------------------------------------------------------------------
- * Return pointer to '\0' terminated string value.
+ * Compare significant parts of two qstrings -- returns the usual -ve, 0, +ve
+ * cmp result.
*
- * If qs is NULL or body is NULL returns pointer to constant empty '\0'
- * terminated string.
+ * By significant, mean excluding leading/trailing isspace() and treating
+ * multiple isspace() as single isspace().
*
- * If string is terminated, return address of string, which may be an alias
- * address.
- *
- * Otherwise, makes sure that there is a '\0' at the qs->len position, making
- * a copy of any aliased string if required, and returns address of result.
- *
- * NB: value returned may be the address of the qstring body, or the address of
- * an aliased string. In any event, the string should not be changed or
- * reset until this pointer has been discarded !
+ * NULL qstring is treated as empty.
*/
-Inline const char*
-qs_string(qstring qs)
+Inline int
+qs_cmp_sig(qstring a, qstring b)
{
- if ((qs == NULL) || (qs_len_nn(qs) == 0))
- return "" ;
-
- qs_terminate_nn(qs) ;
+ return els_cmp_sig(qs_els(a), qs_els(b)) ;
+} ;
- return qs_body_nn(qs) ;
+/*------------------------------------------------------------------------------
+ * Are two qstrings equal ? -- returns true if they are.
+ */
+Inline bool
+qs_equal(qstring a, qstring b)
+{
+ return els_equal(qs_els(a), qs_els(b)) ;
} ;
/*------------------------------------------------------------------------------
- * Assuming the given address is within the size of the given qstring,
- * set qs->len and insert '\0' terminator there.
- *
- * Does NOT affect qs->cp.
- *
- * NB: must NOT be a NULL qs.
+ * Is 'b' a leading substring of 'a' ? -- returns true if it is.
*
- * NB: must NOT be an aliased qstring.
- *
- * NB: must NOT have a NULL body.
+ * If 'b' is empty it is always a leading substring.
*/
-Inline void
-qs_term_here(qstring qs, char* here)
+Inline bool
+qs_substring(qstring a, qstring b)
{
- assert((here >= qs->s.char_body) && (here < (qs->s.char_body + qs->size))) ;
-
- qs->len = (here - qs->s.char_body) ;
- *here = '\0' ;
+ return els_substring(qs_els(a), qs_els(b)) ;
} ;
#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qtime.c b/lib/qtime.c
index 3ec34a72..92e67e27 100644
--- a/lib/qtime.c
+++ b/lib/qtime.c
@@ -192,3 +192,51 @@ qt_craft_monotonic(void) {
return (monotonic * times_scale_q) +
((monotonic * times_scale_r) / times_clk_tcks) ;
} ;
+
+/*==============================================================================
+ * A simple minded random number generator.
+ *
+ * Designed to be reasonably unpredictable... particularly the ms bits !
+ */
+
+static inline uint32_t
+qt_rand(uint64_t q, uint64_t s)
+{
+ /* Takes q ^ s and reduces to 32 bits by xoring ms and ls halves
+ * then uses Knuth recommended linear congruent to randomise that, so that
+ * most of the original bits affect the result.
+ *
+ * Note that linear congruent tends to be "more random" in the ms bits.
+ */
+ q ^= s ;
+ q = (q ^ (q >> 32)) & 0xFFFFFFFF ;
+ return ((q * 2650845021) + 5) & 0xFFFFFFFF ;
+} ;
+
+extern uint32_t
+qt_random(uint32_t seed)
+{
+ uint32_t x, y ;
+ uint64_t t ;
+
+ t = qt_get_realtime() ;
+
+ /* Set x by munging the time, the address of x, the current contents of x,
+ * and the "seed". (Munge the seed a bit for good measure.)
+ */
+ x = qt_rand(t ^ (uint64_t)x ^ (uint64_t)&x, seed ^ 3141592653) ;
+ /* munge the address and the contents with the seed */
+
+ /* Set y by munging the time, the address of y, the current contents of y,
+ * and the "seed". (Munge the seed a bit for good measure.)
+ */
+ y = qt_rand(t ^ (uint64_t)y ^ (uint64_t)&y, seed ^ 3562951413) ;
+ /* munge the current real time with the seed */
+
+ /* Return x and y munged together.
+ *
+ * Note that we swap the halves of y before munging, in order to spread
+ * the "more random" part of y down to the ls end of the result.
+ */
+ return x ^ ((y >> 16) & 0xFFFF) ^ ((y & 0xFFFF) << 16) ;
+} ;
diff --git a/lib/qtime.h b/lib/qtime.h
index 38e9ac1a..94d468a8 100644
--- a/lib/qtime.h
+++ b/lib/qtime.h
@@ -23,14 +23,11 @@
#define _ZEBRA_QTIME_H
#include "misc.h"
-#include <stdlib.h>
+
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
-#include "zassert.h"
-#include "config.h"
-
/*==============================================================================
* qtime_t -- signed 64-bit integer.
*
@@ -64,17 +61,13 @@ typedef qtime_t qtime_tod_t ; /* qtime_t value, timeofday time-base... */
/* Note that the time to convert may be a float. */
#define QTIME(s) ((qtime_t)((s) * (qtime_t)QTIME_SECOND))
-Inline qtime_t
-timespec2qtime(struct timespec* p_ts) ;
-
-Inline qtime_t
-timeval2qtime(struct timeval* p_tv) ;
-
-Inline struct timespec*
-qtime2timespec(struct timespec* p_ts, qtime_t qt) ;
-
-Inline struct timeval*
-qtime2timeval(struct timeval* p_tv, qtime_t qt) ;
+/*==============================================================================
+ * Conversion functions
+ */
+Inline qtime_t timespec2qtime(struct timespec* p_ts) ;
+Inline qtime_t timeval2qtime(struct timeval* p_tv) ;
+Inline struct timespec* qtime2timespec(struct timespec* p_ts, qtime_t qt) ;
+Inline struct timeval* qtime2timeval(struct timeval* p_tv, qtime_t qt) ;
/*==============================================================================
* Clocks.
@@ -93,44 +86,37 @@ qtime2timeval(struct timeval* p_tv, qtime_t qt) ;
* a manufactured equivalent using times() -- see qt_craft_monotonic().
*/
-Inline qtime_real_t
-qt_get_realtime(void) ; /* clock_gettime(CLOCK_REALTIME, ...) */
+Inline qtime_real_t qt_get_realtime(void) ;
+ /* clock_gettime(CLOCK_REALTIME, ...) */
+Inline qtime_mono_t qt_add_realtime(qtime_t interval) ;
+ /* qt_get_realtime() + interval */
-Inline qtime_mono_t
-qt_add_realtime(qtime_t interval) ; /* qt_get_realtime() + interval */
-
-Inline qtime_mono_t
-qt_get_monotonic(void) ; /* clock_gettime(CLOCK_MONOTONIC, ...) */
+Inline qtime_mono_t qt_get_monotonic(void) ;
+ /* clock_gettime(CLOCK_MONOTONIC, ...) */
/* OR equivalent using times() */
-Inline qtime_mono_t
-qt_add_monotonic(qtime_t interval) ; /* qt_get_monotonic() + interval */
-
-Inline qtime_mono_t /* monotonic time from CLOCK_REALTIME */
-qt_realtime2monotonic(qtime_real_t realtime) ;
-Inline qtime_real_t /* CLOCK_REALTIME from monotonic time */
-qt_monotonic2realtime(qtime_mono_t monotonic) ;
-
-/* Function to manufacture a monotonic clock. */
-extern qtime_mono_t
-qt_craft_monotonic(void) ;
+Inline qtime_mono_t qt_add_monotonic(qtime_t interval) ;
+ /* qt_get_monotonic() + interval */
/* These are provided just in case gettimeofday() != CLOCK_REALTIME */
-Inline qtime_tod_t
-qt_get_timeofday(void) ; /* gettimeofday(&tv, NULL) */
+Inline qtime_tod_t qt_get_timeofday(void) ;
+ /* gettimeofday(&tv, NULL) */
+Inline qtime_tod_t qt_add_timeofday(qtime_t interval) ;
+ /* qt_get_timeofday() + interval */
-Inline qtime_tod_t
-qt_add_timeofday(qtime_t interval) ; /* qt_get_timeofday() + interval */
-
-Inline qtime_mono_t /* monotonic time from timeofday */
-qt_timeofday2monotonic(qtime_tod_t timeofday) ;
-Inline qtime_tod_t /* timeofday from monotonic time */
-qt_monotonic2timeofday(qtime_mono_t monotonic) ;
+/*==============================================================================
+ * Primitive random number generation.
+ *
+ * Uses time and other stuff to produce something which is not particularly
+ * predictable.
+ */
+extern uint32_t qt_random(uint32_t seed) ;
/*==============================================================================
* Inline conversion functions
*/
-/* Convert timespec to qtime_t
+/*------------------------------------------------------------------------------
+ * Convert timespec to qtime_t
*
* Returns qtime_t value.
*/
@@ -141,7 +127,8 @@ timespec2qtime(struct timespec* p_ts)
confirm(QTIME_SECOND == TIMESPEC_SECOND) ;
} ;
-/* Convert timeval to qtime_t
+/*------------------------------------------------------------------------------
+ * Convert timeval to qtime_t
*
* Returns qtime_t value.
*/
@@ -152,7 +139,8 @@ timeval2qtime(struct timeval* p_tv)
confirm(QTIME_SECOND == TIMEVAL_SECOND * 1000) ;
} ;
-/* Convert qtime_t to timespec
+/*------------------------------------------------------------------------------
+ * Convert qtime_t to timespec
*
* Takes address of struct timespec and returns that address.
*/
@@ -169,7 +157,8 @@ qtime2timespec(struct timespec* p_ts, qtime_t qt)
return p_ts ;
} ;
-/* Convert timespec to qtime_t
+/*------------------------------------------------------------------------------
+ * Convert timespec to qtime_t
*
* Takes address of struct timespec and returns that address.
*/
@@ -190,7 +179,11 @@ qtime2timeval(struct timeval* p_tv, qtime_t qt)
* Inline Clock Functions.
*/
-/* Read given clock & return a qtime_t value.
+/* Function to manufacture a monotonic clock. */
+Private qtime_mono_t qt_craft_monotonic(void) ;
+
+/*------------------------------------------------------------------------------
+ * Read given clock & return a qtime_t value.
*
* While possibility of error is essentially theoretical, must treat it as a
* FATAL error -- cannot continue with broken time value !
@@ -207,7 +200,8 @@ qt_clock_gettime(clockid_t clock_id)
return timespec2qtime(&ts) ;
} ;
-/* clock_gettime(CLOCK_REALTIME, ...) -- returning qtime_t value
+/*------------------------------------------------------------------------------
+ * clock_gettime(CLOCK_REALTIME, ...) -- returning qtime_t value
*
* While possibility of error is essentially theoretical, must treat it as a
* FATAL error -- cannot continue with broken time value !
@@ -218,7 +212,8 @@ qt_get_realtime(void)
return qt_clock_gettime(CLOCK_REALTIME) ;
} ;
-/* qt_get_realtime() + interval
+/*------------------------------------------------------------------------------
+ * qt_get_realtime() + interval
*/
Inline qtime_real_t
qt_add_realtime(qtime_t interval)
@@ -226,7 +221,8 @@ qt_add_realtime(qtime_t interval)
return qt_get_realtime() + interval;
} ;
-/* clock_gettime(CLOCK_MONOTONIC, ...) OR qt_craft_monotonic()
+/*------------------------------------------------------------------------------
+ * clock_gettime(CLOCK_MONOTONIC, ...) OR qt_craft_monotonic()
* -- returning qtime_t value
*
* While possibility of error is essentially theoretical, must treat it as a
@@ -242,7 +238,8 @@ qt_get_monotonic(void)
#endif
} ;
-/* qt_get_monotonic() + interval
+/*------------------------------------------------------------------------------
+ * qt_get_monotonic() + interval
*/
Inline qtime_mono_t
qt_add_monotonic(qtime_t interval)
@@ -250,7 +247,8 @@ qt_add_monotonic(qtime_t interval)
return qt_get_monotonic() + interval;
} ;
-/* gettimeofday(&tv, NULL) -- returning qtime_t value
+/*------------------------------------------------------------------------------
+ * gettimeofday(&tv, NULL) -- returning qtime_t value
*/
Inline qtime_tod_t
qt_get_timeofday(void)
@@ -260,7 +258,8 @@ qt_get_timeofday(void)
return timeval2qtime(&tv) ;
}
-/* qt_get_timeofday() + interval
+/*------------------------------------------------------------------------------
+ * qt_get_timeofday() + interval
*/
Inline qtime_tod_t
qt_add_timeofday(qtime_t interval)
@@ -268,36 +267,4 @@ qt_add_timeofday(qtime_t interval)
return qt_get_timeofday() + interval;
} ;
-/*==============================================================================
- * Conversion between realtime/timeofday and monotonic
- */
-
-/* Convert a CLOCK_REALTIME time to our local monotonic time. */
-Inline qtime_mono_t
-qt_realtime2monotonic(qtime_real_t realtime)
-{
- return qt_get_monotonic() + (realtime - qt_get_realtime()) ;
-} ;
-
-/* Convert a local monotonic time to CLOCK_REALTIME time. */
-Inline qtime_real_t
-qt_monotonic2realtime(qtime_mono_t monotonic)
-{
- return qt_get_realtime() + (monotonic - qt_get_monotonic()) ;
-} ;
-
-/* Convert a gettimeofday() time to our local monotonic time. */
-Inline qtime_mono_t
-qt_timeofday2monotonic(qtime_tod_t timeofday)
-{
- return qt_get_monotonic() + (timeofday - qt_get_timeofday()) ;
-} ;
-
-/* Convert a local monotonic time to gettimeofday() time. */
-Inline qtime_tod_t
-qt_monotonic2timeofday(qtime_mono_t monotonic)
-{
- return qt_get_timeofday() + (monotonic - qt_get_monotonic()) ;
-} ;
-
#endif /* _ZEBRA_QTIME_H */
diff --git a/lib/qtimers.c b/lib/qtimers.c
index 5c0f1518..6ccb7f7d 100644
--- a/lib/qtimers.c
+++ b/lib/qtimers.c
@@ -27,14 +27,6 @@
#include "memory.h"
#include "heap.h"
-enum { qdebug =
-#ifdef QDEBUG
- 1
-#else
- 0
-#endif
-};
-
/*==============================================================================
* Quagga Timers -- qtimer_xxxx
*
@@ -170,7 +162,7 @@ qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto)
{
qtimer qtr ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
qtr = heap_top_item(&qtp->timers) ;
@@ -343,7 +335,7 @@ qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
qtp = qtr->pile ;
assert(qtp != NULL) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
qtr->time = when ;
@@ -371,7 +363,7 @@ qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
else
assert(qtr->action != NULL) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
} ;
@@ -387,7 +379,7 @@ qtimer_unset(qtimer qtr)
assert(qtp != NULL) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
if (qtr->active)
@@ -397,7 +389,7 @@ qtimer_unset(qtimer qtr)
heap_delete_item(&qtp->timers, qtr) ;
- if (qdebug)
+ if (qtimers_debug)
qtimer_pile_verify(qtp) ;
qtr->active = false ;
diff --git a/lib/qtimers.h b/lib/qtimers.h
index 8534a789..2af81819 100644
--- a/lib/qtimers.h
+++ b/lib/qtimers.h
@@ -35,6 +35,22 @@
* each with an action to be executed when the timer expires.
*/
+#ifdef QTIMERS_DEBUG /* Can be forced from outside */
+# if QTIMERS_DEBUG
+# define QTIMERS_DEBUG 1 /* Force 1 or 0 */
+#else
+# define QTIMERS_DEBUG 0
+# endif
+#else
+# ifdef QDEBUG
+# define QTIMERS_DEBUG 1 /* Follow QDEBUG */
+#else
+# define QTIMERS_DEBUG 0
+# endif
+#endif
+
+enum { qtimers_debug = QTIMERS_DEBUG } ;
+
/*==============================================================================
* Data Structures.
*/
diff --git a/lib/routemap.c b/lib/routemap.c
index b452530d..d716e0b5 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -959,7 +959,7 @@ DEFUN (route_map,
index = route_map_index_get (map, permit, seq);
vty->index = index;
- vty_set_node(vty, RMAP_NODE) ;
+ vty->node = RMAP_NODE ;
return CMD_SUCCESS;
}
diff --git a/lib/symtab.h b/lib/symtab.h
index 4e33390e..008b7853 100644
--- a/lib/symtab.h
+++ b/lib/symtab.h
@@ -168,7 +168,7 @@ extern void* symbol_set_value(symbol sym, void* new_value) ;
Inline void*
symbol_unset_value(symbol sym)
{
- symbol_set_value(sym, NULL) ;
+ return symbol_set_value(sym, NULL) ;
} ;
extern void symbol_ref_walk_start(symbol sym, symbol_ref walk) ;
diff --git a/lib/thread.h b/lib/thread.h
index fa021486..6f74876d 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -206,7 +206,7 @@ extern int thread_should_yield (struct thread *);
/* Internal libzebra exports */
extern void thread_getrusage (RUSAGE_T *);
-extern struct cmd_element show_thread_cpu_cmd;
+extern struct cmd_command show_thread_cpu_cmd;
/* replacements for the system gettimeofday(), clock_gettime() and
* time() functions, providing support for non-decrementing clock on
diff --git a/lib/uty.h b/lib/uty.h
deleted file mode 100644
index cc759673..00000000
--- a/lib/uty.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/* VTY internal stuff -- header
- * Copyright (C) 1997 Kunihiro Ishiguro
- *
- * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Zebra; see the file COPYING. If not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef _ZEBRA_UTY_H
-#define _ZEBRA_UTY_H
-
-#include "misc.h"
-#include "vargs.h"
-
-#include "qpthreads.h"
-#include "qpnexus.h"
-#include "thread.h"
-#include "list_util.h"
-#include "vty.h"
-#include "vty_io_basic.h"
-#include "node_type.h"
-
-/*==============================================================================
- * This is stuff which is used by the close family of:
- *
- * vty
- * command
- * command_queue
- * log
- *
- * and which is not for use elsewhere.
- *
- * There are also:
- *
- * vty_io
- * vty_cli
- *
- * Which are the "immediate family" of vty:
- *
- * * *nothing* in their ".h" is for use anywhere except the immediate family.
- *
- * * things for use within the rest of the family are published here.
- */
-
-/*==============================================================================
- * Variables in vty.c -- used in any of the family
- */
-extern vty_io vio_list_base ;
-extern vty_io vio_monitors_base ;
-extern vty_io vio_death_watch ;
-
-extern struct thread_master* vty_master ;
-
-extern unsigned long vty_timeout_val ;
-
-extern bool vty_config ;
-
-extern bool no_password_check ;
-extern const bool restricted_mode_default ;
-extern bool restricted_mode ;
-
-char *vty_accesslist_name ;
-char *vty_ipv6_accesslist_name ;
-
-extern qpn_nexus vty_cli_nexus ;
-extern qpn_nexus vty_cmd_nexus ;
-
-/*==============================================================================
- * To make vty qpthread safe we use a single mutex.
- *
- * vty and log recurse through each other, so the same mutex is used
- * for both, i.e. they are treated as being part of the same monitor.
- *
- * A recursive mutex is used. This simplifies the calling from log to vty and
- * back again. It also allows for the vty internals to call each other.
- *
- * There are some "uty" functions which assume the mutex is locked.
- *
- * vty is closely bound to the command handling -- the main vty structure
- * contains the context in which commands are parsed and executed.
- */
-
-extern qpt_mutex_t vty_mutex ;
-
-#ifdef NDEBUG
-# define VTY_DEBUG 0 /* NDEBUG override */
-#else
-# ifndef VTY_DEBUG
-# define VTY_DEBUG 1 /* Set to 1 to turn on debug checks */
-# endif
-#endif
-
-#if VTY_DEBUG
-
-extern int vty_lock_count ;
-extern int vty_assert_fail ;
-
-#endif
-
-Inline void
-VTY_LOCK(void) /* if is qpthreads_enabled, lock vty_mutex */
-{
- qpt_mutex_lock(&vty_mutex) ;
- if (VTY_DEBUG)
- ++vty_lock_count ;
-} ;
-
-Inline void
-VTY_UNLOCK(void) /* if is qpthreads_enabled, unlock vty_mutex */
-{
- if (VTY_DEBUG)
- --vty_lock_count ;
- qpt_mutex_unlock(&vty_mutex) ;
-} ;
-
-Inline bool /* true => is (effectively) cli thread */
-vty_is_cli_thread(void)
-{
- return !qpthreads_enabled || qpt_thread_is_self(vty_cli_nexus->thread_id) ;
-} ;
-
-/* For debug (and documentation) purposes, will VTY_ASSERT_LOCKED where that
- * is required.
- *
- * In some cases, need also to be running in the CLI thread as well.
- */
-#if VTY_DEBUG
-
-Inline void
-VTY_ASSERT_FAILED(void)
-{
- if (vty_assert_fail == 0) ;
- {
- vty_assert_fail = 1 ;
- assert(0) ;
- } ;
-} ;
-
-Inline void
-VTY_ASSERT_LOCKED(void)
-{
- if (vty_lock_count == 0)
- VTY_ASSERT_FAILED() ;
-} ;
-
-Inline void
-VTY_ASSERT_CLI_THREAD(void)
-{
- if (!vty_is_cli_thread())
- VTY_ASSERT_FAILED() ;
-} ;
-
-#else
-
-#define VTY_ASSERT_LOCKED()
-#define VTY_ASSERT_CLI_THREAD()
-
-#endif
-
-/*==============================================================================
- * Shared definitions
- */
-
-enum cli_do
-{
- cli_do_nothing = 0, /* no action required */
-
- cli_do_command, /* dispatch the current command line */
- cli_do_ctrl_c, /* received ^c */
- cli_do_ctrl_d, /* received ^d on empty line */
- cli_do_ctrl_z, /* received ^z */
-
- cli_do_eof, /* hit "EOF" */
-
- cli_do_count /* number of different cli_do_xxx */
-} ;
-
-/*==============================================================================
- * Functions in vty.c -- used in any of the family
- */
-extern enum cmd_return_code uty_command(struct vty *vty) ;
-extern enum cmd_return_code uty_auth (struct vty *vty, const char *buf,
- enum cli_do cli_do) ;
-extern enum cmd_return_code vty_cmd_exit(struct vty* vty) ;
-extern enum cmd_return_code vty_cmd_end(struct vty* vty) ;
-extern enum cmd_return_code uty_cmd_close(struct vty *vty, const char* reason) ;
-extern enum cmd_return_code uty_stop_input(struct vty *vty) ;
-extern enum cmd_return_code uty_end_config (struct vty *vty) ;
-extern enum cmd_return_code uty_down_level (struct vty *vty) ;
-
-extern bool vty_config_lock (struct vty *, enum node_type node);
-extern void vty_config_unlock (struct vty *, enum node_type node);
-extern void uty_config_unlock (struct vty *vty, enum node_type node) ;
-
-/*==============================================================================
- * Functions in vty_cli -- used outside the immediate vty family
- */
-extern void vty_queued_result(struct vty* vty, enum cmd_return_code ret);
-extern void uty_set_host_name(const char* name) ;
-
-/*==============================================================================
- * Functions in vty_io -- used outside the immediate vty family
- */
-extern void vty_open_config_write(struct vty* vty, int fd) ;
-extern int vty_close_config_write(struct vty*) ;
-
-extern void vty_log_fixed (const char *buf, size_t len);
-
-extern void uty_log (struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va);
-
-/*==============================================================================
- * Functions in command.c
- */
-extern void cmd_post_command(struct vty* vty, int ret) ;
-
-#endif /* _ZEBRA_UTY_H */
diff --git a/lib/vector.c b/lib/vector.c
index 6df8d409..15371930 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -22,7 +22,7 @@
* 02111-1307, USA.
*/
-#include <zebra.h>
+//#include <zebra.h>
#include "vector.h"
#include "memory.h"
@@ -1055,7 +1055,7 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
if (v->end <= 1)
{
- *result = (v->end == 0) ? -1 : cmp(&p_val, (const void**)&v->p_items[0]) ;
+ *result = (v->end == 0) ? -1 : cmp(&p_val, (const cvp*)&v->p_items[0]) ;
return 0 ; /* Stop dead if 0 or 1 items */
} ;
@@ -1064,12 +1064,12 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
ih = v->end - 1 ;
/* Pick off the edge cases: >= last and <= first. */
- if ((c = cmp(&p_val, (const void**)&v->p_items[ih])) >= 0)
+ if ((c = cmp(&p_val, (const cvp*)&v->p_items[ih])) >= 0)
{
*result = c ; /* 0 => found. +1 => val > last */
return ih ; /* return high index. */
} ;
- if ((c = cmp(&p_val, (const void**)&v->p_items[il])) <= 0)
+ if ((c = cmp(&p_val, (const cvp*)&v->p_items[il])) <= 0)
{
*result = c ; /* 0 => found. -1 => val < first */
return il ; /* return low index. */
@@ -1086,7 +1086,7 @@ vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
return il ; /* return il: item[il] < val < item[il+1] */
} ;
/* We now know that il < iv < ih */
- c = cmp(&p_val, (const void**)&v->p_items[iv]) ;
+ c = cmp(&p_val, (const cvp*)&v->p_items[iv]) ;
if (c == 0)
{
*result = 0 ;
diff --git a/lib/vector.h b/lib/vector.h
index 58b9e415..bae2a2f8 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -50,6 +50,16 @@ struct vector
typedef struct vector vector_t[1] ; /* embedded vector structure */
typedef struct vector* vector ; /* pointer to vector structure */
+/* Setting a vector object to all zeros is enough to initialise it to
+ * an empty vector.
+ */
+enum
+{
+ VECTOR_INIT_ALL_ZEROS = true
+} ;
+
+#define VECTOR_INIT_EMPTY { .p_items = NULL, .end = 0, .limit = 0 }
+
/* Under very controlled circumstances, may access the vector body */
typedef p_vector_item const* vector_body_t ;
@@ -162,10 +172,10 @@ Inline p_vector_item vector_shift_item(vector v) ;
extern void vector_insert(vector v, vector_index_t i, vector_length_t n) ;
extern void vector_delete(vector v, vector_index_t i, vector_length_t n) ;
-typedef int vector_bsearch_cmp(const void** pp_val, const void** item) ;
+typedef int vector_bsearch_cmp(const cvp* pp_val, const cvp* item) ;
vector_index_t vector_bsearch(vector v, vector_bsearch_cmp* cmp,
const void* p_val, int* result) ;
-typedef int vector_sort_cmp(const void** a, const void** b) ;
+typedef int vector_sort_cmp(const cvp* a, const cvp* b) ;
void vector_sort(vector v, vector_sort_cmp* cmp) ;
extern vector vector_copy_here(vector dst, vector src) ;
diff --git a/lib/version.h.in b/lib/version.h.in
index 429474d1..ded43944 100644
--- a/lib/version.h.in
+++ b/lib/version.h.in
@@ -34,6 +34,8 @@
#define QUAGGA_COPYRIGHT "Copyright 1996-2005 Kunihiro Ishiguro, et al."
+#include <sys/types.h>
+
pid_t pid_output (const char *);
#ifndef HAVE_DAEMON
diff --git a/lib/vio_fifo.c b/lib/vio_fifo.c
index 10c4a343..e6365861 100644
--- a/lib/vio_fifo.c
+++ b/lib/vio_fifo.c
@@ -20,14 +20,13 @@
*/
#include "misc.h"
+#include <stdio.h>
#include <string.h>
#include "vio_fifo.h"
#include "network.h"
-#include "list_util.h"
#include "memory.h"
-#include "zassert.h"
/*==============================================================================
* VTY I/O FIFO manages an arbitrary length byte-wise FIFO buffer.
@@ -39,7 +38,6 @@
* ever needed.
*
* When releasing lumps, keeps one lump "spare", to be reused as necessary.
- * This is used in ... TODO <<<< And is released...
*
*------------------------------------------------------------------------------
* Implementation notes:
@@ -48,97 +46,218 @@
*
* Once a lump has been allocated there is always one lump in the FIFO.
*
+ * The hold_mark allows the get_ptr to move forward, but retaining the data in
+ * the FIFO until the hold_mark is cleared. Can move the get_ptr back to the
+ * hold_mark to reread the data.
+ *
+ * The end_mark allows put_ptr to move forward, but the new data cannot be got
+ * from the FIFO until the end_mark is cleared. Can discard the new data
+ * and move the put_ptr back to the end_mark.
+ *
+ * There are four lumps of interest:
+ *
+ * * head -- where the hold_mark is, if there is one.
+ *
+ * * get_lump -- where the get_ptr is.
+ * Same as head when no hold_mark.
+ *
+ * * end_lump -- where the end_mark is, if there is one.
+ * Same as tail when no end mark.
+ *
+ * * tail -- where the put_ptr is.
+ *
+ * Some or all of those may be the same, depending on how big the FIFO is.
+ *
* The following are expected to be true:
*
- * * put_ptr == get_ptr => FIFO empty
+ * * set <=> at least one lump in the FIFO
+ *
+ * * as_one <=> set && get_lump == tail && !end_mark
+ * => get_end moves with put_ptr
*
- * * put_ptr == tail->end -- at all times (NULL when no lumps)
+ * * hold_mark => there is a hold mark & hold_ptr is valid
+ * & head lump contains mark
+ * !hold_mark => get_lump == head & hold_ptr == NULL
+ *
+ * * end_mark => there is an end_mark & end_end is valid
+ * & end_lump contains mark
+ * !end_mark => end_lump == tail & end_ptr == NULL
+ *
+ * * put_ptr == get_ptr => FIFO empty -- unless hold_mark and
+ * hold_ptr != get_ptr.
+ *
+ * * put_end == tail->end -- at all times (NULL when no lumps)
*
* put_ptr >= tail->data ) otherwise something is broken
* put_ptr <= tail->end )
*
- * * get_ptr == head->end -- when there is more than one lump
- * get_ptr <= put_ptr -- when there is only one lump
+ * * get_end == get_lump->end -- when get_lump != end_lump
+ * == end_end -- when get_lump == end_lump & end_mark set
+ * <= put_ptr -- when get_lump == end_lump & no end_mark
+ *
+ * See note below on get_end and as_one flag.
*
- * get_ptr >= head->data ) otherwise something is broken
- * get_ptr <= head->end )
+ * get_ptr >= get_lump->data ) otherwise something is broken
+ * get_ptr <= get_lump->end )
*
* * put_ptr == put_end => tail lump is full
* put_ptr < put_end => space exists in the tail lump
* put_ptr > put_end => broken
*
- * * get_ptr == get_end => head lump is empty
- * BUT if there is only one lump, make sure that
- * get_end == put_ptr.
- * get_ptr < get_end => data exists in the head lump
+ * * get_ptr == get_end => nothing to get from current get_lump
+ * BUT if as_one, make sure that get_end == put_ptr
+ * get_ptr < get_end => data exists in the current get_lump
* get_ptr > get_end => broken
*
* Note that:
*
- * * when the get_ptr reaches the put_ptr the pointers are reset to the
- * start of the one and only lump.
+ * * while get_ptr <= get_end can get stuff without worrying about other
+ * pointers or moving between lumps etc. It is permissible to leave
+ * get_ptr == get_end -- this will be tidied up on the next get operation,
+ * or any other time vio_fifo_sync_get() is called. Leaving get_ptr in
+ * that state delays the discard of the now empty lump, but has no other
+ * real downside.
+ *
+ * Similarly, while put_ptr <= put_end, can put stuff without worrying
+ * about other pointers or moving between lumps etc.
+ *
+ * When getting, if get_ptr == get_end, or require more data than is
+ * immediately available, need to use vio_fifo_sync_get(), to do that.
+ *
+ * * the value of get_end depends on whether get_lump == end_lump, and then
+ * whether there is an end_mark.
+ *
+ * When get_lump == end_lump && !end_mark, then get_end may be out of date
+ * because get_ptr has been advanced. This is dealt with by
+ * vio_fifo_sync_get(), which uses the as_one flag to signal that it should
+ * set get_end = put_ptr and then (re)check for anything to get.
*
- * Everywhere that the get_ptr is moved, must check for meeting the
- * put_ptr and reset pointers. At the same time, when reaches the end of
- * a lump, gets rid of it.
+ * The as_one flag is there to save a little work. Making sure that the
+ * get_end is up to date can be done often when getting from the FIFO. So
+ * the maintenance of the flag should be worth the effort.
*
- * * when advancing the put_ptr does not check for advancing the get_end.
+ * * some care must be taken to ensure that are not fooled by the
+ * ambiguity of a pointer to the end of one lump and a pointer to the
+ * start of the next -- these are really equal, but they don't look as if
+ * they are !
*
- * The one exception to this, is that when the put_ptr advances to a new
- * block, if there was one lump, sets the get_end to the end of that block.
+ * - put_ptr -- if at the end of the last lump there is no next lump !
*
- * Everywhere that the get_end is used, must check for there being one
- * lump and the possibility that put_ptr has changed.
+ * When a new lump is added, the put_ptr advances to the
+ * start of the new last lump.
+ *
+ * - end_end -- when set from the put_ptr, this can be at the end of
+ * the last lump, but as above, there is no next lump.
+ *
+ * When a new lump is added, if the end_end is at the
+ * end of the last lump, it is moved to the start of the
+ * new last lump (along with the out_ptr).
+ *
+ * So... end_end will never be ambiguous.
+ *
+ * - get_ptr -- this can be ambiguous.
+ *
+ * When getting bytes, if the segment get_ptr..get_end
+ * is sufficient, then nothing else is required.
+ *
+ * Otherwise, vio_fifo_sync_get() will sort things out,
+ * including resetting all pointers if the FIFO has been
+ * emptied.
+ *
+ * - hold_ptr -- when set from get_ptr this could be at the end of the
+ * first lump -- but when vio_fifo_sync_get() is called,
+ * that will be spotted and sorted out.
+ *
+ * If get_ptr is set from an ambiguous hold_ptr, that is
+ * also taken care of by the next vio_fifo_sync_get().
+ *
+ * When doing things with hold_ptr, does a number of
+ * vio_fifo_sync_get() operations, so the hold_ptr should
+ * not, in practice, be ambiguous.
+ *
+ * * Before the first lump is allocated the FIFO appears empty (of course)
+ * but may have hold_mark and/or end_mark set, and these work as expected.
*/
/*==============================================================================
* Initialisation, allocation and freeing of FIFO and lumps thereof.
*/
-/* Return default size, or given size rounded up to 16 byte boundary */
+/*------------------------------------------------------------------------------
+ * Return default size, or given size rounded up to 128 byte boundary
+ */
static size_t
vio_fifo_size(size_t size)
{
+#if VIO_FIFO_DEBUG
+#warning VIO_FIFO_DEBUG and 29 byte lumps !
+ return 29 ;
+#else
if (size == 0)
return 4096 ;
else
- return ((size + 16 - 1) / 16) * 16 ;
+ return ((size + 128 - 1) / 128) * 128 ;
+#endif
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set and return true end of lump.
+ *
+ * End of lump can be set short of true end when putting stuff, if wish to
+ * move to next lump early (eg if not enough room left in current lump).
+ *
+ * When reusing a lump, need to restore the true lump end.
+ */
+static inline char*
+vio_fifo_true_lump_size(vio_fifo_lump lump)
+{
+ return lump->end = lump->data + lump->size ;
} ;
/*==============================================================================
* Initialise VTY I/O FIFO -- allocating if required.
*/
extern vio_fifo
-vio_fifo_init_new(vio_fifo vf, size_t size)
+vio_fifo_init_new(vio_fifo vff, size_t size)
{
- if (vf == NULL)
- vf = XCALLOC(MTYPE_VIO_FIFO, sizeof(vio_fifo_t)) ;
+ if (vff == NULL)
+ vff = XCALLOC(MTYPE_VIO_FIFO, sizeof(vio_fifo_t)) ;
else
- memset(vf, 0, sizeof(vio_fifo_t)) ;
+ memset(vff, 0, sizeof(vio_fifo_t)) ;
/* Zeroising the the vio_fifo_t has set:
*
- * lump -- base pair, both pointers NULL => list is empty
+ * base -- base pair, both pointers NULL => list is empty
*
- * put_ptr -- NULL ) no lump to put anything into
- * put_end -- NULL ) put_ptr == put_end => no room in current lump
+ * set -- false -- nothing set, yet.
+ * as_one -- false -- get_lump != tail or end_mark
+ * hold_mark -- false -- no hold mark
+ * end_mark -- false -- no end mark
+ *
+ * hold_ptr -- NULL no hold_mark
*
+ * get_lump -- NULL )
* get_ptr -- NULL ) no lump to get anything from
* get_end -- NULL ) get_ptr -- get_end => nothing left in current lump
*
- * rdr_lump -- NULL ) no rdr_lump
- * rdr_ptr -- NULL
+ * end_lump -- NULL no lump at end of what can get
+ * end_end -- NULL no end_mark
*
- * spare -- NULL no spare lump
+ * put_ptr -- NULL ) no lump to put anything into
+ * put_end -- NULL ) put_ptr == put_end => no room in current lump
*
- * ALSO put_ptr == get_ptr => FIFO is empty !
+ * size -- 0 no size set for lumps (yet)
+ *
+ * spare -- NULL no spare lump
*/
+ confirm(VIO_FIFO_INIT_ALL_ZEROS) ;
- vf->size = vio_fifo_size(size) ;
+ if (size != 0)
+ vff->size = vio_fifo_size(size) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- return vf ;
+ return vff ;
}
/*------------------------------------------------------------------------------
@@ -146,7 +265,8 @@ vio_fifo_init_new(vio_fifo vf, size_t size)
*
* Does nothing if given a NULL pointer -- must already have been freed !
*
- * If does not free the FIFO structure, resets it all empty.
+ * If does not free the FIFO structure, resets it all empty, keeping the
+ * current size setting.
*
* Frees *all* FIFO lumps.
*
@@ -154,104 +274,289 @@ vio_fifo_init_new(vio_fifo vf, size_t size)
* vio_fifo_reset_free(vio_fifo)
*/
extern vio_fifo
-vio_fifo_reset(vio_fifo vf, int free_structure)
+vio_fifo_reset(vio_fifo vff, free_keep_b free_structure)
{
vio_fifo_lump lump ;
- if (vf == NULL)
+ if (vff == NULL)
return NULL ;
- while (ddl_pop(&lump, vf->base, list) != NULL)
+ while (ddl_pop(&lump, vff->base, list) != NULL)
XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
- if (vf->spare != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ;
+ if (vff->spare != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, vff->spare) ;
+
+ confirm(free_it == true) ;
if (free_structure)
- XFREE(MTYPE_VIO_FIFO, vf) ; /* sets vf = NULL */
+ XFREE(MTYPE_VIO_FIFO, vff) ; /* sets vff = NULL */
else
- vio_fifo_init_new(vf, vf->size) ;
+ vio_fifo_init_new(vff, vff->size) ;
- return vf ;
+ return vff ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The FIFO has one lump -- set all pointers.
+ *
+ * Preserves end_mark -- setting to new put_ptr position
+ * Preserves hold_mark -- setting to new put_ptr position
+ *
+ * Sets as_one if no end_mark.
+ */
+inline static void
+vio_fifo_ptr_set(vio_fifo vff, vio_fifo_lump lump)
+{
+ vff->as_one = !vff->end_mark ;
+
+ vff->put_ptr = lump->data ;
+ vff->put_end = vio_fifo_true_lump_size(lump) ;
+
+ vff->hold_ptr = vff->hold_mark ? vff->put_ptr : NULL ;
+
+ vff->get_lump = lump ;
+ vff->get_ptr = vff->put_ptr ;
+ vff->get_end = vff->put_ptr ;
+
+ vff->end_lump = lump ;
+ vff->end_end = vff->end_mark ? vff->put_ptr : NULL ;
} ;
/*------------------------------------------------------------------------------
* The FIFO is empty, with one lump -- reset all pointers.
+ *
+ * Preserves end_mark -- setting to new put_ptr position
+ * Preserves hold_mark -- setting to new put_ptr position
+ *
+ * Sets as_one if no end_mark.
*/
inline static void
-vio_fifo_ptr_reset(vio_fifo vf, vio_fifo_lump lump)
+vio_fifo_ptr_reset(vio_fifo vff, vio_fifo_lump lump)
{
- if (vf->rdr_lump != NULL)
- {
- assert((lump == vf->rdr_lump) && (vf->rdr_ptr == vf->get_ptr)) ;
- vf->rdr_ptr = lump->data ;
- } ;
+ assert(vff->set) ;
+ assert(lump == ddl_tail(vff->base)) ; /* must be tail */
+ assert(lump == ddl_head(vff->base)) ; /* and must be head */
+
+ vio_fifo_ptr_set(vff, lump) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Synchronise get_end and put_ptr (if required)
+ *
+ * When in the same lump, the get_end and the put_ptr should be the same. But
+ * maintaining that every time something is put into the buffer is a pain, so
+ * we allow the put_ptr to get ahead, and re-synchronise when get_ptr hits
+ * get_end, or any other time we need the pointers to be straight.
+ *
+ * The as_one flag takes a little maintenance... it means:
+ *
+ * (get_lump == tail) && set && !end_mark
+ *
+ * If get_ptr is at the end of what can be got from the current lump, advances
+ * to the next lump if possible, releasing anything that can be released.
+ *
+ * If the FIFO is empty (with one lump), will crash all pointers down to start
+ * of current lump. The FIFO is empty if get_ptr == put_ptr, and there are no
+ * bytes held before the get_ptr.
+ *
+ * NB: object of the hold_mark is to allow users of the FIFO to store pointers
+ * to sections of the FIFO returned by vio_fifo_get_lump() and stepped
+ * over by vio_fifo_got_upto(). Note that where there is nothing to
+ * read vio_fifo_get_lump() returns NULL -- so do not have to preserve
+ * the hold_ptr when hold_ptr == get_ptr (== end_end) == put_ptr -- that
+ * is, if hold_ptr == get_ptr, we can move the hold_ptr around with the
+ * get_ptr.
+ *
+ * Can reach empty state without realising it, because the "get" stuff is not
+ * required to check every time a byte is read -- in fact, it is not necessary
+ * to check until cannot get anything because get_ptr == get_end.
+ *
+ * NB: with an end_mark there is the ambiguous position at the end of a
+ * lump -- which is the same as the start of the next lump, if any.
+ *
+ * Elsewhere we advance the end_end if it was at the very end of the FIFO
+ * and we advance the put_ptr to a new lump. But we cope here in any
+ * case.
+ *
+ * Returns: true <=> there is something in the (now) current get_lump.
+ * false <=> there is nothing else to be got (though if there is an
+ * end_mark, there may be stuff beyond that).
+ */
+static bool vio_fifo_sync_get_next(vio_fifo vff) ;
+static void vio_fifo_release_head(vio_fifo vff, vio_fifo_lump lump) ;
+
+inline static bool
+vio_fifo_sync_get(vio_fifo vff)
+{
+ if (vff->as_one) /* false if !vff->set */
+ vff->get_end = vff->put_ptr ;
- /* Note that sets the lump->end to the true lump->end */
- vf->get_ptr = vf->get_end = vf->put_ptr = lump->data ;
- vf->put_end = lump->end = lump->data + lump->size ;
+ if (vff->get_ptr < vff->get_end) /* both NULL if !vff->set */
+ return true ; /* have at least one byte */
+
+ return vio_fifo_sync_get_next(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See if fifo really is or is not empty (get_ptr == end_end or == put_ptr).
+ *
+ * Used by vio_fifo_empty(). Called iff get_ptr >= get_end !
+ *
+ * So need to vio_fifo_sync_get() and test again against end_end & put_ptr.
+ */
+Private bool
+vio_fifo_do_empty(vio_fifo vff)
+{
+ return ! vio_fifo_sync_get(vff) ;
} ;
/*------------------------------------------------------------------------------
- * The FIFO is utterly empty, with ZERO lumps -- unset all pointers.
+ * Set a new get_lump/get_ptr/get_end set. And set as_one to suit.
*/
inline static void
-vio_fifo_ptr_unset(vio_fifo vf)
+vio_fifo_set_get_ptr(vio_fifo vff, void* ptr, vio_fifo_lump lump)
{
- assert((ddl_head(vf->base) == NULL) && (ddl_tail(vf->base) == NULL)) ;
+ vff->get_lump = lump ;
+ vff->get_ptr = ptr ;
- vf->one = false ;
+ if (lump != vff->end_lump)
+ {
+ vff->get_end = lump->end ;
+ vff->as_one = false ;
+ }
+ else if (vff->end_mark)
+ {
+ vff->get_end = vff->end_end ;
+ vff->as_one = false ;
+ }
+ else
+ {
+ vff->get_end = vff->put_ptr ;
+ vff->as_one = true ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move on to next lump to get stuff from.
+ *
+ * For use by vio_fifo_sync_get() *ONLY*.
+ *
+ * Asserts that (vff->get_ptr == vff->get_end) and assumes that if as_one, then
+ * get_end == put_ptr !
+ *
+ * Returns: true <=> at least one byte in FIFO available to get.
+ */
+static bool
+vio_fifo_sync_get_next(vio_fifo vff)
+{
+ bool hold_empty ;
- vf->put_ptr = NULL ;
- vf->put_end = NULL ;
- vf->get_ptr = NULL ;
- vf->get_end = NULL ;
+ assert(vff->get_ptr == vff->get_end) ;
- vf->rdr_lump = NULL ;
- vf->rdr_ptr = NULL ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ if (!vff->set)
+ return false ; /* quit before get into any trouble */
+
+ /* Worry about whether there is anything held.
+ *
+ * NB: we know that at this point the get_ptr == get_end, so is at the end
+ * of the current get_lump. The hold_ptr cannot be ahead of get_ptr,
+ * so the test hold_ptr == get_ptr is sufficient to detect that there
+ * is nothing, in fact, held.
+ */
+ hold_empty = !vff->hold_mark || (vff->hold_ptr == vff->get_ptr) ;
+
+ /* If we are not at the end_lump, step forward, discarding current
+ * lump unless hold_mark.
+ */
+ if (vff->get_lump != vff->end_lump)
+ {
+ vio_fifo_lump next ;
+ next = ddl_next(vff->get_lump, list) ;
+
+ vio_fifo_set_get_ptr(vff, next->data, next) ;
+
+ if (hold_empty)
+ {
+ vio_fifo_release_head(vff, next) ;
+
+ /* If there is a hold_mark, then had hold_ptr == get_ptr
+ * and this is where we keep hold_ptr & get_ptr in sync.
+ */
+ if (vff->hold_mark)
+ vff->hold_ptr = vff->get_ptr ;
+ } ;
+
+ /* Return the get state now */
+ if (vff->get_ptr < vff->get_end)
+ return true ;
+ } ;
+
+ /* Still have get_ptr == get_end => nothing more to get.
+ *
+ * Check now for empty FIFO, and reset all pointers if that is the case.
+ */
+ if ((vff->get_ptr == vff->put_ptr) && hold_empty)
+ {
+ if (vff->hold_mark)
+ assert(vff->hold_ptr == vff->get_ptr) ;
+
+ if (vff->end_mark)
+ assert(vff->end_end == vff->put_ptr) ;
+
+ vio_fifo_ptr_reset(vff, vff->end_lump) ;
+ }
+
+ return false ; /* nothing to get */
} ;
/*------------------------------------------------------------------------------
* Clear out contents of FIFO -- will continue to use the FIFO.
*
+ * If required, clears any hold mark and/or end mark.
+ *
* Keeps one FIFO lump. (Frees everything else, including any spare.)
*
* Does nothing if there is no FIFO !
*/
extern void
-vio_fifo_clear(vio_fifo vf)
+vio_fifo_clear(vio_fifo vff, bool clear_marks)
{
- vio_fifo_lump tail ;
-
- if (vf == NULL)
+ if (vff == NULL)
return ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- tail = ddl_tail(vf->base) ;
+ vff->as_one = vff->set ;
+ if (clear_marks)
+ {
+ vff->hold_mark = false ; /* Discard marks */
+ vff->end_mark = false ;
+ } ;
- if (tail != NULL)
+ if (vff->set)
{
- while (ddl_head(vf->base) != tail)
+ vio_fifo_lump lump ;
+
+ while (1)
{
- vio_fifo_lump lump ;
- ddl_pop(&lump, vf->base, list) ;
+ lump = ddl_head(vff->base) ;
+ if (lump == ddl_tail(vff->base))
+ break ;
+
+ ddl_pop(&lump, vff->base, list) ;
XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
} ;
- vf->rdr_lump = NULL ; /* clear rdr */
- vf->rdr_ptr = NULL ;
-
- vf->one = true ;
- vio_fifo_ptr_reset(vf, tail) ;
- }
- else
- vio_fifo_ptr_unset(vf) ;
+ vio_fifo_ptr_reset(vff, lump) ;
+ } ;
- if (vf->spare != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ; /* sets vf->spare = NULL */
+ if (vff->spare != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, vff->spare) ; /* sets vff->spare = NULL */
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
@@ -266,279 +571,226 @@ vio_fifo_clear(vio_fifo vf)
* Returns: room available as described
*/
extern size_t
-vio_fifo_room(vio_fifo vf)
+vio_fifo_room(vio_fifo vff)
{
- if (vf->put_ptr != NULL)
- return vf->put_end - vf->put_ptr ;
+ if (vff->set)
+ return vff->put_end - vff->put_ptr ;
else
- return vf->size ;
+ return vff->size ;
} ;
/*------------------------------------------------------------------------------
- * Allocate another lump for putting into.
+ * Need a new lump to put stuff into.
*
- * Call when (vf->put_ptr >= vf->put_end) -- asserts that they are equal.
+ * Call when (vff->put_ptr >= vff->put_end) -- asserts that they are equal.
*
- * Set the put_ptr/put_end pointers to point at the new lump.
+ * If the FIFO is, in fact, empty but with at least one lump, then does not
+ * allocate anything more, but releases all lumps but the last lump and then
+ * resets all pointers to the start of that lump.
*
- * If this is the first lump allocated, set the get_ptr/get_end pointers too.
+ * Otherwise, allocates a new lump (or reuses the spare) to the requested size,
+ * and updates all pointers as required. (Allocates to vff->size if
+ * requested size is zero.)
*
- * If have just filled the first lump on the list, update the get_end pointer
- * to reflect the fact that the out lump is now full.
+ * NB: if there is an end_mark, makes sure that it advances with the put_ptr
+ * if currently end_end == the put_ptr.
*/
-extern void
-vio_fifo_lump_new(vio_fifo vf, size_t size)
+Private void
+vio_fifo_lump_new(vio_fifo vff, size_t size)
{
vio_fifo_lump lump ;
- int first_alloc ;
+ size_t std_size ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ passert(vff->put_ptr == vff->put_end) ; /* must be end of tail lump
+ (or both NULL) */
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- passert(vf->put_ptr == vf->put_end) ; /* must be end of tail lump */
+ /* First, make sure that the get side is synchronised, which may advance
+ * various pointers, release lumps and possibly reset now empty FIFO.
+ *
+ * If synchronising the get side does not yield space, then the FIFO is
+ * either not set or it has something in it (including held stuff).
+ */
+ vio_fifo_sync_get(vff) ;
- if (vf->one)
- vf->get_end = vf->put_ptr ; /* update get_end */
+ if (vff->put_ptr < vff->put_end)
+ return ; /* Done if have created space */
- lump = ddl_tail(vf->base) ;
+ /* If we can use the spare, do so, otherwise make it to size */
+ lump = vff->spare ;
+ vff->spare = NULL ;
- first_alloc = (lump == NULL) ; /* extra initialisation needed */
+ std_size = vio_fifo_size(vff->size) ; /* normalised standard */
- if (first_alloc)
- assert(vf->put_ptr == NULL) ; /* must all be NULL together */
+ if (size <= std_size)
+ size = std_size ; /* use standard as a minimum */
else
- assert(vf->put_ptr == lump->end) ; /* must be end of tail lump */
+#if VIO_FIFO_DEBUG
+ size |= 3 ; /* most of the time a little bigger */
+#else
+ size = vio_fifo_size(size) ; /* normalise requested size */
+#endif
- size = vio_fifo_size(size) ;
-
- if ((vf->spare != NULL) && (vf->spare->size >= size))
- {
- lump = vf->spare ;
- vf->spare = NULL ;
- }
- else
+ if ((lump == NULL) || (lump->size < size))
{
- lump = XMALLOC(MTYPE_VIO_FIFO_LUMP,
- offsetof(vio_fifo_lump_t, data[size])) ;
+ /* If there was no spare, lump == NULL and XREALLOC == XMALLOC.
+ * If there was a spare that was too small, better extend that than
+ * keep a sub-standard spare.
+ */
+ lump = XREALLOC(MTYPE_VIO_FIFO_LUMP, lump,
+ offsetof(vio_fifo_lump_t, data[size])) ;
lump->size = size ;
+ lump->end = lump->data + size ;
} ;
- lump->end = lump->data + lump->size ;
-
- ddl_append(vf->base, lump, list) ;
-
- vf->one = first_alloc ;
-
- vf->put_ptr = lump->data ;
- vf->put_end = lump->end ;
-
- if (first_alloc)
- {
- vf->get_ptr = vf->put_ptr ; /* get_ptr == put_ptr => empty */
- vf->get_end = vf->put_ptr ;
- } ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-} ;
+ ddl_append(vff->base, lump, list) ;
-/*------------------------------------------------------------------------------
- * Release lump, head or tail (or both) and update pointers.
- *
- * Note that this does release the lump if it is the only lump.
- *
- * Do nothing if nothing is yet allocated.
- *
- * If releasing the only lump in the FIFO, resets all pointers to NULL.
- *
- * If releasing the head lump:
- *
- * * the lump MUST be finished with -- so vf->get_ptr must be at the end
- *
- * If releasing the only lump, the FIFO MUST be empty.
- *
- * * if the lump is the current vf->rdr_lump, the reader must be at the
- * end too -- ie it must be the same as the vf->get_ptr !
- *
- * If releasing the tail lump:
- *
- * * the lump MUST be empty
- *
- * If releasing the only lump, the FIFO MUST be empty.
- *
- * * if the lump is the current vf->rdr_lump, the reader must be at the
- * end too -- ie it must be the same as the vf->get_ptr !
- */
-static void
-vio_fifo_lump_release(vio_fifo vf, vio_fifo_lump lump)
-{
- vio_fifo_lump head ;
- vio_fifo_lump tail ;
- vio_fifo_lump free ;
- bool release_head ;
- bool release_tail ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- /* Prepare and check whether removing head or tail (or both) */
- head = ddl_head(vf->base) ;
- tail = ddl_tail(vf->base) ;
-
- release_head = (lump == head) ;
- release_tail = (lump == tail) ;
-
- assert(release_head || release_tail) ;
-
- /* Unless nothing ever allocated -- release the lump. */
- free = lump ; /* expect to free the lump */
- if (lump != NULL)
+ /* If not just allocated the first lump, set the put_ptr and normalise any
+ * end_mark or update end_lump.
+ *
+ * If is first block to be allocated, set all pointers, taking into account
+ * any end_mark and/or hold_mark.
+ */
+ if (vff->set)
{
- vio_fifo_lump keep ;
+ /* Allocated new lump on the end of a not-empty fifo.
+ *
+ * Have to watch out for cases where the get_ptr and/or end_end are
+ * equal to the put_ptr, which is about to move to the start of a new
+ * lump, so as to avoid ambiguity !
+ */
- /* Consistency checks */
- if (release_head)
+ if (vff->get_ptr == vff->put_ptr)
{
- if (release_tail)
- assert(vf->get_ptr == vf->put_ptr) ;
- else
- assert(vf->get_ptr == lump->end) ;
+ /* If the fifo is empty, the vio_fifo_sync_get() above will have
+ * spotted it. So can only get here iff there is something held in
+ * the fifo behind the get_ptr.
+ *
+ * If was as_one, is still as_one.
+ *
+ * If was not as_one then must have end_mark with end_end == put_ptr,
+ * which will be dealt with below.
+ */
+ assert(vff->hold_mark && (vff->get_ptr != vff->hold_ptr)) ;
- if (vf->rdr_lump == lump)
- assert(vf->rdr_ptr == vf->get_ptr) ;
+ vff->get_ptr = lump->data ;
+ vff->get_end = lump->data ;
+ vff->get_lump = lump ;
}
- else if (release_tail)
+ else
{
- assert(vf->put_ptr == lump->data) ;
-
- if (vf->rdr_lump == lump)
- assert(vf->rdr_ptr == vf->put_ptr) ;
+ /* If were as_one, then will no longer be, because put_ptr is about
+ * to advance to the start of the new lump.
+ */
+ vff->as_one = false ;
} ;
- /* Remove lump from FIFO and decide whether to keep as spare, or
- * which of spare and this to free.
- */
- ddl_del(vf->base, lump, list) ;
-
- keep = vf->spare ; /* expect to keep current spare */
+ if (vff->end_mark)
+ {
+ assert(!vff->as_one) ;
- if ((keep == NULL) || (keep->size < lump->size))
+ /* The end_end follows the put_ptr iff they are equal
+ *
+ * If the get_ptr also equals the put_ptr, it has already advanced.
+ */
+ if (vff->end_end == vff->put_ptr)
+ {
+ vff->end_end = lump->data ;
+ vff->end_lump = lump ;
+ } ;
+ }
+ else
{
- keep = lump ;
- free = vf->spare ;
+ /* No end_mark => end_lump simply follows the put_ptr. */
+ vff->end_lump = lump ;
} ;
- vf->spare = keep ;
-
- head = ddl_head(vf->base) ; /* changed if released head */
- tail = ddl_tail(vf->base) ; /* changed if released tail */
+ vff->put_ptr = lump->data ;
+ vff->put_end = vio_fifo_true_lump_size(lump) ;
+ }
+ else
+ {
+ /* Allocated lump for previously empty fifo -- set all pointers to the
+ * start of the lump, except for put_end.
+ */
+ vff->set = true ;
+ vio_fifo_ptr_set(vff, lump) ;
} ;
- /* Now update pointers... depending on what was released and what have
- * left.
- */
- if (head == NULL)
- {
- /* Deal with FIFO that now has no lumps or had none to start with */
- if (lump != NULL)
- assert(vf->one) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
- vio_fifo_ptr_unset(vf) ;
- }
- else
+/*------------------------------------------------------------------------------
+ * Release the given lump, provided it is neither get_lump nor end_lump.
+ *
+ * If don't have a spare lump, keep this one. Otherwise, keep larger of
+ * this and current spare.
+ */
+static void
+vio_fifo_release_lump(vio_fifo vff, vio_fifo_lump lump)
+{
+ assert(lump != NULL) ;
+ assert(lump != vff->get_lump) ;
+ assert(lump != vff->end_lump) ;
+
+ if (vff->spare != NULL)
{
- /* Have at least one lump left -- so must have had at least two ! */
- assert(!vf->one) ;
+ vio_fifo_lump free ;
- vf->one = (head == tail) ; /* update */
+ free = vff->spare ;
- if (release_head)
+ if (free->size > lump->size)
{
- /* Released the head.
- *
- * Update the vf->get_ptr and the vf->get_end.
- */
- vf->get_ptr = head->data ;
- if (vf->one)
- vf->get_end = vf->put_ptr ;
- else
- vf->get_end = head->end ;
-
- /* Update vf->rdr_ptr and vf->rdr_lump. */
- if (vf->rdr_lump == lump)
- {
- vf->rdr_lump = head ;
- vf->rdr_ptr = head->data ;
- } ;
- }
- else
- {
- /* Released the tail.
- * Update the vf->put_ptr and vf->put_end
- */
- vf->put_ptr = vf->put_end = tail->end ;
-
- /* Update vf->rdr_ptr and vf->rdr_lump. */
- if (vf->rdr_lump == lump)
- {
- vf->rdr_lump = tail ;
- vf->rdr_ptr = tail->end ;
- } ;
+ free = lump ;
+ lump = vff->spare ;
} ;
- } ;
-
- /* Finally, free any lump that is actually to be freed */
- if (free != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, free) ;
+ XFREE(MTYPE_VIO_FIFO_LUMP, free) ;
+ } ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vff->spare = lump ;
} ;
/*------------------------------------------------------------------------------
- * Re-allocate lump for putting into.
- *
- * Call when vf->put_ptr == start of last lump, and that lump is not big
- * enough !
+ * Release lumps from head up to, but not including, given lump.
*
- * There must be at least one lump.
+ * NB: must be "set" and must not attempt to release the get_lump or the
+ * end_lump.
*
- * Updates put_ptr/put_end pointers to point at the new lump.
+ * So MUST advance at least the get_lump before calling this.
*
- * Updates get_ptr/get_end pointers if required.
- *
- * Updates rdr_ptr if required.
+ * It is the caller's responsibility to update get and/or hold pointers.
*/
static void
-vio_fifo_lump_renew(vio_fifo vf, vio_fifo_lump lump, size_t size)
+vio_fifo_release_head(vio_fifo vff, vio_fifo_lump upto)
{
- bool rdr_set ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- /* FIFO may not be completely empty.
- * This must be the last lump.
- * The last lump must be empty.
- */
- assert((lump != NULL) && (lump == ddl_tail(vf->base))) ;
-
- /* Remove the last, *empty* lump, and update all pointers to suit. */
- rdr_set = (vf->rdr_lump == lump) ;
-
- vio_fifo_lump_release(vf, lump) ;
+ assert(vff->set) ;
- /* Now allocate a new lump with the required size */
- vio_fifo_lump_new(vf, size) ;
-
- /* Restore the rdr_ptr, if required */
- if (rdr_set)
+ while (upto != ddl_head(vff->base))
{
- vio_fifo_lump tail ;
+ vio_fifo_lump lump ;
+ vio_fifo_release_lump(vff, ddl_pop(&lump, vff->base, list)) ;
+ } ;
+} ;
- tail = ddl_tail(vf->base) ;
+/*------------------------------------------------------------------------------
+ * Release lumps from tail back to, but not including, given lump.
+ *
+ * NB: must be "set" and must not attempt to release the get_lump or the
+ * end_lump.
+ *
+ * It is the caller's responsibility to update end and/or put pointers.
+ */
+static void
+vio_fifo_release_tail(vio_fifo vff, vio_fifo_lump backto)
+{
+ assert(vff->set) ;
- vf->rdr_lump = tail ;
- vf->rdr_ptr = tail->data ;
+ while (backto != ddl_tail(vff->base))
+ {
+ vio_fifo_lump lump ;
+ vio_fifo_release_lump(vff, ddl_crop(&lump, vff->base, list)) ;
} ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
} ;
/*==============================================================================
@@ -546,196 +798,461 @@ vio_fifo_lump_renew(vio_fifo vf, vio_fifo_lump lump, size_t size)
*/
/*------------------------------------------------------------------------------
- * Store 'n' bytes -- allocate new lump if current is exhausted.
+ * Put 'n' bytes -- allocating as required.
*/
extern void
-vio_fifo_put(vio_fifo vf, const char* src, size_t n)
+vio_fifo_put_bytes(vio_fifo vff, const char* src, size_t n)
{
- size_t take ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
while (n > 0)
{
- if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf, vf->size) ; /* traps put_ptr > put_end */
+ size_t take ;
- take = (vf->put_end - vf->put_ptr) ;
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_lump_new(vff, 0) ; /* traps put_ptr > put_end */
+
+ take = (vff->put_end - vff->put_ptr) ;
if (take > n)
take = n ;
- memcpy(vf->put_ptr, src, take) ;
- vf->put_ptr += take ;
+ memcpy(vff->put_ptr, src, take) ;
+ vff->put_ptr += take ;
src += take ;
n -= take ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Formatted print to fifo -- cf printf()
+ * Formatted print to FIFO -- cf printf()
*
* Returns: >= 0 -- number of bytes written
* < 0 -- failed (unlikely though that is)
*/
extern int
-vio_fifo_printf(vio_fifo vf, const char* format, ...)
+vio_fifo_printf(vio_fifo vff, const char* format, ...)
{
va_list args;
int len ;
va_start (args, format);
- len = vio_fifo_vprintf(vf, format, args);
+ len = vio_fifo_vprintf(vff, format, args);
va_end (args);
return len;
} ;
/*------------------------------------------------------------------------------
- * Formatted print to fifo -- cf vprintf()
+ * Formatted print to FIFO -- cf vprintf()
+ *
+ * Does nothing if vff is NULL !
*
* Returns: >= 0 -- number of bytes written
* < 0 -- failed (unlikely though that is)
+ *
+ * NB: does not extend an existing lump in order to make things fit, but
+ * splits the result across two lumps. This ensures that at all times
+ * pointers into existing lumps are stable -- so pointer returned by
+ * vio_fifo_get_lump(), for example, cannot be upset !
*/
extern int
-vio_fifo_vprintf(vio_fifo vf, const char *format, va_list args)
+vio_fifo_vprintf(vio_fifo vff, const char *format, va_list args)
{
va_list ac ;
int len ;
int have ;
- size_t size ;
- vio_fifo_lump lump ;
+ int had ;
+ int need ;
+ char* last ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vff == NULL)
+ return 0 ;
- size = vf->size ; /* standard allocation size */
- while (1)
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ /* First the simple way.
+ *
+ * Note that vsnprintf() returns the length of what it would like to
+ * have produced, if it had the space. That length does not include
+ * the trailing '\0'. In the meantime, it has output as much as it
+ * can, with a trailing '\0'.
+ */
+ assert(vff->put_ptr <= vff->put_end) ;
+
+ have = vff->put_end - vff->put_ptr ; /* what can do in current lump
+ if any. */
+ va_copy(ac, args) ;
+ len = vsnprintf(vff->put_ptr, have, format, ac) ;
+ va_end(ac) ;
+
+ if ((len < have) || (len == 0)) /* OK, or failed ! */
{
- /* Find what space is left in the tail lump, and allocate a new,
- * empty lump if required.
- */
- if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf, size) ; /* traps put_ptr > put_end */
+ if (len > 0)
+ vff->put_ptr += len ; /* advance put_ptr as required */
- have = vf->put_end - vf->put_ptr ;
- assert(have > 0) ;
+ return len ;
+ } ;
- /* Note that vsnprintf() returns the length of what it would like to
- * have produced, if it had the space. That length does not include
- * the trailing '\0'.
- */
- va_copy(ac, args) ;
- len = vsnprintf(vf->put_ptr, have, format, ac) ;
- va_end(ac) ;
+ /* Now know that we need len + 1 bytes in all to complete the task, and
+ * that it has written have - 1 bytes to the existing lump (if any).
+ *
+ * Also, len > 0.
+ *
+ * Allocate a new lump in which we can write the entire result, even if
+ * that is a non-standard size.
+ */
+ need = len + 1 ; /* need includes the '\0' */
+ had = have ;
- if (len < have)
- {
- if (len < 0)
- break ; /* quit if failed */
+ if (had > 0)
+ vff->put_ptr += had ; /* step to the end */
+ last = vff->put_ptr ; /* point at end of lump (NULL if none) */
- vf->put_ptr += len ;
- break ; /* done */
- } ;
+ vio_fifo_lump_new(vff, need) ;/* new lump to do it all */
- /* Not able to complete the operation in the current buffer.
- *
- * If the required space (len + 1) is greater than the standard
- * allocation, then need to increase the allocation for the next lump.
- *
- * If the current lump is empty, need to renew it with a fresh lump of
- * the now known required size.
- *
- * If the current lump is not empty, need to cut the end off and then
- * allocate a fresh lump (of the standard or now known required size).
- */
- if (len >= (int)size)
- size = len + 1 ; /* need a non-standard size */
+ have = vff->put_end - vff->put_ptr ;
+ assert(have >= need) ; /* have >= 2 */
- lump = ddl_tail(vf->base) ;
+ /* We really expect to get the same result a second time ! */
+ va_copy(ac, args) ;
+ len = vsnprintf(vff->put_ptr, have, format, ac) ;
+ va_end(ac) ;
- if (vf->put_ptr == lump->data)
- /* Need to replace the last, empty, lump with another empty lump, but
- * big enough.
- */
- vio_fifo_lump_renew(vf, lump, size) ;
- else
- /* Need to cut this lump short, and allocate new lump at top of loop.
- */
- lump->end = vf->put_end = vf->put_ptr ;
+ /* Since have >= what previously said it needed, things have gone
+ * badly wrong if the new len is >= have.
+ *
+ * Also, things have gone badly wrong if new len is < what previously
+ * had, which was then not enough !
+ *
+ * Also, things have gone badly wrong if new len == 0, because previously
+ * it was > 0 !
+ */
+ if ((len >= have) || (len < had) || (len == 0))
+ return (len < 0) ? len : -1 ;
+
+ /* Move result around if required -- len >= had */
+ have = len ;
+ if (had > 0)
+ {
+ char* frag ;
+ frag = vff->put_ptr + had ; /* first character to keep */
+ *(last - 1) = *(frag - 1) ; /* replace the '\0' */
+
+ have -= had ; /* amount to keep */
+ if (have > 0)
+ memmove(vff->put_ptr, frag, have) ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ /* Advance the put_ptr past what we have in the new lump. */
+ vff->put_ptr += have ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
return len ;
} ;
+/*------------------------------------------------------------------------------
+ * Read part of file into FIFO -- assuming non-blocking file
+ *
+ * Will read up to the end of the lump which meets, or exceeds the number of
+ * bytes requested, or until would block.
+ *
+ * Returns: 0..n -- number of bytes read
+ * -1 => failed -- see errno
+ * -2 => EOF met immediately
+ *
+ * Note: will work perfectly well for a non-blocking file -- which should
+ * never return EAGAIN/EWOULDBLOCK, so will return from here with
+ * something, error or EOF.
+ */
+extern int
+vio_fifo_read_nb(vio_fifo vff, int fd, size_t request)
+{
+ size_t total ;
+
+ total = 0 ;
+
+ do
+ {
+ int got ;
+
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_lump_new(vff, 0) ; /* traps put_ptr > put_end */
+
+ got = read_nb(fd, vff->put_ptr, vff->put_end - vff->put_ptr) ;
+
+ if (got <= 0)
+ {
+ if (got == -2) /* EOF met */
+ return (total > 0) ? (int)total : got ;
+ else
+ return (got == 0) ? (int)total : got ;
+ } ;
+
+ vff->put_ptr += got ;
+ total += got ;
+
+ } while (total < request) ;
+
+ return total ;
+} ;
+
/*==============================================================================
- * Get data from the FIFO.
+ * Copy operations -- from one fifo to another.
*/
-static bool vio_fifo_get_next_lump(vio_fifo vf) ;
+/*------------------------------------------------------------------------------
+ * Copy src fifo (everything from get_ptr to end_mark or put_ptr) to dst fifo.
+ *
+ * Create a dst fifo if there isn't one.
+ *
+ * Appends to the dst fifo.
+ *
+ * Does not change the src fifo in any way.
+ */
+extern vio_fifo
+vio_fifo_copy(vio_fifo dst, vio_fifo src)
+{
+ if (dst == NULL)
+ dst = vio_fifo_init_new(dst, 0) ;
+
+ if ((src != 0) && (src->set))
+ {
+ vio_fifo_lump src_lump ;
+ char* src_ptr ;
+
+ if (src->get_ptr >= src->get_end)
+ vio_fifo_sync_get(src) ;
+
+ src_lump = src->get_lump ;
+ src_ptr = src->get_ptr ;
+
+ while (1)
+ {
+ char* src_end ;
+
+ if (src_lump != src->end_lump)
+ src_end = src_lump->end ;
+ else
+ src_end = (src->end_mark) ? src->end_end : src->put_ptr ;
+
+ vio_fifo_put_bytes(dst, src_ptr, src_end - src_ptr) ;
+
+ if (src_lump == src->end_lump)
+ break ;
+
+ src_lump = ddl_next(src_lump, list) ;
+ src_ptr = src_lump->data ;
+ } ;
+ } ;
+
+ return dst ;
+} ;
/*------------------------------------------------------------------------------
- * Get ready to read something out of the FIFO.
+ * Copy tail of src fifo (everything from end_mark to put_ptr) to dst fifo.
+ *
+ * Create a dst fifo if there isn't one.
*
- * Makes sure vf->get_end is up to date (if required) and if the FIFO is not
- * empty, makes sure vf->get_ptr points at the next byte to be read.
+ * Appends to the dst fifo.
*
- * Returns: true <=> there is something in the FIFO.
+ * Does not change the src fifo in any way.
*/
-static inline bool
-vio_fifo_get_ready(vio_fifo vf)
+extern vio_fifo
+vio_fifo_copy_tail(vio_fifo dst, vio_fifo src)
{
- assert(vf->rdr_lump == NULL) ;
+ if (dst == NULL)
+ dst = vio_fifo_init_new(dst, 0) ;
- if (vf->one)
- vf->get_end = vf->put_ptr ; /* make sure have everything */
+ if ((src != 0) && (src->end_mark) && (src->set))
+ {
+ vio_fifo_lump src_lump ;
+ char* src_ptr ;
+ vio_fifo_lump tail ;
- if (vf->get_ptr >= vf->get_end)
- if (!vio_fifo_get_next_lump(vf))
- return 0 ; /* quit now if nothing there */
+ src_lump = src->end_lump ;
+ src_ptr = src->end_end ;
+ tail = ddl_tail(src->base) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ while (1)
+ {
+ char* src_end ;
- return 1 ;
+ if (src_lump != tail)
+ src_end = src_lump->end ;
+ else
+ src_end = src->put_ptr ;
+
+ vio_fifo_put_bytes(dst, src_ptr, src_end - src_ptr) ;
+
+ if (src_lump == tail)
+ break ;
+
+ src_lump = ddl_next(src_lump, list) ;
+ src_ptr = src_lump->data ;
+ } ;
+ } ;
+
+ return dst ;
} ;
+/*==============================================================================
+ * End Mark Operations.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set end_mark at the current put position.
+ *
+ * If there was an end_mark before, move it (forward) to the current put_ptr,
+ * which keeps everything in between in the FIFO.
+ *
+ * Set the get_end to the new reality.
+ */
+extern void
+vio_fifo_set_end_mark(vio_fifo vff)
+{
+ if (vff->set)
+ {
+ vio_fifo_sync_get(vff) ; /* in case is currently empty */
+
+ vff->end_lump = ddl_tail(vff->base) ;
+ vff->end_end = vff->put_ptr ;
+
+ vff->get_end = (vff->get_lump == vff->end_lump) ? vff->end_end
+ : vff->get_lump->end ;
+ } ;
+
+ vff->as_one = false ; /* not as_one with end_mark */
+ vff->end_mark = true ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is an end mark, advance it to the put_ptr.
+ *
+ * If there was an end_mark before, move it (forward) to the current put_ptr,
+ * which keeps everything in between in the FIFO.
+ *
+ * If there was no end_mark before, do nothing.
+ *
+ * Set the get_end to the new reality.
+ */
+extern void
+vio_fifo_step_end_mark(vio_fifo vff)
+{
+ if (vff->end_mark)
+ vio_fifo_set_end_mark(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If there is an end_mark, clear it -- everything between end mark and
+ * current put_ptr is kept in the FIFO.
+ *
+ * Set the get_end to the new reality.
+ */
+extern void
+vio_fifo_clear_end_mark(vio_fifo vff)
+{
+ if (vff->end_mark)
+ {
+ vff->end_mark = false ;
+
+ if (vff->set)
+ {
+ vff->end_lump = ddl_tail(vff->base) ;
+ vff->end_end = NULL ;
+
+ vff->as_one = (vff->get_lump == vff->end_lump) ;
+ /* since now no end_mark */
+ if (!vff->as_one)
+ vff->get_end = vff->get_lump->end ;
+ /* would have been end_end */
+
+ vio_fifo_sync_get(vff) ; /* sets get_end if as_one and
+ tidies up if now empty (!) */
+ } ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move put_ptr back to the end mark, if any, and discard data.
+ *
+ * If there is an end_mark, keep it if required.
+ *
+ * If there is no end mark, do nothing.
+ */
+extern void
+vio_fifo_back_to_end_mark(vio_fifo vff, bool keep)
+{
+ if (vff->end_mark)
+ {
+ if (vff->set)
+ {
+ vio_fifo_release_tail(vff, vff->end_lump) ;
+
+ vff->put_ptr = vff->end_end ;
+ vff->put_end = vio_fifo_true_lump_size(vff->end_lump) ;
+
+ /* If retaining the existing end_mark, we retain the end_end and
+ * the current as_one (false).
+ *
+ * Otherwise...
+ */
+ if (!keep)
+ {
+ vff->end_mark = false ;
+ vff->end_end = NULL ;
+ vff->as_one = (vff->get_lump == vff->end_lump) ;
+ } ;
+
+ vio_fifo_sync_get(vff) ; /* in case now empty */
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+ }
+ else
+ vff->end_mark = keep ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Get data from the FIFO.
+ */
+
/*------------------------------------------------------------------------------
* Get upto 'n' bytes.
*
* Returns: number of bytes got -- may be zero.
*/
extern size_t
-vio_fifo_get(vio_fifo vf, void* dst, size_t n)
+vio_fifo_get_bytes(vio_fifo vff, void* dst, size_t n)
{
size_t have ;
void* dst_in ;
- if (!vio_fifo_get_ready(vf))
- return 0 ; /* quit now if nothing there */
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
dst_in = dst ;
- while (n > 0)
+ while (vio_fifo_sync_get(vff) && (n > 0))
{
- have = vf->get_end - vf->get_ptr ;
+ have = vff->get_end - vff->get_ptr ;
if (have > n)
have = n ;
- memcpy(dst, vf->get_ptr, have) ;
- vf->get_ptr += have ;
+ memcpy(dst, vff->get_ptr, have) ;
+ vff->get_ptr += have ;
dst = (char*)dst + have ;
- if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
- if (!vio_fifo_get_next_lump(vf))
- break ; /* quit if nothing more to come */
-
n -= have ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
return (char*)dst - (char*)dst_in ;
} ;
@@ -752,76 +1269,84 @@ vio_fifo_get(vio_fifo vf, void* dst, size_t n)
* Returns: 0x00..0xFF -- byte value (as an int)
* -1 => FIFO is empty.
*/
-
-extern int
-vio_fifo_get_next_byte(vio_fifo vf)
+Private int
+vio_fifo_get_next_byte(vio_fifo vff)
{
- unsigned char u ;
-
- if (!vio_fifo_get_ready(vf))
- return -1 ; /* quit now if nothing there */
-
- u = *vf->get_ptr++ ;
-
- /* As soon as reach the end want either to discard empty lump, or reset
- * the pointers.
- */
- if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
- vio_fifo_get_next_lump(vf) ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vio_fifo_sync_get(vff))
+ return (uchar)*vff->get_ptr++ ;
- return u ;
+ return -1 ;
} ;
/*------------------------------------------------------------------------------
- * Get pointer to a lump of bytes.
+ * Get pointer to as many bytes as are available in the current lump (or next
+ * lump if nothing available in the current).
*
* Returns: address of next byte to get, *p_have = number of bytes available
* or: NULL => FIFO is empty, *p_have = 0
*
- * If the FIFO is not empty, will return pointer to at least one byte.
- *
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
+ * If the FIFO is not empty and not at the end_mark, will return pointer to at
+ * least one byte. There may be more bytes to get in further lumps.
*/
extern void*
-vio_fifo_get_lump(vio_fifo vf, size_t* p_have)
+vio_fifo_get(vio_fifo vff, size_t* p_have)
{
- if (!vio_fifo_get_ready(vf))
+ if (vio_fifo_sync_get(vff))
{
- *p_have = 0 ;
- return NULL ;
+ *p_have = (vff->get_end - vff->get_ptr) ;
+ return vff->get_ptr ;
} ;
- *p_have = (vf->get_end - vf->get_ptr) ;
- return vf->get_ptr ;
+ *p_have = 0 ;
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
- * Advance FIFO to position reached.
+ * Step FIFO past bytes used.
*
- * Having done vio_fifo_get_lump(), can take any number of bytes (up to the
- * number that "have"), then call this function to advance the pointers.
+ * Can be called after a vio_fifo_get() or vio_fifo_step_get().
*
- * The "here" argument must the the address returned by vio_fifo_get_lump()
- * plus the number of bytes taken.
+ * NB: the "step" argument MUST not exceed the "have" previously returned.
*/
extern void
-vio_fifo_got_upto(vio_fifo vf, void* here)
+vio_fifo_step(vio_fifo vff, size_t step)
+{
+ vff->get_ptr += step ;
+ vio_fifo_sync_get(vff) ; /* ensure up to date with that */
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Step FIFO past bytes used, the get pointer to as many bytes as are available
+ * in the current lump (or next lump if nothing available in the current).
+ *
+ * Same as vio_fifo_step() followed by vio_fifo_get().
+ *
+ * Can be called after a vio_fifo_get() or vio_fifo_step_get().
+ *
+ * NB: the "step" argument MUST not exceed the "have" previously returned.
+ */
+extern void*
+vio_fifo_step_get(vio_fifo vff, size_t* p_have, size_t step)
{
- vf->get_ptr = here ;
+ vff->get_ptr += step ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vio_fifo_sync_get(vff))
+ {
+ *p_have = (vff->get_end - vff->get_ptr) ;
+ return vff->get_ptr ;
+ } ;
- if (vf->get_ptr >= vf->get_end)
- vio_fifo_get_next_lump(vf) ;
+ *p_have = 0 ;
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
* Write contents of FIFO -- assuming non-blocking file
*
- * Will write all of FIFO, or upto but excluding the last lump.
+ * Will write all of FIFO up to end mark or put_ptr, or upto but excluding
+ * the last lump.
*
* Returns: > 0 => blocked
* 0 => all gone (up to last lump if !all)
@@ -831,338 +1356,314 @@ vio_fifo_got_upto(vio_fifo vf, void* here)
* never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
*/
extern int
-vio_fifo_write_nb(vio_fifo vf, int fd, bool all)
+vio_fifo_write_nb(vio_fifo vff, int fd, bool all)
{
char* src ;
size_t have ;
- int done ;
- while ((src = vio_fifo_get_lump(vf, &have)) != NULL)
+ while ((src = vio_fifo_get(vff, &have)) != NULL)
{
- if (!all && vf->one)
- break ; /* don't write last lump */
+ int done ;
+
+ if ((vff->get_lump == vff->end_lump) && !all)
+ break ; /* don't write last lump */
done = write_nb(fd, src, have) ;
if (done < 0)
- return -1 ; /* failed */
+ return -1 ; /* failed */
- vio_fifo_got_upto(vf, src + done) ;
+ vio_fifo_step(vff, done) ;
if (done < (int)have)
- return 1 ; /* blocked */
+ return 1 ; /* blocked */
} ;
- return 0 ; /* all gone */
-} ;
-
-/*------------------------------------------------------------------------------
- * Get the current rdr_end value.
- *
- * Unlike get_end, do not have a field for this, but find it each time.
- */
-inline static char*
-vio_fifo_rdr_end(vio_fifo vf)
-{
- if (vf->rdr_lump == ddl_tail(vf->base))
- return vf->put_ptr ;
- else
- return vf->rdr_lump->end ;
+ return 0 ; /* all gone */
} ;
/*------------------------------------------------------------------------------
- * Get the current rdr position -- sets it up if not currently set.
- *
- * Returns: address of next byte to get, *p_have = number of bytes available
- * or: NULL => FIFO is empty, *p_have = 0
+ * Write contents of FIFO -- assuming blocking file
*
- * If the FIFO is not empty, will return pointer to at least one byte.
+ * Will write all of FIFO up to end mark or put_ptr.
*
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
+ * Returns: 0 => all gone
+ * < 0 => failed -- see errno
*
- * NB: unless returns FIFO is empty, it is a mistake to now do any "get"
- * operation other than vio_fifo_step_rdr(), until do vio_fifo_sync_rdr()
- * or vio_fifo_drop_rdr.
+ * Note: will work perfectly well for a non-blocking file -- which should
+ * never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
*/
-extern void*
-vio_fifo_get_rdr(vio_fifo vf, size_t* p_have)
+extern int
+vio_fifo_fwrite(vio_fifo vff, FILE* file)
{
- if (!vio_fifo_get_ready(vf))
- {
- *p_have = 0 ;
- return NULL ;
- } ;
+ char* src ;
+ size_t have ;
- if (vf->rdr_lump == NULL) /* set up new rdr if required */
+ while ((src = vio_fifo_get(vff, &have)) != NULL)
{
- vf->rdr_lump = ddl_head(vf->base) ;
- vf->rdr_ptr = vf->get_ptr ;
- } ;
+ size_t done ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ done = fwrite(src, have, 1, file) ;
- *p_have = vio_fifo_rdr_end(vf) - vf->rdr_ptr ;
- return vf->rdr_ptr ;
+ if (done < 1)
+ return -1 ; /* failed */
+
+ vio_fifo_step(vff, have) ;
+ } ;
+
+ return 0 ; /* all gone */
} ;
/*------------------------------------------------------------------------------
- * Step the rdr forward by the given number of bytes.
- *
- * Returns: address of next byte to get, *p_have = number of bytes available
- * or: NULL => FIFO is empty, *p_have = 0
- *
- * If the FIFO is not empty, will return pointer to at least one byte.
+ * Skip get_ptr to the current end -- which may be the current end_mark.
*
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
- *
- * NB: this does not change the get pointers, so all the data being stepped
- * over is preserved in the FIFO, until vio_fifo_sync_rdr().
- *
- * NB: the step may NOT exceed the last reported "have".
+ * Does not clear any hold_mark or end_mark.
*/
-extern void*
-vio_fifo_step_rdr(vio_fifo vf, size_t* p_have, size_t step)
+extern void
+vio_fifo_skip_to_end(vio_fifo vff)
{
- char* rdr_end ;
+ vio_fifo_sync_get(vff) ; /* ensure all straight */
- assert(vf->rdr_lump != NULL) ;
+ /* Setting the get_ptr to the start of the end_lump does the bulk
+ * of the work -- then just skip to the get_end which that sets.
+ */
+ vio_fifo_set_get_ptr(vff, vff->end_lump->data, vff->end_lump) ;
+ vff->get_ptr = vff->get_end ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vio_fifo_sync_get(vff) ; /* crunch */
+} ;
- rdr_end = vio_fifo_rdr_end(vf) ;
- vf->rdr_ptr += step ;
+/*==============================================================================
+ * Hold Mark Operations.
+ */
- if (vf->rdr_ptr >= rdr_end)
+/*------------------------------------------------------------------------------
+ * If there is a hold_mark, clear it -- discard all contents up to the
+ * current get_ptr.
+ *
+ * Set hold_mark at the current get_ptr.
+ */
+extern void
+vio_fifo_set_hold_mark(vio_fifo vff)
+{
+ if (vff->set)
{
- assert(vf->rdr_ptr == rdr_end) ;
-
- if (vf->rdr_lump != ddl_tail(vf->base))
- {
- vf->rdr_lump = ddl_next(vf->rdr_lump, list) ;
- vf->rdr_ptr = vf->rdr_lump->data ;
+ if (vff->hold_mark)
+ vio_fifo_clear_hold_mark(vff) ; /* clear existing mark & sync */
+ else
+ vio_fifo_sync_get(vff) ; /* ensure all straight */
- rdr_end = vio_fifo_rdr_end(vf) ;
- } ;
+ vff->hold_ptr = vff->get_ptr ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vff->hold_mark = true ;
- *p_have = (rdr_end - vf->rdr_ptr) ;
- return (*p_have > 0) ? vf->rdr_ptr : NULL ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Move FIFO get position to the rdr position, if any.
+ * If there is a hold_mark, clear it -- discard all contents up to the
+ * current get_ptr.
*
- * This clears the rdr position, and removes all data between the current and
- * new get positions from the FIFO.
+ * The get_ptr is synchronised.
*/
extern void
-vio_fifo_sync_rdr(vio_fifo vf)
+vio_fifo_clear_hold_mark(vio_fifo vff)
{
- vio_fifo_lump head ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- if (vf->rdr_lump == NULL)
- return ;
+ /* Make sure all is up to date, and in particular that the get_ptr
+ * is not sitting at the end of a lump when there is a following lump.
+ */
+ vio_fifo_sync_get(vff) ;
- while ((head = ddl_head(vf->base)) != vf->rdr_lump)
+ if ((vff->hold_mark) && (vff->set))
{
- vf->get_ptr = vf->get_end ; /* jump to end of lump */
- vio_fifo_lump_release(vf, head) ;
- } ;
+ /* Release everything upto but not including the current get_lump.
+ *
+ * This has no effect on the get_ptr etc. so they remain straight.
+ */
+ vio_fifo_release_head(vff, vff->get_lump) ;
- vf->get_ptr = vf->rdr_ptr ; /* jump to rdr_ptr */
+ vff->hold_ptr = NULL ;
+ } ;
- vf->rdr_lump = NULL ; /* clear the rdr */
- vf->rdr_ptr = NULL ;
+ vff->hold_mark = false ;
- if (vf->one)
- {
- if (vf->put_ptr == vf->get_ptr) /* reset pointers if FIFO empty */
- vio_fifo_ptr_reset(vf, head) ;
- else
- vf->get_end = vf->put_ptr ;
- }
- else
- vf->get_end = head->end ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Drop the rdr position (if any).
+ * If there is an hold_mark, reset get_ptr *back* to it.
*
- * This clears the rdr position leaving the get position and FIFO unchanged.
+ * Leave hold mark set or clear.
*/
extern void
-vio_fifo_drop_rdr(vio_fifo vf)
-{
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- vf->rdr_lump = NULL ;
- vf->rdr_ptr = NULL ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Move on to next lump to get stuff from.
- *
- * Advance pointers etc. so that have at least one byte available, unless
- * the FIFO is entirely empty.
- *
- * This should be called if (vf->get_ptr >= vf->get_end) -- asserts that
- * these are equal !
- *
- * NB: when there is only one block, it may be that get_end is out of date,
- * and should be advanced to the current put_ptr position.
- *
- * That is done here, but may be worth updating get_end before testing
- * against get_ptr.
- *
- * Returns: true <=> at least one byte in FIFO.
- *
- * NB: if finds that the FIFO is empty, resets the pointers to the start
- * of the last lump -- does not release the last lump.
- */
-static bool
-vio_fifo_get_next_lump(vio_fifo vf)
+vio_fifo_back_to_hold_mark(vio_fifo vff, bool mark)
{
- vio_fifo_lump head ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
- assert(vf->get_ptr == vf->get_end) ;
-
- head = ddl_head(vf->base) ; /* current lump for get */
-
- /* Deal with the simple case of one lump, first.
- *
- * To save work when putting data into the FIFO (particularly when putting
- * a byte at a time) does not keep the vf->get_end up to date (when there is
- * only one lump).
- *
- * If the FIFO is empty, reset pointers and return empty.
- */
- if (vf->one)
+ if (vff->hold_mark)
{
- assert( (head != NULL) && (head == ddl_tail(vf->base)) ) ;
-
- if (vf->get_ptr < vf->put_ptr)
+ if (vff->set)
{
- /* Had an out of date vf->get_end */
- vf->get_end = vf->put_ptr ;
+ vio_fifo_set_get_ptr(vff, vff->hold_ptr, ddl_head(vff->base)) ;
+ /* Set back to hold position */
- return true ; /* FIFO not empty */
- } ;
-
- assert(vf->get_ptr == vf->put_ptr) ;
-
- /* FIFO is empty -- reset pointers and exit */
- vio_fifo_ptr_reset(vf, head) ;
-
- return false ; /* FIFO empty */
- } ;
+ vff->end_mark = mark ; /* new state */
+ if (!mark)
+ vff->hold_ptr = NULL ; /* clear if required */
- /* Release the head and update pointers
- *
- * Deals with possibility that nothing has yet been allocated
- */
- vio_fifo_lump_release(vf, head) ;
+ vio_fifo_sync_get(vff) ; /* to be absolutely sure ! */
+ } ;
- return (vf->get_ptr < vf->get_end) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+ }
+ else if (mark)
+ vio_fifo_set_end_mark(vff) ;
} ;
/*==============================================================================
* For debug purposes -- verify the state of the given FIFO
*/
Private void
-vio_fifo_verify(vio_fifo vf)
+vio_fifo_verify(vio_fifo vff)
{
vio_fifo_lump head ;
vio_fifo_lump lump ;
vio_fifo_lump tail ;
- head = ddl_head(vf->base) ;
- tail = ddl_tail(vf->base) ;
+ head = ddl_head(vff->base) ;
+ tail = ddl_tail(vff->base) ;
- /* If nothing allocated, should all be NULL & !vf->one */
+ /* If nothing allocated, should all be NULL & !vff->set */
/* If something allocated, tail must not be NULL */
if (head == NULL)
{
if ( (tail != NULL)
- || (vf->put_ptr != NULL)
- || (vf->put_end != NULL)
- || (vf->get_ptr != NULL)
- || (vf->rdr_lump != NULL)
- || (vf->rdr_ptr != NULL)
- || (vf->one) )
+ || (vff->set)
+ || (vff->as_one)
+ || (vff->hold_ptr != NULL)
+ || (vff->get_lump != NULL)
+ || (vff->get_ptr != NULL)
+ || (vff->get_end != NULL)
+ || (vff->end_lump != NULL)
+ || (vff->end_end != NULL)
+ || (vff->put_ptr != NULL)
+ || (vff->put_end != NULL) )
zabort("nothing allocated, but not all NULL") ;
return ;
}
else
{
if (tail == NULL)
- zabort("head pointer not NULL, but tail pointer is") ;
+ zabort("head not NULL, but tail is") ;
} ;
- /* Check that all the pointers are within respective lumps
+ /* Must now be set ! */
+ if (!vff->set)
+ zabort("head not NULL, but set is false") ;
+
+ /* Make sure that the lump pointers all work
*
- * Know that put_end is always tail->end, but get_end need not be.
+ * When finished, know that head <= get_lump <= end_lump <= tail.
*/
- if ( (tail->data > vf->put_ptr)
- || (vf->put_ptr > vf->put_end)
- || (vf->put_end != tail->end) )
- zabort("put pointers outside the tail lump") ;
+ lump = head ;
+ while (lump != vff->get_lump)
+ {
+ lump = ddl_next(lump, list) ;
+ if (lump == NULL)
+ zabort("ran out of lumps looking for get_lump") ;
+ } ;
- if ( (head->data > vf->get_ptr)
- || (vf->get_ptr > vf->get_end)
- || (vf->get_end > head->end) )
- zabort("get pointers outside the head lump") ;
+ while (lump != vff->end_lump)
+ {
+ lump = ddl_next(lump, list) ;
+ if (lump == NULL)
+ zabort("ran out of lumps looking for end_lump") ;
+ } ;
- /* If head == tail, should be vf->one, etc. */
- if (head == tail)
+ while (lump != tail)
{
- if (!vf->one)
- zabort("have one lump, but !vf->one") ;
+ lump = ddl_next(lump, list) ;
+ if (lump == NULL)
+ zabort("ran out of lumps looking for tail") ;
+ } ;
- if (vf->get_end > vf->put_ptr)
- zabort("get_end is greater than put_ptr when vf->one") ;
+ /* Check that all the pointers are within respective lumps
+ *
+ * Know that put_end is always tail->end, but get_end need not be.
+ *
+ * When finished, know that:
+ *
+ * - get_lump == head if !hold_mark
+ * - end_lump == tail if !end_mark
+ * - that all pointers are within their respective lumps
+ * - all ptr are <= their respective ends
+ * - if hold_mark: hold_ptr <= get_ptr or head != get_lump
+ * - if end_mark: end_end <= put_ptr or tail != end_lump
+ */
+ if (vff->hold_mark)
+ {
+ if ( (head->data > vff->hold_ptr)
+ || (vff->hold_ptr > head->end) )
+ zabort("hold pointer outside the head lump") ;
+
+ if ((vff->get_lump == head) && (vff->hold_ptr > vff->get_ptr))
+ zabort("hold pointer greater than get pointer") ;
}
else
{
- if (vf->one)
- zabort("have two or more lumps, but vf->one is true") ;
-
- if (vf->get_end != head->end)
- zabort("get_end is not head->end when !vf->one") ;
+ if (vff->hold_ptr != NULL)
+ zabort("no hold_mark, but hold pointer not NULL") ;
+ if (vff->get_lump != head)
+ zabort("no hold_mark, but get_lump is not head") ;
} ;
- /* If have an rdr_lump -- make sure everything else is valid */
- if (vf->rdr_lump != NULL)
+ if ( (vff->get_lump->data > vff->get_ptr)
+ || (vff->get_ptr > vff->get_end)
+ || (vff->get_end > vff->get_lump->end))
+ zabort("get pointers outside the get lump") ;
+
+ if (vff->end_mark)
{
- lump = head ;
- while (lump != vf->rdr_lump)
- {
- if (lump == tail)
- zabort("rdr_lump is not part of FIFO") ;
- lump = ddl_next(lump, list) ;
- } ;
+ if ( (vff->end_lump->data > vff->end_end)
+ || (vff->end_end > vff->end_lump->end) )
+ zabort("end pointer outside the end lump") ;
- if ( (lump->data > vf->rdr_ptr)
- || (vf->rdr_ptr > lump->end) )
- zabort("rdr_ptr outside its lump") ;
+ if ((vff->end_lump == tail) && (vff->end_end > vff->put_ptr))
+ zabort("end pointer greater than put pointer") ;
+ }
+ else
+ {
+ if (vff->end_end != NULL)
+ zabort("no end_mark, but end end not NULL") ;
+ if (vff->end_lump != tail)
+ zabort("no end_mark, but end_lump is not tail") ;
+ } ;
- if ( (lump == head) && (vf->rdr_ptr < vf->get_ptr))
- zabort("rdr_ptr is less than get_ptr in first lump") ;
+ if ( (tail->data > vff->put_ptr)
+ || (vff->put_ptr > vff->put_end)
+ || (vff->put_end != tail->end) )
+ zabort("put pointers outside the tail lump") ;
- if ( (lump == tail) && (vf->rdr_ptr > vf->put_ptr))
- zabort("rdr_ptr is greater than put_ptr in last lump") ;
+ /* The as_one state & get_end
+ */
+ if (vff->get_lump != vff->end_lump)
+ {
+ if (vff->as_one)
+ zabort("get_lump != end_lump, but as_one true") ;
+ if (vff->get_end != vff->get_lump->end)
+ zabort("get_lump != end_lump, but get_end != get_lump->end") ;
}
- else
+ else if (vff->end_mark)
{
- if (vf->rdr_ptr != NULL)
- zabort("rdr_ptr not NULL when rdr_lump is") ;
+ if (vff->as_one)
+ zabort("end_mark true, but as_one also true") ;
+ if (vff->get_end != vff->end_end)
+ zabort("get_lump == end_lump and end_mark, but get_end != end_end") ;
}
+ else
+ {
+ if (!vff->as_one)
+ zabort("get_lump == end_lump and !end_mark, but as_one not true") ;
+ if (vff->get_end > vff->put_ptr)
+ zabort("is as_one, but get_end > put_ptr") ;
+ } ;
} ;
diff --git a/lib/vio_fifo.h b/lib/vio_fifo.h
index 08af4590..1b7b05d3 100644
--- a/lib/vio_fifo.h
+++ b/lib/vio_fifo.h
@@ -22,37 +22,35 @@
#ifndef _ZEBRA_VIO_FIFO_H
#define _ZEBRA_VIO_FIFO_H
-#include "zebra.h"
#include "misc.h"
+#include "vargs.h"
+#include <stdio.h>
#include "list_util.h"
-#include "zassert.h"
-
-/* GCC have printf type attribute check. */
-#ifdef __GNUC__
-#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
-#else
-#define PRINTF_ATTRIBUTE(a,b)
-#endif /* __GNUC__ */
/*==============================================================================
* VTY I/O FIFO -- buffering of arbitrary amounts of I/O.
*/
-#ifdef NDEBUG
-# define VIO_FIFO_DEBUG 0 /* NDEBUG override */
+#ifdef VIO_FIFO_DEBUG /* Can be forced from outside */
+# if VIO_FIFO_DEBUG
+# define VIO_FIFO_DEBUG 1 /* Force 1 or 0 */
+#else
+# define VIO_FIFO_DEBUG 0
+# endif
+#else
+# ifdef QDEBUG
+# define VIO_FIFO_DEBUG 1 /* Follow QDEBUG */
#else
-# ifndef VIO_FIFO_DEBUG
-# define VIO_FIFO_DEBUG 1 /* Set to 1 to turn on debug checks */
+# define VIO_FIFO_DEBUG 0
# endif
#endif
+enum { vio_fifo_debug = VIO_FIFO_DEBUG } ;
+
/*==============================================================================
* Data Structures
*/
-typedef struct vio_fifo vio_fifo_t ;
-typedef struct vio_fifo* vio_fifo ;
-
typedef struct vio_fifo_lump vio_fifo_lump_t ;
typedef struct vio_fifo_lump* vio_fifo_lump ;
@@ -60,22 +58,46 @@ struct vio_fifo
{
struct dl_base_pair(vio_fifo_lump) base ;
- bool one ;
+ bool set ; /* have at least one lump */
- char* put_ptr ;
- char* put_end ;
+ bool as_one ; /* get_lump == tail && !end_mark
+ => get_end may not be up to date */
+ bool hold_mark ; /* hold stuff while getting */
+ bool end_mark ; /* do not get beyond end */
+
+ char* hold_ptr ; /* implicitly in the head lump */
+ /* used only if "hold_mark" */
+
+ vio_fifo_lump get_lump ; /* head lump unless "hold_mark" */
char* get_ptr ;
char* get_end ;
- vio_fifo_lump rdr_lump ;
- char* rdr_ptr ;
+ vio_fifo_lump end_lump ; /* tail lump unless "end_mark" */
+ char* end_end ; /* used only if "end_mark" */
+
+ char* put_ptr ; /* implicitly in the tail lump */
+ char* put_end ;
size_t size ;
vio_fifo_lump spare ;
} ;
+typedef struct vio_fifo vio_fifo_t[1] ; /* embedded */
+typedef struct vio_fifo* vio_fifo ;
+
+/* Setting a FIFO object to all zeros is enough to initialise it to an
+ * empty FIFO (with default lump sizes).
+ */
+enum
+{
+ VIO_FIFO_INIT_ALL_ZEROS = true,
+ VIO_FIFO_DEFAULT_LUMP_SIZE = 4 * 1024
+} ;
+
+#define VIO_FIFO_INIT_EMPTY { 0 }
+
struct vio_fifo_lump
{
struct dl_list_pair(vio_fifo_lump) list ;
@@ -89,76 +111,114 @@ struct vio_fifo_lump
* Functions
*/
-extern vio_fifo vio_fifo_init_new(vio_fifo vf, size_t size) ;
-extern vio_fifo vio_fifo_reset(vio_fifo vf, int free_structure) ;
+extern vio_fifo vio_fifo_init_new(vio_fifo vff, size_t size) ;
+extern vio_fifo vio_fifo_reset(vio_fifo vff, free_keep_b free_structure) ;
-#define vio_fifo_reset_keep(vf) vio_fifo_reset(vf, 0)
-#define vio_fifo_reset_free(vf) vio_fifo_reset(vf, 1)
+extern void vio_fifo_clear(vio_fifo vff, bool clear_marks) ;
+Inline bool vio_fifo_empty(vio_fifo vff) ;
+extern size_t vio_fifo_room(vio_fifo vff) ;
-extern void vio_fifo_clear(vio_fifo vf) ;
-Inline bool vio_fifo_empty(vio_fifo vf) ;
-extern size_t vio_fifo_room(vio_fifo vf) ;
+extern void vio_fifo_put_bytes(vio_fifo vff, const char* src, size_t n) ;
+Inline void vio_fifo_put_byte(vio_fifo vff, char b) ;
-extern void vio_fifo_put(vio_fifo vf, const char* src, size_t n) ;
-Inline void vio_fifo_put_byte(vio_fifo vf, char b) ;
-
-extern int vio_fifo_printf(vio_fifo vf, const char* format, ...)
+extern int vio_fifo_printf(vio_fifo vff, const char* format, ...)
PRINTF_ATTRIBUTE(2, 3) ;
-extern int vio_fifo_vprintf(vio_fifo vf, const char *format, va_list args) ;
-
-extern size_t vio_fifo_get(vio_fifo vf, void* dst, size_t n) ;
-Inline int vio_fifo_get_byte(vio_fifo vf) ;
-extern void* vio_fifo_get_lump(vio_fifo vf, size_t* have) ;
-extern void vio_fifo_got_upto(vio_fifo vf, void* here) ;
-
-Inline bool vio_fifo_full_lump(vio_fifo vf) ;
-extern int vio_fifo_write_nb(vio_fifo vf, int fd, bool all) ;
-
-extern void* vio_fifo_get_rdr(vio_fifo vf, size_t* have) ;
-extern void* vio_fifo_step_rdr(vio_fifo vf, size_t* have, size_t step) ;
-extern void vio_fifo_sync_rdr(vio_fifo vf) ;
-extern void vio_fifo_drop_rdr(vio_fifo vf) ;
-
-Private void vio_fifo_lump_new(vio_fifo vf, size_t size) ;
-Private int vio_fifo_get_next_byte(vio_fifo vf) ;
+extern int vio_fifo_vprintf(vio_fifo vff, const char *format, va_list args) ;
+extern int vio_fifo_read_nb(vio_fifo vff, int fd, size_t request) ;
+
+extern size_t vio_fifo_get_bytes(vio_fifo vff, void* dst, size_t n) ;
+Inline int vio_fifo_get_byte(vio_fifo vff) ;
+extern void* vio_fifo_get(vio_fifo vff, size_t* have) ;
+extern void vio_fifo_step(vio_fifo vff, size_t step) ;
+extern void* vio_fifo_step_get(vio_fifo vff, size_t* p_have, size_t step) ;
+
+extern vio_fifo vio_fifo_copy(vio_fifo dst, vio_fifo src) ;
+extern vio_fifo vio_fifo_copy_tail(vio_fifo dst, vio_fifo src) ;
+
+Inline bool vio_fifo_full_lump(vio_fifo vff) ;
+extern int vio_fifo_write_nb(vio_fifo vff, int fd, bool all) ;
+extern int vio_fifo_fwrite(vio_fifo vff, FILE* file) ;
+
+extern void vio_fifo_skip_to_end(vio_fifo vff) ;
+extern void vio_fifo_set_end_mark(vio_fifo vff) ;
+extern void vio_fifo_step_end_mark(vio_fifo vff) ;
+extern void vio_fifo_clear_end_mark(vio_fifo vff) ;
+extern void vio_fifo_back_to_end_mark(vio_fifo vff, bool keep) ;
+extern void vio_fifo_set_hold_mark(vio_fifo vff) ;
+extern void vio_fifo_clear_hold_mark(vio_fifo vff) ;
+extern void vio_fifo_back_to_hold_mark(vio_fifo vff, bool keep) ;
/*==============================================================================
* Debug -- verification function
*/
-Private void vio_fifo_verify(vio_fifo vf) ;
+Private void vio_fifo_verify(vio_fifo vff) ;
#if VIO_FIFO_DEBUG
-# define VIO_FIFO_DEBUG_VERIFY(vf) vio_fifo_verify(vf)
+# define VIO_FIFO_DEBUG_VERIFY(vff) vio_fifo_verify(vff)
#else
-# define VIO_FIFO_DEBUG_VERIFY(vf)
+# define VIO_FIFO_DEBUG_VERIFY(vff)
#endif
/*==============================================================================
* Inline Functions
*/
+Private void vio_fifo_lump_new(vio_fifo vff, size_t size) ;
+Private int vio_fifo_get_next_byte(vio_fifo vff) ;
+Private bool vio_fifo_do_empty(vio_fifo vff) ;
+
/*------------------------------------------------------------------------------
- * Returns true <=> FIFO is empty
+ * Returns true <=> FIFO is empty -- at least: get_ptr == end_end (if any)
+ * or: get_ptr == put_ptr.
*/
Inline bool
-vio_fifo_empty(vio_fifo vf)
+vio_fifo_empty(vio_fifo vff)
{
- return (vf->get_ptr == vf->put_ptr) ;
-}
+ /* if vff is NULL, treat as empty !
+ * if !set, then all pointers should be NULL (so get_ptr == put_ptr)
+ * if !end_mark, then vff->end_end will be NULL (so get_ptr != end_end,
+ * unless !set)
+ * Is definitely empty if any of the following is true.
+ */
+ if ((vff == NULL) || (vff->get_ptr == vff->put_ptr)
+ || (vff->get_ptr == vff->end_end))
+ return true ;
+
+ /* If get_ptr < get_end is NOT empty */
+ if (vff->get_ptr < vff->get_end)
+ return false ;
+
+ /* See if can advance get_ptr, and if so whether is then empty. */
+ return vio_fifo_do_empty(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Returns true <=> FIFO is empty beyond end_end (if any).
+ */
+Inline bool
+vio_fifo_empty_tail(vio_fifo vff)
+{
+ /* if vff is NULL, treat as empty !
+ * if !set, then all pointers should be NULL (so end_end == put_ptr)
+ * if !end_mark, then tail is empty !
+ * else tail is empty iff end_end == put_ptr.
+ */
+ return (vff == NULL) || !vff->end_mark || (vff->end_end == vff->put_ptr) ;
+} ;
/*------------------------------------------------------------------------------
* Put one byte to the FIFO
*/
Inline void
-vio_fifo_put_byte(vio_fifo vf, char b)
+vio_fifo_put_byte(vio_fifo vff, char b)
{
- if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf, vf->size) ; /* traps put_ptr > put_end */
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_lump_new(vff, 0) ; /* traps put_ptr > put_end */
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- *vf->put_ptr++ = b ;
+ *vff->put_ptr++ = b ;
} ;
/*------------------------------------------------------------------------------
@@ -168,14 +228,12 @@ vio_fifo_put_byte(vio_fifo vf, char b)
* -1 => FIFO is empty.
*/
Inline int
-vio_fifo_get_byte(vio_fifo vf)
+vio_fifo_get_byte(vio_fifo vff)
{
- if (vf->get_end <= (vf->get_ptr + 1))
- return vio_fifo_get_next_byte(vf) ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vff->get_ptr < vff->get_end)
+ return (uchar)*vff->get_ptr++ ;
- return (unsigned char)*vf->get_ptr++ ;
+ return vio_fifo_get_next_byte(vff) ;
} ;
/*------------------------------------------------------------------------------
@@ -188,9 +246,9 @@ vio_fifo_get_byte(vio_fifo vf)
* (excluding the last lump if it happens to be full)
*/
Inline bool
-vio_fifo_full_lump(vio_fifo vf)
+vio_fifo_full_lump(vio_fifo vff)
{
- return (!vf->one && (vf->put_ptr != NULL)) ;
+ return (ddl_head(vff->base) != ddl_tail(vff->base)) ;
} ;
#endif /* _ZEBRA_VIO_FIFO_H */
diff --git a/lib/vio_lines.c b/lib/vio_lines.c
index 2ac874d1..a9268fd5 100644
--- a/lib/vio_lines.c
+++ b/lib/vio_lines.c
@@ -119,7 +119,7 @@ vio_lc_init_new(vio_line_control lc, int width, int height)
* Returns: address of vio_line_control (if any) -- NULL if structure released
*/
extern vio_line_control
-vio_lc_reset(vio_line_control lc, bool free_structure)
+vio_lc_reset(vio_line_control lc, free_keep_b free_structure)
{
if (lc != NULL)
{
diff --git a/lib/vio_lines.h b/lib/vio_lines.h
index 7097fe9c..dd5545cc 100644
--- a/lib/vio_lines.h
+++ b/lib/vio_lines.h
@@ -22,7 +22,6 @@
#ifndef _ZEBRA_VIO_LINES_H
#define _ZEBRA_VIO_LINES_H
-#include "zebra.h"
#include "misc.h"
#include "qiovec.h"
@@ -35,9 +34,6 @@
*
* NB: a completely zero structure is a valid, clear vio_line_control.
*/
-
-typedef struct vio_line_control* vio_line_control ;
-typedef struct vio_line_control vio_line_control_t ;
struct vio_line_control
{
unsigned width ; /* console width -- 0 => HUGE */
@@ -54,15 +50,21 @@ struct vio_line_control
bool writing ; /* write started, but not completed */
} ;
+typedef struct vio_line_control* vio_line_control ;
+typedef struct vio_line_control vio_line_control_t[1] ;
+
+enum
+{
+ VIO_LINE_CONTROL_INIT_ALL_ZEROS = true
+} ;
+
/*==============================================================================
* Functions
*/
extern vio_line_control vio_lc_init_new(vio_line_control lc, int width,
int height) ;
-extern vio_line_control vio_lc_reset(vio_line_control lc, bool free_structure) ;
-
-#define vio_lc_reset_keep(lc) vio_lc_reset(lc, 0)
-#define vio_lc_reset_free(lc) vio_lc_reset(lc, 1)
+extern vio_line_control vio_lc_reset(vio_line_control lc,
+ free_keep_b free_structure) ;
Inline bool vio_lc_empty(vio_line_control lc) ;
extern void vio_lc_clear(vio_line_control lc) ;
diff --git a/lib/vty.c b/lib/vty.c
index 3c1e62b7..da94b992 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -1,4 +1,4 @@
-/* VTY top level
+/* VTY external interface
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
* Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
@@ -21,36 +21,74 @@
* 02111-1307, USA.
*/
-#include "zebra.h"
+#include "zconfig.h"
#include "misc.h"
#include "lib/version.h"
-#include "vty_io.h"
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
#include "vty.h"
-#include "uty.h"
+#include "vty_local.h"
+#include "vty_io.h"
+#include "vty_command.h"
#include "vty_cli.h"
+#include "vio_fifo.h"
#include "list_util.h"
#include "command.h"
-#include "command_queue.h"
+#include "command_local.h"
#include "command_execute.h"
+#include "command_parse.h"
#include "memory.h"
#include "log.h"
#include "mqueue.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * The vty family comprises:
+ *
+ * vty -- level visible from outside the vty/command/log family
+ * and within those families.
+ *
+ * vty_common.h -- definitions ...
+ * vty_local.h
+ *
+ * vty_io -- top level of the vio handling
+ *
+ * vty_command -- functions called by the command family
+ * vty_log -- functions called by the log family
+ *
+ * vty_cli -- terminal command line handling
+ * vty_io_term -- terminal (telnet) I/O
+ * vty_io_vsh -- vtysh I/O
+ * vty_io_file -- file I/O
+ * vty_io_shell -- system shell I/O
+ *
+ * vty_io_basic -- common low level I/O handling
+ * encapsulates the differences between qpselect and legacy
+ * thread/select worlds.
+ *
+ * vio_lines -- for terminal: handles width, CRLF, line counting etc.
+ * vio_fifo --
+ * qiovec
+ *
+ */
+
/*==============================================================================
- * Variables etc. (see uty.h)
+ * Variables etc. (see vty_local.h)
*/
/* The mutex and related debug counters */
qpt_mutex_t vty_mutex ;
-#if VTY_DEBUG
-
int vty_lock_count = 0 ;
-int vty_assert_fail = 0 ;
+#if VTY_DEBUG
+int vty_assert_fail = 0 ;
#endif
/* For thread handling -- initialised in vty_init */
@@ -59,7 +97,14 @@ struct thread_master* vty_master = NULL ;
/* In the qpthreads world, have nexus for the CLI and one for the Routeing
* Engine. Some commands are processed directly in the CLI, most have to
* be sent to the Routeing Engine.
+ *
+ * If not in the qpthreads world, vty_cli_nexus == vty_cmd_nexus == NULL.
+ *
+ * If in the qpthreads world these vty_cli_nexus == vty_cmd_nexus if not
+ * actually running pthreaded.
*/
+bool vty_nexus ; /* true <=> in the qpthreads world */
+
qpn_nexus vty_cli_nexus = NULL ;
qpn_nexus vty_cmd_nexus = NULL ;
@@ -72,28 +117,6 @@ vty_io vio_monitors_base = NULL ;
/* List of all vty which are on death watch */
vty_io vio_death_watch = NULL ;
-/* Vty timeout value -- see "exec timeout" command */
-unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
-
-/* Vty access-class command */
-char *vty_accesslist_name = NULL;
-
-/* Vty access-class for IPv6. */
-char *vty_ipv6_accesslist_name = NULL;
-
-/* Current directory -- initialised in vty_init() */
-static char *vty_cwd = NULL;
-
-/* Configure lock -- only one vty may be in CONFIG_NODE or above ! */
-bool vty_config = 0 ;
-
-/* Login password check override. */
-bool no_password_check = 0;
-
-/* Restrict unauthenticated logins? */
-const bool restricted_mode_default = 0 ;
- bool restricted_mode = 0 ;
-
/*------------------------------------------------------------------------------
* VTYSH stuff
*/
@@ -107,9 +130,10 @@ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ;
static void uty_reset (bool final, const char* why) ;
static void uty_init_commands (void) ;
static void vty_save_cwd (void) ;
-static bool vty_terminal (struct vty *);
-static bool vty_shell_server (struct vty *);
-static bool vty_shell_client (struct vty *);
+
+//static bool vty_terminal (struct vty *);
+//static bool vty_shell_server (struct vty *);
+//static bool vty_shell_client (struct vty *);
/*------------------------------------------------------------------------------
* Tracking the initialisation state.
@@ -143,8 +167,8 @@ static enum vty_init_state vty_init_state ;
extern void
vty_init (struct thread_master *master_thread)
{
- VTY_LOCK() ; /* Does nothing if !qpthreads_enabled */
VTY_ASSERT_CLI_THREAD() ; /* True if !qpthreads_enabled */
+ VTY_LOCK() ; /* Does nothing if !qpthreads_enabled */
assert(vty_init_state == vty_init_pending) ;
@@ -156,7 +180,8 @@ vty_init (struct thread_master *master_thread)
vio_monitors_base = NULL ;
vio_death_watch = NULL ;
- vty_cli_nexus = NULL ; /* not running qnexus-wise */
+ vty_nexus = false ; /* not running qnexus-wise */
+ vty_cli_nexus = NULL ;
vty_cmd_nexus = NULL ;
uty_watch_dog_init() ; /* empty watch dog */
@@ -190,6 +215,7 @@ vty_init_r (qpn_nexus cli, qpn_nexus cmd)
{
assert(vty_init_state == vty_init_1st_stage) ;
+ vty_nexus = true ;
vty_cli_nexus = cli ;
vty_cmd_nexus = cmd ;
@@ -214,7 +240,7 @@ vty_init_vtysh (void)
/*------------------------------------------------------------------------------
* Start the VTY going.
*
- * This starts the listeners for VTY_TERM and VTY_SHELL_SERV.
+ * This starts the listeners for VTY_TERMINAL and VTY_SHELL_SERVER.
*
* Also starts the watch dog.
*
@@ -222,14 +248,12 @@ vty_init_vtysh (void)
* 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 void
vty_start(const char *addr, unsigned short port, const char *path)
{
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
assert( (vty_init_state == vty_init_1st_stage)
|| (vty_init_state == vty_init_2nd_stage) ) ;
@@ -254,10 +278,11 @@ vty_reset()
/*------------------------------------------------------------------------------
* Reset all VTY status
*
- * This is done in response to SIGHUP -- and runs in the CLI thread.
+ * This is done in response to SIGHUP/SIGINT/SIGTERM -- and runs in the
+ * CLI thread (if there is one).
*
- * Half closes all VTY, leaving the death watch to tidy up once all output
- * and any command in progress have completed.
+ * Closes all VTY, leaving the death watch to tidy up once all output and any
+ * command in progress have completed.
*
* Closes all listening sockets.
*
@@ -268,21 +293,24 @@ vty_reset()
extern void
vty_reset_because(const char* why)
{
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
- assert(vty_init_state == vty_init_started) ;
+ if (vty_init_state != vty_init_reset)
+ {
+ assert(vty_init_state == vty_init_started) ;
- uty_reset(0, why) ; /* not final ! */
+ uty_reset(false, why) ; /* not final ! */
- vty_init_state = vty_init_reset ;
+ 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.
+ * This starts the listeners for VTY_TERMINAL and VTY_SHELL_SERVER, again.
*
* NB: may be called once, and once only, *after* a vty_reset().
*
@@ -296,7 +324,7 @@ struct vty_restart_args
} ;
MQB_ARGS_SIZE_OK(vty_restart_args) ;
-static void uty_restart_action(mqueue_block mqb, mqb_flag_t flag) ;
+static void vty_restart_action(mqueue_block mqb, mqb_flag_t flag) ;
static void uty_restart(const char *addr, unsigned short port,
const char *path) ;
extern void
@@ -308,14 +336,14 @@ vty_restart(const char *addr, unsigned short port, const char *path)
*
* Otherwise, construct and dispatch message to do a uty_restart.
*/
- if (!vty_cli_nexus)
+ if (!vty_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) ;
+ mqb = mqb_init_new(NULL, vty_restart_action, vty_cli_nexus) ;
args = mqb_get_args(mqb) ;
if (addr != NULL)
@@ -330,7 +358,7 @@ vty_restart(const char *addr, unsigned short port, const char *path)
else
args->path = NULL ;
- mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
+ mqueue_enqueue(vty_cli_nexus->queue, mqb, mqb_priority) ;
} ;
VTY_UNLOCK() ;
@@ -338,7 +366,7 @@ vty_restart(const char *addr, unsigned short port, const char *path)
/* Deal with the uty_restart message */
static void
-uty_restart_action(mqueue_block mqb, mqb_flag_t flag)
+vty_restart_action(mqueue_block mqb, mqb_flag_t flag)
{
struct vty_restart_args* args ;
args = mqb_get_args(mqb) ;
@@ -388,17 +416,18 @@ uty_restart(const char *addr, unsigned short port, const char *path)
extern void
vty_terminate (void)
{
+ VTY_ASSERT_CLI_THREAD() ;
+
if ( (vty_init_state == vty_init_pending)
|| (vty_init_state == vty_init_terminated) )
- return ; /* nothing to do ! */
+ return ; /* nothing to do ! */
VTY_LOCK() ;
- VTY_ASSERT_CLI_THREAD() ;
assert( (vty_init_state > vty_init_pending)
&& (vty_init_state < vty_init_terminated) ) ;
- uty_reset(1, "Shut down") ; /* final reset */
+ uty_reset(true, "Shut down") ; /* final reset */
VTY_UNLOCK() ;
@@ -408,12 +437,14 @@ vty_terminate (void)
}
/*------------------------------------------------------------------------------
- * Reset -- final or for SIGHUP
+ * Reset -- final curtain or for SIGHUP
*
* Closes listeners.
*
- * Closes (final) or half closes (SIGHUP) all VTY, and revokes any outstanding
- * commands.
+ * Revokes any outstanding commands and close (SIGHUP) or close_final
+ * (curtains) all VTY.
+ *
+ *
*
* Resets the vty timeout and access lists.
*
@@ -440,34 +471,22 @@ uty_reset (bool curtains, const char* why)
vio = next ;
next = sdl_next(vio, vio_list) ;
- if (uty_is_terminal(vio->vty))
- cq_revoke(vio->vty) ;
-
- if (curtains)
- uty_close_final(vio, why) ;
- else
- uty_close(vio, why) ;
+ uty_close(vio, curtains, qs_set(NULL, why)) ;
} ;
- vty_timeout_val = VTY_TIMEOUT_DEFAULT;
-
- if (vty_accesslist_name)
- {
- XFREE(MTYPE_VTY, vty_accesslist_name);
- vty_accesslist_name = NULL;
- }
+ host.vty_timeout_val = VTY_TIMEOUT_DEFAULT;
- if (vty_ipv6_accesslist_name)
- {
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
- vty_ipv6_accesslist_name = NULL;
- }
+ XFREE(MTYPE_HOST, host.vty_accesslist_name) ;
+ /* sets host.vty_accesslist_name = NULL */
- if (curtains && vty_cwd)
- XFREE (MTYPE_TMP, vty_cwd);
+ XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name);
+ /* sets host.vty_ipv6_accesslist_name = NULL */
if (curtains)
- uty_watch_dog_stop() ; /* and final death watch run */
+ {
+ XFREE (MTYPE_HOST, host.vty_cwd);
+ uty_watch_dog_stop() ; /* and final death watch run */
+ } ;
} ;
/*==============================================================================
@@ -481,15 +500,17 @@ uty_reset (bool curtains, const char* why)
/*------------------------------------------------------------------------------
* Create a new VTY of the given type
*
- * The type may NOT be: VTY_TERM or VTY_SHELL_SERV
+ * The type may NOT be: VTY_TERMINAL or VTY_SHELL_SERVER
+ *
+ * Appears only to be used by vtysh !! TODO ????
*/
-extern struct vty *
-vty_open(enum vty_type type)
+extern vty
+vty_open(vty_type_t type)
{
struct vty* vty ;
VTY_LOCK() ;
- vty = uty_new(type, -1) ; /* fails for VTY_TERM or VTY_SHELL_SERV */
+ vty = uty_new(type) ;
VTY_UNLOCK() ;
return vty ;
@@ -498,12 +519,16 @@ vty_open(enum vty_type type)
/*------------------------------------------------------------------------------
* Close the given VTY
*/
-extern void
-vty_close (struct vty *vty)
+extern bool
+vty_close(vty vty, bool final, qstring reason)
{
+ bool closed ;
+
VTY_LOCK() ;
- uty_close(vty->vio) ;
+ closed = uty_close(vty->vio, final, reason) ;
VTY_UNLOCK() ;
+
+ return closed ;
}
/*==============================================================================
@@ -517,59 +542,22 @@ vty_close (struct vty *vty)
/*------------------------------------------------------------------------------
* VTY output -- cf fprintf !
*
- * This is for command output, which may be suppressed
- *
- * Returns: >= 0 => OK
- * < 0 => failed (see errno)
- */
-extern int
-vty_out (struct vty *vty, const char *format, ...)
-{
- int ret ;
- va_list args ;
-
- VTY_LOCK() ;
-
- if (vty->output_enabled)
- {
- va_start (args, format) ;
- ret = uty_vprintf(vty, format, args) ;
- va_end (args) ;
- }
- else
- ret = 0 ;
-
- VTY_UNLOCK() ;
- return ret ;
-}
-
-/*------------------------------------------------------------------------------
- * VTY output error message -- cf fprintf !
- *
- * If command has not yet been reflected, do that first.
+ * This is for command output, which may later be suppressed
*
* Returns: >= 0 => OK
* < 0 => failed (see errno)
*/
extern int
-vty_out_error (struct vty *vty, const char *format, ...)
+vty_out(struct vty *vty, const char *format, ...)
{
int ret ;
va_list args ;
VTY_LOCK() ;
- if (!vty->reflected)
- ret = uty_reflect(vty) ;
- else
- ret = 0 ;
-
- if (ret == 0)
- {
- va_start (args, format) ;
- ret = uty_vprintf(vty, format, args) ;
- va_end (args) ;
- } ;
+ va_start (args, format) ;
+ ret = vio_fifo_vprintf(vty->vio->obuf, format, args) ;
+ va_end (args) ;
VTY_UNLOCK() ;
return ret ;
@@ -631,7 +619,7 @@ vty_hello (struct vty *vty)
VTY_LOCK() ;
#ifdef QDEBUG
- uty_out (vty, "%s\n", debug_banner);
+ vty_out (vty, "%s\n", debug_banner);
#endif
if (host.motdfile)
{
@@ -648,15 +636,15 @@ vty_hello (struct vty *vty)
for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
s--);
*s = '\0';
- uty_output (vty, "%s\n", buf);
+ vty_out(vty, "%s\n", buf);
}
fclose (f);
}
else
- uty_output (vty, "MOTD file %s not found\n", host.motdfile);
+ vty_out(vty, "MOTD file %s not found\n", host.motdfile);
}
else if (host.motd)
- uty_output (vty, "%s", host.motd);
+ vty_out(vty, "%s", host.motd);
VTY_UNLOCK() ;
}
@@ -665,7 +653,7 @@ vty_hello (struct vty *vty)
* Clear the contents of the command output FIFO etc.
*/
extern void
-vty_out_clear(struct vty* vty)
+vty_out_clear(vty vty)
{
VTY_LOCK() ;
uty_out_clear(vty->vio) ;
@@ -673,429 +661,6 @@ vty_out_clear(struct vty* vty)
} ;
/*==============================================================================
- * Command Execution
- */
-
-/*------------------------------------------------------------------------------
- * 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: command return code
- */
-extern enum cmd_return_code
-uty_command(struct vty *vty)
-{
- enum cmd_return_code ret;
-
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- assert(uty_is_terminal(vty)) ;
-
- /* 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;
-
- GETRUSAGE(&before);
-#endif /* CONSUMED_TIME_CHECK */
-
- 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, vty->buf) ;
-#endif /* CONSUMED_TIME_CHECK */
- } ;
-
- /* Deal with the return code */
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- vty_out_error(vty, "%% Ambiguous command.\n");
- break;
-
- case CMD_ERR_NO_MATCH:
- vty_out_error(vty, "%% Unknown command.\n") ;
- break;
-
- case CMD_ERR_INCOMPLETE:
- vty_out_error(vty, "%% Command incomplete.\n");
- break;
-
- default:
- break ;
- } ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Authentication of vty
- *
- * During AUTH_NODE and AUTH_ENABLE_NODE, when a command line is dispatched by
- * any means this function is called.
- *
- * Note that if the AUTH_NODE password fails too many times, the terminal is
- * closed.
- *
- * Returns: command return code
- */
-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 ?
- *
- * In fact, all the exotic command terminators simply discard any input
- * and return.
- */
- switch (cli_do)
- {
- case cli_do_nothing:
- case cli_do_ctrl_c:
- case cli_do_ctrl_z:
- 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;
- else
- passwd = host.password;
- if (host.advanced)
- next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
- else
- next_node = VIEW_NODE;
- break;
-
- case AUTH_ENABLE_NODE:
- if (host.encrypt)
- passwd = host.enable_encrypt;
- else
- passwd = host.enable;
- next_node = ENABLE_NODE;
- break;
-
- default:
- zabort("unknown node type") ;
- }
-
- if (passwd)
- {
- if (host.encrypt)
- fail = strcmp (crypt(buf, passwd), passwd);
- else
- fail = strcmp (buf, passwd);
- }
- else
- fail = 1;
-
- ret = CMD_SUCCESS ;
-
- if (! fail)
- {
- vio->fail = 0;
- vty->node = next_node; /* Success ! */
- }
- else
- {
- vio->fail++;
- if (vio->fail >= 3)
- {
- if (vty->node == AUTH_NODE)
- {
- ret = uty_cmd_close(vty, "Bad passwords, too many failures!") ;
- }
- else
- {
- /* AUTH_ENABLE_NODE */
- vio->fail = 0;
- vty_out_error(vty,
- "%% Bad enable passwords, too many failures!\n") ;
- vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
-
- ret = CMD_WARNING ;
- }
- }
- }
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Command line "exit" command -- aka "quit"
- *
- * Falls back one NODE level.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-vty_cmd_exit(struct vty* vty)
-{
- enum cmd_return_code ret ;
-
- VTY_LOCK() ;
-
- ret = CMD_SUCCESS ;
- switch (vty->node)
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- if (uty_shell_client(vty))
- exit (0);
- else
- ret = uty_cmd_close(vty, "Exit") ;
- break;
- case CONFIG_NODE:
- uty_config_unlock (vty, ENABLE_NODE);
- break;
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case BGP_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case MASC_NODE:
- case RMAP_NODE:
- case VTY_NODE:
- vty->node = CONFIG_NODE ;
- break;
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- vty->node = BGP_NODE ;
- break;
- case KEYCHAIN_KEY_NODE:
- vty->node = KEYCHAIN_NODE ;
- break;
- default:
- break;
- }
-
- VTY_UNLOCK() ;
- return ret ;
-}
-
-/*------------------------------------------------------------------------------
- * Command line "end" command
- *
- * Falls back to ENABLE_NODE.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-vty_cmd_end(struct vty* vty)
-{
- VTY_LOCK() ;
-
- switch (vty->node)
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- /* Nothing to do. */
- break;
- case CONFIG_NODE:
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case BGP_NODE:
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- case RMAP_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case KEYCHAIN_KEY_NODE:
- case MASC_NODE:
- case VTY_NODE:
- uty_config_unlock (vty, ENABLE_NODE);
- break;
- default:
- break;
- }
-
- 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 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Command line ^C action.
- *
- * Ignores contents of command line (including not adding to history).
- *
- * Fall back to ENABLE_NODE if in any one of a number of nodes.
- *
- * Resets the history pointer.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-uty_stop_input(struct vty *vty)
-{
- vty_io vio = vty->vio ;
-
- VTY_ASSERT_LOCKED() ;
-
- switch (vty->node)
- {
- case CONFIG_NODE:
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case BGP_NODE:
- case RMAP_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case KEYCHAIN_KEY_NODE:
- case MASC_NODE:
- case VTY_NODE:
- uty_config_unlock (vty, ENABLE_NODE) ;
- break;
- default:
- /* Unknown node, we have to ignore it. */
- break;
- }
-
- /* Set history pointer to the latest one. */
- vio->hp = vio->hindex;
-
- return CMD_SUCCESS ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Command ^Z action.
- *
- * Ignores contents of command line (including not adding to history).
- *
- * Fall back to ENABLE_NODE if in any one of a number of nodes.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-uty_end_config (struct vty *vty)
-{
- VTY_ASSERT_LOCKED() ;
-
- switch (vty->node)
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- /* Nothing to do. */
- break;
- case CONFIG_NODE:
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case BGP_NODE:
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- case RMAP_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case KEYCHAIN_KEY_NODE:
- case MASC_NODE:
- case VTY_NODE:
- uty_config_unlock (vty, ENABLE_NODE) ;
- break;
- default:
- /* Unknown node, we have to ignore it. */
- break;
- }
-
- return CMD_SUCCESS ;
-}
-
-/*------------------------------------------------------------------------------
- * Command ^D action -- when nothing else on command line.
- *
- * Same as "exit" command.
- *
- * Returns: command return code
- */
-extern enum cmd_return_code
-uty_down_level (struct vty *vty)
-{
- return vty_cmd_exit(vty) ;
-} ;
-
-/*==============================================================================
* Reading of configuration file
*
* The reading of the configuration file occurs at two times:
@@ -1116,18 +681,18 @@ uty_down_level (struct vty *vty)
* run directly in the thread -- no commands are queued.
*/
-static FILE * vty_use_backup_config (char *fullpath) ;
-static void vty_read_file (FILE *confp, struct cmd_element* first_cmd,
- bool ignore_warnings) ;
+static int vty_use_backup_config (const char *fullpath) ;
+static void vty_read_file (int conf_fd, const char* name,
+ cmd_command first_cmd, bool ignore_warnings) ;
/*------------------------------------------------------------------------------
* Read the given configuration file.
*/
extern void
-vty_read_config (char *config_file,
- char *config_default)
+vty_read_config (const char *config_file,
+ const char *config_default)
{
- vty_read_config_first_cmd_special(config_file, config_default, NULL, 1);
+ vty_read_config_first_cmd_special(config_file, config_default, NULL, true);
}
/*------------------------------------------------------------------------------
@@ -1148,15 +713,15 @@ vty_read_config (char *config_file,
* command.
*/
extern void
-vty_read_config_first_cmd_special(char *config_file,
- char *config_default,
- struct cmd_element* first_cmd,
+vty_read_config_first_cmd_special(const char *config_file,
+ const char *config_default,
+ cmd_command first_cmd,
bool ignore_warnings)
{
- char cwd[MAXPATHLEN];
- FILE *confp = NULL;
- char *fullpath;
- char *tmp = NULL;
+ char cwd[MAXPATHLEN] ;
+ int conf_fd ;
+ const char *fullpath ;
+ char *tmp = NULL ;
/* Deal with VTYSH_ENABLED magic */
if (VTYSH_ENABLED && (config_file == NULL))
@@ -1204,15 +769,15 @@ vty_read_config_first_cmd_special(char *config_file,
} ;
/* try to open the configuration file */
- confp = fopen (fullpath, "r");
+ conf_fd = uty_vfd_file_open(fullpath, vfd_io_read | vfd_io_blocking) ;
- if (confp == NULL)
+ if (conf_fd < 0)
{
fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
__func__, fullpath, errtostr(errno, 0).str);
- confp = vty_use_backup_config (fullpath);
- if (confp)
+ conf_fd = vty_use_backup_config (fullpath);
+ if (conf_fd >= 0)
fprintf (stderr, "WARNING: using backup configuration file!\n");
else
{
@@ -1226,8 +791,7 @@ vty_read_config_first_cmd_special(char *config_file,
fprintf(stderr, "Reading config file: %s\n", fullpath);
#endif
- vty_read_file (confp, first_cmd, ignore_warnings);
- fclose (confp);
+ vty_read_file(conf_fd, fullpath, first_cmd, ignore_warnings);
host_config_set (fullpath);
@@ -1249,17 +813,17 @@ vty_read_config_first_cmd_special(char *config_file,
* - call it "<fullpath>"
* - return an open FILE
*
- * Returns: NULL => no "<fullpath>.sav", or faild doing any of the above
- * otherwise, returns FILE* for open file.
+ * Returns: < 0 => no "<fullpath>.sav", or failed doing any of the above
+ * >= 0 otherwise, fd file.
*/
-static FILE *
-vty_use_backup_config (char *fullpath)
+static int
+vty_use_backup_config (const char *fullpath)
{
- char *tmp_path ;
+ char *tmp_path ;
struct stat buf;
- int ret, tmp, sav;
- int c;
- char buffer[4096] ;
+ int ret, tmp, sav;
+ int c, err;
+ char* buffer ;
enum { xl = 32 } ;
tmp_path = malloc(strlen(fullpath) + xl) ;
@@ -1268,14 +832,17 @@ vty_use_backup_config (char *fullpath)
confirm(xl > sizeof(CONF_BACKUP_EXT)) ;
sprintf (tmp_path, "%s%s", fullpath, CONF_BACKUP_EXT) ;
- sav = -1 ;
if (stat (tmp_path, &buf) != -1)
- sav = open (tmp_path, O_RDONLY);
+ sav = uty_vfd_file_open(tmp_path, vfd_io_read | vfd_io_blocking) ;
+ else
+ sav = -1 ;
if (sav < 0)
{
- free (tmp_path);
- return NULL;
+ err = errno ; /* making sure */
+ free (tmp_path) ;
+ errno = err ;
+ return sav ;
} ;
/* construct a temporary file and copy "<fullpath.sav>" to it. */
@@ -1286,34 +853,60 @@ vty_use_backup_config (char *fullpath)
tmp = mkstemp (tmp_path);
if (tmp < 0)
{
+ err = errno ;
free (tmp_path);
close(sav);
- return NULL;
+ errno = err ;
+ return tmp;
}
- while((c = read (sav, buffer, sizeof(buffer))) > 0)
- write (tmp, buffer, c);
+ enum { buffer_size = 64 * 1024 } ;
+ buffer = malloc(buffer_size) ;
+
+ c = 1 ;
+ while (c > 0)
+ {
+ c = read(sav, buffer, buffer_size) ;
+ if (c > 0)
+ {
+ if (write(tmp, buffer, c) < c)
+ c = -1 ;
+ } ;
+ } ;
+ err = errno ;
+
+ free(buffer) ;
+ close(sav) ;
+ close(tmp) ;
- close (sav);
- close (tmp);
+ if (c < 0)
+ {
+ errno = err ;
+ return c ;
+ } ;
/* Make sure that have the required file status */
if (chmod(tmp_path, CONFIGFILE_MASK) != 0)
{
+ err = errno ;
unlink (tmp_path);
free (tmp_path);
- return NULL;
- }
+ errno = err ;
+ return -1 ;
+ } ;
/* Make <fullpath> be a name for the new file just created. */
ret = link (tmp_path, fullpath) ;
/* Discard the temporary, now */
+ err = errno ;
unlink (tmp_path) ;
free (tmp_path) ;
+ errno = err ;
/* If link was successful, try to open -- otherwise, failed. */
- return (ret == 0) ? fopen (fullpath, "r") : NULL ;
+ return (ret == 0) ? uty_vfd_file_open(fullpath, vfd_io_read | vfd_io_blocking)
+ : -1 ;
} ;
/*------------------------------------------------------------------------------
@@ -1337,68 +930,49 @@ vty_use_backup_config (char *fullpath)
* so all commands are executed directly.
*/
static void
-vty_read_file (FILE *confp, struct cmd_element* first_cmd, bool ignore_warnings)
+vty_read_file (int conf_fd, const char* name,
+ cmd_command first_cmd, bool ignore_warnings)
{
- enum cmd_return_code ret ;
- struct vty *vty ;
+ cmd_return_code_t ret ;
+ vty vty ;
+ vty_io vio ;
+ vio_vf vf ;
+
+ VTY_LOCK() ;
/* 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) ;
+ vio = vty->vio ;
- /* Execute configuration file */
- ret = config_from_file (vty, confp, first_cmd, &vty->vio->clx,
- ignore_warnings) ;
-
- VTY_LOCK() ;
-
- if (ret != CMD_SUCCESS)
- {
- fprintf (stderr, "%% while processing line %u of the configuration:\n"
- "%s", vty->lineno, vty->buf) ;
-
- switch (ret)
- {
- case CMD_WARNING:
- fprintf (stderr, "%% Warning...\n");
- break;
+ vf = uty_vf_new(vio, name, conf_fd, vfd_file, vfd_io_read | vfd_io_blocking) ;
- case CMD_ERROR:
- fprintf (stderr, "%% Error...\n");
- break;
+ uty_vin_open( vio, vf, VIN_CONFIG, NULL, NULL, 64 * 1024) ;
+ uty_vout_open(vio, vf, VOUT_STDERR, NULL, NULL, 4 * 1024) ;
- case CMD_ERR_AMBIGUOUS:
- fprintf (stderr, "%% Ambiguous command.\n");
- break;
+ vio->vin->parse_type = cmd_parse_strict | cmd_parse_no_do ;
- case CMD_ERR_NO_MATCH:
- fprintf (stderr, "%% There is no such command.\n");
- break;
+ /* When we get here the VTY is set up and all ready to go. */
+ uty_cmd_prepare(vio) ;
- case CMD_ERR_INCOMPLETE:
- fprintf (stderr, "%% Incomplete command.\n");
- break;
+ VTY_UNLOCK() ;
- default:
- fprintf(stderr, "%% (unknown cause %d)\n", ret) ;
- break ;
- } ;
+ /* Execute configuration file */
+ ret = cmd_read_config(vty, first_cmd, ignore_warnings) ;
- uty_out_fflush(vty->vio, stderr) ; /* flush command output buffer */
+ ret = vty_cmd_loop_exit(vty, ret) ;
+ if (ret != CMD_SUCCESS)
+ {
+ vty_close(vty, true, qs_set(NULL, "Error in configuration file.")) ;
exit (1);
} ;
- uty_close(vty->vio) ;
- VTY_UNLOCK() ;
+ vty_close(vty, false, NULL) ;
} ;
+#if 0
/*------------------------------------------------------------------------------
* Flush the contents of the command output FIFO to the given file.
*
@@ -1414,84 +988,59 @@ uty_out_fflush(vty_io vio, FILE* file)
fflush(file) ;
- while ((src = vio_fifo_get_lump(&vio->cmd_obuf, &have)) != NULL)
+ src = vio_fifo_get_lump(&vio->cmd_obuf, &have) ;
+ while (src != NULL)
{
fwrite(src, 1, have, file) ;
- vio_fifo_got_upto(&vio->cmd_obuf, src + have) ;
+ src = vio_fifo_step_get_lump(&vio->cmd_obuf, &have, have) ;
} ;
fflush(file) ;
} ;
-
-
-
+#endif
/*==============================================================================
- * Configuration node/state handling
- *
- * At most one VTY may hold the configuration symbol of power at any time.
+ * For writing configuration file by command, temporarily redirect output to
+ * an actual file.
*/
/*------------------------------------------------------------------------------
- * Attempt to gain the configuration symbol of power
- *
- * If succeeds, set the given node.
- *
- * Returns: true <=> now own the symbol of power.
+ * Set the given fd as the VTY_FILE output.
*/
-extern bool
-vty_config_lock (struct vty *vty, enum node_type node)
+extern void
+vty_open_config_write(vty vty, int fd)
{
- bool result;
+ vty_io vio ;
+ vio_vf vf ;
VTY_LOCK() ;
- if (!vty_config)
- {
- vty->config = true ;
- vty_config = true ;
- } ;
+ vio = vty->vio ;
- result = vty->config;
+ vf = uty_vf_new(vio, "config write", fd, vfd_file, vfd_io_read) ;
- if (result)
- vty->node = node ;
+ uty_vout_open(vio, vf, VOUT_FILE, NULL, NULL, 16 * 1024) ;
VTY_UNLOCK() ;
-
- return result;
-}
+} ;
/*------------------------------------------------------------------------------
- * Give back the configuration symbol of power -- if own it.
- *
- * Set the given node -- which must be <= MAX_NON_CONFIG_NODE
+ * Write away any pending stuff, and return the VTY to normal.
*/
-extern void
-vty_config_unlock (struct vty *vty, enum node_type node)
+extern int
+vty_close_config_write(struct vty* vty)
{
+// vty_io vio ;
+// int err ;
+
VTY_LOCK() ;
- uty_config_unlock(vty, node);
- VTY_UNLOCK() ;
-}
-/*------------------------------------------------------------------------------
- * Give back the configuration symbol of power -- if own it.
- *
- * Set the given node -- which must be <= MAX_NON_CONFIG_NODE
- */
-extern void
-uty_config_unlock (struct vty *vty, enum node_type node)
-{
- VTY_ASSERT_LOCKED() ;
- if (vty_config && vty->config)
- {
- vty->config = false ;
- vty_config = false ;
- }
+ uty_vout_close(vty->vio, false) ;
+
+ VTY_UNLOCK() ;
- assert(node <= MAX_NON_CONFIG_NODE) ;
- vty->node = node ;
+// return err ;
+ return 0 ;
} ;
/*==============================================================================
@@ -1508,13 +1057,15 @@ DEFUN_CALL (config_who,
VTY_LOCK() ;
- vio = vio_list_base ;
+ vio = vio_list_base ; /* once locked */
+
while (vio != NULL) /* TODO: show only VTY_TERM ??? */
{
- uty_output (vty, "%svty[%d] connected from %s.\n",
+ vty_out(vty, "%svty[%d] connected from %s.\n",
vio->vty->config ? "*" : " ", i, uty_get_name(vio));
vio = sdl_next(vio, vio_list) ;
} ;
+
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1532,7 +1083,11 @@ DEFUN_CALL (line_vty,
return CMD_SUCCESS;
}
-/* Set time out value. */
+/*------------------------------------------------------------------------------
+ * Set time out value.
+ *
+ * Affects this and any future VTY_TERMINAL or VTY_SHELL_SERVER type VTY.
+ */
static int
exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
{
@@ -1550,10 +1105,9 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
if (sec_str)
timeout += strtol (sec_str, NULL, 10);
- vty_timeout_val = timeout;
+ host.vty_timeout_val = timeout;
- if (uty_is_terminal(vty) || uty_is_shell_server(vty))
- uty_sock_set_timer(&vty->vio->sock, timeout) ;
+ uty_set_timeout(vty->vio, timeout) ; /* update own timeout, if required */
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1596,10 +1150,9 @@ DEFUN_CALL (vty_access_class,
{
VTY_LOCK() ;
- if (vty_accesslist_name)
- XFREE(MTYPE_VTY, vty_accesslist_name);
+ XFREE(MTYPE_HOST, host.vty_accesslist_name);
- vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
+ host.vty_accesslist_name = XSTRDUP(MTYPE_HOST, argv[0]);
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1613,22 +1166,25 @@ DEFUN_CALL (no_vty_access_class,
"Filter connections based on an IP access list\n"
"IP access list\n")
{
- int result = CMD_SUCCESS;
+ cmd_return_code_t ret ;
VTY_LOCK() ;
- if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
+
+ if ((argc == 0) || ( (host.vty_accesslist_name != NULL) &&
+ (strcmp(host.vty_accesslist_name, argv[0]) == 0) ))
{
- vty_out_error(vty, "Access-class is not currently applied to vty\n");
- result = CMD_WARNING;
+ XFREE(MTYPE_HOST, host.vty_accesslist_name);
+ /* sets host.vty_accesslist_name = NULL */
+ ret = CMD_SUCCESS ;
}
else
{
- XFREE(MTYPE_VTY, vty_accesslist_name);
- vty_accesslist_name = NULL;
- }
+ vty_out(vty, "Access-class is not currently applied to vty\n") ;
+ ret = CMD_WARNING;
+ } ;
VTY_UNLOCK() ;
- return result;
+ return ret;
}
#ifdef HAVE_IPV6
@@ -1641,10 +1197,10 @@ DEFUN_CALL (vty_ipv6_access_class,
"IPv6 access list\n")
{
VTY_LOCK() ;
- if (vty_ipv6_accesslist_name)
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
- vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
+ XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name);
+
+ host.vty_ipv6_accesslist_name = XSTRDUP(MTYPE_HOST, argv[0]);
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1659,25 +1215,25 @@ DEFUN_CALL (no_vty_ipv6_access_class,
"Filter connections based on an IP access list\n"
"IPv6 access list\n")
{
- int result = CMD_SUCCESS;
+ cmd_return_code_t ret ;
VTY_LOCK() ;
- if (! vty_ipv6_accesslist_name ||
- (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
+ if ((argc == 0) || ( (host.vty_ipv6_accesslist_name != NULL) &&
+ (strcmp(host.vty_ipv6_accesslist_name, argv[0]) == 0) ))
{
- vty_out_error(vty, "IPv6 access-class is not currently applied to vty\n") ;
- result = CMD_WARNING;
+ XFREE(MTYPE_HOST, host.vty_ipv6_accesslist_name);
+ /* sets host.vty_ipv6_accesslist_name = NULL */
+ ret = CMD_SUCCESS ;
}
else
{
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
-
- vty_ipv6_accesslist_name = NULL;
- }
+ vty_out(vty, "IPv6 access-class is not currently applied to vty\n") ;
+ ret = CMD_WARNING;
+ } ;
VTY_UNLOCK() ;
- return CMD_SUCCESS;
+ return ret;
}
#endif /* HAVE_IPV6 */
@@ -1688,7 +1244,7 @@ DEFUN_CALL (vty_login,
"Enable password checking\n")
{
VTY_LOCK() ;
- no_password_check = 0;
+ host.no_password_check = false ;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1700,7 +1256,7 @@ DEFUN_CALL (no_vty_login,
"Enable password checking\n")
{
VTY_LOCK() ;
- no_password_check = 1;
+ host.no_password_check = true ;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1712,7 +1268,7 @@ DEFUN_CALL (vty_restricted_mode,
"Restrict view commands available in anonymous, unauthenticated vty\n")
{
VTY_LOCK() ;
- restricted_mode = 1;
+ host.restricted_mode = true;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1724,7 +1280,7 @@ DEFUN_CALL (vty_no_restricted_mode,
"Enable password checking\n")
{
VTY_LOCK() ;
- restricted_mode = 0;
+ host.restricted_mode = false;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1736,7 +1292,7 @@ DEFUN_CALL (service_advanced_vty,
"Enable advanced mode vty interface\n")
{
VTY_LOCK() ;
- host.advanced = 1;
+ host.advanced = true;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1749,7 +1305,7 @@ DEFUN_CALL (no_service_advanced_vty,
"Enable advanced mode vty interface\n")
{
VTY_LOCK() ;
- host.advanced = 0;
+ host.advanced = false;
VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -1792,26 +1348,10 @@ DEFUN_CALL (show_history,
SHOW_STR
"Display the session command history\n")
{
- int index;
-
VTY_LOCK() ;
- for (index = vty->vio->hindex + 1; index != vty->vio->hindex;)
- {
- qstring line ;
-
- if (index == VTY_MAXHIST)
- {
- index = 0;
- continue;
- }
-
- line = vector_get_item(vty->vio->hist, index) ;
- if (line != NULL)
- uty_output (vty, " %s\n", line->char_body);
-
- index++;
- }
+ if (vty->type == VTY_TERMINAL)
+ uty_cli_hist_show(vty->vio->vin->cli) ;
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1827,22 +1367,22 @@ vty_config_write (struct vty *vty)
{
vty_out (vty, "line vty\n");
- if (vty_accesslist_name)
- vty_out (vty, " access-class %s\n", vty_accesslist_name);
+ if (host.vty_accesslist_name)
+ vty_out (vty, " access-class %s\n", host.vty_accesslist_name);
- if (vty_ipv6_accesslist_name)
- vty_out (vty, " ipv6 access-class %s\n", vty_ipv6_accesslist_name);
+ if (host.vty_ipv6_accesslist_name)
+ vty_out (vty, " ipv6 access-class %s\n", host.vty_ipv6_accesslist_name);
/* exec-timeout */
- if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
- vty_out (vty, " exec-timeout %ld %ld\n", vty_timeout_val / 60,
- vty_timeout_val % 60);
+ if (host.vty_timeout_val != VTY_TIMEOUT_DEFAULT)
+ vty_out (vty, " exec-timeout %ld %ld\n", host.vty_timeout_val / 60,
+ host.vty_timeout_val % 60);
/* login */
- if (no_password_check)
+ if (host.no_password_check)
vty_out (vty, " no login\n");
- if (restricted_mode != restricted_mode_default)
+ if (host.restricted_mode != restricted_mode_default)
{
if (restricted_mode_default)
vty_out (vty, " no anonymous restricted\n");
@@ -1879,7 +1419,7 @@ vty_save_cwd (void)
getcwd (cwd, MAXPATHLEN);
}
- vty_cwd = XSTRDUP(MTYPE_TMP, cwd) ;
+ host.vty_cwd = XSTRDUP(MTYPE_HOST, cwd) ;
} ;
/*------------------------------------------------------------------------------
@@ -1888,12 +1428,13 @@ vty_save_cwd (void)
char *
vty_get_cwd ()
{
- return vty_cwd;
+ return host.vty_cwd;
}
/*==============================================================================
* Access functions for VTY values, where locking is or might be required.
*/
+#if 0
static bool
vty_is_terminal(struct vty *vty)
@@ -1925,31 +1466,16 @@ vty_is_shell_client (struct vty *vty)
return result;
}
-enum node_type
-vty_get_node(struct vty *vty)
-{
- int result;
- VTY_LOCK() ;
- result = vty->node;
- VTY_UNLOCK() ;
- return result;
-}
-
-void
-vty_set_node(struct vty *vty, enum node_type node)
-{
- VTY_LOCK() ;
- vty->node = node;
- VTY_UNLOCK() ;
-}
+#endif
void
vty_set_lines(struct vty *vty, int lines)
{
VTY_LOCK() ;
- vty->vio->lines = lines;
- vty->vio->lines_set = 1 ;
- uty_set_height(vty->vio) ;
+
+ if (vty->type == VTY_TERMINAL)
+ uty_cli_set_lines(vty->vio->vin->cli, lines, true) ;
+
VTY_UNLOCK() ;
}
diff --git a/lib/vty.h b/lib/vty.h
index b8621588..99b08d96 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -1,4 +1,4 @@
-/* VTY top level
+/* VTY external interface -- header
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
* Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
@@ -25,6 +25,10 @@
#define _ZEBRA_VTY_H
#include "misc.h"
+#include "vargs.h"
+
+#include "command_common.h" /* NB: *not* command.h */
+#include "vty_common.h" /* struct vty & VTY types */
#include "thread.h"
#include "log.h"
@@ -35,128 +39,12 @@
#include "list_util.h"
#include "vector.h"
#include "qstring.h"
-#include "node_type.h"
-
-/*==============================================================================
- * The VTYSH uses a unix socket to talk to the daemon.
- *
- * The ability to respond to a connection from VTYSH appears to be a *compile*
- * time option. In the interests of keeping the code up to date, the VTYSH
- * option is turned into a testable constant.
- */
-#ifdef VTYSH
- enum { VTYSH_ENABLED = true } ;
-#else
- enum { VTYSH_ENABLED = false } ;
-#endif
-
-/*==============================================================================
- * VTY Types
- */
-enum vty_type /* Command output */
-{
- VTY_TERMINAL, /* a telnet terminal server */
- VTY_SHELL_SERVER, /* a vty_shell server */
-
- VTY_SHELL_CLIENT, /* a vty_shell client */
-
- VTY_CONFIG_READ, /* configuration file reader */
-
- VTY_STDOUT, /* stdout */
- VTY_STDERR, /* stderr */
-} ;
-typedef enum vty_type vty_type_t ;
-
-/*==============================================================================
- *
- *
- *
- *
- */
-
-typedef unsigned long vty_timer_time ; /* Time out time in seconds */
-
-enum
-{
- VTY_WATCH_DOG_INTERVAL = 5, /* interval between barks */
-
- VTY_HALF_CLOSE_TIMEOUT = 120, /* timeout after half_close */
-
- VTY_TIMEOUT_DEFAULT = 600, /* terminal timeout value */
-} ;
/*==============================================================================
- * VTY struct.
- */
-
-typedef struct vty_io* vty_io ; /* private to vty.c */
-
-struct cmd_parsed ; /* in case vty.h expanded before command.h */
-
-typedef struct vty* vty ;
-struct vty
-{
- vty_type_t type ;
-
- /*----------------------------------------------------------------------
- * The following are the context in which commands are executed.
- */
-
- /* Node status of this vty
- *
- * NB: when using qpthreads should lock VTY to access this -- so use
- * vty_get_node() and vty_set_node().
- */
- enum node_type node ;
-
- /* For current referencing point of interface, route-map, access-list
- * etc...
- *
- * NB: this value is private to the command execution, which is assumed
- * to all be in the one thread... so no lock required.
- */
- void *index ;
-
- /* For multiple level index treatment such as key chain and key.
- *
- * NB: this value is private to the command execution, which is assumed
- * to all be in the one thread... so no lock required.
- */
- void *index_sub ;
-
- /* In configure mode. */
- bool config;
-
- /*----------------------------------------------------------------------------
- * The current command line.
- *
- * These are set when a command is parsed and dispatched.
- *
- * They are not touched until the command completes -- so may be read while
- * the command is being parsed and executed.
- */
- const char* buf ;
- struct cmd_parsed* parsed ;
- unsigned lineno ;
-
- bool output_enabled ;
- bool reflect_enabled ;
- bool more_enabled ;
-
- bool reflected ;
-
- /*----------------------------------------------------------------------
- * The following are used inside vty.c only.
- */
- vty_io vio ; /* one vio object per vty */
-} ;
-
-/*------------------------------------------------------------------------------
- * Can now include this
+ * These are definitions and functions for things which are required outside
+ * the vty and command family.
*/
-#include "command.h"
-
/*------------------------------------------------------------------------------
*
*/
@@ -187,13 +75,6 @@ enum { VTY_MAX_SPACES = 40 } ;
#define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP)
#endif
-/* GCC have printf type attribute check. */
-#ifdef __GNUC__
-#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
-#else
-#define PRINTF_ATTRIBUTE(a,b)
-#endif /* __GNUC__ */
-
/* Utility macros to convert VTY argument to unsigned long or integer. */
#define VTY_GET_LONG(NAME,V,STR) \
do { \
@@ -267,22 +148,23 @@ extern void vty_reset_because(const char* why) ;
extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
extern int vty_out_indent(struct vty *vty, int indent) ;
-extern int vty_out_error (struct vty *vty, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3);
extern void vty_out_clear(struct vty *vty) ;
-extern void vty_read_config (char *config_file, char *config_default);
-extern void vty_read_config_first_cmd_special(
- char *config_file, char *config_default,
- struct cmd_element* first_cmd, bool ignore_warnings) ;
+
+
+
+
+extern void vty_read_config (const char* config_file,
+ const char* config_default);
+extern void vty_read_config_first_cmd_special(const char* config_file,
+ const char* config_default,
+ cmd_command first_cmd,
+ bool ignore_warnings) ;
extern void vty_time_print (struct vty *, int);
extern char *vty_get_cwd (void);
extern void vty_hello (struct vty *);
-extern enum node_type vty_get_node(struct vty *);
-extern void vty_set_node(struct vty *, enum node_type);
-extern void vty_set_lines(struct vty *, int);
#endif /* _ZEBRA_VTY_H */
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
index 17351f58..42d78349 100644
--- a/lib/vty_cli.c
+++ b/lib/vty_cli.c
@@ -20,17 +20,20 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
+#include "misc.h"
+#include <ctype.h>
-#include <zebra.h>
-
-#include "keystroke.h"
-#include "vty.h"
-#include "uty.h"
+#include "vty_local.h"
+#include "vty_command.h"
#include "vty_cli.h"
+#include "vty_io_term.h"
#include "vty_io.h"
#include "vio_lines.h"
+#include "vio_fifo.h"
-#include "command.h"
+#include "keystroke.h"
+
+#include "command_common.h"
#include "command_parse.h"
#include "command_execute.h"
#include "command_queue.h"
@@ -38,86 +41,323 @@
#include "memory.h"
/*==============================================================================
- * Host name handling
+ * Construct and destroy CLI object
+ *
+ *
+ *
+ */
+static bool uty_cli_iac_callback(keystroke_iac_callback_args) ;
+static void uty_cli_update_more(vty_cli cli) ;
+
+/*------------------------------------------------------------------------------
+ * Construct and initialise a new CLI object -- never embedded.
*
- * The host name is used in the command line prompt. The name used is either
- * the name set by "hostname" command, or the current machine host name.
+ * The CLI is started up in the state it is in waiting for a command to
+ * complete. This means that all start-up messages etc. are handled as the
+ * output from an implied start command. uty_cli_start() is called when the
+ * start-up messages etc. are complete.
*
- * Static variables -- under the VTY_LOCK !
+ * Note that the vty_io_term stuff sends out a bunch of telnet commands, which
+ * will be buffered in the CLI buffer. That will be output, before the
+ * start-up messages etc. on uty_cli_start().
*/
+extern vty_cli
+uty_cli_new(vio_vf vf)
+{
+ vty_cli cli ;
+
+ cli = XCALLOC(MTYPE_VTY_CLI, sizeof(struct vty_cli)) ;
+
+ /* Zeroising has initialised:
+ *
+ * vf = NULL -- set below
+ *
+ * hist = empty vector (embedded) -- set up when first used.
+ * hp = 0 -- hp == h_now => in the present ...
+ * h_now = 0 -- ... see uty_cli_hist_add()
+ *
+ * width = 0 -- unknown width ) Telnet window size
+ * height = 0 -- unknown height )
+ *
+ * lines = 0 -- set below
+ * lines_set = false -- set below
+ *
+ * monitor = false -- not a "monitor"
+ * monitor_busy = false -- so not busy either
+ *
+ * v_timeout = ???
+ *
+ * key_stream = X -- set below
+ *
+ * drawn = false
+ * dirty = false
+ *
+ * prompt_len = 0 -- not drawn, in any case
+ * extra_len = 0 -- not drawn, in any case
+ *
+ * echo_suppress = false
+ *
+ * prompt_node = NULL_NODE -- so not set !
+ * prompt_gen = 0 -- not a generation number
+ * prompt_for_node = empty qstring (embedded)
+ *
+ * password_fail = 0 -- so far, so good.
+ *
+ * blocked = false -- see below
+ * in_progress = false -- see below
+ * out_active = false
+ *
+ * more_wait = false
+ * more_active = false
+ *
+ * out_done = false -- not that it matters
+ *
+ * more_enabled = false -- not in "--More--" state
+ *
+ * node = NULL_NODE -- set in vty_cli_start()
+ * to_do = cmd_do_nothing
+ *
+ * cl = NULL qstring -- set below
+ * clx = NULL qstring -- set below
+ *
+ * parsed = all zeros -- empty parsed object
+ * cbuf = empty fifo (embedded)
+ *
+ * olc = empty line control (embedded)
+ */
+ confirm(VECTOR_INIT_ALL_ZEROS) ; /* hist */
+ confirm(QSTRING_INIT_ALL_ZEROS) ; /* prompt_for_node */
+ confirm(NULL_NODE == 0) ; /* prompt_node & node */
+ confirm(cmd_do_nothing == 0) ; /* to_do */
+ confirm(VIO_FIFO_INIT_ALL_ZEROS) ; /* cbuf */
+ confirm(VIO_LINE_CONTROL_INIT_ALL_ZEROS) ; /* olc */
+ confirm(CMD_PARSED_INIT_ALL_ZEROS) ; /* parsed */
+
+ cli->vf = vf ;
+
+ /* Allocate and initialise a keystroke stream TODO: CSI ?? */
+ cli->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, cli) ;
-static char* vty_host_name = NULL ;
-int vty_host_name_set = 0 ;
+ /* Set up cl and clx qstrings and the command line output fifo */
+ cli->cl = qs_init_new(NULL, 120) ; /* reasonable line length */
+ cli->clx = qs_init_new(NULL, 120) ;
-static void uty_new_host_name(const char* name) ;
+ vio_fifo_init_new(cli->cbuf, 500) ; /* just for the CLI stuff */
+
+ /* Use global 'lines' setting, as default -- but 'lines_set' false. */
+ uty_cli_set_lines(cli, host.lines, false) ;
+ uty_cli_update_more(cli) ; /* and update the olc etc to suit. */
+
+ /* Ready to be started -- out_active & more_wait are false. */
+ cli->blocked = true ;
+ cli->in_progress = true ;
+
+ return cli ;
+} ;
/*------------------------------------------------------------------------------
- * Update vty_host_name as per "hostname" or "no hostname" command
+ * Start the CLI.
+ *
+ * The implied start "command" is complete and everything is ready to start
+ * the CLI.
+ *
+ * Note that before releasing any pending output, calls
+ *
+ * Returns: write_ready -- so the first event is a write event, to flush
+ * any output to date.
*/
extern void
-uty_set_host_name(const char* name)
+uty_cli_start(vty_cli cli, node_type_t node)
+{
+ uty_cli_done_command(cli, node) ; /* implied push output */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close CLI object, if any -- may be called more than once.
+ *
+ * Shuts down and discards anything to do with the input.
+ *
+ * Revokes anything that can be revoked, which may allow output to proceed
+ * and the vty to clear itself down.
+ *
+ * If in_progress and not final, keeps the cbuf, the olc and clx if in_progress.
+ * If not final, keeps the cbuf and the olc.
+ *
+ * Destroys self if final.
+ *
+ * Expects other code to:
+ *
+ * - close down command loop and empty the vin/vout stacks.
+ *
+ * - turn of any monitor status.
+ *
+ * - half close the telnet connection.
+ */
+extern vty_cli
+uty_cli_close(vty_cli cli, bool final)
{
+ if (cli == NULL)
+ return NULL ;
+
VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
- vty_host_name_set = (name != NULL) ;
+ /* Revoke any command that is in flight. */
+ cq_revoke(cli->vf->vio->vty) ;
- if (vty_host_name_set)
- uty_new_host_name(name) ;
- else
- uty_check_host_name() ;
+ /* Bring as much of the command handling to a stop as possible, and
+ * turn off "---more--" etc. so that output can proceed.
+ */
+ cli->more_enabled = false ;
+ cli->lines = 0 ; /* so will not be re-enabled */
+
+ uty_cli_wipe(cli, 0) ; /* wipe anything the CLI has on screen */
+
+ cli->more_wait = false ; /* exit more_wait (if was in it) */
+ cli->more_active = false ; /* and so cannot be this */
+
+ cli->out_active = true ; /* if there is any output, it can go */
+
+ /* Ream out the history. */
+ {
+ qstring line ;
+ while ((line = vector_ream(cli->hist, keep_it)) != NULL)
+ qs_reset(line, free_it) ;
+ } ;
+
+ /* Empty the keystroke handling. */
+ cli->key_stream = keystroke_stream_free(cli->key_stream) ;
+
+ /* Can discard active command line if not in_progress. */
+ if (!cli->in_progress || final)
+ cli->clx = qs_reset(cli->clx, free_it) ;
+
+ /* Can discard parsed object. */
+ cmd_parsed_reset(cli->parsed, keep_it) ;
+
+ /* If final, free the CLI object. */
+ if (final)
+ {
+ qs_reset(cli->prompt_for_node, keep_it) ;
+ cli->cl = qs_reset(cli->cl, free_it) ;
+
+ vio_fifo_reset(cli->cbuf, keep_it) ; /* embedded fifo */
+ vio_lc_reset(cli->olc, keep_it) ; /* embedded line control */
+
+ XFREE(MTYPE_VTY_CLI, cli) ; /* sets cli = NULL */
+ } ;
+
+ return cli ;
} ;
/*------------------------------------------------------------------------------
- * If vty_host_name is set, free it.
+ * The keystroke iac callback function.
+ *
+ * This deals with IAC sequences that should be dealt with as soon as they
+ * are read -- not stored in the keystroke stream for later processing.
*/
-extern void
-uty_free_host_name(void)
+static bool
+uty_cli_iac_callback(keystroke_iac_callback_args)
{
- if (vty_host_name != NULL)
- XFREE (MTYPE_HOST, vty_host_name) ; /* sets vty_host_name = NULL */
+ return uty_telnet_command(((vty_cli)context)->vf, stroke, true) ;
} ;
/*------------------------------------------------------------------------------
- * If the host name is not set by command, see if the actual host name has
- * changed, and if so change it.
+ * Set the cli->lines, explicitly or implicitly, and update the "--more--"
+ * state to suit.
*
- * This is done periodically in case the actual host name changes !
+ * lines < 0 => use whatever telnet has said, or no "--more--"
+ * lines = 0 => no "--more--"
+ * lines > 0 => use
*/
extern void
-uty_check_host_name(void)
+uty_cli_set_lines(vty_cli cli, int lines, bool explicit)
{
- struct utsname names ;
-
- VTY_ASSERT_LOCKED() ;
+ cli->lines = lines ;
+ cli->lines_set = explicit ;
- if (vty_host_name_set)
- return ; /* nothing to do if set by command */
+ uty_cli_update_more(cli) ;
+} ;
- uname (&names) ;
+/*------------------------------------------------------------------------------
+ * Set the cli->lines, explicitly or implicitly, and update the "--more--"
+ * state to suit.
+ *
+ * lines < 0 => use whatever telnet has said, or no "--more--"
+ * lines = 0 => no "--more--"
+ * lines > 0 =>
+ */
+extern void
+uty_cli_set_window(vty_cli cli, int width, int height)
+{
+ cli->width = width ;
+ cli->height = height ;
- if ((vty_host_name == NULL) || (strcmp(vty_host_name, names.nodename) != 0))
- uty_new_host_name(names.nodename) ;
+ uty_cli_update_more(cli) ;
} ;
/*------------------------------------------------------------------------------
- * Set new vty_host_name and run along list of VTYs to mark the change.
+ * Update the "--more--" state, after changing one or more of:
+ *
+ * cli->lines
+ * cli->lines_set
+ * cli->width
+ * cli->height
+ *
+ * Set the effective height for line control, if required, which 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.
*/
static void
-uty_new_host_name(const char* name)
+uty_cli_update_more(vty_cli cli)
{
- vty_io vio ;
-
- VTY_ASSERT_LOCKED() ;
+ bool on ;
- uty_free_host_name() ;
- vty_host_name = XSTRDUP(MTYPE_HOST, name) ;
+ on = false ; /* default state */
- vio = vio_list_base ;
- while (vio != NULL)
+ if (cli->vf->vout_state == vf_open)
{
- vio->cli_prompt_set = 0 ;
- vio = sdl_next(vio, vio_list) ;
+ int height ;
+
+ height = 0 ; /* default state: no "--more--" */
+
+ if ((cli->width) != 0)
+ {
+ /* If window size is known, use lines or given height */
+ if (cli->lines >= 0)
+ height = cli->lines ;
+ else
+ {
+ /* Window height, leaving one line from previous "page"
+ * and one line for the "--more--" -- if at all possible
+ */
+ height = cli->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 (cli->lines_set)
+ height = cli->lines ;
+ } ;
+
+ if (height > 0)
+ on = true ; /* have a defined height */
+ else
+ height = 200 ; /* but no "--more--" */
+
+ vio_lc_set_window(cli->olc, cli->width, height) ;
} ;
+
+ cli->more_enabled = on ;
} ;
/*==============================================================================
@@ -132,31 +372,49 @@ uty_new_host_name(const char* name)
* -- setting read or write on, means enabling the file for select/pselect to
* consider it for read_ready or write_ready, respectively.
*
+ * All command actions are dispatched via the command queue -- commands are
+ * not executed in the cli. [For legacy threads the event queue is used. If
+ * that is too slow, a priority event queue will have to be invented.]
+ *
* State of the CLI:
*
- * cli_blocked -- the CLI is unable to process any further keystrokes.
+ * in_progress -- a command has been dispatched and has not yet completed.
+ *
+ * The command may be a in_pipe, so there may be many commands
+ * to be completed before the CLI level command is.
+ *
+ * or: the CLI has been closed.
+ *
+ * blocked -- is in_progress and a further command is now ready to be
+ * dispatched.
+ *
+ * or: the CLI has been closed.
+ *
+ * out_active -- the command output FIFO is being emptied.
*
- * cmd_in_progress -- a command has been dispatched and has not yet
- * completed (may have been queued).
+ * This is set when a command completes, and cleared when
+ * everything is written away.
*
- * cmd_out_enabled -- the command FIFO is may be emptied.
+ * Note that in this context a command is an individual
+ * command -- where in_progress may cover any number of
+ * command if the CLI level command is an in_pipe.
*
- * This is set when a command completes, and cleared when
- * everything is written away.
+ * more_wait -- is in "--more--" wait state
*
- * cli_more_wait -- is in "--more--" wait state
+ * more_active -- is in "--more--" wait state, and the "--more--" prompt
+ * has not been written away, yet.
*
* The following are the valid combinations:
*
- * blkd : cip : o_en : m_wt :
- * -----:------:------:------:--------------------------------------------
- * 0 : 0 : 0 : 0 : collecting a new command
- * 0 : 1 : 0 : 0 : command dispatched
- * 1 : 1 : 0 : 0 : waiting for (queued) command to complete
- * 1 : 0 : 1 : 0 : waiting for command output to complete
- * 1 : 0 : 0 : 1 : waiting for "--more--" to be written away
- * 0 : 0 : 0 : 1 : waiting for "--more--" response
- * 1 : 1 : 1 : 0 : waiting for command to complete, after the
+ * in_p: blkd: o_ac: m_wt: m_ac:
+ * ----:-----:-----:-----:-----:-----------------------------------------
+ * 0 : 0 : 0 : 0 : 0 : collecting a new command
+ * 0 : 0 : 1 : 0 : 0 : waiting for command output to finish
+ * 1 : 0 : X : 0 : 0 : command dispatched
+ * 1 : 1 : X : 0 : 0 : waiting for command to complete
+ * X : X : 0 : 1 : 1 : waiting for "--more--" to be written away
+ * X : X : 0 : 1 : 0 : waiting for "--more--" response
+ * 1 : 1 : 1 : 0 : 0 : waiting for command to complete, after the
* CLI has been closed
*
* There are two output FIFOs:
@@ -166,9 +424,9 @@ uty_new_host_name(const char* name)
* 2. for output generated by commands -- vty_out and friends.
*
* The CLI FIFO is emptied whenever possible, in preference to the command
- * FIFO. The command FIFO is emptied when cmd_out_enabled. While
- * cmd_in_progress is also !cmd_out_enabled -- so that all the output from a
- * given command is collected together before being sent to the file.
+ * FIFO. The command FIFO is emptied when out_active. While a command is
+ * in_progress all its output is collected in the command output buffer,
+ * to be written away when the command completes.
*
* Note that only sets read on when the keystroke stream is empty and has not
* yet hit eof. The CLI process is driven mostly by write_ready -- which
@@ -188,32 +446,20 @@ uty_new_host_name(const char* name)
*
* This is largely buried in the output handling.
*
- * While cmd_in_progress is true cmd_out_enabled will be false. When the
- * command completes:
- *
- * * cmd_in_progress is cleared
- *
- * * cmd_out_enabled is set
- *
- * * cli_blocked will be set
- *
- * * the line_control structure is reset
- *
- * * the output process is kicked off by setting write on
+ * When a command completes and its output is pushed to written away,
+ * out_active will be set (and any command line will be wiped). The output
+ * process is then kicked.
*
* The output process used the line_control structure to manage the output, and
* occasionally enter the trivial "--more--" CLI. This is invisible to the
- * main CLI. (See the cli_more_wait flag and its handling.)
+ * main CLI. (See the more_wait and more_active flags and their handling.)
*
- * When all the output has completed the CLI will be kicked, which will see
- * that the output buffer is now empty, and it can proceed.
+ * When all the output has completed the out_active flag is cleared and the CLI
+ * will be kicked.
*
* It is expected that the output will end with a newline -- so that when the
* CLI is kicked, the cursor will be at the start of an empty line.
*
- * This mechanism means that the command output FIFO only ever contains the
- * output from the last command executed.
- *
* If the user decides to abandon output at the "--more--" prompt, then the
* contents of the command output FIFO are discarded.
*
@@ -265,102 +511,15 @@ uty_new_host_name(const char* name)
#define CONTROL(X) ((X) & 0x1F)
-static void uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret) ;
-static enum vty_readiness uty_cli_standard(vty_io vio) ;
-static enum vty_readiness uty_cli_more_wait(vty_io vio) ;
-static void uty_cli_draw(vty_io vio) ;
-static void uty_cli_draw_this(vty_io vio, enum node_type node) ;
-static void uty_cli_wipe(vty_io vio, int len) ;
-
-static void uty_will_echo (vty_io vio) ;
-static void uty_will_suppress_go_ahead (vty_io vio) ;
-static void uty_dont_linemode (vty_io vio) ;
-static void uty_do_window_size (vty_io vio) ;
-static void uty_dont_lflow_ahead (vty_io vio) ;
-
-/*------------------------------------------------------------------------------
- * Initialise CLI.
- *
- * It is assumed that the following have been initialised, empty or zero:
- *
- * cli_prompt_for_node
- * cl
- * clx
- * cli_vbuf
- * cli_obuf
- *
- * cli_drawn
- * cli_dirty
- *
- * cli_prompt_set
- *
- * cli_blocked
- * cmd_in_progress
- * cmd_out_enabled
- * cli_wait_more
- *
- * cli_more_enabled
- *
- * Sets the CLI such that there is apparently a command in progress, so that
- * further initialisation (in particular hello messages and the like) is
- * treated as a "start up command".
- *
- * Sends a suitable set of Telnet commands to start the process.
- */
-extern void
-uty_cli_init(vty_io vio)
-{
- assert(vio->vty->type == VTY_TERMINAL) ;
-
- vio->cmd_in_progress = 1 ;
- vio->cli_blocked = 1 ;
-
- vio->cli_do = cli_do_nothing ;
-
- /* 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) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Start the CLI.
- *
- * All start-up operations are complete -- so the "command" is now complete.
- *
- * Returns: write_ready -- so the first event is a write event, to flush
- * any output to date.
- */
-extern enum vty_readiness
-uty_cli_start(vty_io vio)
-{
- uty_cli_cmd_complete(vio, CMD_SUCCESS) ;
- return write_ready ;
-} ;
+static enum vty_readiness uty_cli_standard(vty_cli cli) ;
+static enum vty_readiness uty_cli_more_wait(vty_cli cli) ;
+static void uty_cli_draw(vty_cli cli) ;
+static void uty_cli_draw_this(vty_cli cli, node_type_t node) ;
/*------------------------------------------------------------------------------
- * Close the CLI
+ * CLI for VTY_TERMINAL
*
- * Note that if any command is revoked, then will clear cmd_in_progress and
- * set cmd_out_enabled -- so any output can now clear.
- */
-extern void
-uty_cli_close(vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
- assert(vio->vty->type == VTY_TERMINAL) ;
-
- cq_revoke(vio->vty) ;
-
- vio->cli_blocked = 1 ; /* don't attempt any more */
- vio->cmd_out_enabled = 1 ; /* allow output to clear */
-} ;
-
-/*------------------------------------------------------------------------------
- * CLI for VTY_TERM
+ * Called by uty_term_ready(), so driven by read/write ready.
*
* Do nothing at all if half closed.
*
@@ -370,29 +529,33 @@ uty_cli_close(vty_io vio)
* NB: on return, requires that an attempt is made to write away anything that
* may be ready for that.
*/
-extern enum vty_readiness
-uty_cli(vty_io vio)
+extern vty_readiness_t
+uty_cli(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- assert(vio->vty_type == VTY_TERM) ;
- if (vio->half_closed)
- return not_ready ; /* Nothing more if half closed */
+ if (cli->vf->vin_state != vf_open)
+ return not_ready ; /* Nothing more from CLI if closing */
/* Standard or "--more--" CLI ? */
- if (vio->cli_more_wait)
- return uty_cli_more_wait(vio) ;
+ if (cli->more_wait)
+ return uty_cli_more_wait(cli) ;
else
- return uty_cli_standard(vio) ;
+ return uty_cli_standard(cli) ;
} ;
/*==============================================================================
* The Standard CLI
*/
-static enum cli_do uty_cli_process(vty_io vio, enum node_type node) ;
-static void uty_cli_response(vty_io vio, enum cli_do cli_do) ;
-static bool uty_cli_dispatch(vty_io vio) ;
+static cmd_do_t uty_cli_process(vty_cli cli, node_type_t node) ;
+static void uty_cli_response(vty_cli cli, cmd_do_t cmd_do) ;
+
+
+static void uty_cli_dispatch(vty_cli cli) ;
+static cmd_do_t uty_cli_command(vty_cli cli) ;
+static void uty_cli_hist_add (vty_cli cli, qstring clx) ;
+
/*------------------------------------------------------------------------------
* Standard CLI for VTY_TERM -- if not blocked, runs until:
@@ -408,34 +571,21 @@ static bool uty_cli_dispatch(vty_io vio) ;
* write_ready if there is anything in the keystroke stream
* read_ready otherwise
*/
-static enum vty_readiness
-uty_cli_standard(vty_io vio)
+static vty_readiness_t
+uty_cli_standard(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- assert(vio->vty_type == VTY_TERM) ;
- /* cli_blocked is set when is waiting for a command, or its output to
- * complete -- unless either of those has happened, is still blocked.
+ assert(!cli->more_wait) ; /* cannot be here in more_wait state ! */
+
+ /* cli_blocked is set when is waiting for a command, or some output to
+ * complete.
*
* NB: in both these cases, assumes that other forces are at work to
* keep things moving.
*/
- if (vio->cli_blocked)
- {
- assert(vio->cmd_in_progress || vio->cmd_out_enabled) ;
-
- if (vio->cmd_in_progress)
- {
- assert(!vio->cmd_out_enabled) ;
- return not_ready ;
- } ;
-
- if (!vio_fifo_empty(&vio->cmd_obuf))
- return not_ready ;
-
- vio->cli_blocked = 0 ;
- vio->cmd_out_enabled = 0 ;
- } ;
+ if (cli->blocked || cli->out_active)
+ return not_ready ;
/* If there is nothing pending, then can run the CLI until there is
* something to do, or runs out of input.
@@ -444,26 +594,26 @@ uty_cli_standard(vty_io vio)
* now completed, which may have wiped the pending command or changed
* the required prompt.
*/
- if (vio->cli_do == cli_do_nothing)
- vio->cli_do = uty_cli_process(vio, vio->vty->node) ;
+ if (cli->to_do == cmd_do_nothing)
+ cli->to_do = uty_cli_process(cli, cli->node) ;
else
- uty_cli_draw_this(vio, vio->vty->node) ;
+ uty_cli_draw_this(cli, cli->node) ;
- /* If have something to do, do it. */
- if (vio->cli_do != cli_do_nothing)
+ /* If have something to do, do it if we can. */
+ if (cli->to_do != cmd_do_nothing)
{
- /* Reflect immediate response */
- uty_cli_response(vio, vio->cli_do) ;
+ /* Reflect immediate response */
+ uty_cli_response(cli, cli->to_do) ;
/* If command not already in progress, dispatch this one, which may
* set the CLI blocked.
*
* Otherwise is now blocked until queued command completes.
*/
- if (!vio->cmd_in_progress)
- vio->cli_blocked = uty_cli_dispatch(vio) ;
+ if (cli->in_progress)
+ cli->blocked = true ; /* blocked waiting for previous */
else
- vio->cli_blocked = 1 ;
+ uty_cli_dispatch(cli) ; /* can dispatch the latest */
} ;
/* Use write_ready as a proxy for read_ready on the keystroke stream.
@@ -475,166 +625,164 @@ uty_cli_standard(vty_io vio)
* defensive: at worst will generate one unnecessary read_ready/write_ready
* event.
*/
- if (keystroke_stream_empty(vio->key_stream))
+ if (keystroke_stream_empty(cli->key_stream))
return read_ready ;
else
return write_ready ;
} ;
/*------------------------------------------------------------------------------
- * Dispatch the current vio->cli_do -- queueing it if necessary.
+ * Dispatch the current cli->to_do -- queueing it if necessary.
*
* Requires that are NOT blocked and NO command is queued.
*
* Expects to be on new blank line, and when returns will be on new, blank
* line.
*
- * Returns: true <=> command completed and output is pending
- * false => command has been queued and is now in progress
+ * Generally sets cli->to_do = cmd_do_nothing and clears cli->cl to empty.
*
- * Generally sets vio->cl_do = cli_do_nothing and clears vio->cl to empty.
- *
- * Can set vio->cl_do = and vio->cl to be a follow-on command.
+ * Can set cli->cl_do = and cli->cl to be a follow-on command.
*/
-static bool
-uty_cli_dispatch(vty_io vio)
+static void
+uty_cli_dispatch(vty_cli cli)
{
- qstring_t tmp ;
- enum cli_do cli_do ;
- enum cmd_return_code ret ;
+ qstring tmp ;
+ cmd_do_t to_do_now ;
- struct vty* vty = vio->vty ;
+ vty_io vio = cli->vf->vio ;
VTY_ASSERT_LOCKED() ;
- assert(!vio->cli_blocked && !vio->cmd_in_progress) ;
- /* Set vio->clx to the command about to execute.
+ /* About to dispatch a command, so must be in the following state. */
+ assert(!cli->in_progress && !cli->out_active && !cli->blocked) ;
+ assert(cli->node == vio->vty->node) ;
+
+ /* Set cli->clx to the command about to execute & pick up cli->to_do.
*
- * Clear vio->cl and vio->cl_do.
+ * Clear cli->cl and cli->to_do.
*/
- vio->cmd_in_progress = 1 ; /* => vty->buf is valid */
- vio->cmd_out_enabled = 0 ; /* => collect all output */
-
- tmp = vio->clx ; /* swap clx and cl */
- vio->clx = vio->cl ;
- vio->cl = tmp ;
+ tmp = cli->clx ; /* swap clx and cl */
+ cli->clx = cli->cl ;
+ cli->cl = tmp ;
- qs_term(&vio->clx) ; /* ensure string is terminated */
- vty->buf = qs_chars(&vio->clx) ; /* terminated command line */
- cli_do = vio->cli_do ; /* current operation */
+ to_do_now = cli->to_do ; /* current operation */
- vio->cli_do = cli_do_nothing ; /* clear */
- qs_clear(&vio->cl) ; /* set cl empty (with '\0') */
+ cli->to_do = cmd_do_nothing ; /* clear */
+ qs_clear(cli->cl) ; /* set cl empty */
- /* Reset the command output FIFO and line_control */
- assert(vio_fifo_empty(&vio->cmd_obuf)) ;
- uty_out_clear(vio) ; /* clears FIFO and line control */
+ /* Reset the command output FIFO and line_control TODO */
+//uty_out_clear(cli->vio) ; /* clears FIFO and line control */
/* Dispatch command */
- if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
- {
- /* AUTH_NODE and AUTH_ENABLE_NODE are unique */
- ret = uty_auth(vty, vty->buf, cli_do) ;
- }
+ if ((cli->node == AUTH_NODE) && (to_do_now != cmd_do_nothing))
+ to_do_now |= cmd_do_auth ;
+ else if ((cli->node == AUTH_ENABLE_NODE) && (to_do_now != cmd_do_nothing))
+ to_do_now |= cmd_do_auth_enable ;
else
{
/* All other nodes... */
- switch (cli_do)
+ switch (to_do_now)
{
- case cli_do_nothing:
- ret = CMD_SUCCESS ;
+ case cmd_do_nothing:
+ case cmd_do_ctrl_c:
+ case cmd_do_eof:
break ;
- case cli_do_command:
- ret = uty_command(vty) ;
+ case cmd_do_command:
+ to_do_now = uty_cli_command(cli) ;
break ;
- case cli_do_ctrl_c:
- ret = uty_stop_input(vty) ;
+ case cmd_do_ctrl_d:
+ zabort("invalid cmd_do_ctrl_d") ;
break ;
- case cli_do_ctrl_d:
- ret = uty_down_level(vty) ;
- break ;
+ case cmd_do_ctrl_z:
+ to_do_now = uty_cli_command(cli) ;
- case cli_do_ctrl_z:
- ret = uty_command(vty) ;
- if (ret == CMD_QUEUED)
- vio->cli_do = cli_do_ctrl_z ; /* defer the ^Z action */
+ if (to_do_now == cmd_do_nothing)
+ to_do_now = cmd_do_ctrl_z ;
else
- ret = uty_end_config(vty) ; /* do the ^Z now */
- break ;
+ cli->to_do = cmd_do_ctrl_z ; /* defer the ^Z */
- case cli_do_eof:
- ret = uty_cmd_close(vio->vty, "End") ;
break ;
default:
- zabort("unknown cli_do_xxx value") ;
+ zabort("unknown cmd_do_xxx value") ;
} ;
} ;
- if (ret == CMD_QUEUED)
+ cli->hp = cli->h_now ; /* in any event, back to the present */
+
+ if (to_do_now != cmd_do_nothing)
{
- uty_cli_draw(vio) ; /* draw the prompt */
- return false ; /* command not complete */
+ cli->in_progress = true ;
+ uty_cmd_dispatch(vio, to_do_now, cli->clx) ;
}
else
- {
- uty_cli_cmd_complete(vio, ret) ;
- return true ; /* command complete */
- } ;
+ uty_cli_draw(cli) ;
} ;
/*------------------------------------------------------------------------------
- * Queued command has completed.
+ * Enqueue command -- adding to history -- if is not empty or just comment
*
- * Note that sets write on whether there is anything in the output buffer
- * or not... write_ready will kick read_ready.
+ * This is for VTY_TERM type VTY.
+ *
+ * Returns: CMD_SUCCESS => empty command line (or just comment)
+ * CMD_WAITING => enqueued for parse and execute
*/
-extern void
-vty_queued_result(struct vty *vty, enum cmd_return_code ret)
+static cmd_do_t
+uty_cli_command(vty_cli cli)
{
- vty_io vio ;
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
- VTY_LOCK() ;
+ if (cmd_is_empty(qs_els_nn(cli->clx)))
+ return cmd_do_nothing ; /* easy when nothing to do ! */
- vio = vty->tos ;
+ /* Add not empty command to history */
+ uty_cli_hist_add(cli, cli->clx) ;
- if (!vio->closed)
- {
- uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */
+ return cmd_do_command ;
+} ;
- /* Do the command completion actions that were deferred because the
- * command was queued.
- *
- * Return of CMD_QUEUED => command was revoked before being executed.
- * However interesting that might be... frankly don't care.
- */
- uty_cli_cmd_complete(vio, ret) ;
+/*------------------------------------------------------------------------------
+ * A command has completed, and there is (or may be) some output to now
+ * write away.
+ */
+extern void
+uty_cli_out_push(vty_cli cli)
+{
+ VTY_ASSERT_LOCKED() ;
- /* Kick the socket -- to write away any outstanding output, and
- * re-enter the CLI when that's done.
- */
- uty_sock_set_readiness(&vio->sock, write_ready) ;
- }
- else
+ if (cli->more_wait)
+ return ; /* can do nothing */
+
+ if (vio_fifo_empty(cli->vf->obuf))
{
- /* If the VTY is closed, the only reason it still exists is because
- * there was cmd_in_progress.
- */
- vio->cmd_in_progress = 0 ; /* death watch will apply coup de grace */
+ assert(!cli->out_active ) ;
+ return ; /* need do nothing if is empty */
} ;
- VTY_UNLOCK() ;
-}
+ uty_cli_wipe(cli, 0) ; /* wipe any partly constructed line */
+
+ cli->out_active = true ; /* enable the output */
+
+ uty_term_set_readiness(cli->vf, write_ready) ;
+ /* kick the write side into action */
+} ;
/*------------------------------------------------------------------------------
+ * Queued command has completed.
+ *
+ * Note that sets write on whether there is anything in the output buffer
+ * or not... write_ready will kick read_ready.
+
+
* Command has completed, so:
*
* * clear cmd_in_progress
- * * set cmd_out_enabled -- so any output can now proceed
- * * set cli_blocked -- waiting for output to complete
+ * * set cmd_out_active -- so any output can now proceed
+ * * set cli_blocked -- waiting for output to complete
* * and prepare the line control for output
*
* If the return is CMD_CLOSE, then also now does the required half close.
@@ -650,33 +798,153 @@ vty_queued_result(struct vty *vty, enum cmd_return_code ret)
*
* It also means that the decision about whether there is anything to output
* is left to the output code.
+
+
+
*/
-static void
-uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret)
+extern void
+uty_cli_done_command(vty_cli cli, node_type_t node)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_cli_out_push(cli) ; /* just in case */
+
+ cli->in_progress = false ; /* command complete */
+ cli->blocked = false ; /* releases possibly blocked command */
+ cli->node = node ; /* and now in this node */
+
+ /* Reach all the way back, and get the now current node. */
+ cli->node = cli->vf->vio->vty->node ;
+
+ uty_term_set_readiness(cli->vf, write_ready) ;
+ /* kick the write side into action */
+} ;
+
+/*==============================================================================
+ * All commands are dispatched to command_queue.
+ *
+ * Some commands are VLI specific... and end up back here.
+ */
+
+/*------------------------------------------------------------------------------
+ * Authentication of vty
+ *
+ * Note that if the AUTH_NODE password fails too many times, the terminal is
+ * closed.
+ *
+ * Returns: CMD_SUCCESS -- OK, one way or another
+ * CMD_WARNING -- with error message sent to output
+ * CMD_CLOSE -- too many password failures
+ */
+extern cmd_return_code_t
+uty_cli_auth(vty_cli cli)
{
+ char *crypt (const char *, const char *);
+
+ char* passwd = NULL ;
+ bool encrypted = false ;
+ node_type_t next_node = 0 ;
+ cmd_return_code_t ret ;
+ vty_io vio ;
+ cmd_exec exec ;
+
VTY_ASSERT_LOCKED() ;
- assert(vio->cmd_in_progress && !vio->cmd_out_enabled) ;
+ vio = cli->vf->vio ;
+ exec = vio->vty->exec ;
+
+ /* Deal with the exotic terminators. */
+ switch (exec->to_do & cmd_do_mask)
+ {
+ case cmd_do_nothing:
+ case cmd_do_ctrl_c:
+ case cmd_do_ctrl_z:
+ return CMD_SUCCESS ;
+
+ case cmd_do_command:
+ break ;
+
+ case cmd_do_ctrl_d:
+ case cmd_do_eof:
+ return uty_cmd_close(vio, "End") ;
+
+ default:
+ zabort("unknown or invalid cmd_do") ;
+ } ;
+
+ /* Ordinary command dispatch -- see if password is OK.
+ *
+ * Select the password we need to check against.
+ */
+ if ((exec->to_do & ~cmd_do_mask) == cmd_do_auth)
+ {
+ passwd = host.password ;
+ encrypted = host.password_encrypted ;
+
+ if (host.advanced)
+ next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
+ else
+ next_node = VIEW_NODE;
+ }
+ else if ((exec->to_do & ~cmd_do_mask) == cmd_do_auth_enable)
+ {
+ passwd = host.enable ;
+ encrypted = host.enable_encrypted ;
+
+ next_node = ENABLE_NODE;
+ }
+ else
+ zabort("unknown to_do_value") ;
+
+ /* Check against selected password (if any) */
+ if (passwd != NULL)
+ {
+ char* candidate = qs_make_string(exec->line) ;
+
+ if (encrypted)
+ candidate = crypt(candidate, passwd) ;
+
+ if (strcmp(candidate, passwd) == 0)
+ {
+ cli->password_fail = 0 ; /* forgive any recent failures */
+ vio->vty->node = next_node;
+
+ return CMD_SUCCESS ; /* <<< SUCCESS <<<<<<<< */
+ } ;
+ } ;
- if (ret == CMD_CLOSE)
- uty_close(vio, NULL) ;
+ /* Password failed -- or none set ! */
+ cli->password_fail++ ;
- vio->cmd_in_progress = 0 ; /* command complete */
- vio->cmd_out_enabled = 1 ; /* enable the output */
- vio->cli_blocked = 1 ; /* now blocked waiting for output */
+ ret = CMD_SUCCESS ; /* OK so far */
- vio->vty->buf = NULL ; /* finished with command line */
+ if (cli->password_fail >= 3)
+ {
+ if ((exec->to_do & ~cmd_do_mask) == cmd_do_auth)
+ {
+ ret = uty_cmd_close(vio, "Bad passwords, too many failures!") ;
+ }
+ else
+ {
+ /* AUTH_ENABLE_NODE */
+ cli->password_fail = 0 ; /* allow further attempts */
+ uty_out(vio, "%% Bad enable passwords, too many failures!\n") ;
+ vio->vty->node = host.restricted_mode ? RESTRICTED_NODE
+ : VIEW_NODE ;
+ ret = CMD_WARNING ;
+ } ;
+ } ;
- uty_cmd_output_start(vio) ; /* reset line control (belt & braces) */
+ return ret ;
} ;
/*==============================================================================
* The "--more--" CLI
*
- * While command output is being cleared from its FIFO, the CLI is cli_blocked.
+ * While command output is being cleared from its FIFO, the CLI is blocked.
*
* When the output side signals that "--more--" is required, it sets the
- * cli_more_wait flag and clears the cmd_out_enabled flag.
+ * more_wait flag and clears the out_active flag.
*
* The first stage of handling "--more--" is to suck the input dry, so that
* (as far as is reasonably possible) does not steal a keystroke as the
@@ -691,19 +959,20 @@ uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret)
* Outputs the new prompt line.
*/
extern void
-uty_cli_enter_more_wait(vty_io vio)
+uty_cli_enter_more_wait(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- assert(vio->cli_blocked && vio->cmd_out_enabled && !vio->cli_more_wait) ;
+ assert(cli->out_active && !cli->more_wait) ;
- uty_cli_wipe(vio, 0) ; /* make absolutely sure that command line is
+ uty_cli_wipe(cli, 0) ; /* make absolutely sure that command line is
wiped before change the CLI state */
- vio->cmd_out_enabled = 0 ; /* stop output pro tem */
- vio->cli_more_wait = 1 ; /* new state */
+ cli->out_active = false ; /* stop output pro tem */
+ cli->more_wait = true ; /* new state */
+ cli->more_active = true ; /* drawing the "--more--" */
- uty_cli_draw(vio) ; /* draw the "--more--" */
+ uty_cli_draw(cli) ; /* draw the "--more--" */
} ;
/*------------------------------------------------------------------------------
@@ -717,27 +986,26 @@ uty_cli_enter_more_wait(vty_io vio)
* possible that the '--more--' prompt is yet to be completely written away,
* so:
*
- * * assert that is either: !vio->cli_blocked (most of the time it will)
- * or: !vio_fifo_empty(&vio->cli_obuf)
+ * * assert that is either: !cli->blocked (most of the time it will)
+ * or: !vio_fifo_empty(cli->cbuf)
*
* * note that can wipe the prompt even though it hasn't been fully
* written away yet. (The effect is to append the wipe action to the
* cli_obuf !)
*/
extern void
-uty_cli_exit_more_wait(vty_io vio)
+uty_cli_exit_more_wait(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- assert( (!vio->cli_blocked || !vio_fifo_empty(&vio->cli_obuf))
- && !vio->cmd_out_enabled && vio->cli_more_wait) ;
+ assert(cli->more_wait) ;
- uty_cli_wipe(vio, 0) ; /* wipe the prompt ('--more--')
+ uty_cli_wipe(cli, 0) ; /* wipe the prompt ('--more--')
before changing the CLI state */
- vio->cli_blocked = 1 ; /* back to blocked waiting for output */
- vio->cli_more_wait = 0 ; /* exit more_wait */
- vio->cmd_out_enabled = 1 ; /* re-enable output */
+ cli->more_wait = false ; /* exit more_wait */
+ cli->more_active = false ; /* tidy */
+ cli->out_active = true ; /* re-enable output */
} ;
/*------------------------------------------------------------------------------
@@ -754,35 +1022,37 @@ uty_cli_exit_more_wait(vty_io vio)
* now_ready -- just left cli_more_wait
* not_ready -- otherwise
*/
-static enum vty_readiness
-uty_cli_more_wait(vty_io vio)
+static vty_readiness_t
+uty_cli_more_wait(vty_cli cli)
{
struct keystroke steal ;
VTY_ASSERT_LOCKED() ;
+ assert(cli->more_wait) ; /* must be in more_wait state ! */
+
/* Deal with the first stage of "--more--" */
- if (vio->cli_blocked)
+ if (cli->more_active)
{
int get ;
/* If the CLI buffer is not yet empty, then is waiting for the
* initial prompt to clear, so nothing to be done here.
*/
- if (!vio_fifo_empty(&vio->cli_obuf))
- return not_ready ;
+ if (!vio_fifo_empty(cli->cbuf))
+ return write_ready ;
- vio->cli_blocked = 0 ;
+ cli->more_active = false ;
/* empty the input buffer into the keystroke stream */
do
{
- get = uty_read(vio, NULL) ;
+ get = uty_term_read(cli->vf, NULL) ;
} while (get > 0) ;
} ;
- /* Go through the "--more--" process, unless no longer write_open (!) */
- if (vio->sock.write_open)
+ /* Go through the "--more--" process, unless closing */
+ if (cli->vf->vout_state == vf_open)
{
/* The read fetches a reasonable lump from the I/O -- so if there
* is a complete keystroke available, expect to get it.
@@ -791,15 +1061,18 @@ uty_cli_more_wait(vty_io vio)
*
* If has hit EOF (or error etc), returns knull_eof.
*/
- uty_read(vio, &steal) ;
+ uty_term_read(cli->vf, &steal) ;
/* If nothing stolen, make sure prompt is drawn and wait for more
* input.
*/
if ((steal.type == ks_null) && (steal.value != knull_eof))
{
- if (uty_cli_draw_if_required(vio)) /* "--more--" if req. */
- return write_ready ;
+ if (uty_cli_draw_if_required(cli)) /* "--more--" if req. */
+ {
+ cli->more_active = true ; /* written "--more--" again */
+ return write_ready ;
+ }
else
return read_ready ;
} ;
@@ -812,7 +1085,7 @@ uty_cli_more_wait(vty_io vio)
case CONTROL('C'):
case 'q':
case 'Q':
- uty_out_clear(vio) ;
+ uty_out_clear(cli->vf->vio) ;
break;
default: /* everything else, thrown away */
@@ -828,7 +1101,7 @@ uty_cli_more_wait(vty_io vio)
* Return write_ready to tidy up the screen and, unless cleared, write
* some more.
*/
- uty_cli_exit_more_wait(vio) ;
+ uty_cli_exit_more_wait(cli) ;
return now_ready ;
} ;
@@ -838,18 +1111,17 @@ uty_cli_more_wait(vty_io vio)
*
* This is buffered separately from the general (command) VTY output above.
*
- * Has a dedicated buffer in the struct vty, which is flushed regularly during
- * command processing.
+ * Has a dedicated buffer in the struct vty_cli, which is flushed regularly
+ * during command processing.
*
* It is expected that can flush straight to the file, since this is running at
* CLI speed. However, if the CLI is being driven by something other than a
* keyboard, or "monitor" output has filled the buffers, then may need to
* have intermediate buffering.
*
- * No actual I/O takes place here-- all "output" is to vio->cli_obuf
- * and/or vio->cli_ex_obuf
+ * No actual I/O takes place here-- all "output" is to cli->cbuf
*
- * The "cli_echo" functions discard the output if vio->cli_echo_suppress != 0.
+ * The "cli_echo" functions discard the output if cli->echo_suppress.
* This is used while passwords are entered and to allow command line changes
* to be made while the line is not visible.
*/
@@ -857,8 +1129,9 @@ uty_cli_more_wait(vty_io vio)
enum { cli_rep_count = 32 } ;
typedef const char cli_rep_char[cli_rep_count] ;
+typedef const char cli_rep[] ;
-static const char telnet_backspaces[] =
+static cli_rep telnet_backspaces =
{ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
@@ -866,35 +1139,39 @@ static const char telnet_backspaces[] =
} ;
CONFIRM(sizeof(telnet_backspaces) == sizeof(cli_rep_char)) ;
-static const char telnet_spaces[] =
- { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
- } ;
-CONFIRM(sizeof(telnet_spaces) == sizeof(cli_rep_char)) ;
+/* 12345678901234567890123456789012 */
+static cli_rep telnet_spaces = " " ;
+static cli_rep telnet_dots = "................................" ;
+
+CONFIRM(sizeof(telnet_spaces) == (sizeof(cli_rep_char) + 1)) ;
+CONFIRM(sizeof(telnet_dots) == (sizeof(cli_rep_char) + 1)) ;
static const char* telnet_newline = "\r\n" ;
-static void uty_cli_write(vty_io vio, const char *this, int len) ;
-static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ;
+static void uty_cli_write_n(vty_cli cli, cli_rep_char chars, int n) ;
/*------------------------------------------------------------------------------
* CLI VTY output -- cf fprintf()
*/
-static void
-uty_cli_out(vty_io vio, const char *format, ...)
+extern void
+uty_cli_out(vty_cli cli, const char *format, ...)
{
+ va_list args ;
+
VTY_ASSERT_LOCKED() ;
- if (vio->sock.write_open)
- {
- va_list args ;
+ va_start (args, format);
+ vio_fifo_vprintf(cli->cbuf, format, args) ;
+ va_end(args);
+} ;
- va_start (args, format);
- vio_fifo_vprintf(&vio->cli_obuf, format, args) ;
- va_end(args);
- } ;
+/*------------------------------------------------------------------------------
+ * Completely empty the cli command buffer
+ */
+extern void
+uty_cli_out_clear(vty_cli cli)
+{
+ vio_fifo_clear(cli->cbuf, true) ;
} ;
/*------------------------------------------------------------------------------
@@ -903,14 +1180,12 @@ uty_cli_out(vty_io vio, const char *format, ...)
* Do nothing if echo suppressed (eg in AUTH_NODE) or not write_open
*/
static void
-uty_cli_echo(vty_io vio, const char *this, size_t len)
+uty_cli_echo(vty_cli cli, const char *this, size_t len)
{
VTY_ASSERT_LOCKED() ;
- if (vio->cli_echo_suppress || !vio->sock.write_open)
- return ;
-
- uty_cli_write(vio, this, len) ;
+ if (!cli->echo_suppress)
+ uty_cli_write(cli, this, len) ;
}
/*------------------------------------------------------------------------------
@@ -919,33 +1194,30 @@ uty_cli_echo(vty_io vio, const char *this, size_t len)
* Do nothing if echo suppressed (eg in AUTH_NODE)
*/
static void
-uty_cli_echo_n(vty_io vio, cli_rep_char chars, int n)
+uty_cli_echo_n(vty_cli cli, cli_rep_char chars, int n)
{
VTY_ASSERT_LOCKED() ;
- if (vio->cli_echo_suppress || !vio->sock.write_open)
- return ;
-
- uty_cli_write_n(vio, chars, n) ;
+ if (!cli->echo_suppress)
+ uty_cli_write_n(cli, chars, n) ;
}
/*------------------------------------------------------------------------------
* CLI VTY output -- cf write()
*/
-inline static void
-uty_cli_write(vty_io vio, const char *this, int len)
+extern void
+uty_cli_write(vty_cli cli, const char *this, int len)
{
VTY_ASSERT_LOCKED() ;
- if (vio->sock.write_open)
- vio_fifo_put(&vio->cli_obuf, this, len) ;
+ vio_fifo_put_bytes(cli->cbuf, this, len) ;
} ;
/*------------------------------------------------------------------------------
* CLI VTY output -- write 'n' characters using a cli_rep_char string
*/
static void
-uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
+uty_cli_write_n(vty_cli cli, cli_rep_char chars, int n)
{
int len ;
@@ -954,7 +1226,7 @@ uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
{
if (n < len)
len = n ;
- uty_cli_write(vio, chars, len) ;
+ uty_cli_write(cli, chars, len) ;
n -= len ;
} ;
} ;
@@ -965,13 +1237,13 @@ uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
* Returns length of string.
*/
static int
-uty_cli_write_s(vty_io vio, const char *str)
+uty_cli_write_s(vty_cli cli, const char *str)
{
int len ;
len = strlen(str) ;
if (len != 0)
- uty_cli_write(vio, str, len) ;
+ uty_cli_write(cli, str, len) ;
return len ;
} ;
@@ -985,12 +1257,12 @@ uty_cli_write_s(vty_io vio, const char *str)
*
* Clears the cli_drawn and the cli_dirty flags.
*/
-static void
-uty_cli_out_newline(vty_io vio)
+extern void
+uty_cli_out_newline(vty_cli cli)
{
- uty_cli_write(vio, telnet_newline, 2) ;
- vio->cli_drawn = 0 ;
- vio->cli_dirty = 0 ;
+ uty_cli_write(cli, telnet_newline, 2) ;
+ cli->drawn = false ;
+ cli->dirty = false ;
} ;
/*------------------------------------------------------------------------------
@@ -1000,23 +1272,23 @@ uty_cli_out_newline(vty_io vio)
* 'n' > 0, wipes characters forwards, leaving cursor where it is
*/
static void
-uty_cli_out_wipe_n(vty_io vio, int n)
+uty_cli_out_wipe_n(vty_cli cli, int n)
{
if (n < 0)
{
n = abs(n) ;
- uty_cli_write_n(vio, telnet_backspaces, n);
+ uty_cli_write_n(cli, telnet_backspaces, n);
} ;
if (n > 0)
{
- uty_cli_write_n(vio, telnet_spaces, n) ;
- uty_cli_write_n(vio, telnet_backspaces, n) ;
+ uty_cli_write_n(cli, telnet_spaces, n) ;
+ uty_cli_write_n(cli, telnet_backspaces, n) ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Send response to the given cli_do
+ * Send response to the given cmd_do
*
* If no command is in progress, then will send newline to signal that the
* command is about to be dispatched.
@@ -1024,113 +1296,126 @@ uty_cli_out_wipe_n(vty_io vio, int n)
* If command is in progress, then leaves cursor on '^' to signal that is now
* waiting for previous command to complete.
*/
-static const char* cli_response [2][cli_do_count] =
+static const char* cli_response [2][cmd_do_count] =
{
{ /* when not waiting for previous command to complete */
- [cli_do_command] = "",
- [cli_do_ctrl_c] = "^C",
- [cli_do_ctrl_d] = "^D",
- [cli_do_ctrl_z] = "^Z",
- [cli_do_eof] = "^*"
+ [cmd_do_command] = "",
+ [cmd_do_ctrl_c] = "^C",
+ [cmd_do_ctrl_d] = "^D",
+ [cmd_do_ctrl_z] = "^Z",
+ [cmd_do_eof] = "^*"
},
{ /* when waiting for a previous command to complete */
- [cli_do_command] = "^",
- [cli_do_ctrl_c] = "^C",
- [cli_do_ctrl_d] = "^D",
- [cli_do_ctrl_z] = "^Z",
- [cli_do_eof] = "^*"
+ [cmd_do_command] = "^",
+ [cmd_do_ctrl_c] = "^C",
+ [cmd_do_ctrl_d] = "^D",
+ [cmd_do_ctrl_z] = "^Z",
+ [cmd_do_eof] = "^*"
}
} ;
static void
-uty_cli_response(vty_io vio, enum cli_do cli_do)
+uty_cli_response(vty_cli cli, cmd_do_t to_do)
{
const char* str ;
int len ;
- if ((cli_do == cli_do_nothing) || (vio->half_closed))
+ if ((to_do == cmd_do_nothing) || (cli->vf->vin_state != vf_open))
return ;
- str = (cli_do < cli_do_count)
- ? cli_response[vio->cmd_in_progress ? 1 : 0][cli_do] : NULL ;
+ str = (to_do < cmd_do_count)
+ ? cli_response[cli->in_progress ? 1 : 0][to_do] : NULL ;
assert(str != NULL) ;
- len = uty_cli_write_s(vio, str) ;
+ len = uty_cli_write_s(cli, str) ;
- if (vio->cmd_in_progress)
+ if (cli->in_progress)
{
- vio->cli_extra_len = len ;
- uty_cli_write_n(vio, telnet_backspaces, len) ;
+ cli->extra_len = len ;
+ uty_cli_write_n(cli, telnet_backspaces, len) ;
}
else
{
- uty_cli_out_newline(vio) ;
+ uty_cli_out_newline(cli) ;
} ;
} ;
/*------------------------------------------------------------------------------
* Send various messages with trailing newline.
*/
-
+#if 0
static void
-uty_cli_out_CMD_ERR_AMBIGUOUS(vty_io vio)
+uty_cli_out_CMD_ERR_AMBIGUOUS(vty_cli cli)
{
- uty_cli_write_s(vio, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
- uty_cli_out_newline(vio) ;
+ uty_cli_write_s(cli, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
+ uty_cli_out_newline(cli) ;
} ;
static void
-uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio)
+uty_cli_out_CMD_ERR_NO_MATCH(vty_cli cli)
{
- uty_cli_write_s(vio, "% " MSG_CMD_ERR_NO_MATCH ".") ;
- uty_cli_out_newline(vio) ;
+ uty_cli_write_s(cli, "% " MSG_CMD_ERR_NO_MATCH ".") ;
+ uty_cli_out_newline(cli) ;
} ;
-
+#endif
/*==============================================================================
* Command line draw and wipe
*/
/*------------------------------------------------------------------------------
+ * Current prompt length
+ */
+extern ulen
+uty_cli_prompt_len(vty_cli cli)
+{
+ return cli->prompt_len ;
+} ;
+
+/*------------------------------------------------------------------------------
* Wipe the current console line -- if any.
+ *
+ * If it is known that some characters will immediately be written, then
+ * passing the number of them as "len" will reduce the number of characters
+ * that have to be sent.
*/
-static void
-uty_cli_wipe(vty_io vio, int len)
+extern void
+uty_cli_wipe(vty_cli cli, int len)
{
int a ;
int b ;
- if (!vio->cli_drawn)
+ if (!cli->drawn)
return ; /* quit if already wiped */
- assert(vio->cl.cp <= vio->cl.len) ;
+ assert(qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) ;
/* Establish how much ahead and how much behind the cursor */
- a = vio->cli_extra_len ;
- b = vio->cli_prompt_len ;
+ a = cli->extra_len ;
+ b = cli->prompt_len ;
- if (!vio->cli_echo_suppress && !vio->cli_more_wait)
+ if (!cli->echo_suppress && !cli->more_wait)
{
- a += vio->cl.len - vio->cl.cp ;
- b += vio->cl.cp ;
+ a += qs_len_nn(cli->cl) - qs_cp_nn(cli->cl) ;
+ b += qs_cp_nn(cli->cl) ;
} ;
/* Wipe anything ahead of the current position and ahead of new len */
if ((a + b) > len)
- uty_cli_out_wipe_n(vio, +a) ;
+ uty_cli_out_wipe_n(cli, +a) ;
/* Wipe anything behind current position, but ahead of new len */
if (b > len)
{
- uty_cli_out_wipe_n(vio, -(b - len)) ;
+ uty_cli_out_wipe_n(cli, -(b - len)) ;
b = len ; /* moved the cursor back */
} ;
/* Back to the beginning of the line */
- uty_cli_write_n(vio, telnet_backspaces, b) ;
+ uty_cli_write_n(cli, telnet_backspaces, b) ;
/* Nothing there any more */
- vio->cli_drawn = 0 ;
- vio->cli_dirty = 0 ;
+ cli->drawn = false ;
+ cli->dirty = false ;
} ;
/*------------------------------------------------------------------------------
@@ -1140,12 +1425,12 @@ uty_cli_wipe(vty_io vio, int len)
* See uty_cli_draw().
*/
extern bool
-uty_cli_draw_if_required(vty_io vio)
+uty_cli_draw_if_required(vty_cli cli)
{
- if (vio->cli_drawn)
+ if (cli->drawn)
return false ;
- uty_cli_draw(vio) ;
+ uty_cli_draw(cli) ;
return true ;
} ;
@@ -1156,9 +1441,9 @@ uty_cli_draw_if_required(vty_io vio)
* See uty_cli_draw_this()
*/
static void
-uty_cli_draw(vty_io vio)
+uty_cli_draw(vty_cli cli)
{
- uty_cli_draw_this(vio, vio->vty->node) ;
+ uty_cli_draw_this(cli, cli->node) ;
} ;
/*------------------------------------------------------------------------------
@@ -1171,7 +1456,7 @@ uty_cli_draw(vty_io vio)
*
* Draws prompt according to the given 'node', except:
*
- * * if is half_closed, draw nothing -- wipes the current line
+ * * if is closing, draw nothing -- wipes the current line
*
* * if is cli_more_wait, draw the "--more--" prompt
*
@@ -1187,34 +1472,34 @@ uty_cli_draw(vty_io vio)
* cli_echo_suppress = (AUTH_NODE or AUTH_ENABLE_NODE)
*/
static void
-uty_cli_draw_this(vty_io vio, enum node_type node)
+uty_cli_draw_this(vty_cli cli, enum node_type node)
{
const char* prompt ;
size_t l_len ;
int p_len ;
- if (vio->cli_dirty)
- uty_cli_out_newline(vio) ; /* clears cli_dirty and cli_drawn */
+ if (cli->dirty)
+ uty_cli_out_newline(cli) ; /* clears cli_dirty and cli_drawn */
/* Sort out what the prompt is. */
- if (vio->half_closed)
+ if (cli->vf->vin_state != vf_open)
{
prompt = "" ;
p_len = 0 ;
l_len = 0 ;
}
- else if (vio->cli_more_wait)
+ else if (cli->more_wait)
{
prompt = "--more--" ;
p_len = strlen(prompt) ;
l_len = 0 ;
}
- else if (vio->cmd_in_progress)
+ else if (cli->in_progress)
{
/* If there is a queued command, the prompt is a minimal affair. */
prompt = "~ " ;
p_len = strlen(prompt) ;
- l_len = vio->cl.len ;
+ l_len = qs_len_nn(cli->cl) ;
}
else
{
@@ -1225,49 +1510,46 @@ uty_cli_draw_this(vty_io vio, enum node_type node)
*
* If the host name changes, the cli_prompt_set flag is cleared.
*/
- if (!vio->cli_prompt_set || (node != vio->cli_prompt_node))
+ if ((node != cli->prompt_node) || (host.name_gen != cli->prompt_gen))
{
const char* prompt ;
- if (vty_host_name == NULL)
- uty_check_host_name() ; /* should never be required */
-
prompt = cmd_prompt(node) ;
if (prompt == NULL)
{
- zlog_err("vty %s has node %d", uty_get_name(vio), node) ;
+ zlog_err("vty %s has node %d", uty_get_name(cli->vf->vio), node) ;
prompt = "%s ???: " ;
} ;
- qs_printf(&vio->cli_prompt_for_node, prompt, vty_host_name);
+ qs_printf(cli->prompt_for_node, prompt, host.name);
- vio->cli_prompt_node = node ;
- vio->cli_prompt_set = 1 ;
+ cli->prompt_node = node ;
+ cli->prompt_gen = host.name_gen ;
} ;
- prompt = vio->cli_prompt_for_node.body ;
- p_len = vio->cli_prompt_for_node.len ;
- l_len = vio->cl.len ;
+ prompt = qs_char(cli->prompt_for_node) ;
+ p_len = qs_len(cli->prompt_for_node) ;
+ l_len = qs_len_nn(cli->cl) ;
} ;
/* Now, if line is currently drawn, time to wipe it */
- if (vio->cli_drawn)
- uty_cli_wipe(vio, p_len + l_len) ;
+ if (cli->drawn)
+ uty_cli_wipe(cli, p_len + l_len) ;
/* Set about writing the prompt and the line */
- vio->cli_drawn = 1 ;
- vio->cli_extra_len = 0 ;
- vio->cli_echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+ cli->drawn = true ;
+ cli->extra_len = false ;
+ cli->echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
- vio->cli_prompt_len = p_len ;
+ cli->prompt_len = p_len ;
- uty_cli_write(vio, prompt, p_len) ;
+ uty_cli_write(cli, prompt, p_len) ;
if (l_len != 0)
{
- uty_cli_write(vio, qs_chars(&vio->cl), l_len) ;
- if (vio->cl.cp < l_len)
- uty_cli_write_n(vio, telnet_backspaces, l_len - vio->cl.cp) ;
+ uty_cli_write(cli, qs_char(cli->cl), l_len) ;
+ if (qs_cp_nn(cli->cl) < l_len)
+ uty_cli_write_n(cli, telnet_backspaces, l_len - qs_cp_nn(cli->cl)) ;
} ;
} ;
@@ -1298,11 +1580,11 @@ uty_cli_draw_this(vty_io vio, enum node_type node)
* Wipe any existing command line.
*/
extern void
-uty_cli_pre_monitor(vty_io vio, size_t len)
+uty_cli_pre_monitor(vty_cli cli, size_t len)
{
VTY_ASSERT_LOCKED() ;
- uty_cli_wipe(vio, len) ;
+ uty_cli_wipe(cli, len) ;
} ;
/*------------------------------------------------------------------------------
@@ -1317,16 +1599,16 @@ uty_cli_pre_monitor(vty_io vio, size_t len)
* > 0 => rump not empty or some command line stuff written
*/
extern int
-uty_cli_post_monitor(vty_io vio, const char* buf, size_t len)
+uty_cli_post_monitor(vty_cli cli, const char* buf, size_t len)
{
VTY_ASSERT_LOCKED() ;
if (len != 0)
- uty_cli_write(vio, buf, len) ;
+ uty_cli_write(cli, buf, len) ;
- if (vio->cli_more_wait || (vio->cl.len != 0))
+ if (cli->more_wait || (qs_len_nn(cli->cl) != 0))
{
- uty_cli_draw(vio) ;
+ uty_cli_draw(cli) ;
++len ;
} ;
@@ -1337,48 +1619,52 @@ uty_cli_post_monitor(vty_io vio, const char* buf, size_t len)
* Command line processing loop
*/
-static bool uty_telnet_command(vty_io vio, keystroke stroke, bool callback) ;
-static int uty_cli_insert (vty_io vio, const char* chars, int n) ;
-static int uty_cli_overwrite (vty_io vio, char* chars, int n) ;
-static int uty_cli_word_overwrite (vty_io vio, char *str) ;
-static int uty_cli_forwards(vty_io vio, int n) ;
-static int uty_cli_backwards(vty_io vio, int n) ;
-static int uty_cli_del_forwards(vty_io vio, int n) ;
-static int uty_cli_del_backwards(vty_io vio, int n) ;
-static int uty_cli_bol (vty_io vio) ;
-static int uty_cli_eol (vty_io vio) ;
-static int uty_cli_word_forwards_delta(vty_io vio) ;
-static int uty_cli_word_forwards(vty_io vio) ;
-static int uty_cli_word_backwards_delta(vty_io vio, int eat_spaces) ;
-static int uty_cli_word_backwards_pure (vty_io vio) ;
-static int uty_cli_word_backwards (vty_io vio) ;
-static int uty_cli_del_word_forwards(vty_io vio) ;
-static int uty_cli_del_word_backwards(vty_io vio) ;
-static int uty_cli_del_to_eol (vty_io vio) ;
-static int uty_cli_clear_line(vty_io vio) ;
-static int uty_cli_transpose_chars(vty_io vio) ;
-static void uty_cli_history_use(vty_io vio, int step) ;
-static void uty_cli_next_line(vty_io vio) ;
-static void uty_cli_previous_line (vty_io vio) ;
-static void uty_cli_complete_command (vty_io vio, enum node_type node) ;
-static void uty_cli_describe_command (vty_io vio, enum node_type node) ;
+static int uty_cli_insert (vty_cli cli, const char* chars, int n) ;
+static int uty_cli_overwrite (vty_cli cli, char* chars, int n) ;
+//static int uty_cli_word_overwrite (vty_cli cli, char *str) ;
+static int uty_cli_forwards(vty_cli cli, int n) ;
+static int uty_cli_backwards(vty_cli cli, int n) ;
+static int uty_cli_del_forwards(vty_cli cli, int n) ;
+static int uty_cli_del_backwards(vty_cli cli, int n) ;
+static int uty_cli_bol (vty_cli cli) ;
+static int uty_cli_eol (vty_cli cli) ;
+static int uty_cli_word_forwards_delta(vty_cli cli) ;
+static int uty_cli_word_forwards(vty_cli cli) ;
+static int uty_cli_word_backwards_delta(vty_cli cli, int eat_spaces) ;
+//static int uty_cli_word_backwards_pure (vty_cli cli) ;
+static int uty_cli_word_backwards (vty_cli cli) ;
+static int uty_cli_del_word_forwards(vty_cli cli) ;
+static int uty_cli_del_word_backwards(vty_cli cli) ;
+static int uty_cli_del_to_eol (vty_cli cli) ;
+static int uty_cli_clear_line(vty_cli cli) ;
+static int uty_cli_transpose_chars(vty_cli cli) ;
+static void uty_cli_hist_use(vty_cli cli, int step) ;
+static void uty_cli_hist_next(vty_cli cli) ;
+static void uty_cli_hist_previous(vty_cli cli) ;
+static void uty_cli_complete_command (vty_cli cli, node_type_t node) ;
+static void uty_cli_describe_command (vty_cli cli, node_type_t node) ;
/*------------------------------------------------------------------------------
* Fetch next keystroke, reading from the file if required.
*/
-static inline bool
-uty_cli_get_keystroke(vty_io vio, keystroke stroke)
+static bool
+uty_cli_get_keystroke(vty_cli cli, keystroke stroke)
{
- if (keystroke_get(vio->key_stream, stroke))
- return 1 ;
+ int got ;
+
+ do
+ {
+ if (keystroke_get(cli->key_stream, stroke))
+ return true ;
- uty_read(vio, NULL) ; /* not stealing */
+ got = uty_term_read(cli->vf, NULL) ; /* not stealing */
+ } while (got > 0) ;
- return keystroke_get(vio->key_stream, stroke) ;
+ return false ;
} ;
/*------------------------------------------------------------------------------
- * Process keystrokes until run out of input, or get something to cli_do.
+ * Process keystrokes until run out of input, or get something to cmd_do.
*
* If required, draw the prompt and command line.
*
@@ -1388,30 +1674,30 @@ uty_cli_get_keystroke(vty_io vio, keystroke stroke)
* Processes the contents of the keystroke stream. If exhausts that, will set
* ready to read and return. (To give some "sharing".)
*
- * Returns: cli_do_xxxx
+ * Returns: cmd_do_xxxx
*
* When returns the cl is '\0' terminated.
*/
-static enum cli_do
-uty_cli_process(vty_io vio, enum node_type node)
+static cmd_do_t
+uty_cli_process(vty_cli cli, node_type_t node)
{
- struct keystroke stroke ;
- uint8_t u ;
- int auth ;
- enum cli_do ret ;
+ struct keystroke stroke ;
+ uint8_t u ;
+ bool auth ;
+ cmd_do_t to_do ;
auth = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
/* Now process as much as possible of what there is */
- ret = cli_do_nothing ;
- while (ret == cli_do_nothing)
+ to_do = cmd_do_nothing ;
+ while (to_do == cmd_do_nothing)
{
- if (!vio->cli_drawn)
- uty_cli_draw_this(vio, node) ;
+ if (!cli->drawn)
+ uty_cli_draw_this(cli, node) ;
- if (!uty_cli_get_keystroke(vio, &stroke))
+ if (!uty_cli_get_keystroke(cli, &stroke))
{
- ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ;
+ to_do = (stroke.value == knull_eof) ? cmd_do_eof : cmd_do_nothing ;
break ;
} ;
@@ -1431,87 +1717,91 @@ uty_cli_process(vty_io vio, enum node_type node)
switch (stroke.value)
{
case CONTROL('A'):
- uty_cli_bol (vio);
+ uty_cli_bol (cli);
break;
case CONTROL('B'):
- uty_cli_backwards(vio, 1);
+ uty_cli_backwards(cli, 1);
break;
case CONTROL('C'):
- ret = cli_do_ctrl_c ; /* Exit on ^C ..................*/
+ to_do = cmd_do_ctrl_c ; /* Exit on ^C ..................*/
break ;
case CONTROL('D'):
- if (vio->cl.len == 0) /* if at start of empty line */
- ret = cli_do_ctrl_d ; /* Exit on ^D ..................*/
+ if (auth)
+ to_do = cmd_do_ctrl_d ; /* Exit on ^D ..................*/
else
- uty_cli_del_forwards(vio, 1);
+ uty_cli_del_forwards(cli, 1);
break;
case CONTROL('E'):
- uty_cli_eol (vio);
+ uty_cli_eol (cli);
break;
case CONTROL('F'):
- uty_cli_forwards(vio, 1);
+ uty_cli_forwards(cli, 1);
break;
case CONTROL('H'):
case 0x7f:
- uty_cli_del_backwards(vio, 1);
+ uty_cli_del_backwards(cli, 1);
break;
case CONTROL('K'):
- uty_cli_del_to_eol (vio);
+ uty_cli_del_to_eol (cli);
break;
case CONTROL('N'):
- uty_cli_next_line (vio);
+ uty_cli_hist_next (cli);
break;
case CONTROL('P'):
- uty_cli_previous_line (vio);
+ uty_cli_hist_previous(cli);
break;
case CONTROL('T'):
- uty_cli_transpose_chars (vio);
+ uty_cli_transpose_chars (cli);
break;
case CONTROL('U'):
- uty_cli_clear_line(vio);
+ uty_cli_clear_line(cli);
break;
case CONTROL('W'):
- uty_cli_del_word_backwards (vio);
+ uty_cli_del_word_backwards (cli);
break;
case CONTROL('Z'):
- ret = cli_do_ctrl_z ; /* Exit on ^Z ..................*/
+ to_do = cmd_do_ctrl_z ; /* Exit on ^Z ..................*/
break;
case '\n':
case '\r':
- ret = cli_do_command ; /* Exit on CR or LF.............*/
+ to_do = cmd_do_command ; /* Exit on CR or LF.............*/
break ;
case '\t':
if (auth)
- break ;
+ uty_cli_insert (cli, " ", 1) ;
+ else if (cmd_token_position(cli->parsed, cli->cl))
+ uty_cli_insert (cli, " ", 1) ;
else
- uty_cli_complete_command (vio, node);
+ uty_cli_complete_command (cli, node);
break;
case '?':
- if (auth)
- uty_cli_insert (vio, (char*)&u, 1);
+ if (auth)
+ uty_cli_insert (cli, (char*)&u, 1) ;
+ else if (cmd_token_position(cli->parsed, cli->cl))
+ uty_cli_insert (cli, (char*)&u, 1) ;
else
- uty_cli_describe_command (vio, node);
+ uty_cli_describe_command (cli, node);
break;
default:
if ((stroke.value >= 0x20) && (stroke.value < 0x7F))
- uty_cli_insert (vio, (char*)&u, 1) ;
+ uty_cli_insert (cli, (char*)&u, 1) ;
break;
}
break ;
@@ -1521,20 +1811,20 @@ uty_cli_process(vty_io vio, enum node_type node)
switch (stroke.value)
{
case 'b':
- uty_cli_word_backwards (vio);
+ uty_cli_word_backwards (cli);
break;
case 'f':
- uty_cli_word_forwards (vio);
+ uty_cli_word_forwards (cli);
break;
case 'd':
- uty_cli_del_word_forwards (vio);
+ uty_cli_del_word_forwards (cli);
break;
case CONTROL('H'):
case 0x7f:
- uty_cli_del_word_backwards (vio);
+ uty_cli_del_word_backwards (cli);
break;
default:
@@ -1549,20 +1839,20 @@ uty_cli_process(vty_io vio, enum node_type node)
switch (stroke.value)
{
- case ('A'):
- uty_cli_previous_line (vio);
+ case ('A'): /* up arrow */
+ uty_cli_hist_previous(cli);
break;
- case ('B'):
- uty_cli_next_line (vio);
+ case ('B'): /* down arrow */
+ uty_cli_hist_next (cli);
break;
- case ('C'):
- uty_cli_forwards(vio, 1);
+ case ('C'): /* right arrow */
+ uty_cli_forwards(cli, 1);
break;
- case ('D'):
- uty_cli_backwards(vio, 1);
+ case ('D'): /* left arrow */
+ uty_cli_backwards(cli, 1);
break;
default:
@@ -1572,7 +1862,7 @@ uty_cli_process(vty_io vio, enum node_type node)
/* Telnet Command ----------------------------------------------------*/
case ks_iac:
- uty_telnet_command(vio, &stroke, false) ;
+ uty_telnet_command(cli->vf, &stroke, false) ;
break ;
/* Unknown -----------------------------------------------------------*/
@@ -1584,12 +1874,10 @@ uty_cli_process(vty_io vio, enum node_type node)
/* Tidy up and return where got to. */
- if (ret != cli_do_nothing)
- uty_cli_eol (vio) ; /* go to the end of the line */
-
- qs_term(&vio->cl) ; /* add '\0' */
+ if (to_do != cmd_do_nothing)
+ uty_cli_eol (cli) ; /* go to the end of the line */
- return ret ;
+ return to_do ;
} ;
/*==============================================================================
@@ -1602,25 +1890,25 @@ uty_cli_process(vty_io vio, enum node_type node)
* Returns number of characters inserted -- ie 'n'
*/
static int
-uty_cli_insert (vty_io vio, const char* chars, int n)
+uty_cli_insert (vty_cli cli, const char* chars, int n)
{
int after ;
VTY_ASSERT_LOCKED() ;
- assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+ assert((qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) && (n >= 0)) ;
if (n <= 0)
return n ; /* avoid trouble */
- after = qs_insert(&vio->cl, chars, n) ;
+ after = qs_insert(cli->cl, chars, n) ;
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after + n) ;
+ uty_cli_echo(cli, qs_cp_char(cli->cl), after) ;
- if (after != 0)
- uty_cli_echo_n(vio, telnet_backspaces, after) ;
+ if ((after - n) != 0)
+ uty_cli_echo_n(cli, telnet_backspaces, after - n) ;
- vio->cl.cp += n ;
+ qs_move_cp_nn(cli->cl, n) ;
return n ;
} ;
@@ -1633,24 +1921,58 @@ uty_cli_insert (vty_io vio, const char* chars, int n)
* Returns number of characters inserted -- ie 'n'
*/
static int
-uty_cli_overwrite (vty_io vio, char* chars, int n)
+uty_cli_overwrite (vty_cli cli, char* chars, int n)
{
VTY_ASSERT_LOCKED() ;
- assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+ assert((qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) && (n >= 0)) ;
if (n > 0)
{
- qs_replace(&vio->cl, chars, n) ;
- uty_cli_echo(vio, chars, n) ;
+ qs_replace(cli->cl, n, chars, n) ;
+ uty_cli_echo(cli, chars, n) ;
- vio->cl.cp += n ;
+ qs_move_cp_nn(cli->cl, n) ;
} ;
return n ;
}
/*------------------------------------------------------------------------------
+ * Replace 'm' characters at the current position, by 'n' characters and leave
+ * cursor at the end of the inserted characters.
+ *
+ * Returns number of characters inserted -- ie 'n'
+ */
+static int
+uty_cli_replace(vty_cli cli, int m, const char* chars, int n)
+{
+ int a, b ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ assert((qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) && (n >= 0) && (m >= 0)) ;
+
+ b = qs_len_nn(cli->cl) - qs_cp_nn(cli->cl) ;
+ a = qs_replace(cli->cl, m, chars, n) ;
+
+ uty_cli_echo(cli, qs_cp_char(cli->cl), a) ;
+
+ if (a < b)
+ uty_cli_echo_n(cli, telnet_spaces, b - a) ;
+ else
+ b = a ;
+
+ if (b > n)
+ uty_cli_echo_n(cli, telnet_backspaces, b - n) ;
+
+ qs_move_cp_nn(cli->cl, n) ;
+
+ return n ;
+} ;
+
+#if 0
+/*------------------------------------------------------------------------------
* Insert a word into vty interface with overwrite mode.
*
* NB: Assumes result will then be the end of the line.
@@ -1658,17 +1980,18 @@ uty_cli_overwrite (vty_io vio, char* chars, int n)
* Returns number of characters inserted -- ie length of string
*/
static int
-uty_cli_word_overwrite (vty_io vio, char *str)
+uty_cli_word_overwrite (vty_cli cli, char *str)
{
int n ;
VTY_ASSERT_LOCKED() ;
- n = uty_cli_overwrite(vio, str, strlen(str)) ;
+ n = uty_cli_overwrite(cli, str, strlen(str)) ;
- vio->cl.len = vio->cl.cp ;
+ qs_set_len_nn(cli->cl, qs_cp_nn(cli->cl)) ;
return n ;
}
+#endif
/*------------------------------------------------------------------------------
* Forward 'n' characters -- stop at end of line.
@@ -1676,12 +1999,12 @@ uty_cli_word_overwrite (vty_io vio, char *str)
* Returns number of characters actually moved
*/
static int
-uty_cli_forwards(vty_io vio, int n)
+uty_cli_forwards(vty_cli cli, int n)
{
int have ;
VTY_ASSERT_LOCKED() ;
- have = vio->cl.len - vio->cl.cp ;
+ have = qs_after_cp_nn(cli->cl) ;
if (have < n)
n = have ;
@@ -1689,12 +2012,12 @@ uty_cli_forwards(vty_io vio, int n)
if (n > 0)
{
- uty_cli_echo(vio, qs_cp_char(&vio->cl), n) ;
- vio->cl.cp += n ;
+ uty_cli_echo(cli, qs_cp_char(cli->cl), n) ;
+ qs_move_cp_nn(cli->cl, n) ;
} ;
return n ;
-}
+} ;
/*------------------------------------------------------------------------------
* Backwards 'n' characters -- stop at start of line.
@@ -1702,23 +2025,43 @@ uty_cli_forwards(vty_io vio, int n)
* Returns number of characters actually moved
*/
static int
-uty_cli_backwards(vty_io vio, int n)
+uty_cli_backwards(vty_cli cli, int n)
{
VTY_ASSERT_LOCKED() ;
- if ((int)vio->cl.cp < n)
- n = vio->cl.cp ;
+ if ((int)qs_cp_nn(cli->cl) < n)
+ n = qs_cp_nn(cli->cl) ;
assert(n >= 0) ;
if (n > 0)
{
- uty_cli_echo_n(vio, telnet_backspaces, n) ;
- vio->cl.cp -= n ;
+ uty_cli_echo_n(cli, telnet_backspaces, n) ;
+ qs_move_cp_nn(cli->cl, -n) ;
} ;
return n ;
-}
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move forwards (if n > 0) or backwards (if n < 0) -- stop at start or end of
+ * line.
+ *
+ * Returns number of characters actually moved -- signed
+ */
+static int
+uty_cli_move(vty_cli cli, int n)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (n < 0)
+ return - uty_cli_backwards(cli, -n) ;
+
+ if (n > 0)
+ return + uty_cli_forwards(cli, +n) ;
+
+ return 0 ;
+} ;
/*------------------------------------------------------------------------------
* Delete 'n' characters -- forwards -- stop at end of line.
@@ -1726,14 +2069,14 @@ uty_cli_backwards(vty_io vio, int n)
* Returns number of characters actually deleted.
*/
static int
-uty_cli_del_forwards(vty_io vio, int n)
+uty_cli_del_forwards(vty_cli cli, int n)
{
int after ;
int have ;
VTY_ASSERT_LOCKED() ;
- have = vio->cl.len - vio->cl.cp ;
+ have = qs_after_cp_nn(cli->cl) ;
if (have < n)
n = have ; /* cannot delete more than have */
@@ -1742,13 +2085,13 @@ uty_cli_del_forwards(vty_io vio, int n)
if (n <= 0)
return 0 ;
- after = qs_delete(&vio->cl, n) ;
+ after = qs_delete(cli->cl, n) ;
if (after > 0)
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after) ;
+ uty_cli_echo(cli, qs_cp_char(cli->cl), after) ;
- uty_cli_echo_n(vio, telnet_spaces, n) ;
- uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
+ uty_cli_echo_n(cli, telnet_spaces, n) ;
+ uty_cli_echo_n(cli, telnet_backspaces, after + n) ;
return n ;
}
@@ -1759,9 +2102,9 @@ uty_cli_del_forwards(vty_io vio, int n)
* Returns number of characters actually deleted.
*/
static int
-uty_cli_del_backwards(vty_io vio, int n)
+uty_cli_del_backwards(vty_cli cli, int n)
{
- return uty_cli_del_forwards(vio, uty_cli_backwards(vio, n)) ;
+ return uty_cli_del_forwards(cli, uty_cli_backwards(cli, n)) ;
}
/*------------------------------------------------------------------------------
@@ -1770,9 +2113,9 @@ uty_cli_del_backwards(vty_io vio, int n)
* Returns number of characters moved over.
*/
static int
-uty_cli_bol (vty_io vio)
+uty_cli_bol (vty_cli cli)
{
- return uty_cli_backwards(vio, vio->cl.cp) ;
+ return uty_cli_backwards(cli, qs_cp_nn(cli->cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1781,9 +2124,9 @@ uty_cli_bol (vty_io vio)
* Returns number of characters moved over
*/
static int
-uty_cli_eol (vty_io vio)
+uty_cli_eol (vty_cli cli)
{
- return uty_cli_forwards(vio, vio->cl.len - vio->cl.cp) ;
+ return uty_cli_forwards(cli, qs_after_cp_nn(cli->cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1794,7 +2137,7 @@ uty_cli_eol (vty_io vio)
* Steps over non-space characters and then any spaces.
*/
static int
-uty_cli_word_forwards_delta(vty_io vio)
+uty_cli_word_forwards_delta(vty_cli cli)
{
char* cp ;
char* tp ;
@@ -1802,10 +2145,10 @@ uty_cli_word_forwards_delta(vty_io vio)
VTY_ASSERT_LOCKED() ; ;
- assert(vio->cl.cp <= vio->cl.len) ;
+ assert(qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) ;
- cp = qs_cp_char(&vio->cl) ;
- ep = qs_ep_char(&vio->cl) ;
+ cp = qs_cp_char(cli->cl) ;
+ ep = qs_ep_char(cli->cl) ;
tp = cp ;
@@ -1824,9 +2167,9 @@ uty_cli_word_forwards_delta(vty_io vio)
* Moves past any non-spaces, then past any spaces.
*/
static int
-uty_cli_word_forwards(vty_io vio)
+uty_cli_word_forwards(vty_cli cli)
{
- return uty_cli_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+ return uty_cli_forwards(cli, uty_cli_word_forwards_delta(cli)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1838,7 +2181,7 @@ uty_cli_word_forwards(vty_io vio)
* Steps back until next (backwards) character is space, or hits start of line.
*/
static int
-uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
+uty_cli_word_backwards_delta(vty_cli cli, int eat_spaces)
{
char* cp ;
char* tp ;
@@ -1846,10 +2189,10 @@ uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
VTY_ASSERT_LOCKED() ; ;
- assert(vio->cl.cp <= vio->cl.len) ;
+ assert(qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) ;
- cp = qs_cp_char(&vio->cl) ;
- sp = qs_chars(&vio->cl) ;
+ cp = qs_cp_char(cli->cl) ;
+ sp = qs_char(cli->cl) ;
tp = cp ;
@@ -1863,6 +2206,7 @@ uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
return cp - tp ;
} ;
+#if 0
/*------------------------------------------------------------------------------
* Backward word, but not trailing spaces.
*
@@ -1871,10 +2215,11 @@ uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
* Returns number of characters stepped over.
*/
static int
-uty_cli_word_backwards_pure (vty_io vio)
+uty_cli_word_backwards_pure (vty_cli cli)
{
- return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 0)) ;
+ return uty_cli_backwards(cli, uty_cli_word_backwards_delta(cli, 0)) ;
} ;
+#endif
/*------------------------------------------------------------------------------
* Backward word -- move to start of previous word.
@@ -1885,9 +2230,9 @@ uty_cli_word_backwards_pure (vty_io vio)
* Returns number of characters stepped over.
*/
static int
-uty_cli_word_backwards (vty_io vio)
+uty_cli_word_backwards (vty_cli cli)
{
- return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+ return uty_cli_backwards(cli, uty_cli_word_backwards_delta(cli, 1)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1898,9 +2243,9 @@ uty_cli_word_backwards (vty_io vio)
* Returns number of characters deleted.
*/
static int
-uty_cli_del_word_forwards(vty_io vio)
+uty_cli_del_word_forwards(vty_cli cli)
{
- return uty_cli_del_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
+ return uty_cli_del_forwards(cli, uty_cli_word_forwards_delta(cli)) ;
}
/*------------------------------------------------------------------------------
@@ -1911,9 +2256,9 @@ uty_cli_del_word_forwards(vty_io vio)
* Returns number of characters deleted.
*/
static int
-uty_cli_del_word_backwards(vty_io vio)
+uty_cli_del_word_backwards(vty_cli cli)
{
- return uty_cli_del_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+ return uty_cli_del_backwards(cli, uty_cli_word_backwards_delta(cli, 1)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1922,9 +2267,9 @@ uty_cli_del_word_backwards(vty_io vio)
* Returns number of characters deleted.
*/
static int
-uty_cli_del_to_eol (vty_io vio)
+uty_cli_del_to_eol (vty_cli cli)
{
- return uty_cli_del_forwards(vio, vio->cl.len - vio->cl.cp) ;
+ return uty_cli_del_forwards(cli, qs_after_cp_nn(cli->cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -1933,10 +2278,10 @@ uty_cli_del_to_eol (vty_io vio)
* Returns number of characters deleted.
*/
static int
-uty_cli_clear_line(vty_io vio)
+uty_cli_clear_line(vty_cli cli)
{
- uty_cli_bol(vio) ;
- return uty_cli_del_to_eol(vio) ;
+ uty_cli_bol(cli) ;
+ return uty_cli_del_to_eol(cli) ;
} ;
/*------------------------------------------------------------------------------
@@ -1945,7 +2290,7 @@ uty_cli_clear_line(vty_io vio)
* Return number of characters affected.
*/
static int
-uty_cli_transpose_chars(vty_io vio)
+uty_cli_transpose_chars(vty_cli cli)
{
char chars[2] ;
char* cp ;
@@ -1953,54 +2298,79 @@ uty_cli_transpose_chars(vty_io vio)
VTY_ASSERT_LOCKED() ;
/* Give up if < 2 characters or at start of line. */
- if ((vio->cl.len < 2) || (vio->cl.cp < 1))
+ if ((qs_len_nn(cli->cl) < 2) || (qs_cp_nn(cli->cl) < 1))
return 0 ;
/* Move back to first of characters to exchange */
- if (vio->cl.cp == vio->cl.len)
- uty_cli_backwards(vio, 2) ;
+ if (qs_cp_nn(cli->cl) == qs_len_nn(cli->cl))
+ uty_cli_backwards(cli, 2) ;
else
- uty_cli_backwards(vio, 1) ;
+ uty_cli_backwards(cli, 1) ;
/* Pick up in the new order */
- cp = qs_cp_char(&vio->cl) ;
+ cp = qs_cp_char(cli->cl) ;
chars[1] = *cp++ ;
chars[0] = *cp ;
/* And overwrite */
- return uty_cli_overwrite(vio, chars, 2) ;
+ return uty_cli_overwrite(cli, chars, 2) ;
} ;
/*==============================================================================
* Command line history handling
+ *
+ * cli->hist is vector of qstrings
+ * cli->h_now is index of the present time
+ * cli->hp is index of most recent line read back
+ *
+ * cli->hist is initialised empty, with h_now == hp == 0.
+ *
+ * On first use it is set to VTY_MAX_HIST entries, and its size never changes.
+ * Before VTY_MAX_HIST lines have been inserted, a NULL entry signals the end
+ * of history (to date).
+ *
+ * h_now is incremented after a line is inserted (and wraps round). So
+ * stepping +1 moves towards the present (down) and -1 moves towards the past
+ * (up).
+ *
+ * hp == h_now means we are in the present.
+ *
+ * Cannot step forwards from hp == h_now (into the future !).
+ *
+ * Before stepping backwards from hp == hp_now, sets hp_now to be a copy of
+ * the current line (complete with cp), so can return to the present.
+ *
+ * Cannot step backwards to hp == hp_now -- that would be to wrap round from
+ * the ancient past to the now time.
+ *
+ * When storing a line in the history, replaces the last line stored if that
+ * is the same as the new line, apart from whitespace.
*/
/*------------------------------------------------------------------------------
* Add given command line to the history buffer.
*
* This is inserting the vty->buf line into the history.
+ *
+ * Resets hp == h_now.
*/
-extern void
-uty_cli_hist_add (vty_io vio, const char* cmd_line)
+static void
+uty_cli_hist_add (vty_cli cli, qstring clx)
{
- qstring prev_line ;
- qstring_t line ;
- int prev_index ;
+ qstring hist_line ;
+ int prev ;
VTY_ASSERT_LOCKED() ;
- /* Construct a dummy qstring for the given command line */
- qs_dummy(&line, cmd_line, 1) ; /* set cursor to the end */
-
/* make sure have a suitable history vector */
- vector_set_min_length(vio->hist, VTY_MAXHIST) ;
+ vector_set_min_length(cli->hist, VTY_MAXHIST) ;
- /* find the previous command line in the history */
- prev_index = vio->hindex - 1 ;
- if (prev_index < 0)
- prev_index = VTY_MAXHIST - 1 ;
+ /* get the previous command line */
+ prev = cli->h_now - 1 ;
+ if (prev < 0)
+ prev = VTY_MAXHIST - 1 ;
- prev_line = vector_get_item(vio->hist, prev_index) ;
+ hist_line = vector_get_item(cli->hist, prev) ;
/* If the previous line is NULL, that means the history is empty.
*
@@ -2008,23 +2378,32 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
* replace it with the current line -- so that the latest whitespace
* version is saved.
*
- * Either way, replace the the previous line entry by moving hindex
- * back !
+ * In both those cases, replace the the previous line entry by moving
+ * h_now back to it -- leaving hist_line pointing at it.
+ *
+ * Otherwise, leave cli->h_now and point hist_line at the most ancient
+ * line in history.
*/
- if ((prev_line == NULL) || (qs_cmp_sig(prev_line, &line) == 0))
- vio->hindex = prev_index ;
+ if ((hist_line == NULL) || (qs_cmp_sig(hist_line, clx) == 0))
+ cli->h_now = prev ;
else
- prev_line = vector_get_item(vio->hist, vio->hindex) ;
+ hist_line = vector_get_item(cli->hist, cli->h_now) ;
- /* Now replace the hindex entry */
- vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
+ /* Now replace the h_now entry
+ *
+ * Note that the line inserted in the history has it's 'cp' set to the end of
+ * the line -- so that it is there when it comes back out again.
+ */
+ hist_line = qs_copy(hist_line, clx) ;
+ qs_set_cp_nn(hist_line, qs_len_nn(hist_line)) ;
+ vector_set_item(cli->hist, cli->h_now, hist_line) ;
/* Advance to the near future and reset the history pointer */
- vio->hindex++;
- if (vio->hindex == VTY_MAXHIST)
- vio->hindex = 0;
+ cli->h_now++;
+ if (cli->h_now == VTY_MAXHIST)
+ cli->h_now = 0;
- vio->hp = vio->hindex;
+ cli->hp = cli->h_now;
} ;
/*------------------------------------------------------------------------------
@@ -2032,668 +2411,503 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
*
* This function is called from vty_next_line and vty_previous_line.
*
- * Step +1 is towards the present
- * -1 is into the past
+ * Step -1 is into the past (up)
+ * +1 is towards the present (down)
*/
static void
-uty_cli_history_use(vty_io vio, int step)
+uty_cli_hist_use(vty_cli cli, int step)
{
- int index ;
- unsigned old_len ;
- unsigned after ;
- unsigned back ;
- qstring hist ;
+ int hp ;
+ ulen old_len ;
+ ulen new_len ;
+ ulen after ;
+ ulen back ;
+ qstring hist_line ;
VTY_ASSERT_LOCKED() ;
assert((step == +1) || (step == -1)) ;
- index = vio->hp ;
+ hp = cli->hp ;
/* Special case of being at the insertion point */
- if (index == vio->hindex)
+ if (hp == cli->h_now)
{
if (step > 0)
return ; /* already in the present */
/* before stepping back from the present, take a copy of the
* current command line -- so can get back to it.
+ *
+ * Note that the 'cp' is stored with the line. If return to here
+ * and later enter the line it will replace this.
*/
- hist = vector_get_item(vio->hist, vio->hindex) ;
- vector_set_item(vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
+ hist_line = vector_get_item(cli->hist, cli->h_now) ;
+ vector_set_item(cli->hist, cli->h_now, qs_copy(hist_line, cli->cl)) ;
} ;
/* Advance or retreat */
- index += step ;
- if (index < 0)
- index = VTY_MAXHIST - 1 ;
- else if (index >= VTY_MAXHIST)
- index = 0 ;
+ hp += step ;
+ if (hp < 0)
+ hp = VTY_MAXHIST - 1 ;
+ else if (hp >= VTY_MAXHIST)
+ hp = 0 ;
- hist = vector_get_item(vio->hist, index) ;
+ hist_line = vector_get_item(cli->hist, hp) ;
- /* If moving backwards in time, may not move back to the insertion
+ /* If moving backwards in time, may not move back to the h_now
* point (that would be wrapping round to the present) and may not
* move back to a NULL entry (that would be going back before '.').
+ *
+ * If moving forwards in time, may return to the present, with
+ * hp == cli->h_now.
*/
if (step < 0)
- if ((hist == NULL) || (index == vio->hindex))
+ if ((hist_line == NULL) || (hp == cli->h_now))
return ;
- /* Now, if arrived at the insertion point, this is returning to the
- * present, which is fine.
- */
- vio->hp = index;
+ cli->hp = hp ;
/* Move back to the start of the current line */
- uty_cli_bol(vio) ;
+ uty_cli_bol(cli) ;
/* Get previous line from history buffer and echo that */
- old_len = vio->cl.len ;
- qs_copy(&vio->cl, hist) ;
+ old_len = qs_len_nn(cli->cl) ;
+ qs_copy(cli->cl, hist_line) ;
+ new_len = qs_len_nn(cli->cl) ;
/* Sort out wiping out any excess and setting the cursor position */
- if (old_len > vio->cl.len)
- after = old_len - vio->cl.len ;
+ if (old_len > new_len)
+ after = old_len - new_len ;
else
after = 0 ;
- back = after ;
- if (vio->cl.len > vio->cl.cp)
- back += (vio->cl.len - vio->cl.cp) ;
+ /* Return cursor to stored 'cp' -- which will be end of line unless
+ * this is the copy of the original current line stored above.
+ */
+ back = after + qs_after_cp_nn(cli->cl) ;
- if (vio->cl.len > 0)
- uty_cli_echo(vio, vio->cl.body, vio->cl.len) ;
+ if (new_len > 0)
+ uty_cli_echo(cli, qs_char_nn(cli->cl), new_len) ;
if (after > 0)
- uty_cli_echo_n(vio, telnet_spaces, after) ;
+ uty_cli_echo_n(cli, telnet_spaces, after) ;
if (back > 0)
- uty_cli_echo_n(vio, telnet_backspaces, back) ;
+ uty_cli_echo_n(cli, telnet_backspaces, back) ;
return ;
} ;
/*------------------------------------------------------------------------------
- * Use next history line, if any.
+ * Use previous history line, if any (up arrow).
*/
static void
-uty_cli_next_line(vty_io vio)
+uty_cli_hist_previous(vty_cli cli)
{
- uty_cli_history_use(vio, +1) ;
+ uty_cli_hist_use(cli, -1) ;
}
/*------------------------------------------------------------------------------
- * Use previous history line, if any.
+ * Use next history line, if any (down arrow).
*/
static void
-uty_cli_previous_line (vty_io vio)
+uty_cli_hist_next(vty_cli cli)
{
- uty_cli_history_use(vio, -1) ;
+ uty_cli_hist_use(cli, +1) ;
}
-/*==============================================================================
- * Command Completion and Command Description
- *
- */
-static void uty_cli_describe_show(vty_io vio, vector describe) ;
-static void uty_cli_describe_fold (vty_io vio, int cmd_width,
- unsigned int desc_width, struct desc *desc) ;
-static void uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
- const char* str) ;
-
-static vector uty_cli_cmd_prepare(vty_io vio, int help) ;
-
/*------------------------------------------------------------------------------
- * Command completion
+ * Show the contents of the history
*/
-static void
-uty_cli_complete_command (vty_io vio, enum node_type node)
+extern void
+uty_cli_hist_show(vty_cli cli)
{
- unsigned i ;
- int ret ;
- int len ;
- int n ;
- vector matched ;
- vector vline ;
+ int hp ;
VTY_ASSERT_LOCKED() ;
- /* Try and match the tokenised command line */
- vline = uty_cli_cmd_prepare(vio, 1) ;
- matched = cmd_complete_command (vline, node, &ret);
- cmd_free_strvec (vline);
+ hp = cli->h_now ;
- /* Show the result. */
- switch (ret)
+ while(1)
{
- case CMD_ERR_AMBIGUOUS:
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
- break ;
-
- case CMD_ERR_NO_MATCH:
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
- break ;
-
- case CMD_COMPLETE_FULL_MATCH:
- uty_cli_eol (vio) ;
- uty_cli_word_backwards_pure (vio);
- uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
- uty_cli_insert(vio, " ", 1);
- break ;
-
- case CMD_COMPLETE_MATCH:
- uty_cli_eol (vio) ;
- uty_cli_word_backwards_pure (vio);
- uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
- break ;
-
- case CMD_COMPLETE_LIST_MATCH:
- len = 6 ;
- for (i = 0; i < vector_end(matched); i++)
- {
- int sl = strlen((char*)vector_get_item(matched, i)) ;
- if (len < sl)
- len = sl ;
- } ;
+ qstring line ;
- n = vio->width ;
- if (n == 0)
- n = 60 ;
- n = n / (len + 2) ;
- if (n == 0)
- n = 1 ;
+ ++hp ;
+ if (hp == VTY_MAXHIST)
+ hp = 0 ;
- for (i = 0; i < vector_end(matched); i++)
- {
- if ((i % n) == 0)
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out(vio, "%-*s ", len, (char*)vector_get_item(matched, i));
- }
- uty_cli_out_newline(vio) ;
+ if (hp == cli->h_now)
+ break ; /* wrapped round to "now" */
- break;
+ line = vector_get_item(cli->hist, hp) ;
- case CMD_COMPLETE_ALREADY:
- default:
- break;
- } ;
+ if (line == NULL)
+ break ; /* reached limit of history so far */
- cmd_free_strvec(matched);
+ uty_out(cli->vf->vio, " %s\n", qs_string(line));
+ }
} ;
-/*------------------------------------------------------------------------------
- * Command Description
+/*==============================================================================
+ * Command Completion and Command Description
+ *
*/
-static void
-uty_cli_describe_command (vty_io vio, node_type_t node)
-{
- cmd_return_code_t ret ;
- vector describe ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* Try and match the tokenised command line */
- describe = cmd_describe_command (qs_term(&vio->cl), node, &ret);
-
- uty_cli_out_newline(vio); /* clears cli_drawn */
-
- /* Deal with result. */
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
- break ;
+static uint uty_cli_help_parse(vty_cli cli, node_type_t node) ;
+static void uty_cli_out_message(vty_cli cli, const char* msg) ;
- case CMD_ERR_NO_MATCH:
- uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
- break ;
+static void uty_cli_complete_keyword(vty_cli cli, const char* keyword) ;
+static void uty_cli_complete_list(vty_cli cli, vector item_v) ;
- default:
- uty_cli_describe_show(vio, describe) ;
- break ;
- } ;
+static void uty_cli_describe_list(vty_cli cli, vector item_v) ;
+static void uty_cli_describe_line(vty_cli cli, uint str_width, const char* str,
+ const char* doc, uint len) ;
- if (describe != NULL)
- vector_free (describe);
-}
+static uint uty_cli_width_to_use(vty_cli cli) ;
/*------------------------------------------------------------------------------
- * Show the command description.
- *
- * Generates lines of the form:
- *
- * word description text
- *
- * Where the word field is adjusted to suit the longest word, and the
- * description text is wrapped if required (if the width of the console is
- * known) so that get:
+ * Command completion
*
- * word description ..................................
- * .............text
+ * Requires that cmd_token_position() has been called to tokenise the line and
+ * establish which token the cursor is in. Must NOT call this if the cursor
+ * is in a "special" place.
*
- * If one of the options is '<cr>', that is always shown last.
*/
static void
-uty_cli_describe_show(vty_io vio, vector describe)
+uty_cli_complete_command (vty_cli cli, node_type_t node)
{
- unsigned int i, cmd_width, desc_width;
- struct desc *desc, *desc_cr ;
+ uint n_items ;
+ cmd_parsed parsed ;
+ cmd_item item ;
- /* Get width of the longest "word" */
- cmd_width = 0;
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- unsigned int len;
+ VTY_ASSERT_LOCKED() ;
+
+ parsed = cli->parsed ;
- if (desc->cmd[0] == '\0')
- continue;
+ /* Establish what items may be present at the current token position. */
+ n_items = uty_cli_help_parse(cli, node) ;
- len = strlen (desc->cmd);
- if (desc->cmd[0] == '.')
- len--;
+ if (n_items == 0) /* quit if nothing to consider */
+ return ;
- if (cmd_width < len)
- cmd_width = len;
- }
+ if (n_items > 1) /* render list of alternatives */
+ return uty_cli_complete_list(cli, parsed->item_v) ;
- /* Set width of description string. */
- desc_width = vio->width - (cmd_width + 6);
+ /* One possible item -- one or more possible commands */
+ item = vector_get_item(parsed->item_v, 0) ;
- /* Print out description. */
- desc_cr = NULL ; /* put <cr> last if it appears */
+ switch (item->type)
+ {
+ case item_null:
+ zabort("invalid item_null") ;
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- if (desc->cmd[0] == '\0')
- continue;
+ case item_eol:
- if (strcmp (desc->cmd, command_cr) == 0)
- {
- desc_cr = desc;
- continue;
- }
+ case item_option_word:
+
+ case item_vararg:
+
+ case item_word:
+
+ case item_ipv6_prefix:
+ case item_ipv6_address:
+ case item_ipv4_prefix:
+ case item_ipv4_address:
+
+ case item_range:
+ return uty_cli_describe_list(cli, parsed->item_v) ;
- uty_cli_describe_fold (vio, cmd_width, desc_width, desc);
- }
+ case item_keyword:
+ return uty_cli_complete_keyword(cli, item->str) ;
- if (desc_cr != NULL)
- uty_cli_describe_fold (vio, cmd_width, desc_width, desc_cr);
+ default:
+ zabort("unknown item type") ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * Show one word and the description, folding the description as required.
+ * Command Description
*/
static void
-uty_cli_describe_fold (vty_io vio, int cmd_width,
- unsigned int desc_width, struct desc *desc)
+uty_cli_describe_command (vty_cli cli, node_type_t node)
{
- char *buf;
- const char *cmd, *p;
- int pos;
+ uint n_items ;
VTY_ASSERT_LOCKED() ;
- cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
- p = desc->str ;
+ /* Establish what items may be present at the current token position. */
+ n_items = uty_cli_help_parse(cli, node) ;
- /* If have a sensible description width */
- if (desc_width > 20)
- {
- buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
+ if (n_items > 0) /* render list of possibilities */
+ uty_cli_describe_list(cli, cli->parsed->item_v) ;
+} ;
- while (strlen (p) > desc_width)
- {
- /* move back to first space */
- for (pos = desc_width; pos > 0; pos--)
- if (*(p + pos) == ' ')
- break;
+/*------------------------------------------------------------------------------
+ * Parse for command completion and command description.
+ *
+ * Requires that cmd_token_position() has been called to tokenise the line and
+ * establish which token the cursor is in. Must NOT call this if the cursor
+ * is in a "special" place.
+ *
+ * Deal with all cases which yield no items at all.
+ *
+ * Returns: number of items to consider.
+ */
+static uint
+uty_cli_help_parse(vty_cli cli, node_type_t node)
+{
+ const char* msg ;
+ cmd_return_code_t ret ;
+ uint n_items ;
- /* if did not find a space, break at width */
- if (pos == 0)
- pos = desc_width ;
+ /* The preflight checks avoid getting into trouble doing command completion
+ * on a line with comment
+ */
+ msg = cmd_help_preflight(cli->parsed) ;
+ if (msg != NULL)
+ {
+ uty_cli_out_message(cli, msg) ;
+ return 0 ;
+ } ;
- strncpy (buf, p, pos);
- buf[pos] = '\0';
- uty_cli_describe_line(vio, cmd_width, cmd, buf) ;
+ /* Now see what the cmd_completion can come up with. */
+ ret = cmd_completion(cli->parsed, node) ;
- cmd = ""; /* for 2nd and subsequent lines */
+ if (ret == CMD_ERR_PARSING)
+ {
+ uint eloc = cli->prompt_len + cli->parsed->eloc ;
- p += pos ; /* step past what just wrote */
- while (*p == ' ')
- ++p ; /* skip spaces */
- } ;
+ uty_cli_out_newline(cli) ; /* clears cli_drawn */
+ uty_cli_write_n(cli, telnet_dots, eloc) ;
+ uty_cli_write_s(cli, "^") ;
- XFREE (MTYPE_TMP, buf);
+ uty_cli_out_message(cli, cli->parsed->emess) ;
+
+ return 0 ;
} ;
- uty_cli_describe_line(vio, cmd_width, cmd, p) ;
-} ;
+ /* Will now have 0, 1 or more items which match at the current
+ * cursor token.
+ */
+ n_items = vector_length(cli->parsed->item_v) ;
-/*------------------------------------------------------------------------------
- * Show one description line.
- */
-static void
-uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
- const char* str)
-{
- if (str != NULL)
- uty_cli_out (vio, " %-*s %s", cmd_width, cmd, str) ;
- else
- uty_cli_out (vio, " %-s", cmd) ;
- uty_cli_out_newline(vio) ;
+ if (n_items == 0)
+ uty_cli_out_message(cli, "command not recognised") ;
+
+ return n_items ;
} ;
-/*------------------------------------------------------------------------------
- * Prepare "vline" token array for command handler.
- *
- * For "help" (command completion/description), if the command line is empty,
- * or ends in ' ', adds an empty token to the end of the token array.
- */
-static vector
-uty_cli_cmd_prepare(vty_io vio, int help)
-{
- vector vline ;
- vline = cmd_make_strvec(qs_term(&vio->cl)) ;
- /* Note that if there is a vector of tokens, then there is at least one
- * token, so can guarantee that vio->cl.len >= 1 !
- */
- if (help)
- if ((vline == NULL) || isspace(*qs_chars_at(&vio->cl, vio->cl.len - 1)))
- vline = cmd_add_to_strvec(vline, "") ;
- return vline ;
-} ;
-/*==============================================================================
- * VTY telnet stuff
- */
-
-#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
-
-static const char* telnet_commands[256] =
-{
- [tn_IAC ] = "IAC",
- [tn_DONT ] = "DONT",
- [tn_DO ] = "DO",
- [tn_WONT ] = "WONT",
- [tn_WILL ] = "WILL",
- [tn_SB ] = "SB",
- [tn_GA ] = "GA",
- [tn_EL ] = "EL",
- [tn_EC ] = "EC",
- [tn_AYT ] = "AYT",
- [tn_AO ] = "AO",
- [tn_IP ] = "IP",
- [tn_BREAK] = "BREAK",
- [tn_DM ] = "DM",
- [tn_NOP ] = "NOP",
- [tn_SE ] = "SE",
- [tn_EOR ] = "EOR",
- [tn_ABORT] = "ABORT",
- [tn_SUSP ] = "SUSP",
- [tn_EOF ] = "EOF",
-} ;
-
-static const char* telnet_options[256] =
-{
- [to_BINARY] = "BINARY", /* 8-bit data path */
- [to_ECHO] = "ECHO", /* echo */
- [to_RCP] = "RCP", /* prepare to reconnect */
- [to_SGA] = "SGA", /* suppress go ahead */
- [to_NAMS] = "NAMS", /* approximate message size */
- [to_STATUS] = "STATUS", /* give status */
- [to_TM] = "TM", /* timing mark */
- [to_RCTE] = "RCTE", /* remote controlled tx and echo */
- [to_NAOL] = "NAOL", /* neg. about output line width */
- [to_NAOP] = "NAOP", /* neg. about output page size */
- [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
- [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
- [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
- [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
- [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
- [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
- [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
- [to_XASCII] = "XASCII", /* extended ascii character set */
- [to_LOGOUT] = "LOGOUT", /* force logout */
- [to_BM] = "BM", /* byte macro */
- [to_DET] = "DET", /* data entry terminal */
- [to_SUPDUP] = "SUPDUP", /* supdup protocol */
- [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
- [to_SNDLOC] = "SNDLOC", /* send location */
- [to_TTYPE] = "TTYPE", /* terminal type */
- [to_EOR] = "EOR", /* end or record */
- [to_TUID] = "TUID", /* TACACS user identification */
- [to_OUTMRK] = "OUTMRK", /* output marking */
- [to_TTYLOC] = "TTYLOC", /* terminal location number */
- [to_3270REGIME] = "3270REGIME", /* 3270 regime */
- [to_X3PAD] = "X3PAD", /* X.3 PAD */
- [to_NAWS] = "NAWS", /* window size */
- [to_TSPEED] = "TSPEED", /* terminal speed */
- [to_LFLOW] = "LFLOW", /* remote flow control */
- [to_LINEMODE] = "LINEMODE", /* Linemode option */
- [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
- [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
- [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
- [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
- [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
- [to_EXOPL] = "EXOPL", /* extended-options-list */
-} ;
-
-/*------------------------------------------------------------------------------
- * For debug. Put string or value as decimal.
- */
+
+
+
static void
-uty_cli_out_dec(vty_io vio, const char* str, unsigned char u)
+uty_cli_out_message(vty_cli cli, const char* msg)
{
- if (str != NULL)
- uty_cli_out(vio, "%s ", str) ;
- else
- uty_cli_out(vio, "%d ", (int)u) ;
+ uty_cli_out_newline(cli) ; /* clears cli_drawn */
+ uty_cli_write_s(cli, "% ") ;
+ uty_cli_write_s(cli, msg) ;
+ uty_cli_out_newline(cli) ;
} ;
-/*------------------------------------------------------------------------------
- * For debug. Put string or value as hex.
- */
+
+
static void
-uty_cli_out_hex(vty_io vio, const char* str, unsigned char u)
+uty_cli_complete_keyword(vty_cli cli, const char* keyword)
{
- if (str != NULL)
- uty_cli_out(vio, "%s ", str) ;
- else
- uty_cli_out(vio, "0x%02x ", (unsigned)u) ;
+ int pre, rep, ins, mov ;
+
+ cmd_complete_keyword(cli->parsed, &pre, &rep, &ins, &mov) ;
+
+ uty_cli_move(cli, pre) ; /* move to start of token */
+ uty_cli_replace(cli, rep, keyword, strlen(keyword)) ;
+
+ if (ins > 0)
+ uty_cli_insert(cli, " ", ins) ;
+
+ uty_cli_move(cli, mov) ;
+
+ return ;
} ;
/*------------------------------------------------------------------------------
- * Send telnet: "WILL TELOPT_ECHO"
+ * Show the command completions -- usually more than one.
+ *
+ * Generates a table of possible items, with fixed width entries, depending
+ * on the longest option.
+ *
+ * NB: the items will have been sorted before we get here. Inter alia, that
+ * ensures that any <cr> is shown last.
*/
static void
-uty_will_echo (vty_io vio)
+uty_cli_complete_list(vty_cli cli, vector item_v)
{
- unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ uint i, len, n ;
-/*------------------------------------------------------------------------------
- * Send telnet: "suppress Go-Ahead"
- */
-static void
-uty_will_suppress_go_ahead (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ len = 6 ;
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
+ {
+ cmd_item item ;
+ uint sl ;
-/*------------------------------------------------------------------------------
- * Send telnet: "don't use linemode"
- */
-static void
-uty_dont_linemode (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ item = vector_get_item(item_v, i) ;
-/*------------------------------------------------------------------------------
- * Send telnet: "Use window size"
- */
-static void
-uty_do_window_size (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ sl = strlen(item->str) ;
+ if (len < sl)
+ len = sl ;
+ } ;
-/*------------------------------------------------------------------------------
- * Send telnet: "don't use lflow" -- not currently used
- */
-static void
-uty_dont_lflow_ahead (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
+ n = uty_cli_width_to_use(cli) / (len + 2) ;
-/*------------------------------------------------------------------------------
- * The keystroke iac callback function.
- *
- * This deals with IAC sequences that should be dealt with as soon as they
- * are read -- not stored in the keystroke stream for later processing.
- */
-extern bool
-uty_cli_iac_callback(keystroke_iac_callback_args)
-{
- return uty_telnet_command((vty_io)context, stroke, true) ;
+ if (n == 0)
+ n = 1 ;
+
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
+ {
+ cmd_item item ;
+ item = vector_get_item(item_v, i) ;
+
+ if ((i % n) == 0)
+ uty_cli_out_newline(cli) ; /* clears cli_drawn */
+
+ uty_cli_out(cli, "%-*s ", len, item->str) ;
+ }
+ uty_cli_out_newline(cli) ;
} ;
/*------------------------------------------------------------------------------
- * Process incoming Telnet Option(s)
+ * Show the command description.
+ *
+ * Generates lines of the form:
*
- * May be called during keystroke iac callback, or when processing CLI
- * keystrokes.
+ * word description text
*
- * In particular: get telnet window size.
+ * Where the word field is adjusted to suit the longest word, and the
+ * description text is wrapped if required (if the width of the console is
+ * known) so that get:
*
- * Returns: true <=> dealt with, for:
+ * word description ..................................
+ * .............text
*
- * * telnet window size.
+ * NB: the items will have been sorted before we get here. Inter alia, that
+ * ensures that any <cr> is shown last.
*/
-static bool
-uty_telnet_command(vty_io vio, keystroke stroke, bool callback)
+static void
+uty_cli_describe_list(vty_cli cli, vector item_v)
{
- uint8_t* p ;
- uint8_t o ;
- int left ;
- bool dealt_with ;
+ uint i, str_width, doc_width, width ;
- /* Echo to the other end if required */
- if (TELNET_OPTION_DEBUG)
+ /* Get width of the longest "word" */
+ str_width = 0;
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
{
- uty_cli_wipe(vio, 0) ;
+ cmd_item item ;
+ uint len ;
+
+ item = vector_get_item(item_v, i) ;
- p = stroke->buf ;
- left = stroke->len ;
+ len = strlen(item->str) ;
+ if (item->str[0] == '.')
+ len--;
- uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
+ if (len > str_width)
+ str_width = len ;
+ } ;
- if (left-- > 0)
- uty_cli_out_dec(vio, telnet_commands[*p], *p) ;
- ++p ;
+ /* Set width of description string.
+ *
+ * Format is:
+ *
+ * __wo.....rd__description...
+ * ...continues
+ *
+ * The width of the word part has been established above as the width of the
+ * widest word.
+ *
+ * There are two spaces on either side of the word, so we here calculate the
+ * width of the description part
+ */
+ width = uty_cli_width_to_use(cli) ;
- if (left-- > 0)
- uty_cli_out_dec(vio, telnet_options[*p], *p) ;
- ++p ;
+ if (width > ((str_width + 6) + 20))
+ doc_width = width - (str_width + 6) ;
+ else
+ doc_width = 0 ;
- if (left > 0)
- {
- while(left-- > 0)
- uty_cli_out_hex(vio, NULL, *p++) ;
+ /* Print out description. */
+ for (i = 0 ; i < vector_length(item_v) ; ++i)
+ {
+ cmd_item item ;
+ const char* str, * dp, * ep ;
+
+ item = vector_get_item(item_v, i) ;
- if (stroke->flags & kf_truncated)
- uty_cli_out(vio, "... ") ;
+ str = item->str[0] == '.' ? item->str + 1 : item->str;
+ dp = item->doc ;
+ ep = dp + strlen(dp) ;
- if (!(stroke->flags & kf_broken))
+ /* If have a sensible description width */
+ if (doc_width > 20)
+ {
+ while ((ep - dp) > doc_width)
{
- uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
- uty_cli_out_hex(vio, telnet_commands[tn_SE], tn_SE) ;
- }
- } ;
+ const char* np ;
- if (stroke->flags & kf_broken)
- uty_cli_out (vio, "BROKEN") ;
+ np = dp + doc_width ; /* target next position */
- uty_cli_out (vio, "\r\n") ;
- } ;
+ while ((np > dp) && (*np != ' '))
+ --np ; /* seek back to ' ' */
- /* Process the telnet command */
- dealt_with = false ;
+ if (np == dp) /* if no space... */
+ np = dp + doc_width ; /* ...force break */
- if (stroke->flags != 0)
- return dealt_with ; /* go no further if broken */
+ uty_cli_describe_line(cli, str_width, str, dp, np - dp) ;
- p = stroke->buf ;
- left = stroke->len ;
+ str = ""; /* for 2nd and subsequent lines */
- passert(left >= 1) ; /* must be if not broken ! */
- passert(stroke->value == *p) ; /* or something is wrong */
+ dp = np ; /* step past what just wrote */
+ while (*dp == ' ')
+ ++dp ; /* skip spaces */
+ } ;
+ } ;
- ++p ; /* step past X of IAC X */
- --left ;
+ uty_cli_describe_line(cli, str_width, str, dp, ep - dp) ;
+ } ;
- /* Decode the one command that is interesting -- "NAWS" */
- switch (stroke->value)
- {
- case tn_SB:
- passert(left > 0) ; /* or parser failed */
+ uty_cli_out_newline(cli) ;
+} ;
- o = *p++ ; /* the option byte */
- --left ;
- switch(o)
- {
- case to_NAWS:
- if (left != 4)
- {
- uzlog(NULL, LOG_WARNING,
- "RFC 1073 violation detected: telnet NAWS option "
- "should send %d characters, but we received %d",
- (3 + 4 + 2), (3 + left + 2)) ;
- }
- else
- {
- vio->width = *p++ << 8 ;
- vio->width += *p++ ;
- vio->height = *p++ << 8 ;
- vio->height += *p ;
-
- if (TELNET_OPTION_DEBUG)
- uty_cli_out(vio, "TELNET NAWS window size received: "
- "width %d, height %d%s",
- vio->width, vio->height, telnet_newline) ;
- uty_set_height(vio) ;
-
- dealt_with = true ;
- } ;
- break ;
+/*------------------------------------------------------------------------------
+ * Show one description line.
+ */
+static void
+uty_cli_describe_line(vty_cli cli, uint str_width, const char* str,
+ const char* doc, uint len)
+{
+ if ((*str == '\0') && (len == 0))
+ return ; /* quit if nothing to say */
- default: /* no other IAC SB <option> */
- break ;
- } ;
- break ;
+ uty_cli_out_newline(cli) ;
- default: /* no other IAC X */
- break ;
- } ;
+ if (len == 0)
+ uty_cli_out(cli, " %s", str) ; /* left justify */
+ else
+ {
+ uty_cli_out(cli, " %-*s ", str_width, str) ;
+ uty_cli_write(cli, doc, len) ;
+ } ;
+} ;
- return dealt_with ;
+/*------------------------------------------------------------------------------
+ * Return the actual or assumed console width.
+ *
+ * If we know the width we use it. Otherwise just assume something reasonable.
+ */
+static uint
+uty_cli_width_to_use(vty_cli cli)
+{
+ return (cli->width == 0) ? 60 : cli->width ;
} ;
diff --git a/lib/vty_cli.h b/lib/vty_cli.h
index f532616a..94e502f0 100644
--- a/lib/vty_cli.h
+++ b/lib/vty_cli.h
@@ -24,26 +24,166 @@
#ifndef _ZEBRA_VTY_CLI_H
#define _ZEBRA_VTY_CLI_H
+#include "misc.h"
+#include "vargs.h"
+
+#include "command_local.h"
#include "vty_io.h"
+#include "vty_io_basic.h"
+#include "vio_fifo.h"
+#include "vio_lines.h"
+#include "qstring.h"
#include "keystroke.h"
-extern void uty_cli_init(vty_io vio) ;
-extern enum vty_readiness uty_cli_start(vty_io vio) ;
-extern void uty_cli_close(vty_io vio) ;
+/*------------------------------------------------------------------------------
+ * The vty_cli structure pointed to by the vty_io structure.
+ */
+typedef struct vty_cli* vty_cli ;
+
+struct vty_cli
+{
+ vio_vf vf ; /* parent */
+
+ /* History of commands */
+ vector_t hist ; /* embedded */
+ int hp ; /* current place in history */
+ int h_now ; /* the present moment */
+
+ /* Window width/height as reported by Telnet. 0 => unknown */
+ int width;
+ int height;
+
+ /* Configure lines. */
+ int lines;
+ bool lines_set ; /* true <=> explicitly set */
+
+ /* Terminal monitor. */
+ bool monitor ;
+ bool monitor_busy ;
+
+ /* Terminal timeout in seconds -- 0 => none */
+ vty_timer_time v_timeout ;
+
+ /* The incoming stuff */
+ keystroke_stream key_stream ;
+
+ /* drawn <=> the current prompt and user input occupy the current
+ * line on the screen.
+ *
+ * dirty <=> the last command output did not end with a newline.
+ *
+ * If drawn is true, the following are valid:
+ *
+ * prompt_len -- the length of the prompt part.
+ * (will be the "--more--" prompt in cli_more_wait)
+ *
+ * extra_len -- the length of any ^X at the cursor position
+ * (for when blocked waiting for queued command)
+ *
+ * echo_suppress -- the user part of the command line is suppressed
+ *
+ * NB: echo_suppress is only used for password entry.
+ */
+ bool drawn ;
+ bool dirty ;
+
+ int prompt_len ;
+ int extra_len ;
+
+ bool echo_suppress ;
+
+ /* "cache" for prompt -- when node or host name changes, prompt does */
+ node_type_t prompt_node ;
+ name_gen_t prompt_gen ;
+ qstring_t prompt_for_node ;
+
+ /* password failure count -- main login or enable login. */
+ int password_fail ;
+
+ /* State of the CLI
+ *
+ * in_progress -- command dispatched
+ * blocked -- blocked until current command completes
+ * out_active -- contents of the command FIFO are being written away
+ *
+ * more_wait -- is in "--more--" wait state
+ * more_active -- more_wait and waiting for "--more--" prompt to be
+ * written away.
+ */
+ bool in_progress ;
+ bool blocked ;
+ bool out_active ;
+
+ bool more_wait ;
+ bool more_active ;
+
+ /* This is used to control command output, so that each write_ready event
+ * generates at most one tranche of output.
+ */
+ bool out_done ;
+
+ /* This is set only if the "--more--" handling is enabled */
+ bool more_enabled ;
+
+ /* Command Line(s)
+ *
+ * node -- the node that the CLI is in. This may be some way behind
+ * the VTY, but is updated when the CLI level command completes.
+ *
+ * to_do -- when current command being prepared is completed (by
+ * CR/LF or otherwise) this says what there now is to be done.
+ *
+ * cl -- current command line being prepared.
+ *
+ * clx -- current command line being executed.
+ *
+ * NB: during command execution vty->buf is set to point at the '\0'
+ * terminated current command line being executed.
+ */
+ node_type_t node ;
+
+ cmd_do_t to_do ;
+
+ qstring cl ;
+ qstring clx ;
+
+ cmd_parsed_t parsed ; /* embedded */
+
+ /* CLI line buffering */
+ vio_fifo_t cbuf ; /* embedded */
+
+ /* CLI line control for command output & "--more--" stuff */
+ vio_line_control_t olc ; /* embedded */
+} ;
+
+extern vty_cli uty_cli_new(vio_vf vf) ;
+extern void uty_cli_start(vty_cli cli, node_type_t node) ;
+
+extern vty_cli uty_cli_close(vty_cli cli, bool final) ;
+
+extern cmd_return_code_t uty_cli_auth(vty_cli) ;
+extern void uty_cli_hist_show(vty_cli cli) ;
+extern ulen uty_cli_prompt_len(vty_cli cli) ;
-extern enum vty_readiness uty_cli(vty_io vio) ;
-extern keystroke_callback uty_cli_iac_callback ;
+extern vty_readiness_t uty_cli(vty_cli cli) ;
+extern void uty_cli_out_push(vty_cli cli) ;
+extern void uty_cli_done_command(vty_cli cli, node_type_t node) ;
-extern void uty_cli_hist_add (vty_io vio, const char* cmd_line) ;
-extern void uty_cli_enter_more_wait(vty_io vio) ;
-extern void uty_cli_exit_more_wait(vty_io vio) ;
+extern void uty_cli_out(vty_cli cli, const char *format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern void uty_cli_out_newline(vty_cli cli) ;
+extern void uty_cli_out_clear(vty_cli cli) ;
+extern void uty_cli_write(vty_cli cli, const char *this, int len) ;
+extern void uty_cli_wipe(vty_cli cli, int len) ;
-extern void uty_free_host_name(void) ;
-extern void uty_check_host_name(void) ;
+extern void uty_cli_set_lines(vty_cli cli, int lines, bool explicit) ;
+extern void uty_cli_set_window(vty_cli cli, int width, int height) ;
+extern void uty_cli_enter_more_wait(vty_cli cli) ;
+extern void uty_cli_exit_more_wait(vty_cli cli) ;
-extern bool uty_cli_draw_if_required(vty_io vio) ;
+extern bool uty_cli_draw_if_required(vty_cli cli) ;
-extern void uty_cli_pre_monitor(vty_io vio, size_t len) ;
-extern int uty_cli_post_monitor(vty_io vio, const char* buf, size_t len) ;
+extern void uty_cli_pre_monitor(vty_cli cli, size_t len) ;
+extern int uty_cli_post_monitor(vty_cli cli, const char* buf, size_t len) ;
#endif /* _ZEBRA_VTY_CLI_H */
diff --git a/lib/vty_io.c b/lib/vty_io.c
index c116e6b9..4b50c526 100644
--- a/lib/vty_io.c
+++ b/lib/vty_io.c
@@ -24,10 +24,14 @@
#include "vty.h"
#include "vty_io.h"
#include "vty_io_term.h"
+#include "vty_io_file.h"
#include "vty_cli.h"
+#include "vty_command.h"
#include "qstring.h"
#include "keystroke.h"
#include "list_util.h"
+#include "command_parse.h"
+#include "command_execute.h"
#include "memory.h"
@@ -45,20 +49,18 @@
/*==============================================================================
* Basic output to VTY.
- *
- *
*/
/*------------------------------------------------------------------------------
- * UTY output function -- cf fprintf
+ * VTY output -- cf fprintf ! Same as vty_out, less the VTY_LOCK().
*
- * NB: this is NOT suppressed by ! vty->output_enabled
+ * This is for command output, which may later be suppressed
*
* Returns: >= 0 => OK
* < 0 => failed (see errno)
*/
extern int
-uty_output(struct vty *vty, const char *format, ...)
+uty_out(vty_io vio, const char *format, ...)
{
int ret ;
va_list args ;
@@ -66,122 +68,10 @@ uty_output(struct vty *vty, const char *format, ...)
VTY_ASSERT_LOCKED() ;
va_start (args, format) ;
- ret = uty_vprintf(vty, format, args) ;
+ ret = uty_vprintf(vio, format, args) ;
va_end (args) ;
return ret ;
-}
-
-/*------------------------------------------------------------------------------
- * UTY reflect command line, if not already reflected
- *
- * NB: this is NOT suppressed by ! vty->output_enabled
- *
- * Returns: >= 0 => OK
- * < 0 => failed (see errno)
- */
-extern int
-uty_reflect(struct vty *vty)
-{
- int ret ;
-
- if (!vty->reflected)
- ret = uty_output(vty, "%s\n", vty->buf) ;
- else
- ret = 0 ;
-
- vty->reflected = true ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * VTY output function -- cf vfprintf
- *
- * 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_vprintf(struct vty *vty, const char *format, va_list args)
-{
- vio_vf vf ;
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- vf = vty->vio->vout ;
-
- switch (vf->vout_type)
- {
- case VOUT_NONE:
- ret = 0 ;
- break ;
-
- case VOUT_TERM:
- ret = uty_term_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_SHELL:
- ret = uty_shell_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_FILE:
- ret = uty_file_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_PIPE:
- ret = uty_pipe_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_STDOUT:
- ret = uty_stdout_vprintf(vf, format, args) ;
- break ;
-
- case VOUT_STDERR:
- ret = uty_stderr_vprintf(vf, format, args) ;
- break ;
-
- default:
- zabort("impossible VTY Output type") ;
- } ;
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Clear the contents of the command output FIFO etc.
- *
- * 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_clear(vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->vout != NULL)
- {
- vio_fifo_clear(vio->vout->obuf) ;
- vio_lc_clear(vio->vout->olc) ;
- } ;
} ;
/*==============================================================================
@@ -238,7 +128,7 @@ uty_watch_dog_stop(void)
static vty_timer_time
uty_watch_dog_bark(vio_timer_t* timer, void* info)
{
- uty_check_host_name() ; /* check for host name change */
+ cmd_host_name(true) ; /* check for host name change */
uty_death_watch_scan(false) ; /* scan the death-watch list */
@@ -248,11 +138,12 @@ uty_watch_dog_bark(vio_timer_t* timer, void* info)
/*------------------------------------------------------------------------------
* Scan the death watch list.
*
- * A vty may finally be freed if it is closed and there is no command in
- * progress.
+ * A vty can finally be freed if it is closed and there is no command running.
+ *
+ * At curtains the command running state is forced off.
*/
static bool
-uty_death_watch_scan(bool final)
+uty_death_watch_scan(bool curtains)
{
vty_io vio ;
vty_io next ;
@@ -263,21 +154,19 @@ uty_death_watch_scan(bool final)
vio = next ;
next = sdl_next(vio, vio_list) ;
- if (final && !vio->closed)
-
-
- if (vio->closed)
+ /* If this is curtains, override cmd_running ! */
+ if (curtains)
+ vio->cmd_running = false ;
-
- if (vio->closed && !vio->cmd_in_progress)
+ if (uty_close(vio, curtains, NULL))
{
- uty_close(vio) ; /* closes again to ensure that all buffers
- are released. */
+ vty vty = vio->vty ;
sdl_del(vio_death_watch, vio, vio_list) ;
- XFREE(MTYPE_VTY, vio->vty) ;
- XFREE(MTYPE_VTY, vio) ;
+ cmd_exec_free(vty->exec) ;
+ XFREE(MTYPE_VTY, vty->vio) ;
+ XFREE(MTYPE_VTY, vty) ;
} ;
} ;
@@ -288,189 +177,128 @@ uty_death_watch_scan(bool final)
* Prototypes.
*/
-static void uty_vf_half_close(vio_vf vf) ;
-static void uty_vf_close(vio_vf vf) ;
+static void uty_vf_read_close(vio_vf vf) ;
+static bool uty_vf_write_close(vio_vf vf, bool final) ;
+static vio_vf uty_vf_free(vio_vf vf) ;
/*==============================================================================
* Creation and destruction of VTY objects
*/
/*------------------------------------------------------------------------------
- * Allocate new vty struct
- *
- * VTY_TERMINAL a telnet terminal server
- *
- * Must be in cli thread.
- *
- * Requires fd = socket for telnet terminal.
- *
- * VTY_SHELL_SERVER a vty_shell server
- *
- * Must be in cli thread.
+ * Allocate new vty structure, including empty vty_io and empty execution
+ * structures.
*
- * Requires fd = socket for talking to the VTY_SHELL_CLIENT
+ * Caller must complete the initialisation of the vty_io, which means:
*
- * VTY_SHELL_CLIENT a vty_shell client
+ * * constructing a suitable vio_vf and doing uty_vin_open() to set the
+ * vin_base.
*
- * VTY_CONFIG_READ configuration file reader
+ * All vty_io MUST have a vin_base, even if it is /dev/null.
*
- * Requires fd = file descriptor for reading config file
+ * * constructing a suitable vio_vf and doing uty_vout_open() to set the
+ * vout_base.
*
- * VTY_STDOUT stdout
- * VTY_STDERR stderr
+ * All vty_io MUST have a vout_base, even if it is /dev/null.
*
+ * * setting the vio->obuf to the vout_base obuf... TODO ???
*
+ * * setting vio->cli, if required.
*
- *
- *
- *
- *
- *
- * Allocates and initialises basic vty and vty_io structures, setting the
- * given type.
- *
- * Note that where is not setting up a vty_file, this *may* be called from
- * any thread.
- *
- * Returns: new vty
+ * Caller must also set the initial vty->node, if any.
*/
-extern struct vty *
-uty_new(enum vty_type type, int fd)
+extern vty
+uty_new(vty_type_t type)
{
vty vty ;
vty_io vio ;
+ bool execution ;
+
VTY_ASSERT_LOCKED() ;
/* 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_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 !)
- * closed = 0 -- NOT closed (important !)
- * close_reason = NULL -- no reason, yet
+ vty = XCALLOC(MTYPE_VTY, sizeof(struct vty)) ;
+ /* Zeroising the vty structure has set:
*
- * real_type = 0 -- not material
- * file_fd = 0 -- not material
- * file_error = 0 -- not material
+ * type = X -- set to actual type, below
*
- * key_stream = NULL -- no key stream (always empty, at EOF)
+ * node = NULL_NODE -- set to something sensible elsewhere
*
- * cli_drawn = 0 -- not drawn
- * cli_dirty = 0 -- not dirty
- * cli_prompt_len = 0 )
- * cli_extra_len = 0 ) not material
- * cli_echo_suppress = 0 )
+ * index = NULL -- nothing, yet
+ * index_sub = NULL -- nothing, yet
*
- * cli_prompt_node = 0 -- not material
- * cli_prompt_set = 0 -- so prompt needs to be constructed
+ * config = false -- not in configure mode
*
- * 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--"
+ * execution = X -- set below
+ * vio = X -- set below
+ */
+ confirm(NULL_NODE == 0) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+
+ vty->type = type ;
+
+ vio = XCALLOC(MTYPE_VTY, sizeof(struct vty_io)) ;
+
+ /* Zeroising the vty_io structure has set:
*
- * cli_more_enabled = 0 -- not enabled for "--more--"
+ * vty = X -- set to point to parent vty, below
*
- * cmd_out_done = 0 -- not material
+ * name = NULL -- no name, yet TODO ???
*
- * cli_do = 0 == cli_do_nothing
+ * vin = NULL -- empty input stack
+ * vin_base = NULL -- empty input stack
+ * vin_depth = 0 -- no stacked vin's, yet
*
- * cmd_lc = NULL -- no line control
+ * vout = NULL -- empty output stack
+ * vout_base = NULL -- empty output stack
+ * vout_depth = 0 -- no stacked vout's, yet
*
- * fail = 0 -- no login failures yet
+ * vout_closing = NULL -- nothing closing yet
*
- * hist = empty vector
- * hp = 0 -- at the beginning
- * hindex = 0 -- the beginning
+ * vio_list = NULLs -- not on the vio_list, yet
+ * mon_list = NULLs -- not on the monitors list
*
- * width = 0 -- unknown console width
- * height = 0 -- unknown console height
+ * blocking = X -- set below: false unless VTY_CONFIG_READ
+ * cmd_running = false -- no commands running, yet
*
- * lines = 0 -- no limit
- * lines_set = 0 -- no explicit setting
+ * state = X -- set vf_open, below.
*
- * monitor = 0 -- not a monitor
- * monitor_busy = 0 -- not a busy monitor
+ * close_reason = NULL -- not closed, yet
*
- * config = 0 -- not holder of "config" mode
+ * obuf = NULL -- no output buffer, yet
*/
- confirm(cli_do_nothing == 0) ;
- confirm(AUTH_NODE == 0) ; /* default node type */
-
- vty->type = type ;
-
+ vty->vio = vio ;
+ vio->vty = vty ;
+ vio->blocking = (type == VTY_CONFIG_READ) ;
+ vio->state = vf_open ;
+ /* Create and initialise the command execution environment (if any) */
+ execution = true ;
- /* 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 this is a VTY_TERM or a VTY_SHELL, place */
- switch (type)
+ switch(type)
{
- case VTY_TERMINAL: /* Require fd -- Telnet session */
- VTY_ASSERT_CLI_THREAD() ;
- assert(fd >= 0) ;
-
- uty_term_new(vio, fd) ;
- break ;
-
- case VTY_SHELL_SERVER: /* Require fd -- Unix socket */
- VTY_ASSERT_CLI_THREAD() ;
- assert(fd >= 0) ;
-
-
- break ;
-
- case VTY_CONFIG_READ: /* Require fd -- file to read */
- assert(fd >= 0) ;
+ case VTY_TERMINAL:
+ case VTY_SHELL_SERVER:
+ case VTY_SHELL_CLIENT:
+ case VTY_CONFIG_READ:
+ execution = true ;
break ;
case VTY_STDOUT:
case VTY_STDERR:
- case VTY_SHELL_CLIENT:
- fd = -1 ; /* No fd -- output to stdout/stderr */
+ execution = false ;
break ;
default:
- zabort("unknown VTY type") ;
+ zabort("unknown vty type") ;
} ;
-
-
-
- /* 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) ;
-
- vio_fifo_init_new(&vio->cli_obuf, 2 * 1024) ; /* allocate in 2K lumps */
-
- vio_fifo_init_new(&vio->cmd_obuf, 8 * 1024) ;
+ if (execution)
+ vty->exec = cmd_exec_new(vty) ;
/* Place on list of known vio/vty */
sdl_push(vio_list_base, vio, vio_list) ;
@@ -483,73 +311,127 @@ uty_new(enum vty_type type, int fd)
*
* Sets the vf->vin_type and set vf->read_open.
*
- * Sets the read ready action and the read timer timeout action.
+ * Initialises an input buffer if required, and sets line_complete and
+ * line_step so that first attempt to fetch a line will give line 1.
*
- * NB: may add a VIN_NONE *only* as the first vin item.
+ * Sets the read ready action and the read timer timeout action.
*
- * Can have a write only VTY_XXX object, which requires a vin entry so that
- * do not have to everywhere deal with NULL vio_vf pointers.
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
*
- * But for subsequent write only vio_vf objects, must not add to the vin
- * stack.
+ * That is not done here because the vin state may not be complete (in
+ * particular the vin->parse_type and vin->reflect_enabled !).
*/
extern void
-uty_vin_add(vty_io vio, vio_vf vf, vio_in_type_t type,
- vio_fd_action* read_action, vio_timer_action* read_timer_action)
+uty_vin_open(vty_io vio, vio_vf vf, vio_in_type_t type,
+ vio_vfd_action* read_action,
+ vio_timer_action* read_timer_action,
+ usize ibuf_size)
{
vf->vin_type = type ;
- vf->read_open = true ;
+ vf->vin_state = vf_open ;
- vio_fd_set_read_action(vf->vfd, read_action) ;
- vio_fd_set_read_timeout_action(vf->vfd, read_timer_action) ;
+ if (type < VIN_SPECIALS)
+ {
+ vio_vfd_set_read_action(vf->vfd, read_action) ;
+ vio_vfd_set_read_timeout_action(vf->vfd, read_timer_action) ;
+ } ;
ssl_push(vio->vin, vf, vin_next) ;
if (vio->vin_base == NULL)
- vio->vin_base = vf ;
+ {
+ assert(vio->vin_depth == 0) ;
+ vio->vin_base = vf ;
+ }
else
- assert(type != VIN_NONE) ;
+ {
+ assert(type != VIN_NONE) ;
+ ++vio->vin_depth ;
+ } ;
+
+ if (ibuf_size != 0)
+ {
+ vf->ibuf = vio_fifo_init_new(NULL, ibuf_size) ;
+
+ vf->line_complete = true ;
+ vf->line_step = 1 ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * Add a new vf to the vio->vout stack, and set write stuff.
+ * Push a new vf to the vio->vout stack, and set write stuff.
*
* Sets the vf->vout_type and set vf->write_open.
*
* Sets the write ready action and the write timer timeout action.
*
- * NB: may add a VOUT_NONE *only* as the first vout item.
+ * Initialises an output buffer and sets an end_mark.
+ *
+ * NB: VOUT_DEV_NULL, VOUT_STDOUT and VOUT_STDERR are special.
+ *
+ * The write_action and the write_timer_action are ignored.
*
- * Can have a read only VTY_XXX object, which requires a vout entry so that
- * do not have to everywhere deal with NULL vio_vf pointers.
+ * All actual I/O to these outputs is direct, blocking and via standard
+ * I/O -- except VOUT_DEV_NULL where all I/O is discarded.
*
- * But for subsequent read only vio_vf objects, must not add to the vout
- * stack.
+ * NB: all outputs are set up with an obuf, so all output is collected, even
+ * if it is later to be discarded.
+ *
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
+ *
+ * That is not done here because the vout state may not be complete (in
+ * particular the vout->out_enabled !).
*/
extern void
-uty_vout_add(vty_io vio, vio_vf vf, vio_out_type_t type,
- vio_fd_action* write_action, vio_timer_action* write_timer_action)
+uty_vout_open(vty_io vio, vio_vf vf, vio_out_type_t type,
+ vio_vfd_action* write_action,
+ vio_timer_action* write_timer_action,
+ usize obuf_size)
{
vf->vout_type = type ;
- vf->write_open = true ;
+ vf->vout_state = vf_open ;
- vio_fd_set_write_action(vf->vfd, write_action) ;
- vio_fd_set_write_timeout_action(vf->vfd, write_timer_action) ;
+ if (type < VOUT_SPECIALS)
+ {
+ vio_vfd_set_write_action(vf->vfd, write_action) ;
+ vio_vfd_set_write_timeout_action(vf->vfd, write_timer_action) ;
+ } ;
ssl_push(vio->vout, vf, vout_next) ;
if (vio->vout_base == NULL)
- vio->vout_base = vf ;
+ {
+ assert(vio->vout_depth == 0) ;
+ vio->vout_base = vf ;
+ }
else
- assert(type != VOUT_NONE) ;
-} ;
-
-
-
+ {
+ assert(type != VOUT_NONE) ;
+ ++vio->vout_depth ;
+ } ;
+ vf->obuf = vio_fifo_init_new(NULL, obuf_size) ;
+ vio_fifo_set_end_mark(vf->obuf) ;
+ vio->obuf = vio->vout->obuf ;
+} ;
+/*------------------------------------------------------------------------------
+ * Set timeout value.
+ *
+ * This is only ever called when a command (eg exec-timeout) sets a new
+ * time out value -- which applies only to VIN_TERM and VTY_VTYSH.
+ */
+extern void
+uty_set_timeout(vty_io vio, vty_timer_time timeout)
+{
+ vio_in_type_t vt ;
+ VTY_ASSERT_LOCKED() ;
+ vt = vio->vin_base->vin_type ;
+ if ((vt == VIN_TERM) || (vt == VIN_VTYSH))
+ uty_vf_set_read_timeout(vio->vin_base, timeout) ;
+} ;
/*------------------------------------------------------------------------------
* Set/Clear "monitor" state:
@@ -561,10 +443,10 @@ extern void
uty_set_monitor(vty_io vio, bool on)
{
VTY_ASSERT_LOCKED() ;
-
+#if 0
if (on && !vio->monitor)
{
- if ((vio->vty->type == VTY_TERMINAL) && !vio->half_closed)
+ if ((vio->vty->type == VTY_TERMINAL) && !vio->closing)
{
vio->monitor = 1 ;
sdl_push(vio_monitors_base, vio, mon_list) ;
@@ -575,267 +457,278 @@ uty_set_monitor(vty_io vio, bool on)
vio->monitor = 0 ;
sdl_del(vio_monitors_base, vio, mon_list) ;
}
+#endif
} ;
/*------------------------------------------------------------------------------
- * Return "name" of VTY
+ * Return "name" of VTY.
*
- * For VTY_TERM this is the IP address of the far end of the telnet connection.
+ * The name of the base vin, or (failing that) the base vout.
*/
extern const char*
uty_get_name(vty_io vio)
{
- return (vio->name != NULL) ? vio->name : "?" ;
+ const char* name ;
+
+ name = vio->vin_base->name ;
+ if (name == NULL)
+ name = vio->vout_base->name ;
+
+ return (name != NULL) ? name : "?" ;
} ;
/*------------------------------------------------------------------------------
- * Close all the readers.
+ * Close all vin excluding the vin_base.
*
- * Half-close everything on the vin stack. Empties the vin stack down to the
- * base entry. Discards any read-only vio_vf (except for last vin entry).
+ * This is done on close and when there is a command error.
+ */
+extern void
+uty_vin_close_stack(vty_io vio)
+{
+ while (vio->vin != vio->vin_base)
+ uty_vin_close(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close all vout except for the vout_base.
*
- * VIO is placed on death watch, and will stay there until:
+ * This is done on close and when there is a command error.
*
- * * outstanding commands complete.
+ * Should only be "final" on a call from the watch-dog !
+ */
+extern void
+uty_vout_close_stack(vty_io vio, bool final)
+{
+ while (vio->vout != vio->vout_base)
+ uty_vout_close(vio, final) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close VTY -- may be called any number of times !
*
- * * outstanding output completes, or times out, or program terminates.
+ * If no reason for the close has already been set, sets the given reason.
+ * The close reason is reported at the end of the output to the vout_base.
*
- * For VTY_TERMINAL (must be in CLI thread):
+ * Once a VTY has been closed, it will provide no further input. However, until
+ * "final" close, it will continue to output anything which is pending at the
+ * time of the close, which includes: (a) anything in the output buffer,
+ * (b) any further output from a then running command and (c) anything that an
+ * out_pipe may be in the process of returning.
*
- * * shut the socket for reading
- * * discard all buffered input, setting it to "EOF"
- * * turns off any monitor status !
- * * drop down to RESTRICTED_NODE
+ * The main complication is that in a multi-threaded world there may be a
+ * vio->cmd_running. The close shuts down everything on the input stack, so
+ * any active command loop will come to a halt as soon as the current command
+ * does complete. The output stack is preserved, but will be closed on
+ * completion of the command.
*
- * For VTY_SHELL_SERVER (must be in CLI thread):
+ * Closing a VTY places it on the death-watch list. Once any pending output
+ * has been dealt with and any cmd_running has completed, then the death-watch
+ * will apply the coup de grace.
*
- * * shut the socket for reading
- * * discard all buffered input
- * * drop down to RESTRICTED_NODE
+ * Getting the death-watch to finally close the VTY also allows the vty to be
+ * closed from somewhere deep (e.g. when there is an I/O error) without
+ * destroying the VTY and upsetting code that might not realise the VTY has
+ * vanished.
*
- * If no reason for the close has already been set, sets the given reason.
+ * The close sequence is:
+ *
+ * 1. if a close reason has not already been set, set any given one.
+ *
+ * 2. if not already closing -- ie this is the first call of uty_close()
+ *
+ * a. transfer to death-watch list & set closing.
+ *
+ * b. turn off any monitor state
+ *
+ * c. close down the input/read side completely.
+ *
+ * Empties the vin stack down to vin_base, closing all input.
+ *
+ * Close the vin_base. The vin_base is left !read_open.
+ *
+ * All read-only vio_vf (except the vin_base) are freed.
+ *
+ * Note that everything is read closed before anything is write
+ * closed.
+ *
+ * A vio_vf is read closed once and only once.
+ *
+ * The vin_base is closed only by this function. Until the final
+ * uty_close, there is always at least the vin_base, even if it is
+ * read_closed.
+ *
+ * 3. try to close everything on the vout_closing list.
+ *
+ * even if cmd_running
*
- * If already half-closed, does nothing else.
*
- * Returns true <=> first half close.
*/
-static bool
-uty_do_half_close(vty_io vio, const char* reason)
+extern bool
+uty_close(vty_io vio, bool final, qstring reason)
{
- vio_vf vf ;
+ vio_vf vf_next ;
VTY_ASSERT_LOCKED() ;
- if ((vio->close_reason == NULL) && (reason != NULL))
- vio->close_reason = XSTRDUP(MTYPE_TMP, reason) ;
+ /* quit if already closed */
+ if (vio->state == vf_closed)
+ return true ;
- if (vio->half_closed)
- return false ;
+ /* Set the close reason, if not already set. */
+ if (reason != NULL)
+ {
+ if (vio->close_reason == NULL)
+ vio->close_reason = reason ;
+ else
+ qs_reset(reason, free_it) ;
+ } ;
- /* Half close everything on the vin stack.
- *
- * Leave stack with just the base entry (closed for read).
+ /* If not already closing, set closing and transfer to the death watch
+ * list -- turn off any "monitor" status immediately.
*/
- while (1)
+ if (vio->state == vf_open)
{
- vf = vio->vin ; /* Current first on list */
+ vio->state = vf_closing ;
- uty_vf_half_close(vf) ; /* fd level half close etc. */
+ /* Transfer to the death-watch */
+ sdl_del(vio_list_base, vio, vio_list) ;
+ sdl_push(vio_death_watch, vio, vio_list) ;
- switch(vf->vin_type) /* tidy up each type */
- {
- case VIN_NONE:
- break ;
+ /* TODO turn off any "monitor" IMMEDIATELY. */
- case VIN_TERM:
- uty_term_half_close(vf) ;
- uty_cli_close(vio) ; /* tell the CLI to stop */
- break ;
+ /* Flush the vin stack.
+ *
+ * Where possible this will revoke commands bring command processing to
+ * as sudden a halt as possible -- though may still be processing
+ * commands.
+ */
+ uty_vin_close_stack(vio) ;
+ assert(vio->vin == vio->vin_base) ;
+ uty_vf_read_close(vio->vin) ;
+ } ;
+
+ /* If there is anything on the vout_closing list, give it a shove in case
+ * that manages to close anything -- which it will do if "final".
+ */
+ vf_next = vio->vout_closing ;
+ while (vf_next != NULL)
+ {
+ vio_vf vf = vf_next ;
+ vf_next = ssl_next(vf, vout_next) ;
- case VIN_SHELL:
- break ;
+ uty_vf_write_close(vf, final) ; /* removes from vout_closing if
+ is now completely closed. */
+ } ;
- case VIN_FILE:
- break ;
+ /* If is vio->cmd_running, this is as far as we can go this time. */
+ if (vio->cmd_running)
+ return false ;
- case VIN_PIPE:
- break ;
+ /* Make sure no longer holding the config symbol of power */
+ uty_config_unlock(vio->vty, NULL_NODE) ;
- case VIN_CONFIG:
- break ;
+ /* Flush the vout stack.
+ *
+ * If cannot completely close a vf, places it on the vout_closing list,
+ * except for the vout_base.
+ */
+ uty_vout_close_stack(vio, final) ;
+ assert(vio->vout == vio->vout_base) ;
+ uty_vf_write_close(vio->vout, final) ;
- default:
- zabort("unknown VIN type") ;
- } ;
+ /* See if have now successfully closed everything. */
+ if ((vio->vout_closing != NULL) || (vio->vout_base->vout_state != vf_closed))
+ return false ; /* something is still open */
- /* Finished if half closed the last on the list */
- if (vf == vio->vin_base)
- break ;
+ /* Empty everything out of the vio and return the good news.
+ *
+ * NB: retains vio->vty & the vio->vio_list for death-watch.
+ */
+ assert((vio->vin == vio->vin_base) && (vio->vin_depth == 0)) ;
- /* Hack off head of list. If it is read-only, close & free. */
- ssl_del_head(vio->vin, vin_next) ;
+ if (vio->vin != vio->vout)
+ vio->vin = uty_vf_free(vio->vin) ;
+ else
+ vio->vin = NULL ;
+ vio->vin_base = NULL ;
- if (vf->vout_type == VOUT_NONE)
- uty_vf_close(vf) ;
- } ;
+ assert((vio->vout == vio->vout_base) && (vio->vout_depth == 0)) ;
- /* Make sure no longer holding the config symbol of power */
- uty_config_unlock(vio->vty, RESTRICTED_NODE) ;
+ vio->vout = uty_vf_free(vio->vout) ;
- /* Move to the death watch list */
- sdl_del(vio_list_base, vio, vio_list) ;
- sdl_push(vio_death_watch, vio, vio_list) ;
+ vio->close_reason = qs_reset(vio->close_reason, free_it) ;
- vio->half_closed = true ;
+ vio->obuf = NULL ;
+ vio->state = vf_closed ;
return true ;
} ;
/*------------------------------------------------------------------------------
- * Close VTY.
- *
+ * Pop and close the top of the vin stack -- MUST NOT be vin_base.
*
+ * Read closes the vio_vfd -- if the vio_vfd was read-only, this will fully
+ * close it, and the vio_vfd will have been freed.
*
- * If no reason for the close has already been set, sets the given reason.
- *
- * If not already half-closed, close all readers as described above, and then
- * kick everything on the vout stack and apply VTY_HALF_CLOSE_TIMEOUT.
+ * If this is read-only will free the vio_vf and all its contents.
*
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
*
+ * That is not done here because this may be called from outside the
+ * command loop -- in particular by uty_close().
*/
extern void
-uty_close (vty_io vio, const char* reason)
+uty_vin_close(vty_io vio)
{
- if (uty_do_half_close(vio, reason))
- {
- vio_vf vf ;
+ vio_vf vf ;
- /* Run down the vout stack, and write-ready enable everything with
- * the half close timeout set.
- *
- * This will have the effect of kicking all output.
- */
- vf = vio->vout ;
- while (vf != NULL)
- {
- vf->write_timeout = 0 ;
- uty_vf_set_write(vf, on) ;
+ vf = vio->vin ;
- vf = ssl_next(vf, vout_next) ;
- } ;
- } ;
+ assert(vf != vio->vin_base) ;
+ assert(vio->vin_depth > 0) ;
+
+ ssl_del_head(vio->vin, vin_next) ;
+ --vio->vin_depth ;
+
+ uty_vf_read_close(vf) ;
} ;
/*------------------------------------------------------------------------------
- * Closing down VTY.
- *
- * Shuts down everything and discards all buffers etc. etc.
+ * Pop and close the top of the vout stack -- MUST NOT be vout_base.
*
- * If cmd_in_progress, cannot complete the process -- but sets the closed
- * flag.
+ * Moves the vio_vf to the vout_closing list.
*
- * Can call vty_close() any number of times.
+ * Before closing, push any outstanding output.
*
- * The vty structure is placed on death watch, which will finally free the
- * structure once no longer cmd_in_progress.
+ * Unless "final", the close is soft, that is, if there is any output still
+ * outstanding does not actually close the vout.
*
+ * If there is no outstanding output (or if final) will completely close the
+ * vf and free it.
*
- * If no reason for the close has already been set, sets the given reason.
+ * NB: a uty_cmd_prepare() is required before command processing can continue.
*
+ * That is not done here because this may be called from outside the
+ * command loop -- in particular by uty_close().
*/
extern void
-uty_close_final(vty_io vio, const char* reason)
+uty_vout_close(vty_io vio, bool final)
{
- VTY_ASSERT_LOCKED() ;
-
- /* If not already closed, close. */
- uty_do_half_close(vio, reason) ; /* set reason if required, and
- make sure is half closed. */
- if (!vio->closed)
- {
- vio_vf vf ;
-
- /* Empty the vin stack */
- assert(vio->vin != NULL) ;
- assert(vio->vin == vio->vin_base) ;
- if (vio->vin != vio->vout_base)
- uty_vf_close(vio->vin) ;
- vio->vin_base = vio->vin = NULL ;
-
- /* Now kick everything in the vout stack, in case can get stuff
- * written away. And then close.
- */
- while (ssl_pop(&vf, vio->vout, vout_next) != NULL)
- {
- uty_vf_set_write(vf, off) ; /* stop any write ready... */
- vf->closing = true ; /* ...permanently. */
-
- switch(vf->vout_type) /* tidy up each type */
- {
- case VOUT_NONE:
- break ;
-
- case VOUT_TERM:
- break ;
-
- case VOUT_SHELL:
- break ;
-
- case VOUT_FILE:
- break ;
-
- case VOUT_PIPE:
- break ;
-
- case VOUT_STDOUT:
- break ;
-
- case VOUT_STDERR:
- break ;
-
- default:
- zabort("unknown VOUT type") ;
- } ;
-
- uty_vf_close(vf) ; /* close and free */
- } ;
+ vio_vf vf ;
- vio->vout_base = NULL ; /* stack is empty */
- vio->closed = true ; /* now closed */
- } ;
+ vf = vio->vout ;
- /* 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) ;
+ assert(vf != vio->vout_base) ;
+ assert(vio->vout_depth > 0) ;
- vio->key_stream = keystroke_stream_free(vio->key_stream) ;
+ ssl_del_head(vio->vout, vout_next) ;
+ --vio->vout_depth ;
- qs_reset(&vio->cli_prompt_for_node, keep_it) ;
- qs_reset(&vio->cl, keep_it) ;
+ ssl_push(vio->vout_closing, vf, vout_next) ;
- {
- qstring line ;
- while ((line = vector_ream(vio->hist, keep_it)) != NULL)
- qs_reset_free(line) ;
- } ;
+ vio->obuf = vio->vout->obuf ;
- /* 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_reset(&vio->clx, keep_it) ;
- vio->vty->buf = NULL ;
- } ;
+ uty_vf_write_close(vf, final) ;
} ;
/*==============================================================================
@@ -847,96 +740,294 @@ uty_close_final(vty_io vio, const char* reason)
*
* There are no errors, yet.
*
- * This leaves most things unset/NULL/false. Caller will want to set:
+ * This leaves most things unset/NULL/false. Caller will need to:
+ *
+ * - uty_vin_open() and/or uty_vout_open()
+ *
+ * - once those are done, the following optional items remain to be set
+ * if they are required:
+ *
+ * read_timeout -- default = 0 => no timeout
+ * write_timeout -- default = 0 => no timeout
+ *
+ * parse_type -- default = cmd_parse_standard
+ * reflect_enabled -- default = false
+ * out_enabled -- default = true iff vfd_io_write
+ *
+ * If vio->blocking, adds vfd_io_blocking to the io_type.
*
+ * NB: if there is no fd for this vio_vf, it should be set to -1, and the
+ * type (recommend vfd_none) and io_type are ignored -- except for
+ * vfd_io_write and the out_enabled state.
*
+ * A VTY_STDOUT or a VTY_STDERR (output only, to the standard I/O) can
+ * be set up without an fd, and with/without out_enabled.
*
- * Sets timeout to no timeout at all -- timeout is optional.
+ * A VTY_CONFIG_READ can be set up with the fd of the input file, but
+ * MUST be type = vfd_file and io_type = vfd_io_read -- because the fd
+ * is for input only. This will set out_enabled false (as required).
+ * The required VOUT_STDERR will be set by uty_vout_open().
*/
extern vio_vf
-uty_vf_new(vty_io vio, int fd, vfd_type_t type, vfd_io_type_t io_type)
+uty_vf_new(vty_io vio, const char* name, int fd, vfd_type_t type,
+ vfd_io_type_t io_type)
{
vio_vf vf ;
VTY_ASSERT_LOCKED() ;
+ if (vio->blocking)
+ io_type |= vfd_io_blocking ;
+
vf = XCALLOC (MTYPE_VTY, sizeof(struct vio_vf)) ;
/* Zeroising the structure has set:
*
- * vin_type = 0 -- VIN_NONE
- * vin_next = NULL -- not on a vin list, yet
+ * vio = X -- set below
+ * name = X -- set below
+ *
+ * vin_type = VIN_NONE -- see uty_vin_open()
+ * vin_state = vf_closed -- see uty_vin_open()
+ * vin_next = NULL -- see uty_vin_open()
+ *
+ * cli = NULL -- no CLI, yet
+ *
+ * ibuf = NULL -- none, yet -- see uty_vin_open()
+ *
+ * cl = zeros -- empty qstring (embedded)
+ * line_complete = false -- see uty_vout_open()
+ * line_number = 0 -- nothing yet
+ * line_step = 0 -- see uty_vout_open()
+ *
+ * parse_type = cmd_parse_standard
+ * reflect_enabled = false
*
- * vout_type = NULL -- VOUT_NONE
- * vout_next = NULL -- not on a vout list, yet
+ * vout_type = VOUT_NONE -- see uty_vout_open()
+ * vout_state = vf_closed -- see uty_vout_open()
+ * vout_next = NULL -- see uty_vout_open()
*
- * obuf = NULL -- none, yet
- * olc = NULL -- none, yet
+ * obuf = NULL -- none, yet -- see uty_vout_open()
*
- * blocking = false
- * closing = false
+ * out_enabled = X -- set below: true iff vfd_io_write
*
- * read_open = false
- * write_open = false
- * error_seen = 0 -- no error seen, yet
+ * vfd = NULL -- no vfd, yet
*
- * read_timeout = 0 -- none
- * write_timeout = 0 -- none
+ * blocking = false -- default is non-blocking I/O
+ * closing = false -- definitely not
+ *
+ * error_seen = 0 -- no error seen, yet
+ *
+ * read_on = false
+ * write_on = false
+ *
+ * read_timeout = 0 -- none
+ * write_timeout = 0 -- none
*/
confirm((VIN_NONE == 0) && (VOUT_NONE == 0)) ;
+ confirm(vf_closed == 0) ;
+ confirm(QSTRING_INIT_ALL_ZEROS) ;
+ confirm(cmd_parse_standard == 0) ;
vf->vio = vio ;
- vf->vfd = vio_fd_new(fd, type, io_type, vf) ;
+
+ if (name != NULL)
+ vf->name = XSTRDUP(MTYPE_VTY_NAME, name) ;
+
+ if (fd >= 0)
+ vf->vfd = vio_vfd_new(fd, type, io_type, vf) ;
+
+ vf->out_enabled = ((io_type & vfd_io_write) != 0) ;
return vf ;
} ;
/*------------------------------------------------------------------------------
- * Half close a vio_vf.
+ * Close the read side of the given vio_vf.
+ *
+ * Read closes the vio_vfd -- if the vio_vfd was read-only, this will fully
+ * close it, and the vio_vfd will have been freed.
+ *
+ * If this is read-only (ie vout_type == VOUT_NONE) will free the vio_vf as
+ * well as the vio_vfd -- unless this is the vin_base.
*
- * Half closes the vio_fd and shuts down all reading. If the vio_fd was
- * read-only, the half-close will fully close it, and the vio_fd will have
- * been freed.
+ * Note that read_close is effective immediately, there is no delay as there is
+ * with write_close -- so no need for a "final" option.
*/
static void
-uty_vf_half_close(vio_vf vf)
+uty_vf_read_close(vio_vf vf)
{
- /* the vfd level half close will close completely if is read only. */
- vf->vfd = vio_fd_half_close(vf->vfd) ;
+ assert(vf->vin_state != vf_closed) ;
+
+ /* Do the vfd level read close and mark the vf no longer read_open */
+ if (vf->vin_type < VIN_SPECIALS)
+ vf->vfd = vio_vfd_read_close(vf->vfd) ;
+
+ vf->vin_state = vf_closed ;
+ vf->read_on = off ;
+
+ /* Now the vin_type specific clean up. */
+ switch(vf->vin_type)
+ {
+ case VIN_NONE:
+ zabort("invalid VIN_NONE") ;
+ break ;
+
+ case VIN_TERM:
+ uty_term_read_close(vf) ;
+ break ;
- vf->read_open = false ; /* no more read operations */
- vf->read_on = off ; /* of course */
+ case VIN_VTYSH:
+ break ;
- if (vf->vfd == NULL) /* check really was read-only */
- assert(!vf->write_open && !vf->read_on) ;
+ case VIN_FILE:
+ uty_file_read_close(vf) ;
+ break ;
+
+ case VIN_PIPE:
+ break ;
+
+ case VIN_CONFIG:
+ break ;
+
+ case VIN_DEV_NULL:
+ break ;
+
+ default:
+ zabort("unknown VIN type") ;
+ } ;
+
+ if ((vf->vout_type == VOUT_NONE) && (vf != vf->vio->vin_base))
+ uty_vf_free(vf) ;
} ;
/*------------------------------------------------------------------------------
- * Close given vio_vf and free the vio_vf structure and all its contents.
+ * Close the write side of the given vio_vf, if can.
+ *
+ * Pushes any outstanding output -- non-blocking if not a blocking vty.
*
- * Assumes has been removed from vio->vin and vio->vout !
+ * The vio_vf MUST either be on the vout_closing list or be vout_base.
+ * The vio_vf MUST be write_open and MUST NOT be read_open.
+ *
+ * If output is empty, or if final, close the vf, then if it is on the
+ * vout_closing list, remove and free it.
+ *
+ * Returns whether output was empty or not.
*/
-static void
-uty_vf_close(vio_vf vf)
+static bool
+uty_vf_write_close(vio_vf vf, bool final)
+{
+ bool empty ;
+
+ assert((vf->vout_state != vf_closed) && (vf->vin_state == vf_closed)) ;
+
+ /* Worry about remaining return input from vout pipe TODO */
+
+ /* Worry about appending the close reason to the vout_base TODO */
+
+ /* Now the vout_type specific clean up. */
+ empty = false ;
+
+ switch(vf->vout_type)
+ {
+ case VOUT_NONE:
+ zabort("invalid VOUT_NONE") ;
+ break ;
+
+ case VOUT_TERM:
+ empty = uty_term_write_close(vf, final) ;
+ break ;
+
+ case VOUT_VTYSH:
+ break ;
+
+ case VOUT_FILE:
+ empty = uty_file_write_close(vf, final) ;
+ break ;
+
+ case VOUT_PIPE:
+ break ;
+
+ case VOUT_CONFIG:
+ break ;
+
+ case VOUT_DEV_NULL:
+ case VOUT_STDOUT:
+ case VOUT_STDERR:
+ empty = true ;
+ break ;
+
+ default:
+ zabort("unknown VOUT type") ;
+ } ;
+
+ if (empty || final)
+ {
+ /* Do the vfd level close and mark the vf no longer write_open */
+ if (vf->vout_type < VOUT_SPECIALS)
+ vf->vfd = vio_vfd_close(vf->vfd) ;
+ else
+ assert(vf->vfd == NULL) ;
+
+ vf->vout_state = vf_closed ;
+ vf->write_on = off ;
+
+ if (ssl_del(vf->vio->vout_closing, vf, vout_next))
+ uty_vf_free(vf) ;
+ else
+ assert(vf == vf->vio->vout_base) ;
+ } ;
+
+ return empty ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the given vio_vf structure and all its contents.
+ *
+ * Expects the vfd to already have been closed, but will close it if not.
+ *
+ * Expects any cli to be closed, but will close it if not.
+ *
+ * Assumes has been removed from any and all lists !
+ */
+static vio_vf
+uty_vf_free(vio_vf vf)
{
- vf->vfd = vio_fd_close(vf->vfd) ;
+ XFREE(MTYPE_VTY_NAME, vf->name) ;
+
+ vf->ibuf = vio_fifo_reset(vf->ibuf, free_it) ;
+ vf->obuf = vio_fifo_reset(vf->obuf, free_it) ;
+
+ qs_reset(vf->cl, keep_it) ;
- vf->obuf = vio_fifo_reset_free(vf->obuf) ;
- vf->olc = vio_lc_reset_free(vf->olc) ;
+ vf->cli = uty_cli_close(vf->cli, true) ;
+ vf->vfd = vio_vfd_close(vf->vfd) ; /* for completeness */
XFREE(MTYPE_VTY, vf) ;
+
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
+ *
+ *
+ */
+
+
+
+
+
+/*------------------------------------------------------------------------------
* Dealing with an I/O error on VTY socket
*
* If this is the first error for this VTY, produce suitable log message.
*
* If is a "monitor", turn that off, *before* issuing log message.
*/
-static int
-uty_vf_error(vty_io vio, const char* what)
+extern int
+uty_vf_error(vio_vf vf, const char* what, int err)
{
+ vty_io vio = vf->vio ;
+
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
@@ -944,24 +1035,14 @@ uty_vf_error(vty_io vio, const char* what)
uty_set_monitor(vio, 0) ;
/* if this is the first error, log it */
- if (vio->sock.error_seen == 0)
+ if (vf->error_seen == 0)
{
- const char* type ;
- switch (vio->vty_type)
- {
- case VTY_TERM:
- type = "VTY Terminal" ;
- break ;
- case VTY_SHELL:
- type = "VTY Shell Server" ;
- break ;
- default:
- zabort("unknown VTY type for uty_file_error()") ;
- } ;
-
- vio->sock.error_seen = errno ;
+ const char* type = "?" ; /* TODO */
+
+
+ vf->error_seen = err ;
uzlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
- type, what, vio->sock.fd, errtoa(vio->sock.error_seen, 0).str) ;
+ type, what, vio_vfd_fd(vf->vfd), errtoa(err, 0).str) ;
} ;
return -1 ;
@@ -970,33 +1051,34 @@ uty_vf_error(vty_io vio, const char* what)
/*------------------------------------------------------------------------------
* Set required read ready state. Applies the current read timeout.
*
- * Forces off if: vf->closing
+ * Forces off if: vf->vin_state != vf_open
*
- * Does nothing if: !vf->read_open
+ * Does nothing if: vf->vin_state == vf_closed
* or: vf->vfd == NULL
+ *
+ * Returns new state of vf->read_on
*/
-extern on_off_t
-uty_vf_set_read(vio_vf vf, on_off_t how)
+extern on_off_b
+uty_vf_set_read(vio_vf vf, on_off_b how)
{
- if (vf->closing)
+ if (vf->vin_state != vf_open)
how = off ;
- return vf->read_on = vf->read_open
- ? vio_fd_set_read(vf->vfd, how, vf->read_timeout)
+
+ return vf->read_on = (vf->vin_state != vf_closed)
+ ? vio_vfd_set_read(vf->vfd, how, vf->read_timeout)
: off ;
} ;
/*------------------------------------------------------------------------------
* Set required read ready timeout -- if already read_on, restart it.
*
- * Forces read ready off if: vf->closing
- *
- * Does nothing if: !vf->read_open
- * or: vf->vfd == NULL
+ * Returns new state of vf->read_on
*/
-extern on_off_t
+extern on_off_b
uty_vf_set_read_timeout(vio_vf vf, vty_timer_time read_timeout)
{
vf->read_timeout = read_timeout ;
+
return vf->read_on ? uty_vf_set_read(vf, on)
: off ;
} ;
@@ -1004,34 +1086,34 @@ uty_vf_set_read_timeout(vio_vf vf, vty_timer_time read_timeout)
/*------------------------------------------------------------------------------
* Set required write ready state. Applies the current write timeout.
*
- * Forces off if: vf->closing
+ * Forces off if: vf->vout_state != vf_open
*
- * Does nothing if: !vf->write_open
+ * Does nothing if: vf->vout_state == vf_closed
* or: vf->vfd == NULL
+ *
+ * Returns new state of vf->write_on
*/
-extern on_off_t
-uty_vf_set_write(vio_vf vf, on_off_t how)
+extern on_off_b
+uty_vf_set_write(vio_vf vf, on_off_b how)
{
- if (vf->closing)
+ if (vf->vout_state != vf_open)
how = off ;
- return vf->write_on = vf->write_open
- ? vio_fd_set_write(vf->vfd, how, vf->write_timeout)
+ return vf->write_on = (vf->vout_state != vf_closed)
+ ? vio_vfd_set_write(vf->vfd, how, vf->write_timeout)
: off ;
} ;
/*------------------------------------------------------------------------------
* Set required write ready timeout -- if already write_on, restart it.
*
- * Forces write ready off if: vf->closing
- *
- * Does nothing if: !vf->write_open
- * or: vf->vfd == NULL
+ * Returns new state of vf->write_on
*/
-extern on_off_t
+extern on_off_b
uty_vf_set_write_timeout(vio_vf vf, vty_timer_time write_timeout)
{
vf->write_timeout = write_timeout ;
+
return vf->write_on ? uty_vf_set_write(vf, on)
: off ;
} ;
@@ -1061,9 +1143,11 @@ uty_open_listeners(const char *addr, unsigned short port, const char *path)
if (port)
uty_term_open_listeners(addr, port) ;
+#if 0
/* If want to listen for vtysh, set up listener now */
if (VTYSH_ENABLED && (path != NULL))
uty_serv_vtysh(path) ;
+#endif
} ;
/*------------------------------------------------------------------------------
@@ -1072,7 +1156,7 @@ uty_open_listeners(const char *addr, unsigned short port, const char *path)
* Adds to list of listeners for close down.
*/
extern void
-uty_add_listener(int fd, vio_fd_accept* accept_action)
+uty_add_listener(int fd, vio_vfd_accept* accept_action)
{
vio_listener vl ;
diff --git a/lib/vty_io.h b/lib/vty_io.h
index ecd7a451..5db12ca2 100644
--- a/lib/vty_io.h
+++ b/lib/vty_io.h
@@ -25,145 +25,251 @@
#ifndef _ZEBRA_VTY_IO_H
#define _ZEBRA_VTY_IO_H
-#include "zebra.h"
+//#include "zebra.h"
#include "misc.h"
+//#include <errno.h>
-#include <errno.h>
-
+#include "vty_local.h"
+#include "command_local.h"
#include "vty_io_basic.h"
-#include "uty.h"
-#include "vty.h"
#include "vio_fifo.h"
-#include "vio_lines.h"
-#include "keystroke.h"
#include "thread.h"
-#include "command.h"
+#include "command_execute.h"
#include "qstring.h"
/*==============================================================================
- * Here are structures and other definitions which are shared by:
- *
- * vty.c -- the main vty handler
- * vty_cli.c -- which handles the command line stuff
- * vty_io.c -- ....
- *
- * The "struct vty" is used extensively across the Quagga daemons, where it
- * has two functions relating to command handling as:
- *
- * 1) a "file handle" for output produced by commands
- *
- * 2) the holder of some context -- notably the current command "node" -- for
- * command execution to use
+ * Structures and other definitions for the top level VTY I/O.
*
- * The bulk of "struct vty" is, therefore, private to vty.c and is factored
- * out into the "struct vty_io".
- *
- * To reduce the size of vty.c, some groups of functions are separated into:
- *
- * vty_cli.c -- which looks after the keystroke by keystroke handling
- * of the command line.
+ * There is one struct vty_io per VTY, which contains, inter alia, the vin
+ * and vout stacks.
*
+ * The vin and vout stacks contain one or more struct vty_vf -- one per
+ * input and/or output associated with the VTY.
*/
-/*==============================================================================
- * VTY CLI and OUT types
+enum
+{
+ VTY_WATCH_DOG_INTERVAL = 5, /* interval between barks */
+
+ VTY_HALF_CLOSE_TIMEOUT = 120, /* timeout after half_close */
+
+ VTY_TIMEOUT_DEFAULT = 600, /* terminal timeout value */
+} ;
+
+/*------------------------------------------------------------------------------
+ * VTY VIN and OUT types
*/
enum vio_in_type /* Command input */
{
- VIN_NONE = 0, /* no input at all */
+ VIN_NONE = 0, /* not a valid input type */
VIN_TERM, /* telnet terminal */
- VIN_SHELL, /* vty_shell input */
+ VIN_VTYSH, /* vty_shell input */
VIN_FILE, /* ordinary file input */
VIN_PIPE, /* pipe (from child process) */
- VIN_CONFIG, /* config file ?? */
+ VIN_CONFIG, /* config file */
+
+ VIN_PIPE_RETURN, /* */
+
+ /* The VIN types >= VIN_SPECIALS do not have an associated fd.
+ *
+ * These can coexist with a VOUT which does have an associated fd.
+ */
+ VIN_SPECIALS, /* all special from now on */
+
+ VIN_DEV_NULL = VIN_SPECIALS,
+ /* black hole input */
} ;
typedef enum vio_in_type vio_in_type_t ;
enum vio_out_type /* Command output */
{
- VOUT_NONE = 0, /* no output at all */
+ VOUT_NONE = 0, /* not a valid output type */
VOUT_TERM, /* a telnet terminal */
- VOUT_SHELL, /* a vty_shell output pipe */
+ VOUT_VTYSH, /* a vty_shell output pipe */
VOUT_FILE, /* ordinary file */
VOUT_PIPE, /* pipe (to child process) */
+ VOUT_CONFIG, /* config file ?? */
+
+ /* The VOUT types >= VOUT_SPECIALS do not have an associated fd.
+ *
+ * These can coexist with a VIN which does have an associated fd.
+ */
+ VOUT_SPECIALS, /* all special from now on */
+
+ VOUT_DEV_NULL = VOUT_SPECIALS,
+ /* black hole output */
+
VOUT_STDOUT, /* stdout */
VOUT_STDERR, /* stderr */
};
typedef enum vio_out_type vio_out_type_t ;
/*------------------------------------------------------------------------------
- * VIO file structure
+ * State of a vf or of the entire vio.
+ *
+ * For a vf: I/O is possible iff vf_open -- has separate state for vin/vout.
+ *
+ * For a vio: used to manage the closing process.
+ */
+enum vf_state
+{
+ vf_closed = 0, /* not active -- may be discarded */
+
+ vf_open = 1, /* open for business */
+
+ vf_eof = 2, /* open, but at eof */
+ vf_error = 3, /* open, but error reported */
+
+ vf_closing = 4, /* open, but in process of closing */
+} ;
+typedef enum vf_state vf_state_t ;
+
+/*------------------------------------------------------------------------------
+ * vty_vf -- "vty file" structure
*
- * All I/O is non-blocking for all sources and sinks of VIO stuff.
+ * A vio_vf may be a read, write or read/write object.
+ *
+ * All I/O is via vio_vfd objects, except for VOUT_STDOUT and VOUT_STDERR.
+ * The vio_vfd layer hides the differences between the qpthreads an legacy
+ * thread environments.
+ *
+ * The VOUT_STDOUT and VOUT_STDERR are handled as direct output to the standard
+ * i/o file handles. In the case of a VTY_CONFIG_READ, the vin is VIN_CONFIG
+ * and the vout is VOUT_STDOUT, and these can share a single vty_vf.
*
* Also used for the associated listeners.
*/
+struct vty_io ; /* Forward reference */
+typedef struct vty_io* vty_io ;
+
typedef struct vio_vf* vio_vf ;
struct vio_vf
{
- vty_io vio ; /* parent */
+ vty_io vio ; /* parent */
+
+ char* name ; /* MTYPE_VTY_NAME (if any) */
+
+ /* Input side. */
+
+ vio_in_type_t vin_type ;
+ vf_state_t vin_state ;
+ vio_vf vin_next ; /* list of inputs */
+
+
+ struct vty_cli* cli ; /* NULL if not a VTY_TERMINAL ! */
+
+ vio_fifo ibuf ; /* input fifo (if required) */
- vio_in_type_t vin_type ;
- vio_vf vin_next ; /* list of inputs */
+ qstring_t cl ; /* command line buffer */
+ bool line_complete ; /* false => line in construction */
+ uint line_number ; /* number of first line in cl */
+ uint line_step ; /* number of real lines in cl */
- vio_out_type_t vout_type ;
- vio_vf vout_next ; /* list of outputs */
+ cmd_parse_type_t parse_type ; /* iff vin_type != VIN_NONE */
+ bool reflect_enabled ; /* false if vin_type == VIN_NONE */
- vio_fifo obuf ; /* pointer to fifo */
- vio_line_control olc ; /* pointer to lc */
+ /* Output side. */
- vio_fd vfd ;
+ vio_out_type_t vout_type ;
+ vf_state_t vout_state ;
+ vio_vf vout_next ; /* list of outputs */
- bool blocking ; /* using blocking reads */
- bool closing ; /* suppress read/write ready */
+ vio_fifo obuf ; /* output fifo (if required) */
- bool read_open ; /* reads returns 0 if not */
- bool write_open ; /* writes complete instantly if not */
- int error_seen ; /* non-zero => failed */
+ bool out_enabled ; /* false if vout_type == VOUT_NONE */
- on_off_b read_on ;
- on_off_b write_on ;
+ /* I/O */
+
+ vio_vfd vfd ; /* vty_io_basic "file descriptor" */
+
+ bool blocking ; /* using blocking I/O (config read) */
+
+ int error_seen ; /* non-zero => failed */
+
+ on_off_b read_on ;
+ on_off_b write_on ;
vty_timer_time read_timeout ;
vty_timer_time write_timeout ;
+
} ;
-enum vty_readiness /* bit significant */
+enum vty_readiness /* bit significant */
{
not_ready = 0,
- read_ready = 1,
- write_ready = 2, /* takes precedence */
- now_ready = 4
+ read_ready = BIT(0),
+ write_ready = BIT(1), /* may take precedence */
+ now_ready = BIT(2)
} ;
+typedef enum vty_readiness vty_readiness_t ;
/*------------------------------------------------------------------------------
* The vty_io structure
*
+ * The main elements of the vty_io object are the vin and vout stacks.
+ *
+ * One of the challenges is managing the closing of VTY objects. First,
+ * cannot close and free a VTY object which is in the hands of a command
+ * function, or which is queued to or from a command function. Second,
+ * do not wish to completely close an output until have given it a chance
+ * to clear any buffered output.
+ *
+ *
*
+ * "cmd_running" means that the VTY is in hands of (or has been passed to)
+ * a command loop -- the VTY cannot be fully closed until that is no
+ * longer the case.
*
+ * "blocking" is set for configuration reading VTY, so that everything is
+ * done with blocking I/O.
*
+ * "closing" means that the vty has been closed (!), but a command
+ * and or output may still be active:
*
+ * - if is a socket, will have shut_down the read side (half-closed)
+ *
+ * - any further attempts to read will give "eof"
+ *
+ * - there may be a command in execution -- see "cmd_running".
+ *
+ * - further writing will be honoured.
+ *
+ * - the write side may still be active, attempting to empty out any
+ * pending output.
+ *
+ * "closed" means the vty has been fully and finally closed.
+ *
+ * - any further attempts to write will be ignored, but return instant
+ * success.
+ *
+ * - the file/socket has been fully closed.
+ *
+ * - the VTY and all attached structures can be reaped by the death_watch.
*/
+struct vty_cli ; /* forward reference -- vty_cli.h is
+ *not* included, because that refers
+ back to the vty_io ! */
-struct vty_io
+struct vty_io /* typedef appears above */
{
- struct vty* vty ; /* the related vty */
- char *name ; /* for VTY_TERM is IP address) */
+ vty vty ; /* the related vty */
- /* vin stack */
- vio_vf vin ;
+ vio_vf vin ; /* vin stack */
vio_vf vin_base ;
+ uint vin_depth ;
- /* vout stack */
- vio_vf vout ;
+ vio_vf vout ; /* vout stack */
vio_vf vout_base ;
+ uint vout_depth ;
+
+ vio_vf vout_closing ; /* vout closing list */
/* List of all vty_io objects */
struct dl_list_pair(vty_io) vio_list ;
@@ -171,193 +277,102 @@ struct vty_io
/* List of all vty_io that are in monitor state */
struct dl_list_pair(vty_io) mon_list ;
- /* VTY state */
-
- bool half_closed ; /* => on death watch list until closed */
- bool closed ; /* => all I/O terminated
- will also be half_closed */
-
- char* close_reason ; /* message to be sent, once all other
- output has completed, giving reason
- for closing the VTY. */
-
-
-
-
-
- /* When writing configuration file */
- enum vty_type real_type ;
-
- int file_fd ;
- int file_error ;
-
- /* Failure count for login attempts */
- int fail;
-
- /* History of commands */
- vector_t hist ;
- int hp ; /* History lookup current point */
- int hindex; /* History insert end point */
-
- /* Window width/height as reported by Telnet. 0 => unknown */
- int width;
- int height;
-
- /* Configure lines. */
- int lines;
- bool lines_set ; /* true <=> explicitly set */
-
- /* Terminal monitor. */
- bool monitor ;
- bool monitor_busy ;
-
- /* Terminal timeout in seconds -- 0 => none */
- vty_timer_time v_timeout ;
-
- /*-------------------------------------------------------------------------
- * CLI_TERM stuff.
- */
-
- keystroke_stream key_stream ;
-
- /* cli_drawn <=> the current prompt and user input occupy the current
- * line on the screen.
+ /* VTY state
*
- * cli_dirty <=> the last command output did not end with a newline.
+ * "blocking" is set for configuration reading VTY, so that everything is
+ * done with blocking I/O.
*
- * If cli_drawn is true, the following are valid:
+ * "half_closed" means that the vty has been closed (!), but a command
+ * and or output may still be active:
*
- * cli_prompt_len -- the length of the prompt part.
- * (will be the "--more--" prompt in cli_more_wait)
+ * - if is a socket, will have shut_down the read side (half-closed)
*
- * cli_extra_len -- the length of any ^X at the cursor position
- * (for when blocked waiting for queued command)
+ * - any further attempts to read will give "eof"
*
- * cli_echo_suppress -- the user part of the command line is suppressed
+ * - there may be a command in execution -- see "cmd_running".
*
- * NB: cli_echo_suppress is only used for password entry.
- */
- bool cli_drawn ;
- bool cli_dirty ;
-
- int cli_prompt_len ;
- int cli_extra_len ;
-
- bool cli_echo_suppress ;
-
- /* "cache" for prompt -- when node or host name changes, prompt does */
- enum node_type cli_prompt_node ;
- bool cli_prompt_set ;
- qstring_t cli_prompt_for_node ;
-
- /* State of the CLI
+ * - further writing will be honoured.
*
- * cli_blocked -- blocked from processing keystrokes
- * cmd_in_progress -- command dispatched (may be queued)
- * cmd_out_enabled -- contents of the command FIFO may be written away
- * cli_more_wait -- is in "--more--" wait state
- */
- bool cli_blocked ;
- bool cmd_in_progress ;
- bool cmd_out_enabled ;
- bool cli_more_wait ;
-
- /* This is used to control command output, so that each write_ready event
- * generates at most one tranche of output.
- */
- bool cmd_out_done ;
-
- /* This is set only if the "--more--" handling is enabled */
- bool cli_more_enabled ;
-
- /* Command Line(s)
+ * - the write side may still be active, attempting to empty out any
+ * pending output.
+ *
+ * "cmd_running" means that the VTY is in hands of (or has been passed to)
+ * a command loop -- the VTY cannot be fully closed until that is no
+ * longer the case.
*
- * cli_do -- when current command being prepared is completed (by
- * CR/LF or otherwise) this says what there now is to be done.
+ * "closed" means the vty has been fully and finally closed.
*
- * cl -- current command line being prepared.
+ * - any further attempts to write will be ignored, but return instant
+ * success.
*
- * clx -- current command line being executed.
+ * - the file/socket has been fully closed.
*
- * NB: during command execution vty->buf is set to point at the '\0'
- * terminated current command line being executed.
+ * - the VTY and all attached structures can be reaped by the death_watch.
*/
- enum cli_do cli_do ;
+ bool blocking ; /* => all I/O is blocking. */
- qstring_t cl ;
- qstring_t clx ;
+ bool cmd_running ; /* => cannot be fully closed */
- /* CLI output buffering */
- vio_fifo_t cli_obuf ;
+ vf_state_t state ;
-} ;
+ qstring close_reason ; /* message to be sent, once all other
+ output has completed, giving reason
+ for closing the VTY. */
-/*==============================================================================
- * If possible, will use getaddrinfo() to find all the things to listen on.
- */
-enum {
-#if defined(HAVE_IPV6) && !defined(NRL)
- VTY_USE_ADDRINFO = 1,
-#else
- VTY_USE_ADDRINFO = 0,
-#endif
+ /* For ease of output, pointer to current vout->obuf */
+ vio_fifo obuf ; /* NULL => no vout ! */
} ;
/*==============================================================================
* Functions
*/
-extern vty uty_new (vty_type_t type, int sock_fd) ;
-extern void uty_close (vty_io vio, const char* reason) ;
-extern void uty_close_final(vty_io vio, const char* reason) ;
+extern int uty_out (vty_io vio, const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
+Inline int uty_vprintf(vty_io vio, const char *format, va_list args) ;
+
+Inline void uty_out_clear(vty_io vio) ;
+Inline void uty_out_accept(vty_io vio) ;
+Inline void uty_out_reject(vty_io vio) ;
+extern vty uty_new (vty_type_t type) ;
+extern bool uty_close(vty_io vio, bool final, qstring reason) ;
-extern void uty_vin_add(vty_io vio, vio_vf vf, vio_in_type_t type,
- vio_fd_action* read_action, vio_timer_action* read_timer_action) ;
-extern void uty_vout_add(vty_io vio, vio_vf vf, vio_out_type_t type,
- vio_fd_action* write_action, vio_timer_action* write_timer_action) ;
+extern void uty_vin_close(vty_io vio) ;
+extern void uty_vout_close(vty_io vio, bool final) ;
+extern void uty_vin_close_stack(vty_io vio) ;
+extern void uty_vout_close_stack(vty_io vio, bool final) ;
+extern void uty_set_timeout(vty_io vio, vty_timer_time timeout) ;
+extern void uty_vin_open(vty_io vio, vio_vf vf, vio_in_type_t type,
+ vio_vfd_action* read_action,
+ vio_timer_action* read_timer_action,
+ usize ibuf_size) ;
+extern void uty_vout_open(vty_io vio, vio_vf vf, vio_out_type_t type,
+ vio_vfd_action* write_action,
+ vio_timer_action* write_timer_action,
+ usize obuf_size) ;
-extern vio_vf uty_vf_new(vty_io vio, int fd, vfd_type_t type,
+extern vio_vf uty_vf_new(vty_io vio, const char* name, int fd, vfd_type_t type,
vfd_io_type_t io_type) ;
-Inline int uty_vf_fd(vio_vf vf) ;
-extern on_off_t uty_vf_set_read(vio_vf vf, on_off_t on) ;
-extern on_off_t uty_vf_set_read_timeout(vio_vf vf,
+extern on_off_b uty_vf_set_read(vio_vf vf, on_off_b on) ;
+extern on_off_b uty_vf_set_read_timeout(vio_vf vf,
vty_timer_time read_timeout) ;
-extern on_off_t uty_vf_set_write(vio_vf vf, on_off_t on) ;
-extern on_off_t uty_vf_set_write_timeout(vio_vf vf,
+extern on_off_b uty_vf_set_write(vio_vf vf, on_off_b on) ;
+extern on_off_b uty_vf_set_write_timeout(vio_vf vf,
vty_timer_time write_timeout) ;
-
+extern int uty_vf_error(vio_vf vf, const char* what, int err) ;
extern void uty_open_listeners(const char *addr, unsigned short port,
const char *path) ;
-extern void uty_add_listener(int fd, vio_fd_accept* accept) ;
+extern void uty_add_listener(int fd, vio_vfd_accept* accept) ;
extern void uty_close_listeners(void) ;
extern void uty_watch_dog_init(void) ;
extern void uty_watch_dog_start(void) ;
extern void uty_watch_dog_stop(void) ;
-
-
-extern int uty_output (struct vty *vty, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3) ;
-extern int uty_vprintf(struct vty *vty, const char *format, va_list args) ;
-extern int uty_reflect(struct vty *vty) ;
-extern void uty_out_clear(vty_io vio) ;
-extern void uty_out_fflush(vty_io vio, FILE* file) ;
-
-extern void uty_set_height(vty_io vio) ;
-extern void uty_cmd_output_start(vty_io vio) ;
-
-extern void uty_file_set_readiness(vio_vf vf, enum vty_readiness ready) ;
-extern void uty_file_set_timer(vio_vf vf, unsigned long timeout) ;
-
-extern int uty_read (vty_io vio, keystroke steal) ;
-extern int utysh_read (vty_io vio, qstring cl, qstring buf) ;
-
extern const char* uty_get_name(vty_io vio) ;
extern void uty_set_monitor(vty_io vio, bool on) ;
@@ -366,19 +381,6 @@ extern void uty_set_monitor(vty_io vio, bool on) ;
* Inline Functions
*/
-/*------------------------------------------------------------------------------
- * Return the fd from a vio_fd structure
- */
-Inline int
-uty_vf_fd(vio_vf vf)
-{
- return vio_fd_fd(vf->vfd) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Return the fd from a vio_fd structure
- */
-
Inline bool
uty_is_terminal(struct vty *vty)
{
@@ -397,4 +399,41 @@ uty_is_shell_client(struct vty *vty)
return vty->type == VTY_SHELL_CLIENT ;
}
+/*------------------------------------------------------------------------------
+ * Command output -- append to output buffer.
+ */
+Inline int
+uty_vprintf(vty_io vio, const char *format, va_list args)
+{
+ return vio_fifo_vprintf(vio->obuf, format, args) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear command output -- discard anything in the buffer, but keep markers.
+ */
+Inline void
+uty_out_clear(vty_io vio)
+{
+ vio_fifo_clear(vio->obuf, false) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept command output -- advance any end_mark to current put position.
+ */
+Inline void
+uty_out_accept(vty_io vio)
+{
+ vio_fifo_step_end_mark(vio->obuf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reject command output -- discard anything after the end_mark in the buffer,
+ * but keep markers.
+ */
+Inline void
+uty_out_reject(vty_io vio)
+{
+ vio_fifo_back_to_end_mark(vio->obuf, true) ;
+} ;
+
#endif /* _ZEBRA_VTY_IO_H */
diff --git a/lib/vty_io_basic.c b/lib/vty_io_basic.c
index 3bead51f..85ea2587 100644
--- a/lib/vty_io_basic.c
+++ b/lib/vty_io_basic.c
@@ -21,9 +21,67 @@
* 02111-1307, USA.
*/
-#include "zebra.h"
-
#include "vty_io_basic.h"
+#include "vty_local.h"
+
+#include "memory.h"
+
+#include <sys/socket.h>
+#include <fcntl.h>
+
+/*==============================================================================
+ * Base level open operations -- files and pipes
+ *
+ */
+
+/*==============================================================================
+ * Try to open the given file for the given type of I/O.
+ *
+ * vfd_io_write => create if does not exist (mode 0600)
+ *
+ * vfd_io_append => set O_APPEND
+ * otherwise => truncate file if it does exist
+ *
+ * vfd_io_read => fail if does not exist
+ *
+ * vfd_io_read_write => create if does not exist (mode 0600)
+ *
+ * vfd_io_append => set O_APPEND
+ * otherwise => leave file as is
+ *
+ * vfd_io_blocking => do not open O_NONBLOCK
+ *
+ * (if none of the above, treat as vfd_io_read)
+ *
+ * Returns: if >= 0 -- the fd of the open file
+ * < 0 -- failed to open file -- see errno
+ */
+extern int
+uty_vfd_file_open(const char* name, vfd_io_type_t io_type)
+{
+ int oflag ;
+
+ oflag = 0 ;
+ if ((io_type & vfd_io_read_write) == vfd_io_read_write)
+ oflag = O_RDWR | O_CREAT ;
+ else if ((io_type & vfd_io_write) != 0)
+ oflag = O_WRONLY | O_CREAT ;
+ else
+ oflag = O_RDONLY ;
+
+ if ((io_type & vfd_io_write) != 0)
+ {
+ if ((io_type & vfd_io_append) != 0)
+ oflag |= O_APPEND ;
+ else if ((io_type & vfd_io_read) == 0)
+ oflag |= O_TRUNC ;
+ } ;
+
+ if ((io_type & vfd_io_blocking) == 0)
+ oflag |= O_NONBLOCK ;
+
+ return open(name, oflag, S_IRUSR | S_IWUSR) ;
+} ;
/*==============================================================================
* Base level I/O and Timer handling....
@@ -34,25 +92,23 @@
struct vio_io_set_args /* to CLI thread */
{
- bool active ; /* set when queued, cleared when dequeued */
- bool die ; /* set when is queued and vio_fd is closed */
- bool close ; /* close and free the vio_fd and mqb */
+ bool active ; /* set when queued, cleared when dequeued */
+ bool close ; /* close and free the vio_vfd and mqb */
bool readable ; /* set when read state to be changed */
- on_off_t read_on ; /* what to change read to */
+ on_off_b read_on ; /* what to change read to */
vty_timer_time read_timeout ;
/* what to set the timeout to, if any */
bool writable ; /* set when write state to be changed */
- on_off_t write_on ; /* what to change write to */
+ on_off_b write_on ; /* what to change write to */
vty_timer_time write_timeout ;
/* what to set the timeout to, if any */
} ;
MQB_ARGS_SIZE_OK(vio_io_set_args) ;
-static void vio_fd_mqb_dispatch(vio_fd vfd) ;
-static void vio_fd_mqb_free(vio_fd vfd) ;
-static struct vio_io_set_args* vio_fd_mqb_args(vio_fd vfd) ;
+static void vio_vfd_mqb_dispatch(vio_vfd vfd) ;
+static struct vio_io_set_args* vio_vfd_mqb_args(vio_vfd vfd) ;
/*==============================================================================
* File Descriptor handling
@@ -69,22 +125,22 @@ static struct vio_io_set_args* vio_fd_mqb_args(vio_fd vfd) ;
* see above.
*/
-static void vio_fd_qps_read_action(qps_file qf, void* file_info) ;
-static void vio_fd_qps_write_action(qps_file qf, void* file_info) ;
-static int vio_fd_thread_read_action(struct thread *thread) ;
-static int vio_fd_thread_write_action(struct thread *thread) ;
+static void vio_vfd_qps_read_action(qps_file qf, void* file_info) ;
+static void vio_vfd_qps_write_action(qps_file qf, void* file_info) ;
+static int vio_vfd_thread_read_action(struct thread *thread) ;
+static int vio_vfd_thread_write_action(struct thread *thread) ;
static void vio_timer_squelch(vio_timer_t* timer) ;
Inline void
-vio_fd_do_read_action(vio_fd vfd)
+vio_vfd_do_read_action(vio_vfd vfd)
{
if (vfd->active)
vfd->read_action(vfd, vfd->action_info) ;
}
Inline void
-vio_fd_do_write_action(vio_fd vfd)
+vio_vfd_do_write_action(vio_vfd vfd)
{
if (vfd->active)
vfd->write_action(vfd, vfd->action_info) ;
@@ -93,12 +149,12 @@ vio_fd_do_write_action(vio_fd vfd)
/*------------------------------------------------------------------------------
* Create a new vfd structure.
*/
-extern vio_fd
-vio_fd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
+extern vio_vfd
+vio_vfd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
{
- vio_fd vfd ;
+ vio_vfd vfd ;
- vfd = XCALLOC(MTYPE_VTY, sizeof(struct vio_fd)) ;
+ vfd = XCALLOC(MTYPE_VTY, sizeof(struct vio_vfd)) ;
/* Has set:
*
@@ -114,12 +170,12 @@ vio_fd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
* mqb -- NULL
*/
- vio_fd_set_fd(vfd, fd, type, io_type) ;
+ vio_vfd_set_fd(vfd, fd, type, io_type) ;
vio_timer_init(&vfd->read_timer, NULL, NULL) ;
vio_timer_init(&vfd->write_timer, NULL, NULL) ;
- vio_fd_set_action_info(vfd, action_info) ;
+ vio_vfd_set_action_info(vfd, action_info) ;
return vfd ;
} ;
@@ -127,13 +183,13 @@ vio_fd_new(int fd, vfd_type_t type, vfd_io_type_t io_type, void* action_info)
/*------------------------------------------------------------------------------
* If vfd was not fully set up when created, set it up now.
*
- * To close an active vfd, use vio_fd_close() !
+ * To close an active vfd, use vio_vfd_close() !
*
* NB: for use when vfd has been created, but the fd was not known at that
* time -- ie the vfd is NOT active.
*/
extern void
-vio_fd_set_fd(vio_fd vfd, int fd, vfd_type_t type, vfd_io_type_t io_type)
+vio_vfd_set_fd(vio_vfd vfd, int fd, vfd_type_t type, vfd_io_type_t io_type)
{
assert(!vfd->active) ;
@@ -144,74 +200,76 @@ vio_fd_set_fd(vio_fd vfd, int fd, vfd_type_t type, vfd_io_type_t io_type)
} ;
/*------------------------------------------------------------------------------
- * Set the read action field for the given vio_fd.
+ * Set the read action field for the given vio_vfd.
*/
extern void
-vio_fd_set_read_action(vio_fd vfd, vio_fd_action* action)
+vio_vfd_set_read_action(vio_vfd vfd, vio_vfd_action* action)
{
vfd->read_action = action ;
} ;
/*------------------------------------------------------------------------------
- * Set the write action field for the given vio_fd.
+ * Set the write action field for the given vio_vfd.
*/
extern void
-vio_fd_set_write_action(vio_fd vfd, vio_fd_action* action)
+vio_vfd_set_write_action(vio_vfd vfd, vio_vfd_action* action)
{
vfd->write_action = action ;
} ;
/*------------------------------------------------------------------------------
- * Set the read action field for the given vio_fd.
+ * Set the read action field for the given vio_vfd.
*/
extern void
-vio_fd_set_read_timeout_action(vio_fd vfd, vio_timer_action* action)
+vio_vfd_set_read_timeout_action(vio_vfd vfd, vio_timer_action* action)
{
vio_timer_set_action(&vfd->read_timer, action) ;
} ;
/*------------------------------------------------------------------------------
- * Set the write action field for the given vio_fd.
+ * Set the write action field for the given vio_vfd.
*/
extern void
-vio_fd_set_write_timeout_action(vio_fd vfd, vio_timer_action* action)
+vio_vfd_set_write_timeout_action(vio_vfd vfd, vio_timer_action* action)
{
vio_timer_set_action(&vfd->write_timer, action) ;
} ;
/*------------------------------------------------------------------------------
- * Set the action_info field for the given vio_fd read/write action.
+ * Set the action_info field for the given vio_vfd read/write action.
*/
extern void
-vio_fd_set_action_info(vio_fd vfd, void* action_info)
+vio_vfd_set_action_info(vio_vfd vfd, void* action_info)
{
vfd->action_info = action_info ;
vio_timer_set_info(&vfd->read_timer, action_info) ;
vio_timer_set_info(&vfd->write_timer, action_info) ;
} ;
+#if 0
/*------------------------------------------------------------------------------
- * If there is a read action set for the give vio_fd (if any), then kick it.
+ * If there is a read action set for the give vio_vfd (if any), then kick it.
*/
extern void
-vio_fd_do_read_action(vio_fd vfd)
+vio_vfd_kick_read_action(vio_vfd vfd)
{
if ((vfd != NULL) && (vfd->read_action != NULL))
- vio_fd_do_read_action(vfd) ;
+ vio_vfd_do_read_action(vfd) ;
} ;
/*------------------------------------------------------------------------------
- * If there is a write action set for the give vio_fd (if any), then kick it.
+ * If there is a write action set for the give vio_vfd (if any), then kick it.
*/
extern void
-vio_fd_do_write_action(vio_fd vfd)
+vio_vfd_kick_write_action(vio_vfd vfd)
{
if ((vfd != NULL) && (vfd->write_action != NULL))
- vio_fd_do_read_action(vfd) ;
+ vio_vfd_do_read_action(vfd) ;
} ;
+#endif
/*------------------------------------------------------------------------------
- * Half close the given vfd (if any).
+ * Close the read side of the given vfd (if any).
*
* If the vfd is a socket, then does a shutdown of the read side.
*
@@ -221,8 +279,8 @@ vio_fd_do_write_action(vio_fd vfd)
*
* Returns original vfd, or NULL if it has been closed.
*/
-extern vio_fd
-vio_fd_half_close(vio_fd vfd)
+extern vio_vfd
+vio_vfd_read_close(vio_vfd vfd)
{
VTY_ASSERT_LOCKED() ;
@@ -239,14 +297,14 @@ vio_fd_half_close(vio_fd vfd)
{
/* read & write, so really half-close if can */
if (vfd->type == vfd_socket)
- shutdown(vfd->fd, SHUT_RD) ;
- vio_fd_set_read(vfd, off, 0) ;
+ shutdown(vfd->fd, SHUT_RD) ; /* ignore errors TODO */
+ vio_vfd_set_read(vfd, off, 0) ;
vfd->io_type ^= vfd_io_read ; /* now write only ! */
}
else
{
/* read only, so fully close */
- vfd = vio_fd_close(vfd) ;
+ vfd = vio_vfd_close(vfd) ;
} ;
} ;
}
@@ -259,32 +317,31 @@ vio_fd_half_close(vio_fd vfd)
/*------------------------------------------------------------------------------
* If there is an fd, close it.
*
- * Stops any read/write waiting and releases all memory.
- *
- * NB: this can done from any thread, but if not done from the CLI thread,
- * and there is a qf, a message must be sent to the CLI thread to actually
- * implement: which passes the vio_fd to the CLI thread for later
- * close and destruction.
- *
- * The actual close has to be delayed, so that cannot open another fd
- * and bang into a still active qps_file !
- *
- * The message looks after freeing the vio_fd, the qps_file and the mqb.
+ * Stops any read/write waiting and releases all memory, including the vfd
+ * itself if required..
*/
static void
-vio_fd_do_close(vio_fd vfd)
+vio_vfd_do_close(vio_vfd vfd, free_keep_b free)
{
+ if (vfd == NULL)
+ return ;
+
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf != NULL)
{
assert(vfd->fd == qps_file_fd(vfd->f.qf)) ;
vfd->f.qf = qps_file_free(vfd->f.qf) ;
} ;
- vio_fd_mqb_free(vfd) ;
+
+ if (vfd->mqb != NULL)
+ {
+ mqb_free(vfd->mqb) ;
+ vfd->mqb = NULL ;
+ } ;
}
else
{
@@ -306,12 +363,13 @@ vio_fd_do_close(vio_fd vfd)
} ;
if (vfd->fd >= 0)
- close(vfd->fd) ;
+ close(vfd->fd) ; /* ignores errors TODO */
vio_timer_reset(&vfd->read_timer) ;
vio_timer_reset(&vfd->write_timer) ;
- XFREE(MTYPE_VTY, vfd) ;
+ if (free == free_it)
+ XFREE(MTYPE_VTY, vfd) ;
} ;
/*------------------------------------------------------------------------------
@@ -322,16 +380,16 @@ vio_fd_do_close(vio_fd vfd)
*
* NB: this can done from any thread, but if not done from the CLI thread,
* and there is a qf, a message must be sent to the CLI thread to actually
- * implement: which passes the vio_fd to the CLI thread for later
+ * implement: which passes the vio_vfd to the CLI thread for later
* close and destruction.
*
* The actual close has to be delayed, so that cannot open another fd
* and bang into a still active qps_file !
*
- * The message looks after freeing the vio_fd, the qps_file and the mqb.
+ * The message looks after freeing the vio_vfd, the qps_file and the mqb.
*/
-extern vio_fd
-vio_fd_close(vio_fd vfd)
+extern vio_vfd
+vio_vfd_close(vio_vfd vfd)
{
VTY_ASSERT_LOCKED() ;
@@ -340,9 +398,9 @@ vio_fd_close(vio_fd vfd)
if (vfd->fd < 0)
{
- /* closing an inactive vio_fd -- make sure all is quiet */
+ /* closing an inactive vio_vfd -- make sure all is quiet */
assert(!vfd->active) ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
assert(vfd->f.qf == NULL) ;
}
@@ -355,19 +413,18 @@ vio_fd_close(vio_fd vfd)
}
else
{
- /* closing an active vio_fd */
+ /* closing an active vio_vfd */
if (vty_is_cli_thread())
{
/* In cli thread, so close directly */
- vio_fd_do_close(vfd) ;
+ vio_vfd_do_close(vfd, free_it) ;
}
else
{
/* Rats... have to send message to cli thread to close */
- struct vio_io_set_args* args = vio_fd_mqb_args(vfd) ;
+ struct vio_io_set_args* args = vio_vfd_mqb_args(vfd) ;
args->close = true ;
- args->die = true ;
/* in case something goes ready before the close message
* is processed, squelch.
@@ -381,7 +438,7 @@ vio_fd_close(vio_fd vfd)
vio_timer_squelch(&vfd->write_timer) ;
assert(vfd == mqb_get_arg0(vfd->mqb)) ;
- vio_fd_mqb_dispatch(vfd) ;
+ vio_vfd_mqb_dispatch(vfd) ;
} ;
} ;
@@ -389,16 +446,16 @@ vio_fd_close(vio_fd vfd)
} ;
/*------------------------------------------------------------------------------
- * Set or unset read ready state on given vio_fd (if any) if it is active.
+ * Set or unset read ready state on given vio_vfd (if any) if it is active.
*
- * If setting read_on, starts any read timeout timer.
+ * If setting read_on, starts any read timeout timer (or stops it if 0).
* If setting read off, stops any read timeout timer.
*
* NB: this can done from any thread, but if not done from the CLI thread,
* a message must be sent to the CLI thread to actually implement.
*/
-extern on_off_t
-vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
+extern on_off_b
+vio_vfd_set_read(vio_vfd vfd, on_off_b on, vty_timer_time timeout)
{
struct vio_io_set_args* args ;
@@ -422,7 +479,7 @@ vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
{
assert(vfd->read_action != NULL) ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf == NULL)
{
@@ -431,20 +488,20 @@ vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
vfd->fd, vfd) ;
} ;
qps_enable_mode(vfd->f.qf, qps_read_mnum,
- vio_fd_qps_read_action) ;
+ vio_vfd_qps_read_action) ;
}
else
{
if (vfd->f.thread.read == NULL)
vfd->f.thread.read = thread_add_read(vty_master,
- vio_fd_thread_read_action, vfd, vfd->fd) ;
+ vio_vfd_thread_read_action, vfd, vfd->fd) ;
} ;
vio_timer_set(&vfd->read_timer, timeout) ;
}
else
{
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf != NULL)
qps_disable_modes(vfd->f.qf, qps_read_mbit) ;
@@ -462,19 +519,19 @@ vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
{
/* In other threads, must send message to cli thread */
- args = vio_fd_mqb_args(vfd) ;
+ args = vio_vfd_mqb_args(vfd) ;
args->readable = true ;
args->read_on = on ;
args->read_timeout = timeout ;
vio_timer_squelch(&vfd->read_timer) ;
- vio_fd_mqb_dispatch(vfd) ;
+ vio_vfd_mqb_dispatch(vfd) ;
} ;
return on ;
} ;
/*------------------------------------------------------------------------------
- * Set or unset write ready state on given vio_fd (if any) if it is active.
+ * Set or unset write ready state on given vio_vfd (if any) if it is active.
*
* If setting write_on, starts any write timeout timer.
* If setting write off, stops any write timeout timer.
@@ -482,8 +539,8 @@ vio_fd_set_read(vio_fd vfd, on_off_t on, vty_timer_time timeout)
* NB: this can done from any thread, but if not done from the CLI thread,
* a message must be sent to the CLI thread to actually implement.
*/
-extern on_off_t
-vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
+extern on_off_b
+vio_vfd_set_write(vio_vfd vfd, on_off_b on, vty_timer_time timeout)
{
struct vio_io_set_args* args ;
@@ -507,7 +564,7 @@ vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
{
assert(vfd->write_action != NULL) ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf == NULL)
{
@@ -516,20 +573,20 @@ vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
vfd->fd, vfd) ;
} ;
qps_enable_mode(vfd->f.qf, qps_write_mnum,
- vio_fd_qps_write_action) ;
+ vio_vfd_qps_write_action) ;
}
else
{
if (vfd->f.thread.write == NULL)
vfd->f.thread.write = thread_add_write(vty_master,
- vio_fd_thread_write_action, vfd, vfd->fd) ;
+ vio_vfd_thread_write_action, vfd, vfd->fd) ;
} ;
vio_timer_set(&vfd->write_timer, timeout) ;
}
else
{
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (vfd->f.qf != NULL)
qps_disable_modes(vfd->f.qf, qps_write_mbit) ;
@@ -547,12 +604,12 @@ vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
{
/* In other threads, must send message to cli thread */
- args = vio_fd_mqb_args(vfd) ;
+ args = vio_vfd_mqb_args(vfd) ;
args->writable = true ;
args->write_on = on ;
args->write_timeout = timeout ;
vio_timer_squelch(&vfd->write_timer) ;
- vio_fd_mqb_dispatch(vfd) ;
+ vio_vfd_mqb_dispatch(vfd) ;
} ;
return on ;
@@ -567,19 +624,21 @@ vio_fd_set_write(vio_fd vfd, on_off_t on, vty_timer_time timeout)
* message is yet to be procesed.
*/
static void
-vio_fd_qps_read_action(qps_file qf, void* file_info)
+vio_vfd_qps_read_action(qps_file qf, void* file_info)
{
- vio_fd vfd = file_info ;
+ vio_vfd vfd ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+
+ vfd = file_info ;
assert((vfd->fd == qf->fd) && (vfd->f.qf == qf)) ;
qps_disable_modes(vfd->f.qf, qps_read_mbit) ;
vio_timer_unset(&vfd->read_timer) ;
- vio_fd_do_read_action(vfd) ;
+ vio_vfd_do_read_action(vfd) ;
VTY_UNLOCK() ;
} ;
@@ -593,19 +652,21 @@ vio_fd_qps_read_action(qps_file qf, void* file_info)
* message is yet to be procesed.
*/
static int
-vio_fd_thread_read_action(struct thread *thread)
+vio_vfd_thread_read_action(struct thread *thread)
{
- vio_fd vfd = THREAD_ARG (thread);
+ vio_vfd vfd ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+
+ vfd = THREAD_ARG(thread);
assert(vfd->fd == THREAD_FD(thread)) ;
vfd->f.thread.read = NULL ; /* implicitly */
vio_timer_unset(&vfd->read_timer) ;
- vio_fd_do_read_action(vfd) ;
+ vio_vfd_do_read_action(vfd) ;
VTY_UNLOCK() ;
return 0 ;
@@ -620,19 +681,19 @@ vio_fd_thread_read_action(struct thread *thread)
* message is yet to be procesed.
*/
static void
-vio_fd_qps_write_action(qps_file qf, void* file_info)
+vio_vfd_qps_write_action(qps_file qf, void* file_info)
{
- vio_fd vfd = file_info ;
+ vio_vfd vfd = file_info ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
assert((vfd->fd == qf->fd) && (vfd->f.qf == qf)) ;
qps_disable_modes(vfd->f.qf, qps_write_mbit) ;
vio_timer_unset(&vfd->write_timer) ;
- vio_fd_do_write_action(vfd) ;
+ vio_vfd_do_write_action(vfd) ;
VTY_UNLOCK() ;
} ;
@@ -646,19 +707,19 @@ vio_fd_qps_write_action(qps_file qf, void* file_info)
* message is yet to be procesed.
*/
static int
-vio_fd_thread_write_action(struct thread *thread)
+vio_vfd_thread_write_action(struct thread *thread)
{
- vio_fd vfd = THREAD_ARG (thread);
+ vio_vfd vfd = THREAD_ARG (thread);
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
assert(vfd->fd == THREAD_FD(thread)) ;
vio_timer_unset(&vfd->write_timer) ;
vfd->f.thread.write = NULL ; /* implicitly */
- vio_fd_do_write_action(vfd) ;
+ vio_vfd_do_write_action(vfd) ;
VTY_UNLOCK() ;
return 0 ;
@@ -675,57 +736,31 @@ vio_fd_thread_write_action(struct thread *thread)
* further dispatch is required. When it has been dequeued and processed,
* it is marked inactive.
*
- * If the vfd is closed while the message is active, it is marked to die,
+ * If the vfd is closed while the message is active, it is marked to close,
* which it will do when it is dequeued and actioned.
*/
-static void vio_fd_set_action(mqueue_block mqb, mqb_flag_t flag) ;
+static void vio_vfd_set_action(mqueue_block mqb, mqb_flag_t flag) ;
/*------------------------------------------------------------------------------
* Get mqb for the given vfd -- make one if required.
*/
static struct vio_io_set_args*
-vio_fd_mqb_args(vio_fd vfd)
+vio_vfd_mqb_args(vio_vfd vfd)
{
VTY_ASSERT_LOCKED() ;
if (vfd->mqb == NULL)
- vfd->mqb = mqb_init_new(NULL, vio_fd_set_action, vfd) ;
+ vfd->mqb = mqb_init_new(NULL, vio_vfd_set_action, vfd) ;
return mqb_get_args(vfd->mqb) ;
} ;
/*------------------------------------------------------------------------------
- * Free mqb for the given vfd -- if any.
- */
-static void
-vio_fd_mqb_free(vio_fd vfd)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vfd->mqb != NULL)
- {
- struct vio_io_set_args* args = mqb_get_args(vfd->mqb) ;
-
- if (args->active)
- {
- args->die = true ;
- mqb_set_arg0(vfd->mqb, NULL) ;
- }
- else
- {
- mqb_free(vfd->mqb) ;
- } ;
-
- vfd->mqb = NULL ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
* Dispatch mqb, if not already active
*/
static void
-vio_fd_mqb_dispatch(vio_fd vfd)
+vio_vfd_mqb_dispatch(vio_vfd vfd)
{
struct vio_io_set_args* args = mqb_get_args(vfd->mqb) ;
@@ -741,33 +776,43 @@ vio_fd_mqb_dispatch(vio_fd vfd)
/*------------------------------------------------------------------------------
* Action routine for the read/write on/off setting message.
*
- * If the mqb is marked to die, then it and any qps_file it points to have been
- * cut loose, and now is the time to close the fd and release the qps_file,
- * along with releasing the mqb.
+ * If the mqb is marked to close, then it and any qps_file it points to have
+ * been cut loose, and now is the time to close the fd and release the
+ * qps_file, along with releasing the mqb.
+ *
+ * If the mqb is being revoked
*/
static void
-vio_fd_set_action(mqueue_block mqb, mqb_flag_t flag)
+vio_vfd_set_action(mqueue_block mqb, mqb_flag_t flag)
{
- struct vio_io_set_args* args = mqb_get_args(mqb) ;
- vio_fd vfd = mqb_get_arg0(mqb) ;
+ struct vio_io_set_args* args ;
+ vio_vfd vfd ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+
+ args = mqb_get_args(mqb) ;
+ vfd = mqb_get_arg0(mqb) ;
args->active = false ;
- if ((flag != mqb_destroy) && (!args->die))
+ if ((flag != mqb_destroy) && (!args->close))
{
if (args->readable)
- vio_fd_set_read(vfd, args->read_on, args->read_timeout) ;
+ vio_vfd_set_read(vfd, args->read_on, args->read_timeout) ;
if (args->writable)
- vio_fd_set_write(vfd, args->write_on, args->write_timeout) ;
+ vio_vfd_set_write(vfd, args->write_on, args->write_timeout) ;
}
else
{
- if (args->close)
- vio_fd_do_close(vfd) ;
- mqb_free(mqb) ;
+ /* Revoke and/or close.
+ *
+ * If close can free the vfd.
+ *
+ * If just revoke (should not happen), then cannot free the vfd, because
+ * somewhere there can be a dangling reference.
+ */
+ vio_vfd_do_close(vfd, args->close) ; /* frees the mqb */
} ;
VTY_UNLOCK() ;
@@ -779,7 +824,7 @@ vio_fd_set_action(mqueue_block mqb, mqb_flag_t flag)
*
*/
-static void vio_accept(vio_fd vfd, void* info) ;
+static void vio_accept(vio_vfd vfd, void* info) ;
/*------------------------------------------------------------------------------
* Create a new listener object for the newly opened listener socket.
@@ -790,7 +835,7 @@ static void vio_accept(vio_fd vfd, void* info) ;
* Returns address of newly created listener structure.
*/
extern vio_listener
-vio_listener_new(int fd, vio_fd_accept* accept_action)
+vio_listener_new(int fd, vio_vfd_accept* accept_action)
{
vio_listener listener ;
@@ -800,12 +845,12 @@ vio_listener_new(int fd, vio_fd_accept* accept_action)
listener = XCALLOC(MTYPE_VTY, sizeof(struct vio_listener)) ;
/* sets the next pointer to NULL */
- listener->vfd = vio_fd_new(fd, vfd_listener, vfd_io_read, listener) ;
+ listener->vfd = vio_vfd_new(fd, vfd_listener, vfd_io_read, listener) ;
listener->accept_action = accept_action ;
- vio_fd_set_read_action(listener->vfd, vio_accept) ;
- vio_fd_set_read(listener->vfd, on, 0) ;
+ vio_vfd_set_read_action(listener->vfd, vio_accept) ;
+ vio_vfd_set_read(listener->vfd, on, 0) ;
return listener ;
} ;
@@ -822,7 +867,7 @@ vio_listener_close(vio_listener listener)
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
- vio_fd_close(listener->vfd) ;
+ vio_vfd_close(listener->vfd) ;
XFREE(MTYPE_VTY, listener) ;
} ;
@@ -832,7 +877,7 @@ vio_listener_close(vio_listener listener)
* info points at the listener object.
*/
static void
-vio_accept(vio_fd vfd, void* info)
+vio_accept(vio_vfd vfd, void* info)
{
vio_listener listener ;
@@ -842,6 +887,8 @@ vio_accept(vio_fd vfd, void* info)
listener = info ;
assert(vfd == listener->vfd) ;
+ vio_vfd_set_read(listener->vfd, on, 0) ;
+
listener->accept_action(vfd->fd) ;
} ;
@@ -902,7 +949,6 @@ static void
vio_timer_squelch(vio_timer_t* timer)
{
VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
timer->squelch = true ;
} ;
@@ -920,7 +966,7 @@ vio_timer_reset(vio_timer_t* timer)
if (timer->t.anon != NULL)
{
- if (vty_cli_nexus)
+ if (vty_nexus)
qtimer_free(timer->t.qtr) ;
else
thread_cancel(timer->t.thread) ;
@@ -953,7 +999,7 @@ vio_timer_set(vio_timer_t* timer, vty_timer_time time)
assert(timer->action != NULL) ;
- if (vty_cli_nexus)
+ if (vty_nexus)
{
if (timer->t.qtr == NULL) /* allocate qtr if required */
timer->t.qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
@@ -983,7 +1029,7 @@ vio_timer_unset(vio_timer_t* timer)
if (timer->active)
{
- if (vty_cli_nexus)
+ if (vty_nexus)
{
assert(timer->t.qtr != NULL) ;
qtimer_unset(timer->t.qtr) ;
@@ -1007,13 +1053,15 @@ vio_timer_unset(vio_timer_t* timer)
static void
vio_timer_qtr_action(qtimer qtr, void* timer_info, qtime_t when)
{
- vio_timer_t* timer = timer_info ;
+ vio_timer_t* timer ;
vty_timer_time time ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+
+ timer = timer_info ;
- if (!timer->squelch) /* do nothing if squelched */
+ if ((timer->action != NULL) && (!timer->squelch))
{
time = timer->action(timer, timer->action_info) ;
if (time != 0)
@@ -1036,15 +1084,16 @@ vio_timer_qtr_action(qtimer qtr, void* timer_info, qtime_t when)
static int
vio_timer_thread_action(struct thread *thread)
{
- vio_timer_t* timer = THREAD_ARG (thread);
+ vio_timer_t* timer ;
vty_timer_time time ;
- VTY_LOCK() ;
VTY_ASSERT_CLI_THREAD() ;
+ VTY_LOCK() ;
+ timer = THREAD_ARG(thread) ;
timer->t.thread = NULL ; /* implicitly */
- if (!timer->squelch) /* do nothing if squelched */
+ if ((timer->action != NULL) && (!timer->squelch))
{
time = timer->action(timer, timer->action_info) ;
if (time != 0)
diff --git a/lib/vty_io_basic.h b/lib/vty_io_basic.h
index a2b1cbb3..00ff923b 100644
--- a/lib/vty_io_basic.h
+++ b/lib/vty_io_basic.h
@@ -27,7 +27,7 @@
#include "misc.h"
-#include "vty.h"
+//#include "vty_local.h"
#include "qpselect.h"
#include "thread.h"
@@ -55,15 +55,23 @@ typedef enum vfd_type vfd_type_t ;
enum vfd_io_type /* NB: *bit*significant* */
{
vfd_io_none = 0,
- vfd_io_read = 1,
- vfd_io_write = 2,
- vfd_io_read_write = 3,
+
+ vfd_io_read = BIT(0),
+ vfd_io_write = BIT(1),
+ vfd_io_read_write = vfd_io_read | vfd_io_write,
+
+ vfd_io_append = BIT(2),
+ vfd_io_blocking = BIT(3),
} ;
typedef enum vfd_io_type vfd_io_type_t ;
/*------------------------------------------------------------------------------
* Timers -- implemented as qtimer or thread timer, depending on environment.
+ *
+ * Timer action function returns a new value for the timer; 0 => off.
*/
+typedef unsigned long vty_timer_time ; /* Time out time in seconds */
+
typedef struct vio_timer vio_timer_t ;
typedef vty_timer_time vio_timer_action(vio_timer_t* timer, void* action_info) ;
@@ -88,10 +96,11 @@ struct vio_timer
* Implemented as qps_file or as read/write thread, depending on the
* environment.
*/
-typedef struct vio_fd* vio_fd ;
-typedef void vio_fd_action(vio_fd vfd, void* action_info) ;
+typedef struct vio_vfd* vio_vfd ;
+
+typedef void vio_vfd_action(vio_vfd vfd, void* action_info) ;
-struct vio_fd
+struct vio_vfd
{
int fd ;
bool active ; /* used for close message */
@@ -99,8 +108,8 @@ struct vio_fd
vfd_type_t type ; /* used for half-close */
vfd_io_type_t io_type ; /* read, write, read/write */
- vio_fd_action* read_action ;
- vio_fd_action* write_action ;
+ vio_vfd_action* read_action ;
+ vio_vfd_action* write_action ;
vio_timer_t read_timer ;
vio_timer_t write_timer ;
@@ -127,39 +136,41 @@ struct vio_fd
typedef struct vio_listener* vio_listener ;
-typedef void vio_fd_accept(int fd) ;
+typedef void vio_vfd_accept(int fd) ;
struct vio_listener
{
vio_listener next ; /* ssl type list */
- vio_fd vfd ;
- vio_fd_accept* accept_action ;
+ vio_vfd vfd ;
+ vio_vfd_accept* accept_action ;
};
/*==============================================================================
* Functions
*/
-extern vio_fd vio_fd_new(int fd, vfd_type_t type,
+extern int uty_vfd_file_open(const char* name, vfd_io_type_t io_type) ;
+
+extern vio_vfd vio_vfd_new(int fd, vfd_type_t type,
vfd_io_type_t io_type, void* action_info) ;
-extern void vio_fd_set_fd(vio_fd vfd, int fd, vfd_type_t type,
+extern void vio_vfd_set_fd(vio_vfd vfd, int fd, vfd_type_t type,
vfd_io_type_t io_type) ;
-extern void vio_fd_set_read_action(vio_fd vfd, vio_fd_action* action) ;
-extern void vio_fd_set_write_action(vio_fd vfd, vio_fd_action* action) ;
-extern void vio_fd_set_read_timeout_action(vio_fd vfd,
+extern void vio_vfd_set_read_action(vio_vfd vfd, vio_vfd_action* action) ;
+extern void vio_vfd_set_write_action(vio_vfd vfd, vio_vfd_action* action) ;
+extern void vio_vfd_set_read_timeout_action(vio_vfd vfd,
vio_timer_action* action) ;
-extern void vio_fd_set_write_timeout_action(vio_fd vfd,
+extern void vio_vfd_set_write_timeout_action(vio_vfd vfd,
vio_timer_action* action) ;
-extern void vio_fd_set_action_info(vio_fd vfd, void* action_info) ;
-extern vio_fd vio_fd_half_close(vio_fd vfd) ;
-extern vio_fd vio_fd_close(vio_fd vfd) ;
-extern on_off_t vio_fd_set_read(vio_fd vfd, on_off_t on,
+extern void vio_vfd_set_action_info(vio_vfd vfd, void* action_info) ;
+extern vio_vfd vio_vfd_read_close(vio_vfd vfd) ;
+extern vio_vfd vio_vfd_close(vio_vfd vfd) ;
+extern on_off_b vio_vfd_set_read(vio_vfd vfd, on_off_b on,
vty_timer_time timeout) ;
-extern on_off_t vio_fd_set_write(vio_fd vfd, on_off_t on,
+extern on_off_b vio_vfd_set_write(vio_vfd vfd, on_off_b on,
vty_timer_time timeout) ;
-Inline int vio_fd_fd(vio_fd vfd) ;
+Inline int vio_vfd_fd(vio_vfd vfd) ;
-extern vio_listener vio_listener_new(int fd, vio_fd_accept* accept) ;
+extern vio_listener vio_listener_new(int fd, vio_vfd_accept* accept) ;
extern void vio_listener_close(vio_listener listener) ;
extern void vio_timer_init(vio_timer_t* timer, vio_timer_action* action,
@@ -175,10 +186,10 @@ extern void vio_timer_unset(vio_timer_t* timer) ;
*/
/*------------------------------------------------------------------------------
- * Return the fd from a vio_fd structure
+ * Return the fd from a vio_vfd structure
*/
Inline int
-vio_fd_fd(vio_fd vfd)
+vio_vfd_fd(vio_vfd vfd)
{
return vfd->fd ;
} ;
diff --git a/lib/vty_io_file.c b/lib/vty_io_file.c
index ed2c35e8..2d17276a 100644
--- a/lib/vty_io_file.c
+++ b/lib/vty_io_file.c
@@ -1,7 +1,6 @@
-/* VTY Log Functions
- * Copyright (C) 1997, 98 Kunihiro Ishiguro
+/* VTY I/O for Files
*
- * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
*
* This file is part of GNU Zebra.
*
@@ -20,2589 +19,304 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
+#include "misc.h"
-#include "zebra.h"
+#include "command_local.h"
-#include "vty.h"
-#include "vty_io.h"
-#include "vty_cli.h"
-#include "qstring.h"
-#include "keystroke.h"
-
-#include "memory.h"
-
-#include "prefix.h"
-#include "filter.h"
-#include "privs.h"
-#include "sockunion.h"
-#include "network.h"
-
-#include <arpa/telnet.h>
-#include <sys/un.h> /* for VTYSH */
-#include <sys/socket.h>
-
-#define VTYSH_DEBUG 0
+#include "vty_io_file.h"
+#include "vty_io_basic.h"
/*==============================================================================
- * VTY Log Output -- provides a "vty" interface to logging.
+ * VTY File Output
*
- * Once a VTY_LOG object is created, can be used as an (output only) vty.
+ * This is for input and output of configuration files and piped stuff.
*
- * All output is sent to the logging system,
- *
- *
- * During command processing the output sent here is held until the command
- * completes.
+ * When reading the configuration (and piped stuff in the configuration) I/O
+ * is blocking... nothing else can run while this is going on. Otherwise,
+ * all I/O is non-blocking.
*/
-static int uty_config_write(vty_io vio, bool all) ;
-
-/*------------------------------------------------------------------------------
- * VTY output function -- cf fprintf
- *
- * Returns: >= 0 => OK
- * < 0 => failed (see errno)
- */
-extern int
-uty_out (struct vty *vty, const char *format, ...)
-{
- int result;
- VTY_ASSERT_LOCKED() ;
- va_list args;
- va_start (args, format);
- result = uty_vout(vty, format, args);
- va_end (args);
- return result;
-}
-
-/*------------------------------------------------------------------------------
- * VTY output function -- cf vfprintf
- *
- * 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)
-{
- vty_io vio ;
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- vio = vty->vio ;
-
- switch (vio->type)
- {
- case VTY_STDOUT:
- case VTY_SHELL:
- 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_TERM:
- case VTY_SHELL_SERV:
- assert(vio->cmd_in_progress) ;
-
- 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 ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Clear the contents of the command output FIFO etc.
- *
- * 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_clear(vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
-
- 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) ;
-} ;
/*==============================================================================
* Prototypes.
*/
-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) ;
-static void vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when) ;
-
-static int vty_read_thread (struct thread *thread) ;
-static int vty_write_thread (struct thread *thread) ;
-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
- */
/*------------------------------------------------------------------------------
- * Allocate new vty struct
- *
- * Allocates and initialises basic vty and vty_io structures, setting the
- * given type.
- *
- * Note that where is not setting up a vty_sock, this *may* be called from
- * any thread.
*
- * 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(enum vty_type type, int sock_fd)
-{
- struct vty *vty ;
- struct vty_io* vio ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* 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_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 !)
- * closed = 0 -- NOT closed (important !)
- * close_reason = NULL -- no reason, yet
- *
- * real_type = 0 -- not material
- * file_fd = 0 -- not material
- * file_error = 0 -- not material
- *
- * key_stream = NULL -- no key stream (always empty, at EOF)
- *
- * cli_drawn = 0 -- not drawn
- * cli_dirty = 0 -- not dirty
- * cli_prompt_len = 0 )
- * 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_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--"
- *
- * cmd_out_done = 0 -- not material
- *
- * cli_do = 0 == cli_do_nothing
- *
- * cmd_lc = NULL -- no line control
- *
- * fail = 0 -- no login failures yet
- *
- * hist = empty vector
- * hp = 0 -- at the beginning
- * hindex = 0 -- the beginning
- *
- * width = 0 -- unknown console width
- * height = 0 -- unknown console height
- *
- * lines = 0 -- no limit
- * lines_set = 0 -- no explicit setting
- *
- * monitor = 0 -- not a monitor
- * monitor_busy = 0 -- not a busy monitor
- *
- * config = 0 -- not holder of "config" mode
- */
- confirm(cli_do_nothing == 0) ;
- confirm(AUTH_NODE == 0) ; /* default node type */
-
- vio->type = type ;
-
- /* 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
- */
-
- /* 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) ;
-
- vio_fifo_init_new(&vio->cli_obuf, 2 * 1024) ; /* allocate in 2K lumps */
-
- vio_fifo_init_new(&vio->cmd_obuf, 8 * 1024) ;
-
- /* Place on list of known vio/vty */
- sdl_push(vio_list_base, vio, vio_list) ;
-
- return vty;
-} ;
-
-/*------------------------------------------------------------------------------
- * Create new vty of type VTY_TERM -- ie attached to a telnet session.
*
- * Returns: new vty
*/
-static struct vty *
-uty_new_term(int sock_fd, union sockunion *su)
+extern cmd_return_code_t
+uty_file_read_open(vty_io vio, qstring name, bool reflect)
{
- 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_TERM, sock_fd) ;
- vio = vty->vio ;
-
- /* 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->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->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 */
- vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
-
- /* Set the initial node */
- if (no_password_check)
- {
- if (restricted_mode)
- vty->node = RESTRICTED_NODE;
- else if (host.advanced)
- vty->node = ENABLE_NODE;
- else
- vty->node = VIEW_NODE;
- }
- else
- vty->node = AUTH_NODE;
-
- /* Pick up current timeout setting */
- vio->sock.v_timeout = vty_timeout_val;
-
- /* Use global 'lines' setting, as default. May be -1 => unset */
- vio->lines = host.lines ;
-
- /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
- vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
- uty_set_height(vio) ; /* set initial state */
-
- /* Initialise the CLI, ready for start-up messages etc. */
- uty_cli_init(vio) ;
-
- /* Reject connection if password isn't set, and not "no password" */
- if ((host.password == NULL) && (host.password_encrypt == NULL)
- && ! no_password_check)
- {
- uty_half_close (vio, "Vty password is not set.");
- vty = NULL;
- }
- else
- {
- /* Say hello to the world. */
- vty_hello (vty);
-
- if (! no_password_check)
- uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
- VTY_NEWLINE, VTY_NEWLINE);
- } ;
+ const char* name_str ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
+ name_str = qs_make_string(name) ;
- return vty;
-} ;
+ iot = vfd_io_read | vfd_io_blocking ; /* TODO blocking */
-/*------------------------------------------------------------------------------
- * Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
- *
- * Returns: new vty
- */
-static struct vty *
-uty_new_shell_serv(int sock_fd)
-{
- struct vty *vty ;
- vty_io vio ;
+ /* Do the basic file open. */
+ fd = uty_vfd_file_open(name_str, iot) ;
- VTY_ASSERT_LOCKED() ;
-
- /* Allocate new vty structure and set up default values. */
- vty = uty_new (VTY_SHELL_SERV, sock_fd) ;
- vio = vty->vio ;
-
- /* Set the action functions */
- if (vty_cli_nexus)
+ if (fd < 0)
{
- vio->sock.action.read.qnexus = vtysh_read_qnexus ;
- vio->sock.action.write.qnexus = vty_write_qnexus ;
- vio->sock.action.timer.qnexus = NULL ;
- }
- else
- {
- 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;
+ uty_out(vio, "%% Could not open input file %s\n", name_str) ;
- /* Kick start the CLI etc. */
- uty_sock_set_readiness(&vio->sock, write_ready) ;
-
- return vty;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set/Clear "monitor" state:
- *
- * set: if VTY_TERM and not already "monitor" (and write_open !)
- * clear: if is "monitor"
- */
-extern void
-uty_set_monitor(vty_io vio, bool on)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (on && !vio->monitor)
- {
- if ((vio->type == VTY_TERM) && vio->sock.write_open)
- {
- vio->monitor = 1 ;
- sdl_push(vio_monitors_base, vio, mon_list) ;
- } ;
- }
- else if (!on && vio->monitor)
- {
- vio->monitor = 0 ;
- sdl_del(vio_monitors_base, vio, mon_list) ;
+ return CMD_WARNING ;
}
-} ;
-
-/*------------------------------------------------------------------------------
- * Return "name" of VTY
- *
- * For VTY_TERM this is the IP address of the far end of the telnet connection.
- */
-extern const char*
-uty_get_name(vty_io vio)
-{
- return (vio->name != NULL) ? vio->name : "?" ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Closing down VTY for reading.
- *
- * 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
- *
- * 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, const char* reason)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->half_closed)
- return ;
-
- if (reason != NULL)
- vio->close_reason = reason ;
-
- /* 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) ;
-
- /* Discard everything in the keystroke stream and force it to EOF */
- if (vio->key_stream != NULL)
- keystroke_stream_set_eof(vio->key_stream) ;
-
- /* Turn off "--more--" so that all output clears without interruption.
- *
- * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
- */
- vio->cli_more_enabled = 0 ;
-
- if (vio->cli_more_wait)
- uty_cli_exit_more_wait(vio) ;
-
- /* If a command is not in progress, enable output, which will clear
- * the output buffer if there is anything there, plus any close reason,
- * and then close.
- *
- * If command is in progress, then this process will start when it
- * completes.
- */
- if (!vio->cmd_in_progress)
- vio->cmd_out_enabled = 1 ;
-
- /* Make sure no longer holding the config symbol of power */
- 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) ;
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ;
+ uty_vin_open(vio, vf, VIN_FILE, NULL, NULL, 16 * 1024) ;
- /* Move to the death watch list */
- sdl_del(vio_list_base, vio, vio_list) ;
- sdl_push(vio_death_watch, vio, vio_list) ;
+ vf->parse_type = cmd_parse_strict ;
+ vf->reflect_enabled = reflect ;
- vio->half_closed = 1 ;
+ return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
- * Closing down VTY.
*
- * Shuts down everything and discards all buffers etc. etc.
*
- * 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() ;
-
- /* 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 */
- if (vio->type == VTY_TERM)
- 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_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_reset(&vio->cli_prompt_for_node, keep_it) ;
- qs_reset(&vio->cl, keep_it) ;
-
- {
- qstring line ;
- while ((line = vector_ream(vio->hist, keep_it)) != NULL)
- qs_reset_free(line) ;
- } ;
-
- /* 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_reset(&vio->clx, keep_it) ;
- vio->vty->buf = NULL ;
- } ;
-} ;
-
-/*==============================================================================
- * For writing configuration file by command, temporarily redirect output to
- * an actual file.
*/
-
-/*------------------------------------------------------------------------------
- * Set the given fd as the VTY_FILE output.
- */
-extern void
-vty_open_config_write(struct vty* vty, int fd)
+extern cmd_return_code_t
+uty_file_write_open(vty_io vio, qstring name, bool append)
{
- vty_io vio ;
-
- VTY_LOCK() ;
+ const char* name_str ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
- vio = vty->vio ;
+ iot = vfd_io_write | vfd_io_blocking ; /* TODO blocking */
- assert((vio->type != VTY_CONFIG_WRITE) && (vio->type != VTY_NONE)) ;
+ if (append)
+ iot |= vfd_io_append ;
- vio->real_type = vio->type ;
+ name_str = qs_make_string(name) ;
- vio->type = VTY_CONFIG_WRITE ;
- vio->file_fd = fd ;
- vio->file_error = 0 ;
+ /* Do the basic file open. */
+ fd = uty_vfd_file_open(name_str, iot) ;
- VTY_UNLOCK() ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Write away configuration file stuff -- all or just the full lump(s).
- *
- * Returns: > 0 => blocked
- * 0 => all gone (up to last lump if !all)
- * < 0 => failed -- see vio->file_error
- */
-static int
-uty_config_write(vty_io vio, bool all)
-{
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- if (vio->file_error == 0)
+ if (fd < 0)
{
- ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->file_fd, all) ;
+ uty_out(vio, "%% Could not open output file %s\n", name_str) ;
- if (ret < 0)
- vio->file_error = errno ;
+ return CMD_WARNING ;
}
- else
- ret = -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 */
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ;
+ uty_vout_open(vio, vf, VOUT_FILE, NULL, NULL, 16 * 1024) ;
- err = vio->file_error ;
+ vf->out_enabled = true ;
- vio->type = vio->real_type ;
- vio->file_fd = -1 ;
- vio->file_error = 0 ;
-
- VTY_UNLOCK() ;
-
- return err ;
+ return CMD_SUCCESS ;
} ;
/*==============================================================================
- * vio_sock level operations
- */
-
-/*------------------------------------------------------------------------------
- * Initialise a new vio_sock structure.
- *
- * Requires that: the vio_sock structure is not currently in use.
- *
- * 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_sock_init_new(vio_sock sock, int fd, void* info)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (fd >= 0)
- VTY_ASSERT_CLI_THREAD() ;
-
- memset(sock, 0, sizeof(struct vio_sock)) ;
-
- /* Zeroising the structure has set:
- *
- * action = all the actions set NULL
- *
- * error_seen = 0 -- no error, yet
- *
- * qf = NULL -- no qfile, yet
- * t_read = NULL ) no threads, yet
- * t_write = NULL )
- *
- * v_timeout = 0 -- no timeout set
- * timer_runing = 0 -- not running, yet
- * t_timer = NULL -- no timer thread, yet
- * qtr = NULL -- no qtimer, yet
- */
- sock->fd = fd ;
- sock->info = info ;
-
- sock->read_open = (fd >= 0) ;
- sock->write_open = (fd >= 0) ;
-
- if ((fd >= 0) && vty_cli_nexus)
- {
- sock->qf = qps_file_init_new(NULL, NULL);
- qps_add_file(vty_cli_nexus->selection, sock->qf, sock->fd, sock->info);
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Restart the timer.
+ * Command line fetch from a file or pipe.
*
- * If a timeout time is set, then start or restart the timer with that value.
- *
- * If no timeout time is set, and the timer is running, unset it.
+ * Returns: CMD_SUCCESS -- have another command line ready to go
+ * CMD_WAITING -- do not have a command line at the moment
+ * CMD_EOF -- ran into EOF
+ * CMD_IO_ERROR -- ran into an I/O error
*/
-static void
-uty_sock_restart_timer(vio_sock sock)
+extern cmd_return_code_t
+uty_file_fetch_command_line(vio_vf vf, qstring* line)
{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
+ assert(vf->vin_state == vf_open) ;
- if (sock->v_timeout != 0)
+ if (vf->line_complete)
{
- assert(sock->action.timer.anon != NULL) ;
+ vio_fifo_set_hold_mark(vf->ibuf) ; /* advance hold */
- if (vty_cli_nexus)
- {
- 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 (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) ;
- } ;
-
- sock->timer_running = 1 ;
- }
- else if (sock->timer_running)
- {
- if (vty_cli_nexus)
- {
- if (sock->qtr != NULL)
- qtimer_unset(sock->qtr) ;
- }
- else
- {
- if (sock->t_timer != NULL)
- thread_cancel (sock->t_timer) ;
- } ;
+ vf->line_complete = false ;
+ vf->line_number += vf->line_step ;
- sock->timer_running = 0 ;
+ qs_set_len_nn(vf->cl, 0) ;
+ vf->line_step = 0 ;
} ;
-} ;
-/*------------------------------------------------------------------------------
- * Set read on/off
- *
- * Returns: the on/off state set
- */
-static bool
-uty_sock_set_read(vio_sock sock, bool on)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- if (sock->fd < 0)
- return 0 ;
-
- if (on)
+ while (1)
{
- assert(sock->action.read.anon != NULL) ;
+ char* s, * p, * q, * e ;
+ size_t have ;
+ ulen len ;
- if (vty_cli_nexus)
- qps_enable_mode(sock->qf, qps_read_mnum, sock->action.read.qnexus) ;
- else
- {
- if (sock->t_read != NULL)
- thread_cancel(sock->t_read) ;
+ s = vio_fifo_get(vf->ibuf, &have) ;
- sock->t_read = thread_add_read(vty_master,
- sock->action.read.thread, sock->info, sock->fd) ;
- } ;
- }
- else
- {
- if (vty_cli_nexus)
- qps_disable_modes(sock->qf, qps_read_mbit) ;
- else
+ /* If nothing in hand, try and get some more.
+ *
+ * Either exits or loops back to set s & have.
+ */
+ if (have == 0)
{
- if (sock->t_read != NULL)
- thread_cancel (sock->t_read) ;
- } ;
- } ;
-
- return on ;
-} ;
+ int get ;
-/*------------------------------------------------------------------------------
- * Set write on/off
- *
- * Returns: the on/off state set
- */
-static bool
-uty_sock_set_write(vio_sock sock, bool on)
-{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
+ get = vio_fifo_read_nb(vf->ibuf, vio_vfd_fd(vf->vfd), 100) ;
- if (sock->fd < 0)
- return 0 ;
+ if (get > 0)
+ continue ; /* loop back */
- if (on)
- {
- assert(sock->action.write.anon != NULL) ;
+ if (get == 0)
+ return CMD_WAITING ; /* need to set read ready ! */
- if (vty_cli_nexus)
- qps_enable_mode(sock->qf, qps_write_mnum, sock->action.write.qnexus) ;
- else
- {
- if (sock->t_write != NULL)
- thread_cancel(sock->t_write) ;
+ if (get == -1)
+ ; /* register error */
- sock->t_write = thread_add_write(vty_master,
- sock->action.write.thread, sock->info, sock->fd) ;
+ if (get == -2)
+ return (qs_len_nn(vf->cl) > 0) ? CMD_SUCCESS : CMD_EOF ;
} ;
- }
- else
- {
- if (vty_cli_nexus)
- qps_disable_modes(sock->qf, qps_write_mbit) ;
- else
- {
- if (sock->t_write != NULL)
- thread_cancel (sock->t_write) ;
- } ;
- } ;
-
- 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)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * 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_sock_half_close(vio_sock sock)
-{
- VTY_ASSERT_LOCKED() ;
-
- sock->read_open = 0 ; /* make sure */
-
- if (sock->fd < 0)
- return ; /* nothing more if no socket */
-
- VTY_ASSERT_CLI_THREAD() ;
-
- 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_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_sock_close(vio_sock sock)
-{
- VTY_ASSERT_LOCKED() ;
-
- sock->read_open = 0 ; /* make sure */
- sock->write_open = 0 ;
-
- 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 */
- } ;
-
- VTY_ASSERT_CLI_THREAD() ;
- close(sock->fd) ;
-
- 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 ;
- } ;
-
- sock->fd = -1 ;
-
- if (sock->t_read != NULL)
- thread_cancel(sock->t_write) ;
- if (sock->t_write != NULL)
- thread_cancel(sock->t_write) ;
-
- sock->t_read = NULL ;
- sock->t_write = NULL ;
-
- sock->info = NULL ;
- sock->action.read.anon = NULL ;
- sock->action.write.anon = NULL ;
- sock->action.timer.anon = 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 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Dealing with an I/O error on VTY socket
- *
- * If this is the first error for this VTY, produce suitable log message.
- *
- * 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() ;
-
- /* 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, errtoa(vio->sock.error_seen, 0).str) ;
- } ;
-
- return -1 ;
-} ;
-
-/*==============================================================================
- * Readiness and the VTY_TERM type VTY.
- *
- * 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.
- *
- * The VTY_TERM uses read ready only when it doesn't set write ready. Does
- * not set both at once.
- *
- * So there is only one, common, uty_ready function, which:
- *
- * 1. attempts to clear any output it can.
- *
- * The state of the output affects the CLI, so must always do this before
- * before invoking the CLI.
- *
- * 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_ready(vty_io vio)
-{
- enum vty_readiness ready ;
-
- 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
- */
-static void
-vty_read_qnexus(qps_file qf, void* file_info)
-{
- vty_io vio = file_info;
-
- VTY_LOCK() ;
-
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
-
- uty_ready(vio) ;
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- threads: ready to read -> kicking CLI
- */
-static int
-vty_read_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
- vio->sock.t_read = NULL ; /* implicitly */
- uty_ready(vio);
-
- VTY_UNLOCK() ;
- return 0 ;
-}
-
-/*------------------------------------------------------------------------------
- * Read a lump of bytes and shovel into the keystroke stream
- *
- * Steal keystroke if required -- see keystroke_input()
- *
- * Returns: 0 => nothing available
- * > 0 => read at least one byte
- * -1 => EOF (or not open, or failed)
- */
-extern int
-uty_read (vty_io vio, keystroke steal)
-{
- unsigned char buf[500] ;
- int get ;
-
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open */
-
- 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_sock_error(vio, "read") ;
-
- vio->sock.read_open = 0 ;
- keystroke_input(vio->key_stream, NULL, 0, steal) ;
-
- get = -1 ;
- } ;
-
- return get ;
-} ;
-
-/*==============================================================================
- * 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 written to the file only while
- * cmd_out_enabled.
- *
- * The cli output takes precedence.
- *
- * Output of command stuff is subject to line_control, and may go through the
- * "--more--" mechanism.
- */
-
-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
- */
-static void
-vty_write_qnexus(qps_file qf, void* file_info)
-{
- vty_io vio = file_info ;
-
- VTY_LOCK() ;
-
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
-
- uty_ready(vio) ;
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- thread: ready to write -> try to empty buffers
- */
-static int
-vty_write_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
-
- vio->sock.t_write = NULL; /* implicitly */
- uty_ready(vio) ;
-
- VTY_UNLOCK() ;
- return 0 ;
-}
-
-/*------------------------------------------------------------------------------
- * Write as much as possible of what there is.
- *
- * If not cmd_in_progress, clears cli_blocked if both FIFOs are, or become,
- * empty.
- *
- * 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 writing everything available for output
- * by write() threatening to block.
- *
- * Returns: write_ready if should now set write on
- * now_ready if should loop back and try again
- * not_ready otherwise
- */
-static enum vty_readiness
-uty_write(vty_io vio)
-{
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- 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 ;
- }
-
- /* Next: empty out the cli output */
- ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
- if (ret != 0)
- break ;
-
- /* Finished now if not allowed to progress the command stuff */
- if (!vio->cmd_out_enabled)
- return not_ready ; /* done all can do */
+ /* Try to find a '\n' -- converting all other control chars to ' '
+ *
+ * When we find '\n' step back across any trailing ' ' (which includes
+ * any control chars before the '\n').
+ *
+ * This means that we cope with "\r\n" line terminators. But not anything
+ * more exotic.
+ */
+ p = s ;
+ e = s + have ; /* have != 0 */
+ q = NULL ;
- /* Last: if there is something in the command buffer, do that */
- if (!vio_fifo_empty(&vio->cmd_obuf))
+ while (p < e)
{
- 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 (*p++ < 0x20)
+ {
+ if (*(p-1) != '\n')
+ {
+ *(p-1) = ' ' ; /* everything other than '\n' */
+ continue ;
+ } ;
- 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) ;
+ ++vf->line_step ; /* got a '\n' */
- /* 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 ;
+ q = p ; /* point just past '\n' */
+ do --q ; while ((q > s) && (*(q-1) == ' ')) ;
+ /* discard trailing "spaces" */
+ break ;
} ;
+ } ;
- if (ret != 0)
- break ;
- }
+ /* Step past what have just consumed -- we have a hold_mark, so
+ * stuff is still in the fifo.
+ */
+ vio_fifo_step(vf->ibuf, p - s) ;
- /* Exciting stuff: there is nothing left to output...
+ /* If not found '\n', then we have a line fragment that needs to be
+ * appended to any previous line fragments.
*
- * ... watch out for half closed state.
+ * Loops back to try to get some more form the fifo.
*/
- if (vio->half_closed)
+ if (q == NULL)
{
- if (vio->close_reason != NULL)
- {
- vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
-
- struct vty* vty = vio->vty ;
- if (vio->cli_drawn || vio->cli_dirty)
- vty_out(vty, VTY_NEWLINE) ;
- vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
-
- vio->cmd_in_progress = 0 ;
-
- 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 */
+ qs_append_n(vf->cl, s, p - s) ;
+ continue ;
} ;
- /* For VTY_TERM: if the command line is not drawn, now is a good
- * time to do that.
+ /* If we have nothing so far, set alias to point at what we have in
+ * the fifo. Otherwise, append to what we have.
+ *
+ * End up with: s = start of entire line, so far.
+ * p = end of entire line so far.
*/
- 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 ;
- } ;
-
- /* 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.
- */
- if (vio->sock.write_open)
- {
- if (!vio->sock.read_open)
- uty_sock_error(vio, "write") ;
-
- 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) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * 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).
- *
- * NB: expects that the sock is write_open
- *
- * Returns: > 0 => blocked or completed one tranche
- * 0 => all gone
- * < 0 => failed
- */
-static int
-uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
-{
- int ret ;
- char* src ;
- size_t have ;
-
- /* 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))
- {
- size_t take ;
- take = vio_lc_append(lc, src, have) ;
- src = vio_fifo_step_rdr(vf, &have, take) ;
- } ;
-
- vio->cli_dirty = (lc->col != 0) ;
-
- /* Write the contents of the line control */
- ret = uty_write_lc(vio, vf, lc) ;
-
- if (ret < 0)
- return ret ; /* give up now if failed. */
-
- if ((ret == 0) && vio_fifo_empty(vf))
- return 0 ; /* FIFO and line control empty */
-
- /* 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_enter_more_wait(vio) ;
-
- return 1 ; /* FIFO or line control, not empty */
-} ;
-
-/*------------------------------------------------------------------------------
- * 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 ;
-
- ret = vio_lc_write_nb(vio->sock.fd, lc) ;
-
- if (ret <= 0)
- vio_fifo_sync_rdr(vf) ; /* finished with FIFO contents */
-
- return ret ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Start command output -- clears down the line control.
- *
- * Requires that that current line is empty -- restarts the line control
- * on the basis that is at column 0.
- */
-extern void
-uty_cmd_output_start(vty_io vio)
-{
- 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 */
-
- if ((vio->width) != 0)
+ len = q - s ; /* length to add */
+ if (qs_len_nn(vf->cl) == 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 ;
- } ;
+ qs_set_alias_n(vf->cl, s, len) ;
+ p = q ;
}
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 ;
-} ;
-
-/*==============================================================================
- * Timer for VTY_TERM (and VTY_SHELL_SERV).
- */
-
-/*------------------------------------------------------------------------------
- * Timer has expired.
- *
- * If half_closed, then this is curtains -- have waited long enough !
- *
- * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
- */
-static void
-uty_timer_expired (vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->half_closed)
- return uty_close(vio) ; /* curtains */
-
- uty_half_close(vio, "Timed out") ; /* bring input side to a halt */
- } ;
-
-/*------------------------------------------------------------------------------
- * Callback -- qnexus: deal with timer timeout.
- */
-static void
-vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when)
-{
- vty_io vio = timer_info ;
-
- VTY_LOCK() ;
-
- uty_timer_expired(vio);
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- thread: deal with timer timeout.
- */
-static int
-vty_timer_thread (struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- vio->sock.t_timer = NULL ; /* implicitly */
-
- uty_timer_expired(vio) ;
-
- VTY_UNLOCK() ;
- return 0;
-}
-
-/*==============================================================================
- * VTY Listener(s)
- *
- * Have listeners for VTY_TERM and VTY_SHELL_SERV types of VTY.
- */
-
-typedef struct vty_listener* vty_listener ;
-
-struct vty_listener
-{
- vty_listener next ; /* ssl type list */
-
- enum vty_type type ;
-
- struct vio_sock sock ;
-};
-
-/* List of listeners so can tidy up. */
-static vty_listener vty_listeners_list = NULL ;
-
-/* Prototypes for listener stuff */
-static int uty_serv_sock_addrinfo (const char *hostname, unsigned short port) ;
-static int uty_serv_sock(const char* addr, unsigned short port) ;
-static int uty_serv_sock_open(sa_family_t family, int type, int protocol,
- struct sockaddr* sa, unsigned short port) ;
-static int uty_serv_vtysh(const char *path) ;
-static int vty_accept_thread(struct thread *thread) ;
-static void vty_accept_qnexus(qps_file qf, void* listener) ;
-static int uty_accept(vty_listener listener, int listen_sock) ;
-static int uty_accept_term(vty_listener listener) ;
-static int uty_accept_shell_serv (vty_listener listener) ;
-
-static void uty_serv_start_listener(int fd, enum vty_type type) ;
-
-/*------------------------------------------------------------------------------
- * If possible, will use getaddrinfo() to find all the things to listen on.
- */
-
-#if defined(HAVE_IPV6) && !defined(NRL)
-# define VTY_USE_ADDRINFO 1
-#else
-# define VTY_USE_ADDRINFO 0
-#endif
-
-/*------------------------------------------------------------------------------
- * Open VTY listener(s)
- *
- * addr -- address ) to listen for VTY_TERM connections
- * port -- port )
- * path -- path for VTYSH connections -- if VTYSH_ENABLED
- */
-extern void
-uty_open_listeners(const char *addr, unsigned short port, const char *path)
-{
- VTY_ASSERT_LOCKED() ;
-
- /* If port is set to 0, do not listen on TCP/IP at all! */
- if (port)
- {
- int n ;
-
- if (VTY_USE_ADDRINFO)
- n = uty_serv_sock_addrinfo(addr, port);
- else
- n = uty_serv_sock(addr, port);
-
- if (n == 0)
- uzlog(NULL, LOG_ERR, "could not open any VTY listeners") ;
- }
-
- /* If want to listen for vtysh, set up listener now */
- if (VTYSH_ENABLED && (path != NULL))
- uty_serv_vtysh(path) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Close VTY listener
- *
- * addr -- address ) to listen for VTY_TERM connections
- * port -- port )
- * path -- path for VTYSH connections -- if VTYSH_ENABLED
- */
-extern void
-uty_close_listeners(void)
-{
- vty_listener listener ;
-
- VTY_ASSERT_LOCKED() ;
-
- while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
- {
- uty_sock_close(&listener->sock) ; /* no ceremony, no flowers */
- XFREE(MTYPE_VTY, listener) ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Open listener(s) for VTY_TERM -- using getaddrinfo().
- *
- * Returns: number of listeners successfully opened.
- */
-static int
-uty_serv_sock_addrinfo (const char *hostname, unsigned short port)
-{
-#if VTY_USE_ADDRINFO
-
-# ifndef HAVE_IPV6
-# error Using getaddrinfo() but HAVE_IPV6 is not defined ??
-# endif
-
- int ret;
- int n ;
- struct addrinfo req;
- struct addrinfo *ainfo;
- struct addrinfo *ainfo_save;
- char port_str[16];
-
- VTY_ASSERT_LOCKED() ;
-
- /* Want to listen, TCP-wise, on all available address families, on the
- * given port.
- */
- memset (&req, 0, sizeof (struct addrinfo));
- req.ai_flags = AI_PASSIVE;
- req.ai_family = AF_UNSPEC;
- req.ai_socktype = SOCK_STREAM;
- snprintf(port_str, sizeof(port_str), "%d", port);
-
- ret = getaddrinfo (hostname, port_str, &req, &ainfo);
-
- if (ret != 0)
- {
- fprintf (stderr, "getaddrinfo failed: %s\n", eaitoa(ret, errno, 0).str);
- exit (1);
- }
-
- /* Open up sockets on all AF_INET and AF_INET6 addresses */
- ainfo_save = ainfo;
-
- n = 0 ;
- do
- {
- if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
- continue;
-
- assert(ainfo->ai_family == ainfo->ai_addr->sa_family) ;
-
- ret = uty_serv_sock_open(ainfo->ai_family, ainfo->ai_socktype,
- ainfo->ai_protocol, ainfo->ai_addr, port) ;
- if (ret >= 0)
- ++n ;
- }
- while ((ainfo = ainfo->ai_next) != NULL);
-
- freeaddrinfo (ainfo_save);
-
- return n ;
-
-#else
- zabort("uty_serv_sock_addrinfo not implemented") ;
-#endif /* VTY_USE_ADDRINFO */
-}
-
-/*------------------------------------------------------------------------------
- * Open listener(s) for VTY_TERM -- not using getaddrinfo() !
- *
- * Returns: number of listeners successfully opened.
- */
-static int
-uty_serv_sock(const char* addr, unsigned short port)
-{
- int ret;
- int n ;
- union sockunion su_addr ;
- struct sockaddr* sa ;
-
- VTY_ASSERT_LOCKED() ;
-
- n = 0 ; /* nothing opened yet */
-
- /* If have an address, see what kind and whether valid */
- sa = NULL ;
-
- if (addr != NULL)
- {
- ret = str2sockunion (addr, &su_addr) ;
- if (ret == 0)
- sa = &su_addr.sa ;
- else
- uzlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
- } ;
-
- /* Try for AF_INET */
- ret = uty_serv_sock_open(AF_INET, SOCK_STREAM, 0, sa, port) ;
- if (ret >= 0)
- ++n ; /* opened socket */
- if (ret == 1)
- sa = NULL ; /* used the address */
-
-#if HAVE_IPV6
- /* Try for AF_INET6 */
- ret = uty_serv_sock_open(AF_INET6, SOCK_STREAM, 0, sa, port) ;
- if (ret >= 0)
- ++n ; /* opened socket */
- if (ret == 1)
- sa = NULL ; /* used the address */
-#endif
-
- /* If not used the address... something wrong */
- if (sa != NULL)
- uzlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
-
- /* Done */
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Open a VTY_TERM listener socket.
- *
- * The sockaddr 'sa' may be NULL or of a different address family, in which
- * case "any" address is used.
- *
- * If the sockaddr 'sa' is used, only the address portion is used.
- *
- * Returns: < 0 => failed
- * == 0 => OK -- did not use the sockaddr 'sa'.
- * > 1 => OK -- and did use the sockaddr 'sa'
- */
-static int
-uty_serv_sock_open(sa_family_t family, int type, int protocol,
- struct sockaddr* sa, unsigned short port)
-{
- union sockunion su ;
- int sock ;
- int ret ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* Is there an address and is it for this family ? */
- if ((sa != NULL) || (sa->sa_family == family))
- /* Set up sockunion containing required family and address */
- sockunion_new_sockaddr(&su, sa) ;
- else
- {
- /* no address or wrong family -- set up empty sockunion of
- * required family */
- sockunion_init_new(&su, family) ;
- sa = NULL ;
- } ;
-
- /* Open the socket and set its properties */
- sock = sockunion_socket(family, type, protocol) ;
- if (sock < 0)
- return -1 ;
-
- ret = sockopt_reuseaddr (sock);
-
- if (ret >= 0)
- ret = sockopt_reuseport (sock);
-
- 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) ;
+ if (len != 0)
+ qs_append_n(vf->cl, s, len) ;
- if (ret >= 0)
- ret = sockunion_listen (sock, 3);
+ s = qs_char_nn(vf->cl) ;
+ p = s + qs_len_nn(vf->cl) ;
- if (ret < 0)
- {
- close (sock);
- return -1 ;
- }
-
- /* Socket is open -- set VTY Term listener going */
- uty_serv_start_listener(sock, VTY_TERM) ;
-
- /* Return OK and signal whether used address or not */
- return (sa != NULL) ? 1 : 0 ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Open a VTY_SHEL_SERV listener socket (UNIX domain).
- *
- * Returns: < 0 => failed
- * >= 0 => OK
- */
-static int
-uty_serv_vtysh(const char *path)
-{
- int ret;
- int sock, sa_len, path_len ;
- struct sockaddr_un sa_un ;
- mode_t old_mask;
- struct zprivs_ids_t ids;
-
- VTY_ASSERT_LOCKED() ;
-
- /* worry about the path length */
- path_len = strlen(path) + 1 ;
- if (path_len >= (int)sizeof(sa_un.sun_path))
- {
- uzlog(NULL, LOG_ERR, "path too long for unix stream socket: '%s'", path);
- return -1 ;
- } ;
-
- /* First of all, unlink existing socket */
- unlink (path);
-
- /* Make UNIX domain socket. */
- sock = socket (AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- {
- uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
- errtoa(errno, 0).str) ;
- return -1 ;
- }
+ if ((len == 0) && (p > s) && (*(p-1) == ' '))
+ {
+ /* Have an empty end of line section, and the last character
+ * of what we have so far is ' ', so need now to trim trailing
+ * spaces off the stored stuff.
+ */
+ do --p ; while ((p > s) && (*(p-1) == ' ')) ;
- /* Bind to the required path */
- memset (&sa_un, 0, sizeof(sa_un));
- sa_un.sun_family = AF_UNIX;
- strncpy (sa_un.sun_path, path, sizeof(sa_un.sun_path) - 1);
+ qs_set_len_nn(vf->cl, p - s) ;
+ } ;
+ } ;
- sa_len = SUN_LEN(&sa_un) ;
+ /* Now worry about we have a trailing '\'. */
-#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
- sa_un.sun_len = sa_len ;
-#endif
+ if ((p == s) || (*(p-1) != '\\'))
+ break ; /* no \ => no continuation => success */
- old_mask = umask (0007);
+ /* Have a trailing '\'.
+ *
+ * If there are an odd number of '\', strip the last one and loop
+ * round to collect the continuation.
+ *
+ * If there are an even number of '\', then this is not a continuation.
+ *
+ * Note that this rule deals with the case of the continuation line
+ * being empty... e.g. ....\\\ n n -- where n is '\n'
+ */
+ q = p ;
+ do --q ; while ((q > s) && (*(q-1) == '\\')) ;
- ret = bind (sock, (struct sockaddr *) &sa_un, sa_len) ;
- if (ret < 0)
- uzlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, errtoa(errno, 0).str);
+ if (((p - q) & 1) == 0)
+ break ; /* even => no continuation => success */
- if (ret >= 0)
- ret = set_nonblocking(sock);
+ qs_set_len_nn(vf->cl, p - s - 1) ; /* strip odd '\' */
- if (ret >= 0)
- {
- ret = listen (sock, 5);
- if (ret < 0)
- uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
- errtoa(errno, 0).str) ;
+ continue ; /* loop back to fetch more */
} ;
- zprivs_get_ids(&ids);
-
- if (ids.gid_vty > 0)
- {
- /* set group of socket */
- if ( chown (path, -1, ids.gid_vty) )
- uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
- errtoa(errno, 0).str) ;
- }
+ /* Success have a line in hand */
- umask (old_mask);
+ vf->line_complete = true ;
+ *line = vf->cl ;
- /* Give up now if failed along the way */
- if (ret < 0)
- {
- close (sock) ;
- return -1 ;
- } ;
-
- /* Socket is open -- set VTY Term listener going */
- uty_serv_start_listener(sock, VTY_SHELL_SERV) ;
-
- return 0 ;
+ return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
- * Socket is open -- set a VTY listener going
+ * Command output push to a file or pipe.
*
- * Note that the vyt_listener structure is passed to the accept action function.
+ * Returns: CMD_SUCCESS -- done
+ * CMD_IO_ERROR -- ran into an I/O error
*/
-static void
-uty_serv_start_listener(int fd, enum vty_type type)
+extern cmd_return_code_t
+uty_file_out_push(vio_vf vf)
{
- vty_listener listener ;
-
- listener = XCALLOC(MTYPE_VTY, sizeof (struct vty_listener));
+ assert(vf->vout_state == vf_open) ;
- ssl_push(vty_listeners_list, listener, next) ;
- uty_sock_init_new(&listener->sock, fd, listener) ;
-
- listener->type = type ;
-
- if (vty_cli_nexus)
- listener->sock.action.read.qnexus = vty_accept_qnexus ;
+ if (vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), false) >= 0)
+ return CMD_SUCCESS ;
else
- listener->sock.action.read.thread = vty_accept_thread ;
-
- uty_sock_set_read(&listener->sock, on) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Accept action for the thread world -- create and dispatch VTY
- */
-static int
-vty_accept_thread(struct thread *thread)
-{
- vty_listener listener = THREAD_ARG(thread) ;
- int result ;
-
- VTY_LOCK() ;
-
- result = uty_accept(listener, THREAD_FD(thread));
-
- uty_sock_set_read(&listener->sock, on) ;
-
- VTY_UNLOCK() ;
- return result ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Accept action for the qnexus world -- create and dispatch VTY
- */
-static void
-vty_accept_qnexus(qps_file qf, void* listener)
-{
- VTY_LOCK() ;
-
- uty_accept(listener, qf->fd);
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM or VTY_SHELL_SERV
- */
-static int
-uty_accept(vty_listener listener, int listen_sock)
-{
- VTY_ASSERT_LOCKED() ;
-
- assert(listener->sock.fd == listen_sock) ;
-
- switch (listener->type)
- {
- case VTY_TERM:
- return uty_accept_term(listener) ;
-
- case VTY_SHELL_SERV:
- return uty_accept_shell_serv(listener) ;
-
- default:
- zabort("unknown vty type") ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM
- */
-static int
-uty_accept_term(vty_listener listener)
-{
- int sock_fd;
- union sockunion su;
- int ret;
- unsigned int on;
- struct prefix *p ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* We can handle IPv4 or IPv6 socket. */
- sockunion_init_new(&su, AF_UNSPEC) ;
-
- sock_fd = sockunion_accept (listener->sock.fd, &su);
-
- if (sock_fd < 0)
- {
- if (sock_fd == -1)
- uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
- errtoa(errno, 0).str) ;
- return -1;
- }
-
- /* Really MUST have non-blocking */
- ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
- if (ret < 0)
- {
- close(sock_fd) ;
- return -1 ;
- } ;
-
- /* New socket is open... worry about access lists */
- p = sockunion2hostprefix (&su);
- ret = 0 ; /* so far, so good */
-
- if ((p->family == AF_INET) && vty_accesslist_name)
- {
- /* VTY's accesslist apply. */
- struct access_list* acl ;
-
- if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- ret = -1 ;
- }
-
-#ifdef HAVE_IPV6
- if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
- {
- /* VTY's ipv6 accesslist apply. */
- struct access_list* acl ;
-
- if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- ret = -1 ;
- }
-#endif /* HAVE_IPV6 */
-
- prefix_free (p);
-
- if (ret != 0)
- {
- uzlog (NULL, LOG_INFO, "Vty connection refused from %s", sutoa(&su).str) ;
- close (sock_fd);
- return 0;
- } ;
-
- /* Final options (optional) */
- on = 1 ;
- ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_NODELAY,
- (void*)&on, sizeof (on));
- if (ret < 0)
- uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
- sock_fd, errtoa(errno, 0).str) ;
-
- /* All set -- create the VTY_TERM */
- uty_new_term(sock_fd, &su);
-
- /* Log new VTY */
- uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
- sock_fd) ;
-
- return 0;
-}
-
-/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_SHELL_SERV
- */
-static int
-uty_accept_shell_serv (vty_listener listener)
-{
- int sock_fd ;
- int ret ;
- int client_len ;
- struct sockaddr_un client ;
-
- VTY_ASSERT_LOCKED() ;
-
- client_len = sizeof(client);
- memset (&client, 0, client_len);
-
- sock_fd = accept(listener->sock.fd, (struct sockaddr *) &client,
- (socklen_t *) &client_len) ;
-
- if (sock_fd < 0)
- {
- uzlog (NULL, LOG_WARNING, "can't accept vty shell socket : %s",
- errtoa(errno, 0).str) ;
- return -1;
- }
-
- /* Really MUST have non-blocking */
- ret = set_nonblocking(sock_fd) ; /* issues WARNING if fails */
- if (ret < 0)
- {
- close(sock_fd) ;
- return -1 ;
- } ;
-
- /* All set -- create the VTY_SHELL_SERV */
- if (VTYSH_DEBUG)
- printf ("VTY shell accept\n");
-
- uty_new_shell_serv(sock_fd) ;
-
- /* Log new VTY */
- uzlog (NULL, LOG_INFO, "Vty shell connection (fd %d)", sock_fd);
- return 0;
-}
-
-/*==============================================================================
- * Reading from the VTY_SHELL_SERV type sock.
- *
- * The select/pselect call-back ends up in utysh_read_ready().
- */
-
-/*------------------------------------------------------------------------------
- * Ready to read -> kicking the "SHELL_SERV CLI"
- *
- * End up here when there is something ready to be read.
- *
- * 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.
- *
- * Note that nothing is actually read here -- reading is done in the CLI itself,
- * if required.
- *
- * The CLI decides whether to re-enable read, or enable write, or both.
- */
-static void
-utysh_read_ready(vty_io vio)
-{
- 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
- * following four bytes: '\0' '\0' '\0' <ret>
- * Where <ret> is the command return code.
- */
+ return CMD_IO_ERROR ;
} ;
/*------------------------------------------------------------------------------
- * Callback -- qnexus: ready to read -> kicking the "SHELL_SERV CLI"
- */
-static void
-vtysh_read_qnexus(qps_file qf, void* file_info)
-{
- vty_io vio = file_info;
-
- VTY_LOCK() ;
-
- assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
-
- utysh_read_ready(vio) ;
-
- VTY_UNLOCK() ;
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- threads: ready to read -> kicking the "SHELL_SERV CLI"
- */
-static int
-vtysh_read_thread(struct thread *thread)
-{
- vty_io vio = THREAD_ARG (thread);
-
- VTY_LOCK() ;
-
- assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
-
- vio->sock.t_read = NULL ; /* implicitly */
- utysh_read_ready(vio);
-
- VTY_UNLOCK() ;
- return 0 ;
-}
-
-/*------------------------------------------------------------------------------
- * Read a lump of bytes and shovel into the command line buffer
- *
- * Lines coming in are terminated by '\0'.
- *
- * Assumes that the incoming command line is empty or otherwise incomplete.
- *
- * 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 sock.
- *
- * Returns: 0 => command line is incomplete
- * 1 => have a complete command line
- * -1 => EOF (or not open, or failed)
- */
-extern int
-utysh_read (vty_io vio, qstring cl, qstring buf)
-{
- int get ;
- char* cp ;
- char* ep ;
- size_t have ;
-
- while (1)
- {
- /* process what there is in the buffer */
- if (buf->len > buf->cp)
- {
- cp = qs_cp_char(buf) ;
- ep = qs_ep_char(buf) ;
- have = ep - cp ;
-
- ep = memchr(cp, '\0', have) ;
- if (ep != NULL)
- have = ep - cp ; /* have upto, but excluding '\0' */
-
- if (have > 0) /* take what have */
- {
- qs_insert(cl, cp, have) ;
- cl->cp += have ;
- buf->cp += have ;
- } ;
-
- if (ep != NULL) /* if found '\0' */
- {
- qs_term(cl) ; /* '\0' terminate */
- ++buf->cp ; /* step past it */
- return 1 ; /* have a complete line <<<<<<<<<<<<< */
- }
- } ;
-
- /* buffer is empty -- try and get some more stuff */
- assert(buf->len == buf->cp) ;
-
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open <<<<<<<<<<<<< */
-
- qs_need(buf, 500) ; /* need a reasonable lump */
- qs_clear(buf) ; /* set cp = len = 0 */
-
- get = read_nb(vio->sock.fd, buf->body, buf->size) ;
- if (get > 0)
- buf->len = get ;
- else if (get == 0)
- return 0 ; /* have an incomplete line <<<<<<<<<<<< */
- else
- {
- if (get == -1)
- uty_sock_error(vio, "read") ;
-
- vio->sock.read_open = 0 ;
-
- return -1 ; /* at EOF or failed <<<<<<<<<<<<< */
- } ;
- } ;
-} ;
-
-/*==============================================================================
- * Output to vty which are set to "monitor".
- *
- * 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.
- *
- * 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.
- */
-
-/*------------------------------------------------------------------------------
- * Output logging information to all vty which are set to "monitor".
+ * Tidy up after input file has been closed
*/
extern void
-uty_log(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va)
+uty_file_read_close(vio_vf vf)
{
- vty_io vio ;
-
- VTY_ASSERT_LOCKED() ;
-
- vio = sdl_head(vio_monitors_base) ;
-
- if (vio == NULL)
- return ; /* go no further if no "monitor" vtys */
-
- /* Prepare line for output. */
- uvzlog_line(ll, zl, priority, format, va, llt_crlf) ; /* with crlf */
-
- /* write to all known "monitor" vty
- *
- */
- while (vio != NULL)
- {
- if (!vio->monitor_busy)
- {
- int ret ;
-
- vio->monitor_busy = 1 ; /* close the door */
-
- uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
-
- ret = uty_write_monitor(vio) ;
- if (ret == 0)
- {
- 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) ;
- } ;
- } ;
-
- if (ret != 0)
- /* need to prod */ ;
-
- if (ret >= 0)
- vio->monitor_busy = 0 ;
- } ;
-
- vio = sdl_next(vio, mon_list) ;
- } ;
+ return ;
} ;
/*------------------------------------------------------------------------------
- * Async-signal-safe version of vty_log for fixed strings.
+ * Flush output buffer and close.
*
- * This is last gasp operation.
+ * Returns: true <=> buffer (now) empty
*/
-void
-vty_log_fixed (const char *buf, size_t len)
+extern bool
+uty_file_write_close(vio_vf vf, bool final)
{
- vty_io vio ;
-
- /* Write to all known "monitor" vty
- *
- * Forget all the niceties -- about to die in any case.
- */
- vio = sdl_head(vio_monitors_base) ;
- while (vio != NULL)
- {
- write(vio->sock.fd, buf, len) ;
- write(vio->sock.fd, "\r\n", 2) ;
-
- vio = sdl_next(vio, mon_list) ;
- } ;
+ return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), true) == 0 ;
} ;
diff --git a/lib/vty_io_file.h b/lib/vty_io_file.h
index b4a79f52..ab852e08 100644
--- a/lib/vty_io_file.h
+++ b/lib/vty_io_file.h
@@ -1,8 +1,6 @@
-/* VTY IO FILE -- File I/O -- header
- * Virtual terminal [aka TeletYpe] interface routine.
- * Copyright (C) 1997, 98 Kunihiro Ishiguro
+/* VTY I/O for Files -- Header
*
- * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman
+ * Copyright (C) 2010 Chris Hall (GMCH), Highwayman
*
* This file is part of GNU Zebra.
*
@@ -26,17 +24,8 @@
#define _ZEBRA_VTY_IO_FILE_H
#include "misc.h"
-#include <errno.h>
-#include "uty.h"
-#include "vty.h"
#include "vty_io.h"
-#include "vio_fifo.h"
-#include "vio_lines.h"
-#include "keystroke.h"
-#include "thread.h"
-#include "command.h"
-#include "qstring.h"
/*==============================================================================
* Here are structures and other definitions which are shared by:
@@ -50,7 +39,15 @@
* Functions
*/
-extern int uty_vprintf_file(vty_io vio, const char *format, va_list args) ;
+extern cmd_return_code_t uty_file_read_open(vty_io vio, qstring name,
+ bool reflect) ;
+extern cmd_return_code_t uty_file_write_open(vty_io vio, qstring name,
+ bool append) ;
+extern cmd_return_code_t uty_file_fetch_command_line(vio_vf vf, qstring* line) ;
+extern cmd_return_code_t uty_file_out_push(vio_vf vf) ;
+
+extern void uty_file_read_close(vio_vf vf) ;
+extern bool uty_file_write_close(vio_vf vf, bool final) ;
#endif /* _ZEBRA_VTY_IO_FILE_H */
diff --git a/lib/vty_io_shell.c b/lib/vty_io_shell.c
index c56cc2d2..7b24b871 100644
--- a/lib/vty_io_shell.c
+++ b/lib/vty_io_shell.c
@@ -43,6 +43,7 @@
#define VTYSH_DEBUG 0
+#if 0
/*------------------------------------------------------------------------------
* Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
@@ -62,7 +63,7 @@ uty_new_shell_serv(int sock_fd)
vio = vty->vio ;
/* Set the action functions */
- if (vty_cli_nexus)
+ if (vty_nexus)
{
vio->sock.action.read.qnexus = vtysh_read_qnexus ;
vio->sock.action.write.qnexus = vty_write_qnexus ;
@@ -364,3 +365,5 @@ utysh_read (vty_io vio, qstring cl, qstring buf)
} ;
} ;
} ;
+
+#endif
diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c
index 2053dcc5..0f8e834d 100644
--- a/lib/vty_io_term.c
+++ b/lib/vty_io_term.c
@@ -20,11 +20,15 @@
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
+#include "zconfig.h"
+#include "misc.h"
-#include "zebra.h"
-
+#include "vty_local.h"
+#include "vty_io.h"
#include "vty_io_term.h"
#include "vty_cli.h"
+#include "vty_command.h"
+#include "vio_fifo.h"
#include "qstring.h"
#include "keystroke.h"
@@ -38,149 +42,159 @@
#include "network.h"
#include <arpa/telnet.h>
-#include <sys/un.h> /* for VTYSH */
#include <sys/socket.h>
+#include <errno.h>
-#define VTYSH_DEBUG 0
-
-/*------------------------------------------------------------------------------
- * Create new vty of type VTY_TERMINAL -- ie attached to a telnet session.
+/*==============================================================================
+ * The I/O side of Telnet VTY_TERMINAL. The CLI side is vty_cli.c.
+ *
+ * A VTY_TERMINAL comes into being when a telnet connection is accepted, and
+ * is closed either on command, or on timeout, or when the daemon is reset
+ * or terminated.
+ *
+ *
+ *
*
- * This is called by the accept action for the VTY_TERMINAL listener.
*/
-static void
-uty_term_accept_new(int sock_fd, union sockunion *su)
-{
- struct vty *vty ;
- vty_io vio ;
- enum vty_readiness ready ;
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
- /* Allocate new vty structure and set up default values. */
- vty = uty_new(VTY_TERMINAL, sock_fd) ;
- vio = vty->vio ;
- /* The text form of the address identifies the VTY */
- vty->vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
- /* Set the initial node */
- if (no_password_check)
- {
- if (restricted_mode)
- vty->node = RESTRICTED_NODE;
- else if (host.advanced)
- vty->node = ENABLE_NODE;
- else
- vty->node = VIEW_NODE;
- }
- else
- vty->node = AUTH_NODE;
- /* Pick up current timeout setting */
- vio->sock.v_timeout = vty_timeout_val;
- /* Use global 'lines' setting, as default. May be -1 => unset */
- vio->lines = host.lines ;
-
- /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
- vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
- uty_set_height(vio) ; /* set initial state */
-
- /* Initialise the CLI, ready for start-up messages etc. */
- uty_cli_init(vio) ;
+/*==============================================================================
+ * If possible, will use getaddrinfo() to find all the things to listen on.
+ */
+#if defined(HAVE_IPV6) && !defined(NRL)
+# define VTY_USE_ADDRINFO 1
+#else
+# define VTY_USE_ADDRINFO 0
+#endif
- /* Reject connection if password isn't set, and not "no password" */
- if ((host.password == NULL) && (host.password_encrypt == NULL)
- && ! no_password_check)
- {
- uty_close(vio, "Vty password is not set.");
- vty = NULL;
- }
- else
- {
- /* Say hello to the world. */
- vty_hello (vty);
+/*==============================================================================
+ * Opening and closing VTY_TERMINAL type
+ */
- if (! no_password_check)
- uty_output (vty, "\nUser Access Verification\n\n");
- } ;
+static void uty_term_ready(vio_vfd vfd, void* action_info) ;
+static vty_timer_time uty_term_read_timeout(vio_timer_t* timer,
+ void* action_info) ;
+static vty_timer_time uty_term_write_timeout(vio_timer_t* timer,
+ void* action_info) ;
+static vty_readiness_t uty_term_write(vio_vf vf) ;
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
-} ;
+static void uty_term_will_echo(vty_cli cli) ;
+static void uty_term_will_suppress_go_ahead(vty_cli cli) ;
+static void uty_term_dont_linemode(vty_cli cli) ;
+static void uty_term_do_window_size(vty_cli cli) ;
+static void uty_term_dont_lflow_ahead(vty_cli cli) ;
/*------------------------------------------------------------------------------
- * Construct vio_vf structure for new VTY_TERMINAL type VTY, and add same.
- *
+ * Create new vty of type VTY_TERMINAL -- ie attached to a telnet session.
*
+ * This is called by the accept action for the VTY_TERMINAL listener.
*/
-
static void
-uty_term_new(vty_io vio, int sock_fd)
+uty_term_open(int sock_fd, union sockunion *su)
{
- vio_vf vf ;
-
- enum vty_readiness ready ;
+ vty vty ;
+ vty_io vio ;
+ vio_vf vf ;
VTY_ASSERT_LOCKED() ;
VTY_ASSERT_CLI_THREAD() ;
- /* May only be the first vio_vf ! */
- assert((vio->vin_base == NULL) && (vio->vout_base == NULL)) ;
-
- /* Construct and add to the vio */
- vf = uty_vf_new(vio, sock_fd, vfd_socket, vfd_io_read_write) ;
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new(VTY_TERMINAL) ;
+ vio = vty->vio ;
- uty_vin_add(vio, vf, VIN_TERM, uty_term_ready, uty_term_read_timeout) ;
- uty_vout_add(vio, vf, VIN_TERM, uty_term_ready, uty_term_write_timeout) ;
+ /* The initial vty->node depends on a number of vty type things, so
+ * we set that now.
+ *
+ * This completes the initialisation of the vty object, except that the
+ * execution and vio objects are largely empty.
+ */
+ if (host.no_password_check)
+ {
+ if (host.restricted_mode)
+ vio->vty->node = RESTRICTED_NODE;
+ else if (host.advanced)
+ vio->vty->node = ENABLE_NODE;
+ else
+ vio->vty->node = VIEW_NODE;
+ }
+ else
+ vio->vty->node = AUTH_NODE;
- /* Allocate and initialise a keystroke stream TODO: CSI ?? */
- vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
+ /* Complete the initialisation of the vty_io object.
+ *
+ * Note that the defaults for:
+ *
+ * - read_timeout -- default = 0 => no timeout
+ * - write_timeout -- default = 0 => no timeout
+ *
+ * - parse_type -- default = cmd_parse_standard
+ * - reflect_enabled -- default = false
+ * - out_enabled -- default = true iff vfd_io_write
+ *
+ * Are OK, except that we want the read_timeout set to the current EXEC
+ * timeout value.
+ *
+ * The text form of the address identifies the VTY.
+ */
+ vf = uty_vf_new(vio, sutoa(su).str, sock_fd, vfd_socket, vfd_io_read_write) ;
- /* Pick up current timeout setting */
- vf->read_timeout = vty_timeout_val;
+ uty_vin_open( vio, vf, VIN_TERM, uty_term_ready,
+ uty_term_read_timeout,
+ 0) ; /* no ibuf required */
+ uty_vout_open(vio, vf, VOUT_TERM, uty_term_ready,
+ uty_term_write_timeout,
+ 4096) ; /* obuf is required */
- /* Use global 'lines' setting, as default. May be -1 => unset */
- vio->lines = host.lines ;
+ vf->read_timeout = host.vty_timeout_val ; /* current EXEC timeout */
- /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
- vf->olc = vio_lc_init_new(NULL, 0, 0) ;
- uty_set_height(vio) ; /* set initial state */
+ /* Set up the CLI object & initialise */
+ vf->cli = uty_cli_new(vf) ;
- /* Initialise the CLI, ready for start-up messages etc. */
- uty_cli_init(vio) ;
+ /* When we get here the VTY is set up and all ready to go. */
+ uty_cmd_prepare(vio) ;
- /* Reject connection if password isn't set, and not "no password" */
- if ((host.password == NULL) && (host.password_encrypt == NULL)
- && ! no_password_check)
- {
- uty_close(vio, "Vty password is not set.");
- vty = NULL;
- }
- else
+ /* Issue Telnet commands/escapes to be a good telnet citizen -- not much
+ * real negotiating going on -- just a statement of intentions !
+ */
+ uty_term_will_echo (vf->cli);
+ uty_term_will_suppress_go_ahead (vf->cli);
+ uty_term_dont_linemode (vf->cli);
+ uty_term_do_window_size (vf->cli);
+ if (0)
+ uty_term_dont_lflow_ahead (vf->cli) ;
+
+ /* Say hello */
+ vty_hello(vty);
+
+ /* If need password, issue prompt or give up if no password to check
+ * against !
+ */
+ if (vty->node == AUTH_NODE)
{
- /* Say hello to the world. */
- vty_hello (vty);
-
- if (! no_password_check)
- uty_output (vty, "\nUser Access Verification\n\n");
+ if (host.password != NULL)
+ vty_out(vty, "\nUser Access Verification\n\n");
+ else
+ uty_close(vio, false, qs_set(NULL, "vty password is not set."));
} ;
- /* Now start the CLI and set a suitable state of readiness */
- ready = uty_cli_start(vio) ;
- uty_sock_set_readiness(&vio->sock, ready) ;
+ /* Push the output to date and start the CLI */
+ uty_cmd_out_push(vio) ;
+ uty_cli_start(vf->cli, vty->node) ;
} ;
-
-
/*------------------------------------------------------------------------------
+ * Close the reading side of VTY_TERMINAL, and close down CLI as far as
+ * possible, given that output may be continuing.
*
+ * Expects to be called once only for the VTY_TERMINAL.
*/
extern void
-uty_term_half_close(vio_vf vf)
+uty_term_read_close(vio_vf vf)
{
vty_io vio ;
@@ -193,93 +207,60 @@ uty_term_half_close(vio_vf vf)
* Note that half closing the file sets a new timeout, sets read off
* and write on.
*/
-
-
-
uty_set_monitor(vio, 0) ;
- /* Discard everything in the keystroke stream and force it to EOF */
- if (vio->key_stream != NULL)
- keystroke_stream_set_eof(vio->key_stream) ;
-
- /* Turn off "--more--" so that all output clears without interruption.
- *
- * If is sitting on a "--more--" prompt, then exit the wait_more CLI.
- */
- vio->cli_more_enabled = 0 ;
-
- if (vio->cli_more_wait)
- uty_cli_exit_more_wait(vio) ;
-
- /* If a command is not in progress, enable output, which will clear
- * the output buffer if there is anything there, plus any close reason,
- * and then close.
- *
- * If command is in progress, then this process will start when it
- * completes.
- */
- if (!vio->cmd_in_progress)
- vio->cmd_out_enabled = 1 ;
+ uty_cli_close(vf->cli, false) ;
/* Log closing of VTY_TERM */
assert(vio->vty->type == VTY_TERMINAL) ;
- uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", uty_vf_fd(vf)) ;
+ uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio_vfd_fd(vf->vfd)) ;
} ;
/*------------------------------------------------------------------------------
- * vprintf to VTY_TERM
- *
- * All output goes to output fifo until command completes.
+ * Close the writing side of VTY_TERMINAL.
*
- * NB: MUST be cmd_in_progress
+ * Pushes any buffered stuff to output and
*
- * Discards output if the socket is not open for whatever reason.
*/
-extern int
-uty_term_vprintf(vio_vf vf, const char *format, va_list args)
+extern bool
+uty_term_write_close(vio_vf vf, bool final)
{
- VTY_ASSERT_LOCKED() ;
-
- if (!vf->write_open)
- return 0 ; /* discard output if not open ! */
+ vty_io vio ;
+ vty_readiness_t ready ;
- assert(vf->vio->cmd_in_progress) ;
+ /* Get the vio and ensure that we are all straight */
+ vio = vf->vio ;
+ assert((vio->vin == vio->vin_base) && (vio->vin == vf)) ;
- return vio_fifo_vprintf(vf->obuf, format, args) ;
-} ;
+ /* Do the file side of things
+ *
+ * Note that half closing the file sets a new timeout, sets read off
+ * and write on.
+ */
+ uty_set_monitor(vio, 0) ;
-/*------------------------------------------------------------------------------
- * Set/Clear "monitor" state:
- *
- * set: if VTY_TERM and not already "monitor" (and write_open !)
- * clear: if is "monitor"
- */
-extern void
-uty_set_monitor(vty_io vio, bool on)
-{
- VTY_ASSERT_LOCKED() ;
+ vf->cli->out_active = true ; /* force the issue */
- if (on && !vio->monitor)
- {
- if ((vio->type == VTY_TERM) && vio->sock.write_open)
- {
- vio->monitor = 1 ;
- sdl_push(vio_monitors_base, vio, mon_list) ;
- } ;
- }
- else if (!on && vio->monitor)
+ do
{
- vio->monitor = 0 ;
- sdl_del(vio_monitors_base, vio, mon_list) ;
- }
+ vf->cli->out_done = false ;
+ ready = uty_term_write(vf) ;
+ } while ((ready != write_ready) && vf->cli->out_active) ;
+
+ final = final || !vf->cli->out_active ;
+
+ if (!final)
+ uty_term_set_readiness(vf, ready) ;
+
+ vf->cli = uty_cli_close(vf->cli, final) ;
+
+ return final ;
} ;
/*==============================================================================
* Action routines for VIN_TERM/VOUT_TERM type vin/vout objects
*/
-static enum vty_readiness uty_term_write(vio_vf vf) ;
-
/*==============================================================================
* Readiness and the VIN_TERM type vin.
*
@@ -289,7 +270,23 @@ static enum vty_readiness uty_term_write(vio_vf vf) ;
*
* The VIN_TERM uses read ready only when it doesn't set write ready. Does
* not set both at once.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set read/write readiness -- for VIN_TERM/VOUT_TERM
*
+ * Note that sets only one of read or write, and sets write for preference.
+ */
+extern void
+uty_term_set_readiness(vio_vf vf, vty_readiness_t ready)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_vf_set_read(vf, (ready == read_ready)) ;
+ uty_vf_set_write(vf, (ready >= write_ready)) ;
+} ;
+
+/*------------------------------------------------------------------------------
* So there is only one, common, uty_term_ready function, which:
*
* 1. attempts to clear any output it can.
@@ -326,39 +323,41 @@ static enum vty_readiness uty_term_write(vio_vf vf) ;
* Resets the timer because something happened.
*/
static void
-uty_term_ready(vio_fd vfd, void* action_info)
+uty_term_ready(vio_vfd vfd, void* action_info)
{
- enum vty_readiness ready ;
+ vty_readiness_t ready ;
vio_vf vf = action_info ;
- vty_io vio = vf->vio ;
- VTY_ASSERT_LOCKED() ;
+ assert(vfd == vf->vfd) ;
- vio->cmd_out_done = 0 ; /* not done any command output yet */
+ VTY_ASSERT_LOCKED() ;
uty_term_write(vf) ; /* try to clear outstanding stuff */
do
{
- ready = uty_cli(vio) ; /* do any CLI work... */
+ ready = uty_cli(vf->cli) ; /* do any CLI work... */
ready |= uty_term_write(vf) ; /* ...and any output that generates */
} while (ready >= now_ready) ;
- uty_file_set_readiness(vf, ready) ;
+ uty_term_set_readiness(vf, ready) ;
} ;
/*==============================================================================
- * Reading from VTY_TERM.
- *
- * The select/pselect call-back ends up in uty_read_ready().
+ * Reading from VTY_TERMINAL.
*
- * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
- * current CLI.
+ * The select/pselect call-back ends up in uty_term_ready().
*/
/*------------------------------------------------------------------------------
* Read a lump of bytes and shovel into the keystroke stream
*
+ * This function is called from the vty_cli to top up the keystroke buffer,
+ * or in the stealing of a keystroke to end "--more--" state.
+ *
+ * NB: need not be in the term_ready path. Indeed, when the VTY_TERMINAL is
+ * initialised, this is called to suck up any telnet preamble.
+ *
* Steal keystroke if required -- see keystroke_input()
*
* Returns: 0 => nothing available
@@ -366,25 +365,24 @@ uty_term_ready(vio_fd vfd, void* action_info)
* -1 => EOF (or not open, or failed)
*/
extern int
-uty_read (vty_io vio, keystroke steal)
+uty_term_read(vio_vf vf, keystroke steal)
{
unsigned char buf[500] ;
int get ;
- if (!vio->sock.read_open)
- return -1 ; /* at EOF if not open */
+ if (vf->vin_state != vf_open)
+ return -1 ; /* at EOF if not open */
- get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
+ get = read_nb(vio_vfd_fd(vf->vfd), buf, sizeof(buf)) ;
if (get >= 0)
- keystroke_input(vio->key_stream, buf, get, steal) ;
+ keystroke_input(vf->cli->key_stream, buf, get, steal) ;
else if (get < 0)
{
if (get == -1)
- uty_file_error(vio, "read") ;
-
- vio->sock.read_open = 0 ;
- keystroke_input(vio->key_stream, NULL, 0, steal) ;
+ uty_vf_error(vf, "read", errno) ;
+ keystroke_input(vf->cli->key_stream, NULL, 0, steal) ;
+ /* Tell keystroke stream that EOF met */
get = -1 ;
} ;
@@ -396,10 +394,10 @@ uty_read (vty_io vio, keystroke steal)
*
* There are two sets of buffering:
*
- * cli -- command line -- which reflects the status of the command line
+ * cli->cbuf -- command line -- reflects the status of the command line
*
- * cmd -- command output -- which is written to the file only while
- * cmd_out_enabled.
+ * vf->obuf -- command output -- which is written to the file only while
+ * out_active.
*
* The cli output takes precedence.
*
@@ -407,8 +405,8 @@ uty_read (vty_io vio, keystroke steal)
* "--more--" mechanism.
*/
-static int uty_write_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
-static int uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
+static int uty_write_lc(vio_vf vf, vio_fifo vff, vio_line_control lc) ;
+static int uty_write_fifo_lc(vio_vf vf, vio_fifo vff, vio_line_control lc) ;
/*------------------------------------------------------------------------------
* Write as much as possible of what there is.
@@ -426,83 +424,79 @@ static int uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc) ;
* now_ready if should loop back and try again
* not_ready otherwise
*/
-static enum vty_readiness
+static vty_readiness_t
uty_term_write(vio_vf vf)
{
- vty_io vio = vf->vio ;
+ vty_cli cli = vf->cli ;
int ret ;
VTY_ASSERT_LOCKED() ;
ret = -1 ;
- while (vf->write_open)
+ while (vf->vout_state == vf_open)
{
/* Any outstanding line control output takes precedence */
- if (vf->olc != NULL)
- {
- ret = uty_write_lc(vf, vf->obuf, vf->olc) ;
- if (ret != 0)
- break ;
- }
+ ret = uty_write_lc(vf, vf->obuf, cli->olc) ;
+ if (ret != 0)
+ break ;
/* Next: empty out the cli output */
- ret = vio_fifo_write_nb(&vf->cli_obuf, vio->sock.fd, true) ;
+ ret = vio_fifo_write_nb(cli->cbuf, vio_vfd_fd(vf->vfd), true) ;
if (ret != 0)
break ;
/* Finished now if not allowed to progress the command stuff */
- if (!vio->cmd_out_enabled)
+ if (!cli->out_active)
return not_ready ; /* done all can do */
- /* Last: if there is something in the command buffer, do that */
+ /* If there is something in the command buffer, do that */
if (!vio_fifo_empty(vf->obuf))
{
- if (vio->cmd_out_done)
+#if 0
+ if (cli->out_done)
break ; /* ...but not if done once */
- vio->cmd_out_done = 1 ; /* done this once */
-
- assert(!vio->cli_more_wait) ;
+ cli->out_done = true ; /* done this once */
+#endif
+ assert(!cli->more_wait) ;
- if (vio->cmd_lc != NULL)
- ret = uty_write_fifo_lc(vf, vf->obuf, vf->olc) ;
- else
- ret = vio_fifo_write_nb(vf->obuf, vio->sock.fd, true) ;
-
- /* If moved into "--more--" state@
- *
- * * the "--more--" prompt is ready to be written, so do that now
- *
- * * if that completes, then want to run the CLI *now* to perform the
- * first stage of the "--more--" process.
- */
- if (vio->cli_more_wait)
+ ret = uty_write_fifo_lc(vf, vf->obuf, cli->olc) ;
+ if (ret != 0)
{
- ret = vio_fifo_write_nb(vf->obuf, vio->sock.fd, true) ;
- if (ret == 0)
- return now_ready ;
- } ;
+ if (ret < 0)
+ break ; /* failed */
+
+ if (!cli->more_wait)
+ return write_ready ; /* done a tranche */
+
+ /* Moved into "--more--" state
+ *
+ * * the "--more--" prompt is ready to be written, so do that
+ * now
+ *
+ * * if that completes, then want to run the CLI *now* to
+ * perform the first stage of the "--more--" process.
+ */
+ ret = vio_fifo_write_nb(cli->cbuf, vio_vfd_fd(vf->vfd), true) ;
+ if (ret != 0)
+ break ;
- if (ret != 0)
- break ;
- }
+ return now_ready ;
+ } ;
+ } ;
/* Exciting stuff: there is nothing left to output...
*
* ... watch out for half closed state.
*/
- if (vio->half_closed)
+#if 0
+ if (vio->closing)
{
if (vio->close_reason != NULL)
{
- vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
-
- struct vty* vty = vio->vty ;
- if (vio->cli_drawn || vio->cli_dirty)
- vty_out(vty, VTY_NEWLINE) ;
- vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
-
- vio->cmd_in_progress = 0 ;
+ if (cli->drawn || cli->dirty)
+ uty_out(vio, "\n") ;
+ uty_out(vio, "%% %s\n", vio->close_reason) ;
vio->close_reason = NULL ; /* MUST discard now... */
continue ; /* ... and write away */
@@ -513,42 +507,39 @@ uty_term_write(vio_vf vf)
return not_ready ; /* it's all over */
} ;
+#endif
- /* For VTY_TERM: if the command line is not drawn, now is a good
- * time to do that.
- */
- if (vio->vty_type == VTY_TERM)
- if (uty_cli_draw_if_required(vio))
- continue ; /* do that now. */
+ if (uty_cli_draw_if_required(cli))
+ continue ; /* do that now. */
/* There really is nothing left to output */
+ cli->out_active = false ;
+
return not_ready ;
} ;
/* Arrives here if there is more to do, or failed (or was !write_open) */
- if (ret >= 0)
+ if (ret > 0)
return write_ready ;
+ if (ret == 0) /* just in case */
+ return not_ready ;
+
/* If is write_open, then report the error
*
* If still read_open, let the reader pick up and report the error, when it
* has finished anything it has buffered.
*/
- if (vf->write_open)
- {
- if (!vf->read_open)
- uty_file_error(vio, "write") ;
-
- vf->write_open = false ; /* crash close write */
- } ;
+ if (vf->vout_state == vf_open)
+ uty_vf_error(vf, "write", errno) ;
/* For whatever reason, is no longer write_open -- clear all buffers.
*/
- vio_fifo_clear(vf->obuf) ; /* throw away cli stuff */
- uty_out_clear(vio) ; /* throw away cmd stuff */
+ vio_fifo_clear(vf->obuf, true) ; /* throw away cli stuff */
+ uty_cli_out_clear(cli) ; /* throw away cmd stuff */
- vio->close_reason = NULL ; /* too late for this */
+ cli->out_active = false ;
return not_ready ; /* NB: NOT blocked by I/O */
} ;
@@ -575,6 +566,7 @@ uty_term_write(vio_vf vf)
* 0 => all gone
* < 0 => failed (or !write_open)
*/
+#if 0
static int
uty_write_monitor(vio_vf vf)
{
@@ -592,11 +584,12 @@ uty_write_monitor(vio_vf vf)
return ret ;
} ;
- return vio_fifo_write_nb(vf->obuf, uty_vf_fd(vf), true) ;
+ return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf), true) ;
} ;
+#endif
/*------------------------------------------------------------------------------
- * Write the given FIFO to output -- subject to possible line control.
+ * Write the given FIFO to output -- subject to line control.
*
* Note that even if no "--more--" is set, will have set some height, so
* that does not attempt to empty the FIFO completely all in one go.
@@ -606,16 +599,19 @@ uty_write_monitor(vio_vf vf)
*
* NB: expects that the sock is write_open
*
- * Returns: > 0 => blocked or completed one tranche
+ * Returns: > 0 => blocked or completed one tranche (more to go)
* 0 => all gone
* < 0 => failed
*/
static int
-uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
+uty_write_fifo_lc(vio_vf vf, vio_fifo vff, vio_line_control lc)
{
int ret ;
char* src ;
size_t have ;
+ vty_cli cli ;
+
+ cli = vf->cli ;
/* Collect another line_control height's worth of output.
*
@@ -624,24 +620,29 @@ uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
*/
vio_lc_set_pause(lc) ; /* clears lc->paused */
- src = vio_fifo_get_rdr(vfifo, &have) ;
+ vio_fifo_set_hold_mark(vff) ;
+ src = vio_fifo_get(vff, &have) ;
while ((src != NULL) && (!lc->paused))
{
size_t take ;
+
+ if (src == NULL)
+ break ;
+
take = vio_lc_append(lc, src, have) ;
- src = vio_fifo_step_rdr(vfifo, &have, take) ;
+ src = vio_fifo_step_get(vff, &have, take) ;
} ;
- vf->vio->cli_dirty = (lc->col != 0) ;
+ cli->dirty = (lc->col != 0) ;
/* Write the contents of the line control */
- ret = uty_write_lc(vf, vfifo, lc) ;
+ ret = uty_write_lc(vf, vff, lc) ;
if (ret < 0)
return ret ; /* give up now if failed. */
- if ((ret == 0) && vio_fifo_empty(vfifo))
+ if ((ret == 0) && vio_fifo_empty(vff))
return 0 ; /* FIFO and line control empty */
/* If should now do "--more--", now is the time to prepare for that.
@@ -652,8 +653,8 @@ uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
* The "--more--" cli will not do anything until the CLI buffer has
* cleared.
*/
- if (lc->paused && vf->vio->cli_more_enabled)
- uty_cli_enter_more_wait(vf->vio) ;
+ if (lc->paused && cli->more_enabled)
+ uty_cli_enter_more_wait(cli) ;
return 1 ; /* FIFO or line control, not empty */
} ;
@@ -670,18 +671,21 @@ uty_write_fifo_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
* < 0 => failed
*/
static int
-uty_write_lc(vio_vf vf, vio_fifo vfifo, vio_line_control lc)
+uty_write_lc(vio_vf vf, vio_fifo vff, vio_line_control lc)
{
int ret ;
- ret = vio_lc_write_nb(uty_vf_fd(vf), lc) ;
+ ret = vio_lc_write_nb(vio_vfd_fd(vf->vfd), lc) ;
if (ret <= 0)
- vio_fifo_sync_rdr(vfifo) ; /* finished with FIFO contents */
+ vio_fifo_clear_hold_mark(vff) ; /* finished with FIFO contents */
return ret ;
} ;
+
+#if 0
+
/*------------------------------------------------------------------------------
* Start command output -- clears down the line control.
*
@@ -693,137 +697,60 @@ uty_cmd_output_start(vio_vf vf)
{
if (vf->olc != NULL)
vio_lc_clear(vf->olc) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set the effective height for line control (if any)
- *
- * If using line_control, may enable the "--more--" output handling.
- *
- * If not, want some limit on the amount of stuff output at a time.
- *
- * Sets the line control window width and height.
- * Sets cli_more_enabled if "--more--" is enabled.
- */
-extern void
-uty_set_height(vio_vf vf)
-{
- vty_io vio = vf->vio ;
- bool on ;
-
- on = 0 ; /* default state */
- if ((vf->olc != NULL) && !vio->half_closed)
- {
- int height ;
-
- height = 0 ; /* default state */
+ vio_fifo_set_hold_mark(vf->obuf) ; /* mark to keep until all gone */
+} ;
- if ((vio->width) != 0)
- {
- /* If window size is known, use lines or given height */
- if (vio->lines >= 0)
- height = vio->lines ;
- else
- {
- /* Window height, leaving one line from previous "page"
- * and one line for the "--more--" -- if at all possible
- */
- height = vio->height - 2 ;
- if (height < 1)
- height = 1 ;
- } ;
- }
- else
- {
- /* If window size not known, use lines if that has been set
- * explicitly for this terminal.
- */
- if (vio->lines_set)
- height = vio->lines ;
- } ;
+#endif
- if (height > 0)
- on = 1 ; /* have a defined height */
- else
- height = 200 ; /* but no "--more--" */
- vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
- } ;
- vio->cli_more_enabled = on ;
-} ;
/*==============================================================================
- * Timer for VTY_TERM (and VTY_SHELL_SERV).
+ * Timer actions for VTY_TERMINAL
*/
/*------------------------------------------------------------------------------
- * Timer has expired.
+ * Read timer has expired.
+ *
+ * If closing, then this is curtains -- have waited long enough !
*
- * If half_closed, then this is curtains -- have waited long enough !
+ * TODO .... sort out the VTY_TERMINAL time-out & death-watch timeout
*
* Otherwise, half close the VTY and leave it to the death-watch to sweep up.
*/
-static void
-uty_timer_expired (vty_io vio)
+static vty_timer_time
+uty_term_read_timeout(vio_timer_t* timer, void* action_info)
{
+ vty_io vio = action_info ;
+
VTY_ASSERT_LOCKED() ;
- if (vio->half_closed)
- return uty_close(vio) ; /* curtains */
+ uty_close(vio, true, qs_set(NULL, "Timed out")) ;
- uty_close(vio, "Timed out") ; /* bring input side to a halt */
+ return 0 ;
} ;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
/*------------------------------------------------------------------------------
- * Set read/write readiness -- for VTY_TERM
+ * Write timer has expired.
+ *
+ * If closing, then this is curtains -- have waited long enough !
+ *
+ * TODO .... sort out the VTY_TERMINAL time-out & death-watch timeout
*
- * Note that for VTY_TERM, set only one of read or write, and sets write for
- * preference.
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
*/
-extern void
-uty_file_set_readiness(vio_vf vf, enum vty_readiness ready)
+static vty_timer_time
+uty_term_write_timeout(vio_timer_t* timer, void* action_info)
{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- vio_fd_set_read(vf, (ready == read_ready)) ;
- vio_fd_set_write(vf, (ready >= write_ready)) ;
-} ;
+ vty_io vio = action_info ;
-/*------------------------------------------------------------------------------
- * Set a new timer value.
- */
-extern void
-uty_file_set_timer(vio_vf vf, unsigned long timeout)
-{
VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- vf->v_timeout = timeout ;
-} ;
-
-
-
+ uty_close(vio, true, qs_set(NULL, "Timed out")) ;
+ return 0 ;
+} ;
/*==============================================================================
* VTY Listener(s) for VTY_TERMINAL
@@ -1055,7 +982,7 @@ uty_term_listen_open(sa_family_t family, int type, int protocol,
} ;
/*------------------------------------------------------------------------------
- * Accept action -- create and dispatch VTY_TERM
+ * Accept action -- create and dispatch VTY_TERMINAL
*/
static void
uty_term_accept(int sock_listen)
@@ -1093,24 +1020,24 @@ uty_term_accept(int sock_listen)
p = sockunion2hostprefix (&su);
ret = 0 ; /* so far, so good */
- if ((p->family == AF_INET) && vty_accesslist_name)
+ if ((p->family == AF_INET) && host.vty_accesslist_name)
{
/* VTY's accesslist apply. */
struct access_list* acl ;
- if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
+ if ((acl = access_list_lookup (AFI_IP, host.vty_accesslist_name))
+ && (access_list_apply (acl, p) == FILTER_DENY))
ret = -1 ;
}
#ifdef HAVE_IPV6
- if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
+ if ((p->family == AF_INET6) && host.vty_ipv6_accesslist_name)
{
/* VTY's ipv6 accesslist apply. */
struct access_list* acl ;
- if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
+ if ((acl = access_list_lookup (AFI_IP6, host.vty_ipv6_accesslist_name))
+ && (access_list_apply (acl, p) == FILTER_DENY))
ret = -1 ;
}
#endif /* HAVE_IPV6 */
@@ -1132,8 +1059,8 @@ uty_term_accept(int sock_listen)
uzlog (NULL, LOG_INFO, "can't set sockopt to socket %d: %s",
sock_fd, errtoa(errno, 0).str) ;
- /* All set -- create the VTY_TERM */
- uty_term_accept_new(sock_fd, &su);
+ /* All set -- create the VTY_TERMINAL and set it going */
+ uty_term_open(sock_fd, &su);
/* Log new VTY */
uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)", sutoa(&su).str,
@@ -1143,6 +1070,289 @@ uty_term_accept(int sock_listen)
} ;
/*==============================================================================
+ * VTY telnet stuff
+ *
+ * Note that all telnet commands (escapes) and any debug stuff is treated as
+ * CLI output.
+ */
+
+#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
+
+static const char* telnet_commands[256] =
+{
+ [tn_IAC ] = "IAC",
+ [tn_DONT ] = "DONT",
+ [tn_DO ] = "DO",
+ [tn_WONT ] = "WONT",
+ [tn_WILL ] = "WILL",
+ [tn_SB ] = "SB",
+ [tn_GA ] = "GA",
+ [tn_EL ] = "EL",
+ [tn_EC ] = "EC",
+ [tn_AYT ] = "AYT",
+ [tn_AO ] = "AO",
+ [tn_IP ] = "IP",
+ [tn_BREAK] = "BREAK",
+ [tn_DM ] = "DM",
+ [tn_NOP ] = "NOP",
+ [tn_SE ] = "SE",
+ [tn_EOR ] = "EOR",
+ [tn_ABORT] = "ABORT",
+ [tn_SUSP ] = "SUSP",
+ [tn_EOF ] = "EOF",
+} ;
+
+static const char* telnet_options[256] =
+{
+ [to_BINARY] = "BINARY", /* 8-bit data path */
+ [to_ECHO] = "ECHO", /* echo */
+ [to_RCP] = "RCP", /* prepare to reconnect */
+ [to_SGA] = "SGA", /* suppress go ahead */
+ [to_NAMS] = "NAMS", /* approximate message size */
+ [to_STATUS] = "STATUS", /* give status */
+ [to_TM] = "TM", /* timing mark */
+ [to_RCTE] = "RCTE", /* remote controlled tx and echo */
+ [to_NAOL] = "NAOL", /* neg. about output line width */
+ [to_NAOP] = "NAOP", /* neg. about output page size */
+ [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
+ [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
+ [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
+ [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
+ [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
+ [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
+ [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
+ [to_XASCII] = "XASCII", /* extended ascii character set */
+ [to_LOGOUT] = "LOGOUT", /* force logout */
+ [to_BM] = "BM", /* byte macro */
+ [to_DET] = "DET", /* data entry terminal */
+ [to_SUPDUP] = "SUPDUP", /* supdup protocol */
+ [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
+ [to_SNDLOC] = "SNDLOC", /* send location */
+ [to_TTYPE] = "TTYPE", /* terminal type */
+ [to_EOR] = "EOR", /* end or record */
+ [to_TUID] = "TUID", /* TACACS user identification */
+ [to_OUTMRK] = "OUTMRK", /* output marking */
+ [to_TTYLOC] = "TTYLOC", /* terminal location number */
+ [to_3270REGIME] = "3270REGIME", /* 3270 regime */
+ [to_X3PAD] = "X3PAD", /* X.3 PAD */
+ [to_NAWS] = "NAWS", /* window size */
+ [to_TSPEED] = "TSPEED", /* terminal speed */
+ [to_LFLOW] = "LFLOW", /* remote flow control */
+ [to_LINEMODE] = "LINEMODE", /* Linemode option */
+ [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
+ [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
+ [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
+ [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
+ [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
+ [to_EXOPL] = "EXOPL", /* extended-options-list */
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as decimal.
+ */
+static void
+uty_cli_out_dec(vty_cli cli, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(cli, "%s ", str) ;
+ else
+ uty_cli_out(cli, "%d ", (int)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as hex.
+ */
+static void
+uty_cli_out_hex(vty_cli cli, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(cli, "%s ", str) ;
+ else
+ uty_cli_out(cli, "0x%02x ", (unsigned)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "WILL TELOPT_ECHO"
+ */
+static void
+uty_term_will_echo (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "suppress Go-Ahead"
+ */
+static void
+uty_term_will_suppress_go_ahead (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use linemode"
+ */
+static void
+uty_term_dont_linemode (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "Use window size"
+ */
+static void
+uty_term_do_window_size (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use lflow" -- not currently used
+ */
+static void
+uty_term_dont_lflow_ahead (vty_cli cli)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (cli, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Process incoming Telnet Option(s)
+ *
+ * May be called during keystroke iac callback, or when processing CLI
+ * keystrokes.
+ *
+ * In particular: get telnet window size.
+ *
+ * Returns: true <=> dealt with, for:
+ *
+ * * telnet window size.
+ */
+extern bool
+uty_telnet_command(vio_vf vf, keystroke stroke, bool callback)
+{
+ uint8_t* p ;
+ uint8_t o ;
+ int left ;
+ bool dealt_with ;
+
+ vty_cli cli = vf->cli ;
+
+ /* Echo to the other end if required */
+ if (TELNET_OPTION_DEBUG)
+ {
+ uty_cli_wipe(cli, 0) ;
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ uty_cli_out_hex(cli, telnet_commands[tn_IAC], tn_IAC) ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(cli, telnet_commands[*p], *p) ;
+ ++p ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(cli, telnet_options[*p], *p) ;
+ ++p ;
+
+ if (left > 0)
+ {
+ while(left-- > 0)
+ uty_cli_out_hex(cli, NULL, *p++) ;
+
+ if (stroke->flags & kf_truncated)
+ uty_cli_out(cli, "... ") ;
+
+ if (!(stroke->flags & kf_broken))
+ {
+ uty_cli_out_hex(cli, telnet_commands[tn_IAC], tn_IAC) ;
+ uty_cli_out_hex(cli, telnet_commands[tn_SE], tn_SE) ;
+ }
+ } ;
+
+ if (stroke->flags & kf_broken)
+ uty_cli_out (cli, "BROKEN") ;
+
+ uty_cli_out_newline(cli) ;
+ } ;
+
+ /* Process the telnet command */
+ dealt_with = false ;
+
+ if (stroke->flags != 0)
+ return dealt_with ; /* go no further if broken */
+
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ passert(left >= 1) ; /* must be if not broken ! */
+ passert(stroke->value == *p) ; /* or something is wrong */
+
+ ++p ; /* step past X of IAC X */
+ --left ;
+
+ /* Decode the one command that is interesting -- "NAWS" */
+ switch (stroke->value)
+ {
+ case tn_SB:
+ passert(left > 0) ; /* or parser failed */
+
+ o = *p++ ; /* the option byte */
+ --left ;
+ switch(o)
+ {
+ case to_NAWS:
+ if (left != 4)
+ {
+ uzlog(NULL, LOG_WARNING,
+ "RFC 1073 violation detected: telnet NAWS option "
+ "should send %d characters, but we received %d",
+ (3 + 4 + 2), (3 + left + 2)) ;
+ }
+ else
+ {
+ int width, height ;
+
+ width = *p++ << 8 ; width += *p++ ;
+ height = *p++ << 8 ; height += *p ;
+
+ if (TELNET_OPTION_DEBUG)
+ {
+ uty_cli_out(cli, "TELNET NAWS window size received: "
+ "width %d, height %d", width, height) ;
+ uty_cli_out_newline(cli) ;
+ } ;
+
+ uty_cli_set_window(cli, width, height) ;
+
+ dealt_with = true ;
+ } ;
+ break ;
+
+ default: /* no other IAC SB <option> */
+ break ;
+ } ;
+ break ;
+
+ default: /* no other IAC X */
+ break ;
+ } ;
+
+ return dealt_with ;
+} ;
+
+/*==============================================================================
* Output to vty which are set to "monitor".
*
* This is VERY TRICKY.
@@ -1230,6 +1440,7 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
*/
while (vio != NULL)
{
+#if 0
if (!vio->monitor_busy)
{
int ret ;
@@ -1241,7 +1452,7 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
ret = uty_write_monitor(vio) ;
if (ret == 0)
{
- ret = write_nb(uty_vf_fd(vf), ll->line, ll->len) ;
+ ret = write_nb(vio_vfd_fd(vf->vfd), ll->line, ll->len) ;
if (ret >= 0)
{
@@ -1258,7 +1469,7 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
if (ret >= 0)
vio->monitor_busy = 0 ;
} ;
-
+#endif
vio = sdl_next(vio, mon_list) ;
} ;
} ;
@@ -1280,8 +1491,8 @@ vty_log_fixed (const char *buf, size_t len)
vio = sdl_head(vio_monitors_base) ;
while (vio != NULL)
{
- write(uty_vf_fd(vf), buf, len) ;
- write(uty_vf_fd(vf), "\r\n", 2) ;
+ write(vio_vfd_fd(vio->vout_base->vfd), buf, len) ;
+ write(vio_vfd_fd(vio->vout_base->vfd), "\r\n", 2) ;
vio = sdl_next(vio, mon_list) ;
} ;
diff --git a/lib/vty_io_term.h b/lib/vty_io_term.h
index ef975a71..f1823a92 100644
--- a/lib/vty_io_term.h
+++ b/lib/vty_io_term.h
@@ -28,16 +28,16 @@
#include "misc.h"
#include <errno.h>
-#include "uty.h"
#include "vty.h"
#include "vty_io_basic.h"
#include "vty_io.h"
+#include "vty_cli.h"
#include "vio_fifo.h"
#include "vio_lines.h"
#include "keystroke.h"
#include "thread.h"
-#include "command.h"
+#include "command_local.h"
#include "qstring.h"
/*==============================================================================
@@ -54,8 +54,15 @@
extern void uty_term_new(vty_io vio, int sock_fd) ;
-extern void uty_term_half_close(vio_vf vf) ;
-extern int uty_term_vprintf(vio_vf vf, const char *format, va_list args) ;
+extern void uty_term_read_close(vio_vf vf) ;
+extern bool uty_term_write_close(vio_vf vf, bool final) ;
+
+extern int uty_term_read(vio_vf vf, keystroke steal) ;
+extern void uty_term_set_readiness(vio_vf vf, vty_readiness_t ready) ;
+
+
+
+extern bool uty_telnet_command(vio_vf, keystroke stroke, bool callback) ;
extern void uty_term_open_listeners(const char *addr, unsigned short port) ;
diff --git a/lib/vty_local.h b/lib/vty_local.h
index 2767f9d3..97e5786c 100644
--- a/lib/vty_local.h
+++ b/lib/vty_local.h
@@ -89,33 +89,36 @@ extern qpn_nexus vty_cmd_nexus ;
extern qpt_mutex_t vty_mutex ;
-#ifdef NDEBUG
-# define VTY_DEBUG 0 /* NDEBUG override */
+#ifdef VTY_DEBUG /* Can be forced from outside */
+# if VTY_DEBUG
+# define VTY_DEBUG 1 /* Force 1 or 0 */
#else
-# ifndef VTY_DEBUG
-# define VTY_DEBUG 1 /* Set to 1 to turn on debug checks */
+# define VTY_DEBUG 0
+# endif
+#else
+# ifdef QDEBUG
+# define VTY_DEBUG 1 /* Follow QDEBUG */
+#else
+# define VTY_DEBUG 0
# endif
#endif
-#if VTY_DEBUG
+enum { vty_debug = VTY_DEBUG } ;
extern int vty_lock_count ;
-extern int vty_assert_fail ;
-
-#endif
Inline void
VTY_LOCK(void) /* if is qpthreads_enabled, lock vty_mutex */
{
qpt_mutex_lock(&vty_mutex) ;
- if (VTY_DEBUG)
+ if (vty_debug)
++vty_lock_count ;
} ;
Inline void
VTY_UNLOCK(void) /* if is qpthreads_enabled, unlock vty_mutex */
{
- if (VTY_DEBUG)
+ if (vty_debug)
--vty_lock_count ;
qpt_mutex_unlock(&vty_mutex) ;
} ;
@@ -137,6 +140,8 @@ vty_is_cli_thread(void)
*/
#if VTY_DEBUG
+extern int vty_assert_fail ;
+
Inline void
VTY_ASSERT_FAILED(void)
{
diff --git a/lib/vty_pipe.c b/lib/vty_pipe.c
index d4f1c9ba..3276085c 100644
--- a/lib/vty_pipe.c
+++ b/lib/vty_pipe.c
@@ -24,12 +24,11 @@
#include <zebra.h>
#include "vty.h"
-#include "uty.h"
#include "vty_cli.h"
+#include "vty_command.h"
#include "vty_io.h"
#include "vio_lines.h"
-#include "command.h"
#include "command_execute.h"
#include "command_queue.h"
@@ -194,7 +193,7 @@
*
*/
-
+#if 0
@@ -684,7 +683,7 @@ uty_cli_dispatch(vty_io vio)
cli_do = vio->cli_do ; /* current operation */
vio->cli_do = cli_do_nothing ; /* clear */
- qs_clear(&vio->cl) ; /* set cl empty (with '\0') */
+ qs_clear(vio->cl) ; /* set cl empty (with '\0') */
/* Reset the command output FIFO and line_control */
assert(vio_fifo_empty(&vio->cmd_obuf)) ;
@@ -706,7 +705,7 @@ uty_cli_dispatch(vty_io vio)
break ;
case cli_do_command:
- ret = uty_command(vty) ;
+ ret = uty_dispatch_command(vty) ;
break ;
case cli_do_ctrl_c:
@@ -1401,7 +1400,7 @@ uty_cli_draw_this(vty_io vio, enum node_type node)
prompt = "%s ???: " ;
} ;
- qs_printf(&vio->cli_prompt_for_node, prompt, vty_host_name);
+ qs_printf(vio->cli_prompt_for_node, prompt, vty_host_name);
vio->cli_prompt_node = node ;
vio->cli_prompt_set = 1 ;
@@ -1778,12 +1777,12 @@ uty_cli_insert (vty_io vio, const char* chars, int n)
if (n <= 0)
return n ; /* avoid trouble */
- after = qs_insert(&vio->cl, chars, n) ;
+ after = qs_insert(vio->cl, chars, n) ;
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after + n) ;
+ uty_cli_echo(vio, qs_cp_char(vio->cl), after) ;
- if (after != 0)
- uty_cli_echo_n(vio, telnet_backspaces, after) ;
+ if ((after - n) != 0)
+ uty_cli_echo_n(vio, telnet_backspaces, after - n) ;
vio->cl.cp += n ;
@@ -1806,7 +1805,7 @@ uty_cli_overwrite (vty_io vio, char* chars, int n)
if (n > 0)
{
- qs_replace(&vio->cl, chars, n) ;
+ qs_replace(vio->cl, n, chars, n) ;
uty_cli_echo(vio, chars, n) ;
vio->cl.cp += n ;
@@ -1854,7 +1853,7 @@ uty_cli_forwards(vty_io vio, int n)
if (n > 0)
{
- uty_cli_echo(vio, qs_cp_char(&vio->cl), n) ;
+ uty_cli_echo(vio, qs_cp_char(vio->cl), n) ;
vio->cl.cp += n ;
} ;
@@ -1907,10 +1906,10 @@ uty_cli_del_forwards(vty_io vio, int n)
if (n <= 0)
return 0 ;
- after = qs_delete(&vio->cl, n) ;
+ after = qs_delete(vio->cl, n) ;
if (after > 0)
- uty_cli_echo(vio, qs_cp_char(&vio->cl), after) ;
+ uty_cli_echo(vio, qs_cp_char(vio->cl), after) ;
uty_cli_echo_n(vio, telnet_spaces, n) ;
uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
@@ -1969,8 +1968,8 @@ uty_cli_word_forwards_delta(vty_io vio)
assert(vio->cl.cp <= vio->cl.len) ;
- cp = qs_cp_char(&vio->cl) ;
- ep = qs_ep_char(&vio->cl) ;
+ cp = qs_cp_char(vio->cl) ;
+ ep = qs_ep_char(vio->cl) ;
tp = cp ;
@@ -2013,8 +2012,8 @@ uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
assert(vio->cl.cp <= vio->cl.len) ;
- cp = qs_cp_char(&vio->cl) ;
- sp = qs_chars(&vio->cl) ;
+ cp = qs_cp_char(vio->cl) ;
+ sp = qs_chars(vio->cl) ;
tp = cp ;
@@ -2128,7 +2127,7 @@ uty_cli_transpose_chars(vty_io vio)
uty_cli_backwards(vio, 1) ;
/* Pick up in the new order */
- cp = qs_cp_char(&vio->cl) ;
+ cp = qs_cp_char(vio->cl) ;
chars[1] = *cp++ ;
chars[0] = *cp ;
@@ -2176,13 +2175,13 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
* Either way, replace the the previous line entry by moving hindex
* back !
*/
- if ((prev_line == NULL) || (qs_cmp_sig(prev_line, &line) == 0))
+ if ((prev_line == NULL) || (qs_cmp_sig(prev_line, line) == 0))
vio->hindex = prev_index ;
else
prev_line = vector_get_item(vio->hist, vio->hindex) ;
/* Now replace the hindex entry */
- vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
+ vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, line)) ;
/* Advance to the near future and reset the history pointer */
vio->hindex++;
@@ -2225,7 +2224,7 @@ uty_cli_history_use(vty_io vio, int step)
* current command line -- so can get back to it.
*/
hist = vector_get_item(&vio->hist, vio->hindex) ;
- vector_set_item(vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
+ vector_set_item(vio->hist, vio->hindex, qs_copy(hist, vio->cl)) ;
} ;
/* Advance or retreat */
@@ -2255,7 +2254,7 @@ uty_cli_history_use(vty_io vio, int step)
/* Get previous line from history buffer and echo that */
old_len = vio->cl.len ;
- qs_copy(&vio->cl, hist) ;
+ qs_copy(vio->cl, hist) ;
/* Sort out wiping out any excess and setting the cursor position */
if (old_len > vio->cl.len)
@@ -2448,7 +2447,7 @@ static void
uty_cli_describe_show(vty_io vio, vector describe)
{
unsigned int i, cmd_width, desc_width;
- struct desc *desc, *desc_cr ;
+ struct desc *desc, *cr_item ;
/* Get width of the longest "word" */
cmd_width = 0;
@@ -2472,7 +2471,7 @@ uty_cli_describe_show(vty_io vio, vector describe)
desc_width = vio->width - (cmd_width + 6);
/* Print out description. */
- desc_cr = NULL ; /* put <cr> last if it appears */
+ cr_item = NULL ; /* put <cr> last if it appears */
for (i = 0; i < vector_active (describe); i++)
if ((desc = vector_slot (describe, i)) != NULL)
@@ -2480,17 +2479,17 @@ uty_cli_describe_show(vty_io vio, vector describe)
if (desc->cmd[0] == '\0')
continue;
- if (strcmp (desc->cmd, command_cr) == 0)
+ if (strcmp (desc->cmd, cr_string) == 0)
{
- desc_cr = desc;
+ cr_item = desc;
continue;
}
uty_cli_describe_fold (vio, cmd_width, desc_width, desc);
}
- if (desc_cr != NULL)
- uty_cli_describe_fold (vio, cmd_width, desc_width, desc_cr);
+ if (cr_item != NULL)
+ uty_cli_describe_fold (vio, cmd_width, desc_width, cr_item);
} ;
/*------------------------------------------------------------------------------
@@ -2865,3 +2864,5 @@ uty_telnet_command(vty_io vio, keystroke stroke, bool callback)
return dealt_with ;
} ;
+
+#endif
diff --git a/lib/workqueue.h b/lib/workqueue.h
index 34e68cd8..d9f32a04 100644
--- a/lib/workqueue.h
+++ b/lib/workqueue.h
@@ -161,7 +161,7 @@ extern void work_queue_unplug (struct work_queue *wq);
/* Helpers, exported for thread.c and command.c */
extern int work_queue_run (struct thread *);
-extern struct cmd_element show_work_queues_cmd;
+extern struct cmd_command show_work_queues_cmd;
/*==============================================================================
* The Inline functions
diff --git a/lib/zebra.h b/lib/zebra.h
index 799cfc3d..52e5d571 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -21,9 +21,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _ZEBRA_H
#define _ZEBRA_H
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
+#include "zconfig.h"
#ifdef SUNOS_5
#define _XPG4_2