summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am6
-rw-r--r--lib/command.c1860
-rw-r--r--lib/command.h184
-rw-r--r--lib/command_queue.c100
-rw-r--r--lib/command_queue.h5
-rw-r--r--lib/keystroke.c393
-rw-r--r--lib/keystroke.h13
-rw-r--r--lib/list_util.h10
-rw-r--r--lib/log.c167
-rw-r--r--lib/log.h26
-rw-r--r--lib/mem_tracker.c3
-rw-r--r--lib/memtypes.c10
-rw-r--r--lib/mqueue.c16
-rw-r--r--lib/qfstring.c1066
-rw-r--r--lib/qfstring.h158
-rw-r--r--lib/qiovec.c261
-rw-r--r--lib/qiovec.h99
-rw-r--r--lib/qpnexus.c37
-rw-r--r--lib/qpnexus.h5
-rw-r--r--lib/qpselect.c29
-rw-r--r--lib/qpselect.h2
-rw-r--r--lib/qpthreads.h25
-rw-r--r--lib/qstring.c434
-rw-r--r--lib/qstring.h247
-rw-r--r--lib/qtimers.c180
-rw-r--r--lib/qtimers.h108
-rw-r--r--lib/sockopt.c317
-rw-r--r--lib/sockopt.h17
-rw-r--r--lib/sockunion.c314
-rw-r--r--lib/sockunion.h23
-rw-r--r--lib/uty.h154
-rw-r--r--lib/vector.c34
-rw-r--r--lib/vector.h60
-rw-r--r--lib/vio_fifo.c761
-rw-r--r--lib/vio_fifo.h86
-rw-r--r--lib/vio_lines.c380
-rw-r--r--lib/vio_lines.h91
-rw-r--r--lib/vty.c816
-rw-r--r--lib/vty.c.x4414
-rw-r--r--lib/vty.h127
-rw-r--r--lib/vty_cli.c1187
-rw-r--r--lib/vty_cli.h22
-rw-r--r--lib/vty_io.c1785
-rw-r--r--lib/vty_io.h203
-rw-r--r--lib/zassert.h5
-rw-r--r--lib/zclient.c76
46 files changed, 8495 insertions, 7821 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 17940f8d..9022c245 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -15,7 +15,8 @@ libzebra_la_SOURCES = \
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_queue.c qlib_init.c pthread_safe.c list_util.c \
- vty_io.c vty_cli.c keystroke.c qstring.c vio_fifo.c
+ vty_io.c vty_cli.c keystroke.c qstring.c vio_fifo.c vio_lines.c \
+ qiovec.c qfstring.c
BUILT_SOURCES = memtypes.h route_types.h
@@ -34,7 +35,8 @@ pkginclude_HEADERS = \
qtime.h qpthreads.h mqueue.h qpselect.h qtimers.h qpnexus.h \
command_queue.h qlib_init.h qafi_safi.h \
confirm.h miyagi.h pthread_safe.h list_util.h node_type.h uty.h \
- vty_io.h vty_cli.h keystroke.h qstring.h vio_fifo.h
+ vty_io.h vty_cli.h keystroke.h qstring.h vio_fifo.h vio_lines.h \
+ qiovec.h qfstring.h
EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.awk route_types.txt
diff --git a/lib/command.c b/lib/command.c
index 251c8963..6e1726dc 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -33,6 +33,7 @@ Boston, MA 02111-1307, USA. */
#include "uty.h"
#include "qstring.h"
#include "command.h"
+//#include "lib/route_types.h"
#include "workqueue.h"
#include "command_queue.h"
@@ -172,7 +173,7 @@ print_version (const char *progname)
/* Utility function to concatenate argv argument into a single string
with inserting ' ' character between each argument. */
char *
-argv_concat (const char **argv, int argc, int shift)
+argv_concat (const char* const* argv, int argc, int shift)
{
int i;
size_t len;
@@ -196,6 +197,28 @@ argv_concat (const char **argv, int argc, int shift)
return str;
}
+#if 0 //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+/* Compare two command's string. Used in sort_node (). */
+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;
+
+ return strcmp (a->string, b->string);
+}
+
+static int
+cmp_desc (const void *p, const void *q)
+{
+ const struct desc *a = *(struct desc * const *)p;
+ const struct desc *b = *(struct desc * const *)q;
+
+ return strcmp (a->cmd, b->cmd);
+}
+
+#endif //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
/* Install top node of command vector. */
void
install_node (struct cmd_node *node,
@@ -208,41 +231,57 @@ install_node (struct cmd_node *node,
/* Compare two command's string. Used in sort_node (). */
static int
-cmp_node (const struct cmd_element *a, const struct cmd_element *b)
+cmp_node (const struct cmd_element **a, const struct cmd_element **b)
{
- return strcmp (a->string, b->string);
+ return strcmp ((*a)->string, (*b)->string);
}
static int
-cmp_desc (const struct desc *a, const struct desc *b)
+cmp_desc (const struct desc **a, const struct desc **b)
{
- return strcmp (a->cmd, b->cmd);
+ return strcmp ((*a)->cmd, (*b)->cmd);
}
/* Sort each node's command element according to command string. */
void
sort_node ()
{
- unsigned int i, j;
- struct cmd_node *cnode;
- struct cmd_element *cmd_element;
+ unsigned int i ;
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((cnode = vector_slot (cmdvec, i)) != NULL)
- {
- vector cmd_vector = cnode->cmd_vector;
- vector_sort(cmd_vector, (vector_sort_cmp*)cmp_node) ;
+ for (i = 0; i < vector_length(cmdvec); i++)
+ {
+ struct cmd_node *cnode;
+ vector cmd_vector ;
+ unsigned int j;
- for (j = 0; j < vector_active (cmd_vector); j++)
- if ((cmd_element = vector_slot (cmd_vector, j)) != NULL
- && vector_active (cmd_element->strvec))
- {
- vector descvec = vector_slot (cmd_element->strvec,
- vector_active (cmd_element->strvec) - 1);
- vector_sort(descvec, (vector_sort_cmp*)cmp_desc) ;
- }
- }
-}
+ cnode = vector_get_item(cmdvec, i) ;
+
+ if (cnode == NULL)
+ continue ;
+
+ cmd_vector = cnode->cmd_vector;
+ if (cmd_vector == NULL)
+ 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) ;
+ } ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
* Take string and break it into tokens.
@@ -260,57 +299,132 @@ sort_node ()
* Returns: NULL => empty line (after white-space trimming) or comment line.
* otherwise: is vector containing one or more tokens.
*
+ * ....
+ *
+ *
* Note: all the tokens in the vector have at least one character, and no
* entries are NULL.
*
* NB: it is the caller's responsibility to release the vector and its contents,
* see cmd_free_strvec().
*/
-extern vector
-cmd_make_strvec (const char *string)
+static vector
+cmd_make_vline(vector vline, qstring qs, const char *string)
{
- const char *cp, *start;
- char *token;
- int strlen;
- vector strvec;
+ char *token, *tp ;
+ const char *cp, *sp, *ep, *op ;
- if (string == NULL)
- return NULL;
+ /* Reset any existing vline, and empty the qstring if given. */
+ if (vline != NULL)
+ vector_set_length(vline, 0) ;
+ qs_clear(qs) ;
+
+ /* Strip leading and trailing white-space and deal with empty or effectively
+ * empty lines -- comment lines are treated as effectively empty.
+ */
cp = string;
- /* Skip white spaces. */
+ if (string == NULL)
+ return NULL;
+
while (isspace((int) *cp))
cp++;
- /* Return if line is empty or effectively empty */
- if ((*cp == '\0') || (*cp == '!') || (*cp == '#'))
+ ep = cp + strlen(cp) ;
+
+ while ((ep > cp) && (isspace((int)*(ep - 1))))
+ --ep ;
+
+ if ((cp == ep) || (*cp == '!') || (*cp == '#'))
return NULL;
/* Prepare return vector -- expect some reasonable number of tokens. */
- strvec = vector_init (10) ;
+ if (vline == NULL)
+ vline = vector_init(10) ;
- /* Copy each command piece and set into vector. */
- while (1)
+ /* If writing the words to a qstring, copy the body of the original (less
+ * any leading/trailing whitespace) to the qstring and '\0' terminate.
+ */
+ if (qs != NULL)
{
- start = cp;
- while (!isspace((int) *cp) && (*cp != '\0'))
- cp++ ; /* eat token characters */
- strlen = cp - start;
- token = XMALLOC (MTYPE_STRVEC, strlen + 1);
- memcpy (token, start, strlen);
- *(token + strlen) = '\0';
- vector_push_item(strvec, token);
+ qs_set_n(qs, cp, ep - cp) ;
+ tp = (char*)qs->body ; /* start at the beginning */
+ }
+ else
+ tp = NULL ; /* not used, but not undefined */
+ op = cp ; /* original start position */
+
+ /* Now have string cp..ep with no leading/trailing whitespace.
+ *
+ * If using a qstring, a copy of that exists at tp, complete with terminating
+ * '\0'. Writes '\0' terminators after each word found -- overwriting first
+ * separating white-space or the '\0' at the end.
+ *
+ * If not using a qstring, construct a new MTYPE_STRVEC for each word.
+ */
+ while (cp < ep)
+ {
while (isspace((int) *cp))
- cp++ ; /* skip white-space */
+ cp++ ; /* skip white-space */
- if (*cp == '\0')
- return strvec;
- }
+ sp = cp ;
+ while ((cp < ep) && !isspace((int) *cp))
+ cp++ ; /* eat token characters */
+
+ if (qs == NULL)
+ {
+ /* creating array of MTYPE_STRVEC */
+ size_t len ;
+
+ len = cp - sp ;
+ token = XMALLOC (MTYPE_STRVEC, len + 1);
+ memcpy (token, sp, len);
+ *(token + len) = '\0';
+ }
+ else
+ {
+ /* using qstring */
+ token = tp + (sp - op) ; /* token in qstring */
+ *(tp + (cp - op)) = '\0' ; /* terminate */
+ } ;
+
+ vector_push_item(vline, token);
+ } ;
+
+ return vline ;
}
/*------------------------------------------------------------------------------
+ * Take string and break it into tokens.
+ *
+ * Discards leading and trailing white-space.
+ *
+ * Treats lines that start with '!' or '#' (after any leading white-space)
+ * as empty -- these are comment lines.
+ *
+ * Tokens are non-whitespace separated by one or more white-space.
+ *
+ * White-space is anything that isspace() thinks is a space. (Which in the
+ * 'C' locale is ' ', '\t', '\r, '\n', '\f' and '\v'.)
+ *
+ * Returns: NULL => empty line (after white-space trimming) or comment line.
+ * otherwise: is vector containing one or more tokens.
+ *
+ * Note: all the tokens in the vector have at least one character, and no
+ * entries are NULL.
+ *
+ * NB: it is the caller's responsibility to release the vector and its contents,
+ * see cmd_free_strvec().
+ */
+extern vector
+cmd_make_strvec (const char *string)
+{
+ return cmd_make_vline(NULL, NULL, string) ;
+} ;
+
+/*------------------------------------------------------------------------------
* Add given string to vector of strings.
*
* Create vector if required.
@@ -478,23 +592,27 @@ cmd_cmdsize (vector strvec)
{
unsigned int i;
int size = 0;
- vector descvec;
- struct desc *desc;
- for (i = 0; i < vector_active (strvec); i++)
- if ((descvec = vector_slot (strvec, i)) != NULL)
+ for (i = 0; i < vector_length(strvec); i++)
{
- if ((vector_active (descvec)) == 1
- && (desc = vector_slot (descvec, 0)) != NULL)
- {
- if (desc->cmd == NULL || CMD_OPTION (desc->cmd))
- return size;
- else
- size++;
- }
- else
- size++;
- }
+ 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;
}
@@ -509,7 +627,7 @@ cmd_prompt (enum node_type node)
cnode = NULL ;
if (node < cmdvec->limit)
- cnode = vector_slot (cmdvec, node);
+ cnode = vector_get_item (cmdvec, node);
if (cnode == NULL)
{
@@ -530,7 +648,7 @@ install_element (enum node_type ntype, struct cmd_element *cmd)
if (!cmdvec)
return;
- cnode = vector_slot (cmdvec, ntype);
+ cnode = vector_get_item (cmdvec, ntype);
if (cnode == NULL)
{
@@ -675,7 +793,7 @@ config_write_host (struct vty *vty)
static vector
cmd_node_vector (vector v, enum node_type ntype)
{
- struct cmd_node *cnode = vector_slot (v, ntype);
+ struct cmd_node *cnode = vector_get_item (v, ntype);
return cnode->cmd_vector;
}
@@ -1290,12 +1408,14 @@ static enum match_type
cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
{
unsigned int i;
+ unsigned int k;
enum match_type match_type;
match_type = no_match;
- /* If command and cmd_element string does not match set NULL to vector */
- for (i = 0; i < vector_active (cmd_v); i++)
+ /* If command and cmd_element string does not match, remove from vector */
+ k = 0 ;
+ for (i = 0; i < vector_length (cmd_v); i++)
{
const char *str;
struct cmd_element *cmd_element;
@@ -1304,110 +1424,113 @@ cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
unsigned int j;
int matched ;
+ cmd_element = vector_get_item(cmd_v, i) ;
+
/* Skip past cmd_v entries that have already been set NULL */
- if ((cmd_element = vector_slot (cmd_v, i)) == NULL)
+ if (cmd_element == NULL)
continue ;
/* Discard cmd_v entry that has no token at the current position */
- if (index >= vector_active (cmd_element->strvec))
- {
- vector_slot (cmd_v, i) = NULL;
- continue ;
- } ;
+ descvec = vector_get_item(cmd_element->strvec, index) ;
+ if (descvec == NULL)
+ continue ;
/* See if get any sort of match at current position */
matched = 0 ;
- descvec = vector_slot (cmd_element->strvec, index);
-
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
-
- if (CMD_VARARG (str))
- {
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
+ 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 (match_type < vararg_match)
+ match_type = vararg_match;
+ matched++;
+ }
+ else if (CMD_RANGE (str))
+ {
+ if (cmd_range_match (str, command))
+ {
+ if (match_type < range_match)
+ match_type = range_match;
- matched++;
- }
- }
+ matched++;
+ }
+ }
#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command))
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
+ else if (CMD_IPV6 (str))
+ {
+ if (cmd_ipv6_match (command))
+ {
+ if (match_type < ipv6_match)
+ match_type = ipv6_match;
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command))
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
+ matched++;
+ }
+ }
+ else if (CMD_IPV6_PREFIX (str))
+ {
+ if (cmd_ipv6_prefix_match (command))
+ {
+ if (match_type < ipv6_prefix_match)
+ match_type = ipv6_prefix_match;
- matched++;
- }
- }
+ matched++;
+ }
+ }
#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command))
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
+ else if (CMD_IPV4 (str))
+ {
+ if (cmd_ipv4_match (command))
+ {
+ if (match_type < ipv4_match)
+ match_type = ipv4_match;
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command))
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- /* Check is this point's argument optional ? */
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else if (strncmp (command, str, strlen (command)) == 0)
- {
- if (strcmp (command, str) == 0)
- match_type = exact_match;
- else
- {
- if (match_type < partly_match)
- match_type = partly_match;
- }
- matched++;
- } ;
- } ;
+ matched++;
+ }
+ }
+ else if (CMD_IPV4_PREFIX (str))
+ {
+ if (cmd_ipv4_prefix_match (command))
+ {
+ if (match_type < ipv4_prefix_match)
+ match_type = ipv4_prefix_match;
+ matched++;
+ }
+ }
+ else if (CMD_OPTION (str) || CMD_VARIABLE (str))
+ /* Check is this point's argument optional ? */
+ {
+ if (match_type < extend_match)
+ match_type = extend_match;
+ matched++;
+ }
+ else if (strncmp (command, str, strlen (command)) == 0)
+ {
+ if (strcmp (command, str) == 0)
+ match_type = exact_match;
+ else
+ {
+ if (match_type < partly_match)
+ match_type = partly_match;
+ }
+ matched++;
+ } ;
+ } ;
- /* Discard cmd_v entry that has no match at this position */
- if (!matched)
- vector_slot (cmd_v, i) = NULL;
- }
+ /* Keep cmd_v entry that has a match at this position */
+ if (matched)
+ vector_set_item(cmd_v, k++, cmd_element) ;
+ } ;
+
+ vector_set_length(cmd_v, k) ; /* discard what did not keep */
return match_type;
-}
+} ;
/*------------------------------------------------------------------------------
* Filter vector by command character with index.
@@ -1420,107 +1543,120 @@ cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
static enum match_type
cmd_filter_by_string (char *command, vector cmd_v, unsigned int index)
{
- unsigned int i;
- const char *str;
- struct cmd_element *cmd_element;
+ unsigned int i ;
+ unsigned int k ;
enum match_type match_type;
- vector descvec;
- struct desc *desc;
match_type = no_match;
- /* If command and cmd_element string does not match set NULL to vector */
- for (i = 0; i < vector_active (cmd_v); i++)
- if ((cmd_element = vector_slot (cmd_v, i)) != NULL)
- {
- /* If given index is bigger than max string vector of command,
- set NULL */
- if (index >= vector_active (cmd_element->strvec))
- vector_slot (cmd_v, i) = NULL;
- else
- {
- unsigned int j;
- int matched = 0;
+ /* If command and cmd_element string do match, keep in vector */
+ k = 0 ;
+ for (i = 0; i < vector_length(cmd_v); i++)
+ {
+ unsigned int j;
+ int matched ;
+ const char *str;
+ struct cmd_element *cmd_element;
+ vector descvec;
+ struct desc *desc;
- descvec = vector_slot (cmd_element->strvec, index);
+ cmd_element = vector_get_item(cmd_v, i) ;
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
+ /* Skip past NULL cmd_v entries (just in case) */
+ if (cmd_element == NULL)
+ continue ;
- if (CMD_VARARG (str))
- {
- if (match_type < vararg_match)
- match_type = vararg_match;
- matched++;
- }
- else if (CMD_RANGE (str))
- {
- if (cmd_range_match (str, command))
- {
- if (match_type < range_match)
- match_type = range_match;
- matched++;
- }
- }
+ /* 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 have a match against any of the current possibilities */
+ 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 (match_type < vararg_match)
+ match_type = vararg_match;
+ matched++;
+ }
+ else if (CMD_RANGE (str))
+ {
+ if (cmd_range_match (str, command))
+ {
+ if (match_type < range_match)
+ match_type = range_match;
+ matched++;
+ }
+ }
#ifdef HAVE_IPV6
- else if (CMD_IPV6 (str))
- {
- if (cmd_ipv6_match (command) == exact_match)
- {
- if (match_type < ipv6_match)
- match_type = ipv6_match;
- matched++;
- }
- }
- else if (CMD_IPV6_PREFIX (str))
- {
- if (cmd_ipv6_prefix_match (command) == exact_match)
- {
- if (match_type < ipv6_prefix_match)
- match_type = ipv6_prefix_match;
- matched++;
- }
- }
+ else if (CMD_IPV6 (str))
+ {
+ if (cmd_ipv6_match (command) == exact_match)
+ {
+ if (match_type < ipv6_match)
+ match_type = ipv6_match;
+ matched++;
+ }
+ }
+ else if (CMD_IPV6_PREFIX (str))
+ {
+ if (cmd_ipv6_prefix_match (command) == exact_match)
+ {
+ if (match_type < ipv6_prefix_match)
+ match_type = ipv6_prefix_match;
+ matched++;
+ }
+ }
#endif /* HAVE_IPV6 */
- else if (CMD_IPV4 (str))
- {
- if (cmd_ipv4_match (command) == exact_match)
- {
- if (match_type < ipv4_match)
- match_type = ipv4_match;
- matched++;
- }
- }
- else if (CMD_IPV4_PREFIX (str))
- {
- if (cmd_ipv4_prefix_match (command) == exact_match)
- {
- if (match_type < ipv4_prefix_match)
- match_type = ipv4_prefix_match;
- matched++;
- }
- }
- else if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- if (match_type < extend_match)
- match_type = extend_match;
- matched++;
- }
- else
- {
- if (strcmp (command, str) == 0)
- {
- match_type = exact_match;
- matched++;
- }
- }
- }
- if (!matched)
- vector_slot (cmd_v, i) = NULL;
- }
- }
+ else if (CMD_IPV4 (str))
+ {
+ if (cmd_ipv4_match (command) == exact_match)
+ {
+ if (match_type < ipv4_match)
+ match_type = ipv4_match;
+ matched++;
+ }
+ }
+ else if (CMD_IPV4_PREFIX (str))
+ {
+ if (cmd_ipv4_prefix_match (command) == exact_match)
+ {
+ if (match_type < ipv4_prefix_match)
+ match_type = ipv4_prefix_match;
+ matched++;
+ }
+ }
+ else if (CMD_OPTION (str) || CMD_VARIABLE (str))
+ {
+ if (match_type < extend_match)
+ match_type = extend_match;
+ matched++;
+ }
+ else
+ {
+ if (strcmp (command, str) == 0)
+ {
+ match_type = exact_match;
+ matched++;
+ } ;
+ } ;
+ } ;
+
+ /* 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 match_type;
}
@@ -1558,102 +1694,146 @@ cmd_filter_by_string (char *command, vector cmd_v, unsigned int index)
* 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 (char *command, vector cmd_v, int index, enum match_type type)
{
unsigned int i;
- unsigned int j;
- const char *str = NULL;
- struct cmd_element *cmd_element;
- const char *matched = NULL;
- vector descvec;
- struct desc *desc;
+ unsigned int k;
+ int ret ;
- for (i = 0; i < vector_active (cmd_v); i++)
- if ((cmd_element = vector_slot (cmd_v, i)) != NULL)
- {
- int match = 0;
+ ret = 0 ; /* all's well so far */
+ k = 0 ; /* nothing kept, yet */
- descvec = vector_slot (cmd_element->strvec, index);
+ 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;
+ int matched ;
+ enum match_type mt ;
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- enum match_type ret;
+ cmd_element = vector_get_item (cmd_v, i) ;
- str = desc->cmd;
+ /* 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++;
+ 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++;
+ }
+ 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++;
+ }
+ break;
- switch (type)
- {
- case exact_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
- && strcmp (command, str) == 0)
- match++;
- break;
- case partly_match:
- if (!(CMD_OPTION (str) || CMD_VARIABLE (str))
- && strncmp (command, str, strlen (command)) == 0)
- {
- if (matched && strcmp (matched, str) != 0)
- return 1; /* There is ambiguous match. */
- else
- matched = str;
- match++;
- }
- break;
- case range_match:
- if (cmd_range_match (str, command))
- {
- if (matched && strcmp (matched, str) != 0)
- return 1;
- else
- matched = str;
- match++;
- }
- break;
#ifdef HAVE_IPV6
- case ipv6_match:
- if (CMD_IPV6 (str))
- match++;
- break;
- case ipv6_prefix_match:
- if ((ret = cmd_ipv6_prefix_match (command)) != no_match)
- {
- if (ret == partly_match)
- return 2; /* There is incomplete match. */
+ case ipv6_match:
+ if (CMD_IPV6 (str))
+ matched++;
+ break;
- match++;
- }
- break;
+ case ipv6_prefix_match:
+ if ((mt = cmd_ipv6_prefix_match (command)) != no_match)
+ {
+ if (mt == partly_match)
+ if (ret != 1)
+ ret = 2; /* There is incomplete match. */
+
+ matched++;
+ }
+ break;
#endif /* HAVE_IPV6 */
- case ipv4_match:
- if (CMD_IPV4 (str))
- match++;
- break;
- case ipv4_prefix_match:
- if ((ret = cmd_ipv4_prefix_match (command)) != no_match)
- {
- if (ret == partly_match)
- return 2; /* There is incomplete match. */
- match++;
- }
- break;
- case extend_match:
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
- match++;
- break;
- case no_match:
- default:
- break;
- }
- }
- if (!match)
- vector_slot (cmd_v, i) = NULL;
- }
- return 0;
-}
+ case ipv4_match:
+ if (CMD_IPV4 (str))
+ matched++;
+ break;
+
+ case ipv4_prefix_match:
+ if ((mt = cmd_ipv4_prefix_match (command)) != no_match)
+ {
+ if (mt == partly_match)
+ if (ret != 1)
+ ret = 2; /* There is incomplete match. */
+
+ matched++;
+ }
+ break;
+
+ case extend_match:
+ if (CMD_OPTION (str) || CMD_VARIABLE (str))
+ matched++;
+ 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
@@ -1748,14 +1928,14 @@ cmd_entry_function_desc (const char *src, const char *dst)
* Returns: 0 => found same string in the vector
* 1 => NOT found same string in the vector
*/
-static int
+static bool
cmd_unique_string (vector v, const char *str)
{
unsigned int i;
char *match;
- for (i = 0; i < vector_active (v); i++)
- if ((match = vector_slot (v, i)) != NULL)
+ 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;
@@ -1763,20 +1943,20 @@ cmd_unique_string (vector v, const char *str)
/* Compare string to description vector. If there is same string
return 1 else return 0. */
-static int
+static bool
desc_unique_string (vector v, const char *str)
{
unsigned int i;
struct desc *desc;
- for (i = 0; i < vector_active (v); i++)
- if ((desc = vector_slot (v, i)) != NULL)
+ 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;
}
-static int
+static bool
cmd_try_do_shortcut (enum node_type node, char* first_word) {
return (node >= MIN_DO_SHORTCUT_NODE)
&& (first_word != NULL)
@@ -1798,13 +1978,13 @@ cmd_describe_command_real (vector vline, int node, int *status)
char *command;
/* Set index. */
- if (vector_active (vline) == 0)
+ if (vector_length (vline) == 0)
{
*status = CMD_ERR_NO_MATCH;
return NULL;
}
else
- index = vector_active (vline) - 1;
+ index = vector_length (vline) - 1;
/* Make copy vector of current node's command vector. */
cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
@@ -1815,7 +1995,7 @@ cmd_describe_command_real (vector vline, int node, int *status)
/* Filter commands. */
/* Only words precedes current word will be checked in this loop. */
for (i = 0; i < index; i++)
- if ((command = vector_slot (vline, i)))
+ if ((command = vector_get_item (vline, i)))
{
match = cmd_filter_by_completion (command, cmd_vector, i);
@@ -1825,15 +2005,15 @@ cmd_describe_command_real (vector vline, int node, int *status)
vector descvec;
unsigned int j, k;
- for (j = 0; j < vector_active (cmd_vector); j++)
- if ((cmd_element = vector_slot (cmd_vector, j)) != NULL
- && (vector_active (cmd_element->strvec)))
+ 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_slot (cmd_element->strvec,
- vector_active (cmd_element->strvec) - 1);
- for (k = 0; k < vector_active (descvec); k++)
+ 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_slot (descvec, k);
+ struct desc *desc = vector_get_item (descvec, k);
vector_set (matchvec, desc);
}
}
@@ -1863,53 +2043,58 @@ cmd_describe_command_real (vector vline, int node, int *status)
/* Prepare match vector */
/* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
- /* Make sure that cmd_vector is filtered based on current word */
- command = vector_slot (vline, index);
+ /* Make sure that cmd_vector is filtered based on current word */
+ command = vector_get_item (vline, index);
if (command)
match = cmd_filter_by_completion (command, cmd_vector, index);
/* Make description vector. */
- for (i = 0; i < vector_active (cmd_vector); i++)
- if ((cmd_element = vector_slot (cmd_vector, i)) != NULL)
- {
- vector strvec = cmd_element->strvec;
+ 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);
+ }
+ } ;
+ } ;
- /* if command is NULL, index may be equal to vector_active */
- if (command && index >= vector_active (strvec))
- vector_slot (cmd_vector, i) = NULL;
- else
- {
- /* Check if command is completed. */
- if (command == NULL && index == vector_active (strvec))
- {
- if (!desc_unique_string (matchvec, command_cr))
- vector_set (matchvec, &desc_cr);
- }
- else
- {
- unsigned int j;
- vector descvec = vector_slot (strvec, index);
- struct desc *desc;
-
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- const char *string;
-
- string = cmd_entry_function_desc (command, desc->cmd);
- if (string)
- {
- /* Uniqueness check */
- if (!desc_unique_string (matchvec, string))
- vector_set (matchvec, desc);
- }
- }
- }
- }
- }
vector_free (cmd_vector);
- if (vector_slot (matchvec, 0) == NULL)
+ if (vector_length(matchvec) == 0)
{
vector_free (matchvec);
*status = CMD_ERR_NO_MATCH;
@@ -1938,7 +2123,7 @@ cmd_describe_command (vector vline, int node, int *status)
{
vector ret;
- if ( cmd_try_do_shortcut(node, vector_slot(vline, 0) ) )
+ if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) )
{
vector shifted_vline;
unsigned int index;
@@ -1947,7 +2132,7 @@ cmd_describe_command (vector vline, int node, int *status)
shifted_vline = vector_init (vector_count(vline));
/* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
+ for (index = 1; index < vector_length (vline); index++)
{
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
@@ -2023,7 +2208,7 @@ cmd_complete_command_real (vector vline, int node, int *status)
int n ;
/* Stop immediately if the vline is empty. */
- if (vector_active (vline) == 0)
+ if (vector_length (vline) == 0)
{
*status = CMD_ERR_NO_MATCH;
return NULL;
@@ -2033,7 +2218,7 @@ cmd_complete_command_real (vector vline, int node, int *status)
cmd_v = vector_copy (cmd_node_vector (cmdvec, node));
/* First, filter upto, but excluding last token */
- last_ivl = vector_active (vline) - 1;
+ last_ivl = vector_length (vline) - 1;
for (ivl = 0; ivl < last_ivl; ivl++)
{
@@ -2041,7 +2226,7 @@ cmd_complete_command_real (vector vline, int node, int *status)
int ret;
/* TODO: does this test make any sense ? */
- if ((token = vector_slot (vline, ivl)) == NULL)
+ if ((token = vector_get_item (vline, ivl)) == NULL)
continue ;
/* First try completion match, return best kind of match */
@@ -2078,37 +2263,33 @@ cmd_complete_command_real (vector vline, int node, int *status)
/* Now we got into completion */
index = last_ivl ;
- token = vector_slot(vline, last_ivl) ; /* is now the last token */
+ token = vector_get_item(vline, last_ivl) ; /* is now the last token */
- for (i = 0; i < vector_active (cmd_v); i++)
+ for (i = 0; i < vector_length (cmd_v); i++)
{
unsigned int j;
const char *string;
- if ((cmd_element = vector_slot (cmd_v, i)) == NULL)
+ if ((cmd_element = vector_get_item (cmd_v, i)) == NULL)
continue ;
- vector strvec = cmd_element->strvec;
+ descvec = vector_get_item (cmd_element->strvec, index);
+ if (descvec == NULL)
+ continue ;
- /* Check field length */
- if (index >= vector_active (strvec))
+ for (j = 0; j < vector_length (descvec); j++)
{
- vector_slot (cmd_v, i) = NULL;
- continue ;
- }
-
- descvec = vector_slot (strvec, index);
+ desc = vector_get_item (descvec, j) ;
+ if (desc == NULL)
+ continue ;
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)) != NULL)
- {
- string = cmd_entry_function(token, desc->cmd) ;
- if ((string != NULL) && cmd_unique_string(matchvec, string))
- cmd_add_to_strvec (matchvec, string) ;
- }
+ string = cmd_entry_function(token, desc->cmd) ;
+ if ((string != NULL) && cmd_unique_string(matchvec, string))
+ cmd_add_to_strvec (matchvec, string) ;
+ } ;
} ;
- n = vector_end(matchvec) ; /* number of entries in the matchvec */
+ n = vector_length(matchvec) ; /* number of entries in the matchvec */
/* We don't need cmd_v any more. */
vector_free (cmd_v);
@@ -2121,13 +2302,12 @@ cmd_complete_command_real (vector vline, int node, int *status)
/* In case of 'command \t' pattern. Do you need '?' command at
the end of the line. */
if (*token == '\0')
- *status = CMD_ERR_NOTHING_TODO;
+ *status = CMD_COMPLETE_ALREADY;
else
*status = CMD_ERR_NO_MATCH;
return NULL;
}
-/* XXX: TODO: stop poking around inside vector */
/* Only one matched */
if (n == 1)
{
@@ -2152,7 +2332,7 @@ cmd_complete_command_real (vector vline, int node, int *status)
cmd_free_strvec(matchvec) ; /* discard the match vector */
- matchvec = vector_init (INIT_MATCHVEC_SIZE);
+ matchvec = vector_init (1);
vector_push_item(matchvec, lcdstr) ;
*status = CMD_COMPLETE_MATCH;
@@ -2173,7 +2353,7 @@ cmd_complete_command (vector vline, int node, int *status)
{
vector ret;
- if ( cmd_try_do_shortcut(node, vector_slot(vline, 0) ) )
+ if ( cmd_try_do_shortcut(node, vector_get_item(vline, 0) ) )
{
vector shifted_vline;
unsigned int index;
@@ -2182,7 +2362,7 @@ cmd_complete_command (vector vline, int node, int *status)
shifted_vline = vector_init (vector_count(vline));
/* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
+ for (index = 1; index < vector_length (vline); index++)
{
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
@@ -2224,104 +2404,259 @@ node_parent ( enum node_type node )
}
/*------------------------------------------------------------------------------
+ * Initialise a new struct cmd_parsed, allocating if required
+ */
+extern cmd_parsed
+cmd_parse_init_new(cmd_parsed parsed)
+{
+ if (parsed == NULL)
+ parsed = XCALLOC(MTYPE_CMD_PARSED, sizeof(*parsed)) ;
+ else
+ memset(parsed, 0, sizeof(*parsed)) ;
+
+ /* 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)
+ *
+ * line = zeroised qstring -- empty
+ * words = zeroised qstring -- empty
+ *
+ * vline = zeroised vector -- empty
+ *
+ * so nothing else to do
+ */
+
+ return parsed ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise a new struct cmd_parsed, allocating if required
+ */
+extern cmd_parsed
+cmd_parse_reset(cmd_parsed parsed, bool free_structure)
+{
+ if (parsed != NULL)
+ {
+ qs_reset_keep(&parsed->words) ;
+ vector_reset_keep(&parsed->vline) ;
+
+ if (free_structure)
+ XFREE(MTYPE_CMD_PARSED, parsed) ; /* sets parsed = NULL */
+ else
+ cmd_parse_init_new(parsed) ;
+ } ;
+
+ return parsed ;
+} ;
+
+/*------------------------------------------------------------------------------
* 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 'strict': use cmd_filter_by_string()
+ * otherwise: use cmd_filter_by_completion()
*
- * Takes the node from parsed->cnode.
+ * 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_this(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.
*
- * CMD_SUCCESS_DAEMON => as CMD_SUCCESS, and the command has a
- * "daemon" value.
+ * 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 )
*
- * otherwise => some failure to parse
+ * 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 argv[] in the parsed structure contains *copies* of the char*
- * pointers in the given vline.
+ * NB: the command line MUST be preserved until the parsed command is no
+ * longer required -- no copy is made.
*
- * The vline may not be released until the parsed structure is.
+ * 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.
*
- * The parsed structure may be released without worrying about the contents
- * of the argv[].
+ * See elsewhere for description of parsed structure.
*/
-enum cmd_parse_type
+extern enum cmd_return_code
+cmd_parse_command(struct vty* vty, enum cmd_parse_type type)
{
- cmd_parse_completion = 0,
- cmd_parse_strict = 1,
-};
+ enum cmd_return_code ret ;
+ enum cmd_return_code first_ret ;
+ cmd_parsed parsed ;
-static int
-cmd_parse_command(vector vline, int first, struct cmd_parsed* parsed,
- enum cmd_parse_type type)
+ /* Initialise the parsed structure -- assuming no 'do' */
+ if (vty->parsed == NULL)
+ vty->parsed = cmd_parse_init_new(NULL) ;
+ parsed = vty->parsed ;
+
+ parsed->onode = parsed->cnode = vty->node ;
+
+ parsed->cmd = NULL ;
+ parsed->do_shortcut = 0 ;
+
+ /* Parse the line into words -- set up parsed->words and parsed->vline */
+ cmd_make_vline(&parsed->vline, &parsed->words, vty->buf) ;
+
+ if (vector_length(&parsed->vline) == 0)
+ return CMD_EMPTY ; /* NB: parsed->cmd == NULL */
+
+ /* 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->cnode, vector_get_item(&parsed->vline, 0)))
+ {
+ parsed->cnode = ENABLE_NODE ;
+ parsed->do_shortcut = 1 ;
+ } ;
+
+ /* Try in the current node */
+ ret = cmd_parse_this(parsed, ((type & cmd_parse_strict) != 0)) ;
+
+ 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_this(parsed, ((type & cmd_parse_strict) != 0)) ;
+ } ;
+ } ;
+
+ return vty->parsed->cmd->daemon ? CMD_SUCCESS_DAEMON
+ : CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Work function for cmd_parse_command
+ *
+ * Takes a parsed structure, with the:
+ *
+ * cnode -- node to parse in
+ * vline -- the line broken into words
+ * do_shortcut -- true if first word is 'do' (to be ignored)
+ *
+ * and parses either strictly or with command completion.
+ *
+ * If successful, reduces the vline structure down to the variable portions,
+ * ie to the argv[] for the command function.
+ *
+ * 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_this(cmd_parsed parsed, bool strict)
{
unsigned int i ;
unsigned int ivl ;
unsigned index ;
+ unsigned first ;
+ unsigned argc ;
vector cmd_v;
struct cmd_element *cmd_element;
struct cmd_element *matched_element;
unsigned int matched_count, incomplete_count;
- int argc;
enum match_type match = 0;
int varflag;
char *command;
- int strict = (type == cmd_parse_strict) ;
+ /* Need length of vline, discounting the first entry if required */
+ first = parsed->do_shortcut ? 1 : 0 ;
- parsed->cmd = NULL ; /* return this if parsing fails */
- parsed->argc = 0 ;
+ assert(vector_length(&parsed->vline) >= first) ;
+ ivl = vector_length(&parsed->vline) - first ;
/* Make copy of command elements. */
cmd_v = vector_copy (cmd_node_vector (cmdvec, parsed->cnode));
/* Look for an unambiguous result */
- index = 0 ;
- for (ivl = first; ivl < vector_active (vline); ivl++)
- if ((command = vector_slot (vline, ivl)) != NULL)
- {
- int ret ;
+ for (index = 0 ; index < ivl; index++)
+ {
+ int ret ;
- index = ivl - first ;
- match = strict ? cmd_filter_by_string(command, cmd_v, index)
- : cmd_filter_by_completion(command, cmd_v, index) ;
+ command = vector_get_item(&parsed->vline, index + first) ;
+ if (command == NULL)
+ continue ;
- if (match == vararg_match)
- break;
+ match = strict ? cmd_filter_by_string(command, cmd_v, index)
+ : cmd_filter_by_completion(command, cmd_v, index) ;
- ret = is_cmd_ambiguous (command, cmd_v, index, match);
+ if (match == vararg_match)
+ break;
- if (ret != 0)
- {
- assert((ret == 1) || (ret == 2)) ;
- vector_free (cmd_v);
- return (ret == 1) ? CMD_ERR_AMBIGUOUS : CMD_ERR_NO_MATCH ;
- }
- }
+ 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_active (cmd_v); i++)
- if ((cmd_element = vector_slot (cmd_v, i)) != NULL)
- {
- if (match == vararg_match || index >= cmd_element->cmdsize)
- {
- matched_element = cmd_element;
+ 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);
+ printf ("DEBUG: %s\n", cmd_element->string);
#endif
- matched_count++;
- }
- else
- {
- incomplete_count++;
- }
- }
+ matched_count++;
+ }
+ else
+ {
+ incomplete_count++;
+ }
+ } ;
/* Finished with cmd_v. */
vector_free (cmd_v);
@@ -2339,18 +2674,17 @@ cmd_parse_command(vector vline, int first, struct cmd_parsed* parsed,
varflag = 0 ;
argc = 0 ;
- for (ivl = first; ivl < vector_active (vline); ivl++)
+ for (index = 0; index < ivl ; index++)
{
int take = varflag ;
if (!varflag)
{
- int index = ivl - first ;
- vector descvec = vector_slot (matched_element->strvec, index);
+ vector descvec = vector_get_item (matched_element->strvec, index);
- if (vector_active (descvec) == 1)
+ if (vector_length (descvec) == 1)
{
- struct desc *desc = vector_slot (descvec, 0);
+ struct desc *desc = vector_get_item (descvec, 0);
if (CMD_VARARG (desc->cmd))
take = varflag = 1 ;
@@ -2362,109 +2696,53 @@ cmd_parse_command(vector vline, int first, struct cmd_parsed* parsed,
}
if (take)
- {
- if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXCEED_ARGC_MAX ;
- parsed->argv[argc++] = vector_slot (vline, ivl);
- } ;
+ vector_assign_item(&parsed->vline, argc++, index + first) ;
} ;
+ vector_set_length(&parsed->vline, argc) ; /* set to new length */
+
/* Everything checks out... ready to execute command */
parsed->cmd = matched_element ;
- parsed->argc = argc ;
-
- if (parsed->cmd->daemon)
- return CMD_SUCCESS_DAEMON;
return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
- * Parse a command in the given "node", or any of its ancestors.
- *
- * Takes the node to start in from parsed->cnode.
- *
- * Returns: CMD_SUCCESS => successfully parsed command, and the result is
- * in the given parsed structure, ready for execution.
- *
- * NB: parsed->cnode may have changed.
+ * Dispatch a parsed command.
*
- * CMD_SUCCESS_DAEMON => as CMD_SUCCESS, and the command has a
- * "daemon" value.
+ * Returns: command return code. NB: may be CMD_QUEUED (unless no_queue).
*
- * otherwise => some failure to parse
- *
- * NB: returns error from attempt to parse in the
- * starting parsed->cnode (which is returned
- * unchanged).
- *
- * See cmd_parse_command() for more detail.
+ * 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.
*/
-static int
-cmd_parse_command_tree(vector vline, int first, struct cmd_parsed* parsed,
- enum cmd_parse_type type)
+extern enum cmd_return_code
+cmd_dispatch(struct vty* vty, bool no_queue)
{
- int ret ;
- int first_ret ;
- enum node_type first_node ;
-
- /* Try in the current node */
- ret = cmd_parse_command(vline, first, parsed, type) ;
+ cmd_parsed parsed = vty->parsed ;
+ enum cmd_return_code ret ;
- if ((ret == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON))
- return ret ; /* done if found command */
+ if (parsed->cmd == NULL)
+ return CMD_SUCCESS ; /* NULL commands are easy */
- /* Try in parent node(s) */
- first_node = parsed->cnode ;
- first_ret = ret ;
+ vty->node = parsed->cnode ;
- while (parsed->cnode <= CONFIG_NODE)
+ if (no_queue || !vty_cli_nexus)
{
- parsed->cnode = node_parent(parsed->cnode) ;
- ret = cmd_parse_command(vline, first, parsed, type) ;
-
- if ((ret == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON))
- return ret ; /* done if found command */
- } ;
-
- parsed->cnode = first_node ; /* restore node state */
- return first_ret ; /* return original result */
-}
-
-/*------------------------------------------------------------------------------
- * Prepare to parse command -- deal with possible "do" shortcut.
- *
- * Initialises parsed structure, setting cnode and onode to vty->node.
- *
- * Checks to see if this is a valid "do" shortcut, and adjusts cnode if so.
- *
- * Returns: 0 => not "do" -- first word of actual command is first word
- * 1 => is "do" -- first word of actual command is second word
- */
-static int
-cmd_pre_command(struct vty* vty, struct cmd_parsed* parsed, vector vline)
-{
- int first ;
-
- parsed->onode = parsed->cnode = vty_get_node(vty) ;
-
- parsed->cmd = NULL ;
- parsed->argc = 0 ;
-
- /* 'do' forces command to be parsed in ENABLE_NODE (if allowed) */
- if (cmd_try_do_shortcut(parsed->cnode, vector_slot(vline, 0)))
- {
- parsed->cnode = ENABLE_NODE ;
- parsed->do_shortcut = 1 ;
- first = 1 ;
+ ret = cmd_dispatch_call(vty) ;
+ cmd_post_command(vty, ret) ;
}
else
{
- parsed->do_shortcut = 0 ;
- first = 0 ;
+ /* 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 first ;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
@@ -2477,266 +2755,153 @@ cmd_pre_command(struct vty* vty, struct cmd_parsed* parsed, vector vline)
* state.
*
* Arguments: ret = CMD_XXXX -- NB: CMD_QUEUED => command revoked
- * action = true unless command return is being revoked
*/
extern void
-cmd_post_command(struct vty* vty, struct cmd_parsed* parsed, int ret,
- int action)
+cmd_post_command(struct vty* vty, int ret)
{
- if (parsed->do_shortcut)
+ if (vty->parsed->do_shortcut)
{
- if (vty_get_node(vty) == ENABLE_NODE)
- vty_set_node(vty, parsed->onode) ;
+ if (vty->node == ENABLE_NODE)
+ vty->node = vty->parsed->onode ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Execute command:
+ * Parse and execute a command.
+ *
+ * The command is given by vty->buf and vty->node.
+ *
+ * Uses vty->parsed.
*
- * -- use cmd_parse_completion type parsing.
+ * -- use strict/completion parsing, as required.
*
- * -- unless vtysh: if does not parse in current node, try ancestors
+ * -- 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
+ * -- 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 int
-cmd_execute_command (vector vline, struct vty *vty,
- struct cmd_element **cmd, qpn_nexus to_nexus,
- qpn_nexus from_nexus, int vtysh)
+extern enum cmd_return_code
+cmd_execute_command(struct vty *vty,
+ enum cmd_parse_type type, struct cmd_element **cmd)
{
- int ret ;
- int first ;
- struct cmd_parsed parsed ;
-
- /* Set up parsed structure & deal with "do" shortcut prefix */
- first = cmd_pre_command(vty, &parsed, vline) ;
-
- /* Try to parse in parsed.cnode node or, if required, ancestors thereof. */
- if (vtysh)
- ret = cmd_parse_command(vline, first, &parsed, cmd_parse_completion) ;
- else
- ret = cmd_parse_command_tree(vline, first, &parsed, cmd_parse_completion) ;
-
- if (cmd)
- *cmd = parsed.cmd ; /* for vtysh */
+ enum cmd_return_code ret ;
- if (ret != CMD_SUCCESS)
- return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */
-
- /* Execute the parsed command */
-
- vty_set_node(vty, parsed.cnode) ;
+ /* Try to parse in vty->node or, if required, ancestors thereof. */
+ ret = cmd_parse_command(vty, type) ;
- if (qpthreads_enabled && !(parsed.cmd->attr & CMD_ATTR_CALL))
- {
- /* Don't do it now, but send to bgp qpthread */
- cq_enqueue(vty, &parsed, to_nexus, from_nexus) ;
- ret = CMD_QUEUED ;
- }
- else
- {
- ret = (*(parsed.cmd->func))(parsed.cmd, vty, parsed.argc, parsed.argv) ;
+ if (cmd != NULL)
+ *cmd = vty->parsed->cmd ; /* for vtysh */
- cmd_post_command(vty, &parsed, ret, 1) ;
- } ;
+ if (ret == CMD_SUCCESS)
+ ret = cmd_dispatch(vty, 0) ;
+ else if (ret == CMD_EMPTY)
+ ret = CMD_SUCCESS ;
return ret ;
} ;
/*------------------------------------------------------------------------------
- * Execute command by argument readline.
+ * Read configuration from file.
*
- * -- use cmd_parse_strict type parsing.
+ * In the qpthreads world this assumes that it is running with the vty
+ * locked, and that all commands are to be executed directly.
*
- * -- unless vtysh: if does not parse in current node, try ancestors
+ * 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.
*
- * If does not find in any ancestor, return error from current node.
+ * 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)
*
- * -- does NOT implement the "do" shortcut
+ * If the file is empty, will return CMD_SUCCESS.
*
- * At all times executes the command immediately (no queueing, even if
- * qpthreads_enabled).
+ * Never returns CMD_EMPTY -- that counts as CMD_SUCCESS.
*
- * The vty->node may be changed either when parsing or executing the command.
+ * If
+ *
+ * If return code is not CMD_SUCCESS, the the output buffering contains the
+ * output from the last command attempted.
*/
-extern int
-cmd_execute_command_strict (vector vline, struct vty *vty,
- struct cmd_element **cmd, int vtysh)
+extern enum cmd_return_code
+config_from_file (struct vty *vty, FILE *fp, struct cmd_element* first_cmd,
+ qstring buf, bool ignore_warning)
{
-#if 0 /* replaced by cmd_parse_command() */
- unsigned int i;
- unsigned int index;
- vector cmd_vector;
- struct cmd_element *cmd_element;
- struct cmd_element *matched_element;
- unsigned int matched_count, incomplete_count;
- int argc;
- const char *argv[CMD_ARGC_MAX];
- int varflag;
- enum match_type match = 0;
- char *command;
-
- /* Make copy of command element */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty_get_node(vty)));
-
- for (index = 0; index < vector_active (vline); index++)
- if ((command = vector_slot (vline, index)))
- {
- int ret;
-
- match = cmd_filter_by_string (vector_slot (vline, index),
- cmd_vector, index);
-
- /* If command meets '.VARARG' then finish matching. */
- if (match == vararg_match)
- break;
-
- ret = is_cmd_ambiguous (command, cmd_vector, index, match);
- if (ret == 1)
- {
- vector_free (cmd_vector);
- return CMD_ERR_AMBIGUOUS;
- }
- if (ret == 2)
- {
- vector_free (cmd_vector);
- return CMD_ERR_NO_MATCH;
- }
- }
-
- /* Check matched count. */
- matched_element = NULL;
- matched_count = 0;
- incomplete_count = 0;
- for (i = 0; i < vector_active (cmd_vector); i++)
- if (vector_slot (cmd_vector, i) != NULL)
- {
- cmd_element = vector_slot (cmd_vector, i);
-
- if (match == vararg_match || index >= cmd_element->cmdsize)
- {
- matched_element = cmd_element;
- matched_count++;
- }
- else
- incomplete_count++;
- }
-
- /* Finish of using cmd_vector. */
- vector_free (cmd_vector);
-
- /* To execute command, matched_count must be 1. */
- if (matched_count == 0)
- {
- if (incomplete_count)
- return CMD_ERR_INCOMPLETE;
- else
- return CMD_ERR_NO_MATCH;
- }
-
- if (matched_count > 1)
- return CMD_ERR_AMBIGUOUS;
+ enum cmd_return_code ret;
- /* Argument treatment */
- varflag = 0;
- argc = 0;
+ vty->buf = buf->body ;
+ vty->lineno = 0 ;
- for (i = 0; i < vector_active (vline); i++)
- {
- if (varflag)
- argv[argc++] = vector_slot (vline, i);
- else
- {
- vector descvec = vector_slot (matched_element->strvec, i);
-
- if (vector_active (descvec) == 1)
- {
- struct desc *desc = vector_slot (descvec, 0);
-
- if (CMD_VARARG (desc->cmd))
- varflag = 1;
-
- if (varflag || CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd))
- argv[argc++] = vector_slot (vline, i);
- }
- else
- argv[argc++] = vector_slot (vline, i);
- }
-
- if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXCEED_ARGC_MAX;
- }
-#endif
-
- struct cmd_parsed parsed ;
- int ret ;
-
- /* Try to parse in parsed.cnode node or, if required, ancestors thereof. */
- if (vtysh)
- ret = cmd_parse_command(vline, 0, &parsed, cmd_parse_strict) ;
- else
- ret = cmd_parse_command_tree(vline, 0, &parsed, cmd_parse_strict) ;
-
- if (cmd)
- *cmd = parsed.cmd ; /* for vtysh */
-
- if (ret != CMD_SUCCESS)
- return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */
-
- /* Now execute matched command */
- vty_set_node(vty, parsed.cnode) ;
-
- return (*(parsed.cmd->func)) (parsed.cmd, vty, parsed.argc, parsed.argv);
-}
-
-/*------------------------------------------------------------------------------
- * Read configuration from file.
- *
- */
-int
-config_from_file (struct vty *vty, FILE *fp, void (*after_first_cmd)(void),
- qstring buf)
-{
- int ret;
- vector vline;
- int first_cmd = 1;
+ ret = CMD_SUCCESS ; /* in case file is empty */
+ vty_out_clear(vty) ;
while (fgets (buf->body, buf->size, fp))
{
- vline = cmd_make_strvec (buf->body);
+ ++vty->lineno ;
+
+ /* Execute configuration command : this is strict match */
+ ret = cmd_parse_command(vty, cmd_parse_strict + cmd_parse_tree) ;
- /* In case of comment line */
- if (vline == NULL)
- continue;
+ if (ret == CMD_EMPTY)
+ continue ; /* skip empty/comment */
- /* Execute configuration command : this is strict match */
- ret = cmd_execute_command_strict (vline, vty, NULL, 0);
+ if (ret != CMD_SUCCESS)
+ break ; /* stop on *any* parsing issue */
- /* special handling for after the first command */
- if (first_cmd && after_first_cmd)
+ /* special handling before of first command */
+ if (first_cmd != NULL)
{
- after_first_cmd();
- first_cmd = 0;
+ 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 ;
}
- cmd_free_strvec (vline);
+ /* 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 */
+ } ;
- /* TODO: why does config file not stop on CMD_WARNING ?? */
- if (ret != CMD_SUCCESS && ret != CMD_WARNING
- && ret != CMD_ERR_NOTHING_TODO)
- return ret;
+ vty_out_clear(vty) ;
} ;
- return CMD_SUCCESS;
-}
+ if (ret == CMD_EMPTY)
+ ret = CMD_SUCCESS ; /* OK if end on empty line */
+
+ return ret ;
+} ;
/*----------------------------------------------------------------------------*/
@@ -2762,7 +2927,7 @@ DEFUN_CALL (enable,
{
/* If enable password is NULL, change to ENABLE_NODE */
if ((host.enable == NULL && host.enable_encrypt == NULL) ||
- vty_get_type(vty) == VTY_SHELL_SERV)
+ vty_shell_serv(vty))
vty_set_node(vty, ENABLE_NODE);
else
vty_set_node(vty, AUTH_ENABLE_NODE);
@@ -2787,8 +2952,7 @@ DEFUN_CALL (config_exit,
"exit",
"Exit current mode and down to previous mode\n")
{
- vty_cmd_exit(vty) ;
- return CMD_SUCCESS;
+ return vty_cmd_exit(vty) ;
}
/* quit is alias of exit. */
@@ -2803,8 +2967,7 @@ DEFUN_CALL (config_end,
"end",
"End current mode and change to enable mode.")
{
- vty_cmd_end(vty) ;
- return CMD_SUCCESS;
+ return vty_cmd_end(vty) ;
}
/* Show version. */
@@ -2852,11 +3015,11 @@ DEFUN_CALL (config_list,
"Print command list\n")
{
unsigned int i;
- struct cmd_node *cnode = vector_slot (cmdvec, vty_get_node(vty));
+ struct cmd_node *cnode = vector_get_item (cmdvec, vty_get_node(vty));
struct cmd_element *cmd;
- for (i = 0; i < vector_active (cnode->cmd_vector); i++)
- if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
+ for (i = 0; i < vector_length (cnode->cmd_vector); i++)
+ if ((cmd = vector_get_item (cnode->cmd_vector, i)) != NULL
&& !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
vty_out (vty, " %s%s", cmd->string,
VTY_NEWLINE);
@@ -2872,12 +3035,12 @@ DEFUN (config_write_file,
{
unsigned int i;
int fd;
+ int err;
struct cmd_node *node;
char *config_file;
char *config_file_tmp = NULL;
char *config_file_sav = NULL;
int ret = CMD_WARNING;
- struct vty *file_vty;
/* Check and see if we are operating under vtysh configuration */
if (host.config == NULL)
@@ -2909,47 +3072,61 @@ DEFUN (config_write_file,
}
/* Make vty for configuration file. */
- file_vty = vty_new (fd, VTY_FILE);
+ vty_open_config_write(vty, fd) ;
/* Config file header print. */
- vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
- vty_time_print (file_vty, 1);
- vty_out (file_vty, "!\n");
+ vty_out (vty, "!\n! Zebra configuration saved from vty\n! ");
+ vty_time_print (vty, 1);
+ vty_out (vty, "!\n");
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((node = vector_slot (cmdvec, i)) && node->func)
+ for (i = 0; i < vector_length (cmdvec); i++)
+ if ((node = vector_get_item (cmdvec, i)) && node->func)
{
- if ((*node->func) (file_vty))
- vty_out (file_vty, "!\n");
+ if ((*node->func) (vty))
+ vty_out (vty, "!\n");
}
- vty_close (file_vty);
+
+ err = vty_close_config_write(vty) ;
+ close(fd) ;
+
+ if (err != 0)
+ {
+ vty_out (vty, "Failed while writing configuration file %s.%s",
+ config_file_tmp, VTY_NEWLINE);
+ goto finished;
+ }
if (unlink (config_file_sav) != 0)
if (errno != ENOENT)
{
- vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
- VTY_NEWLINE);
+ vty_out (vty, "Can't unlink backup configuration file %s.%s",
+ config_file_sav, VTY_NEWLINE);
goto finished;
- }
+ } ;
+
if (link (config_file, config_file_sav) != 0)
{
- vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
- VTY_NEWLINE);
+ vty_out (vty, "Can't backup old configuration file %s.%s",
+ config_file_sav, VTY_NEWLINE);
goto finished;
- }
- sync ();
+ } ;
+
+ sync () ;
+
if (unlink (config_file) != 0)
{
- vty_out (vty, "Can't unlink configuration file %s.%s", config_file,
- VTY_NEWLINE);
+ vty_out (vty, "Can't unlink configuration file %s.%s",
+ config_file, VTY_NEWLINE);
goto finished;
- }
+ } ;
+
if (link (config_file_tmp, config_file) != 0)
{
- vty_out (vty, "Can't save configuration file %s.%s", config_file,
- VTY_NEWLINE);
+ vty_out (vty, "Can't save configuration file %s.%s",
+ config_file, VTY_NEWLINE);
goto finished;
- }
+ } ;
+
sync ();
if (chmod (config_file, CONFIGFILE_MASK) != 0)
@@ -2959,8 +3136,8 @@ DEFUN (config_write_file,
goto finished;
}
- vty_out (vty, "Configuration saved to %s%s", config_file,
- VTY_NEWLINE);
+ vty_out (vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE);
+
ret = CMD_SUCCESS;
finished:
@@ -2998,10 +3175,10 @@ DEFUN (config_write_terminal,
unsigned int i;
struct cmd_node *node;
- if (vty_get_type(vty) == VTY_SHELL_SERV)
+ if (vty_shell_serv(vty))
{
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
+ 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);
@@ -3013,8 +3190,8 @@ DEFUN (config_write_terminal,
VTY_NEWLINE);
vty_out (vty, "!%s", VTY_NEWLINE);
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((node = vector_slot (cmdvec, i)) && node->func)
+ 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);
@@ -4012,39 +4189,52 @@ cmd_terminate ()
if (cmdvec)
{
- for (i = 0; i < vector_active (cmdvec); i++)
- if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
- {
- cmd_node_v = cmd_node->cmd_vector;
+ for (i = 0; i < vector_length (cmdvec); i++)
+ {
+ cmd_node = vector_get_item (cmdvec, i) ;
+ if (cmd_node == NULL)
+ continue ;
- for (j = 0; j < vector_active (cmd_node_v); j++)
- if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL &&
- cmd_element->strvec != NULL)
- {
- cmd_element_v = cmd_element->strvec;
-
- for (k = 0; k < vector_active (cmd_element_v); k++)
- if ((desc_v = vector_slot (cmd_element_v, k)) != NULL)
- {
- for (l = 0; l < vector_active (desc_v); l++)
- if ((desc = vector_slot (desc_v, l)) != NULL)
- {
- 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_node_v = cmd_node->cmd_vector;
- vector_free (cmd_node_v);
- }
+ for (j = 0; j < vector_length (cmd_node_v); j++)
+ {
+ 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++)
+ {
+ 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);
+ } ;
+
+ vector_free (cmd_node_v);
+ } ;
vector_free (cmdvec);
cmdvec = NULL;
diff --git a/lib/command.h b/lib/command.h
index 0e8b3630..bd1ad99c 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -23,12 +23,17 @@
#ifndef _ZEBRA_COMMAND_H
#define _ZEBRA_COMMAND_H
+#include <stdbool.h>
+
#include "node_type.h"
#include "vector.h"
-#include "uty.h"
-#include "vty.h"
#include "qstring.h"
-#include "lib/route_types.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+struct vty ; /* in case command.h expanded first */
/* Host configuration variable */
struct host
@@ -86,16 +91,66 @@ enum
{
/* bit significant */
CMD_ATTR_DEPRECATED = 0x01,
- CMD_ATTR_HIDDEN = 0x02,
- CMD_ATTR_CALL = 0x04,
+ 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.
+ */
+enum cmd_return_code
+{
+ CMD_SUCCESS = 0,
+ CMD_WARNING = 1,
+ CMD_ERROR,
+
+ CMD_EMPTY,
+ CMD_SUCCESS_DAEMON,
+
+ 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
+} ;
+
+#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. */
- int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
- const char *doc; /* Documentation of this command. */
+ 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; /* Pointing out each description vector. */
unsigned int cmdsize; /* Command index count. */
@@ -111,55 +166,48 @@ struct desc
char *str; /* Command's description. */
};
-/* Argc max counts. */
-#define CMD_ARGC_MAX 25
+/* Command parsing options */
+enum cmd_parse_type /* bit significant */
+{
+ cmd_parse_completion = 0x00,
+ cmd_parse_strict = 0x01,
+
+ cmd_parse_do = 0x02,
+ cmd_parse_tree = 0x04,
+} ;
-/* Parsed command */
+/* Parsed command */
+typedef struct cmd_parsed* cmd_parsed ;
struct cmd_parsed
{
- struct cmd_element *cmd ;
+ struct cmd_element *cmd ; /* NULL if empty command
+ or fails to parse */
+
+ enum node_type cnode ; /* node command is in */
+ enum node_type onode ; /* node the parser started in */
- enum node_type cnode ; /* node command is in */
+ bool do_shortcut ; /* true => is "do" command */
- int do_shortcut ; /* true => is "do" command */
- enum node_type onode ; /* vty->node before "do" */
+ qstring_t words ; /* the words, '\0' separated */
- int argc ;
- const char* argv[CMD_ARGC_MAX] ;
+ vector_t vline ; /* pointers to the words */
} ;
-/* 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.
- */
-enum cmd_return_code
-{
- CMD_SUCCESS = 0,
- CMD_WARNING = 1,
- CMD_ERR_NO_MATCH = 2,
- CMD_ERR_AMBIGUOUS = 3,
- CMD_ERR_INCOMPLETE = 4,
- CMD_ERR_EXCEED_ARGC_MAX = 5,
- CMD_ERR_NOTHING_TODO = 6,
- CMD_COMPLETE_FULL_MATCH = 7,
- CMD_COMPLETE_MATCH = 8,
- CMD_COMPLETE_LIST_MATCH = 9,
-
- CMD_SUCCESS_DAEMON = 10,
- CMD_QUEUED = 11
+/* Command dispatch options */
+enum {
+ cmd_no_queue = true,
+ cmd_may_queue = false,
} ;
-#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"
-#define MSG_CMD_ERR_EXEED_ARGC_MAX "Exceeds maximum word count"
+/*------------------------------------------------------------------------------
+ * Can now include these
+ */
+#include "vty.h"
+#include "uty.h"
+
+/*----------------------------------------------------------------------------*/
/* Turn off these macros when uisng cpp with extract.pl */
#ifndef VTYSH_EXTRACT_PL
@@ -175,14 +223,10 @@ enum cmd_return_code
};
#define DEFUN_CMD_FUNC_DECL(funcname) \
- static int funcname (struct cmd_element *, struct vty *, int, const char *[]);
+ static cmd_function funcname;
#define DEFUN_CMD_FUNC_TEXT(funcname) \
- static int funcname \
- (struct cmd_element *self __attribute__ ((unused)), \
- struct vty *vty __attribute__ ((unused)), \
- int argc __attribute__ ((unused)), \
- const char *argv[] __attribute__ ((unused)) )
+ static DEFUN_CMD_FUNCTION(funcname)
/* DEFUN for vty command interafce. Little bit hacky ;-). */
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
@@ -339,7 +383,7 @@ extern void sort_node (void);
/* 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 **argv, int argc, int shift);
+extern char *argv_concat (const char* const* argv, int argc, int shift);
extern vector cmd_make_strvec (const char *);
extern vector cmd_add_to_strvec (vector v, const char* str) ;
@@ -347,17 +391,35 @@ extern void cmd_free_strvec (vector);
extern vector cmd_describe_command (vector, int, int *status);
extern vector cmd_complete_command (vector, int, int *status);
extern const char *cmd_prompt (enum node_type);
-extern int config_from_file (struct vty *, FILE *, void (*)(void), qstring buf);
+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 int cmd_execute_command (vector, struct vty* vty,
- struct cmd_element **cmd, qpn_nexus to_nexus,
- qpn_nexus from_nexus,
- int vtysh) ;
-extern void cmd_post_command(struct vty* vty, struct cmd_parsed* parsed,
- int ret, int action) ;
-extern int cmd_execute_command_strict (vector vline, struct vty *vty,
- struct cmd_element **cmd, int vtysh) ;
+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)
+{
+ cmd_parsed parsed = vty->parsed ;
+ return (*(parsed->cmd->func))(parsed->cmd, vty,
+ vector_length(&parsed->vline),
+ (const char * const*)vector_body(&parsed->vline)) ;
+} ;
+
+#define cmd_parse_reset_keep(parsed) cmd_parse_reset(parsed, 0)
+#define cmd_parse_reset_free(parsed) cmd_parse_reset(parsed, 1)
+
extern void config_replace_string (struct cmd_element *, char *, ...);
+
extern void cmd_init (int);
extern void cmd_terminate (void);
diff --git a/lib/command_queue.c b/lib/command_queue.c
index b6aab580..ea8ac469 100644
--- a/lib/command_queue.c
+++ b/lib/command_queue.c
@@ -25,6 +25,10 @@
#include "qpnexus.h"
#include "memory.h"
#include "command_queue.h"
+#include "vty.h"
+#include "uty.h"
+#include "vector.h"
+#include "qstring.h"
/*------------------------------------------------------------------------------
* Form of message passed with command to be executed
@@ -32,17 +36,7 @@
struct cq_command_args
{
- qpn_nexus ret_nexus ;
-
- struct cmd_element *cmd ;
-
- enum node_type cnode ; /* vty->node before execution */
- enum node_type onode ; /* vty->node before "do" */
-
- short int do_shortcut ; /* true => is "do" command */
-
- short int argc ; /* count of arguments */
- short int ret ; /* return code */
+ enum cmd_return_code ret ; /* return code from command */
} ;
MQB_ARGS_SIZE_OK(cq_command_args) ;
@@ -56,28 +50,16 @@ static void cq_return(mqueue_block mqb, mqb_flag_t flag);
* Enqueue vty and argv[] for execution in given nexus.
*/
void
-cq_enqueue(struct vty *vty, struct cmd_parsed* parsed, qpn_nexus to_nexus,
- qpn_nexus from_nexus)
+cq_enqueue(struct vty *vty, qpn_nexus dst)
{
- int i;
struct cq_command_args* args ;
mqueue_block mqb = mqb_init_new(NULL, cq_action, vty) ;
args = mqb_get_args(mqb) ;
- args->cmd = parsed->cmd ;
- args->cnode = parsed->cnode ;
- args->onode = parsed->onode ;
- args->do_shortcut = parsed->do_shortcut ;
- args->argc = parsed->argc ;
-
- args->ret_nexus = from_nexus ;
- args->ret = CMD_SUCCESS ;
-
- for (i = 0; i < parsed->argc; ++i)
- mqb_push_argv_p(mqb, XSTRDUP(MTYPE_MARSHAL, parsed->argv[i]));
+ args->ret = CMD_QUEUED ;
- mqueue_enqueue(to_nexus->queue, mqb, 0) ;
+ mqueue_enqueue(dst->queue, mqb, 0) ;
}
/*------------------------------------------------------------------------------
@@ -85,6 +67,8 @@ cq_enqueue(struct vty *vty, struct cmd_parsed* parsed, qpn_nexus to_nexus,
*
* 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.
*/
static void
cq_action(mqueue_block mqb, mqb_flag_t flag)
@@ -97,59 +81,63 @@ cq_action(mqueue_block mqb, mqb_flag_t flag)
if (flag == mqb_action)
{
- const char** argv = mqb_get_argv(mqb) ;
-
- args->ret = (args->cmd->func)(args->cmd, vty, args->argc, argv) ;
+ args->ret = cmd_dispatch_call(vty) ;
+ assert(args->ret != CMD_QUEUED) ; /* avoid confusion ! */
}
else
args->ret = CMD_QUEUED ;
mqb_set_action(mqb, cq_return) ;
- mqueue_enqueue(args->ret_nexus->queue, mqb, 0) ;
+ mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
} ;
/*------------------------------------------------------------------------------
- * Accept return from command executed in another thread.
+ * Accept return from command which has completed.
*
* The command line processing for the vty may be stalled (with read mode
* disabled) waiting for the return from the command.
*
- * If the message is being revoked/deleted the state of the vty is still
- * updated (to show that the command has completed) BUT nothing is kicked.
- * It is up to the revoke/delete function to deal with any possibility of the
- * vty remaining stalled.
+ * 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.
+ *
+ * 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.
*/
static void
cq_return(mqueue_block mqb, mqb_flag_t flag)
{
struct vty *vty ;
struct cq_command_args* args ;
- int i ;
- void** argv ;
- struct cmd_parsed parsed ;
vty = mqb_get_arg0(mqb) ;
args = mqb_get_args(mqb) ;
- /* clean up */
- argv = mqb_get_argv(mqb) ;
-
- for (i = 0; i < args->argc; ++i)
- XFREE(MTYPE_MARSHAL, argv[i]);
-
- /* signal end of command -- passing the action state */
- parsed.cmd = args->cmd ;
- parsed.cnode = args->cnode ;
- parsed.onode = args->onode ;
- parsed.do_shortcut = args->do_shortcut ;
- parsed.argc = 0 ;
- cmd_post_command(vty, &parsed, args->ret, (flag == mqb_action)) ;
+ /* signal end of command */
+ cmd_post_command(vty, args->ret) ;
+ vty_queued_result(vty, args->ret) ;
- /* update the state of the vty -- passing the "action" state */
- vty_queued_result(vty, args->ret, (flag == mqb_action));
-
- if (qpthreads_enabled)
- qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
+//if (qpthreads_enabled)
+// qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
mqb_free(mqb);
}
+
+/*------------------------------------------------------------------------------
+ * Revoke any messages related to the given VTY
+ *
+ * Revokes in vty_cmd_nexus -- so before command is started
+ * and in vty_cli_nexus -- so after command has completed
+ *
+ * Can do nothing about any command actually being executed in the
+ * vty_cmd_nexus.
+ */
+void
+cq_revoke(struct vty *vty)
+{
+ mqueue_revoke(vty_cmd_nexus->queue, vty) ;
+ mqueue_revoke(vty_cli_nexus->queue, vty) ;
+}
+
diff --git a/lib/command_queue.h b/lib/command_queue.h
index 257c9552..804dfa1f 100644
--- a/lib/command_queue.h
+++ b/lib/command_queue.h
@@ -25,8 +25,7 @@
#include "command.h"
#include "qpnexus.h"
-extern void
-cq_enqueue(struct vty *vty, struct cmd_parsed* parsed, qpn_nexus to_nexus,
- qpn_nexus from_nexus) ;
+extern void cq_enqueue(struct vty *vty, qpn_nexus dst) ;
+extern void cq_revoke(struct vty* vty) ;
#endif /* COMMAND_QUEUE_H_ */
diff --git a/lib/keystroke.c b/lib/keystroke.c
index 09f0fed0..c6eb811e 100644
--- a/lib/keystroke.c
+++ b/lib/keystroke.c
@@ -19,7 +19,8 @@
* Boston, MA 02111-1307, USA.
*/
-#include "string.h"
+#include <stdbool.h>
+#include <string.h>
#include "keystroke.h"
@@ -41,7 +42,7 @@
*
* Handles:
*
- * 0. Null, returned as:
+ * 0. Nothing, returned as:
*
* type = ks_null
* value = knull_eof
@@ -52,7 +53,10 @@
* broken = false
* buf -- not used
*
- * This is returned when there is nothing else available.
+ * This is returned when there is nothing there.
+ *
+ * Note that this is NOT returned for NUL ('\0') characters. Those are
+ * real characters (whose value just happens to be null).
*
* 1. Characters, returned as:
*
@@ -67,6 +71,8 @@
* buf -- if OK, the representation for the character (UTF-8 ?)
* if truncated or broken, the raw bytes
*
+ * See notes below on the handling of '\r' and '\n'.
+ *
* 2. ESC X -- where X is single character, other than '['.
*
* Returned as:
@@ -147,6 +153,30 @@
*
* Extended Option objects (O = 0xFF) are exotic, but the above will
* parse them.
+ *
+ *------------------------------------------------------------------------------
+ * CR, LF and NUL
+ *
+ * Telnet requires CR LF newlines. Where a CR is to appear alone it must be
+ * followed by NUL.
+ *
+ * This code accepts:
+ *
+ * * CR LF pair, returning LF ('\n') -- discards CR
+ *
+ * * CR NUL pair, returning CR ('\r') -- discards NUL
+ *
+ * * CR CR pair, returning CR ('\r') == discards one CR (seems pointless)
+ *
+ * * CR XX pair, returning CR and XX -- where XX is anything other than
+ * CR, LF or NUL
+ *
+ * It is tempting to throw away all NUL characters... but that doesn't seem
+ * like a job for this level.
+ *
+ * As a small compromise, will not steal a NUL character.
+ *
+ * Note that NUL appears as a real character. ks_null means literally nothing.
*/
/*------------------------------------------------------------------------------
@@ -225,6 +255,7 @@ enum stream_state
kst_null, /* nothing special (but see iac) */
kst_char, /* collecting a multi-byte character */
+ kst_cr, /* collecting '\r''\0' or '\r''\n' */
kst_esc, /* collecting an ESC sequence */
kst_csi, /* collecting an ESC '[' or CSI sequence */
@@ -243,6 +274,9 @@ struct keystroke_stream
{
vio_fifo_t fifo ; /* the keystrokes */
+ keystroke_callback* iac_callback ;
+ void* iac_callback_context ;
+
uint8_t CSI ; /* CSI character value (if any) */
bool eof_met ; /* nothing more to come */
@@ -264,17 +298,19 @@ enum { keystroke_buffer_len = 2000 } ; /* should be plenty ! */
/*------------------------------------------------------------------------------
* Prototypes
*/
+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 uint8_t keystroke_get_byte(keystroke_stream stream) ;
-static inline void keystroke_add_raw(keystroke_stream stream, uint8_t u) ;
+inline static void keystroke_add_raw(keystroke_stream stream, uint8_t u) ;
static void keystroke_put_char(keystroke_stream stream, uint32_t u) ;
inline static void keystroke_put_esc(keystroke_stream stream, uint8_t u,
int len) ;
inline static void keystroke_put_csi(keystroke_stream stream, uint8_t u) ;
-inline static void keystroke_put_iac(keystroke_stream stream, uint8_t u,
- int len) ;
-inline static void keystroke_put_iac_long(keystroke_stream stream,
- int broken) ;
+static void keystroke_put_iac_one(keystroke_stream stream, uint8_t u);
+static void keystroke_put_iac_long(keystroke_stream stream, bool broken) ;
+static void keystroke_clear_iac(keystroke_stream stream) ;
static void keystroke_steal_char(keystroke steal, keystroke_stream stream,
uint8_t u) ;
static void keystroke_steal_esc(keystroke steal, keystroke_stream stream,
@@ -282,7 +318,7 @@ static void keystroke_steal_esc(keystroke steal, keystroke_stream stream,
static void keystroke_steal_csi(keystroke steal, keystroke_stream stream,
uint8_t u) ;
static void keystroke_put(keystroke_stream stream, enum keystroke_type type,
- int broken, uint8_t* p, int len) ;
+ bool broken, uint8_t* bytes, int len) ;
/*==============================================================================
* Creating and freeing keystroke streams and keystroke stream buffers.
@@ -292,9 +328,21 @@ static void keystroke_put(keystroke_stream stream, enum keystroke_type type,
* Create and initialise a keystroke stream.
*
* Can set CSI character value. '\0' => none. (As does '\x1B' !)
+ *
+ * The callback function is called when an IAC sequence is seen, the callback
+ * is:
+ *
+ * bool callback(void* context, keystroke stroke)
+ *
+ * see: #define keystroke_iac_callback_args
+ * and: typedef for keystroke_callback
+ *
+ * The callback must return true iff the IAC sequence has been dealt with, and
+ * should NOT be stored for later processing.
*/
extern keystroke_stream
-keystroke_stream_new(uint8_t csi_char)
+keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
+ void* iac_callback_context)
{
keystroke_stream stream ;
@@ -302,6 +350,9 @@ keystroke_stream_new(uint8_t csi_char)
/* Zeroising the structure sets:
*
+ * iac_callback = NULL -- none
+ * iac_callback_context = NULL -- none
+ *
* eof_met = false -- no EOF yet
* steal = false -- no stealing set
* iac = false -- last character was not an IAC
@@ -314,6 +365,9 @@ keystroke_stream_new(uint8_t csi_char)
*/
confirm(kst_null == 0) ;
+ stream->iac_callback = iac_callback ;
+ stream->iac_callback_context = iac_callback_context ;
+
vio_fifo_init_new(&stream->fifo, keystroke_buffer_len) ;
stream->CSI = (csi_char != '\0') ? csi_char : 0x1B ;
@@ -323,16 +377,20 @@ keystroke_stream_new(uint8_t csi_char)
/*------------------------------------------------------------------------------
* Free keystroke stream and all associated buffers.
+ *
+ * Returns NULL
*/
-extern void
+extern keystroke_stream
keystroke_stream_free(keystroke_stream stream)
{
- if (stream == NULL)
- return ;
+ if (stream != NULL)
+ {
+ vio_fifo_reset_keep(&stream->fifo) ;
- vio_fifo_reset_keep(&stream->fifo) ;
+ XFREE(MTYPE_KEY_STREAM, stream) ;
+ } ;
- XFREE(MTYPE_KEY_STREAM, stream) ;
+ return NULL ;
} ;
/*==============================================================================
@@ -349,7 +407,7 @@ keystroke_stream_free(keystroke_stream stream)
extern bool
keystroke_stream_empty(keystroke_stream stream)
{
- return vio_fifo_empty(&stream->fifo) ;
+ return (stream == NULL) || vio_fifo_empty(&stream->fifo) ;
} ;
/*------------------------------------------------------------------------------
@@ -370,7 +428,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 vio_fifo_empty(&stream->fifo) && stream->eof_met ;
+ return (stream == NULL) || (vio_fifo_empty(&stream->fifo) && stream->eof_met);
} ;
/*------------------------------------------------------------------------------
@@ -385,15 +443,14 @@ keystroke_stream_set_eof(keystroke_stream stream)
{
vio_fifo_reset_keep(&stream->fifo) ;
- stream->eof_met = 1 ; /* essential information */
+ stream->eof_met = true ; /* essential information */
- stream->steal_this = 0 ; /* keep tidy */
- stream->iac = 0 ;
+ stream->steal_this = false ; /* keep tidy */
+ stream->iac = false ;
stream->in.state = kst_null ;
stream->pushed_in.state = kst_null ;
} ;
-
/*==============================================================================
* Input raw bytes to given keyboard stream.
*
@@ -437,42 +494,71 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
*/
if ((len == 0) && (ptr == NULL))
{
- stream->eof_met = 1 ;
- stream->steal_this = 0 ;
+ stream->eof_met = true ;
+ stream->steal_this = false ;
- if (stream->iac && (stream->in.state == kst_null))
- keystroke_put_iac(stream, '\0', 0) ;
-
- /* Uses a while loop here to deal with partial IAC which has interrupted
- * a partial escape or other sequence.
+ /* Loop to deal with any pending IAC and partial keystroke.
+ *
+ * An IAC in the middle of a real keystroke sequence appears before
+ * it. Do the same here, even with broken sequences.
+ *
+ * A partial IAC sequence may have pushed a partial real keystroke
+ * sequence -- so loop until have dealt with that.
*/
- while (stream->in.state != kst_null)
+ do
{
switch (stream->in.state)
{
+ case kst_null: /* not expecting anything, unless iac */
+ keystroke_clear_iac(stream) ;
+ break ;
+
+ case kst_cr: /* expecting something after CR */
+ keystroke_clear_iac(stream) ;
+
+ stream->in.len = 0 ;
+ keystroke_add_raw(stream, '\r') ;
+ keystroke_put(stream, ks_char, true,
+ stream->in.raw, stream->in.len) ;
+ break ;
+
case kst_esc: /* expecting rest of escape */
+ keystroke_clear_iac(stream) ;
+
keystroke_put_esc(stream, '\0', 0) ;
- stream->in.state = kst_null ;
break ;
case kst_csi:
+ keystroke_clear_iac(stream) ;
+
keystroke_put_csi(stream, '\0') ;
- stream->in.state = kst_null ;
break ;
case kst_iac_option: /* expecting rest of IAC */
+ assert(!stream->iac) ;
+ /* fall through */
case kst_iac_sub:
- keystroke_put_iac_long(stream, 1) ;
- /* pops the stream->pushed_in */
+ keystroke_put_iac_long(stream, true) ;
+
+ /* For kst_iac_sub, an incomplete IAC could be anything, so
+ * don't include in the broken IAC, but don't lose it
+ * either.
+ */
+ keystroke_clear_iac(stream) ;
break ;
- case kst_char: /* TBD */
+ case kst_char: /* TBD */
zabort("impossible keystroke stream state") ;
default:
zabort("unknown keystroke stream state") ;
} ;
- } ;
+
+ assert(!stream->iac) ; /* must have dealt with this */
+
+ keystroke_in_pop(stream) ; /* pops kst_null, when all done */
+
+ } while (stream->in.state != kst_null) ;
} ;
/* Update the stealing state
@@ -492,7 +578,7 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
* keystroke_input(), while still wish to steal.
*/
if (steal == NULL)
- stream->steal_this = 0 ; /* clear as not now required */
+ stream->steal_this = false; /* not now required */
else
stream->steal_this = (stream->in.state == kst_null) ;
/* want to and can can steal the next
@@ -518,15 +604,25 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
/* IAC handling takes precedence over everything, except the <option>
* byte, which may be EXOPL, which happens to be 255 as well !
+ *
+ * stream->iac means that the last thing seen was an IAC.
+ *
+ * First IAC sets the flag. On the next byte:
+ *
+ * * if is IAC, clears stream->iac, and lets the escaped IAC value
+ * through for further processing -- NOT in IAC state.
+ *
+ * * if is not IAC, will be let through for further processing, in
+ * IAC state.
*/
if ((u == tn_IAC) && (stream->in.state != kst_iac_option))
{
if (stream->iac)
- stream->iac = 0 ; /* IAC IAC => single IAC byte value */
+ stream->iac = false ; /* IAC IAC => single IAC byte value */
else
{
- stream->iac = 1 ; /* seen an IAC */
- continue ; /* wait for next character */
+ stream->iac = true ; /* seen an IAC */
+ continue ; /* wait for next character */
} ;
} ;
@@ -543,24 +639,23 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
*/
if (stream->iac)
{
- stream->iac = 0 ; /* assume will eat the IAC XX */
+ stream->iac = false ; /* expect will eat the IAC XX */
switch (stream->in.state)
{
case kst_null:
+ case kst_cr:
case kst_esc:
case kst_csi:
if (u < tn_SB)
- keystroke_put_iac(stream, u, 1) ;
+ /* This is a simple IAC XX, one byte IAC */
+ keystroke_put_iac_one(stream, u) ;
else
- {
- stream->pushed_in = stream->in ;
-
- stream->in.len = 1 ;
- stream->in.raw[0] = u ;
-
- stream->in.state = kst_iac_option ;
- }
+ /* This is a multi-byte IAC, so push whatever real
+ * keystroke sequence is currently on preparation, and
+ * set into kst_iac_option state.
+ */
+ keystroke_in_push(stream, u) ;
break ;
case kst_iac_sub:
@@ -569,11 +664,11 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
if (u != tn_SE)
{
--ptr ; /* put back the XX */
- stream->iac = 1 ; /* put back the IAC */
+ stream->iac = true ; /* put back the IAC */
} ;
keystroke_put_iac_long(stream, (u != tn_SE)) ;
- /* pops the stream->pushed_in */
+ keystroke_in_pop(stream) ;
break ;
case kst_char: /* TBD */
@@ -593,7 +688,9 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
case kst_null: /* Expecting anything */
stream->steal_this = (steal != NULL) ;
- if (u == 0x1B)
+ if (u == '\r')
+ stream->in.state = kst_cr ;
+ else if (u == 0x1B)
stream->in.state = kst_esc ;
else if (u == stream->CSI) /* NB: CSI == 0x1B => no CSI */
{
@@ -602,12 +699,13 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
}
else
{
- if (!stream->steal_this)
+ /* Won't steal NUL */
+ if (!stream->steal_this || (u == '\0'))
keystroke_put_char(stream, u) ;
else
{
keystroke_steal_char(steal, stream, u) ;
- stream->steal_this = 0 ;
+ stream->steal_this = false ;
steal = NULL ;
} ;
@@ -615,9 +713,33 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
} ;
break ;
- case kst_char: /* TBD */
+ case kst_char: /* TBD */
zabort("impossible keystroke stream state") ;
+ case kst_cr: /* expecting something after CR */
+ if ((u != '\n') && (u != '\r'))
+ {
+ if (u != '\0')
+ {
+ --ptr ; /* put back the duff XX */
+ stream->iac = (u == tn_IAC) ;
+ /* re=escape if is IAC */
+ } ;
+ u = '\r' ;
+ } ;
+
+ if (!stream->steal_this)
+ keystroke_put_char(stream, u) ;
+ else
+ {
+ keystroke_steal_char(steal, stream, u) ;
+ stream->steal_this = false ;
+ steal = NULL ;
+ } ;
+
+ stream->in.state = kst_null ;
+ break ;
+
case kst_esc: /* Expecting XX after ESC */
if (u == '[')
{
@@ -631,7 +753,7 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
else
{
keystroke_steal_esc(steal, stream, u) ;
- stream->steal_this = 0 ;
+ stream->steal_this = false ;
steal = NULL ;
} ;
@@ -644,32 +766,26 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
keystroke_add_raw(stream, u) ;
else
{
- int ok = 1 ;
- int l ;
+ bool ok ;
- if ((u < 0x40) || (u > 0x7F))
+ ok = stream->in.len < keystroke_max_len ;
+ /* have room for terminator */
+
+ if ((u < 0x40) || (u > 0x7E))
{
--ptr ; /* put back the duff XX */
stream->iac = (u == tn_IAC) ;
/* re=escape if is IAC */
u = '\0' ;
- ok = 0 ; /* broken */
- } ;
-
- l = stream->in.len++ ;
- if (l >= keystroke_max_len)
- {
- l = keystroke_max_len - 1 ;
- ok = 0 ; /* truncated */
+ ok = false ; /* broken */
} ;
- stream->in.raw[l] = u ; /* plant terminator */
if (!stream->steal_this || !ok)
keystroke_put_csi(stream, u) ;
else
{
keystroke_steal_csi(steal, stream, u) ;
- stream->steal_this = 0 ;
+ stream->steal_this = false ;
steal = NULL ;
} ;
stream->in.state = kst_null ;
@@ -680,11 +796,13 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
assert(stream->in.len == 1) ;
keystroke_add_raw(stream, u) ;
- if (stream->in.raw[0]== tn_SB)
+ if (stream->in.raw[0] == tn_SB)
stream->in.state = kst_iac_sub ;
else
- keystroke_put_iac_long(stream, 0) ;
- /* pops the stream->pushed_in */
+ {
+ keystroke_put_iac_long(stream, false) ;
+ keystroke_in_pop(stream) ;
+ } ;
break ;
case kst_iac_sub: /* Expecting sub stuff */
@@ -706,6 +824,37 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
keystroke_set_null(stream, steal) ;
} ;
+/*------------------------------------------------------------------------------
+ * Single level stack for keystroke input state, so that can handle IAC
+ * sequences transparently.
+ */
+
+/* Push current state and set new current state for start of IAC option
+ * sequence.
+ */
+static void
+keystroke_in_push(keystroke_stream stream, uint8_t u)
+{
+ assert(stream->pushed_in.state == kst_null) ;
+
+ stream->pushed_in = stream->in ;
+
+ stream->in.len = 1 ;
+ stream->in.raw[0] = u ;
+
+ stream->in.state = kst_iac_option ;
+} ;
+
+/* Pop the pushed state and clear the pushed state to kst_null */
+static void
+keystroke_in_pop(keystroke_stream stream)
+{
+ stream->in = stream->pushed_in ;
+
+ stream->pushed_in.state = kst_null ;
+ stream->pushed_in.len = 0 ;
+} ;
+
/*==============================================================================
* Fetch next keystroke from keystroke stream
*
@@ -742,9 +891,11 @@ keystroke_get(keystroke_stream stream, keystroke stroke)
stroke->type = b & kf_type_mask ;
stroke->value = 0 ;
- stroke->flags = b & (kf_broken | kf_truncated) ;
+ stroke->flags = b & kf_flag_mask ;
stroke->len = keystroke_get_byte(stream) ;
+ assert(stroke->len <= keystroke_max_len) ;
+
/* Fetch what we need to the stroke buffer */
p = stroke->buf ;
e = p + stroke->len ;
@@ -884,7 +1035,7 @@ keystroke_put_char(keystroke_stream stream, uint32_t u)
/*------------------------------------------------------------------------------
* Store simple ESC. Is broken if length (after ESC) == 0 !
*/
-inline static void
+static void
keystroke_put_esc(keystroke_stream stream, uint8_t u, int len)
{
keystroke_put(stream, ks_esc, (len == 0), &u, len) ;
@@ -894,46 +1045,94 @@ keystroke_put_esc(keystroke_stream stream, uint8_t u, int len)
* Store CSI.
*
* Plants the last character of the CSI in the buffer, even if has to overwrite
- * the existing last character -- the sequence is broken in any case, but this
- * way at least we have the end of the sequence.
+ * the existing last character -- the sequence is truncated, but this way at
+ * least the end of the sequence is preserved.
*
* Is broken if u == '\0'. May also be truncated !
*/
-inline static void
+static void
keystroke_put_csi(keystroke_stream stream, uint8_t u)
{
+ int l ;
+
+ l = stream->in.len++ ;
+
+ if (l >= keystroke_max_len)
+ l = keystroke_max_len - 1 ;
+
+ stream->in.raw[l] = u ; /* plant terminator */
+
keystroke_put(stream, ks_csi, (u == '\0'), stream->in.raw, stream->in.len) ;
} ;
/*------------------------------------------------------------------------------
- * Store simple IAC. Is broken (EOF met) if length (after IAC) == 0
+ * Store IAC -- if not broken, send it via any call-back.
*/
-inline static void
-keystroke_put_iac(keystroke_stream stream, uint8_t u, int len)
+static void
+keystroke_put_iac(keystroke_stream stream, bool broken, uint8_t* bytes, int len)
{
- keystroke_put(stream, ks_iac, (len == 0), &u, len) ;
+ bool dealt_with = false ;
+
+ if (!broken && (stream->iac_callback != NULL))
+ {
+ struct keystroke stroke ;
+
+ assert((len >= 1) && (bytes != NULL) && (len <= keystroke_max_len)) ;
+
+ stroke.type = ks_iac ;
+ stroke.value = bytes[0] ;
+ stroke.flags = 0 ;
+ stroke.len = len ;
+
+ memcpy(&stroke.buf, bytes, len) ;
+
+ dealt_with = (*stream->iac_callback)(stream->iac_callback_context,
+ &stroke) ;
+ } ;
+
+ if (!dealt_with)
+ keystroke_put(stream, ks_iac, broken, bytes, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store one byte IAC.
+ */
+static void
+keystroke_put_iac_one(keystroke_stream stream, uint8_t u)
+{
+ keystroke_put_iac(stream, false, &u, 1) ;
} ;
/*------------------------------------------------------------------------------
* Store long IAC. Is broken if says it is.
- *
- * Pops the stream->pushed_in
*/
-inline static void
-keystroke_put_iac_long(keystroke_stream stream, int broken)
+static void
+keystroke_put_iac_long(keystroke_stream stream, bool broken)
{
- keystroke_put(stream, ks_iac, broken, stream->in.raw, stream->in.len) ;
+ keystroke_put_iac(stream, broken, stream->in.raw, stream->in.len) ;
+} ;
- stream->in = stream->pushed_in ;
- stream->pushed_in.state = kst_null ;
+/*------------------------------------------------------------------------------
+ * If in IAC state, issue broken IAC and clear state.
+ */
+static void
+keystroke_clear_iac(keystroke_stream stream)
+{
+ if (stream->iac)
+ {
+ keystroke_put_iac(stream, true, NULL, 0) ;
+ stream->iac = 0 ;
+ } ;
} ;
/*------------------------------------------------------------------------------
* Store <first> <len> [<bytes>]
+ *
+ * If len == 0, bytes may be NULL
*/
static void
-keystroke_put(keystroke_stream stream, enum keystroke_type type, int broken,
- uint8_t* p, int len)
+keystroke_put(keystroke_stream stream, enum keystroke_type type, bool broken,
+ uint8_t* bytes, int len)
{
if (len > keystroke_max_len)
{
@@ -946,7 +1145,7 @@ keystroke_put(keystroke_stream stream, enum keystroke_type type, int broken,
vio_fifo_put_byte(&stream->fifo, len) ;
if (len > 0)
- vio_fifo_put(&stream->fifo, (void*)p, len) ;
+ vio_fifo_put(&stream->fifo, (void*)bytes, len) ;
} ;
/*------------------------------------------------------------------------------
@@ -976,30 +1175,28 @@ keystroke_steal_esc(keystroke steal, keystroke_stream stream, uint8_t u)
} ;
/*------------------------------------------------------------------------------
- * Steal CSI escape.
+ * Steal CSI escape -- cannot be broken or truncated.
*
* In the stream-in.raw buffer the last character is the escape terminator,
* after the escape parameters.
*
* In keystroke buffer the escape parameters are '\0' terminated, and the
* escape terminator is the keystroke value.
- *
- * Does not steal broken or truncated stuff.
*/
static void
keystroke_steal_csi(keystroke steal, keystroke_stream stream, uint8_t u)
{
int len ;
- len = stream->in.len ; /* includes the escape terminator */
- assert(len <= keystroke_max_len) ;
+ len = stream->in.len ; /* excludes the escape terminator */
+ assert((len < keystroke_max_len) && (u >= 0x40) && (u <= 0x7E)) ;
steal->type = ks_esc ;
steal->value = u ;
steal->flags = 0 ;
- steal->len = len - 1 ;
+ steal->len = len ;
- memcpy(steal->buf, stream->in.raw, len - 1) ;
+ memcpy(steal->buf, stream->in.raw, len) ;
steal->buf[len] = '\0' ;
} ;
diff --git a/lib/keystroke.h b/lib/keystroke.h
index 4dc94d12..2b1d4d93 100644
--- a/lib/keystroke.h
+++ b/lib/keystroke.h
@@ -64,11 +64,14 @@ enum keystroke_null
enum keystroke_flags
{
kf_compound = 0x80, /* marker on all compound characters */
+
kf_reserved = 0x40,
kf_broken = 0x20, /* badly formed in some way */
kf_truncated = 0x10, /* too big for buffer ! */
/* for ks_null => EOF */
+ kf_flag_mask = 0x70, /* flags for the keystroke */
+
kf_type_mask = 0x0F, /* extraction of type */
} ;
@@ -80,7 +83,7 @@ typedef struct keystroke_stream* keystroke_stream ;
struct keystroke
{
enum keystroke_type type ;
- uint8_t flags ;
+ uint8_t flags ; /* the kf_flag_mask flags */
uint32_t value ;
@@ -88,6 +91,9 @@ struct keystroke
uint8_t buf[keystroke_max_len] ;
} ;
+#define keystroke_iac_callback_args void* context, keystroke stroke
+typedef bool (keystroke_callback)(keystroke_iac_callback_args) ;
+
/* Telnet commands/options */
enum tn_Command
{
@@ -165,12 +171,13 @@ enum tn_Option
* Functions
*/
extern keystroke_stream
-keystroke_stream_new(uint8_t csi_char) ;
+keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
+ void* iac_callback_context) ;
extern void
keystroke_stream_set_eof(keystroke_stream stream) ;
-extern void
+extern keystroke_stream
keystroke_stream_free(keystroke_stream stream) ;
extern bool
diff --git a/lib/list_util.h b/lib/list_util.h
index b658c7ce..10ba8b0c 100644
--- a/lib/list_util.h
+++ b/lib/list_util.h
@@ -87,7 +87,7 @@
* struct item
* {
* ....
- * struct list_pair(struct item*) foo_list ;
+ * struct dl_list_pair(struct item*) foo_list ;
* ....
* } ;
*
@@ -99,15 +99,15 @@
*
* A double base may be declared:
*
- * struct base_pair(struct item*) foo_base ;
+ * struct dl_base_pair(struct item*) foo_base ;
*
* Various ways to construct structures or structure types:
*
- * typedef struct list_pair(struct foo*) foo_list ;
+ * typedef struct dl_list_pair(struct foo*) foo_list ;
*
- * struct foo_list list_pair(struct foo*) ;
+ * struct foo_list dl_list_pair(struct foo*) ;
*
- * struct foo_base base_pair(struct foo*) ;
+ * struct foo_base dl_base_pair(struct foo*) ;
*/
#define dl_list_pair(ptr_t) { ptr_t next ; ptr_t prev ; }
diff --git a/lib/log.c b/lib/log.c
index 3db6fa81..3e5c80ad 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -37,6 +37,7 @@
#include <ucontext.h>
#endif
#include "qpthreads.h"
+#include "qfstring.h"
/* log is protected by the same mutext as vty, see comments in vty.c */
@@ -89,6 +90,8 @@ const char *zlog_priority[] =
* with any number of decimal digits, but at most 6 will be significant.
*/
+static void uquagga_timestamp(qf_str qfs, int timestamp_precision) ;
+
/*------------------------------------------------------------------------------
* Fill buffer with current time, to given number of decimal digits.
*
@@ -100,21 +103,25 @@ const char *zlog_priority[] =
*
* NB: buflen MUST be > 1 and buf MUST NOT be NULL.
*/
-size_t
+extern size_t
quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
{
- size_t result;
+ qf_str_t qfs ;
+
VTY_LOCK() ;
- result = uquagga_timestamp(timestamp_precision, buf, buflen);
+
+ qfs_init(&qfs, buf, buflen) ;
+ uquagga_timestamp(&qfs, timestamp_precision) ;
+
VTY_UNLOCK() ;
- return result;
+ return qfs_len(&qfs) ;
}
/*------------------------------------------------------------------------------
* unprotected version for when mutex already held
*/
-size_t
-uquagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
+static void
+uquagga_timestamp(qf_str qfs, int timestamp_precision)
{
static struct {
time_t last;
@@ -124,11 +131,6 @@ uquagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
struct timeval clock;
- size_t len ;
- int left ;
-
- assert((buflen > 1) && (buf != NULL)) ;
-
/* would it be sufficient to use global 'recent_time' here? I fear not... */
gettimeofday(&clock, NULL);
@@ -138,61 +140,36 @@ uquagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
struct tm tm;
cache.last = clock.tv_sec;
localtime_r(&cache.last, &tm);
- cache.len = strftime(cache.buf, sizeof(cache.buf),
- "%Y/%m/%d %H:%M:%S", &tm) ;
+ cache.len = strftime(cache.buf, sizeof(cache.buf), TIMESTAMP_FORM, &tm) ;
assert(cache.len > 0) ;
}
- /* note: it's not worth caching the subsecond part, because
- chances are that back-to-back calls are not sufficiently close together
- for the clock not to have ticked forward */
-
- len = cache.len ; /* NB: asserted cache.len > 0 */
- left = (buflen - (len + 1)) ; /* what would be left */
- if (left < 0)
- len = buflen - 1 ; /* NB: asserted buflen > 1 */
+ /* note: it's not worth caching the subsecond part, because
+ * chances are that back-to-back calls are not sufficiently close together
+ * for the clock not to have ticked forward
+ */
- memcpy(buf, cache.buf, len) ;
+ qfs_append_n(qfs, cache.buf, cache.len) ;
- /* Can do decimal part if there is room for the '.' character */
- if ((timestamp_precision > 0) && (left > 0))
+ /* Add decimal part as required. */
+ if (timestamp_precision > 0)
{
/* should we worry about locale issues? */
static const int divisor[] = { 1, /* 0 */
100000, 10000, 1000, /* 1, 2, 3 */
100, 10, 1}; /* 4, 5, 6 */
int prec;
- char *p ;
prec = timestamp_precision ;
- if ((1 + prec) > left)
- prec = left - 1 ; /* NB: left > 0 */
- len += 1 + prec ;
-
- p = buf + prec ; /* point at last decimal digit */
-
- while (prec > 6)
- /* this is unlikely to happen, but protect anyway */
- {
- *p-- = '0';
- --prec ;
- } ;
+ if (prec > 6)
+ prec = 6 ;
- clock.tv_usec /= divisor[prec];
+ qfs_append_n(qfs, ".", 1) ;
+ qfs_unsigned(qfs, clock.tv_usec / divisor[prec], 0, 0, prec) ;
- while (prec > 0) /* could have been reduced to 0 */
- {
- *p-- = '0'+(clock.tv_usec % 10);
- clock.tv_usec /= 10;
- --prec ;
- } ;
-
- *p = '.';
+ if (prec < timestamp_precision)
+ qfs_append_ch_x_n(qfs, '0', timestamp_precision - prec) ;
} ;
-
- buf[len] = '\0';
-
- return len ;
} ;
/*==============================================================================
@@ -223,7 +200,7 @@ uvzlog (struct zlog *zl, int priority, const char *format, va_list va)
/* When zlog_default is also NULL, use stderr for logging. */
if (zl == NULL)
{
- uvzlog_line(&ll, zl, priority, format, va, 0) ;
+ uvzlog_line(&ll, zl, priority, format, va, llt_lf) ;
write(fileno(stderr), ll.line, ll.len) ;
}
else
@@ -240,14 +217,14 @@ uvzlog (struct zlog *zl, int priority, const char *format, va_list va)
/* File output. */
if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
{
- uvzlog_line(&ll, zl, priority, format, va, 0) ;
+ uvzlog_line(&ll, zl, priority, format, va, llt_lf) ;
write(fileno(zl->fp), ll.line, ll.len) ;
}
/* stdout output. */
if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
{
- uvzlog_line(&ll, zl, priority, format, va, 0) ;
+ uvzlog_line(&ll, zl, priority, format, va, llt_lf) ;
write(fileno(zl->fp), ll.line, ll.len) ;
}
@@ -264,54 +241,37 @@ uvzlog (struct zlog *zl, int priority, const char *format, va_list va)
*/
extern void
uvzlog_line(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va, int crlf)
+ const char *format, va_list va, enum ll_term term)
{
- char* p ;
+ char* p ;
+ const char* q ;
p = ll->p_nl ;
if (p != NULL)
{
/* we have the line -- just need to worry about the crlf state */
- if ((crlf && ll->crlf) || (!crlf && !ll->crlf))
+ if (term == ll->term)
return ; /* exit here if all set */
}
else
{
/* must construct the line */
- const char* q ;
- char* e ;
- size_t len ;
+ qf_str_t qfs ;
va_list vac ;
- p = ll->line = ll->buf ;
- e = p + sizeof(ll->buf) - 3 ; /* leave space for '\r', '\n' and '\0' */
-
- /* "<time stamp> " */
- len = uquagga_timestamp((zl != NULL) ? zl->timestamp_precision : 0,
- p, e - p) ;
- p += len ; /* len guaranteed to be <= e - p */
+ qfs_init(&qfs, ll->line, sizeof(ll->line) - 2) ;
+ /* leave space for '\n' or '\r''\n' */
+ /* "<time stamp>" */
+ uquagga_timestamp(&qfs, (zl != NULL) ? zl->timestamp_precision : 0) ;
- if (p < e)
- *p++ = ' ' ;
+ qfs_append_n(&qfs, " ", 1) ;
/* "<priority>: " if required */
if ((zl != NULL) && (zl->record_priority))
{
- q = zlog_priority[priority] ;
- len = strlen(q) ;
-
- if ((p + len) > e)
- len = e - p ;
-
- if (len > 0)
- memcpy(p, q, len) ;
- p += len ;
-
- if (p < e)
- *p++ = ':' ;
- if (p < e)
- *p++ = ' ' ;
+ qfs_append(&qfs, zlog_priority[priority]) ;
+ qfs_append(&qfs, ": ") ;
} ;
/* "<protocol>: " or "unknown: " */
@@ -320,48 +280,29 @@ uvzlog_line(struct logline* ll, struct zlog *zl, int priority,
else
q = "unknown" ;
- len = strlen(q) ;
-
- if ((p + len) > e)
- len = e - p ;
-
- if (len > 0)
- memcpy(p, q, len) ;
- p += len ;
-
- if (p < e)
- *p++ = ':' ;
- if (p < e)
- *p++ = ' ' ;
+ qfs_append(&qfs, q) ;
+ qfs_append(&qfs, ": ") ;
/* Now the log line itself */
- /* Have reserved space for '\n', so have (e - p + 1) of buffer */
- if (p < e)
- {
- va_copy(vac, va);
- len = vsnprintf(p, (e - p + 1), format, vac) ;
- va_end(vac);
-
- p += len ; /* len returned is *required* length */
+ va_copy(vac, va);
+ qfs_vprintf(&qfs, format, vac) ;
+ va_end(vac);
- if (p > e)
- p = e ; /* actual end */
- } ;
-
- ll->p_nl = p ; /* set end pointer */
-
- assert(p <= e) ;
+ /* Set pointer to where the '\0' is. */
+ p = ll->p_nl = qfs_end(&qfs) ;
} ;
/* finish off with '\r''\n''\0' or '\n''\0' as required */
- if (crlf)
+ if (term == llt_crlf)
*p++ = '\r' ;
- *p++ = '\n' ;
+ if (term != llt_nul)
+ *p++ = '\n' ;
+
*p = '\0' ;
ll->len = p - ll->line ;
- ll->crlf = crlf ;
+ ll->term = term ;
} ;
/*============================================================================*/
diff --git a/lib/log.h b/lib/log.h
index b2934ac3..1894aff2 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -228,9 +228,6 @@ enum { timestamp_buffer_len = 32 } ;
extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */,
char *buf, size_t buflen);
-/* unprotected version for when mutex already held */
-extern size_t uquagga_timestamp(int timestamp_precision /* # subsecond digits */,
- char *buf, size_t buflen);
/* Generate line to be logged
*
@@ -241,24 +238,31 @@ extern size_t uquagga_timestamp(int timestamp_precision /* # subsecond digits */
* or '\r''\n''\0'). Do not wish to malloc any larger buffer while logging.
*/
enum { logline_buffer_len = 1008 } ;
+enum ll_term
+{
+ llt_nul = 0, /* NB: also length of the terminator */
+ llt_lf = 1,
+ llt_crlf = 2,
+} ;
+
struct logline {
- char* p_nl ; /* address of the first byte of "\n" or "\r\n" */
- /* NULL => not filled in yet */
+ char* p_nl ; /* address of the terminator */
+
+ enum ll_term term ; /* how line is terminated */
- char* line ; /* address of the buffered line */
- size_t len ; /* length including either '\r''\n' or '\n' */
- int crlf ; /* true if terminated by "\r\n" */
+ size_t len ; /* length including either '\r''\n' or '\n' */
- char buf[logline_buffer_len]; /* buffer */
+ char line[logline_buffer_len]; /* buffer */
} ;
extern void
uvzlog_line(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va, int crlf) ;
+ const char *format, va_list va, enum ll_term term) ;
/* Defines for use in command construction: */
-#define LOG_LEVELS "(emergencies|alerts|critical|errors|warnings|notifications|informational|debugging)"
+#define LOG_LEVELS "(emergencies|alerts|critical|errors|" \
+ "warnings|notifications|informational|debugging)"
#define LOG_LEVEL_DESC \
"System is unusable\n" \
diff --git a/lib/mem_tracker.c b/lib/mem_tracker.c
index a7ee430f..fa967476 100644
--- a/lib/mem_tracker.c
+++ b/lib/mem_tracker.c
@@ -286,6 +286,9 @@ mem_md_free(enum MTYPE mtype, void* address)
mem_descriptor md, prev_md ;
md_index this, next ;
+ if (address == NULL)
+ return ;
+
base = mem_md_base(address) ;
prev_md = NULL ;
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 4ed5cbc0..d0cb1eee 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -15,7 +15,7 @@
struct memory_list memory_list_lib[] =
{
{ MTYPE_TMP, "Temporary memory" },
- { MTYPE_STRVEC, "String vector" },
+ { MTYPE_STRVEC, "String vector" },
{ MTYPE_VECTOR, "Vector structure" },
{ MTYPE_VECTOR_BODY, "Vector body" },
{ MTYPE_SYMBOL_TABLE, "Symbol Table structure" },
@@ -41,17 +41,21 @@ struct memory_list memory_list_lib[] =
{ MTYPE_QTIMER_PILE, "qtimer pile structure" },
{ MTYPE_QTIMER, "qtimer timer" },
{ MTYPE_QPN_NEXUS, "qtn nexus" },
- { MTYPE_MARSHAL, "marshalled commands" },
{ MTYPE_TSD, "Thread specific data" },
{ MTYPE_VTY, "VTY" },
+ { MTYPE_CMD_PARSED, "Parsed command" },
+ { 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_VIO_FIFO, "VTY IO FIFO" },
- { MTYPE_VIO_FIFO_LUMP, "VTY IO FIFO LUMP" },
+ { MTYPE_VIO_FIFO_LUMP, "VTY IO FIFO Lump" },
+ { MTYPE_VIO_LC, "VTY IO Line Control" },
{ MTYPE_QSTRING, "qstring structure" },
{ MTYPE_QSTRING_BODY, "qstring body" },
+ { MTYPE_QIOVEC, "qiovec structure" },
+ { MTYPE_QIOVEC_VEC, "qiovec iovec vector" },
{ MTYPE_IF, "Interface" },
{ MTYPE_CONNECTED, "Connected" },
{ MTYPE_CONNECTED_LABEL, "Connected interface label" },
diff --git a/lib/mqueue.c b/lib/mqueue.c
index 8b557dfe..90e1616c 100644
--- a/lib/mqueue.c
+++ b/lib/mqueue.c
@@ -178,6 +178,7 @@ mqueue_finish(void)
while ((mqb = mqb_free_list) != NULL)
{
assert(mqb_free_count != 0) ;
+ mqb_free_count-- ;
mqb_free_list = mqb->next ;
XFREE(MTYPE_MQUEUE_BLOCK, mqb) ;
} ;
@@ -526,12 +527,17 @@ static void mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig) ;
* for a signal type message queue, each message that arrives will kick one
* waiter.
*
+ * If mq is NULL, the message is not queued but is immediately destroyed.
+ *
* NB: this works perfectly well if !qpthreads enabled. Of course, there can
* never be any waiters... so no kicking is ever done.
*/
extern void
mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority)
{
+ if (mq == NULL)
+ return mqb_dispatch_destroy(mqb) ;
+
qpt_mutex_lock(&mq->mutex) ;
if (mq->head == NULL)
@@ -634,6 +640,8 @@ mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority)
* NB: the argument is ignored if !wait or !qpthreads_enabled, so may be NULL.
*
* Returns a message block if one is available. (And not otherwise.)
+ *
+ * NB: if mq is NULL, returns NULL -- nothing available
*/
extern mqueue_block
mqueue_dequeue(mqueue_queue mq, int wait, void* arg)
@@ -644,6 +652,9 @@ mqueue_dequeue(mqueue_queue mq, int wait, void* arg)
mqueue_thread_signal mtsig ;
qtime_mono_t timeout_time ;
+ if (mq == NULL)
+ return NULL ;
+
qpt_mutex_lock(&mq->mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
while (1)
@@ -748,6 +759,8 @@ done:
* 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.
+ *
+ * If mq is NULL, does nothing.
*/
extern void
mqueue_revoke(mqueue_queue mq, void* arg0)
@@ -755,6 +768,9 @@ mqueue_revoke(mqueue_queue mq, void* arg0)
mqueue_block mqb ;
mqueue_block prev ;
+ if (mq == NULL)
+ return ;
+
qpt_mutex_lock(&mq->mutex) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
prev = NULL ;
diff --git a/lib/qfstring.c b/lib/qfstring.c
new file mode 100644
index 00000000..30ee441c
--- /dev/null
+++ b/lib/qfstring.c
@@ -0,0 +1,1066 @@
+/* Some string handling
+ * Copyright (C) 2010 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.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "qfstring.h"
+
+/*==============================================================================
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise qf_str -- to given size (which includes the '\n')
+ *
+ * Sets pointers and terminates an empty string with one byte reserved for the
+ * terminating '\n'.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_init(qf_str qfs, char* str, size_t size)
+{
+ assert(size > 0) ;
+
+ qfs->str = qfs->ptr = str ;
+ qfs->end = qfs->str + size - 1 ;
+
+ *str = '\0' ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Terminate string with the given string.
+ *
+ * If necessary, characters are discarded from the end of the string in order
+ * to fit in the terminating stuff.
+ *
+ * If the terminating stuff won't fit, as much of the end if the terminating
+ * stuff as possible is copied to the string -- displacing any existing
+ * contents.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_term(qf_str qfs, const char* src)
+{
+ int len ;
+ int excess ;
+
+ if ((src == NULL) || (*src == '\0'))
+ {
+ *qfs->ptr = '\0' ; /* should be true anyway */
+ return ;
+ } ;
+
+ len = strlen(src) ;
+ excess = qfs_len(qfs) - len ;
+ if (excess > 0)
+ {
+ if (excess <= (qfs->ptr - qfs->str))
+ qfs->ptr -= excess ;
+ else
+ {
+ int want = len ;
+ len = qfs->end - qfs->str ; /* take what can... */
+ src += (want - len) ; /* ... from the end */
+ qfs->ptr = qfs->str ;
+ } ;
+ } ;
+
+ memcpy(qfs->ptr, src, len + 1) ; /* include the '\0' */
+ qfs->ptr += len ;
+} ;
+
+/*==============================================================================
+ * Appending to the string
+ */
+
+/*------------------------------------------------------------------------------
+ * Append as much as possible of the source string to the given qf_str.
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append(qf_str qfs, const char* src)
+{
+ int n ;
+
+ if ((src == NULL) || (*src == '\0'))
+ return ;
+
+ n = strlen(src) ;
+
+ if (n > qfs_left(qfs))
+ n = qfs_left(qfs) ;
+
+ if (n == 0)
+ return ;
+
+ memcpy(qfs->ptr, src, n + 1) ;
+ qfs->ptr += n ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append as much as possible of the first 'n' bytes of the source string to
+ * the given qf_str.
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append_n(qf_str qfs, const char* src, size_t n)
+{
+ if ((int)n > qfs_left(qfs))
+ n = qfs_left(qfs) ;
+
+ if (n <= 0)
+ return ;
+
+ memcpy(qfs->ptr, src, n) ;
+ qfs->ptr += n ;
+
+ *qfs->ptr = '\0' ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append upto 'n' copies of the given character to the qf_str
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append_ch_x_n(qf_str qfs, char ch, size_t n)
+{
+ if ((int)n > qfs_left(qfs))
+ n = qfs_left(qfs) ;
+
+ if (n <= 0)
+ return ;
+
+ while (n--)
+ *qfs->ptr++ = ch ;
+
+ *qfs->ptr = '\0' ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append as much as possible of the source string to the given qf_str, left or
+ * right justified to the given width.
+ *
+ * Ignores the width if the string is longer than it.
+ *
+ * Negative width => left justify.
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append_justified(qf_str qfs, const char* src, int width)
+{
+ size_t n ;
+
+ if ((src == NULL) || (*src == '\0'))
+ n = 0 ;
+ else
+ n = strlen(src) ;
+
+ qfs_append_justified_n(qfs, src, n, width) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append as much as possible of the first 'n' bytes of the source string to
+ * the given qf_str, left or right justified to the given width.
+ *
+ * Ignores the width if the string is longer than it.
+ *
+ * Negative width => left justify.
+ *
+ * May append nothing at all !
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_append_justified_n(qf_str qfs, const char* src, size_t n, int width)
+{
+ if (n >= abs(width))
+ width = 0 ;
+
+ if (width > 0)
+ qfs_append_ch_x_n(qfs, ' ', width - n) ;
+
+ qfs_append_n(qfs, src, n) ;
+
+ if (width < 0)
+ qfs_append_ch_x_n(qfs, ' ', - width - n) ;
+} ;
+
+/*==============================================================================
+ * Number conversion
+ */
+
+static void
+qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags,
+ int width, int precision) ;
+
+/*------------------------------------------------------------------------------
+ * Signed integer -- converted as per flags, width and precision.
+ *
+ * Result is appended to the given qf_str.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags,
+ int width, int precision)
+{
+ uintmax_t u_val ;
+ int sign ;
+
+ if (s_val < 0)
+ {
+ sign = -1 ;
+ u_val = (uintmax_t)(-(s_val + 1)) + 1 ;
+ }
+ else
+ {
+ sign = +1 ;
+ u_val = s_val ;
+ } ;
+
+ qfs_number(qfs, u_val, sign, flags & ~pf_unsigned, width, precision) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Unsigned integer -- converted as per flags, width and precision.
+ *
+ * Result is appended to the given qf_str.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags,
+ int width, int precision)
+{
+ qfs_number(qfs, u_val, 0, flags | pf_unsigned, width, precision) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Address -- converted as per flags, width and precision.
+ *
+ * Result is appended to the given qf_str.
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags,
+ int width, int precision)
+{
+ confirm(sizeof(uintmax_t) >= sizeof(uintptr_t)) ;
+ qfs_number(qfs, (uintptr_t)p_val, 0, flags | pf_unsigned, width, precision) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Number conversion function.
+ *
+ * All number conversion ends up here.
+ *
+ * Accepts: pf_commas -- format with commas
+ * pf_plus -- requires '+' or '-'
+ * pf_space -- requires space or '-'
+ * pf_zeros -- zero fill to width
+ * pf_alt -- add '0x' or '0X' if hex (no effect on decimal)
+ *
+ * pf_precision -- explicit precision (needed if precision == 0)
+ *
+ * pf_hex -- render in hex
+ * pf_uc -- render in upper case
+ *
+ * pf_unsigned -- value is unsigned
+ * pf_ptr -- value is a void* pointer
+ *
+ * NB: pf_hex does NOT imply pf_unsigned.
+ * pf_uc does NOT imply pf_hex
+ *
+ * If the width is < 0 -- left justify in abs(width) -- zero fill ignored
+ * == 0 -- no width -- zero fill ignored
+ * > 0 -- right justify in width -- zero filling if req.
+ *
+ * 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.
+ *
+ * Precedence issues:
+ *
+ * * precision comes first. Disables zero fill.
+ *
+ * * commas come before zero fill.
+ *
+ * * signs and prefixes come before zero fill
+ *
+ * * pf_plus takes precedence over pf_space
+ *
+ * * pf_unsigned or sign == 0 takes precedence over pf_plus and pf_space.
+ *
+ * For hex output, pf_commas groups digits in 4's, separated by '_'.
+ *
+ * For hex output if precision is:
+ *
+ * -1 set precision to multiple of 2, just long enough for the value
+ * -2 set precision to multiple of 4, just long enough for the value
+ *
+ * (under all other conditions, -ve precision is ignored).
+ *
+ * Note: if the precision is explicitly 0, and the value is 0, and no other
+ * characters are to be generated -- ie no: pf_plus, pf_space, pf_zeros,
+ * or pf_alt (with pf_hex) -- then nothing is generated.
+ *
+ * This operation is async-signal-safe.
+ */
+static void
+qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags,
+ int width, int precision)
+{
+ enum
+ {
+ max_bits = 256, /* size of number can convert */
+ max_digits = 90, /* could do octal ! */
+ buf_size = 128, /* buffer to use for that */
+ } ;
+
+ confirm((sizeof(uintmax_t) * 8) <= max_bits) ; /* check max_bits */
+ confirm((max_digits * 3) >= max_bits) ; /* check max_digits */
+
+ /* Buffer requires space for sign, '0x', digits, '00', commas, '\0'
+ *
+ * The '00' is for zero fill will commas, and is enough to extend the
+ * number to "000,...." -- that is, a full leading triple.
+ */
+ confirm(buf_size > (1 + 2 + max_digits + (2 + (max_digits / 3)) + 1)) ;
+
+ /* For hex commas the sum is similar, but smaller. */
+ confirm((3 + (max_digits / 4)) < (2 + (max_digits / 3))) ;
+
+ unsigned base ;
+ const char* digits ;
+ const char* radix_str ;
+ const char* sign_str ;
+ char num[buf_size] ;
+ char* p ;
+ char* e ;
+ int len ;
+ int radix_len ;
+ int sign_len ;
+ uintmax_t v ;
+
+ char comma ;
+ int interval ;
+
+ int zeros ;
+
+ static const char lc[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f' } ;
+ static const char uc[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F' } ;
+
+ /* Tidy up the options */
+ if (precision < 0)
+ {
+ if ((flags & pf_hex) && (precision >= -2))
+ {
+ /* special precision for hex output */
+ int unit = (precision == -1) ? 2 : 4 ;
+ v = val | 1 ;
+ precision = 0 ;
+ while (v != 0)
+ {
+ precision += unit ;
+ v >>= (unit * 4) ;
+ } ;
+ }
+ else
+ {
+ /* mostly, -ve precision is ignored */
+ precision = 0 ;
+ flags &= ~pf_precision ; /* ignore precision < 0 */
+ } ;
+ } ;
+
+ if (precision > 0)
+ flags |= pf_precision ; /* act on precision > 0 */
+
+ if ((flags & pf_precision) || (width <= 0))
+ flags &= ~pf_zeros ; /* turn off zero fill */
+
+ /* Set up any required sign and radix prefix */
+ if ((flags & pf_unsigned) || (sign == 0))
+ sign_str = "" ;
+ else if (sign < 0)
+ sign_str = "-" ;
+ else if (flags & pf_plus)
+ sign_str = "+" ;
+ else if (flags & pf_space)
+ sign_str = " " ;
+ else
+ sign_str = "" ;
+
+ sign_len = strlen(sign_str) ;
+
+ radix_str = "" ;
+ if ((flags & (pf_hex | pf_alt)) == (pf_hex | pf_alt))
+ radix_str = (flags & pf_uc) ? "0X" : "0x" ;
+
+ radix_len = strlen(radix_str) ;
+
+ /* Turn off zero fill if left justify (width < 0) */
+ if (width < 0)
+ flags &= ~pf_zeros ;
+
+ /* Special case of explicit zero precision and value == 0 */
+ if ((flags & pf_precision) && (precision == 0) && (val == 0))
+ {
+ if (((flags & pf_zeros) == 0) && (sign_len == 0) && (radix_len == 0))
+ {
+ qfs_append_justified_n(qfs, NULL, 0, width) ;
+ return ;
+ } ;
+ } ;
+
+ /* Start with the basic digit conversion. */
+ base = (flags & pf_hex) ? 16 : 10 ;
+ digits = (flags & pf_uc) ? uc : lc ;
+
+ e = p = num + sizeof(num) - 1 ;
+ *p = '\0' ;
+ v = val ;
+ do
+ {
+ *--p = digits[v % base] ;
+ v /= base ;
+ } while ((v > 0) && (p > num)) ;
+
+ assert(v == 0) ;
+
+ len = e - p ;
+
+ /* Worry about the precision */
+ while ((precision > len) && (len < max_digits))
+ {
+ *--p = '0' ;
+ ++len ;
+ } ;
+
+ /* Worry about commas */
+ comma = (flags & pf_hex) ? '_' : ',' ;
+ interval = (flags & pf_hex) ? 4 : 3 ;
+
+ if (flags & pf_commas)
+ {
+ int c ;
+ int t ;
+ char* cq ;
+ char* cp ;
+
+ c = (len - 1) / interval ; /* number of commas to insert */
+ t = len % interval ; /* digits before first comma */
+ if (t == 0)
+ t = interval ;
+
+ len += c ; /* account for the commas */
+
+ cq = p ;
+ p -= c ;
+ cp = p ;
+
+ assert(p > num) ;
+
+ while (c--)
+ {
+ while (t--)
+ *cp++ = *cq++ ;
+ *cp++ = comma ;
+ } ;
+
+ assert(len == (e - p)) ;
+
+ /* commas and zero fill interact. Here fill the leading group. */
+ zeros = width - (sign_len + radix_len + len) ;
+ if ((flags & pf_zeros) && (zeros > 0))
+ {
+ int group_fill = interval - (len % (interval + 1)) ;
+ assert(group_fill < interval) ;
+ if (group_fill > zeros)
+ group_fill = zeros ;
+
+ len += group_fill ;
+ while (group_fill--)
+ {
+ assert(p > num) ;
+ *--p = '0' ;
+ } ;
+ } ;
+ } ;
+
+ assert(len == (e - p)) ;
+
+ /* See if still need to worry about zero fill */
+ zeros = width - (sign_len + radix_len + len) ;
+ if ((flags & pf_zeros) && (zeros > 0))
+ {
+ /* Need to insert zeros and possible commas between sign and radix
+ * and the start of the number.
+ *
+ * Note that for commas the number has been arranged to have a full
+ * leading group.
+ *
+ * The width can be large... so do this by appending any sign and
+ * radix to the qf_str, and then the required leading zeros (with or
+ * without commas).
+ */
+ if (sign_len != 0)
+ qfs_append_n(qfs, sign_str, sign_len) ;
+
+ if (radix_len != 0)
+ qfs_append_n(qfs, radix_str, radix_len) ;
+
+ if (flags & pf_commas)
+ {
+ /* Leading zeros with commas !
+ *
+ * Start with ',', '0,', '00,' etc to complete the first group.
+ * Thereafter add complete groups.
+ */
+ int g ;
+ int r ;
+ g = (zeros + interval - 1) / (interval + 1) ;
+ r = (zeros - 1) % (interval + 1) ;
+
+ if (r == 0)
+ {
+ qfs_append_ch_x_n(qfs, comma, 1) ;
+ r = interval ;
+ }
+
+ while (g--)
+ {
+ qfs_append_ch_x_n(qfs, '0', r) ;
+ qfs_append_ch_x_n(qfs, comma, 1) ;
+ r = interval ;
+ } ;
+ }
+ else
+ qfs_append_ch_x_n(qfs, '0', zeros) ;
+
+ width = 0 ; /* have dealt with the width. */
+ }
+ else
+ {
+ /* No leading zeros, so complete the number by adding any sign
+ * and radix.
+ */
+ char* cp ;
+
+ p -= sign_len + radix_len ;
+ len += sign_len + radix_len ;
+ assert(p >= num) ;
+
+ cp = p ;
+ while (sign_len--)
+ *cp++ = *sign_str++ ;
+ while (radix_len--)
+ *cp++ = *radix_str++ ;
+ } ;
+
+ /* Finally, can append the number -- respecting any remaining width */
+ assert(len == (e - p)) ;
+
+ qfs_append_justified_n(qfs, p, len, width) ;
+} ;
+
+/*==============================================================================
+ * printf() and vprintf() type functions
+ */
+
+enum pf_phase
+{
+ pfp_null, /* in ascending order */
+ pfp_flags,
+ pfp_width,
+ pfp_precision,
+ pfp_num_type,
+
+ pfp_done,
+ pfp_failed
+} ;
+
+/* Number types for printing */
+enum arg_num_type
+{
+ ant_char, /* hh */
+ ant_short, /* h */
+ ant_int, /* default */
+ ant_long, /* l */
+ ant_long_long, /* ll */
+ ant_intmax_t, /* j */
+ ant_size_t, /* z */
+ ant_ptr_t, /* void* */
+
+ ant_default = ant_int,
+};
+
+static enum pf_phase qfs_arg_string(qf_str qfs, va_list args,
+ enum pf_flags flags, int width, int precision) ;
+static enum pf_phase qfs_arg_char(qf_str qfs, va_list args,
+ enum pf_flags flags, int width, int precision) ;
+static enum pf_phase qfs_arg_number(qf_str qfs, va_list args,
+ enum pf_flags flags, int width, int precision, enum arg_num_type ant) ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qf_str -- cf printf()
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_printf(qf_str qfs, const char* format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ qfs_vprintf(qfs, format, args);
+ va_end (args);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qf_str -- cf vprintf()
+ *
+ * This operation is async-signal-safe.
+ */
+extern void
+qfs_vprintf(qf_str qfs, const char *format, va_list args)
+{
+ if (format == NULL)
+ return ;
+
+ while ((qfs->ptr < qfs->end) && (*format != '\0'))
+ {
+ /* Have space for one byte and current format byte is not '\0' */
+ if (*format != '%')
+ *qfs->ptr++ = *format++ ;
+ else
+ {
+ const char* start = format++ ; /* start points at the '%' ...
+ ... step past it now */
+ bool star = false ;
+ bool digit = false ;
+ int d = 0 ;
+ int width_sign = +1 ;
+ int width = 0 ;
+ int precision = 0 ;
+ enum arg_num_type ant = ant_default ;
+ enum pf_flags flags = pf_none ;
+ enum pf_phase phase = pfp_null ;
+
+ while (phase < pfp_done)
+ {
+ switch (*format++) /* get next and step past it */
+ {
+ case '%': /* %% only */
+ if (phase == pfp_null)
+ *qfs->ptr++ = '%' ;
+ phase = (phase == pfp_null) ? pfp_done : pfp_failed ;
+ break ;
+
+ case '\'':
+ flags |= pf_commas ;
+ phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ;
+ break ;
+
+ case '-':
+ width_sign = -1 ;
+ phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ;
+ break ;
+
+ case '+':
+ flags |= pf_plus ;
+ phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ;
+ break ;
+
+ case ' ':
+ flags |= pf_space ;
+ phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ;
+ break ;
+
+ case '0':
+ if (phase <= pfp_flags)
+ {
+ flags |= pf_zeros ;
+ phase = pfp_flags ;
+ break ;
+ } ;
+ /* fall through */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ d = *(format - 1) - '0' ;
+ if (!star && (phase <= pfp_width))
+ {
+ phase = pfp_width ;
+ width = (width * 10) + (d * width_sign) ;
+ }
+ else if (!star && (phase == pfp_precision))
+ precision = (precision * 10) + d ;
+ else
+ phase = pfp_failed ;
+
+ digit = true ;
+ break ;
+
+ case '*':
+ if (!star && !digit && (phase <= pfp_width))
+ {
+ phase = pfp_width ;
+ width = va_arg(args, int) ;
+ }
+ else if (!star && !digit && (phase == pfp_precision))
+ {
+ precision = va_arg(args, int) ;
+ if (precision < 0)
+ {
+ precision = 0 ;
+ flags &= ~pf_precision ;
+ } ;
+ }
+ else
+ phase = pfp_failed ;
+
+ star = true ;
+ break ;
+
+ case '.':
+ phase = (phase <= pfp_precision) ? pfp_precision : pfp_failed;
+ flags |= pf_precision ;
+ precision = 0 ;
+ break ;
+
+ case 'l': /* 1 or 2 'l', not 'h', 'j' or 'z' */
+ phase = pfp_num_type ;
+ if (ant == ant_default)
+ ant = ant_long ;
+ else if (ant == ant_long)
+ ant = ant_long_long ;
+ else
+ phase = pfp_failed ;
+ break ;
+
+ case 'h': /* 1 or 2 'h', not 'l', 'j' or 'z' */
+ phase = pfp_num_type ;
+ if (ant == ant_default)
+ ant = ant_short ;
+ else if (ant == ant_short)
+ ant = ant_char ;
+ else
+ phase = pfp_failed ;
+ break ;
+
+ case 'j': /* 1 'j', not 'h', 'l' or 'z' */
+ phase = (phase <= pfp_num_type) ? pfp_num_type : pfp_failed ;
+ ant = ant_intmax_t ;
+ break ;
+
+ case 'z': /* 1 'z', not 'h', 'l' or 'j' */
+ phase = (phase <= pfp_num_type) ? pfp_num_type : pfp_failed ;
+ ant = ant_size_t ;
+ break ;
+
+ case 's':
+ if (phase == pfp_num_type)
+ phase = pfp_failed ; /* don't do 'l' etc. */
+ else
+ phase = qfs_arg_string(qfs, args, flags, width, precision) ;
+ break ;
+
+ case 'c':
+ if (phase == pfp_num_type)
+ phase = pfp_failed ; /* don't do 'l' etc. */
+ else
+ phase = qfs_arg_char(qfs, args, flags, width, precision) ;
+ break ;
+
+ case 'd':
+ case 'i':
+ phase = qfs_arg_number(qfs, args, flags, width, precision,
+ ant) ;
+ break ;
+
+ case 'u':
+ phase = qfs_arg_number(qfs, args, flags | pf_unsigned, width,
+ precision, ant) ;
+ break ;
+
+ case 'x':
+ phase = qfs_arg_number(qfs, args, flags | pf_hex_x, width,
+ precision, ant) ;
+ break ;
+
+ case 'X':
+ phase = qfs_arg_number(qfs, args, flags | pf_hex_X, width,
+ precision, ant) ;
+ break ;
+
+ case 'p':
+ if (phase == pfp_num_type)
+ phase = pfp_failed ;
+ else
+ phase = qfs_arg_number(qfs, args, flags | pf_void_p, width,
+ precision, ant_ptr_t) ;
+ break ;
+
+ default: /* unrecognised format */
+ phase = pfp_failed ;
+ break ;
+ } ;
+ } ;
+
+ if (phase == pfp_failed)
+ {
+ format = start ; /* back to the start */
+ *qfs->ptr++ = *format++ ; /* copy the '%' */
+ } ;
+ } ;
+ } ;
+
+ *qfs->ptr = '\0' ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * %s handler
+ *
+ * Accepts: width
+ * precision
+ * pf_precision -- explicit precision
+ *
+ * Rejects: pf_commas -- "'" seen
+ * pf_plus -- "+" seen
+ * pf_space -- " " seen
+ * pf_zeros -- "0" seen
+ * pf_alt -- "#" seen
+ *
+ * Won't get: pf_hex
+ * pf_uc
+ * pf_unsigned
+ * pf_ptr
+ *
+ * This operation is async-signal-safe.
+ */
+static enum pf_phase
+qfs_arg_string(qf_str qfs, va_list args, enum pf_flags flags,
+ int width, int precision)
+{
+ const char* src ;
+ int len ;
+
+ src = va_arg(args, char*) ;
+
+ if (flags != (flags & pf_precision))
+ return pfp_failed ;
+
+ len = strlen(src) ;
+ if (((precision > 0) || (flags & pf_precision)) && (len > precision))
+ len = precision ;
+
+ qfs_append_justified_n(qfs, src, len, width) ;
+
+ return pfp_done ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * %c handler
+ *
+ * Accepts: width
+ *
+ * Rejects: precision
+ * pf_precision -- explicit precision
+ * pf_commas -- "'" seen
+ * pf_plus -- "+" seen
+ * pf_space -- " " seen
+ * pf_zeros -- "0" seen
+ * pf_alt -- "#" seen
+ *
+ * Won't get: pf_hex
+ * pf_uc
+ * pf_unsigned
+ * pf_ptr
+ *
+ * This operation is async-signal-safe.
+ */
+static enum pf_phase
+qfs_arg_char(qf_str qfs, va_list args, enum pf_flags flags,
+ int width, int precision)
+{
+ unsigned char ch ;
+
+ ch = va_arg(args, int) ;
+
+ if ((flags != 0) || (precision != 0))
+ return pfp_failed ;
+
+ qfs_append_justified_n(qfs, (char*)&ch, 1, width) ;
+
+ return pfp_done ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * %d, %i, %u, %x, %X and %p handler
+ *
+ * Accepts: pf_commas -- format with commas
+ * pf_minus -- left justify (any width will be -ve)
+ * pf_plus -- requires sign
+ * pf_space -- requires space or '-'
+ * pf_zeros -- zero fill to width
+ * pf_alt -- '0x' or '0X' for hex
+ *
+ * pf_precision -- precision specified
+ *
+ * pf_unsigned -- value is unsigned
+ * pf_ptr -- value is a void* pointer
+ * pf_hex -- render in hex
+ * pf_uc -- render hex in upper case
+ *
+ * and: all the number argument types.
+ *
+ * This operation is async-signal-safe.
+ */
+static enum pf_phase
+qfs_arg_number(qf_str qfs, va_list args, enum pf_flags flags,
+ int width, int precision, enum arg_num_type ant)
+{
+ uintmax_t u_val ;
+ intmax_t s_val ;
+
+ /* Special for hex with '0... if no explicit precision, set -1 for byte
+ * and -2 for everything else -- see qfs_number().
+ */
+ if (((flags & pf_precision) == 0) && (flags & pf_hex))
+ {
+ if ((flags & (pf_commas | pf_zeros)) == (pf_commas | pf_zeros))
+ {
+ precision = (ant == ant_char) ? -1 : -2 ;
+ flags |= pf_precision ;
+ } ;
+ } ;
+
+ /* It is assumed that all values can be mapped to a uintmax_t */
+ confirm(sizeof(uintmax_t) >= sizeof(uintptr_t)) ;
+
+ if (flags & pf_unsigned)
+ {
+ switch (ant)
+ {
+ case ant_char:
+ case ant_short:
+ u_val = va_arg(args, int) ;
+ break ;
+
+ case ant_int:
+ u_val = va_arg(args, unsigned int) ;
+ break ;
+
+ case ant_long:
+ u_val = va_arg(args, unsigned long) ;
+ break ;
+
+ case ant_long_long:
+ u_val = va_arg(args, unsigned long long) ;
+ break ;
+
+ case ant_intmax_t:
+ u_val = va_arg(args, uintmax_t) ;
+ break ;
+
+ case ant_size_t:
+ u_val = va_arg(args, size_t) ;
+ break ;
+
+ case ant_ptr_t:
+ u_val = va_arg(args, uintptr_t) ;
+ break ;
+
+ default:
+ zabort("impossible integer size") ;
+ } ;
+
+ qfs_unsigned(qfs, u_val, flags, width, precision) ;
+ }
+ else
+ {
+ switch (ant)
+ {
+ case ant_char:
+ case ant_short:
+ s_val = va_arg(args, int) ;
+ break ;
+
+ case ant_int:
+ s_val = va_arg(args, signed int) ;
+ break ;
+
+ case ant_long:
+ s_val = va_arg(args, signed long) ;
+ break ;
+
+ case ant_long_long:
+ s_val = va_arg(args, signed long long) ;
+ break ;
+
+ case ant_intmax_t:
+ s_val = va_arg(args, intmax_t) ;
+ break ;
+
+ case ant_size_t:
+ s_val = va_arg(args, ssize_t) ;
+ break ;
+
+ case ant_ptr_t:
+ s_val = va_arg(args, intptr_t) ;
+ break ;
+
+ default:
+ zabort("impossible integer size") ;
+ } ;
+
+ qfs_signed(qfs, s_val, flags, width, precision) ;
+ } ;
+
+ /* construct a digit string, the hard way */
+
+ return pfp_done ;
+} ;
+
diff --git a/lib/qfstring.h b/lib/qfstring.h
new file mode 100644
index 00000000..fbc83fe8
--- /dev/null
+++ b/lib/qfstring.h
@@ -0,0 +1,158 @@
+/* Some string handling -- header
+ * 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_QFSTRING_H
+#define _ZEBRA_QFSTRING_H
+
+#include "zebra.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifndef Inline
+#define Inline static inline
+#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__ */
+
+/*==============================================================================
+ * These "qfstrings" address the issues of dealing with *fixed* length
+ * strings, particularly where the string handling must be async-signal-safe.
+ */
+
+typedef struct qf_str qf_str_t ;
+typedef struct qf_str* qf_str ;
+
+/* When initialised a qf_string is set:
+ *
+ * str = start of string -- and this is never changed
+ * ptr = start of string -- this is moved as stuff is appended
+ * end = last possible position for terminating '\0'
+ * -- and this is never changed
+ */
+struct qf_str
+{
+ char* str ; /* start of string */
+ char* ptr ; /* current position */
+ char* end ; /* end of string */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Print format flags for number printing
+ */
+enum pf_flags
+{
+ pf_none = 0,
+
+ /* The following correspond to the "flags" */
+ pf_commas = 1 << 0, /* "'" seen */
+ pf_plus = 1 << 1, /* "+" seen */
+ pf_space = 1 << 2, /* " " seen */
+ pf_zeros = 1 << 3, /* "0" seen */
+ pf_alt = 1 << 4, /* "#" seen */
+
+ pf_precision = 1 << 7, /* '.' seen */
+
+ /* The following signal how to render the value */
+ pf_hex = 1 << 8, /* hex */
+ pf_uc = 1 << 9, /* upper-case */
+
+ /* The following signal the type of value */
+ pf_ptr = 1 << 14, /* is a pointer */
+ pf_unsigned = 1 << 15, /* unsigned value */
+
+ /* Common combination */
+ pf_hex_x = pf_unsigned | pf_hex,
+ pf_hex_X = pf_unsigned | pf_hex | pf_uc,
+
+ pf_void_p = pf_ptr | pf_hex_x,
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern void qfs_init(qf_str qfs, char* str, size_t size) ;
+
+extern void qfs_term(qf_str qfs, const char* src) ;
+
+Inline int qfs_len(qf_str qfs) ;
+Inline void* qfs_end(qf_str qfs) ;
+Inline int qfs_left(qf_str qfs) ;
+
+extern void qfs_append(qf_str qfs, const char* src) ;
+extern void qfs_append_n(qf_str qfs, const char* src, size_t n) ;
+
+extern void qfs_append_ch_x_n(qf_str qfs, char ch, size_t n) ;
+extern void qfs_append_justified(qf_str qfs, const char* src, int width) ;
+extern void qfs_append_justified_n(qf_str qfs, const char* src,
+ size_t n, int width) ;
+
+extern void qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags,
+ int width, int precision) ;
+extern void qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags,
+ int width, int precision) ;
+extern void qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags,
+ int width, int precision) ;
+
+extern void qfs_printf(qf_str qfs, const char* format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern void qfs_vprintf(qf_str qfs, const char *format, va_list args) ;
+
+/*==============================================================================
+ * The Inline functions.
+ */
+
+/*------------------------------------------------------------------------------
+ * Current length of qf_str, not counting the terminating '\0'.
+ */
+Inline int
+qfs_len(qf_str qfs)
+{
+ return qfs->ptr - qfs->str ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Address of the terminating '\0'.
+ */
+Inline void*
+qfs_end(qf_str qfs)
+{
+ return qfs->ptr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Current space left in the qstr, given what has been reserved for terminating
+ * '\0' and any other reservation.
+ */
+Inline int
+qfs_left(qf_str qfs)
+{
+ return qfs->end - qfs->ptr ;
+} ;
+
+
+#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qiovec.c b/lib/qiovec.c
new file mode 100644
index 00000000..546dfcb0
--- /dev/null
+++ b/lib/qiovec.c
@@ -0,0 +1,261 @@
+/* Flexible iovec handler
+ * Copyright (C) 2010 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.
+ */
+
+#include "zebra.h"
+
+#include "memory.h"
+#include "zassert.h"
+#include "miyagi.h"
+
+#include "qiovec.h"
+
+/*==============================================================================
+ * Initialise, allocate and reset qiovec
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise new qiovec -- allocate if required.
+ *
+ * This is for initialising a new structure. Any pre-exiting contents are
+ * lost.
+ *
+ * Returns: address of qiovec
+ */
+extern qiovec
+qiovec_init_new(qiovec viov)
+{
+ if (viov == NULL)
+ viov = XCALLOC(MTYPE_QIOVEC, sizeof(struct qiovec)) ;
+ else
+ memset(viov, 0, sizeof(struct qiovec)) ;
+
+ /* Zeroising has set:
+ *
+ * vec = NULL - no array, yet
+ * writing = false -- no writing going on
+ *
+ * i_get = 0 -- next entry to get
+ * i_put = 0 -- next entry to put
+ *
+ * i_alloc = 0; -- no entries allocated
+ *
+ * Nothing more is required.
+ */
+
+ return viov ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset qiovec (if any) -- release body and (if required) the structure.
+ *
+ * Returns: address of qiovec (if any) -- NULL if structure released
+ */
+extern qiovec
+qiovec_reset(qiovec viov, bool free_structure)
+{
+ if (viov != NULL)
+ {
+ if (viov->vec != NULL)
+ XFREE(MTYPE_QIOVEC_VEC, viov->vec) ;
+
+ if (free_structure)
+ XFREE(MTYPE_QIOVEC, viov) ; /* sets viov = NULL */
+ else
+ qiovec_init_new(viov) ; /* re-initialise */
+ } ;
+
+ return viov ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear given qiovec.
+ */
+extern void
+qiovec_clear(qiovec viov)
+{
+ viov->i_get = 0 ;
+ viov->i_put = 0 ;
+ viov->writing = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Push item to given qiovec
+ *
+ * NB: avoids pushing zero length items.
+ */
+extern void
+qiovec_push(qiovec viov, const void* base, size_t len)
+{
+ struct iovec* p_iov ;
+
+ if (len == 0)
+ return ;
+
+ if (viov->i_put >= viov->i_alloc)
+ {
+ size_t size ;
+ assert(viov->i_put == viov->i_alloc) ;
+
+ assert( ((viov->i_alloc == 0) && (viov->vec == NULL))
+ || ((viov->i_alloc != 0) && (viov->vec != NULL)) ) ;
+
+ if (viov->i_get > 200) /* keep in check */
+ {
+ size = (viov->i_put - viov->i_get) * sizeof(struct iovec) ;
+ if (size != 0)
+ memmove(viov->vec, &viov->vec[viov->i_get], size) ;
+ viov->i_put -= viov->i_get ;
+ viov->i_get = 0 ;
+ }
+ else
+ {
+ viov->i_alloc += 100 ; /* a sizable chunk */
+
+ size = viov->i_alloc * sizeof(struct iovec) ;
+ if (viov->vec == NULL)
+ viov->vec = XMALLOC(MTYPE_QIOVEC_VEC, size) ;
+ else
+ viov->vec = XREALLOC(MTYPE_QIOVEC_VEC, viov->vec, size) ;
+ } ;
+ } ;
+
+ p_iov = &viov->vec[viov->i_put++] ;
+
+ p_iov->iov_base = miyagi(base) ;
+ p_iov->iov_len = len ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write given qiovec -- assuming NON-BLOCKING.
+ *
+ * Does nothing if the qiovec is empty.
+ *
+ * Loops internally if gets EINTR.
+ *
+ * When there is nothing left to output, resets the i_put & i_get to zero.
+ *
+ * Returns: > 0 => one or more bytes left to output
+ * 0 => all done -- zero bytes left to output
+ * -1 => failed -- see errno
+ */
+extern int
+qiovec_write_nb(int fd, qiovec viov)
+{
+ int n ;
+ int l ;
+
+ n = viov->i_put - viov->i_get ;
+
+ l = iovec_write_nb(fd, &viov->vec[viov->i_get], n) ;
+
+ if (l == 0)
+ {
+ viov->writing = 0 ;
+ viov->i_get = viov->i_put = 0 ;
+ }
+ else
+ {
+ viov->writing = 1 ;
+ viov->i_get += (n - l) ;
+ } ;
+
+ return l ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write given iovec -- assuming NON-BLOCKING.
+ *
+ * Does nothing if given zero iovec entries (and array may be NULL).
+ *
+ * Loops internally if gets EINTR.
+ *
+ * If does not manage to write everything, then:
+ *
+ * -- updates the length field of all entries up to and including the
+ * last one for which data has been written.
+ *
+ * -- updates the address field of the first entry that still has some
+ * data to be output.
+ *
+ * Can call this again with the same 'p_iov' and the same 'n' -- the entries
+ * which have zero lengths will be stepped over. Output will continue from
+ * where it left off.
+ *
+ * Alternatively, if this returns 'l', then do "p_iov += n - l", and set
+ * "n = l" before calling this again.
+ *
+ * Returns: > 0 => number of entries left to output
+ * 0 => all done -- nothing left to output
+ * -1 => failed -- see errno
+ */
+extern int
+iovec_write_nb(int fd, struct iovec p_iov[], int n)
+{
+ ssize_t ret ;
+
+ assert(n >= 0) ;
+
+ /* Skip past any leading zero length entries */
+ while ((n > 0) && (p_iov->iov_len == 0))
+ {
+ ++p_iov ;
+ --n ;
+ } ;
+
+ while (n > 0)
+ {
+ ret = writev(fd, p_iov, (n < IOV_MAX ? n : IOV_MAX)) ;
+
+ if (ret > 0)
+ {
+ while (ret > 0)
+ {
+ if (ret >= (ssize_t)p_iov->iov_len)
+ {
+ assert(n > 0) ;
+ ret -= p_iov->iov_len ;
+ p_iov->iov_len = 0 ;
+ ++p_iov ;
+ --n ;
+ }
+ else
+ {
+ p_iov->iov_base = (char*)p_iov->iov_base + ret ;
+ p_iov->iov_len -= ret ;
+ ret = 0 ;
+ } ;
+ } ;
+ }
+ else if (ret == 0)
+ break ; /* not sure can happen... but
+ cannot assume will go away */
+ else
+ {
+ int err = errno ;
+ if ((err == EAGAIN) || (err == EWOULDBLOCK))
+ break ;
+ if (err != EINTR)
+ return -1 ; /* failed */
+ } ;
+ } ;
+
+ return n ;
+} ;
diff --git a/lib/qiovec.h b/lib/qiovec.h
new file mode 100644
index 00000000..ee03d2f2
--- /dev/null
+++ b/lib/qiovec.h
@@ -0,0 +1,99 @@
+/* Flexible iovec -- header
+ * Copyright (C) 2010 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_QIOVEC_H
+#define _ZEBRA_QIOVEC_H
+
+#include "zebra.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Flexible size "struct iovec"
+ *
+ * NB: a completely zero structure is a valid, empty qiovec.
+ */
+typedef struct qiovec* qiovec ;
+typedef struct qiovec qiovec_t ;
+struct qiovec
+{
+ struct iovec* vec ; /* the actual iovec array */
+
+ bool writing ; /* started, but not finished */
+
+ unsigned i_get ; /* next entry to get */
+ unsigned i_put ; /* next entry to put */
+
+ unsigned i_alloc ; /* number of entries allocated */
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+extern qiovec qiovec_init_new(qiovec qiov) ;
+extern qiovec qiovec_reset(qiovec qiov, bool free_structure) ;
+
+#define qiovec_reset_keep(qiov) qiovec_reset(qiov, 0)
+#define qiovec_reset_free(qiov) qiovec_reset(qiov, 1)
+
+Inline bool qiovec_empty(qiovec qiov) ;
+extern void qiovec_clear(qiovec qiov) ;
+extern void qiovec_push(qiovec qiov, const void* base, size_t len) ;
+extern int qiovec_write_nb(int fd, qiovec qiov) ;
+
+extern int iovec_write_nb(int fd, struct iovec* p_iov, int n) ;
+Inline void iovec_set(struct iovec* p_iov, const void* base, size_t len) ;
+
+/*------------------------------------------------------------------------------
+ * Is given qiov empty ?
+ *
+ * NB: arranges to never add zero length entries to the iovec vector, so
+ * is empty when there are no active entries.
+ */
+Inline bool
+qiovec_empty(qiovec qiov)
+{
+ return (qiov->i_get == qiov->i_put) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set a given struct iovec
+ *
+ * Gets around the fact that the standard structure does not have a const
+ * pointer !
+ */
+#include "miyagi.h"
+
+Inline void
+iovec_set(struct iovec* p_iov, const void* base, size_t len)
+{
+ p_iov->iov_base = miyagi(base) ;
+ p_iov->iov_len = len ;
+} ;
+
+#endif /* _ZEBRA_QIOVEC_H */
diff --git a/lib/qpnexus.c b/lib/qpnexus.c
index 6fc9129d..e568e7d8 100644
--- a/lib/qpnexus.c
+++ b/lib/qpnexus.c
@@ -20,6 +20,7 @@
*/
#include <zebra.h>
+#include <stdbool.h>
#include "qpnexus.h"
#include "memory.h"
@@ -65,6 +66,9 @@ qpn_init_new(qpn_nexus qpn, int main_thread)
qpn->main_thread = main_thread;
qpn->start = qpn_start;
+ if (main_thread)
+ qpn->thread_id = qpt_thread_self();
+
return qpn;
}
@@ -79,11 +83,15 @@ qpn_add_hook_function(qpn_hook_list list, void* hook)
} ;
/*------------------------------------------------------------------------------
- * free timers, selection, message queue and nexus
- * return NULL
+ * Reset given nexus and, if required, free the nexus structure.
+ *
+ * Free timers, selection, message queue and its thread signal.
+ *
+ * Leaves all pointers to these things NULL -- which generally means that the
+ * object is empty or otherwise out of action.
*/
-qpn_nexus
-qpn_free(qpn_nexus qpn)
+extern qpn_nexus
+qpn_reset(qpn_nexus qpn, bool free_structure)
{
qps_file qf;
qtimer qtr;
@@ -96,6 +104,7 @@ qpn_free(qpn_nexus qpn)
{
while ((qtr = qtimer_pile_ream(qpn->pile, 1)))
qtimer_free(qtr);
+ qpn->pile = NULL ;
}
/* files and selection */
@@ -103,6 +112,7 @@ qpn_free(qpn_nexus qpn)
{
while ((qf = qps_selection_ream(qpn->selection, 1)))
qps_file_free(qf);
+ qpn->selection = NULL ;
}
if (qpn->queue != NULL)
@@ -111,10 +121,11 @@ qpn_free(qpn_nexus qpn)
if (qpn->mts != NULL)
qpn->mts = mqueue_thread_signal_reset(qpn->mts, 1);
- XFREE(MTYPE_QPN_NEXUS, qpn) ;
+ if (free_structure)
+ XFREE(MTYPE_QPN_NEXUS, qpn) ; /* sets qpn = NULL */
- return NULL;
-}
+ return qpn ;
+} ;
/*==============================================================================
* Execution of a nexus
@@ -173,8 +184,8 @@ qpn_start(void* arg)
qpn_in_thread_init(qpn);
/* custom in-thread initialization */
- for (i = 0; i < qpn->in_thread_init.count ; ++i)
- ((qpn_init_function*)(qpn->in_thread_init.hooks[i]))() ;
+ for (i = 0; i < qpn->in_thread_init.count ;)
+ ((qpn_init_function*)(qpn->in_thread_init.hooks[i++]))() ;
/* Until required to terminate, loop */
done = 1 ;
@@ -193,8 +204,8 @@ qpn_start(void* arg)
done = 0 ;
/* Foreground hooks, if any. */
- for (i = 0; i < qpn->foreground.count ; ++i)
- done |= ((qpn_hook_function*)(qpn->foreground.hooks[i]))() ;
+ for (i = 0; i < qpn->foreground.count ;)
+ done |= ((qpn_hook_function*)(qpn->foreground.hooks[i++]))() ;
/* drain the message queue, will be in waiting for signal state
* when it's empty */
@@ -246,8 +257,8 @@ qpn_start(void* arg)
} ;
/* custom in-thread finalization */
- for (i = qpn->in_thread_final.count - 1; i > 0 ; --i)
- ((qpn_init_function*)(qpn->in_thread_final.hooks[i]))() ;
+ for (i = qpn->in_thread_final.count; i > 0 ;)
+ ((qpn_init_function*)(qpn->in_thread_final.hooks[--i]))() ;
return NULL;
}
diff --git a/lib/qpnexus.h b/lib/qpnexus.h
index c2cc6463..f4195a4d 100644
--- a/lib/qpnexus.h
+++ b/lib/qpnexus.h
@@ -150,6 +150,9 @@ extern qpn_nexus qpn_init_new(qpn_nexus qpn, int 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_free(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)
#endif /* _ZEBRA_QPNEXUS_H */
diff --git a/lib/qpselect.c b/lib/qpselect.c
index 882f4173..3cca3805 100644
--- a/lib/qpselect.c
+++ b/lib/qpselect.c
@@ -503,17 +503,32 @@ qps_file_init_new(qps_file qf, qps_file template)
} ;
/*------------------------------------------------------------------------------
- * Free dynamically allocated qps_file structure.
+ * Free dynamically allocated qps_file structure -- if any.
*
- * It is the caller's responsibility to have removed it from any selection it
- * may have been in.
+ * Removes from any selection may be a member of.
+ *
+ * If there is a valid fd -- close it !
+ *
+ * Returns: NULL
*/
-void
+extern qps_file
qps_file_free(qps_file qf)
{
- assert(qf->selection == NULL) ; /* Mustn't be a selection member ! */
+ if (qf != NULL)
+ {
+ if (qf->selection != NULL)
+ qps_remove_file(qf) ;
+
+ if (qf->fd >= 0)
+ {
+ close(qf->fd) ;
+ qf->fd = fd_undef ;
+ } ;
+
+ XFREE(MTYPE_QPS_FILE, qf) ;
+ } ;
- XFREE(MTYPE_QPS_FILE, qf) ;
+ return NULL ;
} ;
/*------------------------------------------------------------------------------
@@ -574,7 +589,7 @@ qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action)
/*------------------------------------------------------------------------------
* Disable file for one or more modes.
*
- * If there are any pending pending results for the modes, those are discarded.
+ * If there are any pending results for the modes, those are discarded.
*
* Note that this is modestly "optimised" to deal with disabling a single mode.
* (Much of the time only the write mode will be being disabled !)
diff --git a/lib/qpselect.h b/lib/qpselect.h
index 561eebb2..8901ea36 100644
--- a/lib/qpselect.h
+++ b/lib/qpselect.h
@@ -199,7 +199,7 @@ qps_dispatch_next(qps_selection qps) ;
extern qps_file
qps_file_init_new(qps_file qf, qps_file template) ;
-extern void
+extern qps_file
qps_file_free(qps_file qf) ;
extern void
diff --git a/lib/qpthreads.h b/lib/qpthreads.h
index 4d71e12d..d73182ef 100644
--- a/lib/qpthreads.h
+++ b/lib/qpthreads.h
@@ -27,6 +27,7 @@
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
+#include <stdbool.h>
#include "zassert.h"
#include "qtime.h"
@@ -142,20 +143,30 @@ private int
qpt_freeze_qpthreads_enabled(void) ; /* get and freeze qpthreads_enabled */
/*==============================================================================
- * Thread self knowledge -- returns 'NULL' if !qpthreads_enabled
+ * Thread self knowledge -- even when !qpthreads_enabled there is one thread
*/
Inline qpt_thread_t qpt_thread_self(void)
{
- return qpthreads_enabled ? pthread_self() : (qpt_thread_t)NULL;
+ return pthread_self() ;
} ;
-/*==============================================================================
- * Thread equality -- returns non-zero (true) if threads are *equal*
- * -- all threads are equal if !qpthreads_enabled
+/*------------------------------------------------------------------------------
+ * Thread equality -- returns true iff threads are *equal*
+ * -- even when !qpthreads_enabled there is one thread
+ */
+Inline bool qpt_threads_equal(qpt_thread_t a_id, qpt_thread_t b_id)
+{
+ return pthread_equal(a_id, b_id) != 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Thread identity -- returns true iff current thread is the given thread
+ * -- even when !qpthreads_enabled there is one thread
*/
-Inline int qpt_threads_equal(qpt_thread_t a, qpt_thread_t b)
+Inline bool qpt_thread_is_self(qpt_thread_t id)
{
- return !qpthreads_enabled || pthread_equal(a, b) ? 1 : 0 ;
+ pthread_t self = pthread_self() ;
+ return pthread_equal(self, id) != 0 ;
} ;
/*==============================================================================
diff --git a/lib/qstring.c b/lib/qstring.c
index f847e0b0..a3dc95cd 100644
--- a/lib/qstring.c
+++ b/lib/qstring.c
@@ -35,29 +35,18 @@
* Returns: address of qstring
*
* NB: assumes initialising a new structure. If not, then caller should
- * use qs_reset() or qs_set_empty().
+ * use qs_reset() or qs_clear().
*/
extern qstring
qs_init_new(qstring qs, size_t len)
{
if (qs == NULL)
- qs = XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
+ qs = qs_new() ;
else
- memset(qs, 0, sizeof(qstring_t)) ;
-
- /* Zeroising has set:
- *
- * body = NULL -- no body
- * size = 0 -- no body
- *
- * len = 0
- * cp = 0
- *
- * Nothing more to do unless initial size != 0
- */
+ memset(qs, 0, sizeof(qstring_t)) ; /* see qs_new() */
if (len != 0)
- qs_alloc(qs, len) ;
+ return qs_make_to_length(qs, len) ;
return qs ;
} ;
@@ -65,48 +54,102 @@ qs_init_new(qstring qs, size_t len)
/*------------------------------------------------------------------------------
* Allocate or reallocate so that string is big enough for the given length.
*
- * Allocates to 16 byte boundaries.
+ * Allocate qstring if required. Returns with a body with size > 0.
*
- * Returns: the number of bytes *allocated*, which includes the byte for
- * possible trailing '\0'.
+ * Allocates to 16 byte boundaries with space for '\0' beyond given length.
*
- * NB: allocates EXTRA space for trailing '\0' beyond given length.
+ * Returns: address of qstring
+ *
+ * NB: allocates new body if the size == 0.
+ *
+ * If the qstring is a "dummy", its contents are now copied to the new
+ * real qstring body -- up to a maximum of the new length.
*/
-extern size_t
-qs_alloc(qstring qs, size_t len)
+extern qstring
+qs_make_to_length(qstring qs, size_t len)
{
- len = (len + 0x10) & ~(size_t)(0x10 - 1) ;
+ size_t size = (len + 0x10) & ~(size_t)(0x10 - 1) ;
- if (qs->body == NULL)
- {
- assert(qs->size == 0) ;
- qs->size = len ;
- qs->body = XMALLOC(MTYPE_QSTRING_BODY, qs->size) ;
- }
- else
+ if (qs == NULL)
+ qs = qs_new() ;
+
+ if (size > qs->size)
{
- assert(qs->size > 0) ;
- qs->size *= 2 ;
- if (qs->size < len)
- qs->size = len ;
- qs->body = XREALLOC(MTYPE_QSTRING_BODY, qs->body, qs->size) ;
- } ;
+ if (qs->size == 0)
+ {
+ void* old ;
+ old = qs->body ;
+
+ qs->size = size ;
+ qs->body = XMALLOC(MTYPE_QSTRING_BODY, qs->size) ;
+
+ if ((qs->len != 0) && (old != NULL))
+ memcpy(qs->body, old, (qs->len <= len) ? qs->len : len) ;
+ }
+ else
+ {
+ qs->size *= 2 ;
+ if (qs->size < size)
+ qs->size = size ;
+ qs->body = XREALLOC(MTYPE_QSTRING_BODY, qs->body, qs->size) ;
+ } ;
+ };
- return qs->size ;
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Add 'n' to the current string length, allocating or extending the body as
+ * required.
+ *
+ * Allocate qstring if required. Returns with a body with size > 0.
+ *
+ * Allocates to 16 byte boundaries with space for '\0' beyond new length.
+ *
+ * Returns: address of qstring
+ *
+ * also: sets char** p_ep to point at the *end* of the new len.
+ *
+ * NB: allocates new body if the size == 0.
+ *
+ * If the qstring is a "dummy", its contents are now copied to the new
+ * real qstring body -- up to a maximum of the new length.
+ */
+extern qstring
+qs_add_len(qstring qs, size_t n, char** p_ep)
+{
+ size_t len ;
+ len = (qs != NULL) ? qs->len + n : n ;
+
+ qs = qs_make_to_length(qs, len) ;
+
+ qs->len = len ;
+
+ *p_ep = (char*)qs->body + len ;
+
+ return qs ;
} ;
/*------------------------------------------------------------------------------
* Free body of qstring -- zeroise size, len and cp
+ *
+ * Does nothing if qstring is NULL
+ *
+ * NB: frees the body if the size != 0. So, a "dummy" qstring will not retain
+ * the old body.
*/
extern void
qs_free_body(qstring qs)
{
- if (qs->body != NULL)
- XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
+ if (qs != NULL)
+ {
+ if (qs->size != 0)
+ XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
- qs->size = 0 ;
- qs->len = 0 ;
- qs->cp = 0 ;
+ qs->size = 0 ;
+ qs->len = 0 ;
+ qs->cp = 0 ;
+ } ;
} ;
/*------------------------------------------------------------------------------
@@ -115,73 +158,102 @@ qs_free_body(qstring qs)
* If not freeing the structure, zeroise size, len and cp -- qs_free_body()
*
* Returns: NULL if freed the structure
- * address of structure, otherwise
+ * address of structure (if any), otherwise
+ *
+ * NB: frees the body if the size != 0. So, a "dummy" qstring will not retain
+ * the old body.
*/
extern qstring
qs_reset(qstring qs, int free_structure)
{
- if (qs->body != NULL)
- XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
-
- if (free_structure)
- XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */
- else
+ if (qs != NULL)
{
- qs->size = 0 ;
- qs->len = 0 ;
- qs->cp = 0 ;
- } ;
+ if (qs->size != 0)
+ XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
+ if (free_structure)
+ XFREE(MTYPE_QSTRING, qs) ; /* sets qs = NULL */
+ else
+ {
+ qs->size = 0 ;
+ qs->len = 0 ;
+ qs->cp = 0 ;
+ } ;
+ } ;
return qs ;
} ;
/*==============================================================================
- * printf(0 and vprintf() type functions
+ * printf() and vprintf() type functions
*/
/*------------------------------------------------------------------------------
* Formatted print to qstring -- cf printf()
+ *
+ * Allocate qstring if required.
+ *
+ * Returns: address of qstring if OK
+ * NULL if failed (unlikely though that is)
*/
-extern int
+extern qstring
qs_printf(qstring qs, const char* format, ...)
{
va_list args;
- int result ;
va_start (args, format);
- result = qs_vprintf(qs, format, args);
+ qs = qs_vprintf(qs, format, args);
va_end (args);
- return result;
+ return qs;
} ;
/*------------------------------------------------------------------------------
* Formatted print to qstring -- cf vprintf()
*
- * 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'.
+ * Allocate qstring if required.
*
- * Also note that given a zero length the string address may be NULL, and the
- * result is still the length required.
+ * Returns: address of qstring if OK
+ * NULL if failed (unlikely though that is)
*/
-extern int
+extern qstring
qs_vprintf(qstring qs, const char *format, va_list args)
{
va_list ac ;
- int len ;
+ int len ;
+ qstring qqs ;
+
+ qqs = qs ;
+ if (qs == NULL)
+ qs = qs_new() ;
while (1)
{
+ /* 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'.
+ *
+ * Also note that given a zero length the string address may be NULL, and
+ * the result is still the length required.
+ */
va_copy(ac, args);
qs->len = len = vsnprintf (qs->body, qs->size, format, ac) ;
va_end(ac);
+ if (len < 0)
+ break ;
+
if (len < (int)qs->size)
- return len ; /* quit if done (or error) */
+ return qs ;
- qs_alloc(qs, len) ;
+ qs_make_to_length(qs, len) ;
} ;
+
+ if (qqs == NULL)
+ qs_reset_free(qs) ; /* discard what was allocated */
+ else
+ qs->len = 0 ;
+
+ return NULL ;
} ;
/*==============================================================================
@@ -191,37 +263,249 @@ qs_vprintf(qstring qs, const char *format, va_list args)
/*------------------------------------------------------------------------------
* Set qstring to be copy of the given string.
*
+ * Allocates a qstring, if required.
+ *
* Sets qs->len to the length of the string (excluding trailing '\0')
*
- * NB: if stc == NULL, sets qstring to be zero length string.
+ * NB: if src == NULL, sets qstring to be zero length string.
+ *
+ * Returns: address of the qstring copied to.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
*/
-extern size_t
+extern qstring
qs_set(qstring qs, const char* src)
{
- qs_set_len(qs, (src != NULL) ? strlen(src) : 0) ;
+ qs = qs_set_len(qs, (src != NULL) ? strlen(src) : 0) ;
if (qs->len != 0)
memcpy(qs->body, src, qs->len + 1) ;
else
*((char*)qs->body) = '\0' ;
- return qs->len ;
+ return qs ;
} ;
/*------------------------------------------------------------------------------
* Set qstring to be leading 'n' bytes of given string.
*
- * NB: src string MUST be at least that long.
+ * Allocates qstring if required.
+ *
+ * Inserts '\0' terminator after the 'n' bytes copied.
+ *
+ * Returns: address of the qstring copied to.
*
- * NB: src may not be NULL unless len == 0.
+ * NB: src string MUST be at least 'n' bytes long.
+ *
+ * NB: src may not be NULL unless n == 0.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
*/
-extern size_t
+extern qstring
qs_set_n(qstring qs, const char* src, size_t n)
{
- qs_need(qs, n) ; /* sets qs->len */
+ qs = qs_set_len(qs, n) ; /* ensures have body > n */
if (n != 0)
memcpy(qs->body, src, n) ;
*((char*)qs->body + n) = '\0' ;
- return n ;
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append given string to a qstring.
+ *
+ * Allocates a qstring, if required.
+ *
+ * Sets qs->len to the length of the result (excluding trailing '\0')
+ *
+ * NB: if src == NULL, appends nothing -- but result will be '\0' terminated.
+ *
+ * Returns: address of the qstring copied to.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ */
+extern qstring qs_append(qstring qs, const char* src)
+{
+ size_t n ;
+ char* ep ;
+
+ n = (src != NULL) ? strlen(src) : 0 ;
+
+ qs = qs_add_len(qs, n, &ep) ;
+ ep = (char*)qs->body + qs->len ;
+
+ if (n != 0)
+ memcpy(ep - n, src, n + 1) ;
+ else
+ *ep = '\0' ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be leading 'n' bytes of given string.
+ *
+ * Allocates qstring if required.
+ *
+ * Returns: address of the qstring copied to.
+ *
+ * NB: src string MUST be at least 'n' bytes long.
+ *
+ * NB: src may not be NULL unless n == 0.
+ *
+ * NB: if n == 0, appends nothing -- but result will be '\0' terminated.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ */
+extern qstring
+qs_append_n(qstring qs, const char* src, size_t n)
+{
+ char* ep ;
+
+ qs = qs_add_len(qs, n, &ep) ;
+
+ if (n != 0)
+ memcpy(ep - n, src, n) ;
+
+ *ep = '\0' ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Copy one qstring to another
+ *
+ * If both are NULL, returns NULL.
+ *
+ * Otherwise if dst is NULL, creates a new qstring.
+ *
+ * Sets dst: body = copy of src->len bytes of src->body -- '\0' terminated.
+ * cp = src->cp
+ * len = src->len
+ *
+ * Where a NULL src has zero cp and len.
+ *
+ * If not NULL, the destination is guaranteed to have a body, and that will be
+ * '\0' terminated.
+ *
+ * Returns: the destination qstring
+ *
+ * NB: if copying to a dummy qstring, the old body is simply discarded.
+ */
+extern qstring
+qs_copy(qstring dst, qstring src)
+{
+ size_t n ;
+
+ if (src == NULL)
+ {
+ if (dst == NULL)
+ return dst ;
+
+ n = 0 ;
+ dst->cp = 0 ;
+ }
+ else
+ {
+ if (dst == NULL)
+ dst = qs_new() ;
+
+ n = src->len ;
+ dst->cp = src->cp ;
+ } ;
+
+ qs_set_len(dst, n) ;
+
+ if (n > 0)
+ memcpy(dst->body, src->body, n) ;
+
+ *((char*)dst->body + n) = '\0' ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Compare significant parts of two qstrings.
+ *
+ * By significant, mean excluding leading/trailing isspace() and treating
+ * multiple isspace() as single isspace().
+ *
+ * Compares the 'len' portions of the strings.
+ *
+ * If either is NULL, it is deemed to be an empty string.
+ *
+ * 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->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->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 ;
} ;
diff --git a/lib/qstring.h b/lib/qstring.h
index 1841657e..0597eda8 100644
--- a/lib/qstring.h
+++ b/lib/qstring.h
@@ -27,6 +27,8 @@
#include <stddef.h>
#include <stdint.h>
+#include "memory.h"
+
#ifndef Inline
#define Inline static inline
#endif
@@ -39,10 +41,14 @@
#endif /* __GNUC__ */
/*==============================================================================
- * These "qstrings" address the ...
- *
+ * These "qstrings" address address the lack of a flexible length string in 'C'.
*
+ * This is not a general purpose strings module, but provides a limited number
+ * of useful string operations such that the caller does not need to worry
+ * about the length of the string, and allocating space and so on.
*
+ * The caller does, however, have to explicitly release the contents of a
+ * qstring when it is done with.
*/
typedef struct qstring qstring_t ;
@@ -50,7 +56,13 @@ typedef struct qstring* qstring ;
struct qstring
{
- void* body ;
+ union
+ {
+ void* body ;
+ const void* const_body ;
+ char* char_body ;
+ unsigned char* uchar_body ;
+ } ;
size_t size ;
size_t len ;
@@ -115,117 +127,161 @@ qs_ep_byte(qstring qs)
* Functions
*/
-extern qstring
-qs_init_new(qstring qs, size_t len) ;
-
-extern size_t
-qs_alloc(qstring qs, size_t len) ;
-
-extern void
-qs_free_body(qstring qs) ;
-
-extern qstring
-qs_reset(qstring qs, int free_structure) ;
+extern qstring qs_init_new(qstring qs, size_t len) ;
+extern qstring qs_make_to_length(qstring qs, size_t len) ;
+extern void qs_free_body(qstring qs) ;
+extern qstring qs_reset(qstring qs, int free_structure) ;
#define qs_reset_keep(qs) qs_reset(qs, 0)
#define qs_reset_free(qs) qs_reset(qs, 1)
-extern int
-qs_printf(qstring qs, const char* format, ...) PRINTF_ATTRIBUTE(2, 3) ;
+Inline qstring qs_new(void) ;
+Inline qstring qs_dummy(qstring qs, const char* src, int pos) ;
-extern int
-qs_vprintf(qstring qs, const char *format, va_list args) ;
+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 size_t
-qs_set(qstring qs, const char* s) ;
+extern qstring qs_set(qstring qs, const char* src) ;
+extern qstring qs_set_n(qstring qs, const char* src, size_t n) ;
-extern size_t
-qs_set_n(qstring qs, const char* s, size_t len) ;
+extern qstring qs_append(qstring qs, const char* src) ;
+extern qstring qs_append_n(qstring qs, const char* src, size_t n) ;
-Inline size_t
-qs_need(qstring qs, size_t len) ;
+Inline qstring qs_need(qstring qs, size_t len) ;
+Inline qstring qs_set_len(qstring qs, size_t len) ;
+extern qstring qs_add_len(qstring qs, size_t n, char** p_ep) ;
+Inline void qs_clear(qstring qs) ;
+Inline size_t qs_len(qstring qs) ;
+Inline size_t qs_size(qstring qs) ;
+Inline void* qs_term(qstring qs) ;
-Inline size_t
-qs_set_len(qstring qs, size_t len) ;
+Inline size_t qs_insert(qstring qs, const void* src, size_t n) ;
+Inline void qs_replace(qstring qs, const void* src, size_t n) ;
+Inline size_t qs_delete(qstring qs, size_t n) ;
-Inline void
-qs_set_empty(qstring qs) ;
+extern qstring qs_copy(qstring dst, qstring src) ;
+extern int qs_cmp_sig(qstring a, qstring b) ;
-Inline size_t
-qs_len(qstring qs) ;
-
-Inline size_t
-qs_size(qstring qs) ;
-
-Inline void*
-qs_term(qstring qs) ;
+/*==============================================================================
+ * The Inline functions.
+ */
-Inline size_t
-qs_insert(qstring qs, const void* src, size_t n) ;
+/*------------------------------------------------------------------------------
+ * Make a brand new, completely empty qstring
+ */
+Inline qstring
+qs_new(void)
+{
+ /* Zeroising has set:
+ *
+ * body = NULL -- no body
+ * size = 0 -- no body
+ *
+ * len = 0
+ * cp = 0
+ *
+ * Nothing more to do unless initial size != 0
+ */
+ return XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
+} ;
-Inline void
-qs_replace(qstring qs, const void* src, size_t n) ;
+/*------------------------------------------------------------------------------
+ * Construct a "dummy" qstring from the given string.
+ *
+ * Allocates a qstring if required.
+ *
+ * This sets: body = the src
+ * len = strlen(src) (0 if src is NULL)
+ * cp = 0 if 'pos' is zero
+ * len otherwise
+ * size = 0
+ *
+ * The zero size means that the qstring handling will not attempt to free
+ * the body, nor will it write to it... Operations which require the qstring
+ * to have a size will allocate a new body, and discard this one.
+ *
+ * Returns: the address of the dummy qstring.
+ */
+Inline qstring
+qs_dummy(qstring qs, const char* src, int pos)
+{
+ if (qs == NULL)
+ qs = qs_new() ;
-Inline size_t
-qs_delete(qstring qs, size_t n) ;
+ qs->const_body = src ;
+ qs->len = (src != NULL) ? strlen(src) : 0 ;
+ qs->cp = (pos == 0) ? 0 : qs->len ;
+ qs->size = 0 ;
-/*==============================================================================
- * The Inline functions.
- */
+ return qs ;
+}
/*------------------------------------------------------------------------------
* Need space for a string of 'len' characters (plus possible '\0').
*
- * Returns: size of the qstring body
- * (which includes the extra space allowed for '\0')
+ * Allocates the qstring, if required.
+ *
+ * Returns: address of qstring
*
* NB: asking for 0 bytes will cause a body to be allocated, ready for any
* '\0' !
*
- * NB: has no effect on 'cp' or 'len'.
+ * NB: has no effect on 'cp' or 'len'. (Will be zero if new qstring allocated.)
*/
-Inline size_t
+Inline qstring
qs_need(qstring qs, size_t len)
{
- if (len < qs->size)
- {
- assert(qs->body != NULL) ;
- return qs->size ;
- }
- else
- return qs_alloc(qs, len) ;
+ if ((qs == NULL) || (len >= qs->size))
+ return qs_make_to_length(qs, len) ;
+
+ assert(qs->body != NULL) ;
+ return qs ;
} ;
/*------------------------------------------------------------------------------
* Set 'len' -- allocate or extend body as required.
*
- * Returns: size of the qstring body
- * (which includes the extra space allowed for '\0')
+ * Allocates the qstring, if required.
*
- * NB: asking for 0 bytes will cause a body to be allocated, ready for any
+ * Returns: address of qstring
+ *
+ * NB: setting len == 0 bytes will cause a body to be allocated, ready for any
* '\0' !
*
* NB: has no effect on 'cp' -- even if 'cp' > 'len'.
+ *
+ * NB: if this is a "dummy" qstring, a copy is made of the original body.
*/
-Inline size_t
+Inline qstring
qs_set_len(qstring qs, size_t len)
{
+ qs = qs_need(qs, len) ;
qs->len = len ;
- return qs_need(qs, len) ;
+ return qs ;
} ;
/*------------------------------------------------------------------------------
* Reset contents of qstring.
*
+ * Does nothing if qstring is NULL
+ *
* Sets 'cp' = 'len' = 0. Sets first byte of body (if any) to NULL.
+ *
+ * For "dummy" qstring, discards the body.
*/
Inline void
-qs_set_empty(qstring qs)
+qs_clear(qstring qs)
{
- qs->len = 0 ;
- qs->cp = 0 ;
- if (qs->body != NULL)
- *((char*)qs->body) = '\0' ;
+ if (qs != NULL)
+ {
+ qs->len = 0 ;
+ qs->cp = 0 ;
+ if (qs->size > 0)
+ *((char*)qs->body) = '\0' ;
+ else
+ qs->body = NULL ;
+ } ;
} ;
/*------------------------------------------------------------------------------
@@ -238,31 +294,63 @@ qs_set_empty(qstring qs)
Inline size_t
qs_len(qstring qs)
{
- return qs->len = (qs->body != NULL) ? strlen(qs_chars(qs)) : 0 ;
+ return (qs != NULL) ? (qs->len = (qs->body != NULL) ? strlen(qs_chars(qs))
+ : 0)
+ : 0 ;
} ;
/*------------------------------------------------------------------------------
* Get size of qstring body.
*
- * NB: if no body has been allocated, size = 0
+ * NB: if no body has been allocated, size == 0
+ * if qstring is NULL, size == 0
+ *
+ * NB: if this is a "dummy" qstring, size == 0.
*/
Inline size_t
qs_size(qstring qs)
{
- return qs->size ;
+ return (qs != NULL) ? qs->size : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get address of current end of qstring body -- ie byte at 'len'.
+ *
+ * NB: allocates body if required.
+ *
+ * There will be space for '\0' after 'len', so the address returned
+ * is within the real body of the string.
+ *
+ * NB: if this is a "dummy" qstring, a copy is made of the original body.
+ *
+ * NB: address of qstring may NOT be NULL.
+ */
+Inline void*
+qs_end(qstring qs)
+{
+ if (qs->len >= qs->size)
+ qs_make_to_length(qs, qs->len) ; /* allows for trailing '\0' */
+
+ return (char*)qs->body + qs->len ;
} ;
/*------------------------------------------------------------------------------
* Set '\0' at qs->len -- allocate or extend body as required.
*
- * Returns address of body.
+ * Returns address of body -- NULL if the qstring is NULL
+ *
+ * NB: if this is a "dummy" qstring, a copy is made of the original body.
*/
Inline void*
qs_term(qstring qs)
{
size_t len ;
+
+ if (qs == NULL)
+ return NULL ;
+
if ((len = qs->len) >= qs->size)
- qs_alloc(qs, len) ;
+ qs_make_to_length(qs, len) ;
*qs_chars_at(qs, len) = '\0' ;
@@ -276,10 +364,14 @@ qs_term(qstring qs)
*
* 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 "dummy" qstring, a copy is made of the original body.
*/
Inline size_t
qs_insert(qstring qs, const void* src, size_t n)
@@ -308,15 +400,19 @@ qs_insert(qstring qs, const void* src, size_t n)
*
* May increase 'len'. but does not affect 'cp'.
*
+ * 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 "dummy" qstring, a copy is made of the original body.
*/
Inline void
qs_replace(qstring qs, const void* src, size_t n)
{
- if (qs->len < qs->cp + n)
+ if ((qs->len < qs->cp + n) || (qs->size == 0))
qs_set_len(qs, qs->cp + n) ; /* set len and ensure have space */
if (n > 0)
@@ -330,6 +426,8 @@ qs_replace(qstring qs, const void* src, size_t n)
*
* 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.
*
@@ -341,6 +439,10 @@ qs_delete(qstring qs, size_t n)
size_t after ;
char* p ;
+ /* Watch out for "dummy" */
+ if (qs->size == 0)
+ qs_make_to_length(qs, qs->len) ;
+
/* If deleting up to or beyond len, then simply set len == cp */
if ((qs->cp + n) >= qs->len)
{
@@ -362,4 +464,5 @@ qs_delete(qstring qs, size_t n)
return after ;
} ;
+
#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qtimers.c b/lib/qtimers.c
index 508fc7d7..8c08a6bc 100644
--- a/lib/qtimers.c
+++ b/lib/qtimers.c
@@ -81,8 +81,11 @@ enum { qdebug =
* timer (which may, or may not, be the current qtimer time).
*
* During an action function timers may be set/unset, actions changed, and so
- * on... there are no restrictions EXCEPT that the qtimer structure may NOT be
- * freed.
+ * on... there are no restrictions EXCEPT that may NOT recurse into the
+ * dispatch function.
+ *
+ * If nothing is done with the time during the action function, the timer is
+ * implicitly unset when the action function returns.
*/
static int
@@ -99,7 +102,8 @@ qtimer_cmp(qtimer* a, qtimer* b) /* the heap discipline */
* qtimer_pile handling
*/
-/* Initialise a timer pile -- allocating it if required.
+/*------------------------------------------------------------------------------
+ * Initialise a timer pile -- allocating it if required.
*
* Returns the qtimer_pile.
*/
@@ -114,6 +118,7 @@ qtimer_pile_init_new(qtimer_pile qtp)
/* Zeroising has initialised:
*
* timers -- invalid heap -- need to properly initialise
+ * current = NULL -- no current timer
*/
/* (The typedef is required to stop Eclipse (3.4.2 with CDT 5.0) whining
@@ -126,13 +131,14 @@ qtimer_pile_init_new(qtimer_pile qtp)
return qtp ;
} ;
-/* Get the timer time for the first timer due to go off in the given pile.
+/*------------------------------------------------------------------------------
+ * Get the timer time for the first timer due to go off in the given pile.
*
* The caller must provide a maximum acceptable time. If the qtimer pile is
* empty, or the top entry times out after the maximum time, then the maximum
* is returned.
*/
-qtime_t
+extern qtime_t
qtimer_pile_top_wait(qtimer_pile qtp, qtime_t max_wait)
{
qtime_t top_wait ;
@@ -146,7 +152,8 @@ qtimer_pile_top_wait(qtimer_pile qtp, qtime_t max_wait)
return (top_wait < max_wait) ? top_wait : max_wait ;
} ;
-/* Dispatch the next timer whose time is <= the given "upto" time.
+/*------------------------------------------------------------------------------
+ * Dispatch the next timer whose time is <= the given "upto" time.
*
* The upto time must be a qtimer time (!) -- see qtimer_time_now().
*
@@ -155,8 +162,10 @@ qtimer_pile_top_wait(qtimer_pile qtp, qtime_t max_wait)
*
* Returns true <=> dispatched a timer, and there may be more to do.
* false <=> nothing to do (and nothing done).
+ *
+ * NB: it is a sad, very sad, mistake to recurse into this !
*/
-int
+extern bool
qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto)
{
qtimer qtr ;
@@ -165,23 +174,26 @@ qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto)
qtimer_pile_verify(qtp) ;
qtr = heap_top_item(&qtp->timers) ;
- if ((qtr != NULL) && (qtr->time <= upto))
- {
- passert(qtp == qtr->pile);
- qtr->state = qtr_state_unset_pending ;
- qtr->action(qtr, qtr->timer_info, upto) ;
+ if ((qtr == NULL) || (qtr->time > upto))
+ return 0 ;
- if (qtr->state == qtr_state_unset_pending)
- qtimer_unset(qtr) ;
+ passert((qtp == qtr->pile) && (qtr->active)) ;
- return 1 ;
- }
+ qtp->implicit_unset = qtr ; /* Timer must be unset if is still here
+ when the action function returns */
+ qtr->action(qtr, qtr->timer_info, upto) ;
+
+ if (qtp->implicit_unset == qtr)
+ qtimer_unset(qtr) ;
else
- return 0 ;
+ assert(qtp->implicit_unset == NULL) ; /* check for tidy-ness */
+
+ return 1 ;
} ;
-/* Ream out (another) item from qtimer_pile.
+/*------------------------------------------------------------------------------
+ * Ream out (another) item from qtimer_pile.
*
* If pile is empty, release the qtimer_pile structure, if required.
*
@@ -207,7 +219,7 @@ qtimer_pile_ream(qtimer_pile qtp, int free_structure)
qtr = heap_ream_keep(&qtp->timers) ; /* ream, keeping the heap structure */
if (qtr != NULL)
- qtr->state = qtr_state_inactive ; /* has been removed from pile */
+ qtr->active = false ; /* has been removed from pile */
else
if (free_structure) /* pile is empty, may now free it */
XFREE(MTYPE_QTIMER_PILE, qtp) ;
@@ -219,7 +231,8 @@ qtimer_pile_ream(qtimer_pile qtp, int free_structure)
* qtimer handling
*/
-/* Initialise qtimer structure -- allocating one if required.
+/*------------------------------------------------------------------------------
+ * Initialise qtimer structure -- allocating one if required.
*
* Associates qtimer with the given pile of timers, and sets up the action and
* the timer_info.
@@ -242,7 +255,7 @@ qtimer_init_new(qtimer qtr, qtimer_pile qtp,
* pile -- NULL -- not in any pile (yet)
* backlink -- unset
*
- * state -- not active
+ * active -- false
*
* time -- unset
* action -- NULL -- no action set (yet)
@@ -251,8 +264,6 @@ qtimer_init_new(qtimer qtr, qtimer_pile qtp,
* interval -- unset
*/
- confirm(qtr_state_inactive == 0) ;
-
qtr->pile = qtp ;
qtr->action = action ;
qtr->timer_info = timer_info ;
@@ -260,53 +271,54 @@ qtimer_init_new(qtimer qtr, qtimer_pile qtp,
return qtr ;
} ;
-/* Free given timer.
+/*------------------------------------------------------------------------------
+ * Free given timer -- if any.
*
- * Unsets it first if it is active.
+ * Unsets it first if it is active or pending unset.
*
- * The timer MAY NOT be currently the subject of qtimer_pile_dispatch_next().
+ * Returns: NULL
*/
-void
+extern qtimer
qtimer_free(qtimer qtr)
{
- assert(qtr->state != qtr_state_unset_pending) ;
+ /* Note that if is the current dispatched timer and an unset is still
+ * pending, then it must still be active.
+ */
+ if (qtr != NULL)
+ {
+ if (qtr->active)
+ qtimer_unset(qtr) ;
- if (qtr->state != qtr_state_inactive)
- qtimer_unset(qtr) ;
+ XFREE(MTYPE_QTIMER, qtr) ;
+ } ;
- XFREE(MTYPE_QTIMER, qtr) ;
+ return NULL ;
} ;
-/* Set pile in which given timer belongs.
+/*------------------------------------------------------------------------------
+ * Set pile in which given timer belongs.
+ *
+ * Does nothing if timer already belongs to the given pile.
*
- * Unsets the timer if active in another pile.
- * (Does nothing if active in the "new" pile.)
+ * Unsets the timer if active in another pile, before reassigning it.
*/
-void
+extern void
qtimer_set_pile(qtimer qtr, qtimer_pile qtp)
{
- if (qtr_is_active(qtr) && (qtr->pile != qtp))
+ if (qtr->pile == qtp)
+ return ;
+
+ /* Note that if is the current dispatched timer and an unset is still
+ * pending, then it must still be active.
+ */
+ if (qtr->active)
qtimer_unset(qtr) ;
+
qtr->pile = qtp ;
}
-/* Set action for given timer.
- */
-void
-qtimer_set_action(qtimer qtr, qtimer_action* action)
-{
- qtr->action = action ;
-} ;
-
-/* Set timer_info for given timer.
- */
-void
-qtimer_set_info(qtimer qtr, void* timer_info)
-{
- qtr->timer_info = timer_info ;
-} ;
-
-/* Set given timer.
+/*------------------------------------------------------------------------------
+ * Set given timer.
*
* Setting a -ve time => qtimer_unset.
*
@@ -319,7 +331,7 @@ qtimer_set_info(qtimer qtr, void* timer_info)
*
* It is an error to set a timer which has a NULL action.
*/
-void
+extern void
qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
{
qtimer_pile qtp ;
@@ -328,20 +340,30 @@ qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
return qtimer_unset(qtr) ;
qtp = qtr->pile ;
- dassert(qtp != NULL) ;
+ assert(qtp != NULL) ;
+
if (qdebug)
qtimer_pile_verify(qtp) ;
qtr->time = when ;
- if (qtr_is_active(qtr))
- heap_update_item(&qtp->timers, qtr) ; /* update in heap */
+ if (qtr->active)
+ {
+ /* Is active, so update the timer in the pile. */
+ heap_update_item(&qtp->timers, qtr) ;
+
+ if (qtr == qtp->implicit_unset)
+ qtp->implicit_unset = NULL ; /* no unset required, now */
+ }
else
- heap_push_item(&qtp->timers, qtr) ; /* add to heap */
+ {
+ /* Is not active, so insert the timer into the pile. */
+ heap_push_item(&qtp->timers, qtr) ;
- assert(qtp == qtr->pile);
+ assert(qtr != qtp->implicit_unset) ; /* because it's not active */
- qtr->state = qtr_state_active ; /* overrides any unset pending */
+ qtr->active = true ;
+ } ;
if (action != NULL)
qtr->action = action ;
@@ -352,28 +374,35 @@ qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
qtimer_pile_verify(qtp) ;
} ;
-/* Unset given timer
+/*------------------------------------------------------------------------------
+ * Unset given timer
*
* If the timer is active, removes from pile and sets inactive.
*/
-void
+extern void
qtimer_unset(qtimer qtr)
{
- if (qtr_is_active(qtr))
- {
- qtimer_pile qtp = qtr->pile ;
- dassert(qtp != NULL) ;
+ qtimer_pile qtp = qtr->pile ;
- if (qdebug)
- qtimer_pile_verify(qtp) ;
+ assert(qtp != NULL) ;
+
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
+
+ if (qtr->active)
+ {
+ if (qtr == qtp->implicit_unset)
+ qtp->implicit_unset = NULL ; /* no unset required, now */
heap_delete_item(&qtp->timers, qtr) ;
if (qdebug)
qtimer_pile_verify(qtp) ;
- qtr->state = qtr_state_inactive ; /* overrides any unset pending */
- } ;
+ qtr->active = false ;
+ }
+ else
+ assert(qtr != qtp->implicit_unset) ;
} ;
/*==============================================================================
@@ -387,6 +416,9 @@ qtimer_pile_verify(qtimer_pile qtp)
vector_index i ;
vector_index e ;
qtimer qtr ;
+ bool seen ;
+
+ assert(qtp != NULL) ;
/* (The typedef is required to stop Eclipse (3.4.2 with CDT 5.0) whining
* about first argument of offsetof().)
@@ -402,9 +434,17 @@ qtimer_pile_verify(qtimer_pile qtp)
for (i = 0 ; i < e ; ++i)
{
qtr = vector_get_item(v, i) ;
+ assert(qtr != NULL) ;
+
+ if (qtr == qtp->implicit_unset)
+ seen = 1 ;
+
+ assert(qtr->active) ;
assert(qtr->pile == qtp) ;
assert(qtr->backlink == i) ;
assert(qtr->action != NULL) ;
} ;
+
+ assert(seen || (qtp->implicit_unset == NULL)) ;
} ;
diff --git a/lib/qtimers.h b/lib/qtimers.h
index 0bc3d7a1..5beb931b 100644
--- a/lib/qtimers.h
+++ b/lib/qtimers.h
@@ -22,6 +22,8 @@
#ifndef _ZEBRA_QTIMERS_H
#define _ZEBRA_QTIMERS_H
+#include <stdbool.h>
+
#include "zassert.h"
#include "qtime.h"
#include "heap.h"
@@ -46,22 +48,12 @@ typedef struct qtimer_pile* qtimer_pile ;
typedef void (qtimer_action)(qtimer qtr, void* timer_info, qtime_mono_t when) ;
-enum qtimer_state {
- qtr_state_inactive = 0,
- qtr_state_active = 1, /* timer is active in its pile */
- qtr_state_unset_pending = 3 /* timer is active, but unset is pending */
-} ;
-
-#define qtr_is_active(qtr) ((qtr)->state != qtr_state_inactive)
-
-typedef enum qtimer_state qtimer_state_t ;
-
struct qtimer
{
qtimer_pile pile ; /* pile currently allocated to */
heap_backlink_t backlink ;
- qtimer_state_t state ;
+ bool active ; /* true => in the pile */
qtime_mono_t time ; /* current time to trigger action */
qtimer_action* action ;
@@ -72,74 +64,50 @@ struct qtimer
struct qtimer_pile
{
- struct heap timers ;
+ struct heap timers ;
+
+ qtimer implicit_unset ; /* used during dispatch */
} ;
/*==============================================================================
* Functions
*/
-qtimer_pile
-qtimer_pile_init_new(qtimer_pile qtp) ;
-
-int
-qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto) ;
-
-qtime_t
-qtimer_pile_top_wait(qtimer_pile qtp, qtime_t max_wait) ;
-
-qtimer
-qtimer_pile_ream(qtimer_pile qtp, int free_structure) ;
+extern qtimer_pile qtimer_pile_init_new(qtimer_pile qtp) ;
+extern bool qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto) ;
+extern qtime_t qtimer_pile_top_wait(qtimer_pile qtp, qtime_t max_wait) ;
+extern qtimer qtimer_pile_ream(qtimer_pile qtp, int free_structure) ;
/* Ream out qtimer pile and free the qtimer structure. */
#define qtimer_pile_ream_free(qtp) qtimer_pile_ream(qtp, 1)
/* Ream out qtimer pile but keep the qtimer structure. */
#define qtimer_pile_ream_keep(qtp) qtimer_pile_ream(qtp, 0)
-qtimer
-qtimer_init_new(qtimer qtr, qtimer_pile qtp,
+extern qtimer qtimer_init_new(qtimer qtr, qtimer_pile qtp,
qtimer_action* action, void* timer_info) ;
-void
-qtimer_set_pile(qtimer qtr, qtimer_pile qtp) ;
+extern void qtimer_set_pile(qtimer qtr, qtimer_pile qtp) ;
+Inline void qtimer_set_action(qtimer qtr, qtimer_action* action) ;
+Inline void qtimer_set_info(qtimer qtr, void* timer_info) ;
-void
-qtimer_set_action(qtimer qtr, qtimer_action* action) ;
+extern qtimer qtimer_free(qtimer qtr) ;
+extern void qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action) ;
+extern void qtimer_unset(qtimer qtr) ;
-void
-qtimer_set_info(qtimer qtr, void* timer_info) ;
+Inline void qtimer_add(qtimer qtr, qtime_t interval, qtimer_action* action) ;
+Inline qtime_mono_t qtimer_get(qtimer qtr) ;
+Inline void qtimer_set_interval(qtimer qtr, qtime_t interval,
+ qtimer_action* action) ;
+Inline void qtimer_add_interval(qtimer qtr, qtimer_action* action) ;
-void
-qtimer_free(qtimer qtr) ;
-
-void
-qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action) ;
-
-void
-qtimer_unset(qtimer qtr) ;
-
-Inline void
-qtimer_add(qtimer qtr, qtime_t interval, qtimer_action* action) ;
-
-Inline qtime_mono_t
-qtimer_get(qtimer qtr) ;
-
-Inline void
-qtimer_set_interval(qtimer qtr, qtime_t interval, qtimer_action* action) ;
-
-Inline void
-qtimer_add_interval(qtimer qtr, qtimer_action* action) ;
-
-Inline qtime_t
-qtimer_get_interval(qtimer qtr) ;
-
-void
-qtimer_pile_verify(qtimer_pile qtp) ;
+Inline qtime_t qtimer_get_interval(qtimer qtr) ;
+extern void qtimer_pile_verify(qtimer_pile qtp) ;
/*==============================================================================
* Inline functions
*/
-/* Set given timer to given time later than *its* current time.
+/*------------------------------------------------------------------------------
+ * Set given timer to given time later than *its* current time.
*/
Inline void
qtimer_add(qtimer qtr, qtime_t interval, qtimer_action* action)
@@ -147,7 +115,8 @@ qtimer_add(qtimer qtr, qtime_t interval, qtimer_action* action)
qtimer_set(qtr, qtimer_get(qtr) + interval, action);
} ;
-/* Get the given timer's time.
+/*------------------------------------------------------------------------------
+ * Get the given timer's time.
*/
Inline qtime_mono_t
qtimer_get(qtimer qtr)
@@ -155,6 +124,27 @@ qtimer_get(qtimer qtr)
return qtr->time ;
} ;
+/*------------------------------------------------------------------------------
+ * Set action for given timer -- setting a NULL action unsets the timer.
+ */
+Inline void
+qtimer_set_action(qtimer qtr, qtimer_action* action)
+{
+ if (action == NULL)
+ qtimer_unset(qtr) ;
+ qtr->action = action ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set timer_info for given timer.
+ */
+Inline void
+qtimer_set_info(qtimer qtr, void* timer_info)
+{
+ qtr->timer_info = timer_info ;
+} ;
+
+
/* Interval handling ---------------------------------------------------------*/
/* Set the interval field
diff --git a/lib/sockopt.c b/lib/sockopt.c
index 3e4580ac..ad4af9af 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -25,44 +25,56 @@
#include "sockunion.h"
int
-setsockopt_so_recvbuf (int sock, int size)
+setsockopt_so_recvbuf (int sock_fd, int size)
{
int ret;
- if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *)
- &size, sizeof (int))) < 0)
- zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
- sock,size,safe_strerror(errno));
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) ;
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_err ("socket %d: cannot setsockopt SO_RCVBUF to %d: %s",
+ sock_fd, size, safe_strerror(err)) ;
+ errno = err ;
+ } ;
return ret;
}
int
-setsockopt_so_sendbuf (const int sock, int size)
+setsockopt_so_sendbuf (const int sock_fd, int size)
{
- int ret = setsockopt (sock, SOL_SOCKET, SO_SNDBUF,
- (char *)&size, sizeof (int));
+ int ret ;
+
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
if (ret < 0)
- zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s",
- sock, size, safe_strerror (errno));
+ {
+ int err = errno ;
+ zlog_err ("socket %d: cannot setsockopt SO_SNDBUF to %d: %s",
+ sock_fd, size, safe_strerror (err));
+ errno = err ;
+ } ;
return ret;
}
int
-getsockopt_so_sendbuf (const int sock)
+getsockopt_so_sendbuf (const int sock_fd)
{
u_int32_t optval;
socklen_t optlen = sizeof (optval);
- int ret = getsockopt (sock, SOL_SOCKET, SO_SNDBUF,
- (char *)&optval, &optlen);
+
+ int ret = getsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen);
if (ret < 0)
{
- zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)",
- sock, errno, safe_strerror (errno));
+ int err = errno ;
+ zlog_err ("socket %d: cannot getsockopt SO_SNDBUF: %s",
+ sock_fd, safe_strerror (err));
+ errno = err ;
return ret;
}
+
return optval;
}
@@ -84,89 +96,125 @@ getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
#ifdef HAVE_IPV6
/* Set IPv6 packet info to the socket. */
int
-setsockopt_ipv6_pktinfo (int sock, int val)
+setsockopt_ipv6_pktinfo (int sock_fd, int val)
{
int ret;
#ifdef IPV6_RECVPKTINFO /*2292bis-01*/
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno));
+ {
+ int err = errno ;
+ zlog_warn ("cannot setsockopt IPV6_RECVPKTINFO: %s", safe_strerror (err));
+ errno = err ;
+ } ;
#else /*RFC2292*/
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno));
+ {
+ int err = errno ;
+ zlog_warn ("cannot setsockopt IPV6_PKTINFO: %s", safe_strerror (err));
+ errno = err ;
+ } ;
#endif /* INIA_IPV6 */
return ret;
}
/* Set multicast hops val to the socket. */
int
-setsockopt_ipv6_checksum (int sock, int val)
+setsockopt_ipv6_checksum (int sock_fd, int val)
{
int ret;
#ifdef GNU_LINUX
- ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
+ ret = setsockopt(sock_fd, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
#else
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
#endif /* GNU_LINUX */
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_CHECKSUM");
+ {
+ int err = errno ;
+ zlog_warn ("cannot setsockopt IPV6_CHECKSUM: %s", safe_strerror (err));
+ errno = err ;
+ } ;
return ret;
}
/* Set multicast hops val to the socket. */
int
-setsockopt_ipv6_multicast_hops (int sock, int val)
+setsockopt_ipv6_multicast_hops (int sock_fd, int val)
{
int ret;
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val,
+ sizeof(val));
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
+ {
+ int err = errno ;
+ zlog_warn ("cannot setsockopt IPV6_MULTICAST_HOPS: %s",
+ safe_strerror (err));
+ errno = err ;
+ } ;
return ret;
}
/* Set multicast hops val to the socket. */
int
-setsockopt_ipv6_unicast_hops (int sock, int val)
+setsockopt_ipv6_unicast_hops (int sock_fd, int val)
{
int ret;
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
+ ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
+ {
+ int err = errno ;
+ zlog_warn("cannot setsockopt IPV6_UNICAST_HOPS: %s", safe_strerror (err));
+ errno = err ;
+ } ;
return ret;
}
int
-setsockopt_ipv6_hoplimit (int sock, int val)
+setsockopt_ipv6_hoplimit (int sock_fd, int val)
{
int ret;
#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
+ ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val,
+ sizeof(val));
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
+ {
+ int err = errno ;
+ zlog_warn("cannot setsockopt IPV6_RECVHOPLIMIT: %s", safe_strerror (err));
+ errno = err ;
+ } ;
#else /*RFC2292*/
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
+ ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
+ {
+ int err = errno ;
+ zlog_warn ("cannot setsockopt IPV6_HOPLIMIT: %s", safe_strerror (err));
+ errno = err ;
+ } ;
#endif
return ret;
}
/* Set multicast loop zero to the socket. */
int
-setsockopt_ipv6_multicast_loop (int sock, int val)
+setsockopt_ipv6_multicast_loop (int sock_fd, int val)
{
int ret;
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
- sizeof (val));
+ ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
+ sizeof (val));
if (ret < 0)
- zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
+ {
+ int err = errno ;
+ zlog_warn ("cannot setsockopt IPV6_MULTICAST_LOOP: %s",
+ safe_strerror (err));
+ errno = err ;
+ } ;
return ret;
}
@@ -204,12 +252,14 @@ getsockopt_ipv6_ifindex (struct msghdr *msgh)
* allow leaves, or implicitly leave all groups joined to down interfaces.
*/
int
-setsockopt_multicast_ipv4(int sock,
- int optname,
- struct in_addr if_addr /* required */,
- unsigned int mcast_addr,
- unsigned int ifindex /* optional: if non-zero, may be
- used instead of if_addr */)
+setsockopt_multicast_ipv4(int sock_fd,
+ int optname,
+ struct in_addr if_addr /* required */,
+ unsigned int mcast_addr,
+ unsigned int ifindex /* optional: if non-zero,
+ may be used instead of
+ if_addr */
+ )
{
#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
@@ -232,31 +282,28 @@ setsockopt_multicast_ipv4(int sock,
else
mreqn.imr_address = if_addr;
- ret = setsockopt(sock, IPPROTO_IP, optname,
- (void *)&mreqn, sizeof(mreqn));
+ ret = setsockopt(sock_fd, IPPROTO_IP, optname, &mreqn, sizeof(mreqn));
if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
{
/* see above: handle possible problem when interface comes back up */
char buf[2][INET_ADDRSTRLEN];
zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
"re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
- sock,
+ sock_fd,
inet_ntop(AF_INET, &if_addr, buf[0], sizeof(buf[0])),
inet_ntop(AF_INET, &mreqn.imr_multiaddr,
buf[1], sizeof(buf[1])), ifindex);
- setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
- (void *)&mreqn, sizeof(mreqn));
- ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- (void *)&mreqn, sizeof(mreqn));
+ setsockopt(sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &mreqn, sizeof(mreqn));
+ ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreqn, sizeof(mreqn));
}
return ret;
- break;
default:
/* Can out and give an understandable error */
errno = EINVAL;
return -1;
- break;
}
/* Example defines for another OS, boilerplate off other code in this
@@ -281,7 +328,7 @@ setsockopt_multicast_ipv4(int sock,
switch (optname)
{
case IP_MULTICAST_IF:
- return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m));
+ return setsockopt (sock_fd, IPPROTO_IP, optname, (void *)&m, sizeof(m));
break;
case IP_ADD_MEMBERSHIP:
@@ -290,48 +337,56 @@ setsockopt_multicast_ipv4(int sock,
mreq.imr_multiaddr.s_addr = mcast_addr;
mreq.imr_interface = m;
- ret = setsockopt (sock, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq));
+ ret = setsockopt (sock_fd, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq));
if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
{
/* see above: handle possible problem when interface comes back up */
char buf[2][INET_ADDRSTRLEN];
zlog_info("setsockopt_multicast_ipv4 attempting to drop and "
"re-add (fd %d, ifaddr %s, mcast %s, ifindex %u)",
- sock,
+ sock_fd,
inet_ntop(AF_INET, &if_addr, buf[0], sizeof(buf[0])),
inet_ntop(AF_INET, &mreq.imr_multiaddr,
buf[1], sizeof(buf[1])), ifindex);
- setsockopt (sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
- (void *)&mreq, sizeof(mreq));
- ret = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- (void *)&mreq, sizeof(mreq));
+ setsockopt (sock_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ &mreq, sizeof(mreq));
+ ret = setsockopt (sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq));
}
return ret;
- break;
default:
/* Can out and give an understandable error */
errno = EINVAL;
return -1;
- break;
}
#endif /* #if OS_TYPE */
}
static int
-setsockopt_ipv4_ifindex (int sock, int val)
+setsockopt_ipv4_ifindex (int sock_fd, int val)
{
int ret;
#if defined (IP_PKTINFO)
- if ((ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val))) < 0)
- zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
- sock,val,safe_strerror(errno));
+ ret = setsockopt (sock_fd, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val)) ;
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
+ sock_fd, val, safe_strerror(err));
+ errno = err ;
+ } ;
#elif defined (IP_RECVIF)
- if ((ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val))) < 0)
- zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
- sock,val,safe_strerror(errno));
+ ret = setsockopt (sock_fd, IPPROTO_IP, IP_RECVIF, &val, sizeof (val)) ;
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
+ sock_fd, val, safe_strerror(err));
+ errno = err ;
+ } ;
#else
#warning "Neither IP_PKTINFO nor IP_RECVIF is available."
#warning "Will not be able to receive link info."
@@ -343,35 +398,42 @@ setsockopt_ipv4_ifindex (int sock, int val)
}
int
-setsockopt_ipv4_tos(int sock, int tos)
+setsockopt_ipv4_tos(int sock_fd, int tos)
{
int ret;
- ret = setsockopt (sock, IPPROTO_IP, IP_TOS, &tos, sizeof (tos));
+ ret = setsockopt (sock_fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos));
if (ret < 0)
- zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s",
- sock, tos, safe_strerror(errno));
+ {
+ int err = errno ;
+ zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s",
+ sock_fd, tos, safe_strerror(err));
+ errno = err ;
+ } ;
return ret;
}
int
-setsockopt_ifindex (int af, int sock, int val)
+setsockopt_ifindex (int af, int sock_fd, int val)
{
int ret = -1;
switch (af)
{
case AF_INET:
- ret = setsockopt_ipv4_ifindex (sock, val);
+ ret = setsockopt_ipv4_ifindex (sock_fd, val);
break;
#ifdef HAVE_IPV6
case AF_INET6:
- ret = setsockopt_ipv6_pktinfo (sock, val);
+ ret = setsockopt_ipv6_pktinfo (sock_fd, val);
break;
#endif
default:
zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
+ ret = -1 ;
+ errno = EINVAL;
+ break ;
}
return ret;
}
@@ -498,17 +560,17 @@ sockopt_iphdrincl_swab_systoh (struct ip *iph)
/*==============================================================================
* Set TCP MD5 signature socket option.
*
- * Returns: 0 => OK
- * errno => failed.
+ * Returns: >= 0 => OK
+ * < 0 => failed, see errno.
*
* NB: returns ENOSYS if TCP MD5 is not supported
*/
int
-sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
+sockopt_tcp_signature (int sock_fd, union sockunion *su, const char *password)
{
+#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX)
int ret ;
-#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX)
/* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's
* version of the Quagga patch (based on work by Rick Payne, and Bruce
* Simpson)
@@ -529,9 +591,9 @@ sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
cmd.keylen = (password != NULL ? strlen (password) : 0);
cmd.key = password;
- ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ;
+ ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ;
- return (ret >= 0) ? 0 : errno ;
+ return (ret >= 0) ? 0 : -1 ;
#elif HAVE_DECL_TCP_MD5SIG
#ifndef GNU_LINUX
@@ -545,11 +607,13 @@ sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
struct tcp_md5sig md5sig ;
union sockunion *su2 ;
union sockunion susock ;
+ int ret, err ;
/* Figure out whether the socket and the sockunion are the same family..
* adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
*/
- if ((ret = sockunion_getsockname(sock, &susock)) != 0)
+ ret = sockunion_getsockname(sock_fd, &susock) ;
+ if (ret < 0)
return ret ;
if (susock.sa.sa_family == su->sa.sa_family)
@@ -564,7 +628,7 @@ sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
#ifdef HAVE_IPV6
/* If this does not work, then all users of this sockopt will need to
- * differentiate between IPv4 and IPv6, and keep seperate sockets for
+ * differentiate between IPv4 and IPv6, and keep separate sockets for
* each.
*
* Sadly, it doesn't seem to work at present. It's unknown whether
@@ -590,65 +654,84 @@ sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
#endif /* GNU_LINUX */
- ret = setsockopt(sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig) ;
+ err = 0 ;
+ ret = setsockopt(sock_fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) ;
if (ret < 0)
{
- ret = errno ;
+ err = errno ;
/* ENOENT is harmless. It is returned when we clear a password for which
one was not previously set. */
- if (ret == ENOENT)
- ret = 0 ;
+ if (err == ENOENT)
+ err = 0 ;
else
- zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s",
- sock, safe_strerror(ret));
+ {
+ zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s",
+ sock_fd, safe_strerror(err));
+ errno = err ;
+ } ;
}
- else
- ret = 0 ; /* no error */
+
+ return (err == 0) ? 0 : -1 ;
#else /* HAVE_TCP_MD5SIG */
- ret = ENOSYS ; /* TCP MD5 is not supported */
+ errno = ENOSYS ; /* TCP MD5 is not supported */
+ return -1 ;
#endif /* !HAVE_TCP_MD5SIG */
-
- return ret;
} ;
/*==============================================================================
- * Set TTL for socket (only used in bgpd)
+ * Set TTL for socket
*
- * Returns: 0 : OK (so far so good)
- * != 0 : error number (from errno or otherwise)
+ * Returns: >= 0 => OK
+ * < 0 => failed, see errno.
*/
-
int
-sockopt_ttl (int family, int sock, int ttl)
+sockopt_ttl (int sock_fd, int ttl)
{
const char* msg ;
int ret ;
+ int family ;
ret = 0 ;
+ msg = NULL ;
+ family = sockunion_getsockfamily(sock_fd) ;
+ if (family < 0)
+ return -1 ;
+
+ switch (family)
+ {
+ case AF_INET:
#ifdef IP_TTL
- if (family == AF_INET)
- {
- ret = setsockopt (sock, IPPROTO_IP, IP_TTL,(void*)&ttl, sizeof(int));
- msg = "can't set sockopt IP_TTL %d to socket %d" ;
- }
+ ret = setsockopt (sock_fd, IPPROTO_IP, IP_TTL,(void*)&ttl, sizeof(int));
+ msg = "IP_TTL" ;
#endif /* IP_TTL */
+ break ;
+
#ifdef HAVE_IPV6
- if (family == AF_INET6)
- {
- ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
- (void*)&ttl, sizeof(int));
- msg = "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d" ;
- }
-#endif /* HAVE_IPV6 */
+ case AF_INET6:
+ ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ (void*)&ttl, sizeof(int));
+ msg = "IPV6_UNICAST_HOPS" ;
+ break ;
+#endif
- ret = (ret < 0) ? errno : 0 ;
+ default: /* ignore unknown family */
+ ret = 0 ;
+ break ;
+ } ;
if (ret != 0)
- zlog (NULL, LOG_WARNING, msg, ttl, sock) ;
-
- return ret ;
+ {
+ int err = errno ;
+ zlog (NULL, LOG_WARNING,
+ "cannot set sockopt %s %d to socket %d: %s", msg, ttl, sock_fd,
+ safe_strerror(err)) ;
+ errno = err ;
+ return -1 ;
+ } ;
+
+ return 0 ;
} ;
diff --git a/lib/sockopt.h b/lib/sockopt.h
index cb05c6fb..ad86f053 100644
--- a/lib/sockopt.h
+++ b/lib/sockopt.h
@@ -16,7 +16,7 @@
* 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.
+ * 02111-1307, USA.
*/
#ifndef _ZEBRA_SOCKOPT_H
@@ -24,9 +24,9 @@
#include "sockunion.h"
-extern int setsockopt_so_recvbuf (int sock, int size);
-extern int setsockopt_so_sendbuf (const int sock, int size);
-extern int getsockopt_so_sendbuf (const int sock);
+extern int setsockopt_so_recvbuf (int sock_fd, int size);
+extern int setsockopt_so_sendbuf (const int sock_fd, int size);
+extern int getsockopt_so_sendbuf (const int sock_fd);
#ifdef HAVE_IPV6
extern int setsockopt_ipv6_pktinfo (int, int);
@@ -82,25 +82,26 @@ extern int setsockopt_ipv6_multicast_loop (int, int);
(((af) == AF_INET) : SOPT_SIZE_CMSG_IFINDEX_IPV4() \
? SOPT_SIZE_CMSG_PKTINFO_IPV6())
-extern int setsockopt_multicast_ipv4(int sock, int optname,
+extern int setsockopt_multicast_ipv4(int sock_fd, int optname,
struct in_addr if_addr
/* required: interface to join on */,
unsigned int mcast_addr,
unsigned int ifindex
/* optional: if non-zero, may be used
instead of if_addr */);
-extern int setsockopt_ipv4_tos(int sock, int tos);
+extern int setsockopt_ipv4_tos(int sock_fd, int tos);
/* Ask for, and get, ifindex, by whatever method is supported. */
extern int setsockopt_ifindex (int, int, int);
extern int getsockopt_ifindex (int, struct msghdr *);
-/* swab the fields in iph between the host order and system order expected
+/* swab the fields in iph between the host order and system order expected
* for IP_HDRINCL.
*/
extern void sockopt_iphdrincl_swab_htosys (struct ip *iph);
extern void sockopt_iphdrincl_swab_systoh (struct ip *iph);
-extern int sockopt_tcp_signature(int sock, union sockunion *su,
+extern int sockopt_ttl (int sock_fd, int ttl);
+extern int sockopt_tcp_signature(int sock_fd, union sockunion *su,
const char *password);
#endif /*_ZEBRA_SOCKOPT_H */
diff --git a/lib/sockunion.c b/lib/sockunion.c
index 4043783c..95626513 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -139,7 +139,7 @@ sockunion_sin_len(sockunion su)
sizeof(struct sockaddr_in);
} ;
-#if HAVE_IPV6
+#ifdef HAVE_IPV6
inline static int
sockunion_sin6_len(sockunion su)
{
@@ -167,11 +167,11 @@ sockunion_set_family(sockunion su, sa_family_t family)
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
if (family == AF_INET)
- su->sin.sin_len = sizeof(struct sockaddr_in);
+ sockunion_sin_len(sockunion su) ;
#endif
#if defined(HAVE_IPV6) && defined(SIN6_LEN)
if (family == AF_INET6)
- su->sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sockunion_sin6_len(sockunion su) ;
#endif
return 0 ;
@@ -244,7 +244,7 @@ sockunion_init_new(sockunion su, sa_family_t family)
else
memset(su, 0, sizeof(union sockunion)) ;
- if (family != 0)
+ if (family != AF_UNSPEC)
sockunion_set_family(su, family) ;
return su ;
@@ -263,7 +263,7 @@ str2sockunion (const char *str, union sockunion *su)
assert(su != NULL) ;
- sockunion_init_new(su, 0) ;
+ sockunion_init_new(su, AF_UNSPEC) ;
ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
if (ret > 0) /* Valid IPv4 address format. */
@@ -341,23 +341,45 @@ sockunion_su2str (union sockunion *su, enum MTYPE type)
}
/*------------------------------------------------------------------------------
- * Convert IPv4 compatible IPv6 address to IPv4 address.
+ * If have an IPv6 mapped IPv4 address, convert it to an IPv4 address.
*/
-static void
-sockunion_normalise_mapped (union sockunion *su)
+extern void
+sockunion_unmap_ipv4 (union sockunion *su)
{
- struct sockaddr_in sin;
+#ifdef HAVE_IPV6
+ union sockunion sux ;
+ struct sockaddr_in* su_in = &sux.sin ;
+ if ( (su->sa.sa_family == AF_INET6)
+ && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) )
+ {
+ sockunion_init_new(&sux, AF_INET) ;
+ memcpy (&su_in->sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4) ;
+ su_in->sin_port = su->sin6.sin6_port ;
+ memcpy (su, &sux, sizeof(sux)) ;
+ confirm(sizeof(*su) == sizeof(sux)) ;
+ }
+#endif /* HAVE_IPV6 */
+}
+
+/*------------------------------------------------------------------------------
+ * If have an IPv4 address, convert it to an IPv6 mapped IPv4 address.
+ */
+extern void
+sockunion_map_ipv4 (union sockunion *su)
+{
#ifdef HAVE_IPV6
- if (su->sa.sa_family == AF_INET6
- && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr))
+ union sockunion sux ;
+ struct sockaddr_in6* su_in6 = &sux.sin6 ;
+
+ if (su->sa.sa_family == AF_INET)
{
- memset (&sin, 0, sizeof (struct sockaddr_in));
- sin.sin_family = AF_INET;
- sin.sin_port = su->sin6.sin6_port;
- memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4);
- memset (su, 0, sizeof(union sockunion)) ;
- memcpy (su, &sin, sizeof (struct sockaddr_in));
+ sockunion_init_new(&sux, AF_INET6) ;
+ memset (((char *)&su_in6->sin6_addr) + 10, 0xFF, 2) ;
+ memcpy (((char *)&su_in6->sin6_addr) + 12, &su->sin.sin_addr, 4) ;
+ su_in6->sin6_port = su->sin.sin_port ;
+ memcpy (su, &sux, sizeof(sux)) ;
+ confirm(sizeof(*su) == sizeof(sux)) ;
}
#endif /* HAVE_IPV6 */
}
@@ -372,31 +394,34 @@ sockunion_normalise_mapped (union sockunion *su)
*
* EINTR -- the usual suspect.
*
+ * Sets the given sockunion to the result of the accept(), converting any
+ * IPv6 mapped IPv4 addresses to IPv4 form. (Hiding the family for the socket.)
+ *
* Returns: >= 0 -- OK, this is the fd (socket)
* -1 -- error -- not one of the above
* -2 -- error -- one of the above
*/
int
-sockunion_accept (int sock, union sockunion *su)
+sockunion_accept (int sock_fd, union sockunion *su)
{
socklen_t len;
- int ret ;
+ int new_fd, err ;
- len = sizeof(union sockunion);
+ len = sizeof(*su);
memset(su, 0, len) ;
- ret = accept(sock, &su->sa, &len) ;
+ new_fd = accept(sock_fd, &su->sa, &len) ;
- if (ret >= 0)
+ if (new_fd >= 0)
{
- sockunion_normalise_mapped(su);
- return ret ; /* OK -- got socket */
+ sockunion_unmap_ipv4(su);
+ return new_fd ; /* OK -- got socket */
} ;
- ret = errno ;
- return ( (ret == EAGAIN)
- || (ret == EWOULDBLOCK)
- || (ret == ECONNABORTED)
- || (ret == EINTR) ) ? -2 : -1 ;
+ err = errno ;
+ return ( (err == EAGAIN)
+ || (err == EWOULDBLOCK)
+ || (err == ECONNABORTED)
+ || (err == EINTR) ) ? -2 : -1 ;
} ;
/*------------------------------------------------------------------------------
@@ -410,18 +435,20 @@ sockunion_accept (int sock, union sockunion *su)
extern int
sockunion_socket(sa_family_t family, int type, int protocol)
{
- int sockfd ;
+ int sock_fd ;
+ int err ;
- sockfd = socket(family, type, protocol);
- if (sockfd < 0)
- {
- zlog (NULL, LOG_WARNING,
- "Can't make socket family=%d, type=%d, protocol=%d : %s",
- (int)family, type, protocol, safe_strerror(errno)) ;
- return -1;
- }
+ sock_fd = socket(family, type, protocol);
+
+ if (sock_fd >= 0)
+ return sock_fd ;
- return sockfd ;
+ err = errno ;
+ zlog (NULL, LOG_WARNING,
+ "Can't make socket family=%d, type=%d, protocol=%d : %s",
+ (int)family, type, protocol, safe_strerror(err)) ;
+ errno = err ;
+ return -1;
}
/*------------------------------------------------------------------------------
@@ -454,12 +481,12 @@ sockunion_stream_socket (union sockunion *su)
* Logs a LOG_INFO message if fails.
*/
extern int
-sockunion_connect(int fd, union sockunion* peer_su, unsigned short port,
+sockunion_connect(int sock_fd, union sockunion* peer_su, unsigned short port,
unsigned int ifindex)
{
char buf[SU_ADDRSTRLEN] ;
union sockunion su ;
- int ret ;
+ int ret, err ;
int sa_len ;
memcpy(&su, peer_su, sizeof(union sockunion)) ;
@@ -486,13 +513,15 @@ sockunion_connect(int fd, union sockunion* peer_su, unsigned short port,
# endif /* KAME */
#endif /* HAVE_IPV6 */
- ret = connect(fd, &su.sa, sa_len) ;
+ ret = connect(sock_fd, &su.sa, sa_len) ;
+ err = (ret >= 0) ? 0 : errno ;
- if ((ret == 0) || (errno == EINPROGRESS))
+ if ((err == 0) || (err == EINPROGRESS))
return 0 ; /* instant success or EINPROGRESS as expected */
- zlog_info("can't connect to %s port %d fd %d : %s",
- sockunion2str(&su, buf, sizeof(buf)), port, fd, safe_strerror(errno)) ;
+ zlog_info("cannot connect to %s port %d socket %d : %s",
+ sockunion2str(&su, buf, sizeof(buf)), port, sock_fd, safe_strerror(err)) ;
+ errno = err ;
return ret ;
} ;
@@ -500,27 +529,26 @@ sockunion_connect(int fd, union sockunion* peer_su, unsigned short port,
/*------------------------------------------------------------------------------
* Start listening on given socket
*
- * Reports EINPROGRESS as success.
- *
- * TODO: discover how the ifindex thing is supposed to work !!
- *
* Returns: 0 : OK (so far so good)
* < 0 : failed -- see errno
*
* Logs a LOG_WARNING message if fails.
*/
extern int
-sockunion_listen(int fd, int backlog)
+sockunion_listen(int sock_fd, int backlog)
{
- int ret ;
+ int ret, err ;
- ret = listen(fd, backlog) ;
+ ret = listen(sock_fd, backlog) ;
if (ret == 0)
return 0 ;
- zlog (NULL, LOG_WARNING, "can't listen on fd %d : %s",
- fd, safe_strerror(errno)) ;
+ err = errno ;
+ zlog (NULL, LOG_WARNING, "cannot listen on socket %d : %s",
+ sock_fd, safe_strerror(err)) ;
+ errno = err ;
+
return ret ;
} ;
@@ -541,10 +569,11 @@ sockunion_listen(int fd, int backlog)
* < 0 => failed -- see errno
*/
int
-sockunion_bind (int sock, union sockunion *su, unsigned short port, void* any)
+sockunion_bind (int sock_fd, union sockunion *su, unsigned short port,
+ void* any)
{
int sa_len ;
- int ret;
+ int ret ;
char buf[SU_ADDRSTRLEN] ;
if (any == NULL)
@@ -552,10 +581,15 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port, void* any)
sa_len = sockunion_set_port(su, port) ;
- ret = bind (sock, &su->sa, sa_len);
+ ret = bind (sock_fd, &su->sa, sa_len);
if (ret < 0)
- zlog (NULL, LOG_WARNING, "can't bind to %s port %d fd %d : %s",
- sockunion2str(su, buf, sizeof(buf)), port, sock, safe_strerror(ret)) ;
+ {
+ int err = errno ;
+ zlog (NULL, LOG_WARNING, "cannot bind to %s port %d socket %d : %s",
+ sockunion2str(su, buf, sizeof(buf)), port, sock_fd,
+ safe_strerror(err)) ;
+ errno = err ;
+ } ;
return ret;
}
@@ -569,20 +603,23 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port, void* any)
* Logs a LOG_WARNING message if fails.
*/
int
-sockopt_reuseaddr (int sock)
+sockopt_reuseaddr (int sock_fd)
{
int ret;
int on = 1;
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
- (void *) &on, sizeof (on));
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on,
+ sizeof (on));
if (ret < 0)
{
+ int err = errno ;
zlog (NULL, LOG_WARNING,
- "can't set sockopt SO_REUSEADDR to socket %d", sock);
- return -1;
- }
- return 0;
+ "cannot set sockopt SO_REUSEADDR to socket %d %s", sock_fd,
+ safe_strerror(err));
+ errno = err ;
+ } ;
+
+ return ret ;
}
/*------------------------------------------------------------------------------
@@ -594,27 +631,34 @@ sockopt_reuseaddr (int sock)
* Logs a LOG_WARNING message if fails.
*/
int
-sockopt_reuseport (int sock)
+sockopt_reuseport (int sock_fd)
{
-#ifdef SO_REUSEPORT
int ret;
+
+#ifdef SO_REUSEPORT
int on = 1;
+ ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEPORT,
+ (void *) &on, sizeof (on));
+#else
+ ret = 0 ;
+#endif
- ret = setsockopt (sock, SOL_SOCKET, SO_REUSEPORT,
- (void *) &on, sizeof (on));
if (ret < 0)
{
+ int err = errno ;
zlog (NULL, LOG_WARNING,
- "can't set sockopt SO_REUSEPORT to socket %d", sock);
- return -1;
- }
-#endif
+ "cannot set sockopt SO_REUSEPORT to socket %d: %s", sock_fd,
+ safe_strerror(err));
+ errno = err ;
+ } ;
- return 0;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
* If same family and same prefix return 1.
+ *
+ * Returns 0 if same family, but not a known family.
*/
int
sockunion_same (union sockunion *su1, union sockunion *su2)
@@ -625,7 +669,7 @@ sockunion_same (union sockunion *su1, union sockunion *su2)
return 0;
switch (su1->sa.sa_family)
- {
+ {
case AF_INET:
return (su1->sin.sin_addr.s_addr == su2->sin.sin_addr.s_addr) ;
@@ -633,27 +677,62 @@ sockunion_same (union sockunion *su1, union sockunion *su2)
case AF_INET6:
ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
sizeof (struct in6_addr));
- break;
+ return (ret == 0) ;
#endif /* HAVE_IPV6 */
- }
- if (ret == 0)
- return 1;
- else
- return 0;
-}
+
+ default:
+ return 0 ;
+ } ;
+} ;
/*------------------------------------------------------------------------------
- * After TCP connection is established. Get local or remote address and port.
+ * Get the address family the given socket is set to.
*
- * Returns: 0 => OK
- * != 0 => failed, value = errno
+ * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0)
+ * < 0 => failed -- see errno
+ *
+ * NB: gets the actual address family -- does NOT look for mapped IPv4.
+ */
+extern int
+sockunion_getsockfamily(int sock_fd)
+{
+ union sockunion su ;
+ int ret ;
+ socklen_t len ;
+
+ if (sock_fd < 0)
+ return AF_UNSPEC ;
+
+ sockunion_init_new(&su, AF_UNSPEC) ;
+ len = sizeof(su) ;
+
+ ret = getsockname(sock_fd, (struct sockaddr *)&su, &len) ;
+ if (ret < 0)
+ {
+ int err = errno ;
+ zlog_warn ("Failed in getsockname for socket %d: %s",
+ sock_fd, safe_strerror(err)) ;
+ errno = err ;
+ return -1 ;
+ } ;
+
+ return su.sa.sa_family ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get local or remote address and port.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (or unknown family) -- see errno
+ *
+ * If address is an IPv4 mapped IPv6 address, returns the IPv4 address.
*
* NB: returns EAFNOSUPPORT if don't recognise the address family.
*/
static int
-sockunion_get_name(int fd, union sockunion* su, int local)
+sockunion_get_name(int sock_fd, union sockunion* su, int local)
{
- int ret;
+ int ret ;
union
{
@@ -671,18 +750,20 @@ sockunion_get_name(int fd, union sockunion* su, int local)
memset(su, 0, sizeof(union sockunion)) ;
if (local)
- ret = getsockname(fd, (struct sockaddr *)&name, &len) ;
+ ret = getsockname(sock_fd, (struct sockaddr *)&name, &len) ;
else
- ret = getpeername(fd, (struct sockaddr *)&name, &len) ;
+ ret = getpeername(sock_fd, (struct sockaddr *)&name, &len) ;
if (ret < 0)
{
- ret = errno ;
- zlog_warn ("Can't get %s address and port: %s",
- local ? "local" : "remote", safe_strerror(ret)) ;
+ int err = errno ;
+ zlog_warn ("Cannot get %s address and port: %s",
+ local ? "local" : "remote", safe_strerror(err)) ;
+ errno = err ;
return ret ;
}
+ ret = 0 ; /* assume all will be well */
switch (name.sa.sa_family)
{
case AF_INET:
@@ -692,43 +773,38 @@ sockunion_get_name(int fd, union sockunion* su, int local)
#ifdef HAVE_IPV6
case AF_INET6:
memcpy(su, &name, sizeof (struct sockaddr_in6)) ;
- sockunion_normalise_mapped(su) ;
+ sockunion_unmap_ipv4(su) ;
break ;
#endif /* HAVE_IPV6 */
default:
- ret = EAFNOSUPPORT ;
+ errno = EAFNOSUPPORT ;
+ ret = -1 ;
} ;
return ret ;
} ;
/*------------------------------------------------------------------------------
- * After TCP connection is established. Get local address and port.
+ * Get local address and port -- ie getsockname().
*
- * Returns: 0 => OK
- * != 0 => failed, value = errno
- *
- * NB: returns EAFNOSUPPORT if don't recognise the socket's address family.
+ * See: sockunion_get_name()
*/
int
-sockunion_getsockname(int fd, union sockunion* su_local)
+sockunion_getsockname(int sock_fd, union sockunion* su_local)
{
- return sockunion_get_name(fd, su_local, 1) ;
+ return sockunion_get_name(sock_fd, su_local, 1) ;
} ;
/*------------------------------------------------------------------------------
- * After TCP connection is established. Get remote address and port.
- *
- * Returns: 0 => OK
- * != 0 => failed, value = errno
+ * Get remote address and port -- ie getpeername().
*
- * NB: returns EAFNOSUPPORT if don't recognise the socket's address family.
+ * See: sockunion_get_name()
*/
int
-sockunion_getpeername (int fd, union sockunion* su_remote)
+sockunion_getpeername (int sock_fd, union sockunion* su_remote)
{
- return sockunion_get_name(fd, su_remote, 0) ;
+ return sockunion_get_name(sock_fd, su_remote, 0) ;
} ;
/*------------------------------------------------------------------------------
@@ -774,6 +850,12 @@ sockunion_print (union sockunion *su)
/*------------------------------------------------------------------------------
* Compare two sockunion values
+ *
+ * Compares family values first, then the body of the address.
+ *
+ * Returns: +1 => su1 > su2
+ * 0 => su1 == su2 (or same, but unknown, family)
+ * -1 => su1 < su2
*/
int
sockunion_cmp (union sockunion *su1, union sockunion *su2)
@@ -837,6 +919,8 @@ sockunion_free (union sockunion *su)
*
* It is the caller's responsibility to free the sockunion at some point.
* (See sockunion_free() or sockunion_unset().)
+ *
+ * For unknown family, returns an empty sockunion of that family.
*/
extern sockunion
sockunion_new_prefix(sockunion su, struct prefix* p)
@@ -870,32 +954,34 @@ sockunion_new_prefix(sockunion su, struct prefix* p)
} ;
/*------------------------------------------------------------------------------
- * Create new sockunion from given sockaddr.
+ * Create new sockunion from given sockaddr -- taking only the address part
*
* It is the caller's responsibility to free the sockunion at some point.
* (See sockunion_free() or sockunion_unset().)
+ *
+ * For unknown family, returns an empty sockunion of that family.
*/
extern sockunion
sockunion_new_sockaddr(sockunion su, struct sockaddr* sa)
{
sa_family_t family ;
- family = (sa != NULL) ? sa->sa_family : 0 ;
+ family = (sa != NULL) ? sa->sa_family : AF_UNSPEC ;
su = sockunion_init_new(su, family) ;
switch (family)
{
- case 0:
+ case AF_UNSPEC:
break ;
case AF_INET:
- su->sin = *(struct sockaddr_in*)sa ;
+ su->sin.sin_addr = ((struct sockaddr_in*)sa)->sin_addr ;
break ;
#ifdef HAVE_IPV6
case AF_INET6:
- su->sin6 = *(struct sockaddr_in6*)sa ;
+ su->sin6.sin6_addr = ((struct sockaddr_in6*)sa)->sin6_addr ;
break ;
#endif
diff --git a/lib/sockunion.h b/lib/sockunion.h
index d1f29b13..3f71cb55 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -55,13 +55,6 @@ union sockunion
#endif /* HAVE_IPV6 */
};
-enum connect_result
-{
- connect_error,
- connect_success,
- connect_in_progress
-};
-
/* Default address family. */
#ifdef HAVE_IPV6
#define AF_INET_UNION AF_INET6
@@ -109,19 +102,23 @@ extern int sockunion_same (union sockunion *, union sockunion *);
extern char* sockunion_su2str (union sockunion* su, enum MTYPE type) ;
extern union sockunion *sockunion_str2su (const char *str);
extern struct in_addr sockunion_get_in_addr (union sockunion *su);
-extern int sockunion_accept (int sock, union sockunion *);
+extern int sockunion_accept (int sock_fd, union sockunion *);
extern int sockunion_stream_socket (union sockunion *);
extern int sockopt_reuseaddr (int);
extern int sockopt_reuseport (int);
-extern int sockunion_bind (int sock, union sockunion *,
+extern int sockunion_bind (int sock_fd, union sockunion *,
unsigned short, void* any);
-extern int sockopt_ttl (int family, int sock, int ttl);
extern int sockunion_socket (sa_family_t family, int type, int protocol) ;
-extern int sockunion_connect (int fd, union sockunion *su, unsigned short port,
- unsigned int) ;
-extern int sockunion_listen(int fd, int backlog) ;
+extern int sockunion_connect (int sock_fd, union sockunion *su,
+ unsigned short port, unsigned int) ;
+extern int sockunion_listen(int sock_fd, int backlog) ;
+
+extern int sockunion_getsockfamily(int sock_fd) ;
extern int sockunion_getsockname (int, union sockunion*);
extern int sockunion_getpeername (int, union sockunion*);
+extern void sockunion_unmap_ipv4 (union sockunion *su) ;
+extern void sockunion_map_ipv4 (union sockunion *su) ;
+
extern union sockunion *sockunion_dup (union sockunion *);
extern void sockunion_free (union sockunion *);
diff --git a/lib/uty.h b/lib/uty.h
index 80a7cae3..d1a8b584 100644
--- a/lib/uty.h
+++ b/lib/uty.h
@@ -43,16 +43,56 @@
*
* vty
* command
+ * command_queue
* log
*
* and which is not for use elsewhere.
*
- * The two: vty_io and vty_cli are treated as private to vty. So anything
- * there that is used in command or log is published here. (Nobody other
- * than vty.c/vty_io.c/vty_cli.c will include either vty_io.h or vty_cli.h.)
+ * 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 ;
+
+union vty_watch_dog
+{
+ qtimer qnexus ; /* when running qnexus */
+ struct thread* thread; /* when running threads */
+ void* anon ;
+};
+
+extern union vty_watch_dog vty_watch_dog ;
+
+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
@@ -80,7 +120,7 @@ extern qpt_mutex_t vty_mutex ;
#if VTY_DEBUG
extern int vty_lock_count ;
-extern int vty_lock_assert_fail ;
+extern int vty_assert_fail ;
#endif
@@ -97,24 +137,45 @@ VTY_UNLOCK(void)
{
if (VTY_DEBUG)
--vty_lock_count ;
- qpt_mutex_lock(&vty_mutex) ;
+ qpt_mutex_unlock(&vty_mutex) ;
} ;
+/* 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_LOCKED(void)
+VTY_ASSERT_FAILED(void)
{
- if (vty_lock_count == 0 && !vty_lock_assert_fail)
+ if (vty_assert_fail == 0) ;
{
- vty_lock_assert_fail = 1;
- assert(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 (qpthreads_enabled)
+ if (!qpt_thread_is_self(vty_cli_nexus->thread_id))
+ VTY_ASSERT_FAILED() ;
} ;
#else
#define VTY_ASSERT_LOCKED()
+#define VTY_ASSERT_CLI_THREAD()
#endif
@@ -131,75 +192,48 @@ enum cli_do
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 */
} ;
/*==============================================================================
- * 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 ;
-
-union vty_watch_dog
-{
- qtimer qnexus ; /* when running qnexus */
- struct thread* thread; /* when running threads */
- void* anon ;
-};
-
-extern union vty_watch_dog vty_watch_dog ;
-
-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 ;
-
-/*==============================================================================
* Functions in vty.c -- used in any of the family
*/
-
-extern int uty_command(struct vty *vty, const char *buf) ;
-extern int uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do) ;
-extern int vty_cmd_exit(struct vty* vty) ;
-extern int vty_cmd_end(struct vty* vty) ;
-extern int uty_stop_input(struct vty *vty) ;
-extern int uty_end_config (struct vty *vty) ;
-extern int uty_down_level (struct vty *vty) ;
+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
+ * Functions in vty_cli -- used outside the immediate vty family
*/
-extern void
-vty_queued_result(struct vty* vty, int ret, int action);
-
-extern void
-uty_set_host_name(const char* name) ;
+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
- *
- * Send a fixed-size message to all vty terminal monitors; this should be
- * an async-signal-safe function.
+ * 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 3fb4cbd9..646f19a5 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -855,32 +855,26 @@ vector_sak(int to_copy, vector to,
* Legacy Vector Operations
*/
-/* This function only returns next empty slot index. It does not mean
- the slot's index memory is assigned, please call vector_ensure()
- after calling this function.
-
- Index returned is <= current (logical) end.
-*/
+/* Set value to the smallest empty slot. */
int
-vector_empty_slot (vector v)
+vector_set (vector v, void *val)
{
vector_index i;
- for (i = 0; i < v->end; i++)
- if (v->p_items[i] == NULL)
- break ;
+ i = 0 ;
+ while (1)
+ {
+ if (i == v->end)
+ {
+ i = vector_extend_by_1(v) ;
+ break ;
+ }
- return i;
-}
+ if (v->p_items[i] == NULL)
+ break ;
-/* Set value to the smallest empty slot. */
-int
-vector_set (vector v, void *val)
-{
- vector_index i;
- i = vector_empty_slot(v) ; /* NB: i <= v->end */
- if (i == v->end)
- i = vector_extend_by_1(v) ;
+ ++i ;
+ } ;
v->p_items[i] = val;
diff --git a/lib/vector.h b/lib/vector.h
index 727d2626..f098d78a 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -44,11 +44,14 @@ typedef struct vector* vector ; /* pointer to vector structure */
typedef struct vector vector_t ; /* embedded vector structure */
struct vector
{
- p_vector_item *p_items ; /* pointer to array of vector item pointers */
- vector_index end ; /* number of "active" item entries */
- vector_index limit ; /* number of allocated item entries */
+ p_vector_item* p_items ; /* pointer to array of vector item pointers */
+ vector_index end ; /* number of "active" item entries */
+ vector_index limit ; /* number of allocated item entries */
};
+/* Under very controlled circumstances, may access the vector body */
+typedef p_vector_item const* vector_body_t ;
+
/* Values that control the allocation of the vector body. */
/* NB: these must all be powers of 2. */
@@ -103,7 +106,6 @@ struct vector
extern vector vector_init (unsigned int size);
Inline void vector_ensure(vector v, vector_index i) ;
-extern int vector_empty_slot (vector v);
extern int vector_set (vector v, void *val);
extern int vector_set_index (vector v, vector_index i, void *val);
#define vector_unset(v, i) (void)vector_unset_item(v, i)
@@ -131,14 +133,20 @@ extern p_vector_item vector_ream(vector v, int free_structure) ;
Inline void vector_set_min_length(vector v, unsigned int len) ;
extern void vector_set_new_min_length(vector v, unsigned int len) ;
+Inline void vector_set_length(vector v, unsigned int len) ;
+#define vector_set_end(v, l) vector_set_length(v, l)
+
Inline vector_index vector_length(vector v) ;
#define vector_end(v) vector_length(v)
Inline int vector_is_empty(vector v) ;
+Inline vector_body_t vector_body(vector v) ;
+
Inline p_vector_item vector_get_item(vector v, vector_index i) ;
Inline p_vector_item vector_get_first_item(vector v) ;
Inline p_vector_item vector_get_last_item(vector v) ;
Inline void vector_set_item(vector v, vector_index i, p_vector_item p_v) ;
+Inline void vector_assign_item(vector v, vector_index dst, vector_index src) ;
extern p_vector_item vector_unset_item(vector v, vector_index i) ;
extern vector_index vector_trim(vector v) ;
extern vector_index vector_condense(vector v) ;
@@ -228,16 +236,34 @@ vector_ensure(vector v, vector_index i)
} ;
/* Want vector to be at least the given length. */
+/* */
/* Adjusts logical and physical end of the vector as required, filling */
/* with NULLs upto any new logical end -- does not allocate any more */
/* than is exactly necessary. */
Inline void
vector_set_min_length(vector v, unsigned int len)
{
- if (len > v->end) /* trivial if within vector */
+ if (len > v->end) /* will not reduce the length */
vector_set_new_min_length(v, len) ;
} ;
+/* Want vector to be the given length. */
+/* */
+/* If this is less than the current length, items are discarded. It */
+/* is the caller's responsibility to have freed anything that needs it. */
+/* */
+/* Adjusts logical and physical end of the vector as required, filling */
+/* with NULLs upto any new logical end -- does not allocate any more */
+/* than is exactly necessary. */
+Inline void
+vector_set_length(vector v, unsigned int len)
+{
+ if (len > v->end)
+ vector_set_new_min_length(v, len) ; /* Extend if new length greater */
+ else
+ v->end = len ; /* chop */
+} ;
+
/* Return index of end of vector (index of last item + 1) */
Inline vector_index
vector_length(vector v)
@@ -252,6 +278,19 @@ vector_is_empty(vector v)
return (v->end == 0) ;
} ;
+/* Returns highly restricted pointer to vector body */
+//Inline const void* const*
+//vector_body(vector v)
+//{
+// return (const void* const*)v->p_items ;
+//} ;
+
+Inline vector_body_t
+vector_body(vector v)
+{
+ return (vector_body_t)v->p_items ;
+} ;
+
/* Access functions -- Inline for obvious reasons. */
/* Get pointer to item. Returns NULL if accessing beyond end. */
@@ -285,6 +324,17 @@ vector_set_item(vector v, vector_index i, void* p_v)
v->p_items[i] = (p_vector_item)p_v ;
} ;
+/* Set dst item to be a copy of the src item. Extend vector if required.
+ *
+ * NB: it is the caller's responsibility to look after the memory being
+ * used by the current dst item or the new (duplicated) src item.
+ */
+Inline void
+vector_assign_item(vector v, vector_index dst, vector_index src)
+{
+ vector_set_item(v, dst, vector_get_item(v, src)) ;
+} ;
+
/* Push value onto vector, extending as required. */
Inline void
vector_push_item(vector v, void* p_v)
diff --git a/lib/vio_fifo.c b/lib/vio_fifo.c
index fb62f192..685f33ec 100644
--- a/lib/vio_fifo.c
+++ b/lib/vio_fifo.c
@@ -23,6 +23,7 @@
#include <string.h>
#include "vio_fifo.h"
+#include "network.h"
#include "list_util.h"
#include "memory.h"
@@ -37,6 +38,9 @@
* The last lump is never released. So, it may be that only one lump is
* ever needed.
*
+ * When releasing lumps, keeps one lump "spare", to be reused as necessary.
+ * This is used in ... TODO <<<< And is released...
+ *
*------------------------------------------------------------------------------
* Implementation notes:
*
@@ -114,13 +118,18 @@ vio_fifo_init_new(vio_fifo vf, size_t size)
/* Zeroising the the vio_fifo_t has set:
*
- * lump -- base pair, both pointers NULL => list is empty
+ * lump -- 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
+ *
+ * get_ptr -- NULL ) no lump to get anything from
+ * get_end -- NULL ) get_ptr -- get_end => nothing left in current lump
*
- * put_ptr -- NULL ) no lump to put anything into
- * put_end -- NULL ) put_ptr == put_end => no room in current lump
+ * rdr_lump -- NULL ) no rdr_lump
+ * rdr_ptr -- NULL
*
- * get_ptr -- NULL ) no lump to get anything from
- * get_end -- NULL ) get_ptr -- get_end => nothing left in current lump
+ * spare -- NULL no spare lump
*
* ALSO put_ptr == get_ptr => FIFO is empty !
*/
@@ -139,6 +148,8 @@ vio_fifo_init_new(vio_fifo vf, size_t size)
*
* If does not free the FIFO structure, resets it all empty.
*
+ * Frees *all* FIFO lumps.
+ *
* See also: vio_fifo_reset_keep(vio_fifo)
* vio_fifo_reset_free(vio_fifo)
*/
@@ -153,6 +164,9 @@ vio_fifo_reset(vio_fifo vf, int free_structure)
while (ddl_pop(&lump, vf->base, list) != NULL)
XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
+ if (vf->spare != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ;
+
if (free_structure)
XFREE(MTYPE_VIO_FIFO, vf) ; /* sets vf = NULL */
else
@@ -162,33 +176,105 @@ vio_fifo_reset(vio_fifo vf, int free_structure)
} ;
/*------------------------------------------------------------------------------
- * Set FIFO empty, discarding current contents -- will continue to use the FIFO.
+ * The FIFO is empty, with one lump -- reset all pointers.
+ */
+inline static void
+vio_fifo_ptr_reset(vio_fifo vf, 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 ;
+ } ;
+
+ /* 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * The FIFO is utterly empty, with ZERO lumps -- unset all pointers.
+ */
+inline static void
+vio_fifo_ptr_unset(vio_fifo vf)
+{
+ assert((ddl_head(vf->base) == NULL) && (ddl_tail(vf->base) == NULL)) ;
+
+ vf->one = false ;
+
+ vf->put_ptr = NULL ;
+ vf->put_end = NULL ;
+ vf->get_ptr = NULL ;
+ vf->get_end = NULL ;
+
+ vf->rdr_lump = NULL ;
+ vf->rdr_ptr = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear out contents of FIFO -- will continue to use the FIFO.
+ *
+ * Keeps one FIFO lump. (Frees everything else, including any spare.)
*/
extern void
-vio_fifo_set_empty(vio_fifo vf)
+vio_fifo_clear(vio_fifo vf)
{
- vio_fifo_lump lump ;
+ vio_fifo_lump tail ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
assert(vf != NULL) ;
- while (ddl_head(vf->base) != ddl_tail(vf->base))
- {
- ddl_pop(&lump, vf->base, list) ;
- XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
- } ;
+ tail = ddl_tail(vf->base) ;
- lump = ddl_head(vf->base) ;
- if (lump != NULL)
+ if (tail != NULL)
{
- vf->get_ptr = vf->get_end = vf->put_ptr = lump->data ;
- vf->put_end = lump->end ;
- } ;
+ while (ddl_head(vf->base) != tail)
+ {
+ vio_fifo_lump lump ;
+ ddl_pop(&lump, vf->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) ;
+
+ if (vf->spare != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ; /* sets vf->spare = NULL */
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See how much room there is in the FIFO.
+ *
+ * If no lumps have been allocated, returns the size of the lump that would
+ * allocate.
+ *
+ * Otherwise, returns the amount of space available *without* allocating any
+ * further lumps.
+ *
+ * Returns: room available as described
+ */
+extern size_t
+vio_fifo_room(vio_fifo vf)
+{
+ if (vf->put_ptr != NULL)
+ return vf->put_end - vf->put_ptr ;
+ else
+ return vf->size ;
} ;
/*------------------------------------------------------------------------------
* Allocate another lump for putting into.
*
- * Call when (vf->put_ptr >= vf->put_end) -- asserts that the pointers are equal.
+ * Call when (vf->put_ptr >= vf->put_end) -- asserts that they are equal.
*
* Set the put_ptr/put_end pointers to point at the new lump.
*
@@ -198,23 +284,19 @@ vio_fifo_set_empty(vio_fifo vf)
* to reflect the fact that the out lump is now full.
*/
extern void
-vio_fifo_lump_new(vio_fifo vf)
+vio_fifo_lump_new(vio_fifo vf, size_t size)
{
vio_fifo_lump lump ;
- size_t size ;
int first_alloc ;
VIO_FIFO_DEBUG_VERIFY(vf) ;
passert(vf->put_ptr == vf->put_end) ; /* must be end of tail lump */
- lump = ddl_tail(vf->base) ;
-
- /* When there is only one lump, the get_end tracks the put_ptr.
- * But when there is more than one lump, it must be the end of that lump.
- */
if (vf->one)
- vf->get_end = lump->end ;
+ vf->get_end = vf->put_ptr ; /* update get_end */
+
+ lump = ddl_tail(vf->base) ;
first_alloc = (lump == NULL) ; /* extra initialisation needed */
@@ -223,9 +305,21 @@ vio_fifo_lump_new(vio_fifo vf)
else
assert(vf->put_ptr == lump->end) ; /* must be end of tail lump */
- size = vio_fifo_size(vf->size) ;
- lump = XMALLOC(MTYPE_VIO_FIFO_LUMP, offsetof(vio_fifo_lump_t, data[size])) ;
- lump->end = (char*)lump->data + vf->size ;
+ size = vio_fifo_size(size) ;
+
+ if ((vf->spare != NULL) && (vf->spare->size >= size))
+ {
+ lump = vf->spare ;
+ vf->spare = NULL ;
+ }
+ else
+ {
+ lump = XMALLOC(MTYPE_VIO_FIFO_LUMP,
+ offsetof(vio_fifo_lump_t, data[size])) ;
+ lump->size = size ;
+ } ;
+
+ lump->end = lump->data + lump->size ;
ddl_append(vf->base, lump, list) ;
@@ -243,6 +337,207 @@ vio_fifo_lump_new(vio_fifo vf)
VIO_FIFO_DEBUG_VERIFY(vf) ;
} ;
+/*------------------------------------------------------------------------------
+ * 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)
+ {
+ vio_fifo_lump keep ;
+
+ /* Consistency checks */
+ if (release_head)
+ {
+ if (release_tail)
+ assert(vf->get_ptr == vf->put_ptr) ;
+ else
+ assert(vf->get_ptr == lump->end) ;
+
+ if (vf->rdr_lump == lump)
+ assert(vf->rdr_ptr == vf->get_ptr) ;
+ }
+ else if (release_tail)
+ {
+ assert(vf->put_ptr == lump->data) ;
+
+ if (vf->rdr_lump == lump)
+ assert(vf->rdr_ptr == vf->put_ptr) ;
+ } ;
+
+ /* 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 ((keep == NULL) || (keep->size < lump->size))
+ {
+ keep = lump ;
+ free = vf->spare ;
+ } ;
+
+ vf->spare = keep ;
+
+ head = ddl_head(vf->base) ; /* changed if released head */
+ tail = ddl_tail(vf->base) ; /* changed if released tail */
+ } ;
+
+ /* 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_ptr_unset(vf) ;
+ }
+ else
+ {
+ /* Have at least one lump left -- so must have had at least two ! */
+ assert(!vf->one) ;
+
+ vf->one = (head == tail) ; /* update */
+
+ if (release_head)
+ {
+ /* 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 ;
+ } ;
+ } ;
+ } ;
+
+ /* Finally, free any lump that is actually to be freed */
+
+ if (free != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, free) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Re-allocate lump for putting into.
+ *
+ * Call when vf->put_ptr == start of last lump, and that lump is not big
+ * enough !
+ *
+ * There must be at least one lump.
+ *
+ * Updates put_ptr/put_end pointers to point at the new lump.
+ *
+ * Updates get_ptr/get_end pointers if required.
+ *
+ * Updates rdr_ptr if required.
+ */
+static void
+vio_fifo_lump_renew(vio_fifo vf, vio_fifo_lump lump, size_t size)
+{
+ 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) ;
+
+ /* Now allocate a new lump with the required size */
+ vio_fifo_lump_new(vf, size) ;
+
+ /* Restore the rdr_ptr, if required */
+ if (rdr_set)
+ {
+ vio_fifo_lump tail ;
+
+ tail = ddl_tail(vf->base) ;
+
+ vf->rdr_lump = tail ;
+ vf->rdr_ptr = tail->data ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
/*==============================================================================
* Put data to the FIFO.
*/
@@ -260,7 +555,7 @@ vio_fifo_put(vio_fifo vf, const char* src, size_t n)
while (n > 0)
{
if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf) ; /* traps broken vf->put_ptr > vf->put_end */
+ vio_fifo_lump_new(vf, vf->size) ; /* traps put_ptr > put_end */
take = (vf->put_end - vf->put_ptr) ;
if (take > n)
@@ -276,6 +571,103 @@ vio_fifo_put(vio_fifo vf, const char* src, size_t n)
VIO_FIFO_DEBUG_VERIFY(vf) ;
} ;
+/*------------------------------------------------------------------------------
+ * 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, ...)
+{
+ va_list args;
+ int len ;
+
+ va_start (args, format);
+ len = vio_fifo_vprintf(vf, format, args);
+ va_end (args);
+
+ return len;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Formatted print to fifo -- cf vprintf()
+ *
+ * Returns: >= 0 -- number of bytes written
+ * < 0 -- failed (unlikely though that is)
+ */
+extern int
+vio_fifo_vprintf(vio_fifo vf, const char *format, va_list args)
+{
+ va_list ac ;
+ int len ;
+ int have ;
+ size_t size ;
+ vio_fifo_lump lump ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ size = vf->size ; /* standard allocation size */
+ while (1)
+ {
+ /* 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 */
+
+ have = vf->put_end - vf->put_ptr ;
+ assert(have > 0) ;
+
+ /* 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) ;
+
+ if (len < have)
+ {
+ if (len < 0)
+ break ; /* quit if failed */
+
+ vf->put_ptr += len ;
+ break ; /* done */
+ } ;
+
+ /* 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 */
+
+ lump = ddl_tail(vf->base) ;
+
+ 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 ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return len ;
+} ;
+
/*==============================================================================
* Get data from the FIFO.
*/
@@ -293,6 +685,8 @@ static bool vio_fifo_get_next_lump(vio_fifo vf) ;
static inline bool
vio_fifo_get_ready(vio_fifo vf)
{
+ assert(vf->rdr_lump == NULL) ;
+
if (vf->one)
vf->get_end = vf->put_ptr ; /* make sure have everything */
@@ -380,8 +774,8 @@ vio_fifo_get_next_byte(vio_fifo vf)
/*------------------------------------------------------------------------------
* Get pointer to a lump of bytes.
*
- * Returns: address of next byte to get, *have = number of bytes available
- * or: NULL => FIFO is empty, *have = 0
+ * 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.
*
@@ -389,15 +783,15 @@ vio_fifo_get_next_byte(vio_fifo vf)
* further lumps beyond the current one.
*/
extern void*
-vio_fifo_get_lump(vio_fifo vf, size_t* have)
+vio_fifo_get_lump(vio_fifo vf, size_t* p_have)
{
if (!vio_fifo_get_ready(vf))
{
- *have = 0 ;
+ *p_have = 0 ;
return NULL ;
} ;
- *have = (vf->get_end - vf->get_ptr) ;
+ *p_have = (vf->get_end - vf->get_ptr) ;
return vf->get_ptr ;
} ;
@@ -422,6 +816,195 @@ vio_fifo_got_upto(vio_fifo vf, void* here)
} ;
/*------------------------------------------------------------------------------
+ * Write contents of FIFO -- assuming non-blocking file
+ *
+ * Will write all of FIFO, or upto but excluding the last lump.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone (up to last lump if !all)
+ * < 0 => failed -- see errno
+ *
+ * Note: will work perfectly well for a non-blocking file -- which should
+ * never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
+ */
+extern int
+vio_fifo_write_nb(vio_fifo vf, int fd, bool all)
+{
+ char* src ;
+ size_t have ;
+ int done ;
+
+ while ((src = vio_fifo_get_lump(vf, &have)) != NULL)
+ {
+ if (!all && vf->one)
+ break ; /* don't write last lump */
+
+ done = write_nb(fd, src, have) ;
+
+ if (done < 0)
+ return -1 ; /* failed */
+
+ vio_fifo_got_upto(vf, src + done) ;
+
+ if (done < (int)have)
+ 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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
+ *
+ * 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.
+ *
+ * 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.
+ */
+extern void*
+vio_fifo_get_rdr(vio_fifo vf, size_t* p_have)
+{
+ if (!vio_fifo_get_ready(vf))
+ {
+ *p_have = 0 ;
+ return NULL ;
+ } ;
+
+ if (vf->rdr_lump == NULL) /* set up new rdr if required */
+ {
+ vf->rdr_lump = ddl_head(vf->base) ;
+ vf->rdr_ptr = vf->get_ptr ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ *p_have = vio_fifo_rdr_end(vf) - vf->rdr_ptr ;
+ return vf->rdr_ptr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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.
+ *
+ * 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".
+ */
+extern void*
+vio_fifo_step_rdr(vio_fifo vf, size_t* p_have, size_t step)
+{
+ char* rdr_end ;
+
+ assert(vf->rdr_lump != NULL) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ rdr_end = vio_fifo_rdr_end(vf) ;
+ vf->rdr_ptr += step ;
+
+ if (vf->rdr_ptr >= rdr_end)
+ {
+ 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 ;
+
+ rdr_end = vio_fifo_rdr_end(vf) ;
+ } ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ *p_have = (rdr_end - vf->rdr_ptr) ;
+ return (*p_have > 0) ? vf->rdr_ptr : NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move FIFO get position to the rdr position, if any.
+ *
+ * This clears the rdr position, and removes all data between the current and
+ * new get positions from the FIFO.
+ */
+extern void
+vio_fifo_sync_rdr(vio_fifo vf)
+{
+ vio_fifo_lump head ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ if (vf->rdr_lump == NULL)
+ return ;
+
+ while ((head = ddl_head(vf->base)) != vf->rdr_lump)
+ {
+ vf->get_ptr = vf->get_end ; /* jump to end of lump */
+ vio_fifo_lump_release(vf, head) ;
+ } ;
+
+ vf->get_ptr = vf->rdr_ptr ; /* jump to rdr_ptr */
+
+ vf->rdr_lump = NULL ; /* clear the rdr */
+ vf->rdr_ptr = NULL ;
+
+ 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) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Drop the rdr position (if any).
+ *
+ * This clears the rdr position leaving the get position and FIFO unchanged.
+ */
+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
@@ -439,70 +1022,51 @@ vio_fifo_got_upto(vio_fifo vf, void* here)
* 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.
+ * of the last lump -- does not release the last lump.
*/
static bool
vio_fifo_get_next_lump(vio_fifo vf)
{
vio_fifo_lump head ;
- vio_fifo_lump tail ;
VIO_FIFO_DEBUG_VERIFY(vf) ;
assert(vf->get_ptr == vf->get_end) ;
- head = ddl_head(vf->base) ; /* current lump for put */
- tail = ddl_tail(vf->base) ; /* current lump for get */
+ head = ddl_head(vf->base) ; /* current lump for get */
- /* Deal with case of one lump only */
+ /* 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)
{
- assert( (head != NULL)
- && (head == tail) ) ;
+ assert( (head != NULL) && (head == ddl_tail(vf->base)) ) ;
- if (vf->get_ptr == vf->put_ptr)
+ if (vf->get_ptr < vf->put_ptr)
{
- /* FIFO is empty -- reset pointers and exit */
- vf->get_ptr = vf->get_end = vf->put_ptr = head->data ;
- assert(vf->put_end == head->end) ;
+ /* Had an out of date vf->get_end */
+ vf->get_end = vf->put_ptr ;
- return 0 ; /* FIFO empty */
+ return true ; /* FIFO not empty */
} ;
- /* Had an out of date vf->get_end */
- assert(vf->get_end < vf->put_ptr) ;
- vf->get_end = vf->put_ptr ;
-
- return 1 ; /* FIFO not empty after all */
- } ;
+ assert(vf->get_ptr == vf->put_ptr) ;
- /* Deal with case of not yet allocated */
- if (head == NULL)
- {
- assert( (tail == NULL)
- && (vf->put_ptr == vf->get_ptr) );
+ /* FIFO is empty -- reset pointers and exit */
+ vio_fifo_ptr_reset(vf, head) ;
- return 0 ; /* FIFO empty */
+ return false ; /* FIFO empty */
} ;
- /* Deal with (remaining) case of two or more lumps */
- assert(vf->get_ptr == head->end) ;
-
- ddl_del_head(vf->base, list) ;
- XFREE(MTYPE_VIO_FIFO_LUMP, head) ;
-
- head = ddl_head(vf->base) ;
- assert(head != NULL) ;
-
- vf->one = (head == tail) ;
-
- vf->get_ptr = head->data ; /* at start of next lump */
-
- if (vf->one)
- vf->get_end = vf->put_ptr ; /* up to current put */
- else
- vf->get_end = head->end ; /* up to end of lump */
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ /* Release the head and update pointers
+ *
+ * Deals with possibility that nothing has yet been allocated
+ */
+ vio_fifo_lump_release(vf, head) ;
return (vf->get_ptr < vf->get_end) ;
} ;
@@ -514,6 +1078,7 @@ Private void
vio_fifo_verify(vio_fifo vf)
{
vio_fifo_lump head ;
+ vio_fifo_lump lump ;
vio_fifo_lump tail ;
head = ddl_head(vf->base) ;
@@ -524,10 +1089,11 @@ vio_fifo_verify(vio_fifo vf)
if (head == NULL)
{
if ( (tail != NULL)
- || (vf->put_ptr != NULL)
- || (vf->put_end != NULL)
- || (vf->get_ptr != NULL)
- || (vf->get_end != NULL)
+ || (vf->put_ptr != NULL)
+ || (vf->put_end != NULL)
+ || (vf->get_ptr != NULL)
+ || (vf->rdr_lump != NULL)
+ || (vf->rdr_ptr != NULL)
|| (vf->one) )
zabort("nothing allocated, but not all NULL") ;
return ;
@@ -541,7 +1107,7 @@ vio_fifo_verify(vio_fifo vf)
/* Check that all the pointers are within respective lumps
*
* Know that put_end is always tail->end, but get_end need not be.
- * */
+ */
if ( (tail->data > vf->put_ptr)
|| (vf->put_ptr > vf->put_end)
|| (vf->put_end != tail->end) )
@@ -568,5 +1134,32 @@ vio_fifo_verify(vio_fifo vf)
if (vf->get_end != head->end)
zabort("get_end is not head->end when !vf->one") ;
+ } ;
+
+ /* If have an rdr_lump -- make sure everything else is valid */
+ if (vf->rdr_lump != NULL)
+ {
+ lump = head ;
+ while (lump != vf->rdr_lump)
+ {
+ if (lump == tail)
+ zabort("rdr_lump is not part of FIFO") ;
+ lump = ddl_next(lump, list) ;
+ } ;
+
+ if ( (lump->data > vf->rdr_ptr)
+ || (vf->rdr_ptr > lump->end) )
+ zabort("rdr_ptr outside its lump") ;
+
+ if ( (lump == head) && (vf->rdr_ptr < vf->get_ptr))
+ zabort("rdr_ptr is less than get_ptr in first lump") ;
+
+ if ( (lump == tail) && (vf->rdr_ptr > vf->put_ptr))
+ zabort("rdr_ptr is greater than put_ptr in last lump") ;
+ }
+ else
+ {
+ if (vf->rdr_ptr != NULL)
+ zabort("rdr_ptr not NULL when rdr_lump is") ;
}
} ;
diff --git a/lib/vio_fifo.h b/lib/vio_fifo.h
index 6d99afe5..52f3455e 100644
--- a/lib/vio_fifo.h
+++ b/lib/vio_fifo.h
@@ -22,7 +22,7 @@
#ifndef _ZEBRA_VIO_FIFO_H
#define _ZEBRA_VIO_FIFO_H
-#include <stddef.h>
+#include "zebra.h"
#include <stdint.h>
#include <stdbool.h>
@@ -37,6 +37,13 @@
#define Private extern
#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__ */
+
/*==============================================================================
* VTY I/O FIFO -- buffering of arbitrary amounts of I/O.
*/
@@ -70,7 +77,12 @@ struct vio_fifo
char* get_ptr ;
char* get_end ;
+ vio_fifo_lump rdr_lump ;
+ char* rdr_ptr ;
+
size_t size ;
+
+ vio_fifo_lump spare ;
} ;
struct vio_fifo_lump
@@ -78,6 +90,7 @@ struct vio_fifo_lump
struct dl_list_pair(vio_fifo_lump) list ;
char* end ; /* end of this particular lump */
+ size_t size ; /* size of lump when allocated */
char data[] ;
} ;
@@ -85,52 +98,44 @@ 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 vf, size_t size) ;
+extern vio_fifo vio_fifo_reset(vio_fifo vf, int 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_set_empty(vio_fifo vf) ;
-
-Inline bool
-vio_fifo_empty(vio_fifo vf) ;
-
-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 size_t
-vio_fifo_get(vio_fifo vf, void* dst, size_t n) ;
+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) ;
-Inline int
-vio_fifo_get_byte(vio_fifo vf) ;
+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 void*
-vio_fifo_get_lump(vio_fifo vf, size_t* have) ;
+extern int vio_fifo_printf(vio_fifo vf, const char* format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern int vio_fifo_vprintf(vio_fifo vf, const char *format, va_list args) ;
-extern void
-vio_fifo_got_upto(vio_fifo vf, void* here) ;
+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) ;
-Private void
-vio_fifo_lump_new(vio_fifo vf) ;
+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 int
-vio_fifo_get_next_byte(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) ;
/*==============================================================================
* Debug -- verification function
*/
-Private void
-vio_fifo_verify(vio_fifo vf) ;
+Private void vio_fifo_verify(vio_fifo vf) ;
#if VIO_FIFO_DEBUG
# define VIO_FIFO_DEBUG_VERIFY(vf) vio_fifo_verify(vf)
@@ -158,7 +163,7 @@ Inline void
vio_fifo_put_byte(vio_fifo vf, char b)
{
if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf) ; /* traps broken vf->put_ptr > vf->put_end */
+ vio_fifo_lump_new(vf, vf->size) ; /* traps put_ptr > put_end */
VIO_FIFO_DEBUG_VERIFY(vf) ;
@@ -182,4 +187,19 @@ vio_fifo_get_byte(vio_fifo vf)
return (unsigned char)*vf->get_ptr++ ;
} ;
+/*------------------------------------------------------------------------------
+ * See if have at least one full lump.
+ *
+ * This may be used with vio_fifo_write_nb(..., false) to use FIFO as a sort of
+ * double buffer.
+ *
+ * Returns: true <=> there is at least one full lump in the FIFO
+ * (excluding the last lump if it happens to be full)
+ */
+Inline bool
+vio_fifo_full_lump(vio_fifo vf)
+{
+ return (!vf->one && (vf->put_ptr != NULL)) ;
+} ;
+
#endif /* _ZEBRA_VIO_FIFO_H */
diff --git a/lib/vio_lines.c b/lib/vio_lines.c
new file mode 100644
index 00000000..c2e9c43c
--- /dev/null
+++ b/lib/vio_lines.c
@@ -0,0 +1,380 @@
+/* Line Control for VTY Terminal output
+ * Copyright (C) 2010 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.
+ */
+
+#include <stdint.h>
+
+#include "memory.h"
+#include "zassert.h"
+
+#include "vio_lines.h"
+#include "qiovec.h"
+
+/*==============================================================================
+ * Line control handles the output of simple text to a telnet connection,
+ * folding and counting lines (for "--more--" purposes) if required.
+ *
+ * LIMITATIONS:
+ *
+ * 1) does not handle '\r' except as part of '\r''\n' pairs.
+ *
+ * Telnet requires that bare '\r' be sent as '\r''\0'. That is not
+ * implemented.
+ *
+ * The handling of '\r' which is not part of '\r''\n' is UNDEFINED.
+ * (In particular, the '\r' may be sent as is, or not sent at all.)
+ *
+ * 2) does not worry about '\t' or '\b' or any other control character.
+ *
+ * Apart from '\r' and '\n' all characters are deemed to be printing
+ * characters -- and to have width == 1.
+ *
+ * 3) has no idea about escape sequences or telnet commands.
+ *
+ * In particular: when looking for '\n' (and '\r') has no way of telling
+ * if those are part of an escape sequence.
+ *
+ * 4) DOES NOT handle 0xFF character value.
+ *
+ * For Telnet this should be escaped. It isn't.
+ *
+ * Current use of VTY command output will not be troubled by these limitations.
+ * To do more would cost code and cpu unnecessarily.
+ *
+ * WHAT IT DOES DO:
+ *
+ * 1) maps bare '\n' to '\r''\n'.
+ *
+ * Swallows '\r' immediately before '\n' if present.
+ *
+ * 2) if required, breaks output into screen width chunks, and counts
+ * down the height of a "screen full".
+ *
+ */
+
+/*==============================================================================
+ * Initialise, allocate, reset etc.
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise new vio_line_control -- allocate if required.
+ *
+ * This is for initialising a new structure. Any current contents are lost.
+ *
+ * A width of <= 0 => very large width indeed.
+ * A height of <= 0 => indefinite height
+ *
+ * Pause is unset. vio_lc_append will collect an indefinite number of lines.
+ *
+ * Column and line position set to zero.
+ *
+ * Returns: address of vio_line_control
+ */
+extern vio_line_control
+vio_lc_init_new(vio_line_control lc, int width, int height)
+{
+ if (lc == NULL)
+ lc = XCALLOC(MTYPE_VIO_LC, sizeof(struct vio_line_control)) ;
+ else
+ memset(lc, 0, sizeof(struct vio_line_control)) ;
+
+ /* Zeroising has set:
+ *
+ * pause = 0 -- no limit on the number of lines to append
+ * paused = 0 -- not paused
+ *
+ * col = 0 -- at column 0
+ * lines = 0 -- no lines collected, yet
+ *
+ * iov = all 0 -- empty
+ * writing = 0 -- not writing
+ */
+
+ lc->width = width >= 0 ? width : 0 ;
+ lc->height = height >= 0 ? height : 0 ;
+
+ return lc ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset vio_line_control (if any) -- release body and (if required) the
+ * structure.
+ *
+ * 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)
+{
+ if (lc != NULL)
+ {
+ if (free_structure)
+ XFREE(MTYPE_VIO_LC, lc) ; /* sets lc = NULL */
+ else
+ vio_lc_init_new(lc, lc->width, lc->height) ;
+ /* re-initialise */
+ } ;
+
+ return lc ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear given vio_line_control.
+ *
+ * Sets: pause = 0
+ * paused = 0
+ * col = 0
+ * writing = 0
+ *
+ * NB: it is the callers responsibility to release anything buffered because
+ * it was earlier appended.
+ */
+extern void
+vio_lc_clear(vio_line_control lc)
+{
+ qiovec_clear(&lc->qiov) ;
+
+ lc->pause = 0 ;
+ lc->paused = 0 ;
+ lc->col = 0 ;
+ lc->writing = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Sets width and height for line control
+ *
+ * A width of <= 0 => very large width indeed.
+ * A height of <= 0 => indefinite height
+ *
+ * Pause is adjusted if it is not zero, and may become zero and set paused.
+ */
+extern void
+vio_lc_set_window(vio_line_control lc, int width, int height)
+{
+ unsigned old_height ;
+
+ old_height = lc->height ;
+
+ lc->width = width >= 0 ? width : 0 ;
+ lc->height = height >= 0 ? height : 0 ;
+
+ if (lc->pause != 0)
+ {
+ if (lc->height > old_height)
+ lc->pause += lc->height - old_height ;
+ else
+ {
+ if (lc->pause >= (old_height - lc->height))
+ lc->pause = 0 ;
+ else
+ lc->pause -= old_height - lc->height ;
+ } ;
+ lc->paused = (lc->pause == 0) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Appending and writing
+ */
+
+/*------------------------------------------------------------------------------
+ * Sets pause to the current height and clear paused.
+ */
+extern void
+vio_lc_set_pause(vio_line_control lc)
+{
+ lc->pause = lc->height ;
+ lc->paused = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Put newline (if required) and account for it
+ */
+static inline void
+vio_lc_newline(vio_line_control lc, bool required)
+{
+ if (required)
+ qiovec_push(&lc->qiov, "\r\n", 2) ;
+
+ lc->col = 0 ;
+ lc->line += 1 ;
+ if (lc->pause != 0)
+ {
+ lc->pause -= 1 ;
+ lc->paused = (lc->pause == 0) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Append a lump of output to the given line control's buffers.
+ *
+ * Breaks the output into lines which are no longer than the lc->width.
+ *
+ * Maps '\n' to '\r''\n'.
+ *
+ * Discards '\r' if found before '\n', and possibly at other times.
+ *
+ * If lc->width == 0, use a very large width indeed.
+ *
+ * If lc->pause == 0, append an indefinite number of lines
+ *
+ * NB: the buffer presented MUST be retained until the contents of the
+ * line control's buffers have been written.
+ *
+ * Returns: number of bytes able to append
+ */
+extern size_t
+vio_lc_append(vio_line_control lc, const void* buf, size_t len)
+{
+ const char* p ;
+ const char* end ;
+
+ unsigned width ;
+ unsigned pause ;
+
+ /* Prepare local width and pause */
+ if (lc->width > 0)
+ width = lc->width ;
+ else
+ width = UINT_MAX ;
+
+ if (lc->pause > 0)
+ pause = 0 ;
+ else
+ pause = 1 ;
+
+ lc->paused = 0 ;
+
+ /* Append: stop when run out of data or run out of lines */
+ end = (const char*)buf + len ;
+ p = buf ;
+
+ while ((p < end) && (lc->pause != pause))
+ {
+ const char* e ;
+ bool nl ;
+ int nlx ;
+
+ nlx = 0 ; /* no line ending chars yet */
+
+ /* scan for '\n'. */
+ e = memchr(p, '\n', (end - p)) ;
+ nl = (e != NULL) ;
+ if (nl)
+ ++nlx ; /* account for the '\n' */
+ else
+ e = end ; /* use all there is */
+
+ /* peel off trailing '\r'.
+ *
+ * NB: if have not got a '\n', then this may discard a bare
+ * '\r' -- but bare '\r' are undefined in any case.
+ */
+ if ((e > p) && (*(e - 1) == '\r'))
+ {
+ --e ; /* strip the '\r' */
+ ++nlx ; /* but account for it */
+ }
+
+ /* have p..e characters and possibly nl to add to the output.
+ *
+ * Note that if enters the while, (e - p) > 0. So there is at least one
+ * character to add. This avoids generating a spurious line ending if
+ * the width has been reduced, and the next thing output is a line end.
+ */
+ while ((p < e) && (lc->pause != pause))
+ {
+ const char* t ;
+ unsigned col ;
+
+ col = lc->col + (e - p) ; /* NB: e > p */
+
+ if (col > width)
+ {
+ /* can use only part of what there is */
+ if (width > lc->col)
+ t = p + (width - lc->col) ;
+ /* take to edge of screen */
+ else
+ t = p ;
+ assert(t < e) ; /* if not need to deal with nl */
+ }
+ else
+ {
+ /* can use all of what there is */
+ if (nlx == 2) /* if have crlf, use it */
+ {
+ e += nlx ; /* use the crlf that's there */
+ nlx = 0 ; /* used it */
+ } ;
+
+ t = e ; /* take it all */
+ } ;
+
+ assert(t >= p) ;
+ if (t != p)
+ qiovec_push(&lc->qiov, p, (t - p)) ;
+
+ /* advance. If not taken all the line, need a crlf */
+ p = t ;
+
+ if (p < e)
+ vio_lc_newline(lc, 1) ;
+ } ;
+
+ /* If taken all of line, deal with any outstanding nl and nlx */
+ if (p == e)
+ {
+ if (nl)
+ vio_lc_newline(lc, (nlx != 0)) ;
+
+ p += nlx ; /* step past '\r' or '\n' */
+ } ;
+ } ;
+
+ /* Exhausted the available data or the line count */
+ assert(p <= end) ;
+
+ return (p - (const char*)buf) ; /* what have taken */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write away any collected output -- assuming NON-BLOCKING.
+ *
+ * Does nothing if the line control is empty.
+ *
+ * Loops internally if gets EINTR.
+ *
+ * Returns: > 0 => one or more bytes left to output
+ * 0 => all done -- zero bytes left to output
+ * -1 => failed -- see errno
+ *
+ * Sets lc->writing if write does not complete
+ */
+extern int
+vio_lc_write_nb(int fd, vio_line_control lc)
+{
+ int ret ;
+
+ ret = qiovec_write_nb(fd, &lc->qiov) ;
+
+ lc->writing = (ret > 0) ;
+
+ return ret ;
+} ;
diff --git a/lib/vio_lines.h b/lib/vio_lines.h
new file mode 100644
index 00000000..ffef94ec
--- /dev/null
+++ b/lib/vio_lines.h
@@ -0,0 +1,91 @@
+/* Line Control for VTY Terminal output -- header
+ * 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_VIO_LINES_H
+#define _ZEBRA_VIO_LINES_H
+
+#include "zebra.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "qiovec.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Line control -- collecting lines of a given width for output.
+ *
+ * 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 */
+ unsigned height ; /* console height -- 0 => indefinite */
+
+ unsigned pause ; /* number of lines to next pause
+ 0 => indefinite */
+ bool paused ; /* true <=> last append stopped on pause */
+
+ unsigned col ; /* current column position */
+ unsigned line ; /* line number of last complete line */
+
+ struct qiovec qiov ; /* iovec control */
+ bool writing ; /* write started, but not completed */
+} ;
+
+/*==============================================================================
+ * 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)
+
+Inline bool vio_lc_empty(vio_line_control lc) ;
+extern void vio_lc_clear(vio_line_control lc) ;
+extern void vio_lc_set_window(vio_line_control lc, int width, int height) ;
+
+extern void vio_lc_set_pause(vio_line_control lc) ;
+extern size_t vio_lc_append(vio_line_control lc, const void* buf, size_t len) ;
+extern int vio_lc_write_nb(int fd, vio_line_control lc) ;
+
+/*------------------------------------------------------------------------------
+ * Is given line control empty ?
+ */
+Inline bool
+vio_lc_empty(vio_line_control lc)
+{
+ return qiovec_empty(&lc->qiov) ;
+} ;
+
+#endif /* _ZEBRA_VIO_LINES_H */
diff --git a/lib/vty.c b/lib/vty.c
index 8acd1403..76f62e01 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -23,6 +23,7 @@
#include "zebra.h"
#include <stdbool.h>
+#include "version.h"
#include "vty_io.h"
#include "vty.h"
@@ -32,15 +33,13 @@
#include "list_util.h"
#include "command.h"
+#include "command_queue.h"
#include "memory.h"
#include "log.h"
+#include "mqueue.h"
/*==============================================================================
- * Variables etc.
- */
-
-/*------------------------------------------------------------------------------
- * Static and Global (see uty.h) Variables
+ * Variables etc. (see uty.h)
*/
/* The mutex and related debug counters */
@@ -48,8 +47,8 @@ qpt_mutex_t vty_mutex ;
#if VTY_DEBUG
-int vty_lock_count = 0 ;
-int vty_lock_assert_fail = 0 ;
+int vty_lock_count = 0 ;
+int vty_assert_fail = 0 ;
#endif
@@ -107,10 +106,25 @@ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ;
/*------------------------------------------------------------------------------
* Prototypes
*/
-static void uty_reset (bool final) ;
+static void uty_reset (bool final, const char* why) ;
static void uty_init_commands (void) ;
static void vty_save_cwd (void) ;
+/*------------------------------------------------------------------------------
+ * Tracking the initialisation state.
+ */
+enum vty_init_state
+{
+ vty_init_pending = 0, /* first and lowest numbered state */
+ vty_init_1st_stage,
+ vty_init_2nd_stage,
+ vty_init_started,
+ vty_init_reset,
+ vty_init_terminated /* final and highest numbered state */
+};
+
+static enum vty_init_state vty_init_state ;
+
/*==============================================================================
* Public Interface
*/
@@ -119,11 +133,19 @@ static void vty_save_cwd (void) ;
* Initialise vty handling (threads and pthreads)
*
* Install vty's own commands like `who' command.
+ *
+ * This runs before any pthreads or nexus stuff starts -- so is, implicitly,
+ * in the CLI thread.
+ *
+ * NB: may be called once and once only.
*/
extern void
vty_init (struct thread_master *master_thread)
{
- VTY_LOCK() ;
+ VTY_LOCK() ; /* Does nothing if !qpthreads_enabled */
+ VTY_ASSERT_CLI_THREAD() ; /* True if !qpthreads_enabled */
+
+ assert(vty_init_state == vty_init_pending) ;
vty_master = master_thread; /* Local pointer to the master thread */
@@ -140,6 +162,8 @@ vty_init (struct thread_master *master_thread)
uty_init_commands() ; /* install nodes */
+ vty_init_state = vty_init_1st_stage ;
+
VTY_UNLOCK() ;
}
@@ -149,30 +173,28 @@ vty_init (struct thread_master *master_thread)
* This is done during "second stage" initialisation, when all nexuses have
* been set up and the qpthread_enabled state established.
*
+ * This is before any threads have been started, so is, implicitly, in the
+ * CLI thread.
+ *
* Need to know where the CLI nexus and the Routeing Engine nexus are.
*
* Initialise mutex.
+ *
+ * Cannot lock or assert in CLI thread while initialising those things !
+ *
+ * NB: may be called once and once only.
*/
extern void
vty_init_r (qpn_nexus cli, qpn_nexus cmd)
{
+ assert(vty_init_state == vty_init_1st_stage) ;
+
vty_cli_nexus = cli ;
vty_cmd_nexus = cmd ;
qpt_mutex_init(&vty_mutex, qpt_mutex_recursive);
-} ;
-/*------------------------------------------------------------------------------
- * Initialise the listeners for VTY_TERM and VTY_SHELL_SERV VTY
- *
- * This is done after the configuration file has been read.
- */
-extern void
-vty_serv_sock(const char *addr, unsigned short port, const char *path)
-{
- VTY_LOCK() ;
- uty_open_listeners(addr, port, path) ;
- VTY_UNLOCK() ;
+ vty_init_state = vty_init_2nd_stage ;
} ;
/*------------------------------------------------------------------------------
@@ -189,86 +211,243 @@ vty_init_vtysh (void)
} ;
/*------------------------------------------------------------------------------
- * Create a new VTY of the given type
+ * Start the VTY going.
+ *
+ * This starts the listeners for VTY_TERM and VTY_SHELL_SERV.
+ *
+ * Also starts the watch dog.
+ *
+ * This is run during early morning start, after any daemonisation, but before
+ * any threads are started -- so is, implicitly, in the CLI thread.
+ *
+ * NB: may be called once and once only.
+ *
+ * NB: MUST be in the CLI thread (if any).
*/
-extern struct vty *
-vty_new (int fd, enum vty_type type)
+extern void
+vty_start(const char *addr, unsigned short port, const char *path)
{
- struct vty* vty ;
-
VTY_LOCK() ;
- vty = uty_new(fd, type);
- VTY_UNLOCK() ;
+ VTY_ASSERT_CLI_THREAD() ;
- return vty ;
+ assert( (vty_init_state == vty_init_1st_stage)
+ || (vty_init_state == vty_init_2nd_stage) ) ;
+
+ uty_watch_dog_start() ;
+
+ uty_open_listeners(addr, port, path) ;
+
+ vty_init_state = vty_init_started ;
+ VTY_UNLOCK() ;
} ;
/*------------------------------------------------------------------------------
- * Close the given VTY completely
+ * Reset all VTY status for reasons unknown -- probably SIGHUP
*/
extern void
-vty_close (struct vty *vty)
+vty_reset()
{
- VTY_LOCK() ;
- uty_close(vty->vio);
- VTY_UNLOCK() ;
+ vty_reset_because("Reset") ;
}
/*------------------------------------------------------------------------------
* Reset all VTY status
*
- * This is done just before the configuration file is re-read (SIGHUP).
+ * This is done in response to SIGHUP -- and runs in the CLI thread.
*
- * Half closes all VTY, leaving the death watch to tidy up once all output has
- * completed.
+ * Half closes all VTY, leaving the death watch to tidy up once all output
+ * and any command in progress have completed.
*
- * NB: old code discarded all output and hard closed all the VTY...
+ * Closes all listening sockets.
*
- * TODO: ...SIGHUP while a command is queued ?
+ * TODO: revoke ?
*
- * Closes all listening sockets.
+ * NB: old code discarded all output and hard closed all the VTY...
*/
extern void
-vty_reset(void)
+vty_reset_because(const char* why)
{
VTY_LOCK() ;
- uty_reset(0) ; /* not final ! */
+ VTY_ASSERT_CLI_THREAD() ;
+
+ assert(vty_init_state == vty_init_started) ;
+
+ uty_reset(0, why) ; /* not final ! */
+
+ vty_init_state = vty_init_reset ;
VTY_UNLOCK() ;
}
/*------------------------------------------------------------------------------
+ * Restart the VTY, following a vty_reset().
+ *
+ * This starts the listeners for VTY_TERM and VTY_SHELL_SERV, again.
+ *
+ * NB: may be called once, and once only, *after* a vty_reset().
+ *
+ * NB: need not be in the CLI thread (if any).
+ */
+struct vty_restart_args
+{
+ char* addr ;
+ unsigned short port ;
+ char* path ;
+} ;
+MQB_ARGS_SIZE_OK(vty_restart_args) ;
+
+static void uty_restart_action(mqueue_block mqb, mqb_flag_t flag) ;
+static void uty_restart(const char *addr, unsigned short port,
+ const char *path) ;
+extern void
+vty_restart(const char *addr, unsigned short port, const char *path)
+{
+ VTY_LOCK() ;
+
+ /* If not running qnexus-wise, call uty_restart directly.
+ *
+ * Otherwise, construct and dispatch message to do a uty_restart.
+ */
+ if (!vty_cli_nexus)
+ uty_restart(addr, port, path) ;
+ else
+ {
+ mqueue_block mqb ;
+ struct vty_restart_args* args ;
+
+ mqb = mqb_init_new(NULL, uty_restart_action, vty_cli_nexus) ;
+ args = mqb_get_args(mqb) ;
+
+ if (addr != NULL)
+ args->addr = XSTRDUP(MTYPE_TMP, addr) ;
+ else
+ args->addr = NULL ;
+
+ args->port = port ;
+
+ if (path != NULL)
+ args->path = XSTRDUP(MTYPE_TMP, path) ;
+ else
+ args->path = NULL ;
+
+ mqueue_enqueue(vty_cli_nexus->queue, mqb, 0) ;
+ } ;
+
+ VTY_UNLOCK() ;
+} ;
+
+/* Deal with the uty_restart message */
+static void
+uty_restart_action(mqueue_block mqb, mqb_flag_t flag)
+{
+ struct vty_restart_args* args ;
+ args = mqb_get_args(mqb) ;
+
+ if (flag == mqb_action)
+ {
+ VTY_LOCK() ;
+
+ uty_restart(args->addr, args->port, args->path) ;
+
+ VTY_UNLOCK() ;
+ } ;
+
+ if (args->addr != NULL)
+ XFREE(MTYPE_TMP, args->addr) ;
+ if (args->path != NULL)
+ XFREE(MTYPE_TMP, args->path) ;
+} ;
+
+/* Do the actual restart */
+static void
+uty_restart(const char *addr, unsigned short port, const char *path)
+{
+ VTY_ASSERT_LOCKED() ;
+ assert(vty_init_state == vty_init_reset) ;
+
+ uty_open_listeners(addr, port, path) ;
+
+ vty_init_state = vty_init_started ;
+} ;
+
+/*------------------------------------------------------------------------------
* System shut-down
*
- * Reset all known vty and release all memory.
+ * In the pthreads world, all threads other than the main (CLI) thread have
+ * been joined -- so this is, implicitly, in the CLI thread.
+ *
+ * Close all known vty and release all memory -- discard all pending output.
+ *
+ * NB: this may be done in any initialisation state.
+ *
+ * Note that all the locking stuff does nothing if not qpthreads_enabled, so
+ * these may be done in any state of initialisation. (It is assumed that the
+ * switch into qpthreads_enabled is an atomic action... so all second stage
+ * initialisation completes together.)
*/
extern void
vty_terminate (void)
{
+ if ( (vty_init_state == vty_init_pending)
+ || (vty_init_state == vty_init_terminated) )
+ return ; /* nothing to do ! */
+
VTY_LOCK() ;
- uty_reset(1) ; /* final reset */
+ VTY_ASSERT_CLI_THREAD() ;
+
+ assert( (vty_init_state > vty_init_pending)
+ && (vty_init_state < vty_init_terminated) ) ;
+
+ uty_reset(1, "Shut down") ; /* final reset */
+
VTY_UNLOCK() ;
qpt_mutex_destroy(&vty_mutex, 0);
+
+ vty_init_state = vty_init_terminated ;
}
/*------------------------------------------------------------------------------
* Reset -- final or for SIGHUP
+ *
+ * Closes listeners.
+ *
+ * Closes (final) or half closes (SIGHUP) all VTY, and revokes any outstanding
+ * commands.
+ *
+ * Resets the vty timeout and access lists.
+ *
+ * When reach final reset it should not be possible for there to be any
+ * commands still in progress. If there are, they are simply left on the
+ * death-watch list... there is no pressing need to do anything more radical,
+ * and the presence of anything on the death watch is grounds for some debug
+ * activity !
*/
static void
-uty_reset (bool curtains)
+uty_reset (bool curtains, const char* why)
{
vty_io vio ;
+ vty_io next ;
VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
uty_close_listeners() ;
- while ((vio = sdl_pop(&vio, vio_list_base, vio_list)) != NULL)
+ next = sdl_head(vio_list_base) ;
+ while (next != NULL)
{
- uty_half_close(vio) ; /* TODO: reason for close */
+ vio = next ;
+ next = sdl_next(vio, vio_list) ;
+
+ cq_revoke(vio->vty) ;
+
+ if (why != NULL)
+ vio->close_reason = why ;
if (curtains)
- uty_full_close(vio) ;
+ uty_close(vio) ;
+ else
+ uty_half_close(vio, why) ;
} ;
vty_timeout_val = VTY_TIMEOUT_DEFAULT;
@@ -287,9 +466,48 @@ uty_reset (bool curtains)
if (curtains && vty_cwd)
XFREE (MTYPE_TMP, vty_cwd);
+
+ if (curtains)
+ uty_watch_dog_stop() ; /* and final death watch run */
} ;
/*==============================================================================
+ * Opening and closing VTY.
+ *
+ * VTY without a socket may be opened and closed at will.
+ *
+ * TODO: sort out the relationship between the non-socket VTY and vty_reset()
+ */
+
+/*------------------------------------------------------------------------------
+ * Create a new VTY of the given type
+ *
+ * The type may NOT be: VTY_TERM or VTY_SHELL_SERV
+ */
+extern struct vty *
+vty_open(enum vty_type type)
+{
+ struct vty* vty ;
+
+ VTY_LOCK() ;
+ vty = uty_new(type, -1) ; /* fails for VTY_TERM or VTY_SHELL_SERV */
+ VTY_UNLOCK() ;
+
+ return vty ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the given VTY
+ */
+extern void
+vty_close (struct vty *vty)
+{
+ VTY_LOCK() ;
+ uty_close(vty->vio) ;
+ VTY_UNLOCK() ;
+}
+
+/*==============================================================================
* General VTY output.
*
* This is mostly used during command execution, to output the results of the
@@ -324,17 +542,14 @@ vty_out (struct vty *vty, const char *format, ...)
const char vty_spaces_string[] = " " ;
CONFIRM(VTY_MAX_SPACES == (sizeof(vty_spaces_string) - 1)) ;
-extern int
+extern void
vty_out_indent(struct vty *vty, int indent)
{
- while (indent > VTY_MAX_SPACES)
+ while (indent > 0)
{
- int ret = vty_out(vty, VTY_SPACES(indent)) ;
- if (ret < 0)
- return ret ;
+ vty_out(vty, VTY_SPACES(indent)) ;
indent -= VTY_MAX_SPACES ;
}
- return vty_out(vty, VTY_SPACES(indent)) ;
} ;
/*------------------------------------------------------------------------------
@@ -394,81 +609,91 @@ vty_hello (struct vty *vty)
VTY_UNLOCK() ;
}
+/*------------------------------------------------------------------------------
+ * Clear the contents of the command output FIFO etc.
+ */
+extern void
+vty_out_clear(struct vty* vty)
+{
+ VTY_LOCK() ;
+ uty_out_clear(vty->vio) ;
+ VTY_UNLOCK() ;
+} ;
+
/*==============================================================================
* Command Execution
*/
/*------------------------------------------------------------------------------
- * Execute command, adding it to the history if not empty or comment
+ * Execute command -- adding to history is not empty or just comment
+ *
+ * This is for VTY_TERM type VTY.
*
* Outputs diagnostics if fails to parse.
*
- * Returns: CMD_xxxx result.
+ * Returns: command return code
*/
-extern int
-uty_command(struct vty *vty, const char *buf)
+extern enum cmd_return_code
+uty_command(struct vty *vty)
{
- int ret;
- vector vline;
- const char *protocolname;
+ enum cmd_return_code ret;
VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
- /* Split readline string up into the vector */
- vline = cmd_make_strvec (buf);
+ assert(vty->vio->type == VTY_TERM) ;
- if (vline == NULL)
- return CMD_SUCCESS; /* quit if empty or comment */
-
- uty_cli_hist_add (vty->vio, buf) ;
+ /* Parse the command and add to history (if not empty) */
+ ret = cmd_parse_command(vty,
+ cmd_parse_completion + cmd_parse_do + cmd_parse_tree) ;
+ if (ret != CMD_EMPTY)
+ uty_cli_hist_add (vty->vio, vty->buf) ;
+ /* If parsed and not empty, dispatch */
+ if (ret == CMD_SUCCESS)
+ {
#ifdef CONSUMED_TIME_CHECK
- {
- RUSAGE_T before;
- RUSAGE_T after;
- unsigned long realtime, cputime;
+ RUSAGE_T before;
+ RUSAGE_T after;
+ unsigned long realtime, cputime;
- GETRUSAGE(&before);
+ GETRUSAGE(&before);
#endif /* CONSUMED_TIME_CHECK */
-//VTY_UNLOCK() ;
- ret = cmd_execute_command (vline, vty, NULL, vty_cmd_nexus, vty_cli_nexus, 0);
-//VTY_LOCK() ;
-
- /* Get the name of the protocol if any */
- protocolname = uzlog_get_proto_name(NULL);
+ ret = cmd_dispatch(vty, cmd_may_queue) ;
#ifdef CONSUMED_TIME_CHECK
- GETRUSAGE(&after);
- if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
- CONSUMED_TIME_CHECK)
- /* Warn about CPU hog that must be fixed. */
- uzlog(NULL, LOG_WARNING, "SLOW COMMAND: command took %lums (cpu time %lums): %s",
- realtime/1000, cputime/1000, buf);
- }
+ GETRUSAGE(&after);
+ if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
+ CONSUMED_TIME_CHECK)
+ /* Warn about CPU hog that must be fixed. */
+ uzlog(NULL, LOG_WARNING,
+ "SLOW COMMAND: command took %lums (cpu time %lums): %s",
+ realtime/1000, cputime/1000, vty->buf) ;
#endif /* CONSUMED_TIME_CHECK */
+ } ;
- if (ret != CMD_SUCCESS)
- switch (ret)
- {
- case CMD_WARNING:
- if (vty->vio->type == VTY_FILE)
- uty_out (vty, "Warning...%s", VTY_NEWLINE);
- break;
- case CMD_ERR_AMBIGUOUS:
- uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
- break;
- case CMD_ERR_NO_MATCH:
- uty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
- break;
- case CMD_ERR_INCOMPLETE:
- uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
- break;
- }
- cmd_free_strvec (vline);
-
- return ret;
-}
+ /* Deal with the return code */
+ switch (ret)
+ {
+ case CMD_ERR_AMBIGUOUS:
+ uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ break;
+
+ case CMD_ERR_NO_MATCH:
+ uty_out (vty, "%% Unknown command.%s", VTY_NEWLINE) ;
+ break;
+
+ case CMD_ERR_INCOMPLETE:
+ uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
+ break;
+
+ default:
+ break ;
+ } ;
+
+ return ret ;
+} ;
/*------------------------------------------------------------------------------
* Authentication of vty
@@ -479,19 +704,21 @@ uty_command(struct vty *vty, const char *buf)
* Note that if the AUTH_NODE password fails too many times, the terminal is
* closed.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
{
char *passwd = NULL;
enum node_type next_node = 0;
int fail;
char *crypt (const char *, const char *);
+ enum cmd_return_code ret ;
vty_io vio = vty->vio ;
VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
/* What to do ?
*
@@ -502,20 +729,23 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
{
case cli_do_nothing:
case cli_do_ctrl_c:
- case cli_do_ctrl_d:
case cli_do_ctrl_z:
- return 0 ;
+ return CMD_SUCCESS ;
case cli_do_command:
break ;
+ case cli_do_ctrl_d:
+ case cli_do_eof:
+ return uty_cmd_close(vty, "End") ;
+
default:
zabort("unknown or invalid cli_do") ;
} ;
/* Ordinary command dispatch -- see if password is OK. */
switch (vty->node)
- {
+ {
case AUTH_NODE:
if (host.encrypt)
passwd = host.password_encrypt;
@@ -526,6 +756,7 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
else
next_node = VIEW_NODE;
break;
+
case AUTH_ENABLE_NODE:
if (host.encrypt)
passwd = host.enable_encrypt;
@@ -533,7 +764,10 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
passwd = host.enable;
next_node = ENABLE_NODE;
break;
- }
+
+ default:
+ zabort("unknown node type") ;
+ }
if (passwd)
{
@@ -545,6 +779,8 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
else
fail = 1;
+ ret = CMD_SUCCESS ;
+
if (! fail)
{
vio->fail = 0;
@@ -557,9 +793,7 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
{
if (vty->node == AUTH_NODE)
{
- uty_out (vty, "%% Bad passwords, too many failures!%s",
- VTY_NEWLINE);
- uty_half_close(vio) ;
+ ret = uty_cmd_close(vty, "Bad passwords, too many failures!%s") ;
}
else
{
@@ -568,11 +802,13 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
uty_out (vty, "%% Bad enable passwords, too many failures!%s",
VTY_NEWLINE);
vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
+
+ ret = CMD_WARNING ;
}
}
}
- return 0 ;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
@@ -580,13 +816,16 @@ uty_auth (struct vty *vty, const char *buf, enum cli_do cli_do)
*
* Falls back one NODE level.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
vty_cmd_exit(struct vty* vty)
{
- VTY_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ enum cmd_return_code ret ;
+
+ VTY_LOCK() ;
+ ret = CMD_SUCCESS ;
switch (vty->node)
{
case VIEW_NODE:
@@ -594,8 +833,8 @@ vty_cmd_exit(struct vty* vty)
case RESTRICTED_NODE:
if (vty_shell (vty))
exit (0);
-// else
-// vty_set_status(vty, VTY_CLOSE);
+ else
+ ret = uty_cmd_close(vty, "Exit") ;
break;
case CONFIG_NODE:
uty_config_unlock (vty, ENABLE_NODE);
@@ -628,8 +867,8 @@ vty_cmd_exit(struct vty* vty)
break;
}
- VTY_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
- return 0 ;
+ VTY_UNLOCK() ;
+ return ret ;
}
/*------------------------------------------------------------------------------
@@ -637,12 +876,12 @@ vty_cmd_exit(struct vty* vty)
*
* Falls back to ENABLE_NODE.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
vty_cmd_end(struct vty* vty)
{
- VTY_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
+ VTY_LOCK() ;
switch (vty->node)
{
@@ -676,8 +915,22 @@ vty_cmd_end(struct vty* vty)
break;
}
- VTY_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
- return 0 ;
+ VTY_UNLOCK() ;
+ return CMD_SUCCESS ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Result of command is to close the input.
+ *
+ * Posts the reason for the close.
+ *
+ * Returns: CMD_CLOSE
+ */
+extern enum cmd_return_code
+uty_cmd_close(struct vty *vty, const char* reason)
+{
+ vty->vio->close_reason = reason ;
+ return CMD_CLOSE ;
} ;
/*------------------------------------------------------------------------------
@@ -689,9 +942,9 @@ vty_cmd_end(struct vty* vty)
*
* Resets the history pointer.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
uty_stop_input(struct vty *vty)
{
vty_io vio = vty->vio ;
@@ -724,7 +977,7 @@ uty_stop_input(struct vty *vty)
/* Set history pointer to the latest one. */
vio->hp = vio->hindex;
- return 0 ;
+ return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
@@ -734,9 +987,9 @@ uty_stop_input(struct vty *vty)
*
* Fall back to ENABLE_NODE if in any one of a number of nodes.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
uty_end_config (struct vty *vty)
{
VTY_ASSERT_LOCKED() ;
@@ -774,7 +1027,7 @@ uty_end_config (struct vty *vty)
break;
}
- return 0 ;
+ return CMD_SUCCESS ;
}
/*------------------------------------------------------------------------------
@@ -782,9 +1035,9 @@ uty_end_config (struct vty *vty)
*
* Same as "exit" command.
*
- * Returns: 0 <=> not queued.
+ * Returns: command return code
*/
-extern int
+extern enum cmd_return_code
uty_down_level (struct vty *vty)
{
return vty_cmd_exit(vty) ;
@@ -792,10 +1045,28 @@ uty_down_level (struct vty *vty)
/*==============================================================================
* Reading of configuration file
+ *
+ * The reading of the configuration file occurs at two times:
+ *
+ * 1. early in the morning, before daemonisation, and before any threads
+ * or nexuses have been set up.
+ *
+ * In the qpthreads world, this means that it is running in the main (CLI)
+ * and only thread and nexus.
+ *
+ * 2. at SIGHUP time.
+ *
+ * In the qpthreads world, this is running in whatever thread is executing
+ * commands.
+ *
+ * Sets up a VTY_CONFIG_READ in which to execute commands. This has no CLI
+ * and no socket. All output is buffered in the cmd_obuf. All commands are
+ * run directly in the thread -- no commands are queued.
*/
static FILE * vty_use_backup_config (char *fullpath) ;
-static void vty_read_file (FILE *confp, void (*after_first_cmd)(void)) ;
+static void vty_read_file (FILE *confp, struct cmd_element* first_cmd,
+ bool ignore_warnings) ;
/*------------------------------------------------------------------------------
* Read the given configuration file.
@@ -804,7 +1075,7 @@ extern void
vty_read_config (char *config_file,
char *config_default)
{
- vty_read_config_first_cmd_special(config_file, config_default, NULL);
+ vty_read_config_first_cmd_special(config_file, config_default, NULL, 1);
}
/*------------------------------------------------------------------------------
@@ -827,7 +1098,8 @@ vty_read_config (char *config_file,
extern void
vty_read_config_first_cmd_special(char *config_file,
char *config_default,
- void (*after_first_cmd)(void))
+ struct cmd_element* first_cmd,
+ bool ignore_warnings)
{
char cwd[MAXPATHLEN];
FILE *confp = NULL;
@@ -902,7 +1174,7 @@ vty_read_config_first_cmd_special(char *config_file,
fprintf(stderr, "Reading config file: %s\n", fullpath);
#endif
- vty_read_file (confp, after_first_cmd);
+ vty_read_file (confp, first_cmd, ignore_warnings);
fclose (confp);
host_config_set (fullpath);
@@ -998,15 +1270,28 @@ vty_use_backup_config (char *fullpath)
* May have a function to call after the first actual command is processed.
* This mechanism supports the setting of qpthreads-ness by configuration file
* command.
+ *
+ * In the qpthreads world:
+ *
+ * * when the configuration is first read, this runs in the CLI thread
+ * (the main and only thread).
+ *
+ * * when the configuration is reread, this runs in the command processor
+ * thread.
+ *
+ * All consoles are shut down, so there can be no interference from that
+ * quarter.
+ *
+ * so all commands are executed directly.
*/
static void
-vty_read_file (FILE *confp, void (*after_first_cmd)(void))
+vty_read_file (FILE *confp, struct cmd_element* first_cmd, bool ignore_warnings)
{
- int ret;
- struct vty *vty;
+ enum cmd_return_code ret ;
+ struct vty *vty ;
- /* TODO: sort out what VTY Type should use for reading config file */
- vty = vty_new (0, VTY_TERM); /* stdout */
+ /* Set up configuration file reader VTY -- which buffers all output */
+ vty = vty_open(VTY_CONFIG_READ);
vty->node = CONFIG_NODE;
/* Make sure we have a suitable buffer, and set vty->buf to point at
@@ -1016,60 +1301,51 @@ vty_read_file (FILE *confp, void (*after_first_cmd)(void))
vty->buf = qs_chars(&vty->vio->clx) ;
/* Execute configuration file */
- ret = config_from_file (vty, confp, after_first_cmd, &vty->vio->clx) ;
+ ret = config_from_file (vty, confp, first_cmd, &vty->vio->clx,
+ ignore_warnings) ;
VTY_LOCK() ;
- if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
+ if (ret != CMD_SUCCESS)
{
+ fprintf (stderr, "%% while processing line %u of the configuration:\n"
+ "%s", vty->lineno, vty->buf) ;
+
switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- fprintf (stderr, "Ambiguous command.\n");
- break;
- case CMD_ERR_NO_MATCH:
- fprintf (stderr, "There is no such command.\n");
- break;
- }
- fprintf (stderr, "Error occurred while processing:\n%s\n", vty->buf);
+ {
+ case CMD_WARNING:
+ fprintf (stderr, "%% Warning...\n");
+ break;
- exit (1);
- }
+ case CMD_ERROR:
+ fprintf (stderr, "%% Error...\n");
+ break;
- uty_half_close (vty->vio);
- VTY_UNLOCK() ;
-}
+ case CMD_ERR_AMBIGUOUS:
+ fprintf (stderr, "%% Ambiguous command.\n");
+ break;
-#ifdef QDEBUG
-/* Tell all terminals that we are shutting down */
-void
-vty_goodbye (void)
-{
- unsigned int i;
- struct vty *vty;
+ case CMD_ERR_NO_MATCH:
+ fprintf (stderr, "%% There is no such command.\n");
+ break;
- VTY_LOCK() ;
+ case CMD_ERR_INCOMPLETE:
+ fprintf (stderr, "%% Incomplete command.\n");
+ break;
- if (vtyvec)
- {
- for (i = 0; i < vector_active (vtyvec); i++)
- {
- if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->vio->type == VTY_TERM)
- {
- uty_cout(vty, QUAGGA_PROGNAME " is shutting down%s", VTY_NEWLINE);
+ default:
+ fprintf(stderr, "%% (unknown cause %d)\n", ret) ;
+ break ;
+ } ;
- /* Wake up */
- if (vty_cli_nexus)
- vty_event (VTY_WRITE, vty->vio->fd, vty);
- }
- }
- if (qpthreads_enabled)
- qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
- }
+ uty_out_fflush(vty->vio, stderr) ; /* flush command output buffer */
- VTY_UNLOCK() ;
-}
-#endif
+ exit (1);
+ } ;
+
+ uty_close(vty->vio) ;
+ VTY_UNLOCK() ;
+} ;
/*==============================================================================
* Configuration node/state handling
@@ -1197,8 +1473,9 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
timeout += strtol (sec_str, NULL, 10);
vty_timeout_val = timeout;
- vty->vio->file.v_timeout = timeout;
-// vty_event (VTY_TIMEOUT_RESET, 0, vty);
+
+ if (vty_term(vty) || vty_shell_serv(vty))
+ uty_sock_set_timer(&vty->vio->sock, timeout) ;
VTY_UNLOCK() ;
return CMD_SUCCESS;
@@ -1445,7 +1722,7 @@ DEFUN_CALL (show_history,
for (index = vty->vio->hindex + 1; index != vty->vio->hindex;)
{
- const char* line ;
+ qstring line ;
if (index == VTY_MAXHIST)
{
@@ -1455,7 +1732,7 @@ DEFUN_CALL (show_history,
line = vector_get_item(&vty->vio->hist, index) ;
if (line != NULL)
- uty_out (vty, " %s%s", line, VTY_NEWLINE);
+ uty_out (vty, " %s%s", line->char_body, VTY_NEWLINE);
index++;
}
@@ -1545,27 +1822,37 @@ vty_get_cwd ()
* Access functions for VTY values, where locking is or might be required.
*/
-int
+bool
vty_shell (struct vty *vty)
{
+ bool result;
VTY_LOCK() ;
- int result;
- result = (vty->vio->type == VTY_SHELL) ? 1 : 0 ;
+ result = (vty->vio->type == VTY_SHELL) ;
VTY_UNLOCK() ;
return result;
}
-int
+bool
+vty_term(struct vty *vty)
+{
+ bool result;
+ VTY_LOCK() ;
+ result = (vty->vio->type == VTY_TERM);
+ VTY_UNLOCK() ;
+ return result;
+}
+
+bool
vty_shell_serv (struct vty *vty)
{
+ bool result;
VTY_LOCK() ;
- int result;
- result = ((vty->vio->type == VTY_SHELL_SERV) ? 1 : 0);
+ result = (vty->vio->type == VTY_SHELL_SERV);
VTY_UNLOCK() ;
return result;
}
-int
+enum node_type
vty_get_node(struct vty *vty)
{
int result;
@@ -1576,39 +1863,129 @@ vty_get_node(struct vty *vty)
}
void
-vty_set_node(struct vty *vty, int node)
+vty_set_node(struct vty *vty, enum node_type node)
{
VTY_LOCK() ;
vty->node = node;
VTY_UNLOCK() ;
}
-int
-vty_get_type(struct vty *vty)
+void
+vty_set_lines(struct vty *vty, int lines)
{
- int result;
VTY_LOCK() ;
- result = vty->vio->type;
+ vty->vio->lines = lines;
+ vty->vio->lines_set = 1 ;
+ uty_set_height(vty->vio) ;
VTY_UNLOCK() ;
- return result;
}
-int
-vty_get_lines(struct vty *vty)
+/*==============================================================================
+ *
+ */
+const char* wordlist[] =
+ {
+ "Lorem",
+ "ipsum",
+ "dolor",
+ "magna",
+ "vita",
+ "brevis",
+ "Aliquot",
+ "in",
+ "tempura",
+ "mores",
+ "ad",
+ "Astronomica",
+ "per",
+ "impedimenta",
+ "quod",
+ "et",
+ "sed",
+ "semper",
+ "ut",
+ "Elisium",
+ "est",
+ };
+
+
+DEFUN (delay_secs,
+ delay_secs_cmd,
+ "delay <0-600> secs <0-10000> lines",
+ "Delay for a number of seconds and spit out a number of lines\n"
+ "Delay time\n"
+ "Delay time units\n"
+ "How much to output\n"
+ "Output units\n")
{
- int result;
- VTY_LOCK() ;
- result = vty->vio->lines;
- VTY_UNLOCK() ;
- return result;
-}
+ unsigned long delay ;
+ unsigned long lines ;
-void
-vty_set_lines(struct vty *vty, int lines)
-{
- VTY_LOCK() ;
- vty->vio->lines = lines;
- VTY_UNLOCK() ;
+ unsigned long unit ;
+
+ delay = strtol(argv[0], NULL, 10) ;
+ lines = strtol(argv[1], NULL, 10) ;
+
+ vty_out(vty, "delay %d secs %d lines\n", (int)delay, (int)lines) ;
+
+ unit = (lines * 100) / delay ;
+
+ while (delay--)
+ {
+ char buf[200] ;
+ char* e ;
+ int n ;
+ int w = sizeof(wordlist) / sizeof(char*) ;
+
+ sleep(1) ;
+
+ n = ((rand() % (unit + 1)) + (unit / 2)) / 100 ;
+
+ if ((n > (int)lines) || (delay == 0))
+ n = lines ;
+
+ lines -= n ;
+
+ while (n--)
+ {
+ char* q ;
+ const char* p ;
+ int a ;
+
+ q = buf ;
+ e = buf + (rand() % 120) + 30 ;
+
+ if ((rand() % 6) == 0)
+ e = buf ;
+
+ a = (rand() % 4) == 1 ;
+ while (q < e)
+ {
+ int s ;
+ s = 0 ;
+ if (a == 1)
+ s = (rand() % 5) + 1 ;
+ else if (a > 1)
+ s = 1 ;
+
+ while (s--)
+ *q++ = ' ' ;
+
+ p = wordlist[rand() % w] ;
+ while (*p != '\0')
+ *q++ = *p++ ;
+
+ a = (rand() % 4) + 1 ;
+ } ;
+
+ *q++ = '\n' ;
+ *q++ = '\0' ;
+
+ vty_out(vty, buf) ;
+ } ;
+ } ;
+
+ return CMD_SUCCESS;
}
/*==============================================================================
@@ -1632,6 +2009,9 @@ uty_init_commands (void)
install_node (&vty_node, vty_config_write);
+ install_element (VIEW_NODE, &delay_secs_cmd);
+ install_element (ENABLE_NODE, &delay_secs_cmd);
+
install_element (RESTRICTED_NODE, &config_who_cmd);
install_element (RESTRICTED_NODE, &show_history_cmd);
install_element (VIEW_NODE, &config_who_cmd);
diff --git a/lib/vty.c.x b/lib/vty.c.x
deleted file mode 100644
index bed6fc28..00000000
--- a/lib/vty.c.x
+++ /dev/null
@@ -1,4414 +0,0 @@
-/*
- * Virtual terminal [aka TeletYpe] interface routine.
- * 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.
- */
-
-#include <zebra.h>
-#include "miyagi.h"
-
-#include "keystroke.h"
-#include "vty_io.h"
-#include "vty_cli.h"
-
-#include "linklist.h"
-#include "thread.h"
-#include "buffer.h"
-#include <lib/version.h>
-#include "command.h"
-#include "sockunion.h"
-#include "memory.h"
-#include "str.h"
-#include "log.h"
-#include "prefix.h"
-#include "filter.h"
-#include "vty.h"
-#include "privs.h"
-#include "network.h"
-
-#include <arpa/telnet.h>
-#include "qpthreads.h"
-#include "qpnexus.h"
-
-
-
-
-/* Needs to be qpthread safe */
-qpt_mutex_t vty_mutex;
-#ifdef NDEBUG
-#define LOCK qpt_mutex_lock(&vty_mutex);
-#define UNLOCK qpt_mutex_unlock(&vty_mutex);
-#else
-int vty_lock_count = 0;
-int vty_lock_asserted = 0;
-#define LOCK qpt_mutex_lock(&vty_mutex);++vty_lock_count;
-#define UNLOCK --vty_lock_count;qpt_mutex_unlock(&vty_mutex);
-#define ASSERTLOCKED if(vty_lock_count==0 && !vty_lock_asserted){vty_lock_asserted=1;assert(0);}
-#endif
-
-/*==============================================================================
- * 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.
- */
-
-/*------------------------------------------------------------------------------
- *
- */
-
-/*------------------------------------------------------------------------------
- * Prototypes
- */
-static int uty_out (struct vty *vty, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3) ;
-static int uty_vout(struct vty *vty, const char *format, va_list args);
-
-static int uty_vbuf(vty_io vio, const char *format, va_list args) ;
-static void uty_cout (vty_io vio, const char *format, ...)
- PRINTF_ATTRIBUTE(2, 3) ;
-static void uty_cwrite(vty_io vio, const char *this, int len) ;
-
-static void uty_cecho(vty_io vio, const char *this, size_t len) ;
-static void uty_cecho_n(vty_io vio, const char *this, size_t len, int n) ;
-
-static void vty_event (enum vty_event, int, struct vty *);
-static void uty_close (struct vty *vty);
-static int uty_config_unlock (struct vty *vty);
-static int uty_read (struct vty *vty);
-static int uty_flush (struct vty *vty, int vty_sock);
-static void vty_event_t (enum vty_event event, int sock, struct vty *vty);
-static void vty_event_r (enum vty_event event, int sock, struct vty *vty);
-static int uty_accept (int accept_sock);
-static int uty_timeout (struct vty *vty);
-static void vty_timeout_r (qtimer qtr, void* timer_info, qtime_t when);
-static void vty_read_r (qps_file qf, void* file_info);
-static void vty_flush_r (qps_file qf, void* file_info);
-void uty_reset (void);
-
-/* Extern host structure from command.c */
-extern struct host host;
-
-/*------------------------------------------------------------------------------
- * Static Variables
- */
-
-/* For thread handling need the thread_master -- initialised in vty_init */
-static struct thread_master *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.
- */
-static qpn_nexus cli_nexus = NULL ;
-static qpn_nexus routing_nexus = NULL ;
-
-/* List of all known vty */
-static struct vty* vty_known = NULL ;
-
-/* List of all vty which are in monitor state. */
-static struct vty* vty_monitors = NULL ;
-
-/* Vty timeout value -- see "exec timeout" command */
-static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
-
-/* Vty access-class command */
-static char *vty_accesslist_name = NULL;
-
-/* Vty access-class for IPv6. */
-static char *vty_ipv6_accesslist_name = NULL;
-
-/* VTY server thread. */
-//static vector Vvty_serv_thread;
-
-/* Current directory -- initialised in vty_init() */
-static char *vty_cwd = NULL;
-
-/* Configure lock -- only one vty may be in CONFIG_NODE or above ! */
-static int vty_config;
-
-/* Login password check override. */
-static int no_password_check = 0;
-
-/* Restrict unauthenticated logins? */
-static const u_char restricted_mode_default = 0;
-static u_char restricted_mode = 0;
-
-/*------------------------------------------------------------------------------
- * Global Variables
- */
-
-/* Integrated configuration file path -- for VTYSH */
-char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ;
-
-/*==============================================================================
- * Interlock mechanism for command execution -- qpthreads version.
- *
- * The meaning and validity of a given command line depends on all previous
- * command lines having been processed.
- *
- * Most commands are not executed in the CLI thread. So, an interlock is
- * required so that a command is not dispatched until the previous one has
- * been processed to completion.
- *
- * In the struct vty is the "node" value -- this is the current command context.
- * There are a few other values which provide further context, but those are
- * not required by the command line handling.
- *
- * The processing of commands proceeds as follows:
- *
- * (1) no command queued and no output buffered
- *
- * The command processor is idle, and waiting for the user to complete
- * a new command. The screen will look like:
- *
- * <prompt>: user ..... input []
- *
- * (where [] is the cursor).
- *
- * When the user hits ENTER, a newline is output, and the command is parsed
- * according to the current "node". If there is a match, the command can be
- * dispatched. Otherwise, may need to work up the "node" tree until get a
- * match or run out of tree.
- *
- * If command parses successfully, proceed to:
- *
- * (2) if the command can be executed in the CLI thread (or if not
- * running qpthreads-wise).
- *
- * (3) if the command must be sent to the routeing engine for execution.
- *
- * If fails to parse the command, then an error message will be output and
- * buffered. There is now output pending -- proceed to (4).
- *
- * (2) command can be executed in the CLI thread.
- *
- * This is always the case if not running qpthreads-wise.
- *
- * The command is executed immediately.
- *
- * During execution the command may generate some output. All that output
- * is buffered until the command completes.
- *
- * No input will be processed while the command is being executed. So there
- * will be no partial command in hand.
- *
- * (3) command must be sent to the routing engine.
- *
- * The command is dispatched to the routing engine, and there is now a
- * "command queued".
- *
- * While the command is executing, any output will be buffered. So the
- * command line processor is free to use the console and the user
- * may enter a further command.
- *
- * NB: If any context sensitive help is requested, it will be given in the
- * current known context -- which may NOT be the actual context when the
- * queued command completes.
- *
- * If the queued command completes before the user hits ENTER, the command
- * line will be wiped out, and proceeds to (4) with the partial command
- * in hand.
- *
- * If the queued command does not complete before the user hits enter,
- * then stops processing input until the queued command does complete, and
- * then proceeds to (4) with the command in hand, and the ENTER pending
- *
- * (4) command completes (possibly because did not parse !).
- *
- * Any buffered output is now actually output. No input processing is
- * done until all output has been sent.
- *
- * TODO: The " --More-- " handling ????
- *
- * Once the output completes, the current prompt is shown, followed by
- * any partial command line.
- *
- * Loop back to (1).
- *
- *
- */
-
-/*==============================================================================
- * General VTY output.
- *
- * This is mostly used during command execution, to output the results of the
- * command.
- *
- * All these end up in uty_vout -- see below.
- *
- * For VTY_TERM and VTY_SHELL_SERV, all output is to the vty obuf. So, all
- * output generated by a command is collected in the obuf, which is flushed
- * to the terminal or to the shell when the command completes. This means
- * that:
- *
- * TODO: what does this all mean ??
- *
- * * the breaking up of ******
- *
- * * other output (in particular "monitor" output) can go directly to the
- * terminal (or shell ?).
- *
- * *
- *
- */
-
-/*------------------------------------------------------------------------------
- * VTY output -- cf fprintf !
- */
-extern int
-vty_out (struct vty *vty, const char *format, ...)
-{
- int result;
-
- LOCK
- va_list args;
- va_start (args, format);
- result = uty_vout(vty, format, args);
- va_end (args);
- UNLOCK
- return result;
-}
-
-/*------------------------------------------------------------------------------
- * VTY output -- output a given numnber of spaces
- */
-
-/* 1 2 3 4 */
-/* 1234567890123456789012345678901234567890 */
-const char vty_spaces_string[] = " " ;
-CONFIRM(VTY_MAX_SPACES == (sizeof(vty_spaces_string) - 1)) ;
-
-extern int
-vty_out_indent(struct vty *vty, int indent)
-{
- while (indent > VTY_MAX_SPACES)
- {
- int ret = vty_out(vty, VTY_SPACES(indent)) ;
- if (ret < 0)
- return ret ;
- indent -= VTY_MAX_SPACES ;
- }
- return vty_out(vty, VTY_SPACES(indent)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * VTY output -- output the current time in standard form, to the second.
- */
-extern void
-vty_time_print (struct vty *vty, int cr)
-{
- char buf [timestamp_buffer_len];
-
- quagga_timestamp(0, buf, sizeof(buf)) ;
-
- if (cr)
- vty_out (vty, "%s\n", buf);
- else
- vty_out (vty, "%s ", buf);
-
- return;
-}
-
-/*------------------------------------------------------------------------------
- * Say hello to vty interface.
- */
-void
-vty_hello (struct vty *vty)
-{
- LOCK
-
-#ifdef QDEBUG
- uty_out (vty, "%s%s", debug_banner, VTY_NEWLINE);
-#endif
- if (host.motdfile)
- {
- FILE *f;
- char buf[4096];
-
- f = fopen (host.motdfile, "r");
- if (f)
- {
- while (fgets (buf, sizeof (buf), f))
- {
- char *s;
- /* work backwards to ignore trailing isspace() */
- for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
- s--);
- *s = '\0';
- uty_out (vty, "%s%s", buf, VTY_NEWLINE);
- }
- fclose (f);
- }
- else
- uty_out (vty, "MOTD file %s not found%s", host.motdfile, VTY_NEWLINE);
- }
- else if (host.motd)
- uty_out (vty, "%s", host.motd);
-
- UNLOCK
-}
-
-
-/*==============================================================================
- * CLI VTY output
- *
- * 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.
- *
- * 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.
- *
- *
- */
-
-/*------------------------------------------------------------------------------
- * CLI VTY output -- cf fprintf()
- *
- * No actual I/O takes place -- all "output" is to vio->cbuf and/or vio->cxbuf
- */
-static void
-uty_cout (vty_io vio, const char *format, ...)
-{
- va_list args;
- int len ;
-
- ASSERTLOCKED
-
- va_start (args, format);
- len = uty_vbuf(vio, format, args) ;
- va_end(args);
-
- if (len > 0)
- uty_cwrite(vio, vio->vbuf, len) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * CLI VTY output -- cf write()
- *
- * No actual I/O takes place -- all "output" is to vio->cbuf and/or vio->cxbuf
- */
-static void
-uty_cwrite(vty_io vio, const char *this, int len)
-{
- ASSERTLOCKED
-
- while (len > 0)
- {
- int take ;
- take = vio->cbuf_end - vio->cbuf_ptr ;
-
- if (take == 0)
- {
- take = vty_cout_buffer_size ;
- if (vio->cbuf == NULL)
- {
- vio->cbuf = XMALLOC(MTYPE_VTY_OUT_BUF, take) ;
- vio->cbuf_end = vio->cbuf + vty_cout_buffer_size ;
- }
- else
- {
- assert((vio->cbuf_ptr - vio->cbuf) == take) ;
- buffer_put(&vio->cxbuf, (u_char*)vio->cbuf, take) ;
- } ;
- vio->cbuf_ptr = vio->cbuf ;
- } ;
-
- if (take > len)
- take = len ;
-
- memcpy(vio->cbuf_ptr, this, take) ;
- vio->cbuf_ptr += take ;
- this += take ;
- len -= take ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * CLI VTY output -- echo user input
- *
- * Do nothing if echo suppressed (eg in AUTH_NODE)
- *
- * No actual I/O takes place -- all "output" is to vio->cbuf and/or vio->cxbuf
- */
-static void
-uty_cecho (vty_io vio, const char *this, size_t len)
-{
- ASSERTLOCKED
-
- if (vio->cecho_suppress)
- return ;
-
- uty_cwrite(vio, this, len) ;
-}
-
-/*------------------------------------------------------------------------------
- * CLI VTY output -- echo given stuff 'n' times
- *
- * Do nothing if echo suppressed (eg in AUTH_NODE)
- *
- * No actual I/O takes place -- all "output" is to vio->cbuf and/or vio->cxbuf
- */
-static void
-uty_cecho_n(vty_io vio, const char *this, size_t len, int n)
-{
- ASSERTLOCKED
-
- if (vio->cecho_suppress)
- return ;
-
- while (n-- > 0)
- uty_cwrite(vio, this, len) ;
-}
-
-/*==============================================================================
- * Output to vty which are set to "monitor".
- *
- *
- *
- */
-
-/*------------------------------------------------------------------------------
- * Output logging information to all vty which are set to "monitor".
- */
-extern void
-uty_log(struct logline* ll, struct zlog *zl, int priority,
- const char *format, va_list va)
-{
- struct vty *vty;
-
- ASSERTLOCKED
-
- vty = sdl_head(vty_monitors) ;
-
- if (vty == NULL)
- return ; /* go no further if no "monitor" vtys */
-
- /* Prepare line for output. */
- uvzlog_line(ll, zl, priority, format, va, 1) ; /* with crlf */
-
- /* write to all known "monitor" vty */
- while (vty != NULL)
- {
- vty_io vio = vty->vio ;
-
- if ((vio != NULL) && vio->monitor)
- {
- vio->monitor = 0 ;
-
- if (write(vio->fd, ll->line, ll->len) < 0)
- {
-#if 0 // TODO: deal with error in write in uty_log()
-
- if (ERRNO_IO_RETRY(errno))
- /* Kernel buffer is full, probably too much debugging output, so just
- drop the data and ignore. */
- return -1;
- /* Fatal I/O error. */
- vty->vio->monitor = 0; /* disable monitoring to avoid infinite recursion */
- uzlog(NULL, LOG_WARNING, "%s: write failed to vty client fd %d, closing: %s",
- __func__, vty->vio->fd, safe_strerror(errno));
- buffer_reset(vty->vio->obuf);
- /* cannot call vty_close, because a parent routine may still try
- to access the vty struct */
- vty->vio->status = VTY_CLOSE;
- shutdown(vty->vio->fd, SHUT_RDWR);
- return -1;
-#endif
- }
-
- vio->monitor = 1 ;
- } ;
- vty = sdl_next(vty, monitor_list) ;
- } ;
-} ;
-
-/*==============================================================================
- */
-
-/*------------------------------------------------------------------------------
- * Allocate new vty struct
- *
- * Allocates and initialises vty_io structure, complete with:
- *
- * Output buffer
- * Input buffer
- * qpselect file -- added to CLI nexus ) if running CLI nexus
- * qtimer )
- *
- * Adds to the known vty's -- which locks/unlocks momentarily.
- *
- */
-struct vty *
-vty_new (int fd, int type)
-{
- struct vty *vty ;
- struct vty_io* vio ;
-
- vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
- vio = XCALLOC (MTYPE_VTY, sizeof (struct vty_io)) ;
-
- vty->vio = vio ;
- vio->vty = vty ;
-
- vio->obuf = buffer_new(0); /* Use default buffer size. */
-
- // vio->cbuf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
- // vio->max = VTY_BUFSIZ;
- // vio->fd = fd;
- // vio->type = type;
-
- if (cli_nexus)
- {
- vio->qf = qps_file_init_new(vio->qf, NULL);
- qps_add_file(cli_nexus->selection, vio->qf, vio->fd, vio);
- vio->qtr = qtimer_init_new(vio->qtr, cli_nexus->pile, vty_timeout_r, vio);
- }
-
- LOCK
- sdl_push(vty_known, vty, vty_list) ;
- UNLOCK
-
- return vty;
-}
-
-
-
-/*==============================================================================
- * VTY telnet stuff
- */
-
-#define TELNET_OPTION_DEBUG
-
-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_BRK ] = "BRK",
- [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_Transmit_Binary] = "Binary",
- [to_Echo ] = "Echo",
- [to_Suppress_GA ] = "Suppress_GA",
- [to_Status ] = "Status",
- [to_Timing_Mark ] = "Timing_Mark",
- [to_Terminal_Type ] = "Terminal_Type",
- [to_Window_Size ] = "Window_Size", /* NAWS */
- [to_Terminal_Speed ] = "Terminal_Speed",
- [to_Line_Mode ] = "Line_Mode",
-} ;
-
-static const char* telnet_actions[2] =
-{
- [ta_Ask ] = "Ask",
- [ta_Value] = "Value",
-} ;
-
-static void
-uty_cout_dec(vty_io vio, const char* str, unsigned char u)
-{
- if (str != NULL)
- uty_cout(vio, "%s ", str) ;
- else
- uty_cout(vio, "%d ", (int)u) ;
-} ;
-
-static void
-uty_cout_hex(vty_io vio, const char* str, unsigned char u)
-{
- if (str != NULL)
- uty_cout(vio, "%s ", str) ;
- else
- uty_cout(vio, "0x%02x ", (unsigned)u) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Send telnet: "WILL TELOPT_ECHO"
- */
-static void
-vty_will_echo (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO };
- ASSERTLOCKED
- uty_cwrite (vty->vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "suppress Go-Ahead"
- */
-static void
-vty_will_suppress_go_ahead (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, WILL, TELOPT_SGA };
- ASSERTLOCKED
- uty_cwrite (vty->vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "don't use linemode"
- */
-static void
-vty_dont_linemode (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE };
- ASSERTLOCKED
- uty_cwrite (vty->vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "Use window size"
- */
-static void
-vty_do_window_size (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, DO, TELOPT_NAWS };
- ASSERTLOCKED
- uty_cwrite (vty->vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "don't use lflow"
- */
-#if 1 /* Currently not used. */
-static void
-vty_dont_lflow_ahead (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW };
- ASSERTLOCKED
- uty_cwrite (vty->vio, (char*)cmd, (int)sizeof(cmd));
-}
-#endif /* 0 */
-
-/*------------------------------------------------------------------------------
- * Process incoming Telnet Option(s)
- *
- * In particular: get telnet window size.
- */
-static void
-uty_telnet_command(vty_io vio, keystroke stroke)
-{
- uint8_t* p ;
- uint8_t o ;
- int left ;
-
-#ifdef TELNET_OPTION_DEBUG
- /* Echo to the other end if required */
-
- p = stroke->buf ;
- left = stroke->len ;
-
- uty_cout_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
-
- if (left-- > 0)
- uty_cout_dec(vio, telnet_commands[*p], *p) ;
- ++p ;
-
- if (left-- > 0)
- uty_cout_dec(vio, telnet_options[*p], *p) ;
- ++p ;
-
- if (left > 0)
- {
- while(left-- > 0)
- uty_cout_hex(vio, NULL, *p++) ;
-
- if (stroke->flags & kf_truncated)
- uty_cout(vio, "... ") ;
-
- if (!(stroke->flags & kf_broken))
- {
- uty_cout_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
- uty_cout_hex(vio, telnet_commands[tn_SE], tn_SE) ;
- }
- } ;
-
- if (!(stroke->flags & kf_broken))
- uty_cout (vio, "BROKEN") ;
-
- uty_cout (vio, "\r\n") ;
-
-#endif
-
- if (stroke->flags != 0)
- return ; /* 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_Window_Size:
- 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 ;
-
-#ifdef TELNET_OPTION_DEBUG
- uty_cout(vio, "TELNET NAWS window size negotiation completed: "
- "width %d, height %d%s",
- vio->width, vio->height, TERM_NEWLINE) ;
-#endif
- } ;
- break ;
-
- default: /* no other IAC SB <option> */
- break ;
- } ;
- break ;
-
- default: /* no other IAC X */
- break ;
- } ;
-
-} ;
-
-/******************************************************************************/
-
-
-static void uty_cl_ensure (vty_io vio, unsigned length) ;
-static char* uty_cl_terminate(vty_io vio) ;
-static int uty_cl_insert (vty_io vio, const char* chars, int n) ;
-static int uty_cl_overwrite (vty_io vio, char* chars, int n) ;
-static int uty_cl_word_overwrite (vty_io vio, char *str) ;
-static int uty_cl_forwards(vty_io vio, int n) ;
-static int uty_cl_backwards(vty_io vio, int n) ;
-static int uty_cl_del_forwards(vty_io vio, int n) ;
-static int uty_cl_del_backwards(vty_io vio, int n) ;
-static int uty_cl_bol (vty_io vio) ;
-static int uty_cl_eol (vty_io vio) ;
-static int uty_cl_word_forwards_delta(vty_io vio) ;
-static int uty_cl_word_forwards(vty_io vio) ;
-static int uty_cl_word_backwards_delta(vty_io vio, int eat_spaces) ;
-static int uty_cl_word_backwards_pure (vty_io vio) ;
-static int uty_cl_word_backwards (vty_io vio) ;
-static int uty_cl_del_word_forwards(vty_io vio) ;
-static int uty_cl_del_word_backwards(vty_io vio) ;
-static int uty_cl_del_to_eol (vty_io vio) ;
-static int uty_cl_clear_line(vty_io vio) ;
-static int uty_cl_transpose_chars(vty_io vio) ;
-static void uty_cl_prompt (vty_io vio) ;
-static int uty_cl_redraw_line (vty_io vio) ;
-static int uty_cl_redraw(vty_io vio) ;
-static void uty_cl_hist_add (vty_io vio) ;
-static void uty_cl_history_use(vty_io vio, int step) ;
-static void uty_cl_next_line(vty_io vio) ;
-static void uty_cl_previous_line (vty_io vio) ;
-static void uty_cl_describe_fold (vty_io vio, int cmd_width,
- unsigned int desc_width, struct desc *desc) ;
-static void uty_cl_describe_command (vty_io vio) ;
-static void uty_cl_complete_command (vty_io vio) ;
-
-
-/*==============================================================================
- * Command line processing loop
- *
- * Process keystrokes until run out of stuff to do, or have a "command line"
- * that must now be executed.
- *
- * Processes the contents of the keystroke stream. If exhausts that, will set
- * ready to read and return. (To give some "sharing".)
- *
- * Returns: cli_null -- no action required
- * cli_dispatch -- CR or LF received
- * cli_ctrl_c -- ^C received
- * cli_ctrl_d -- ^D received, on empty line
- * cli_ctrl_z -- ^Z received
- *
- * When returns the vio->cl is the state of the current command line.
- *
- */
-
-#define CONTROL(X) ((X) - '@')
-
-static enum cli_returns
-uty_cl_process(vty_io vio)
-{
- struct keystroke stroke ;
- uint8_t u ;
-
- /* Now process as much as possible of what there is */
- while (keystroke_get(vio->key_stream, &stroke))
- {
- if (stroke.flags != 0)
- {
- /* TODO: deal with broken keystrokes */
- continue ;
- } ;
-
- switch (stroke.type)
- {
- /* Straightforward character -----------------------------------*/
- /* Note: only interested in 8-bit characters ! */
- case ks_char:
- u = (uint8_t)stroke.value ;
-
- switch (stroke.value)
- {
- case CONTROL('A'):
- uty_cl_bol (vio);
- break;
-
- case CONTROL('B'):
- uty_cl_backwards(vio, 1);
- break;
-
- case CONTROL('C'):
- return cli_ctrl_c ; /* Exit on ^C ..................*/
-
- case CONTROL('D'):
- if (vio->cl.ep == 0) /* if at start of empty line */
- return cli_ctrl_d ; /* Exit on ^D ..................*/
-
- uty_cl_del_forwards(vio, 1);
- break;
-
- case CONTROL('E'):
- uty_cl_eol (vio);
- break;
-
- case CONTROL('F'):
- uty_cl_forwards(vio, 1);
- break;
-
- case CONTROL('H'):
- case 0x7f:
- uty_cl_del_backwards(vio, 1);
- break;
-
- case CONTROL('K'):
- uty_cl_del_to_eol (vio);
- break;
-
- case CONTROL('N'):
- uty_cl_next_line (vio);
- break;
-
- case CONTROL('P'):
- uty_cl_previous_line (vio);
- break;
-
- case CONTROL('T'):
- uty_cl_transpose_chars (vio);
- break;
-
- case CONTROL('U'):
- uty_cl_clear_line(vio);
- break;
-
- case CONTROL('W'):
- uty_cl_del_word_backwards (vio);
- break;
-
- case CONTROL('Z'):
- return cli_ctrl_z ; /* Exit on ^Z ..................*/
- break;
-
- case '\n':
- case '\r':
- return cli_dispatch ; /* Exit on CR or LF.............*/
-
- case '\t':
- uty_cl_complete_command (vio);
- break;
-
- case '?':
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
- uty_cl_insert (vio, (char*)&u, 1);
- else
- uty_cl_describe_command (vio);
- break;
-
- default:
- if ((stroke.value >= 0x20) && (stroke.value < 0x7F))
- uty_cl_insert (vio, (char*)&u, 1) ;
- break;
- }
- break ;
-
- /* ESC X -------------------------------------------------------------*/
- case ks_esc:
- switch (stroke.value)
- {
- case 'b':
- uty_cl_word_backwards (vio);
- break;
-
- case 'f':
- uty_cl_word_forwards (vio);
- break;
-
- case 'd':
- uty_cl_del_word_forwards (vio);
- break;
-
- case CONTROL('H'):
- case 0x7f:
- uty_cl_del_word_backwards (vio);
- break;
-
- default:
- break;
- } ;
- break ;
-
- /* ESC [ X -----------------------------------------------------------*/
- case ks_csi:
- if (stroke.len != 0)
- break ; /* only recognise 3 byte sequences */
-
- switch (stroke.value)
- {
- case ('A'):
- uty_cl_previous_line (vio);
- break;
-
- case ('B'):
- uty_cl_next_line (vio);
- break;
-
- case ('C'):
- uty_cl_forwards(vio, 1);
- break;
-
- case ('D'):
- uty_cl_backwards(vio, 1);
- break;
- default:
- break ;
- } ;
- break ;
-
- /* Telnet Command ----------------------------------------------------*/
- case ks_iac:
- uty_telnet_command(vio, &stroke) ;
- break ;
-
- /* Single byte escape ------------------------------------------------*/
- default:
- zabort("unknown keystroke type") ;
- } ;
-
- /* After each keystroke..... */
-
-
- } ;
-
-
-
- /* Check status. */
- if (vio->status == VTY_CLOSE)
- uty_close (vty);
- else
- {
- vty_event (VTY_WRITE, vio->fd, vty);
- vty_event (VTY_READ, vio->fd, vty);
- }
-
- return 0;
-}
-
-
-
-
-
-
-
-
-
-
-
-/*==============================================================================
- * Command line operations
- */
-
-static const char telnet_backward_char = 0x08;
-static const char telnet_space_char = ' ';
-
-/*------------------------------------------------------------------------------
- * Ensure length of command line buffer.
- *
- * Allocate or reallocate buffer so is large enough for the given length, PLUS
- * one extra for '\0'.
- */
-static void
-uty_cl_ensure (vty_io vio, unsigned length)
-{
- ASSERTLOCKED
-
- if (vio->cl.size <= length) /* allows for trailing '\n' */
- {
- if (vio->cl.size == 0)
- {
- vio->cl.size = 200 ;
- vio->cl.buf = XMALLOC(MTYPE_VTY, vio->cl.size) ;
- }
- else
- {
- vio->cl.size *= 2 ;
- vio->cl.buf = XREALLOC (MTYPE_VTY, vio->cl.buf, vio->cl.size) ;
- } ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Terminate the command line.
- *
- * Allocate or reallocate buffer so is large enough for the given length, plus
- * one extra for '\0'.
- */
-static char*
-uty_cl_terminate(vty_io vio)
-{
- ASSERTLOCKED
-
- uty_cl_ensure (vio, vio->cl.ep) ;
-
- vio->cl.buf[vio->cl.ep] = '\0' ;
-
- return vio->cl.buf ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Insert 'n' characters at current position in the command line
- *
- * Returns number of characters inserted -- ie 'n'
- */
-static int
-uty_cl_insert (vty_io vio, const char* chars, int n)
-{
- int after ;
-
- ASSERTLOCKED
-
- assert((vio->cl.cp <= vio->cl.ep) && (n >= 0)) ;
-
- if (n == 0)
- return n ;
-
- uty_cl_ensure (vio, vio->cl.ep + n) ;
-
- after = vio->cl.ep - vio->cl.cp ;
- if (after != 0)
- memmove (&vio->cl.buf[vio->cl.cp + n], &vio->cl.buf[vio->cl.cp], after) ;
-
- memmove(&vio->cl.buf[vio->cl.cp], chars, n) ;
-
- uty_cecho(vio, (char*)&vio->cl.buf[vio->cl.cp], after + n) ;
-
- if (after != 0)
- uty_cecho_n(vio, &telnet_backward_char, 1, after) ;
-
- vio->cl.cp += n ;
- vio->cl.ep += n ;
-
- return n ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Overstrike 'n' characters at current position in the command line
- *
- * Move current position forwards.
- *
- * Returns number of characters inserted -- ie 'n'
- */
-static int
-uty_cl_overwrite (vty_io vio, char* chars, int n)
-{
- ASSERTLOCKED
-
- assert((vio->cl.cp <= vio->cl.ep) && (n >= 0)) ;
-
- if (n > 0)
- {
- if ((vio->cl.cp + n) > vio->cl.ep)
- {
- vio->cl.ep = vio->cl.cp + n ;
- uty_cl_ensure (vio, vio->cl.ep) ;
- } ;
-
- memmove(&vio->cl.buf[vio->cl.cp], chars, n) ;
- uty_cecho(vio, chars, n) ;
-
- vio->cl.cp += n ;
- } ;
-
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Insert a word into vty interface with overwrite mode.
- *
- * NB: Assumes result will then be the end of the line.
- *
- * Returns number of characters inserted -- ie length of string
- */
-static int
-uty_cl_word_overwrite (vty_io vio, char *str)
-{
- int n ;
- ASSERTLOCKED
-
- n = uty_cl_overwrite(vio, str, strlen(str)) ;
-
- vio->cl.ep = vio->cl.cp ;
-
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Forward 'n' characters -- stop at end of line.
- *
- * Returns number of characters actually moved
- */
-static int
-uty_cl_forwards(vty_io vio, int n)
-{
- int c ;
- ASSERTLOCKED
-
- c = vio->cl.ep - vio->cl.cp ;
- if (n > c)
- n = c ;
-
- assert(n >= 0) ;
-
- if (n > 0)
- {
- uty_cecho(vio, (char*)&vio->cl.buf[vio->cl.cp], n) ;
- vio->cl.cp += n ;
- } ;
-
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Backwards 'n' characters -- stop at start of line.
- *
- * Returns number of characters actually moved
- */
-static int
-uty_cl_backwards(vty_io vio, int n)
-{
- int c ;
- ASSERTLOCKED
-
- c = vio->cl.ep - vio->cl.cp ;
- if (n > c)
- n = c ;
-
- assert(n >= 0) ;
-
- if (n > 0)
- {
- uty_cecho(vio, &telnet_backward_char, n);
- vio->cl.cp -= n ;
- } ;
-
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Delete 'n' characters -- forwards -- stop at end of line.
- *
- * Returns number of characters actually deleted.
- */
-static int
-uty_cl_del_forwards(vty_io vio, int n)
-{
- int after ;
-
- ASSERTLOCKED
-
- after = (vio->cl.ep - vio->cl.cp) ;
- assert((n >= 0) && (after >= 0)) ;
-
- if ((n == 0) || (after == 0))
- return 0 ; /* completion need here? */
-
- if (n > after)
- n = after ;
-
- after -= n ;
-
- if (after > 0)
- {
- memmove (&vio->cl.buf[vio->cl.cp], &vio->cl.buf[vio->cl.cp + n], after) ;
- uty_cecho(vio, (char*)&vio->cl.buf[vio->cl.cp], after) ;
- } ;
-
- uty_cecho_n(vio, &telnet_space_char, 1, n) ;
- uty_cecho_n(vio, &telnet_backward_char, 1, after + n) ;
-
- vio->cl.ep -= n ;
-
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Delete 'n' characters before the point.
- *
- * Returns number of characters actually deleted.
- */
-static int
-uty_cl_del_backwards(vty_io vio, int n)
-{
- return uty_cl_del_forwards(vio, uty_cl_backwards(vio, n)) ;
-}
-
-/*------------------------------------------------------------------------------
- * Move to the beginning of the line.
- *
- * Returns number of characters moved over.
- */
-static int
-uty_cl_bol (vty_io vio)
-{
- return uty_cl_backwards(vio, vio->cl.cp) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Move to the end of the line.
- *
- * Returns number of characters moved over
- */
-static int
-uty_cl_eol (vty_io vio)
-{
- return uty_cl_forwards(vio, vio->cl.ep - vio->cl.cp) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Forward word delta -- distance to start of next word.
- *
- * Return number of characters to step over to reach next word.
- *
- * Steps over non-space characters and then any spaces.
- */
-static int
-uty_cl_word_forwards_delta(vty_io vio)
-{
- unsigned tp = vio->cl.cp ;
-
- ASSERTLOCKED ;
-
- assert(vio->cl.cp <= vio->cl.ep) ;
-
- while ((tp < vio->cl.ep) && (vio->cl.buf[tp] != ' '))
- ++tp ;
-
- while ((tp < vio->cl.ep) && (vio->cl.buf[tp] == ' '))
- ++tp ;
-
- return tp - vio->cl.cp ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Forward word -- move to start of next word.
- *
- * Moves past any non-spaces, then past any spaces.
- */
-static int
-uty_cl_word_forwards(vty_io vio)
-{
- return uty_cl_forwards(vio, uty_cl_word_forwards_delta(vio)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Backward word delta -- distance to start of next word, back.
- *
- * Return number of characters to step over to reach next word.
- *
- * If "eat_spaces", starts by stepping over spaces.
- * Steps back until next (backwards) character is space, or hits start of line.
- */
-static int
-uty_cl_word_backwards_delta(vty_io vio, int eat_spaces)
-{
- int tp = vio->cl.cp ;
-
- ASSERTLOCKED ;
-
- assert(vio->cl.cp <= vio->cl.ep) ;
-
- if (eat_spaces)
- while ((tp > 0) && (vio->cl.buf[tp - 1] == ' '))
- --tp ;
-
- while ((tp > 0) && (vio->cl.buf[tp - 1] != ' '))
- --tp ;
-
- return vio->cl.cp - tp ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Backward word, but not trailing spaces.
- *
- * Move back until next (backwards) character is space or start of line.
- *
- * Returns number of characters stepped over.
- */
-static int
-uty_cl_word_backwards_pure (vty_io vio)
-{
- return uty_cl_backwards(vio, uty_cl_word_backwards_delta(vio, 0)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Backward word -- move to start of previous word.
- *
- * Moves past any spaces, then move back until next (backwards) character is
- * space or start of line.
- *
- * Returns number of characters stepped over.
- */
-static int
-uty_cl_word_backwards (vty_io vio)
-{
- return uty_cl_backwards(vio, uty_cl_word_backwards_delta(vio, 1)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Delete to end of word -- forwards.
- *
- * Deletes any leading spaces, then deletes upto next space or end of line.
- *
- * Returns number of characters deleted.
- */
-static int
-uty_cl_del_word_forwards(vty_io vio)
-{
- return uty_cl_del_forwards(vio, uty_cl_word_forwards_delta(vio)) ;
-}
-
-/*------------------------------------------------------------------------------
- * Delete to start of word -- backwards.
- *
- * Deletes any trailing spaces, then deletes upto next space or start of line.
- *
- * Returns number of characters deleted.
- */
-static int
-uty_cl_del_word_backwards(vty_io vio)
-{
- return uty_cl_del_backwards(vio, uty_cl_word_backwards_delta(vio, 1)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Kill rest of line from current point.
- *
- * Returns number of characters deleted.
- */
-static int
-uty_cl_del_to_eol (vty_io vio)
-{
- return uty_cl_del_forwards(vio, vio->cl.ep - vio->cl.cp) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Kill line from the beginning.
- *
- * Returns number of characters deleted.
- */
-static int
-uty_cl_clear_line(vty_io vio)
-{
- uty_cl_bol(vio) ;
- return uty_cl_del_to_eol(vio) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Transpose chars before or at the point.
- *
- * Return number of characters affected.
- */
-static int
-uty_cl_transpose_chars(vty_io vio)
-{
- char chars[2] ;
-
- ASSERTLOCKED
-
- /* Give up if < 2 characters or at start of line. */
- if ((vio->cl.ep < 2) || (vio->cl.cp < 1))
- return 0 ;
-
- /* Move back to first of characters to exchange */
- if (vio->cl.cp == vio->cl.ep)
- uty_cl_backwards(vio, 2) ;
- else
- uty_cl_backwards(vio, 1) ;
-
- /* Pick up in the new order */
- chars[0] = vio->cl.buf[vio->cl.cp + 1] ;
- chars[1] = vio->cl.buf[vio->cl.cp + 0] ;
-
- /* And overwrite */
- return uty_cl_overwrite(vio, chars, 2) ;
-} ;
-
-/*==============================================================================
- *
- */
-
-/*------------------------------------------------------------------------------
- * If this is a VTY of type VTY_TERM, output the prompt.
- *
- *
- *
- * TODO: fix prompting....
- */
-static void
-uty_cl_prompt (vty_io vio)
-{
- struct utsname names;
- const char* hostname;
- const char* prompt ;
-
- ASSERTLOCKED
-
- if (vio->type == VTY_TERM)
- {
- hostname = host.name;
- if (!hostname)
- {
- uname (&names);
- hostname = names.nodename;
- }
-
- prompt = cmd_prompt(vty->node) ;
- if (prompt == NULL)
- {
- zlog_err("vty %s has node %d", vio->cbuf, vty->node) ;
- prompt = "%s ???: " ;
- } ;
-
- uty_cout (vio, prompt, hostname);
- }
-}
-
-/*------------------------------------------------------------------------------
- * Redraw entire command line, leaving current position at the end of the line
- *
- * Assumes is positioned at start of the command line (just after the prompt),
- * and there is nothing that needs to be wiped out, first.
- *
- * Returns number of characters in line.
- */
-static int
-uty_cl_redraw_line (vty_io vio)
-{
- ASSERTLOCKED
-
- if (vio->cl.ep != 0)
- uty_cecho(vio, (char*)vio->cl.buf, vio->cl.ep) ;
-
- return (vio->cl.cp = vio->cl.ep) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Redraw prompt and entire command line, leaving current position at the end
- * of the line.
- *
- * Assumes is positioned at start of line, and there is nothing that needs to
- * be wiped out, first.
- *
- * Returns number of characters in line.
- */
-static int
-uty_cl_redraw(vty_io vio)
-{
- uty_cl_prompt(vio) ;
- return uty_cl_redraw_line(vio) ;
-} ;
-
-/*==============================================================================
- * Command line history handling
- */
-
-/*------------------------------------------------------------------------------
- * Add current command line to the history buffer.
- */
-static void
-uty_cl_hist_add (vty_io vio)
-{
- int index;
-
- ASSERTLOCKED
-
- /* Do nothing if current command line is empty */
- if (vio->cl.ep == 0)
- return;
-
- /* Make sure line is terminated */
- uty_cl_terminate(vio) ;
-
- index = vio->hindex - 1 ;
- if (index < 0)
- index = VTY_MAXHIST - 1 ;
-
- /* Ignore the same string as previous one. */
- if ((vio->hist[index] != NULL) &&
- (strcmp(vio->cl.buf, vio->hist[index]) == 0))
- {
- vio->hp = vio->hindex ;
- return;
- } ;
-
- /* Insert history entry. */
- if (vio->hist[vio->hindex])
- XFREE (MTYPE_VTY_HIST, vio->hist[vio->hindex]);
- vio->hist[vio->hindex] = XSTRDUP (MTYPE_VTY_HIST, vio->cl.buf) ;
-
- /* History index rotation. */
- vio->hindex++;
- if (vio->hindex == VTY_MAXHIST)
- vio->hindex = 0;
-
- vio->hp = vio->hindex;
-} ;
-
-/*------------------------------------------------------------------------------
- * Replace command line by current history.
- *
- * This function is called from vty_next_line and vty_previous_line.
- */
-static void
-uty_cl_history_use(vty_io vio, int step)
-{
- int index ;
- unsigned length ;
- char* hist ;
-
- ASSERTLOCKED
-
- /* See if have anything usable */
- index = vio->hp ;
-
- if ((step > 0) && (index == vio->hindex))
- return ; /* cannot step forward past the insertion point */
-
- index += step ;
- if (index < 0)
- index = VTY_MAXHIST - 1 ;
- else if (index >= VTY_MAXHIST) ;
- index = 0 ;
-
- if ((step < 0) && (index == vio->hindex))
- return ; /* cannot step back to the insertion point */
-
- hist = vio->hist[index] ;
- if (hist == NULL)
- return ; /* cannot step to unused entry */
-
- vio->hp = index;
-
- /* Move back to the start of the current line */
- uty_cl_bol(vio) ;
-
- /* Get previous line from history buffer and echo that */
- length = strlen(hist) ;
- uty_cl_ensure(vio, length) ;
-
- if (length != 0)
- {
- memcpy(vio->cl.buf, hist, length) ;
- uty_cecho(vio, hist, length) ;
- } ;
-
- /* Move "cursor" to end of the new line */
- vio->cl.cp = length;
-
- /* Set new end of of line -- clearing stuff if old line was longer */
- if (length < vio->cl.ep)
- uty_cl_del_to_eol(vio) ;
- else
- vio->cl.ep = length ;
-
- return ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Use next history line, if any.
- */
-static void
-uty_cl_next_line(vty_io vio)
-{
- uty_cl_history_use(vio, +1) ;
-}
-
-/*------------------------------------------------------------------------------
- * Use previous history line, if any.
- */
-static void
-uty_cl_previous_line (vty_io vio)
-{
- uty_cl_history_use(vio, -1) ;
-}
-
-/*==============================================================================
- * Command Description
- *
- */
-
-/* NB: this is a console level operation */
-static void
-uty_cl_describe_fold (vty_io vio, int cmd_width,
- unsigned int desc_width, struct desc *desc)
-{
- char *buf;
- const char *cmd, *p;
- int pos;
-
- ASSERTLOCKED
-
- cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
-
- if (desc_width <= 0)
- {
- uty_cout (vio, " %-*s %s%s", cmd_width, cmd, desc->str, TERM_NEWLINE);
- return;
- }
-
- buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
-
- for (p = desc->str; strlen (p) > desc_width; p += pos + 1)
- {
- for (pos = desc_width; pos > 0; pos--)
- if (*(p + pos) == ' ')
- break;
-
- if (pos == 0)
- break;
-
- strncpy (buf, p, pos);
- buf[pos] = '\0';
- uty_cout (vio, " %-*s %s%s", cmd_width, cmd, buf, TERM_NEWLINE);
-
- cmd = "";
- }
-
- uty_cout (vio, " %-*s %s%s", cmd_width, cmd, p, TERM_NEWLINE);
-
- XFREE (MTYPE_TMP, buf);
-}
-
-/* Describe matched command function. */
-/* NB: this is a console level command */
-static void
-uty_cl_describe_command (vty_io vio)
-{
- int ret;
- vector vline;
- vector describe;
- unsigned int i, width, desc_width;
- struct desc *desc, *desc_cr = NULL;
-
- ASSERTLOCKED
-
- /* Construct vector of tokens from current command line */
- uty_cl_terminate(vio) ;
- vline = cmd_make_strvec (vio->cl.buf);
-
- /* In case of '> ?' or line ending in ' '
- *
- * Note that if there is a vector of tokens, then there is at least one
- * token, so can guarantee that vio->cl.ep >= 1 !
- */
- if ((vline == NULL) || (isspace ((int) vio->cbuf[vio->cl.ep - 1])))
- vline = cmd_add_to_strvec(vline, "") ;
-
- describe = cmd_describe_command (vline, vio->vty->node, &ret);
-
- uty_cout (vio, TERM_NEWLINE);
-
- /* Ambiguous error. */
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- uty_cout (vio, "%% Ambiguous command.%s", TERM_NEWLINE);
- goto out;
- break;
- case CMD_ERR_NO_MATCH:
- uty_cout (vio, "%% There is no matched command.%s", TERM_NEWLINE);
- goto out;
- break;
- }
-
- /* Get width of command string. */
- width = 0;
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- unsigned int len;
-
- if (desc->cmd[0] == '\0')
- continue;
-
- len = strlen (desc->cmd);
- if (desc->cmd[0] == '.')
- len--;
-
- if (width < len)
- width = len;
- }
-
- /* Get width of description string. */
- desc_width = vio->width - (width + 6);
-
- /* Print out description. */
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- if (desc->cmd[0] == '\0')
- continue;
-
- if (strcmp (desc->cmd, command_cr) == 0)
- {
- desc_cr = desc;
- continue;
- }
-
- if (!desc->str)
- uty_cout (vio, " %-s%s",
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- TERM_NEWLINE);
- else if (desc_width >= strlen (desc->str))
- uty_cout (vio, " %-*s %s%s", width,
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str, TERM_NEWLINE);
- else
- uty_cl_describe_fold (vio, width, desc_width, desc);
-
-#if 0
- uty_cout (vio, " %-*s %s%s", width
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str ? desc->str : "", TERM_NEWLINE);
-#endif /* 0 */
- }
-
- if ((desc = desc_cr))
- {
- if (!desc->str)
- uty_cout (vio, " %-s%s",
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- TERM_NEWLINE);
- else if (desc_width >= strlen (desc->str))
- uty_cout (vio, " %-*s %s%s", width,
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str, TERM_NEWLINE);
- else
- uty_cl_describe_fold (vio, width, desc_width, desc);
- }
-
-out:
- cmd_free_strvec (vline);
- if (describe)
- vector_free (describe);
-
- uty_cl_redraw(vio);
-}
-
-/*==============================================================================
- * Command completion
- *
- *
- */
-static void
-uty_cl_complete_command (vty_io vio)
-{
- enum node_type node ;
- unsigned i ;
- int ret ;
- vector matched ;
- vector vline ;
-
- ASSERTLOCKED
-
- node = vio->vty->node ;
-
- if (node == AUTH_NODE || node == AUTH_ENABLE_NODE)
- return;
-
- /* Construct vector of tokens from current command line */
- uty_cl_terminate(vio) ;
- vline = cmd_make_strvec (vio->cl.buf);
-
- /* In case of '> ?' or line ending in ' '
- *
- * Note that if there is a vector of tokens, then there is at least one
- * token, so can guarantee that vio->cl.ep >= 1 !
- */
- if ((vline == NULL) || (isspace ((int) vio->cl.buf[vio->cl.ep - 1])))
- vline = cmd_add_to_strvec(vline, "") ;
-
- /* Try and match the tokenised command line */
- matched = cmd_complete_command (vline, node, &ret);
-
- cmd_free_strvec (vline);
-
- uty_cout (vio, TERM_NEWLINE);
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- uty_cout (vio, "%% Ambiguous command.%s", TERM_NEWLINE);
- uty_cl_redraw(vio);
- break;
- case CMD_ERR_NO_MATCH:
- uty_cout (vio, "%% There is no matched command.%s", TERM_NEWLINE);
- uty_cl_redraw(vio);
- break;
- case CMD_COMPLETE_FULL_MATCH:
- uty_cl_redraw(vio);
- uty_cl_word_backwards_pure (vio);
- uty_cl_word_overwrite (vio, vector_get_item(matched, 0));
- uty_cl_insert(vio, " ", 1);
- break;
- case CMD_COMPLETE_MATCH:
- uty_cl_redraw(vio);
- uty_cl_word_backwards_pure (vio);
- uty_cl_word_overwrite (vio, vector_get_item(matched, 0));
- break;
- case CMD_COMPLETE_LIST_MATCH:
- for (i = 0; i < vector_end(matched); i++)
- {
- if (i != 0 && ((i % 6) == 0))
- uty_cout (vio, "%s", TERM_NEWLINE);
- uty_cout (vio, "%-10s ", (char*)vector_get_item(matched, i));
- }
- uty_cout (vio, "%s", TERM_NEWLINE);
-
- uty_cl_redraw(vio);
- break;
- case CMD_ERR_NOTHING_TODO:
- uty_cl_redraw(vio);
- break;
- default:
- break;
- }
-
- cmd_free_strvec(matched);
-} ;
-
-/*==============================================================================
- *
- */
-
-/* Authentication of vty */
-static void
-vty_auth (struct vty *vty, char *buf)
-{
- char *passwd = NULL;
- enum node_type next_node = 0;
- int fail;
- char *crypt (const char *, const char *);
-
- ASSERTLOCKED
-
- 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;
- }
-
- if (passwd)
- {
- if (host.encrypt)
- fail = strcmp (crypt(buf, passwd), passwd);
- else
- fail = strcmp (buf, passwd);
- }
- else
- fail = 1;
-
- if (! fail)
- {
- vty->vio->fail = 0;
- vty->node = next_node; /* Success ! */
- }
- else
- {
- vty->vio->fail++;
- if (vty->vio->fail >= 3)
- {
- if (vty->node == AUTH_NODE)
- {
- uty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
- vty->vio->status = VTY_CLOSE;
- }
- else
- {
- /* AUTH_ENABLE_NODE */
- vty->vio->fail = 0;
- uty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
- vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
- }
- }
- }
-}
-
-
-/* ^C stop current input and do not add command line to the history. */
-static void
-vty_stop_input (struct vty *vty)
-{
- ASSERTLOCKED
- vty->vio->cp = vty->vio->length = 0;
- vty_clear_buf (vty);
- uty_cout (vty, "%s", VTY_NEWLINE);
-
- 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 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);
- vty->node = ENABLE_NODE;
- break;
- default:
- /* Unknown node, we have to ignore it. */
- break;
- }
- vty_prompt (vty);
-
- /* Set history pointer to the latest one. */
- vty->vio->hp = vty->vio->hindex;
-}
-
-
-
-/* When '^D' is typed at the beginning of the line we move to the down
- level. */
-static void
-vty_down_level (struct vty *vty)
-{
- ASSERTLOCKED
- uty_out (vty, "%s", VTY_NEWLINE);
- (*config_exit_cmd.func)(NULL, vty, 0, NULL);
- vty_prompt (vty); /* TODO: should command action issue prompt ? */
- vty->vio->cp = 0;
-}
-
-/* When '^Z' is received from vty, move down to the enable mode. */
-static void
-vty_end_config (struct vty *vty)
-{
- ASSERTLOCKED
- uty_out (vty, "%s", VTY_NEWLINE);
-
- 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);
- vty->node = ENABLE_NODE;
- break;
- default:
- /* Unknown node, we have to ignore it. */
- break;
- }
-
- vty_prompt (vty); /* TODO: command action and prompt */
- vty->vio->cp = 0;
-}
-
-
-/*------------------------------------------------------------------------------
- *
- */
-/* Command execution over the vty interface. */
-static int
-vty_command (struct vty *vty, char *buf)
-{
- int ret;
- vector vline;
- const char *protocolname;
-
- ASSERTLOCKED
-
- /* Split readline string up into the vector */
- vline = cmd_make_strvec (buf);
-
- if (vline == NULL)
- return CMD_SUCCESS;
-
-#ifdef CONSUMED_TIME_CHECK
- {
- RUSAGE_T before;
- RUSAGE_T after;
- unsigned long realtime, cputime;
-
- GETRUSAGE(&before);
-#endif /* CONSUMED_TIME_CHECK */
-
- UNLOCK
- ret = cmd_execute_command (vline, vty, NULL, routing_nexus, cli_nexus, 0);
- LOCK
-
- /* Get the name of the protocol if any */
- protocolname = uzlog_get_proto_name(NULL);
-
-#ifdef CONSUMED_TIME_CHECK
- GETRUSAGE(&after);
- if ((realtime = thread_consumed_time(&after, &before, &cputime)) >
- CONSUMED_TIME_CHECK)
- /* Warn about CPU hog that must be fixed. */
- uzlog(NULL, LOG_WARNING, "SLOW COMMAND: command took %lums (cpu time %lums): %s",
- realtime/1000, cputime/1000, buf);
- }
-#endif /* CONSUMED_TIME_CHECK */
-
- if (ret != CMD_SUCCESS)
- switch (ret)
- {
- case CMD_WARNING:
- if (vty->vio->type == VTY_FILE)
- uty_out (vty, "Warning...%s", VTY_NEWLINE);
- break;
- case CMD_ERR_AMBIGUOUS:
- uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
- break;
- case CMD_ERR_NO_MATCH:
- uty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
- break;
- case CMD_ERR_INCOMPLETE:
- uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
- break;
- }
- cmd_free_strvec (vline);
-
- return ret;
-}
-
-/*------------------------------------------------------------------------------
- *
- */
-/* queued command has completed */
-extern void
-vty_queued_result(struct vty *vty, int result, int action)
-{
- LOCK
-
- vty_prompt(vty);
-
- /* Wake up */
- if (cli_nexus)
- {
- vty_event (VTY_WRITE, vty->vio->fd, vty);
- if (qpthreads_enabled)
- qpt_thread_signal(cli_nexus->thread_id, SIGMQUEUE);
- }
-
- UNLOCK
-}
-
-/*------------------------------------------------------------------------------
- * Execute current command line.
- *
- * For qpthreads: this is called *only* if there is no command outstanding. So
- * can execute the command, which may be dispatched to another thread, and
- * there will then be a command outstanding.
- */
-static int
-uty_execute (struct vty *vty)
-{
- int ret;
-
- ret = CMD_SUCCESS;
-
- switch (vty->node)
- {
- case AUTH_NODE:
- case AUTH_ENABLE_NODE:
- vty_auth (vty, vty->vio->cbuf);
- break;
- default:
- ret = vty_command (vty, vty->vio->cbuf);
- if (vty->vio->type == VTY_TERM)
- vty_hist_add (vty);
- break;
- }
-
- if (ret == CMD_QUEUED)
- vty->vio->interlock = vty_cQueued ;
-
- /* Clear command line buffer. */
- vty->vio->cp = vty->vio->length = 0;
- vty_clear_buf (vty);
-
- if (vty->vio->status != VTY_CLOSE && ret != CMD_QUEUED)
- vty_prompt (vty);
-
- return ret;
-}
-
-/* Quit print out to the buffer. */
-static void
-vty_buffer_reset (struct vty *vty)
-{
- ASSERTLOCKED
- buffer_reset (vty->vio->obuf);
- vty_redraw (vty);
-}
-
-/*============================================================================*/
-
-/*------------------------------------------------------------------------------
- * Callback -- qpthreads: Read data via vty socket.
- *
- */
-static void
-vty_read_r (qps_file qf, void* file_info)
-{
- int vty_sock = qf->fd;
- struct vty *vty = (struct vty *)file_info;
-
- LOCK
-
- assert(vty->vio->fd == vty_sock) ;
-
- /* is this necessary? */
- qps_disable_modes(qf, qps_read_mbit);
- uty_read(vty);
-
- UNLOCK
-}
-
-/*------------------------------------------------------------------------------
- * Callback -- threads: Read data via vty socket.
- *
- */
-static int
-vty_read (struct thread *thread)
-{
- int vty_sock = THREAD_FD (thread);
- struct vty *vty = THREAD_ARG (thread);
- int result ;
-
- LOCK
-
- assert(vty->vio->fd == vty_sock) ;
-
- vty->vio->t_read = NULL;
- result = uty_read(vty);
-
- UNLOCK
- return result;
-}
-
-/*------------------------------------------------------------------------------
- * Callback: Read from the vty and execute command line stuff.
- */
-
-#define CONTROL(X) ((X) - '@')
-
-enum vty_escape_state
-{
- VTY_ESCAPE_0 = 0,
- VTY_ESCAPE_1 = 1,
- VTY_ESCAPE_2 = 2
-} ;
-
-static int
-uty_read (struct vty *vty)
-{
- vty_io vio ;
- int pending ;
-//int i ;
- int nbytes ;
- unsigned char* ptr ;
- unsigned char* end ;
-
- vio = vty->vio ;
-
- /* Deal with interlock if present -- for qpthreads only */
- if (vio->interlock & vty_cCompleted)
- {
- /* Is either at end of current input line, or at start of fresh line */
-// vty_wipe_input(vty) ;
- vty_prompt(vty) ;
-// vty_redraw_input(vty) ;
-
- vty->vio->interlock = 0 ;
- } ;
-
- /* Read raw data from socket */
- ptr = vio->ibuf ;
- end = ptr + vio->ibuf_has ;
-
- nbytes = vio->ibuf_size - vio->ibuf_has ;
- if (nbytes > 0)
- nbytes = read(vio->fd, end, nbytes) ;
-
- if (nbytes < 0)
- {
- if (!ERRNO_IO_RETRY(errno))
- {
- vty->vio->monitor = 0; /* disable monitoring to avoid infinite recursion */
- uzlog(NULL, LOG_WARNING, "%s: read error on vty client fd %d, closing: %s",
- __func__, vty->vio->fd, safe_strerror(errno));
- }
- }
- else
- {
- end += nbytes ;
- if (ptr == end) /* buffer empty after read ! */
- {
- /* EOF ! */
-
- buffer_reset(vio->obuf);
- vio->status = VTY_CLOSE;
- } ;
- } ;
-
-
-#if 0
-
- /* Deal with VTY_MORE */
- if (vio->status == VTY_MORE)
- {
- switch (u)
- {
- case CONTROL('C'):
- case 'q':
- case 'Q':
- vty_buffer_reset (vty);
- break;
-
- default:
- break;
- }
- continue;
- }
-#endif
-
-
- if (pending)
- {
- qps_disable_modes(vio->qf, qps_read_mbit);
- vio->interlock |= vty_cPending ;
- } ;
-
-
-
-
-
-
- if (pending)
- {
- qps_disable_modes(vio->qf, qps_read_mbit);
- vio->interlock |= vty_cPending ;
- } ;
-
- /* Check status. */
- if (vio->status == VTY_CLOSE)
- uty_close (vty);
- else
- {
- vty_event (VTY_WRITE, vio->fd, vty);
- vty_event (VTY_READ, vio->fd, vty);
- }
-
- return 0;
-}
-
-/* Callback: qpthreads. Flush buffer to the vty. */
-static void
-vty_flush_r (qps_file qf, void* file_info)
-{
- int vty_sock = qf->fd;
- struct vty *vty = (struct vty *)file_info;
-
- LOCK
-
- qps_disable_modes(qf, qps_write_mbit);
-
- /* Temporary disable read thread. */
- if ((vty->vio->lines == 0))
- {
- qps_disable_modes(qf, qps_read_mbit);
- }
-
- uty_flush(vty, vty_sock);
-
- UNLOCK
-}
-
-/* Callback: threads. Flush buffer to the vty. */
-static int
-vty_flush (struct thread *thread)
-{
- int vty_sock = THREAD_FD (thread);
- struct vty *vty = THREAD_ARG (thread);
- int result;
-
- LOCK
- vty->vio->t_write = NULL;
-
- /* Temporary disable read thread. */
- if ((vty->vio->lines == 0) && vty->vio->t_read)
- {
- thread_cancel (vty->vio->t_read);
- vty->vio->t_read = NULL;
- }
- result = uty_flush(vty, vty_sock);
-
- UNLOCK
- return result;
-}
-
-static int
-uty_flush (struct vty *vty, int vty_sock)
-{
- int erase;
- buffer_status_t flushrc;
-
- /* Function execution continue. */
- erase = ((vty->vio->status == VTY_MORE || vty->vio->status == VTY_MORELINE));
-
- /* N.B. if width is 0, that means we don't know the window size. */
- if ((vty->vio->lines == 0) || (vty->vio->width == 0))
- flushrc = buffer_flush_available(vty->vio->obuf, vty->vio->fd);
- else if (vty->vio->status == VTY_MORELINE)
- flushrc = buffer_flush_window(vty->vio->obuf, vty->vio->fd, vty->vio->width,
- 1, erase, 0);
- else
- flushrc = buffer_flush_window(vty->vio->obuf, vty->vio->fd, vty->vio->width,
- vty->vio->lines >= 0 ? vty->vio->lines :
- vty->vio->height,
- erase, 0);
- switch (flushrc)
- {
- case BUFFER_ERROR:
- vty->vio->monitor = 0; /* disable monitoring to avoid infinite recursion */
- uzlog(NULL, LOG_WARNING, "buffer_flush failed on vty client fd %d, closing",
- vty->vio->fd);
- buffer_reset(vty->vio->obuf);
- uty_close(vty);
- break;
- case BUFFER_EMPTY:
- if (vty->vio->status == VTY_CLOSE)
- uty_close (vty);
- else
- {
- vty->vio->status = VTY_NORMAL;
- if (vty->vio->lines == 0)
- vty_event (VTY_READ, vty_sock, vty);
- }
- break;
- case BUFFER_PENDING:
- /* There is more data waiting to be written. */
- vty->vio->status = VTY_MORE;
- if (vty->vio->lines == 0)
- vty_event (VTY_WRITE, vty_sock, vty);
- break;
- }
-
- return 0;
-}
-
-/* Create new vty structure. */
-static struct vty *
-vty_create (int vty_sock, union sockunion *su)
-{
- struct vty *vty;
-
- ASSERTLOCKED
-
- /* Allocate new vty structure and set up default values. */
- vty = vty_new (vty_sock, VTY_TERM);
-
-
-
- vty->vio->address = sockunion_su2str (su);
- 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;
- vty->vio->fail = 0;
- vty->vio->cp = 0;
- vty_clear_buf (vty);
- vty->vio->length = 0;
- memset (vty->vio->hist, 0, sizeof (vty->vio->hist));
- vty->vio->hp = 0;
- vty->vio->hindex = 0;
-//vector_set_index (vtyvec, vty_sock, vty);
- vty->vio->status = VTY_NORMAL;
- vty->vio->v_timeout = vty_timeout_val;
- if (host.lines >= 0)
- vty->vio->lines = host.lines;
- else
- vty->vio->lines = -1;
- vty->vio->iac = 0;
- vty->vio->iac_sb_in_progress = 0;
- vty->vio->sb_len = 0;
-
- if (! no_password_check)
- {
- /* Vty is not available if password isn't set. */
- if (host.password == NULL && host.password_encrypt == NULL)
- {
- uty_cout (vty, "Vty password is not set.%s", VTY_NEWLINE);
- vty->vio->status = VTY_CLOSE;
- uty_close (vty);
- return NULL;
- }
- }
-
- /* Say hello to the world. */
- vty_hello (vty);
- if (! no_password_check)
- uty_cout (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
-
- /* Setting up terminal. */
- vty_will_echo (vty);
- vty_will_suppress_go_ahead (vty);
-
- vty_dont_linemode (vty);
- vty_do_window_size (vty);
- /* vty_dont_lflow_ahead (vty); */
-
- vty_prompt (vty);
-
- /* Add read/write thread. */
- vty_event (VTY_WRITE, vty_sock, vty);
- vty_event (VTY_READ, vty_sock, vty);
-
- return vty;
-}
-
-/* Callback: qpthreads. Accept connection from the network. */
-static void
-vty_accept_r (qps_file qf, void* file_info)
-{
- LOCK
-
- int accept_sock = qf->fd;
- uty_accept(accept_sock);
-
- UNLOCK
-}
-
-/* Callback: threads. Accept connection from the network. */
-static int
-vty_accept (struct thread *thread)
-{
- int result;
-
- LOCK
-
- int accept_sock = THREAD_FD (thread);
- result = uty_accept(accept_sock);
-
- UNLOCK
- return result;
-}
-
-static int
-uty_accept (int accept_sock)
-{
- int vty_sock;
- struct vty *vty;
- union sockunion su;
- int ret;
- unsigned int on;
- struct prefix *p = NULL;
- struct access_list *acl = NULL;
- char *bufp;
-
- ASSERTLOCKED
-
- /* We continue hearing vty socket. */
- vty_event (VTY_SERV, accept_sock, NULL);
-
- memset (&su, 0, sizeof (union sockunion));
-
- /* We can handle IPv4 or IPv6 socket. */
- vty_sock = sockunion_accept (accept_sock, &su);
- if (vty_sock < 0)
- {
- uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s", safe_strerror (errno));
- return -1;
- }
- set_nonblocking(vty_sock);
-
- p = sockunion2hostprefix (&su);
-
- /* VTY's accesslist apply. */
- if (p->family == AF_INET && vty_accesslist_name)
- {
- if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- {
- char *buf;
- uzlog (NULL, LOG_INFO, "Vty connection refused from %s",
- (buf = sockunion_su2str (&su)));
- free (buf);
- close (vty_sock);
-
- /* continue accepting connections */
- vty_event (VTY_SERV, accept_sock, NULL);
-
- prefix_free (p);
- return 0;
- }
- }
-
-#ifdef HAVE_IPV6
- /* VTY's ipv6 accesslist apply. */
- if (p->family == AF_INET6 && vty_ipv6_accesslist_name)
- {
- if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
- (access_list_apply (acl, p) == FILTER_DENY))
- {
- char *buf;
- uzlog (NULL, LOG_INFO, "Vty connection refused from %s",
- (buf = sockunion_su2str (&su)));
- free (buf);
- close (vty_sock);
-
- /* continue accepting connections */
- vty_event (VTY_SERV, accept_sock, NULL);
-
- prefix_free (p);
- return 0;
- }
- }
-#endif /* HAVE_IPV6 */
-
- prefix_free (p);
-
- on = 1;
- ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
- (char *) &on, sizeof (on));
- if (ret < 0)
- uzlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
- safe_strerror (errno));
-
- uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)",
- (bufp = sockunion_su2str (&su)), vty_sock);
- if (bufp)
- XFREE (MTYPE_TMP, bufp);
-
- vty = vty_create (vty_sock, &su);
-
- return 0;
-}
-
-#if defined(HAVE_IPV6) && !defined(NRL)
-static void
-vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
-{
- int ret;
- struct addrinfo req;
- struct addrinfo *ainfo;
- struct addrinfo *ainfo_save;
- int sock;
- char port_str[BUFSIZ];
-
- ASSERTLOCKED
-
- memset (&req, 0, sizeof (struct addrinfo));
- req.ai_flags = AI_PASSIVE;
- req.ai_family = AF_UNSPEC;
- req.ai_socktype = SOCK_STREAM;
- sprintf (port_str, "%d", port);
- port_str[sizeof (port_str) - 1] = '\0';
-
- ret = getaddrinfo (hostname, port_str, &req, &ainfo);
-
- if (ret != 0)
- {
- fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
- exit (1);
- }
-
- ainfo_save = ainfo;
-
- do
- {
- if (ainfo->ai_family != AF_INET
-#ifdef HAVE_IPV6
- && ainfo->ai_family != AF_INET6
-#endif /* HAVE_IPV6 */
- )
- continue;
-
- sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol);
- if (sock < 0)
- continue;
-
- sockopt_reuseaddr (sock);
- sockopt_reuseport (sock);
-
- /* set non-blocking */
- ret = set_nonblocking(sock);
- if (ret < 0)
- {
- close (sock); /* Avoid sd leak. */
- continue;
- }
-
- ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen);
- if (ret < 0)
- {
- close (sock); /* Avoid sd leak. */
- continue;
- }
-
- ret = listen (sock, 3);
- if (ret < 0)
- {
- close (sock); /* Avoid sd leak. */
- continue;
- }
-
- vty_event (VTY_SERV, sock, NULL);
- }
- while ((ainfo = ainfo->ai_next) != NULL);
-
- freeaddrinfo (ainfo_save);
-}
-#endif /* HAVE_IPV6 && ! NRL */
-
-#if defined(HAVE_IPV6) && defined(NRL) || !defined(HAVE_IPV6)
-/* Make vty server socket. */
-static void
-vty_serv_sock_family (const char* addr, unsigned short port, int family)
-{
- int ret;
- union sockunion su;
- int accept_sock;
- void* naddr=NULL;
-
- ASSERTLOCKED
-
- memset (&su, 0, sizeof (union sockunion));
- su.sa.sa_family = family;
- if(addr)
- switch(family)
- {
- case AF_INET:
- naddr=&su.sin.sin_addr;
-#ifdef HAVE_IPV6
- case AF_INET6:
- naddr=&su.sin6.sin6_addr;
-#endif
- }
-
- if(naddr)
- switch(inet_pton(family,addr,naddr))
- {
- case -1:
- uzlog(NULL, LOG_ERR, "bad address %s",addr);
- naddr=NULL;
- break;
- case 0:
- uzlog(NULL, LOG_ERR, "error translating address %s: %s",addr,safe_strerror(errno));
- naddr=NULL;
- }
-
- /* Make new socket. */
- accept_sock = sockunion_stream_socket (&su);
- if (accept_sock < 0)
- return;
-
- /* This is server, so reuse address. */
- sockopt_reuseaddr (accept_sock);
- sockopt_reuseport (accept_sock);
-
- /* set non-blocking */
- ret = set_nonblocking(accept_sock);
- if (ret < 0)
- {
- close (accept_sock); /* Avoid sd leak. */
- return;
- }
-
- /* Bind socket to universal address and given port. */
- ret = sockunion_bind (accept_sock, &su, port, naddr);
- if (ret < 0)
- {
- uzlog(NULL, LOG_WARNING, "can't bind socket");
- close (accept_sock); /* Avoid sd leak. */
- return;
- }
-
- /* Listen socket under queue 3. */
- ret = listen (accept_sock, 3);
- if (ret < 0)
- {
- uzlog (NULL, LOG_WARNING, "can't listen socket");
- close (accept_sock); /* Avoid sd leak. */
- return;
- }
-
- /* Add vty server event. */
- vty_event (VTY_SERV, accept_sock, NULL);
-}
-#endif /* defined(HAVE_IPV6) && defined(NRL) || !defined(HAVE_IPV6) */
-
-#ifdef VTYSH
-/* For sockaddr_un. */
-#include <sys/un.h>
-
-/* VTY shell UNIX domain socket. */
-static void
-vty_serv_un (const char *path)
-{
- int ret;
- int sock, len;
- struct sockaddr_un serv;
- mode_t old_mask;
- struct zprivs_ids_t ids;
-
- ASSERTLOCKED
-
- /* First of all, unlink existing socket */
- unlink (path);
-
- /* Set umask */
- old_mask = umask (0007);
-
- /* Make UNIX domain socket. */
- sock = socket (AF_UNIX, SOCK_STREAM, 0);
- if (sock < 0)
- {
- uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s", safe_strerror(errno));
- return;
- }
-
- /* Make server socket. */
- memset (&serv, 0, sizeof (struct sockaddr_un));
- serv.sun_family = AF_UNIX;
- strncpy (serv.sun_path, path, strlen (path));
-#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
- len = serv.sun_len = SUN_LEN(&serv);
-#else
- len = sizeof (serv.sun_family) + strlen (serv.sun_path);
-#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
-
- ret = bind (sock, (struct sockaddr *) &serv, len);
- if (ret < 0)
- {
- uzlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, safe_strerror(errno));
- close (sock); /* Avoid sd leak. */
- return;
- }
-
- ret = listen (sock, 5);
- if (ret < 0)
- {
- uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock, safe_strerror(errno));
- close (sock); /* Avoid sd leak. */
- return;
- }
-
- umask (old_mask);
-
- zprivs_get_ids(&ids);
-
- if (ids.gid_vty > 0)
- {
- /* set group of socket */
- if ( chown (path, -1, ids.gid_vty) )
- {
- uzlog (NULL, LOG_ERR, "vty_serv_un: could chown socket, %s",
- safe_strerror (errno) );
- }
- }
-
- vty_event (VTYSH_SERV, sock, NULL);
-}
-
-/* #define VTYSH_DEBUG 1 */
-
-/* Callback: qpthreads. Accept connection */
-void int
-vtysh_accept_r (qps_file qf, void* file_info)
-{
- int accept_sock = qf->fd;
- LOCK
- utysh_accept (accept_sock);
- UNLOCK
-}
-
-/* Callback: threads. Accept connection */
-static int
-vtysh_accept (struct thread *thread)
-{
- int accept_sock = THREAD_FD (thread);
- LOCK
- result = utysh_accept (accept_sock);
- UNLOCK
- return result;
-}
-
-static int
-utysh_accept (int accept_sock)
-{
- int sock;
- int client_len;
- struct sockaddr_un client;
- struct vty *vty;
-
- ASSERTLOCKED
-
- vty_event (VTYSH_SERV, accept_sock, NULL);
-
- memset (&client, 0, sizeof (struct sockaddr_un));
- client_len = sizeof (struct sockaddr_un);
-
- sock = accept (accept_sock, (struct sockaddr *) &client,
- (socklen_t *) &client_len);
-
- if (sock < 0)
- {
- uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s", safe_strerror (errno));
- return -1;
- }
-
- if (set_nonblocking(sock) < 0)
- {
- uzlog (NULL, LOG_WARNING, "vtysh_accept: could not set vty socket %d to non-blocking,"
- " %s, closing", sock, safe_strerror (errno));
- close (sock);
- return -1;
- }
-
-#ifdef VTYSH_DEBUG
- printf ("VTY shell accept\n");
-#endif /* VTYSH_DEBUG */
-
- vty = vty_new ();
- vty->vio->fd = sock;
- vty->vio->type = VTY_SHELL_SERV;
- vty->node = VIEW_NODE;
-
- vty_event (VTYSH_READ, sock, vty);
-
- return 0;
-}
-
-static int
-vtysh_flush(struct vty *vty)
-{
- ASSERTLOCKED
-
- switch (buffer_flush_available(vty->vio->obuf, vty->vio->fd))
- {
- case BUFFER_PENDING:
- vty_event(VTYSH_WRITE, vty->vio->fd, vty);
- break;
- case BUFFER_ERROR:
- vty->vio->monitor = 0; /* disable monitoring to avoid infinite recursion */
- uzlog(NULL, LOG_WARNING, "%s: write error to fd %d, closing", __func__, vty->vio->fd);
- buffer_reset(vty->vio->obuf);
- uty_close(vty);
- return -1;
- break;
- case BUFFER_EMPTY:
- break;
- }
- return 0;
-}
-
-/* Callback: qpthreads., Read data via vty socket. */
-static void
-vtysh_read_r (qps_file qf, void* file_info)
-{
- int vty_sock = qf->fd;
- struct vty *vty = (struct vty *)file_info;
-
- LOCK
-
- /* is this necessary? */
- qps_disable_modes(qf, qps_read_mbit);
- utysh_read(vty, vty_soc);
-
- UNLOCK
-}
-
-/* Callback: threads. Read data via vty socket. */
-static int
-vtysh_read (struct thread *thread)
-{
- int vty_sock = THREAD_FD (thread);
- struct vty *vty = THREAD_ARG (thread);
- int result;
-
- LOCK
-
- vty->vio->t_read = NULL;
- result = uty_read(vty, vty_soc);
-
- UNLOCK
- return result;
-}
-
-static int
-utysh_read (struct vty *vty, int sock)
-{
- int ret;
- int nbytes;
- unsigned char buf[VTY_READ_BUFSIZ];
- unsigned char *p;
- u_char header[4] = {0, 0, 0, 0};
-
- if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
- {
- if (nbytes < 0)
- {
- if (ERRNO_IO_RETRY(errno))
- {
- vty_event (VTYSH_READ, sock, vty);
- return 0;
- }
- vty->vio->monitor = 0; /* disable monitoring to avoid infinite recursion */
- uzlog(NULL, LOG_WARNING, "%s: read failed on vtysh client fd %d, closing: %s",
- __func__, sock, safe_strerror(errno));
- }
- buffer_reset(vty->vio->obuf);
- uty_close (vty);
-#ifdef VTYSH_DEBUG
- printf ("close vtysh\n");
-#endif /* VTYSH_DEBUG */
- return 0;
- }
-
-#ifdef VTYSH_DEBUG
- printf ("line: %.*s\n", nbytes, buf);
-#endif /* VTYSH_DEBUG */
-
- for (p = buf; p < buf+nbytes; p++)
- {
- vty_ensure(vty, vty->vio->length+1);
- vty->vio->cbuf[vty->vio->length++] = *p;
- if (*p == '\0')
- {
- /* Pass this line to parser. */
- ret = vty_execute (vty);
- /* Note that vty_execute clears the command buffer and resets
- vty->vio->length to 0. */
-
- /* Return result. */
-#ifdef VTYSH_DEBUG
- printf ("result: %d\n", ret);
- printf ("vtysh node: %d\n", vty->node);
-#endif /* VTYSH_DEBUG */
-
- header[3] = ret;
- buffer_put(vty->vio->obuf, header, 4);
-
- if (!vty->vio->t_write && (vtysh_flush(vty) < 0))
- /* Try to flush results; exit if a write error occurs. */
- return 0;
- }
- }
-
- vty_event (VTYSH_READ, sock, vty);
-
- return 0;
-}
-
-/* Callback: qpthraeds. Write */
-static void
-vtysh_write_r (qps_file qf, void* file_info)
-{
- struct vty *vty = (struct vty *)file_info;
-
- LOCK
-
- qps_disable_modes(qf, qps_write_mbit);
- vtysh_flush(vty);
-
- UNLOCK
-}
-
-//* Callback: thraeds. Write */
-static int
-vtysh_write (struct thread *thread)
-{
- struct vty *vty = THREAD_ARG (thread);
-
- LOCK
-
- vty->vio->t_write = NULL;
- vtysh_flush(vty);
-
- UNLOCK
- return 0;
-}
-
-#endif /* VTYSH */
-
-/* Determine address family to bind. */
-void
-vty_serv_sock (const char *addr, unsigned short port, const char *path)
-{
- LOCK
-
- /* If port is set to 0, do not listen on TCP/IP at all! */
- if (port)
- {
-
-#ifdef HAVE_IPV6
-#ifdef NRL
- vty_serv_sock_family (addr, port, AF_INET);
- vty_serv_sock_family (addr, port, AF_INET6);
-#else /* ! NRL */
- vty_serv_sock_addrinfo (addr, port);
-#endif /* NRL*/
-#else /* ! HAVE_IPV6 */
- vty_serv_sock_family (addr,port, AF_INET);
-#endif /* HAVE_IPV6 */
- }
-
-#ifdef VTYSH
- vty_serv_un (path);
-#endif /* VTYSH */
-
- UNLOCK
-}
-
-/* Close vty interface. Warning: call this only from functions that
- will be careful not to access the vty afterwards (since it has
- now been freed). This is safest from top-level functions (called
- directly by the thread dispatcher). */
-void
-vty_close (struct vty *vty)
-{
- LOCK
- uty_close(vty);
- UNLOCK
-}
-
-static void
-uty_close (struct vty *vty)
-{
- int i;
-
- ASSERTLOCKED
-
- uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vty->vio->fd) ;
-
- /* Cancel threads.*/
- if (vty->vio->t_read)
- thread_cancel (vty->vio->t_read);
- if (vty->vio->t_write)
- thread_cancel (vty->vio->t_write);
- if (vty->vio->t_timeout)
- thread_cancel (vty->vio->t_timeout);
- if (vty->vio->qf)
- {
- qps_remove_file(vty->vio->qf);
- qps_file_free(vty->vio->qf);
- vty->vio->qf = NULL;
- }
- if (vty->vio->qtr)
- {
- qtimer_free(vty->vio->qtr);
- vty->vio->qtr = NULL;
- }
-
- /* Flush buffer. */
- buffer_flush_all (vty->vio->obuf, vty->vio->fd);
-
- /* Free input buffer. */
- buffer_free (vty->vio->obuf);
-
- /* Free command history. */
- for (i = 0; i < VTY_MAXHIST; i++)
- if (vty->vio->hist[i])
- XFREE (MTYPE_VTY_HIST, vty->vio->hist[i]);
-
- /* Unset vector. */
-//vector_unset (vtyvec, vty->vio->fd);
-
- /* Close socket. */
- if (vty->vio->fd > 0)
- close (vty->vio->fd);
-
- if (vty->vio->address)
- XFREE (MTYPE_TMP, vty->vio->address);
- if (vty->vio->cbuf)
- XFREE (MTYPE_VTY, vty->vio->cbuf);
-
- /* Check configure. */
- uty_config_unlock (vty);
-
- /* OK free vty. */
-//XFREE (MTYPE_VTY, vty);
-}
-
-/* Callback: qpthreads. When time out occur output message then close connection. */
-static void
-vty_timeout_r (qtimer qtr, void* timer_info, qtime_t when)
-{
- struct vty *vty = (struct vty *)timer_info;
- LOCK
- qtimer_unset(qtr);
- uty_timeout(vty);
- UNLOCK
-}
-
-/* Callback: threads. When time out occur output message then close connection. */
-static int
-vty_timeout (struct thread *thread)
-{
- int result;
- struct vty *vty = THREAD_ARG (thread);
- LOCK
- vty->vio->t_timeout = NULL;
- result = uty_timeout(vty);
- UNLOCK
- return result;
-}
-
-static int
-uty_timeout (struct vty *vty)
-{
- vty->vio->v_timeout = 0;
-
- /* Clear buffer*/
- buffer_reset (vty->vio->obuf);
- uty_cout (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
-
- /* Close connection. */
- vty->vio->status = VTY_CLOSE;
- uty_close (vty);
-
- return 0;
-}
-
-/* Read up configuration file from file_name. */
-static void
-vty_read_file (FILE *confp, void (*after_first_cmd)(void))
-{
- int ret;
- struct vty *vty;
-
- vty = vty_new (0, VTY_TERM); /* stdout */
- vty->node = CONFIG_NODE;
-
- /* Execute configuration file */
- ret = config_from_file (vty, confp, after_first_cmd);
-
- LOCK
-
- if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
- {
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- fprintf (stderr, "Ambiguous command.\n");
- break;
- case CMD_ERR_NO_MATCH:
- fprintf (stderr, "There is no such command.\n");
- break;
- }
- fprintf (stderr, "Error occurred while processing:\n%s\n", vty->vio->cbuf);
- uty_close (vty);
- exit (1);
- }
-
- uty_close (vty);
- UNLOCK
-}
-
-static FILE *
-vty_use_backup_config (char *fullpath)
-{
- char *fullpath_sav, *fullpath_tmp;
- FILE *ret = NULL;
- struct stat buf;
- int tmp, sav;
- int c;
- char buffer[512];
-
- fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1);
- strcpy (fullpath_sav, fullpath);
- strcat (fullpath_sav, CONF_BACKUP_EXT);
- if (stat (fullpath_sav, &buf) == -1)
- {
- free (fullpath_sav);
- return NULL;
- }
-
- fullpath_tmp = malloc (strlen (fullpath) + 8);
- sprintf (fullpath_tmp, "%s.XXXXXX", fullpath);
-
- /* Open file to configuration write. */
- tmp = mkstemp (fullpath_tmp);
- if (tmp < 0)
- {
- free (fullpath_sav);
- free (fullpath_tmp);
- return NULL;
- }
-
- sav = open (fullpath_sav, O_RDONLY);
- if (sav < 0)
- {
- unlink (fullpath_tmp);
- free (fullpath_sav);
- free (fullpath_tmp);
- return NULL;
- }
-
- while((c = read (sav, buffer, 512)) > 0)
- write (tmp, buffer, c);
-
- close (sav);
- close (tmp);
-
- if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0)
- {
- unlink (fullpath_tmp);
- free (fullpath_sav);
- free (fullpath_tmp);
- return NULL;
- }
-
- if (link (fullpath_tmp, fullpath) == 0)
- ret = fopen (fullpath, "r");
-
- unlink (fullpath_tmp);
-
- free (fullpath_sav);
- free (fullpath_tmp);
- return ret;
-}
-
-/* Read up configuration file from file_name. */
-void
-vty_read_config (char *config_file,
- char *config_default_dir)
-{
- vty_read_config_first_cmd_special(config_file, config_default_dir, NULL);
-}
-
-/* Read up configuration file from file_name.
- * callback after first command */
-void
-vty_read_config_first_cmd_special(char *config_file,
- char *config_default_dir, void (*after_first_cmd)(void))
-{
- char cwd[MAXPATHLEN];
- FILE *confp = NULL;
- char *fullpath;
- char *tmp = NULL;
-
- /* If -f flag specified. */
- if (config_file != NULL)
- {
- if (! IS_DIRECTORY_SEP (config_file[0]))
- {
- getcwd (cwd, MAXPATHLEN);
- tmp = XMALLOC (MTYPE_TMP,
- strlen (cwd) + strlen (config_file) + 2);
- sprintf (tmp, "%s/%s", cwd, config_file);
- fullpath = tmp;
- }
- else
- fullpath = config_file;
-
- confp = fopen (fullpath, "r");
-
- if (confp == NULL)
- {
- fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
- __func__, fullpath, safe_strerror (errno));
-
- confp = vty_use_backup_config (fullpath);
- if (confp)
- fprintf (stderr, "WARNING: using backup configuration file!\n");
- else
- {
- fprintf (stderr, "can't open configuration file [%s]\n",
- config_file);
- exit(1);
- }
- }
- }
- else
- {
-#ifdef VTYSH
- int ret;
- struct stat conf_stat;
-
- /* !!!!PLEASE LEAVE!!!!
- * This is NEEDED for use with vtysh -b, or else you can get
- * a real configuration food fight with a lot garbage in the
- * merged configuration file it creates coming from the per
- * daemon configuration files. This also allows the daemons
- * to start if there default configuration file is not
- * present or ignore them, as needed when using vtysh -b to
- * configure the daemons at boot - MAG
- */
-
- /* Stat for vtysh Zebra.conf, if found startup and wait for
- * boot configuration
- */
-
- if ( strstr(config_default_dir, "vtysh") == NULL)
- {
- ret = stat (integrate_default, &conf_stat);
- if (ret >= 0)
- return;
- }
-#endif /* VTYSH */
-
- confp = fopen (config_default_dir, "r");
- if (confp == NULL)
- {
- fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
- __func__, config_default_dir, safe_strerror (errno));
-
- confp = vty_use_backup_config (config_default_dir);
- if (confp)
- {
- fprintf (stderr, "WARNING: using backup configuration file!\n");
- fullpath = config_default_dir;
- }
- else
- {
- fprintf (stderr, "can't open configuration file [%s]\n",
- config_default_dir);
- exit (1);
- }
- }
- else
- fullpath = config_default_dir;
- }
-
- vty_read_file (confp, after_first_cmd);
-
- fclose (confp);
-
-#ifdef QDEBUG
- fprintf(stderr, "Reading config file: %s\n", fullpath);
-#endif
- host_config_set (fullpath);
-#ifdef QDEBUG
- fprintf(stderr, "Finished reading config file\n");
-#endif
-
- if (tmp)
- XFREE (MTYPE_TMP, fullpath);
-}
-
-#ifdef QDEBUG
-/* Tell all terminals that we are shutting down */
-void
-vty_goodbye (void)
-{
- unsigned int i;
- struct vty *vty;
-
- LOCK
-
- if (vtyvec)
- {
- for (i = 0; i < vector_active (vtyvec); i++)
- {
- if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->vio->type == VTY_TERM)
- {
- uty_cout(vty, QUAGGA_PROGNAME " is shutting down%s", VTY_NEWLINE);
-
- /* Wake up */
- if (cli_nexus)
- vty_event (VTY_WRITE, vty->vio->fd, vty);
- }
- }
- if (qpthreads_enabled)
- qpt_thread_signal(cli_nexus->thread_id, SIGMQUEUE);
- }
-
- UNLOCK
-}
-#endif
-
-/* Async-signal-safe version of vty_log for fixed strings. */
-void
-vty_log_fixed (const char *buf, size_t len)
-{
-// unsigned int i;
- struct iovec iov[2];
-
- /* vty may not have been initialised */
-// if (!vtyvec)
-// return;
-
- iov[0].iov_base = miyagi(buf) ;
- iov[0].iov_len = len;
- iov[1].iov_base = miyagi("\r\n") ;
- iov[1].iov_len = 2;
-
-// for (i = 0; i < vector_active (vtyvec); i++)
- {
- struct vty *vty;
-// if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->vio->monitor)
- /* N.B. We don't care about the return code, since process is
- most likely just about to die anyway. */
- writev(vty->vio->fd, iov, 2);
- }
-}
-
-int
-vty_config_lock (struct vty *vty)
-{
- int result;
- LOCK
- if (vty_config == 0)
- {
- vty->vio->config = 1;
- vty_config = 1;
- }
- result = vty->vio->config;
- UNLOCK
- return result;
-}
-
-int
-vty_config_unlock (struct vty *vty)
-{
- int result;
- LOCK
- result = uty_config_unlock(vty);
- UNLOCK
- return result;
-}
-
-static int
-uty_config_unlock (struct vty *vty)
-{
- ASSERTLOCKED
- if (vty_config == 1 && vty->vio->config == 1)
- {
- vty->vio->config = 0;
- vty_config = 0;
- }
- return vty->vio->config;
-}
-
-static void
-vty_event (enum vty_event event, int sock, struct vty *vty)
-{
- if (cli_nexus)
- vty_event_r(event, sock, vty);
- else
- vty_event_t(event, sock, vty);
-}
-
-/* thread event setter */
-static void
-vty_event_t (enum vty_event event, int sock, struct vty *vty)
- {
- struct thread *vty_serv_thread;
-
- ASSERTLOCKED
-
- switch (event)
- {
- case VTY_SERV:
- vty_serv_thread = thread_add_read (master, vty_accept, vty, sock);
-// vector_set_index (Vvty_serv_thread, sock, vty_serv_thread);
- break;
-#ifdef VTYSH
- case VTYSH_SERV:
- thread_add_read (master, vtysh_accept, vty, sock);
- break;
- case VTYSH_READ:
- vty->vio->t_read = thread_add_read (master, vtysh_read, vty, sock);
- break;
- case VTYSH_WRITE:
- vty->vio->t_write = thread_add_write (master, vtysh_write, vty, sock);
- break;
-#endif /* VTYSH */
- case VTY_READ:
- vty->vio->t_read = thread_add_read (master, vty_read, vty, sock);
-
- /* Time out treatment. */
- if (vty->vio->v_timeout)
- {
- if (vty->vio->t_timeout)
- thread_cancel (vty->vio->t_timeout);
- vty->vio->t_timeout =
- thread_add_timer (master, vty_timeout, vty, vty->vio->v_timeout);
- }
- break;
- case VTY_WRITE:
- if (! vty->vio->t_write)
- vty->vio->t_write = thread_add_write (master, vty_flush, vty, sock);
- break;
- case VTY_TIMEOUT_RESET:
- if (vty->vio->t_timeout)
- {
- thread_cancel (vty->vio->t_timeout);
- vty->vio->t_timeout = NULL;
- }
- if (vty->vio->v_timeout)
- {
- vty->vio->t_timeout =
- thread_add_timer (master, vty_timeout, vty, vty->vio->v_timeout);
- }
- break;
- }
-}
-
-/* qpthreads event setter */
-static void
-vty_event_r (enum vty_event event, int sock, struct vty *vty)
- {
-
- qps_file accept_file = NULL;
-
- ASSERTLOCKED
-
- switch (event)
- {
- case VTY_SERV:
- accept_file = NULL ;// vector_get_item(Vvty_serv_thread, sock);
- if (accept_file == NULL)
- {
- accept_file = qps_file_init_new(accept_file, NULL);
- qps_add_file(cli_nexus->selection, accept_file, sock, NULL);
-// vector_set_index(Vvty_serv_thread, sock, accept_file);
- }
- qps_enable_mode(accept_file, qps_read_mnum, vty_accept_r) ;
- break;
-#ifdef VTYSH
- case VTYSH_SERV:
- accept_file = vector_get_item(Vvty_serv_thread, sock);
- if (accept_file == NULL)
- {
- accept_file = qps_file_init_new(accept_file, NULL);
- qps_add_file(master, accept_file, sock, NULL);
- vector_set_index(Vvty_serv_thread, sock, accept_file);
- }
- qps_enable_mode(accept_file, qps_read_mnum, vtysh_accept_r) ;
- break;
- case VTYSH_READ:
- qps_enable_mode(vty->vio->file, qps_read_mnum, vtysh_read_r) ;
- break;
- case VTYSH_WRITE:
- qps_enable_mode(vty->vio->file, qps_write_mnum, vtysh_write_r) ;
- break;
-#endif /* VTYSH */
- case VTY_READ:
- qps_enable_mode(vty->vio->qf, qps_read_mnum, vty_read_r) ;
-
- /* Time out treatment. */
- if (vty->vio->v_timeout)
- {
- qtimer_set(vty->vio->qtr, qt_add_monotonic(QTIME(vty->vio->v_timeout)), NULL) ;
- }
- break;
- case VTY_WRITE:
- qps_enable_mode(vty->vio->qf, qps_write_mnum, vty_flush_r) ;
- break;
- case VTY_TIMEOUT_RESET:
- if (vty->vio->qtr == NULL)
- break;
- if (vty->vio->v_timeout)
- {
- qtimer_set(vty->vio->qtr, qt_add_monotonic(QTIME(vty->vio->v_timeout)), NULL) ;
- }
- else
- {
- qtimer_unset(vty->vio->qtr);
- }
- break;
- }
-}
-
-/*==============================================================================
- * Commands
- *
- */
-DEFUN_CALL (config_who,
- config_who_cmd,
- "who",
- "Display who is on vty\n")
-{
- unsigned int i;
- struct vty *v;
-
- LOCK
-// for (i = 0; i < vector_active (vtyvec); i++)
-// if ((v = vector_slot (vtyvec, i)) != NULL)
- uty_out (vty, "%svty[%d] connected from %s.%s",
- v->vio->config ? "*" : " ",
- i, v->vio->address, VTY_NEWLINE);
- UNLOCK
- return CMD_SUCCESS;
-}
-
-/* Move to vty configuration mode. */
-DEFUN_CALL (line_vty,
- line_vty_cmd,
- "line vty",
- "Configure a terminal line\n"
- "Virtual terminal\n")
-{
- LOCK
- vty->node = VTY_NODE;
- UNLOCK
- return CMD_SUCCESS;
-}
-
-/* Set time out value. */
-static int
-exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
-{
- unsigned long timeout = 0;
-
- LOCK
-
- /* min_str and sec_str are already checked by parser. So it must be
- all digit string. */
- if (min_str)
- {
- timeout = strtol (min_str, NULL, 10);
- timeout *= 60;
- }
- if (sec_str)
- timeout += strtol (sec_str, NULL, 10);
-
- vty_timeout_val = timeout;
- vty->vio->v_timeout = timeout;
- vty_event (VTY_TIMEOUT_RESET, 0, vty);
-
- UNLOCK
- return CMD_SUCCESS;
-}
-
-DEFUN_CALL (exec_timeout_min,
- exec_timeout_min_cmd,
- "exec-timeout <0-35791>",
- "Set timeout value\n"
- "Timeout value in minutes\n")
-{
- return exec_timeout (vty, argv[0], NULL);
-}
-
-DEFUN_CALL (exec_timeout_sec,
- exec_timeout_sec_cmd,
- "exec-timeout <0-35791> <0-2147483>",
- "Set the EXEC timeout\n"
- "Timeout in minutes\n"
- "Timeout in seconds\n")
-{
- return exec_timeout (vty, argv[0], argv[1]);
-}
-
-DEFUN_CALL (no_exec_timeout,
- no_exec_timeout_cmd,
- "no exec-timeout",
- NO_STR
- "Set the EXEC timeout\n")
-{
- return exec_timeout (vty, NULL, NULL);
-}
-
-/* Set vty access class. */
-DEFUN_CALL (vty_access_class,
- vty_access_class_cmd,
- "access-class WORD",
- "Filter connections based on an IP access list\n"
- "IP access list\n")
-{
- LOCK
-
- if (vty_accesslist_name)
- XFREE(MTYPE_VTY, vty_accesslist_name);
-
- vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
-
- UNLOCK
- return CMD_SUCCESS;
-}
-
-/* Clear vty access class. */
-DEFUN_CALL (no_vty_access_class,
- no_vty_access_class_cmd,
- "no access-class [WORD]",
- NO_STR
- "Filter connections based on an IP access list\n"
- "IP access list\n")
-{
- int result = CMD_SUCCESS;
-
- LOCK
- if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
- {
- uty_out (vty, "Access-class is not currently applied to vty%s",
- VTY_NEWLINE);
- result = CMD_WARNING;
- }
- else
- {
- XFREE(MTYPE_VTY, vty_accesslist_name);
- vty_accesslist_name = NULL;
- }
-
- UNLOCK
- return result;
-}
-
-#ifdef HAVE_IPV6
-/* Set vty access class. */
-DEFUN_CALL (vty_ipv6_access_class,
- vty_ipv6_access_class_cmd,
- "ipv6 access-class WORD",
- IPV6_STR
- "Filter connections based on an IP access list\n"
- "IPv6 access list\n")
-{
- LOCK
- if (vty_ipv6_accesslist_name)
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
-
- vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
-
- UNLOCK
- return CMD_SUCCESS;
-}
-
-/* Clear vty access class. */
-DEFUN_CALL (no_vty_ipv6_access_class,
- no_vty_ipv6_access_class_cmd,
- "no ipv6 access-class [WORD]",
- NO_STR
- IPV6_STR
- "Filter connections based on an IP access list\n"
- "IPv6 access list\n")
-{
- int result = CMD_SUCCESS;
-
- LOCK
-
- if (! vty_ipv6_accesslist_name ||
- (argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
- {
- uty_out (vty, "IPv6 access-class is not currently applied to vty%s",
- VTY_NEWLINE);
- result = CMD_WARNING;
- }
- else
- {
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
-
- vty_ipv6_accesslist_name = NULL;
- }
-
- UNLOCK
- return CMD_SUCCESS;
-}
-#endif /* HAVE_IPV6 */
-
-/* vty login. */
-DEFUN_CALL (vty_login,
- vty_login_cmd,
- "login",
- "Enable password checking\n")
-{
- LOCK
- no_password_check = 0;
- UNLOCK
- return CMD_SUCCESS;
-}
-
-DEFUN_CALL (no_vty_login,
- no_vty_login_cmd,
- "no login",
- NO_STR
- "Enable password checking\n")
-{
- LOCK
- no_password_check = 1;
- UNLOCK
- return CMD_SUCCESS;
-}
-
-/* initial mode. */
-DEFUN_CALL (vty_restricted_mode,
- vty_restricted_mode_cmd,
- "anonymous restricted",
- "Restrict view commands available in anonymous, unauthenticated vty\n")
-{
- LOCK
- restricted_mode = 1;
- UNLOCK
- return CMD_SUCCESS;
-}
-
-DEFUN_CALL (vty_no_restricted_mode,
- vty_no_restricted_mode_cmd,
- "no anonymous restricted",
- NO_STR
- "Enable password checking\n")
-{
- LOCK
- restricted_mode = 0;
- UNLOCK
- return CMD_SUCCESS;
-}
-
-DEFUN_CALL (service_advanced_vty,
- service_advanced_vty_cmd,
- "service advanced-vty",
- "Set up miscellaneous service\n"
- "Enable advanced mode vty interface\n")
-{
- LOCK
- host.advanced = 1;
- UNLOCK
- return CMD_SUCCESS;
-}
-
-DEFUN_CALL (no_service_advanced_vty,
- no_service_advanced_vty_cmd,
- "no service advanced-vty",
- NO_STR
- "Set up miscellaneous service\n"
- "Enable advanced mode vty interface\n")
-{
- LOCK
- host.advanced = 0;
- UNLOCK
- return CMD_SUCCESS;
-}
-
-DEFUN_CALL (terminal_monitor,
- terminal_monitor_cmd,
- "terminal monitor",
- "Set terminal line parameters\n"
- "Copy debug output to the current terminal line\n")
-{
- LOCK
- vty->vio->monitor = 1;
- UNLOCK
- return CMD_SUCCESS;
-}
-
-DEFUN_CALL (terminal_no_monitor,
- terminal_no_monitor_cmd,
- "terminal no monitor",
- "Set terminal line parameters\n"
- NO_STR
- "Copy debug output to the current terminal line\n")
-{
- LOCK
- vty->vio->monitor = 0;
- UNLOCK
- return CMD_SUCCESS;
-}
-
-ALIAS_CALL (terminal_no_monitor,
- no_terminal_monitor_cmd,
- "no terminal monitor",
- NO_STR
- "Set terminal line parameters\n"
- "Copy debug output to the current terminal line\n")
-
-DEFUN_CALL (show_history,
- show_history_cmd,
- "show history",
- SHOW_STR
- "Display the session command history\n")
-{
- int index;
-
- LOCK
-
- for (index = vty->vio->hindex + 1; index != vty->vio->hindex;)
- {
- if (index == VTY_MAXHIST)
- {
- index = 0;
- continue;
- }
-
- if (vty->vio->hist[index] != NULL)
- uty_out (vty, " %s%s", vty->vio->hist[index], VTY_NEWLINE);
-
- index++;
- }
-
- UNLOCK
- return CMD_SUCCESS;
-}
-
-/* Display current configuration. */
-static int
-vty_config_write (struct vty *vty)
-{
- vty_out (vty, "line vty%s", VTY_NEWLINE);
-
- if (vty_accesslist_name)
- vty_out (vty, " access-class %s%s",
- vty_accesslist_name, VTY_NEWLINE);
-
- if (vty_ipv6_accesslist_name)
- vty_out (vty, " ipv6 access-class %s%s",
- vty_ipv6_accesslist_name, VTY_NEWLINE);
-
- /* exec-timeout */
- if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
- vty_out (vty, " exec-timeout %ld %ld%s",
- vty_timeout_val / 60,
- vty_timeout_val % 60, VTY_NEWLINE);
-
- /* login */
- if (no_password_check)
- vty_out (vty, " no login%s", VTY_NEWLINE);
-
- if (restricted_mode != restricted_mode_default)
- {
- if (restricted_mode_default)
- vty_out (vty, " no anonymous restricted%s", VTY_NEWLINE);
- else
- vty_out (vty, " anonymous restricted%s", VTY_NEWLINE);
- }
-
- vty_out (vty, "!%s", VTY_NEWLINE);
-
- return CMD_SUCCESS;
-}
-
-struct cmd_node vty_node =
-{
- VTY_NODE,
- "%s(config-line)# ",
- 1,
-};
-
-/*==============================================================================
- *
- */
-
-/*------------------------------------------------------------------------------
- * Reset all VTY status
- *
- */
-void
-vty_reset ()
-{
- unsigned int i;
- struct vty *vty;
- struct thread *vty_serv_thread;
- qps_file qf;
-
- LOCK
-
-// for (i = 0; i < vector_active (vtyvec); i++)
-// if ((vty = vector_slot (vtyvec, i)) != NULL)
- {
- buffer_reset (vty->vio->obuf);
- vty->vio->status = VTY_CLOSE;
- uty_close (vty);
- }
-
- if (cli_nexus)
- {
-// for (i = 0; i < vector_active (Vvty_serv_thread); i++)
-// if ((qf = vector_slot (Vvty_serv_thread, i)) != NULL)
- {
- qps_remove_file(qf);
- qps_file_free(qf);
-// vector_slot (Vvty_serv_thread, i) = NULL;
- close (i);
- }
- }
- else
- {
- assert(master);
-// for (i = 0; i < vector_active (Vvty_serv_thread); i++)
-// if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL)
- {
- thread_cancel (vty_serv_thread);
- // vector_slot (Vvty_serv_thread, i) = NULL;
- close (i);
- }
- }
-
- vty_timeout_val = VTY_TIMEOUT_DEFAULT;
-
- if (vty_accesslist_name)
- {
- XFREE(MTYPE_VTY, vty_accesslist_name);
- vty_accesslist_name = NULL;
- }
-
- if (vty_ipv6_accesslist_name)
- {
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
- vty_ipv6_accesslist_name = NULL;
- }
-
- UNLOCK
-}
-
-/*------------------------------------------------------------------------------
- * Save cwd
- *
- * This is done early in the morning so that any future operations on files
- * (in particular the configuration file) can use the original cwd.
- */
-static void
-vty_save_cwd (void)
-{
- char cwd[MAXPATHLEN];
- char *c;
-
- c = getcwd (cwd, MAXPATHLEN);
-
- if (!c)
- {
- chdir (SYSCONFDIR);
- getcwd (cwd, MAXPATHLEN);
- }
-
- vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
- strcpy (vty_cwd, cwd);
-}
-
-/*------------------------------------------------------------------------------
- *
- */
-char *
-vty_get_cwd ()
-{
- return vty_cwd;
-}
-
-int
-vty_shell (struct vty *vty)
-{
- LOCK
- int result;
- result = (vty->vio->type == VTY_SHELL) ? 1 : 0 ;
- UNLOCK
- return result;
-}
-
-int
-vty_shell_serv (struct vty *vty)
-{
- LOCK
- int result;
- result = ((vty->vio->type == VTY_SHELL_SERV) ? 1 : 0);
- UNLOCK
- return result;
-}
-
-void
-vty_init_vtysh ()
-{
- LOCK
-// vtyvec = vector_init (0);
- UNLOCK
-}
-
-int
-vty_get_node(struct vty *vty)
-{
- int result;
- LOCK
- result = vty->node;
- UNLOCK
- return result;
-}
-
-void
-vty_set_node(struct vty *vty, int node)
-{
- LOCK
- vty->node = node;
- UNLOCK
-}
-
-int
-vty_get_type(struct vty *vty)
-{
- int result;
- LOCK
- result = vty->vio->type;
- UNLOCK
- return result;
-}
-
-int
-vty_get_status(struct vty *vty)
-{
- int result;
- LOCK
- result = vty->vio->status;
- UNLOCK
- return result;
-}
-
-void
-vty_set_status(struct vty *vty, int status)
-{
- LOCK
- vty->vio->status = status;
- UNLOCK
-}
-
-int
-vty_get_lines(struct vty *vty)
-{
- int result;
- LOCK
- result = vty->vio->lines;
- UNLOCK
- return result;
-}
-
-void
-vty_set_lines(struct vty *vty, int lines)
-{
- LOCK
- vty->vio->lines = lines;
- UNLOCK
-}
-
-/*==============================================================================
- * System initialisation and shut down
- */
-
-/*------------------------------------------------------------------------------
- * Initialise vty handling (threads and pthreads)
- *
- * Install vty's own commands like `who' command.
- */
-void
-vty_init (struct thread_master *master_thread)
-{
- LOCK
-
- /* Local pointer to the master thread */
- master = master_thread;
-
- /* For further configuration read, preserve current directory */
- vty_save_cwd ();
-
- /* No vty yet */
- vty_known = NULL ;
- vty_monitors = NULL ;
-
- /* Initilize server thread vector. */
-// Vvty_serv_thread = vector_init (0);
-
- /* Install bgp top node. */
- install_node (&vty_node, vty_config_write);
-
- install_element (RESTRICTED_NODE, &config_who_cmd);
- install_element (RESTRICTED_NODE, &show_history_cmd);
- install_element (VIEW_NODE, &config_who_cmd);
- install_element (VIEW_NODE, &show_history_cmd);
- install_element (ENABLE_NODE, &config_who_cmd);
- install_element (CONFIG_NODE, &line_vty_cmd);
- install_element (CONFIG_NODE, &service_advanced_vty_cmd);
- install_element (CONFIG_NODE, &no_service_advanced_vty_cmd);
- install_element (CONFIG_NODE, &show_history_cmd);
- install_element (ENABLE_NODE, &terminal_monitor_cmd);
- install_element (ENABLE_NODE, &terminal_no_monitor_cmd);
- install_element (ENABLE_NODE, &no_terminal_monitor_cmd);
- install_element (ENABLE_NODE, &show_history_cmd);
-
- install_default (VTY_NODE);
- install_element (VTY_NODE, &exec_timeout_min_cmd);
- install_element (VTY_NODE, &exec_timeout_sec_cmd);
- install_element (VTY_NODE, &no_exec_timeout_cmd);
- install_element (VTY_NODE, &vty_access_class_cmd);
- install_element (VTY_NODE, &no_vty_access_class_cmd);
- install_element (VTY_NODE, &vty_login_cmd);
- install_element (VTY_NODE, &no_vty_login_cmd);
- install_element (VTY_NODE, &vty_restricted_mode_cmd);
- install_element (VTY_NODE, &vty_no_restricted_mode_cmd);
-#ifdef HAVE_IPV6
- install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
- install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
-#endif /* HAVE_IPV6 */
-
- UNLOCK
-}
-
-/*------------------------------------------------------------------------------
- * Further initialisation for qpthreads.
- *
- * This is done during "second stage" initialisation, when all nexuses have
- * been set up and the qpthread_enabled state established.
- *
- * Need to know where the CLI nexus and the Routeing Engine nexus are.
- *
- * Initialise mutex.
- */
-void
-vty_init_r (qpn_nexus cli_n, qpn_nexus routing_n)
-{
- cli_nexus = cli_n;
- routing_nexus = routing_n;
- qpt_mutex_init(&vty_mutex, qpt_mutex_recursive);
-} ;
-
-/*------------------------------------------------------------------------------
- * System shut-down
- *
- * Reset all known vty and release all memory.
- */
-void
-vty_terminate (void)
-{
- LOCK
-
- if (vty_cwd)
- XFREE (MTYPE_TMP, vty_cwd);
-
- // if (vtyvec && Vvty_serv_thread)
- {
- uty_reset ();
-// vector_free (vtyvec);
-// vector_free (Vvty_serv_thread);
- }
- UNLOCK
-
- qpt_mutex_destroy(&vty_mutex, 0);
-}
-
-#undef LOCK
-#undef UNLOCK
-#undef ASSERTLOCKED
diff --git a/lib/vty.h b/lib/vty.h
index f43ad59e..126218ab 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -33,6 +33,8 @@
#include "qtimers.h"
#include "qpnexus.h"
#include "list_util.h"
+#include "vector.h"
+#include "qstring.h"
#include "node_type.h"
/* Macro in case there are particular compiler issues. */
@@ -58,16 +60,42 @@ enum { VTYSH_ENABLED = VTYSH_DEFINED } ;
#undef VTYSH_DEFINED
/*==============================================================================
+ * VTY Types
+ */
+enum vty_type
+{
+ VTY_NONE = 0, /* no type at all */
+
+ VTY_TERM, /* a telnet terminal -- input and output */
+ VTY_SHELL_SERV, /* a vty_shell slave -- input and output */
+
+ VTY_CONFIG_READ, /* reading config file -- output is to buffer
+ -- no input */
+
+ VTY_CONFIG_WRITE, /* writing config file -- output is to file
+ -- no input */
+
+ VTY_STDOUT, /* general output -- output is to stdout
+ -- no input */
+
+ VTY_STDERR, /* general output -- output is to stderr
+ -- no input */
+
+ VTY_SHELL, /* vty in vtysh -- output is to stdout */
+} ;
+
+/*==============================================================================
* VTY struct.
*/
typedef struct vty_io* vty_io ; /* private to vty.c */
+struct cmd_parsed ; /* in case vty.h expanded before command.h */
+
struct vty
{
/*----------------------------------------------------------------------
- * The following are used outside vty.c, and represent the context
- * in which commands are executed.
+ * The following are the context in which commands are executed.
*/
/* Node status of this vty
@@ -75,15 +103,7 @@ struct vty
* NB: when using qpthreads should lock VTY to access this -- so use
* vty_get_node() and vty_set_node().
*/
- int node ;
-
- /* The current command line.
- *
- * This is set when the command is dispatched, and not touched until the
- * command completes -- so may be read while the command is being executed,
- * without requiring any lock.
- */
- const char* buf ;
+ enum node_type node ;
/* For current referencing point of interface, route-map, access-list
* etc...
@@ -91,62 +111,49 @@ struct vty
* 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 ;
+ 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 ;
+ void *index_sub ;
/* String which is newline... read only -- no locking */
const char* newline ;
+ /*----------------------------------------------------------------------------
+ * 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 ;
+
/*----------------------------------------------------------------------
* The following are used inside vty.c only.
*/
- /* Pointer to related vty_io structure -- if any. */
+ /* Pointer to related vty_io structure */
vty_io vio ;
};
/*------------------------------------------------------------------------------
- * VTY events
+ * Can now include this
*/
-enum vty_event
-{
- VTY_SERV,
- VTY_READ,
- VTY_WRITE,
- VTY_TIMEOUT_RESET,
-
- VTYSH_SERV,
- VTYSH_READ,
- VTYSH_WRITE
-};
-/*------------------------------------------------------------------------------
- * VTY Types
- */
-enum vty_type
-{
- VTY_TERM, /* a telnet session -- input and output */
- VTY_FILE, /* writing config file -- output is to file
- -- no input */
-
- VTY_STDOUT, /* reading config file -- output is to stdout
- -- no input */
-
- VTY_SHELL, /* vty in vtysh -- output is to stdout */
- VTY_SHELL_SERV /* vty in daemon -- input and output */
-} ;
+#include "command.h"
/*------------------------------------------------------------------------------
*
*/
#define VTY_BUFSIZ 512
-#define VTY_MAXHIST 20
+#define VTY_MAXHIST 51
/* Integrated configuration file. */
#define INTEGRATE_DEFAULT_CONFIG "Quagga.conf"
@@ -241,37 +248,37 @@ extern char integrate_default[];
*/
extern void vty_init (struct thread_master *);
extern void vty_init_r (qpn_nexus, qpn_nexus);
-extern void vty_serv_sock(const char *addr, unsigned short port,
+extern void vty_start(const char *addr, unsigned short port, const char *path) ;
+#define vty_serv_sock(addr, port, path) vty_start(addr, port, path)
+extern void vty_restart(const char *addr, unsigned short port,
const char *path) ;
-extern struct vty* vty_new (int fd, enum vty_type type) ;
-extern void vty_close (struct vty *);
+extern struct vty* vty_open(enum vty_type type) ;
+extern void vty_close(struct vty *);
extern void vty_init_vtysh (void);
extern void vty_terminate (void);
extern void vty_reset (void);
+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 void vty_out_indent(struct vty *vty, int indent) ;
+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 (char *, char *);
-extern void vty_read_config_first_cmd_special (char *, char *, void (*)(void));
extern void vty_time_print (struct vty *, int);
extern char *vty_get_cwd (void);
-extern int vty_shell (struct vty *);
-extern int vty_shell_serv (struct vty *);
+extern bool vty_shell (struct vty *);
+extern bool vty_term (struct vty *);
+extern bool vty_shell_serv (struct vty *);
extern void vty_hello (struct vty *);
-extern int vty_get_node(struct vty *);
-extern void vty_set_node(struct vty *, int);
-extern int vty_get_type(struct vty *);
-extern int vty_get_status(struct vty *);
-extern void vty_set_status(struct vty *, int);
-extern int vty_get_lines(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);
-#ifdef QDEBUG
-extern void vty_goodbye (void);
-#endif
-
#endif /* _ZEBRA_VTY_H */
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
index de9bb53c..53d64716 100644
--- a/lib/vty_cli.c
+++ b/lib/vty_cli.c
@@ -26,10 +26,12 @@
#include "keystroke.h"
#include "vty.h"
#include "uty.h"
-#include "vty_io.h"
#include "vty_cli.h"
+#include "vty_io.h"
+#include "vio_lines.h"
#include "command.h"
+#include "command_queue.h"
#include "memory.h"
@@ -130,60 +132,41 @@ uty_new_host_name(const char* name)
*
* State of the CLI:
*
- * cli_blocked -- a command has been dispatched, and CLI is now waiting
- * for it and/or its output to complete.
- *
- * cmd_in_progress -- a command has been dispatched (and may have been
- * queued).
- *
- * If not blocked: can continue in the CLI until another
- * command is ready to be executed.
- *
- * There are two output FIFOs:
- *
- * 1. for the CLI itself -- uty_cli_out and friends
- *
- * 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 only emptied when !cmd_in_progress -- this means
- * that all the output from a given command is collected together before being
- * sent to the file.
- *
- * The CLI starts with cli_blocked and !cmd_in_progress, with the socket set
- * write on. The CLI progresses as follows:
- *
- * * on read_ready, if cli_blocked: do nothing.
+ * cli_blocked -- the CLI is unable to process any further keystrokes.
*
- * * on write_ready:
+ * cmd_in_progress -- a command has been dispatched and has not yet
+ * completed (may have been queued).
*
- * - empty out the CLI buffer
+ * cmd_out_enabled -- the command FIFO is may be emptied.
*
- * - if ! cmd_in_progress:
+ * This is set when a command completes, and cleared when
+ * everything is written away.
*
- * * empty out the command buffer
+ * cli_more_wait -- is in "--more--" wait state
*
- * * if the command buffer is empty, clear cli_blocked (if was set)
+ * The following are the valid combinations:
*
- * - generate a read_ready event unless write() would block.
+ * 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
+ * CLI has been closed
*
- * So the CLI is kicked once all available output completes.
- *
- * * on read_ready, if !cli_blocked:
- *
- * - process keystrokes into command line under construction.
- *
- * - when required, dispatch the command:
- *
- * * set cmd_in_progress
- *
- * * execute the command -- which may generate output
+ * There are two output FIFOs:
*
- * * clear cmd_in_progress
+ * 1. for the CLI itself -- uty_cli_out and friends
*
- * * set cli_blocked
+ * 2. for output generated by commands -- vty_out and friends.
*
- * - set write on (or read on) as required.
+ * 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.
*
* 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
@@ -203,13 +186,13 @@ uty_new_host_name(const char* name)
*
* This is largely buried in the output handling.
*
- * While cmd_in_progress is true, all output to the command output FIFO is held
- * there -- no actual write() is performed.
- *
- * When the command completes:
+ * 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
@@ -218,10 +201,10 @@ uty_new_host_name(const char* name)
*
* 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 cmd_wait_more flag and its handling.)
+ * main CLI. (See the cli_more_wait flag and its handling.)
*
- * When all the output has completed, cli_blocked is cleared and the CLI will
- * be kicked.
+ * 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.
*
* 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.
@@ -275,70 +258,191 @@ uty_new_host_name(const char* name)
*/
/*==============================================================================
- * Command Line Interface
+ * The CLI
+ */
+
+#define CONTROL(X) ((X) - '@')
+
+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.
*
- * State of the CLI:
+ * It is assumed that the following have been initialised, empty or zero:
+ *
+ * cli_prompt_for_node
+ * cl
+ * clx
+ * cli_vbuf
+ * cli_obuf
*
- * cli_blocked -- a command has been dispatched, and now waiting
- * for it and/or its output to complete.
+ * cli_drawn
+ * cli_dirty
*
- * cmd_in_progress -- a command has been dispatched (and may have been
- * queued).
+ * cli_prompt_set
*
- * Can continue in the CLI until another command is
- * ready to be executed.
+ * 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->type == VTY_TERM) ;
+
+ 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the CLI
+ *
+ * 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)
+{
+ 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
+ *
+ * Do nothing at all if half closed.
+ *
+ * Otherwise do: standard CLI
+ * or: "--more--" CLI
+ *
+ * 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)
+{
+ VTY_ASSERT_LOCKED() ;
+ assert(vio->type == VTY_TERM) ;
+
+ if (vio->half_closed)
+ return not_ready ; /* Nothing more if half closed */
+
+ /* Standard or "--more--" CLI ? */
+ if (vio->cli_more_wait)
+ return uty_cli_more_wait(vio) ;
+ else
+ return uty_cli_standard(vio) ;
+} ;
+
+/*==============================================================================
+ * The Standard CLI
*/
-static void uty_cli_draw(vty_io vio, enum node_type node) ;
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 int uty_cli_dispatch(vty_io vio) ;
+static bool uty_cli_dispatch(vty_io vio) ;
/*------------------------------------------------------------------------------
- * Run the CLI until:
+ * Standard CLI for VTY_TERM -- if not blocked, runs until:
*
- * * becomes blocked
* * runs out of keystrokes
* * executes a command
*
- * When exits will (in general):
- *
- * * set write on if there is anything to be written or have something
- * in the keystroke stream to be processed.
- *
- * * set read on if does not set write on, if not yet hit EOF on the
- * keystroke stream.
- *
* Note that this executes at most one command each time it is called. This
* is to allow for a modicum of sharing of the system. For real keyboard input
* this will make no difference at all !
+ *
+ * Returns: not_ready blocked and was blocked when entered
+ * write_ready if there is anything in the keystroke stream
+ * read_ready otherwise
*/
-extern void
-uty_cli(vty_io vio)
+static enum vty_readiness
+uty_cli_standard(vty_io vio)
{
- bool won ;
-
VTY_ASSERT_LOCKED() ;
+ assert(vio->type == VTY_TERM) ;
/* cli_blocked is set when is waiting for a command, or its output to
- * complete.
+ * complete -- unless either of those has happened, is still blocked.
*
- * There is no good reason to arrive here in that state, and nothing to be
- * done if that happens.
+ * NB: in both these cases, assumes that other forces are at work to
+ * keep things moving.
*/
if (vio->cli_blocked)
- return ;
+ {
+ assert(vio->cmd_in_progress || vio->cmd_out_enabled) ;
- /* If there is something to do, that is because a previous command has
- * now completed, which may have wiped the pending command.
- *
- * If there is nothing pending, then can run the CLI until there is
+ 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 there is nothing pending, then can run the CLI until there is
* something to do, or runs out of input.
+ *
+ * If there is something to do, that is because a previous command has
+ * now completed, which may have wiped the pending command or changed
+ * the required prompt.
*/
- if (vio->cli_do != cli_do_nothing)
- uty_cli_draw(vio, vio->vty->node) ;
- else
+ if (vio->cli_do == cli_do_nothing)
vio->cli_do = uty_cli_process(vio, vio->vty->node) ;
+ else
+ uty_cli_draw_this(vio, vio->vty->node) ;
/* If have something to do, do it. */
if (vio->cli_do != cli_do_nothing)
@@ -357,73 +461,21 @@ uty_cli(vty_io vio)
vio->cli_blocked = 1 ;
} ;
- /* If there is anything in the CLI output FIFO, must set write on to clear
- * it.
+ /* Use write_ready as a proxy for read_ready on the keystroke stream.
*
- * If there is anything in the command output FIFO *and* is !cmd_in_progress,
- * must set write on to clear it.
+ * Also, if the command line is not drawn, then return write_ready, so
+ * that
*
- * Otherwise: if cmd_in_progress, then the command buffer is not to be
- * output yet, and the command completion will kick things into action -- so
- * nothing further is required here.
- *
- * Otherwise: if there is anything in the command output buffer, must set
- * write on to clear it.
- *
- * Otherwise: if there is something in the keystroke stream, then must set
- * write on... the write_ready acts as a proxy for keystroke stream is ready.
- *
- * Otherwise: set read on -- so if further input arrives, will get on it.
+ * Note that if has just gone cli_blocked, still returns ready. This is
+ * defensive: at worst will generate one unnecessary read_ready/write_ready
+ * event.
*/
- do
- {
- won = 1 ; /* generally the case */
-
- /* If have some CLI output -- do that */
- if (!vio_fifo_empty(&vio->cli_obuf))
- break ;
-
- /* If is blocked with command in progress -- do nothing.
- *
- * This state <=> that there is a queued command, and the CLI is now
- * blocked until the command completes. When it does, things will
- * progress.
- */
- if (vio->cli_blocked && vio->cmd_in_progress)
- {
- won = 0 ;
- break ;
- } ;
-
- /* If have some command output which is not held because there is
- * a command in progress -- do that.
- */
- if (!vio_fifo_empty(&vio->cmd_obuf) && !vio->cmd_in_progress)
- break ;
-
- /* If the keystroke buffer is not empty then write_ready is used as a
- * proxy for keystroke ready.
- */
- if (!keystroke_stream_empty(vio->key_stream))
- break ;
-
- /* Finally... if not at keystroke EOF, set read on */
- won = 0 ;
-
- if (!keystroke_stream_eof(vio->key_stream))
- uty_file_set_read(&vio->file, on) ;
-
- } while (0) ;
-
- if (won)
- uty_file_set_write(&vio->file, on) ;
+ if (keystroke_stream_empty(vio->key_stream))
+ return read_ready ;
+ else
+ return write_ready ;
} ;
-/* TODO: dealing with EOF and timeout and closing and such
- *
- * uty_cout (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
- *
- */
/*------------------------------------------------------------------------------
* Dispatch the current vio->cli_do -- queueing it if necessary.
*
@@ -432,19 +484,19 @@ uty_cli(vty_io vio)
* Expects to be on new blank line, and when returns will be on new, blank
* line.
*
- * Returns: true <=> output is pending and command completed
- * false => no output pending or command was queued
+ * Returns: true <=> command completed and output is pending
+ * false => command has been queued and is now in progress
*
* 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.
*/
-static int
+static bool
uty_cli_dispatch(vty_io vio)
{
qstring_t tmp ;
enum cli_do cli_do ;
- int queued ;
+ enum cmd_return_code ret ;
struct vty* vty = vio->vty ;
@@ -455,7 +507,8 @@ uty_cli_dispatch(vty_io vio)
*
* Clear vio->cl and vio->cl_do.
*/
- vio->cmd_in_progress = 1 ; /* => vty->buf is valid */
+ 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 ;
@@ -466,18 +519,17 @@ uty_cli_dispatch(vty_io vio)
cli_do = vio->cli_do ; /* current operation */
vio->cli_do = cli_do_nothing ; /* clear */
- qs_set_empty(&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)) ;
- /* TODO: reset line_control */
+ uty_out_clear(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 */
- queued = uty_auth(vty, vty->buf, cli_do) ;
+ ret = uty_auth(vty, vty->buf, cli_do) ;
}
else
{
@@ -488,23 +540,27 @@ uty_cli_dispatch(vty_io vio)
break ;
case cli_do_command:
- queued = uty_command(vty, vty->buf) ;
+ ret = uty_command(vty) ;
break ;
case cli_do_ctrl_c:
- queued = uty_stop_input(vty) ;
+ ret = uty_stop_input(vty) ;
break ;
case cli_do_ctrl_d:
- queued = uty_down_level(vty) ;
+ ret = uty_down_level(vty) ;
break ;
case cli_do_ctrl_z:
- queued = uty_command(vty, vty->buf) ;
- if (queued)
- vio->cli_do = cli_do_ctrl_z ;
+ ret = uty_command(vty) ;
+ if (ret == CMD_QUEUED)
+ vio->cli_do = cli_do_ctrl_z ; /* defer the ^Z action */
else
- queued = uty_end_config(vty) ;
+ ret = uty_end_config(vty) ; /* do the ^Z now */
+ break ;
+
+ case cli_do_eof:
+ ret = uty_cmd_close(vio->vty, "End") ;
break ;
default:
@@ -512,13 +568,16 @@ uty_cli_dispatch(vty_io vio)
} ;
} ;
- if (!queued)
+ if (ret == CMD_QUEUED)
+ {
+ uty_cli_draw(vio) ; /* draw the prompt */
+ return 0 ; /* command not complete */
+ }
+ else
{
- vio->cmd_in_progress = 0 ; /* command complete */
- vty->buf = NULL ; /* finished with command line */
+ uty_cli_cmd_complete(vio, ret) ;
+ return 1 ; /* command complete */
} ;
-
- return ! (vio->cmd_in_progress || vio_fifo_empty(&vio->cmd_obuf)) ;
} ;
/*------------------------------------------------------------------------------
@@ -528,7 +587,7 @@ uty_cli_dispatch(vty_io vio)
* or not... write_ready will kick read_ready.
*/
extern void
-vty_queued_result(struct vty *vty, int result, int action)
+vty_queued_result(struct vty *vty, enum cmd_return_code ret)
{
vty_io vio ;
@@ -536,20 +595,210 @@ vty_queued_result(struct vty *vty, int result, int action)
vio = vty->vio ;
- vio->cmd_in_progress = 0 ; /* command complete */
- vty->buf = NULL ; /* finished with command line */
+ if (!vio->closed)
+ {
+ uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */
- vio->cli_blocked = !vio_fifo_empty(&vio->cmd_obuf) ;
- /* blocked if output is now pending */
+ /* 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) ;
- uty_cli_wipe(vio) ; /* wipe any partly constructed line */
+ /* 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 the VTY is closed, the only reason it still exists is because
+ * there was cmd_in_progress.
+ */
+ vio->cmd_in_progress = 0 ;
+
+ uty_close(vio) ; /* Final close */
+ } ;
- uty_file_set_write(&vty->vio->file, on) ;
- /* flush any output -- which will do a
- * read_ready when all finished */
VTY_UNLOCK() ;
}
+/*------------------------------------------------------------------------------
+ * 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
+ * * and prepare the line control for output
+ *
+ * If the return is CMD_CLOSE, then also now does the required half close.
+ *
+ * Note that apart from CMD_CLOSE, don't really care what the return was. Any
+ * diagnostics or other action must be dealt with elsewhere (as part of the
+ * command execution.
+ *
+ * Note that everything proceeds as if there is some output. So after every
+ * command goes through at least one write_ready event.
+ *
+ * This ensures some multiplexing at the command level.
+ *
+ * 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)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(vio->cmd_in_progress && !vio->cmd_out_enabled) ;
+
+ if (ret == CMD_CLOSE)
+ uty_half_close(vio, NULL) ;
+
+ vio->cmd_in_progress = 0 ; /* command complete */
+ vio->cmd_out_enabled = 1 ; /* enable the output */
+ vio->cli_blocked = 1 ; /* now blocked waiting for output */
+
+ vio->vty->buf = NULL ; /* finished with command line */
+
+ uty_cmd_output_start(vio) ; /* reset line control (belt & braces) */
+} ;
+
+/*==============================================================================
+ * The "--more--" CLI
+ *
+ * While command output is being cleared from its FIFO, the CLI is cli_blocked.
+ *
+ * When the output side signals that "--more--" is required, it sets the
+ * cli_more_wait flag and clears the cmd_out_enabled 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
+ * "--more--" response which was typed before the prompt was issued.
+ *
+ * The cli_blocked flag indicates that the CLI is in this first stage.
+ */
+
+/*------------------------------------------------------------------------------
+ * Change the CLI to the "--more--" CLI.
+ *
+ * Outputs the new prompt line.
+ */
+extern void
+uty_cli_go_more_wait(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(vio->cli_blocked && vio->cmd_out_enabled && !vio->cli_more_wait) ;
+
+ uty_cli_wipe(vio, 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 */
+
+ uty_cli_draw(vio) ; /* draw the "--more--" */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Handle the "--more--" state.
+ *
+ * Deals with the first stage if cli_blocked.
+ *
+ * Tries to steal a keystroke, and when succeeds wipes the "--more--"
+ * prompt and exits cli_more_wait -- and may cancel all outstanding output.
+ *
+ * EOF on input causes immediate exit from cli_more_state.
+ *
+ * Returns: read_ready -- waiting to steal a keystroke
+ * now_ready -- just left cli_more_wait
+ * not_ready -- otherwise
+ */
+static enum vty_readiness
+uty_cli_more_wait(vty_io vio)
+{
+ struct keystroke steal ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Deal with the first stage of "--more--" */
+ if (vio->cli_blocked)
+ {
+ 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 ;
+
+ vio->cli_blocked = 0 ;
+
+ /* empty the input buffer into the keystroke stream */
+ do
+ {
+ get = uty_read(vio, NULL) ;
+ } while (get > 0) ;
+ } ;
+
+ /* Go through the "--more--" process, unless no longer write_open (!) */
+ if (vio->sock.write_open)
+ {
+ /* The read fetches a reasonable lump from the I/O -- so if there
+ * is a complete keystroke available, expect to get it.
+ *
+ * If no complete keystroke available to steal, returns ks_null.
+ *
+ * If has hit EOF (or error etc), returns knull_eof.
+ */
+ uty_read(vio, &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 ;
+ else
+ return read_ready ;
+ } ;
+
+ /* Stolen a keystroke -- a (very) few terminate all output */
+ if (steal.type == ks_char)
+ {
+ switch (steal.value)
+ {
+ case CONTROL('C'):
+ case 'q':
+ case 'Q':
+ uty_out_clear(vio) ;
+ break;
+
+ default: /* everything else, thrown away */
+ break ;
+ } ;
+ } ;
+ } ;
+
+ /* End of "--more--" process
+ *
+ * Wipe out the prompt and update state.
+ *
+ * Return write_ready to tidy up the screen and, unless cleared, write
+ * some more.
+ */
+ uty_cli_wipe(vio, 0) ;
+
+ 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 */
+
+ return now_ready ;
+} ;
+
/*==============================================================================
* CLI VTY output
*
@@ -600,21 +849,17 @@ static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ;
* CLI VTY output -- cf fprintf()
*/
static void
-uty_cli_out (vty_io vio, const char *format, ...)
+uty_cli_out(vty_io vio, const char *format, ...)
{
VTY_ASSERT_LOCKED() ;
- if (vio->file.write_open)
+ if (vio->sock.write_open)
{
- va_list args;
- int len ;
+ va_list args ;
va_start (args, format);
- len = qs_vprintf(&vio->cli_vbuf, format, args) ;
+ vio_fifo_vprintf(&vio->cli_obuf, format, args) ;
va_end(args);
-
- if (len > 0)
- uty_cli_write(vio, qs_chars(&vio->cli_vbuf), len) ;
} ;
} ;
@@ -624,11 +869,11 @@ 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_io vio, const char *this, size_t len)
{
VTY_ASSERT_LOCKED() ;
- if (vio->cli_echo_suppress || !vio->file.write_open)
+ if (vio->cli_echo_suppress || !vio->sock.write_open)
return ;
uty_cli_write(vio, this, len) ;
@@ -644,7 +889,7 @@ uty_cli_echo_n(vty_io vio, cli_rep_char chars, int n)
{
VTY_ASSERT_LOCKED() ;
- if (vio->cli_echo_suppress || !vio->file.write_open)
+ if (vio->cli_echo_suppress || !vio->sock.write_open)
return ;
uty_cli_write_n(vio, chars, n) ;
@@ -658,7 +903,7 @@ uty_cli_write(vty_io vio, const char *this, int len)
{
VTY_ASSERT_LOCKED() ;
- if (vio->file.write_open)
+ if (vio->sock.write_open)
vio_fifo_put(&vio->cli_obuf, this, len) ;
} ;
@@ -704,13 +949,14 @@ uty_cli_write_s(vty_io vio, const char *str)
/*------------------------------------------------------------------------------
* Send newline to the console.
*
- * Clears the cli_drawn flag.
+ * Clears the cli_drawn and the cli_dirty flags.
*/
static void
uty_cli_out_newline(vty_io vio)
{
uty_cli_write(vio, telnet_newline, 2) ;
vio->cli_drawn = 0 ;
+ vio->cli_dirty = 0 ;
} ;
/*------------------------------------------------------------------------------
@@ -747,16 +993,18 @@ uty_cli_out_wipe_n(vty_io vio, int n)
static const char* cli_response [2][cli_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_command] = "",
+ [cli_do_ctrl_c] = "^C",
+ [cli_do_ctrl_d] = "^D",
+ [cli_do_ctrl_z] = "^Z",
+ [cli_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_command] = "^",
+ [cli_do_ctrl_c] = "^C",
+ [cli_do_ctrl_d] = "^D",
+ [cli_do_ctrl_z] = "^Z",
+ [cli_do_eof] = "^*"
}
} ;
@@ -766,7 +1014,7 @@ uty_cli_response(vty_io vio, enum cli_do cli_do)
const char* str ;
int len ;
- if (cli_do == cli_do_nothing)
+ if ((cli_do == cli_do_nothing) || (vio->half_closed))
return ;
str = (cli_do < cli_do_count)
@@ -811,36 +1059,72 @@ uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio)
/*------------------------------------------------------------------------------
* Wipe the current console line -- if any.
*/
-extern void
-uty_cli_wipe(vty_io vio)
+static void
+uty_cli_wipe(vty_io vio, int len)
{
int a ;
int b ;
- if (!vio->cli_drawn == 0)
+ if (!vio->cli_drawn)
return ; /* quit if already wiped */
assert(vio->cl.cp <= vio->cl.len) ;
- /* deal with echo suppression first */
- if (vio->cli_echo_suppress)
- {
- b = 0 ;
- a = 0 ;
- }
- else
+ /* Establish how much ahead and how much behind the cursor */
+ a = vio->cli_extra_len ;
+ b = vio->cli_prompt_len ;
+
+ if (!vio->cli_echo_suppress && !vio->cli_more_wait)
{
- b = vio->cl.cp ; /* behind cursor */
- a = vio->cl.len - b ; /* ahead of cursor */
- }
+ a += vio->cl.len - vio->cl.cp ;
+ b += vio->cl.cp ;
+ } ;
- /* Stuff ahead of the current position */
- uty_cli_out_wipe_n(vio, a + vio->cli_extra_len) ;
+ /* Stuff ahead of the current position if any ahead of new len */
+ if ((a + b) > len)
+ uty_cli_out_wipe_n(vio, +a) ;
- /* Stuff behind the current position */
- uty_cli_out_wipe_n(vio, vio->cli_prompt_len + b) ;
+ /* Stuff behind current position, but ahead of new len */
+ if (b > len)
+ {
+ uty_cli_out_wipe_n(vio, -(b - len)) ;
+ b = len ; /* moved the cursor back */
+ } ;
+
+ /* Back to the beginning of the line */
+ uty_cli_write_n(vio, telnet_backspaces, b) ;
+ /* Nothing there any more */
vio->cli_drawn = 0 ;
+ vio->cli_dirty = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If not currently drawn, draw prompt etc according to the current state
+ * and node.
+ *
+ * See uty_cli_draw().
+ */
+extern bool
+uty_cli_draw_if_required(vty_io vio)
+{
+ if (vio->cli_drawn)
+ return false ;
+
+ uty_cli_draw(vio) ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Draw prompt etc for the current vty node.
+ *
+ * See uty_cli_draw_this()
+ */
+static void
+uty_cli_draw(vty_io vio)
+{
+ uty_cli_draw_this(vio, vio->vty->node) ;
} ;
/*------------------------------------------------------------------------------
@@ -851,34 +1135,52 @@ uty_cli_wipe(vty_io vio)
*
* Otherwise, assumes is positioned at start of an empty line.
*
- * If there is a command queued, uses a dummy prompt -- because by the time
- * the command is executed, the node may have changed, so the current prompt
- * may be invalid.
+ * Draws prompt according to the given 'node', except:
+ *
+ * * if is half_closed, draw nothing -- wipes the current line
+ *
+ * * if is cli_more_wait, draw the "--more--" prompt
+ *
+ * * if is cmd_in_progress, draw the vestigial prompt.
+ *
+ * By the time the current command completes, the node may have changed, so
+ * the current prompt may be invalid.
*
* Sets: cli_drawn = true
+ * cli_dirty = false
* cli_prompt_len = length of prompt used
* cli_extra_len = 0
* cli_echo_suppress = (AUTH_NODE or AUTH_ENABLE_NODE)
*/
static void
-uty_cli_draw(vty_io vio, enum node_type node)
+uty_cli_draw_this(vty_io vio, enum node_type node)
{
const char* prompt ;
+ size_t l_len ;
+ int p_len ;
- if (vio->cli_drawn)
- uty_cli_wipe(vio) ;
-
- vio->cli_drawn = 1 ;
- vio->cli_extra_len = 0 ;
- vio->cli_echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+ if (vio->cli_dirty)
+ uty_cli_out_newline(vio) ; /* clears cli_dirty and cli_drawn */
/* Sort out what the prompt is. */
-
- if (vio->cmd_in_progress)
+ if (vio->half_closed)
+ {
+ prompt = "" ;
+ p_len = 0 ;
+ l_len = 0 ;
+ }
+ else if (vio->cli_more_wait)
+ {
+ prompt = "--more--" ;
+ p_len = strlen(prompt) ;
+ l_len = 0 ;
+ }
+ else if (vio->cmd_in_progress)
{
/* If there is a queued command, the prompt is a minimal affair. */
prompt = "~ " ;
- vio->cli_prompt_len = strlen(prompt) ;
+ p_len = strlen(prompt) ;
+ l_len = vio->cl.len ;
}
else
{
@@ -909,27 +1211,99 @@ uty_cli_draw(vty_io vio, enum node_type node)
vio->cli_prompt_set = 1 ;
} ;
- prompt = qs_chars(&vio->cli_prompt_for_node) ;
- vio->cli_prompt_len = vio->cli_prompt_for_node.len ;
+ prompt = vio->cli_prompt_for_node.body ;
+ p_len = vio->cli_prompt_for_node.len ;
+ l_len = vio->cl.len ;
} ;
- uty_cli_write(vio, prompt, vio->cli_prompt_len) ;
+ /* Now, if line is currently drawn, time to wipe it */
+ if (vio->cli_drawn)
+ uty_cli_wipe(vio, 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) ;
+
+ vio->cli_prompt_len = p_len ;
+
+ uty_cli_write(vio, prompt, p_len) ;
- if ((vio->cl.len != 0) && !vio->cli_echo_suppress)
+ if (l_len != 0)
{
- uty_cli_write(vio, qs_chars(&vio->cl), vio->cl.len) ;
- if (vio->cl.cp < vio->cl.len)
- uty_cli_write_n(vio, telnet_backspaces, vio->cl.len - vio->cl.cp) ;
+ 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) ;
} ;
} ;
/*==============================================================================
- * Command line processing loop
+ * Monitor output.
+ *
+ * To prepare for monitor output, wipe as much as is necessary for the
+ * monitor line to appear correctly.
+ *
+ * After monitor output, may need to do two things:
+ *
+ * * if the output was incomplete, place the rump in the CLI buffer,
+ * so that:
+ *
+ * a. don't mess up the console with partial lines
+ *
+ * b. don't lose part of a message
+ *
+ * c. act as a brake on further monitor output -- cannot do any more
+ * until the last, part, line is dealt with.
+ *
+ * * restore the command line, unless it is empty !
*/
-#define CONTROL(X) ((X) - '@')
+ /*-----------------------------------------------------------------------------
+ * Prepare for new monitor output line.
+ *
+ * Wipe any existing command line.
+ */
+extern void
+uty_cli_pre_monitor(vty_io vio, size_t len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_cli_wipe(vio, len) ;
+} ;
-static void uty_telnet_command(vty_io vio, keystroke stroke) ;
+/*------------------------------------------------------------------------------
+ * Recover from monitor line output.
+ *
+ * If monitor line failed to complete, append the rump to the CLI buffer.
+ *
+ * If have a non-empty command line, or is cli_more_wait, redraw the command
+ * line.
+ *
+ * Returns: 0 => rump was empty and no command line stuff written
+ * > 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)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (len != 0)
+ uty_cli_write(vio, buf, len) ;
+
+ if (vio->cli_more_wait || (vio->cl.len != 0))
+ {
+ uty_cli_draw(vio) ;
+ ++len ;
+ } ;
+
+ return 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) ;
@@ -999,10 +1373,13 @@ uty_cli_process(vty_io vio, enum node_type node)
while (1)
{
if (!vio->cli_drawn)
- uty_cli_draw(vio, node) ;
+ uty_cli_draw_this(vio, node) ;
if (!uty_cli_get_keystroke(vio, &stroke))
- break ;
+ {
+ ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ;
+ break ;
+ } ;
if (stroke.flags != 0)
{
@@ -1160,10 +1537,10 @@ uty_cli_process(vty_io vio, enum node_type node)
/* Telnet Command ----------------------------------------------------*/
case ks_iac:
- uty_telnet_command(vio, &stroke) ;
+ uty_telnet_command(vio, &stroke, false) ;
break ;
- /* Single byte escape ------------------------------------------------*/
+ /* Unknown -----------------------------------------------------------*/
default:
zabort("unknown keystroke type") ;
} ;
@@ -1185,95 +1562,6 @@ uty_cli_process(vty_io vio, enum node_type node)
} ;
/*==============================================================================
- * Support for the "--More--" handling
- */
-
-#define MSG_MORE_PROMPT "--more--"
-
-/*------------------------------------------------------------------------------
- * Output the "--more--" prompt and enter waiting for more state.
- *
- * As enters the waiting state, empties all the available input into the
- * keystroke FIFO... so doesn't treat anything that arrived before the
- * prompt was issued as a response to the prompt !
- *
- * Enables read -- now waiting for response to --more--
- *
- * NB: it is the caller's responsibility to clear the "--more--" prompt from
- * the CLI buffer.
- */
-extern void
-uty_cli_want_more(vty_io vio)
-{
- int get ;
-
- /* Empty the input buffers into the keystroke FIFO */
- do
- {
- get = uty_read(vio, NULL) ;
- } while (get > 0) ;
-
- /* Output the prompt and update the state */
- uty_cli_write_s(vio, MSG_MORE_PROMPT) ;
-
- vio->cmd_wait_more = 1 ;
-
- uty_file_set_read(&vio->file, on) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Waiting for response to "--more--" prompt.
- *
- * Steal the next available keystroke and process it. Error and EOF appear as
- * a null keystroke.
- *
- * Wipes the prompt and exits cli_wait_more state when has stolen keystroke.
- *
- * Enables write -- need to clear the prompt from the cli buffers and may
- * now continue command output, or signal it's done.
- */
-extern void
-uty_cli_wait_more(vty_io vio)
-{
- struct keystroke steal ;
-
- /* The read fetches a reasonable lump from the I/O -- so if there is a
- * complete keystroke available, expect to get it.
- *
- * If no complete keystroke available to steal, returns ks_null.
- *
- * If has hit EOF (or error etc), returns knull_eof.
- */
- uty_read(vio, &steal) ;
-
- if ((steal.type == ks_null) || (steal.value != knull_eof))
- return ;
-
- /* Stolen a keystroke -- a (very) few terminate all output */
- if (steal.type == ks_char)
- {
- switch (steal.value)
- {
- case CONTROL('C'):
- case 'q':
- case 'Q':
- uty_out_discard(vio) ;
- break;
-
- default: /* evrything else, thrown away */
- break ;
- } ;
- } ;
-
- /* Wipe out the prompt and update state. */
- uty_cli_out_wipe_n(vio, - (int)strlen(MSG_MORE_PROMPT)) ;
-
- vio->cmd_wait_more = 0 ;
-
- uty_file_set_write(&vio->file, on) ;
-} ;
-
-/*==============================================================================
* Command line operations
*/
@@ -1289,7 +1577,7 @@ uty_cli_insert (vty_io vio, const char* chars, int n)
VTY_ASSERT_LOCKED() ;
- assert((vio->cl.cp <= vio->cl.len)&& (n >= 0)) ;
+ assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
if (n <= 0)
return n ; /* avoid trouble */
@@ -1359,12 +1647,12 @@ uty_cli_word_overwrite (vty_io vio, char *str)
static int
uty_cli_forwards(vty_io vio, int n)
{
- int c ;
+ int have ;
VTY_ASSERT_LOCKED() ;
- c = vio->cl.len - vio->cl.cp ;
- if (n > c)
- n = c ;
+ have = vio->cl.len - vio->cl.cp ;
+ if (have < n)
+ n = have ;
assert(n >= 0) ;
@@ -1385,17 +1673,18 @@ uty_cli_forwards(vty_io vio, int n)
static int
uty_cli_backwards(vty_io vio, int n)
{
- int c ;
VTY_ASSERT_LOCKED() ;
- c = vio->cl.len - vio->cl.cp ;
- if (n > c)
- n = c ;
+ if ((int)vio->cl.cp < n)
+ n = vio->cl.cp ;
assert(n >= 0) ;
- uty_cli_echo_n(vio, telnet_backspaces, n) ;
- vio->cl.cp -= n ;
+ if (n > 0)
+ {
+ uty_cli_echo_n(vio, telnet_backspaces, n) ;
+ vio->cl.cp -= n ;
+ } ;
return n ;
}
@@ -1409,10 +1698,15 @@ static int
uty_cli_del_forwards(vty_io vio, int n)
{
int after ;
+ int have ;
VTY_ASSERT_LOCKED() ;
- assert((vio->cl.len - vio->cl.cp) && (n >= 0)) ;
+ have = vio->cl.len - vio->cl.cp ;
+ if (have < n)
+ n = have ; /* cannot delete more than have */
+
+ assert(n >= 0) ;
if (n <= 0)
return 0 ;
@@ -1425,8 +1719,6 @@ uty_cli_del_forwards(vty_io vio, int n)
uty_cli_echo_n(vio, telnet_spaces, n) ;
uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
- vio->cl.len -= n ;
-
return n ;
}
@@ -1660,31 +1952,18 @@ uty_cli_transpose_chars(vty_io vio)
extern void
uty_cli_hist_add (vty_io vio, const char* cmd_line)
{
- char* prev_line ;
- char* line ;
- char* e ;
+ qstring prev_line ;
+ qstring_t line ;
int prev_index ;
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) ;
- /* trim leading and trailing spaces before considering for history */
-
- while (*cmd_line == ' ')
- ++cmd_line ; /* hack off leading spaces */
-
- if (*cmd_line == '\0')
- return ; /* ignore empty lines */
-
- line = XSTRDUP(MTYPE_VTY_HIST, cmd_line) ;
-
- e = line + strlen(line) ;
- while (*(e - 1) == ' ')
- --e ;
- *e = '\0' ; /* hack off any trailing spaces */
-
/* find the previous command line in the history */
prev_index = vio->hindex - 1 ;
if (prev_index < 0)
@@ -1692,24 +1971,27 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
prev_line = vector_get_item(&vio->hist, prev_index) ;
- /* Insert unless same as previous line */
- if ((prev_line == NULL) || (strcmp(line, prev_line) != 0))
- {
- /* Insert history entry. */
- if (prev_line != NULL)
- XFREE (MTYPE_VTY_HIST, prev_line) ;
+ /* If the previous line is NULL, that means the history is empty.
+ *
+ * If the previous line is essentially the same as the current 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 !
+ */
+ 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) ;
- vector_set_item(&vio->hist, vio->hindex, line) ;
+ /* Now replace the hindex entry */
+ vector_set_item(&vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
- /* History index rotation. */
- vio->hindex++;
- if (vio->hindex == VTY_MAXHIST)
- vio->hindex = 0;
- }
- else
- {
- XFREE(MTYPE_VTY_HIST, line) ;
- } ;
+ /* Advance to the near future and reset the history pointer */
+ vio->hindex++;
+ if (vio->hindex == VTY_MAXHIST)
+ vio->hindex = 0;
vio->hp = vio->hindex;
} ;
@@ -1718,36 +2000,58 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
* Replace command line by current history.
*
* This function is called from vty_next_line and vty_previous_line.
+ *
+ * Step +1 is towards the present
+ * -1 is into the past
*/
static void
uty_cli_history_use(vty_io vio, int step)
{
int index ;
- unsigned new_len ;
unsigned old_len ;
- char* hist ;
+ unsigned after ;
+ unsigned back ;
+ qstring hist ;
VTY_ASSERT_LOCKED() ;
- /* See if have anything usable */
+ assert((step == +1) || (step == -1)) ;
+
index = vio->hp ;
- if ((step > 0) && (index == vio->hindex))
- return ; /* cannot step forward past the insertion point */
+ /* Special case of being at the insertion point */
+ if (index == vio->hindex)
+ {
+ 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.
+ */
+ hist = vector_get_item(&vio->hist, vio->hindex) ;
+ vector_set_item(&vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
+ } ;
+
+ /* Advance or retreat */
index += step ;
if (index < 0)
index = VTY_MAXHIST - 1 ;
- else if (index >= VTY_MAXHIST) ;
+ else if (index >= VTY_MAXHIST)
index = 0 ;
- if ((step < 0) && (index == vio->hindex))
- return ; /* cannot step back to the insertion point */
-
hist = vector_get_item(&vio->hist, index) ;
- if (hist == NULL)
- return ; /* cannot step to unused entry */
+ /* If moving backwards in time, may not move back to the insertion
+ * 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 (step < 0)
+ if ((hist == NULL) || (index == vio->hindex))
+ return ;
+
+ /* Now, if arrived at the insertion point, this is returning to the
+ * present, which is fine.
+ */
vio->hp = index;
/* Move back to the start of the current line */
@@ -1755,13 +2059,26 @@ uty_cli_history_use(vty_io vio, int step)
/* Get previous line from history buffer and echo that */
old_len = vio->cl.len ;
- new_len = qs_set(&vio->cl, hist) ;
- vio->cl.cp = new_len ;
+ qs_copy(&vio->cl, hist) ;
+
+ /* Sort out wiping out any excess and setting the cursor position */
+ if (old_len > vio->cl.len)
+ after = old_len - vio->cl.len ;
+ else
+ after = 0 ;
- uty_cli_echo(vio, hist, new_len) ;
+ back = after ;
+ if (vio->cl.len > vio->cl.cp)
+ back += (vio->cl.len - vio->cl.cp) ;
+
+ if (vio->cl.len > 0)
+ uty_cli_echo(vio, vio->cl.body, vio->cl.len) ;
+
+ if (after > 0)
+ uty_cli_echo_n(vio, telnet_spaces, after) ;
- if (old_len < new_len)
- uty_cli_del_to_eol(vio) ;
+ if (back > 0)
+ uty_cli_echo_n(vio, telnet_backspaces, back) ;
return ;
} ;
@@ -1804,6 +2121,8 @@ uty_cli_complete_command (vty_io vio, enum node_type node)
{
unsigned i ;
int ret ;
+ int len ;
+ int n ;
vector matched ;
vector vline ;
@@ -1841,16 +2160,32 @@ uty_cli_complete_command (vty_io vio, enum node_type node)
break ;
case CMD_COMPLETE_LIST_MATCH:
+ len = 6 ;
for (i = 0; i < vector_end(matched); i++)
{
- if ((i % 6) == 0)
+ int sl = strlen((char*)vector_get_item(matched, i)) ;
+ if (len < sl)
+ len = sl ;
+ } ;
+
+ n = vio->width ;
+ if (n == 0)
+ n = 60 ;
+ n = n / (len + 2) ;
+ if (n == 0)
+ n = 1 ;
+
+ for (i = 0; i < vector_end(matched); i++)
+ {
+ if ((i % n) == 0)
uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out (vio, "%-10s ", (char*)vector_get_item(matched, i));
+ uty_cli_out(vio, "%-*s ", len, (char*)vector_get_item(matched, i));
}
+ uty_cli_out_newline(vio) ;
break;
- case CMD_ERR_NOTHING_TODO:
+ case CMD_COMPLETE_ALREADY:
default:
break;
} ;
@@ -2052,7 +2387,7 @@ uty_cli_cmd_prepare(vty_io vio, int help)
* VTY telnet stuff
*/
-#define TELNET_OPTION_DEBUG 1 /* 0 to turn off */
+#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
static const char* telnet_commands[256] =
{
@@ -2150,7 +2485,7 @@ uty_cli_out_hex(vty_io vio, const char* str, unsigned char u)
/*------------------------------------------------------------------------------
* Send telnet: "WILL TELOPT_ECHO"
*/
-extern void
+static void
uty_will_echo (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
@@ -2161,7 +2496,7 @@ uty_will_echo (vty_io vio)
/*------------------------------------------------------------------------------
* Send telnet: "suppress Go-Ahead"
*/
-extern void
+static void
uty_will_suppress_go_ahead (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
@@ -2172,7 +2507,7 @@ uty_will_suppress_go_ahead (vty_io vio)
/*------------------------------------------------------------------------------
* Send telnet: "don't use linemode"
*/
-extern void
+static void
uty_dont_linemode (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
@@ -2183,7 +2518,7 @@ uty_dont_linemode (vty_io vio)
/*------------------------------------------------------------------------------
* Send telnet: "Use window size"
*/
-extern void
+static void
uty_do_window_size (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
@@ -2194,7 +2529,7 @@ uty_do_window_size (vty_io vio)
/*------------------------------------------------------------------------------
* Send telnet: "don't use lflow" -- not currently used
*/
-extern void
+static void
uty_dont_lflow_ahead (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
@@ -2203,20 +2538,42 @@ uty_dont_lflow_ahead (vty_io vio)
}
/*------------------------------------------------------------------------------
+ * 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) ;
+} ;
+
+/*------------------------------------------------------------------------------
* 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.
*/
-static void
-uty_telnet_command(vty_io vio, keystroke stroke)
+static bool
+uty_telnet_command(vty_io vio, keystroke stroke, bool callback)
{
uint8_t* p ;
uint8_t o ;
int left ;
+ bool dealt_with ;
/* Echo to the other end if required */
if (TELNET_OPTION_DEBUG)
{
+ uty_cli_wipe(vio, 0) ;
+
p = stroke->buf ;
left = stroke->len ;
@@ -2245,16 +2602,17 @@ uty_telnet_command(vty_io vio, keystroke stroke)
}
} ;
- if (!(stroke->flags & kf_broken))
+ if (stroke->flags & kf_broken)
uty_cli_out (vio, "BROKEN") ;
uty_cli_out (vio, "\r\n") ;
-
} ;
/* Process the telnet command */
+ dealt_with = false ;
+
if (stroke->flags != 0)
- return ; /* go no further if broken */
+ return dealt_with ; /* go no further if broken */
p = stroke->buf ;
left = stroke->len ;
@@ -2294,6 +2652,9 @@ uty_telnet_command(vty_io vio, keystroke stroke)
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 ;
@@ -2305,4 +2666,6 @@ uty_telnet_command(vty_io vio, keystroke stroke)
default: /* no other IAC X */
break ;
} ;
+
+ return dealt_with ;
} ;
diff --git a/lib/vty_cli.h b/lib/vty_cli.h
index 4fda2db8..5bf682ac 100644
--- a/lib/vty_cli.h
+++ b/lib/vty_cli.h
@@ -25,19 +25,23 @@
#define _ZEBRA_VTY_CLI_H
#include "vty_io.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) ;
+
+extern enum vty_readiness uty_cli(vty_io vio) ;
+extern keystroke_callback uty_cli_iac_callback ;
-extern void uty_cli(vty_io vio) ;
extern void uty_cli_hist_add (vty_io vio, const char* cmd_line) ;
-extern void uty_cli_want_more(vty_io vio) ;
-extern void uty_cli_wait_more(vty_io vio) ;
-extern void uty_cli_wipe(vty_io vio) ;
+extern void uty_cli_go_more_wait(vty_io vio) ;
extern void uty_free_host_name(void) ;
extern void uty_check_host_name(void) ;
-extern void uty_will_echo (vty_io vio) ;
-extern void uty_will_suppress_go_ahead (vty_io vio) ;
-extern void uty_dont_linemode (vty_io vio) ;
-extern void uty_do_window_size (vty_io vio) ;
-extern void uty_dont_lflow_ahead (vty_io vio) ;
+extern bool uty_cli_draw_if_required(vty_io vio) ;
+
+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) ;
#endif /* _ZEBRA_VTY_CLI_H */
diff --git a/lib/vty_io.c b/lib/vty_io.c
index 15f90219..7137fbdc 100644
--- a/lib/vty_io.c
+++ b/lib/vty_io.c
@@ -46,21 +46,12 @@
/*==============================================================================
* VTY Command Output -- base functions
*
- * ALL vty command output ends up here.
- *
- * vty == NULL => vprintf(stdout, ...)
- * VTY_SHELL => vprintf(stdout, ...)
- * VTY_STDOUT => vprintf(stdout, ...)
- *
- * VTY_FILE => write(fd, ....)
- *
- * VTY_TERM => command FIFO
- * VTY_SHELL_SERV => command FIFO
- *
* During command processing the output sent here is held until the command
* completes.
*/
+static int uty_config_write(vty_io vio, bool all) ;
+
/*------------------------------------------------------------------------------
* VTY output function -- cf fprintf
*
@@ -84,71 +75,111 @@ uty_out (struct vty *vty, const char *format, ...)
*
* Returns: >= 0 => OK
* < 0 => failed (see errno)
+ *
+ * NB: for VTY_TERM and for VTY_SHELL_SERV -- this is command output:
+ *
+ * * MAY NOT do any command output if !cmd_enabled
+ *
+ * * first, the life of a vty is not guaranteed unless cmd_in_progress,
+ * so should not attempt to use a vty anywhere other than command
+ * execution.
+ *
+ * * second, cmd_out_enabled is false most of the time, and is only
+ * set true when a command completes, and it is time to write away
+ * the results.
+ *
+ * * all output is placed in the vio->cmd_obuf. When the command completes,
+ * the contents of the cmd_obuf will be written away -- subject to line
+ * control.
+ *
+ * * output is discarded if the vty is no longer write_open
*/
extern int
uty_vout(struct vty *vty, const char *format, va_list args)
{
- enum vty_type type ;
vty_io vio ;
- int len ;
+ int ret ;
VTY_ASSERT_LOCKED() ;
- /* Establish type of vty -- if any */
- if (vty != NULL)
- {
- vio = vty->vio ;
- if (!vio->file.write_open)
- return 0 ; /* discard output if not open ! */
-
- type = vio->type ;
- }
- else
- {
- vio = NULL ;
- type = VTY_STDOUT ;
- } ;
+ vio = vty->vio ;
- /* Output -- process depends on type */
- switch (type)
+ switch (vio->type)
{
case VTY_STDOUT:
case VTY_SHELL:
- len = vprintf (format, args);
+ ret = vprintf (format, args) ;
+ break ;
+
+ case VTY_STDERR:
+ ret = vfprintf (stderr, format, args) ;
+ break ;
+
+ case VTY_CONFIG_WRITE:
+ ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
+ if ((ret > 0) && vio_fifo_full_lump(&vio->cmd_obuf))
+ ret = uty_config_write(vio, false) ;
break ;
- case VTY_FILE:
case VTY_TERM:
case VTY_SHELL_SERV:
+ assert(vio->cmd_in_progress) ;
- len = qs_vprintf(&vio->cmd_vbuf, format, args) ;
- if (len > 0)
- {
- if (type == VTY_FILE)
- len = write(vio->file.fd, vio->cmd_vbuf.body, len) ;
- else
- vio_fifo_put(&vio->cmd_obuf, vio->cmd_vbuf.body, len) ;
- } ;
+ if (!vio->sock.write_open)
+ return 0 ; /* discard output if not open ! */
+
+ /* fall through.... */
+
+ case VTY_CONFIG_READ:
+ ret = vio_fifo_vprintf(&vio->cmd_obuf, format, args) ;
break ;
default:
zabort("impossible VTY type") ;
} ;
- return len;
+ return ret ;
} ;
/*------------------------------------------------------------------------------
- * Discard the current contents of the command FIFO
+ * Clear the contents of the command output FIFO etc.
*
- * TODO: worry about line control ??
+ * NB: does not change any of the cli_blocked/cmd_in_progress/cli_wait_more/etc
+ * flags -- competent parties must deal with those
*/
extern void
-uty_out_discard(vty_io vio)
+uty_out_clear(vty_io vio)
{
VTY_ASSERT_LOCKED() ;
- vio_fifo_set_empty(&vio->cmd_obuf) ;
+ vio_fifo_clear(&vio->cmd_obuf) ;
+
+ if (vio->cmd_lc != NULL)
+ vio_lc_clear(vio->cmd_lc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Flush the contents of the command output FIFO to the given file.
+ *
+ * Takes no notice of any errors !
+ */
+extern void
+uty_out_fflush(vty_io vio, FILE* file)
+{
+ char* src ;
+ size_t have ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ fflush(file) ;
+
+ while ((src = vio_fifo_get_lump(&vio->cmd_obuf, &have)) != NULL)
+ {
+ fwrite(src, 1, have, file) ;
+ vio_fifo_got_upto(&vio->cmd_obuf, src + have) ;
+ } ;
+
+ fflush(file) ;
} ;
/*==============================================================================
@@ -167,30 +198,13 @@ enum { vty_watch_dog_interval = 5 } ;
static void vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when) ;
static int vty_watch_dog_thread(struct thread *thread) ;
+static void uty_watch_dog_bark(void) ;
+static bool uty_death_watch_scan(void) ;
+
/*------------------------------------------------------------------------------
- * Watch dog action
+ * Start watch dog -- the first time a VTY is created.
*/
-static void
-uty_watch_dog_bark(void)
-{
- uty_check_host_name() ; /* check for host name change */
-
- /* TODO: death watch scan */
-
- /* Set timer to go off again later */
- if (vty_cli_nexus)
- qtimer_set(vty_watch_dog.qnexus, qt_add_monotonic(vty_watch_dog_interval),
- vty_watch_dog_qnexus) ;
- else
- {
- if (vty_watch_dog.thread != NULL)
- thread_cancel (vty_watch_dog.thread);
- vty_watch_dog.thread = thread_add_timer (vty_master,
- vty_watch_dog_thread, NULL, vty_watch_dog_interval) ;
- } ;
-} ;
-
-static void
+extern void
uty_watch_dog_start()
{
if (vty_cli_nexus)
@@ -200,6 +214,12 @@ uty_watch_dog_start()
uty_watch_dog_bark() ; /* start up by barking the first time */
}
+/*------------------------------------------------------------------------------
+ * Stop watch dog timer -- at close down.
+ *
+ * Final run along the death-watch
+ *
+ */
extern void
uty_watch_dog_stop(void)
{
@@ -210,6 +230,8 @@ uty_watch_dog_stop(void)
else
thread_cancel(vty_watch_dog.thread) ;
} ;
+
+ uty_death_watch_scan() ; /* scan the death-watch list */
}
/*------------------------------------------------------------------------------
@@ -219,7 +241,9 @@ static void
vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when)
{
VTY_LOCK() ;
+
uty_watch_dog_bark() ;
+
VTY_UNLOCK() ;
} ;
@@ -230,17 +254,77 @@ static int
vty_watch_dog_thread(struct thread *thread)
{
VTY_LOCK() ;
+
+ vty_watch_dog.thread = NULL ;
uty_watch_dog_bark() ;
+
VTY_UNLOCK() ;
return 0 ;
} ;
+/*------------------------------------------------------------------------------
+ * Watch dog action
+ */
+static void
+uty_watch_dog_bark(void)
+{
+ uty_check_host_name() ; /* check for host name change */
+
+ uty_death_watch_scan() ; /* scan the death-watch list */
+
+ /* Set timer to go off again later */
+ if (vty_cli_nexus)
+ qtimer_set(vty_watch_dog.qnexus,
+ qt_add_monotonic(QTIME(vty_watch_dog_interval)),
+ vty_watch_dog_qnexus) ;
+ else
+ {
+ if (vty_watch_dog.thread != NULL)
+ thread_cancel (vty_watch_dog.thread);
+ vty_watch_dog.thread = thread_add_timer (vty_master,
+ vty_watch_dog_thread, NULL, vty_watch_dog_interval) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Scan the death watch list.
+ *
+ * A vty may finally be freed if it is closed and there is no command in
+ * progress.
+ */
+static bool
+uty_death_watch_scan(void)
+{
+ vty_io vio ;
+ vty_io next ;
+
+ next = vio_death_watch ;
+ while (next != NULL)
+ {
+ vio = next ;
+ next = sdl_next(vio, vio_list) ;
+
+ if (vio->closed && !vio->cmd_in_progress)
+ {
+ uty_close(vio) ; /* closes again to ensure that all buffers
+ are released. */
+
+ sdl_del(vio_death_watch, vio, vio_list) ;
+
+ XFREE(MTYPE_VTY, vio->vty) ;
+ XFREE(MTYPE_VTY, vio) ;
+ } ;
+ } ;
+
+ return (vio_death_watch == NULL) ;
+} ;
+
/*==============================================================================
* Prototypes.
*/
-static void uty_file_init_new(vio_file file, int fd, void* info) ;
-static void uty_file_half_close(vio_file file) ;
-static void uty_file_close(vio_file file) ;
+static void uty_sock_init_new(vio_sock sock, int fd, void* info) ;
+static void uty_sock_half_close(vio_sock sock) ;
+static void uty_sock_close(vio_sock sock) ;
static void vty_read_qnexus (qps_file qf, void* file_info) ;
static void vty_write_qnexus (qps_file qf, void* file_info) ;
@@ -253,6 +337,8 @@ static int vty_timer_thread (struct thread *thread) ;
static void vtysh_read_qnexus (qps_file qf, void* file_info) ;
static int vtysh_read_thread (struct thread *thread) ;
+static enum vty_readiness uty_write(vty_io vio) ;
+
/*==============================================================================
* Creation and destruction of VTY objects
*/
@@ -260,109 +346,151 @@ static int vtysh_read_thread (struct thread *thread) ;
/*------------------------------------------------------------------------------
* Allocate new vty struct
*
- * Allocates and initialises vty_io structure, complete with:
+ * Allocates and initialises basic vty and vty_io structures, setting the
+ * given type.
*
- * Output buffer
- * Input buffer
- * qpselect file -- added to CLI nexus ) if running CLI nexus
- * qtimer )
+ * Note that where is not setting up a vty_sock, this *may* be called from
+ * any thread.
*
- * Adds to the known vty's -- which locks/unlocks momentarily.
+ * NB: may not create a VTY_CONFIG_WRITE type vty directly
+ *
+ * see: vty_open_config_write() and vty_close_config_write()
+ *
+ * NB: the sock_fd *must* be valid for VTY_TERM and VTY_SHELL_SERV.
+ * (So MUST be in the CLI thread to set those up !)
+ *
+ * the sock_fd is ignored for everything else.
*
* Returns: new vty
*/
extern struct vty *
-uty_new (int fd, enum vty_type type)
+uty_new(enum vty_type type, int sock_fd)
{
struct vty *vty ;
struct vty_io* vio ;
VTY_ASSERT_LOCKED() ;
- if (vty_watch_dog.anon == NULL)
- uty_watch_dog_start() ;
+ /* If this is a VTY_TERM or a VTY_SHELL, place */
+ switch (type)
+ {
+ case VTY_TERM: /* Require fd -- Telnet session */
+ case VTY_SHELL_SERV: /* Require fd -- Unix socket */
+ assert(sock_fd >= 0) ;
+ break ;
+
+ case VTY_CONFIG_WRITE:
+ zabort("may not make a new VTY_CONFIG_WRITE VTY") ;
+ break ;
+ case VTY_CONFIG_READ:
+ case VTY_STDOUT:
+ case VTY_STDERR:
+ case VTY_SHELL:
+ sock_fd = -1 ; /* No fd -- output to stdout/stderr */
+ break ;
+
+ default:
+ zabort("unknown VTY type") ;
+ } ;
+
+ /* Basic allocation */
vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
vio = XCALLOC (MTYPE_VTY, sizeof (struct vty_io)) ;
vty->vio = vio ;
vio->vty = vty ;
- /* Zeroising the vty structure has set:
- *
- * node = 0 TODO: something better for node value ????
- * buf = NULL -- no command line, yet
- * index = NULL -- nothing, yet
- * index_sub = NULL -- nothing, yet
- */
- confirm(AUTH_NODE == 0) ; /* default node type */
-
- if (type == VTY_TERM)
- vty->newline = "\r\n" ;
- else
- vty->newline = "\n" ;
-
/* Zeroising the vty_io structure has set:
*
+ * name = NULL -- no name, yet
+ *
* vio_list both pointers NULL
+ * mon_list both pointers NULL
*
- * half_closed = 0 -- NOT half closed (important !)
- * timed_out = 0 -- NOT timed out
+ * half_closed = 0 -- NOT half closed (important !)
+ * closed = 0 -- NOT closed (important !)
+ * close_reason = NULL -- no reason, yet
*
- * mon_list both pointers NULL
+ * real_type = 0 -- not material
+ * file_fd = 0 -- not material
+ * file_error = 0 -- not material
*
- * name = NULL -- no name, yet
+ * key_stream = NULL -- no key stream (always empty, at EOF)
*
- * cli_drawn = 0 -- not drawn
+ * cli_drawn = 0 -- not drawn
+ * cli_dirty = 0 -- not dirty
* cli_prompt_len = 0 )
- * cli_extra_len = 0 ) not material
+ * cli_extra_len = 0 ) not material
* cli_echo_suppress = 0 )
*
- * cli_prompt_node = 0 -- not material
- * cli_prompt_set = 0 -- so prompt needs to be constructed
+ * cli_prompt_node = 0 -- not material
+ * cli_prompt_set = 0 -- so prompt needs to be constructed
+ *
+ * cli_blocked = 0 -- not blocked
+ * cmd_in_progress = 0 -- no command in progress
+ * cmd_out_enabled = 0 -- command output is disabled
+ * cli_wait_more = 0 -- not waiting for response to "--more--"
+ *
+ * cli_more_enabled = 0 -- not enabled for "--more--"
*
- * cli_blocked = 0 -- not blocked
- * cmd_in_progress = 0 -- no command in progress
+ * cmd_out_done = 0 -- not material
*
- * cli_do = 0 = cli_do_nothing
+ * cli_do = 0 == cli_do_nothing
*
- * cmd_wait_more = 0 -- not waiting for response to "--more--"
+ * cmd_lc = NULL -- no line control
*
- * fail = 0 -- no login failures yet
+ * fail = 0 -- no login failures yet
*
* hist = empty vector
- * hp = 0 -- at the beginning
- * hindex = 0 -- the beginning
+ * hp = 0 -- at the beginning
+ * hindex = 0 -- the beginning
*
- * width = 0 -- unknown console width
- * height = 0 -- unknown console height
+ * width = 0 -- unknown console width
+ * height = 0 -- unknown console height
*
- * lines = 0 -- unset
+ * lines = 0 -- no limit
+ * lines_set = 0 -- no explicit setting
*
- * monitor = 0 -- not a monitor
+ * monitor = 0 -- not a monitor
+ * monitor_busy = 0 -- not a busy monitor
*
- * config = 0 -- not holder of "config" mode
+ * config = 0 -- not holder of "config" mode
*/
confirm(cli_do_nothing == 0) ;
+ confirm(AUTH_NODE == 0) ; /* default node type */
vio->type = type ;
- uty_file_init_new(&vio->file, fd, vio) ;
-
- vio->key_stream = keystroke_stream_new('\0') ; /* TODO: CSI ?? */
+ /* Zeroising the vty structure has set:
+ *
+ * node = 0 TODO: something better for node value ????
+ * buf = NULL -- no command line, yet
+ * parsed = NULL -- no parsed command, yet
+ * lineno = 0 -- nothing read, yet
+ * index = NULL -- nothing, yet
+ * index_sub = NULL -- nothing, yet
+ */
+ if (type == VTY_TERM)
+ vty->newline = "\n" ; /* line control looks after "\r\n" */
+ else
+ vty->newline = "\n" ;
- qs_init_new(&vio->ibuf, 0) ;
+ /* Initialise the vio_sock, */
+ uty_sock_init_new(&vio->sock, sock_fd, vio) ;
+ /* Make sure all buffers etc. are initialised clean and empty.
+ *
+ * Note that no buffers are actually allocated at this stage.
+ */
qs_init_new(&vio->cli_prompt_for_node, 0) ;
qs_init_new(&vio->cl, 0) ;
qs_init_new(&vio->clx, 0) ;
- qs_init_new(&vio->cli_vbuf, 0) ;
- vio_fifo_init_new(&vio->cli_obuf, 4 * 1024) ; /* allocate in 4K lumps */
+ vio_fifo_init_new(&vio->cli_obuf, 2 * 1024) ; /* allocate in 2K lumps */
- qs_init_new(&vio->cmd_vbuf, 0) ;
- vio_fifo_init_new(&vio->cmd_obuf, 16 * 1024) ;
+ vio_fifo_init_new(&vio->cmd_obuf, 8 * 1024) ;
/* Place on list of known vio/vty */
sdl_push(vio_list_base, vio, vio_list) ;
@@ -376,29 +504,34 @@ uty_new (int fd, enum vty_type type)
* Returns: new vty
*/
static struct vty *
-uty_new_term(int vty_sock, union sockunion *su)
+uty_new_term(int sock_fd, union sockunion *su)
{
struct vty *vty ;
vty_io vio ;
+ enum vty_readiness ready ;
VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
/* Allocate new vty structure and set up default values. */
- vty = uty_new (vty_sock, VTY_TERM) ;
+ vty = uty_new (VTY_TERM, sock_fd) ;
vio = vty->vio ;
- /* Set the action functions */
+ /* Allocate and initialise a keystroke stream TODO: CSI ?? */
+ vio->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, vio) ;
+
+ /* Set the socket action functions */
if (vty_cli_nexus)
{
- vio->file.action.read.qnexus = vty_read_qnexus ;
- vio->file.action.write.qnexus = vty_write_qnexus ;
- vio->file.action.timer.qnexus = vty_timer_qnexus ;
+ vio->sock.action.read.qnexus = vty_read_qnexus ;
+ vio->sock.action.write.qnexus = vty_write_qnexus ;
+ vio->sock.action.timer.qnexus = vty_timer_qnexus ;
}
else
{
- vio->file.action.read.thread = vty_read_thread ;
- vio->file.action.write.thread = vty_write_thread ;
- vio->file.action.timer.thread = vty_timer_thread ;
+ vio->sock.action.read.thread = vty_read_thread ;
+ vio->sock.action.write.thread = vty_write_thread ;
+ vio->sock.action.timer.thread = vty_timer_thread ;
} ;
/* The text form of the address identifies the VTY */
@@ -418,41 +551,38 @@ uty_new_term(int vty_sock, union sockunion *su)
vty->node = AUTH_NODE;
/* Pick up current timeout setting */
- vio->file.v_timeout = vty_timeout_val;
+ vio->sock.v_timeout = vty_timeout_val;
- /* Use global 'lines' setting, otherwise is unset */
- if (host.lines >= 0)
- vio->lines = host.lines;
- else
- vio->lines = -1;
+ /* Use global 'lines' setting, as default. May be -1 => unset */
+ vio->lines = host.lines ;
- /* Setting up terminal. */
- uty_will_echo (vio);
- uty_will_suppress_go_ahead (vio);
- uty_dont_linemode (vio);
- uty_do_window_size (vio);
- if (0)
- uty_dont_lflow_ahead (vio) ;
+ /* For VTY_TERM use vio_line_control for '\n' and "--more--" */
+ vio->cmd_lc = vio_lc_init_new(NULL, 0, 0) ;
+ uty_set_height(vio) ; /* set initial state */
- /* Set CLI into state waiting for output to complete. */
- vio->cli_blocked = 1 ;
- uty_file_set_write(&vio->file, on) ;
+ /* Initialise the CLI, ready for start-up messages etc. */
+ uty_cli_init(vio) ;
/* Reject connection if password isn't set, and not "no password" */
if ((host.password == NULL) && (host.password_encrypt == NULL)
&& ! no_password_check)
{
- uty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
- uty_half_close (vio);
- return NULL;
+ uty_half_close (vio, "Vty password is not set.");
+ vty = NULL;
}
+ else
+ {
+ /* Say hello to the world. */
+ vty_hello (vty);
- /* Say hello to the world. */
- vty_hello (vty);
-
- if (! no_password_check)
- uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
+ if (! no_password_check)
+ uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
VTY_NEWLINE, VTY_NEWLINE);
+ } ;
+
+ /* Now start the CLI and set a suitable state of readiness */
+ ready = uty_cli_start(vio) ;
+ uty_sock_set_readiness(&vio->sock, ready) ;
return vty;
} ;
@@ -463,7 +593,7 @@ uty_new_term(int vty_sock, union sockunion *su)
* Returns: new vty
*/
static struct vty *
-uty_new_shell_serv(int vty_sock)
+uty_new_shell_serv(int sock_fd)
{
struct vty *vty ;
vty_io vio ;
@@ -471,30 +601,27 @@ uty_new_shell_serv(int vty_sock)
VTY_ASSERT_LOCKED() ;
/* Allocate new vty structure and set up default values. */
- vty = uty_new (vty_sock, VTY_SHELL_SERV) ;
+ vty = uty_new (VTY_SHELL_SERV, sock_fd) ;
vio = vty->vio ;
/* Set the action functions */
if (vty_cli_nexus)
{
- vio->file.action.read.qnexus = vtysh_read_qnexus ;
- vio->file.action.write.qnexus = vty_write_qnexus ;
- vio->file.action.timer.qnexus = NULL ;
+ vio->sock.action.read.qnexus = vtysh_read_qnexus ;
+ vio->sock.action.write.qnexus = vty_write_qnexus ;
+ vio->sock.action.timer.qnexus = NULL ;
}
else
{
- vio->file.action.read.thread = vtysh_read_thread ;
- vio->file.action.write.thread = vty_write_thread ;
- vio->file.action.timer.thread = NULL ;
+ vio->sock.action.read.thread = vtysh_read_thread ;
+ vio->sock.action.write.thread = vty_write_thread ;
+ vio->sock.action.timer.thread = NULL ;
} ;
vty->node = VIEW_NODE;
- /* Enable the command output to clear the output to date, and set cli
- * state to blocked waiting for that output to complete.
- */
- vio->cli_blocked = 1 ;
- uty_file_set_write(&vio->file, on) ;
+ /* Kick start the CLI etc. */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
return vty;
} ;
@@ -512,7 +639,7 @@ uty_set_monitor(vty_io vio, bool on)
if (on && !vio->monitor)
{
- if ((vio->type == VTY_TERM) && vio->file.write_open)
+ if ((vio->type == VTY_TERM) && vio->sock.write_open)
{
vio->monitor = 1 ;
sdl_push(vio_monitors_base, vio, mon_list) ;
@@ -539,50 +666,83 @@ uty_get_name(vty_io vio)
/*------------------------------------------------------------------------------
* Closing down VTY for reading.
*
- * Shuts the read side and discards any buffered input.
+ * For VTY_TERM (must be in CLI thread):
+ *
+ * * shut the socket for reading
+ * * discard all buffered input, setting it to "EOF"
+ * * turns off any monitor status !
+ * * drop down to RESTRICTED_NODE
+ *
+ * For VTY_SHELL_SERV (must be in CLI thread):
+ *
+ * * shut the socket for reading
+ * * discard all buffered input
+ * * drop down to RESTRICTED_NODE
*
- * Leaves the output running, but places the VTY on "death watch". When
- * all output completes and there is no queued command or anything else
- * active, the VTY is finally put to sleep.
+ * In all cases:
+ *
+ * * place on death watch
+ * * set the vty half_closed
+ * * sets the reason for closing (if any given)
+ *
+ * For VTY_TERM and VTY_SHELL_SERV, when the output side has emptied out all
+ * the buffers, the VTY is closed.
+ *
+ * May already have set the vio->close_reason, or can set it now. (Passing a
+ * NULL reason has no effect on any existing posted reason.)
*/
extern void
-uty_half_close (vty_io vio)
+uty_half_close (vty_io vio, const char* reason)
{
- char* line ;
-
VTY_ASSERT_LOCKED() ;
if (vio->half_closed)
- return ; /* cannot do it again */
-
- vio->half_closed = 1 ;
+ return ;
- uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio->file.fd) ;
+ if (reason != NULL)
+ vio->close_reason = reason ;
- uty_file_half_close(&vio->file) ;
+ /* Do the file side of things
+ *
+ * Note that half closing the file sets a new timeout, sets read off
+ * and write on.
+ */
+ uty_sock_half_close(&vio->sock) ;
uty_set_monitor(vio, 0) ;
- keystroke_stream_free(vio->key_stream) ;
- qs_free_body(&vio->ibuf) ;
+ /* Discard everything in the keystroke stream and force it to EOF */
+ if (vio->key_stream != NULL)
+ keystroke_stream_set_eof(vio->key_stream) ;
- uty_cli_wipe(vio) ;
-
- while ((line = vector_ream_keep(&vio->hist)) != NULL)
- XFREE(MTYPE_VTY_HIST, line) ;
+ /* Turn off "--more--" so that all output clears without interruption.
+ *
+ * Note that if is waiting for "--more--", then shutting the read side
+ * causes it to be readable, but EOF -- so that will flush through.
+ */
+ vio->cli_more_enabled = 0 ;
- /* Hit the width, height and lines so that all output clears without
- * interruption.
+ /* If a command is not in progress, enable output, which will clear
+ * the output buffer if there is anything there, plus any close reason,
+ * and then close.
+ *
+ * If command is in progress, then this process will start when it
+ * completes.
*/
- vio->width = 0 ;
- vio->height = 0 ;
- vio->lines = 0 ;
+ if (!vio->cmd_in_progress)
+ vio->cmd_out_enabled = 1 ;
/* Make sure no longer holding the config symbol of power */
- uty_config_unlock(vio->vty, AUTH_NODE) ;
+ uty_config_unlock(vio->vty, RESTRICTED_NODE) ;
+
+ /* Log closing of VTY_TERM */
+ if (vio->type == VTY_TERM)
+ uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio->sock.fd) ;
/* Move to the death watch list */
sdl_del(vio_list_base, vio, vio_list) ;
sdl_push(vio_death_watch, vio, vio_list) ;
+
+ vio->half_closed = 1 ;
} ;
/*------------------------------------------------------------------------------
@@ -590,118 +750,185 @@ uty_half_close (vty_io vio)
*
* Shuts down everything and discards all buffers etc. etc.
*
- * If not already on it, places the VTY on "death watch". When there is no
- * queued command or anything else active, the VTY is finally put to sleep.
+ * If cmd_in_progress, cannot complete the process -- but sets the closed
+ * flag.
+ *
+ * Can call vty_close() any number of times.
+ *
+ * The vty structure is placed on death watch, which will finally free the
+ * structure once no longer cmd_in_progress.
*/
extern void
uty_close (vty_io vio)
{
VTY_ASSERT_LOCKED() ;
- uty_file_close(&vio->file) ; /* bring the file to a complete stop */
+ /* Empty all the output buffers */
+ vio_fifo_reset_keep(&vio->cli_obuf) ;
+ vio_fifo_reset_keep(&vio->cmd_obuf) ;
+ vio->cmd_lc = vio_lc_reset_free(vio->cmd_lc) ;
+
+ /* If not already closed, close. */
+ if (!vio->closed)
+ {
+ uty_half_close(vio, NULL) ; /* place on death watch -- if not
+ already done */
+ uty_cli_close(vio) ; /* tell the CLI to stop */
+
+ vio->closed = 1 ; /* now closed (stop uty_write()
+ from recursing) */
+
+ if (vio->sock.write_open)
+ uty_write(vio) ; /* last gasp attempt */
- uty_half_close(vio) ; /* deal with the input side, and place on
- death watch -- if not already done */
+ uty_sock_close(&vio->sock) ;
+
+ } ;
+
+ /* Nothing more should happen, so can now release almost everything,
+ * the exceptions being the things that are related to a cmd_in_progress.
+ *
+ * All writing to buffers is suppressed, and as the sock has been closed,
+ * there will be no more read_ready or write_ready events.
+ */
+ if (vio->name != NULL)
+ XFREE(MTYPE_VTY_NAME, vio->name) ;
+
+ vio->key_stream = keystroke_stream_free(vio->key_stream) ;
qs_free_body(&vio->cli_prompt_for_node) ;
qs_free_body(&vio->cl) ;
- qs_free_body(&vio->clx) ;
- qs_free_body(&vio->cli_vbuf) ;
- qs_free_body(&vio->cmd_vbuf) ;
- vio_fifo_reset_keep(&vio->cli_obuf) ;
- vio_fifo_reset_keep(&vio->cmd_obuf) ;
+ {
+ qstring line ;
+ while ((line = vector_ream_keep(&vio->hist)) != NULL)
+ qs_reset_free(line) ;
+ } ;
- vio->vty->buf = NULL ;
+ /* The final stage cannot be completed if cmd_in_progress.
+ *
+ * The clx is pointed at by vty->buf -- containing the current command.
+ *
+ * Once everything is released, can take the vty off death watch, and
+ * release the vio and the vty.
+ */
+ if (!vio->cmd_in_progress)
+ {
+ qs_free_body(&vio->clx) ;
+ vio->vty->buf = NULL ;
+ } ;
} ;
+/*==============================================================================
+ * For writing configuration file by command, temporarily redirect output to
+ * an actual file.
+ */
+
/*------------------------------------------------------------------------------
- * Closing down VTY completely.
- *
- * Shuts down everything and discards all buffers etc. etc.
- *
- * If not already on it, places the VTY on "death watch". When there is no
- * queued command or anything else active, the VTY is finally put to sleep.
+ * Set the given fd as the VTY_FILE output.
*/
extern void
-uty_full_close (vty_io vio)
+vty_open_config_write(struct vty* vty, int fd)
{
- VTY_ASSERT_LOCKED() ;
+ vty_io vio ;
- uty_file_close(&vio->file) ; /* bring the file to a complete stop */
+ VTY_LOCK() ;
- uty_half_close(vio) ; /* deal with the input side, and place on
- death watch -- if not already done */
+ vio = vty->vio ;
- qs_free_body(&vio->cli_prompt_for_node) ;
- qs_free_body(&vio->cl) ;
- qs_free_body(&vio->clx) ;
- qs_free_body(&vio->cli_vbuf) ;
- qs_free_body(&vio->cmd_vbuf) ;
+ assert((vio->type != VTY_CONFIG_WRITE) && (vio->type != VTY_NONE)) ;
- vio_fifo_reset_keep(&vio->cli_obuf) ;
- vio_fifo_reset_keep(&vio->cmd_obuf) ;
+ vio->real_type = vio->type ;
+
+ vio->type = VTY_CONFIG_WRITE ;
+ vio->file_fd = fd ;
+ vio->file_error = 0 ;
- vio->vty->buf = NULL ;
+ VTY_UNLOCK() ;
} ;
-/*==============================================================================
- * Dealing with am I/O error on VTY
- *
- * If this is the first error for this VTY, produce suitable log message.
+/*------------------------------------------------------------------------------
+ * Write away configuration file stuff -- all or just the full lump(s).
*
- * If is a "monitor", turn that off, *before* issuing log message.
+ * Returns: > 0 => blocked
+ * 0 => all gone (up to last lump if !all)
+ * < 0 => failed -- see vio->file_error
*/
static int
-uty_io_error(vty_io vio, const char* what)
+uty_config_write(vty_io vio, bool all)
{
- /* can no longer be a monitor ! */
- uty_set_monitor(vio, 0) ;
+ int ret ;
- /* if this is the first error, log it */
- if (vio->file.error_seen == 0)
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->file_error == 0)
{
- const char* type ;
- switch (vio->type)
- {
- case VTY_TERM:
- type = "VTY Terminal" ;
- break ;
- case VTY_SHELL_SERV:
- type = "VTY Shell Server" ;
- break ;
- default:
- zabort("unknown VTY type for uty_io_error()") ;
- } ;
+ ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->file_fd, all) ;
- vio->file.error_seen = errno ;
- uzlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
- type, what, vio->file.fd, safe_strerror(vio->file.error_seen)) ;
- } ;
+ if (ret < 0)
+ vio->file_error = errno ;
+ }
+ else
+ ret = -1 ;
- return -1 ;
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write away any pending stuff, and return the VTY to normal.
+ */
+extern int
+vty_close_config_write(struct vty* vty)
+{
+ vty_io vio ;
+ int err ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
+ assert((vio->type == VTY_CONFIG_WRITE) && (vio->real_type != VTY_NONE)) ;
+
+ uty_config_write(vio, true) ; /* write all that is left */
+
+ err = vio->file_error ;
+
+ vio->type = vio->real_type ;
+ vio->file_fd = -1 ;
+ vio->file_error = 0 ;
+
+ VTY_UNLOCK() ;
+
+ return err ;
} ;
/*==============================================================================
- * vio_file level operations
+ * vio_sock level operations
*/
/*------------------------------------------------------------------------------
- * Initialise a new vio_file structure.
+ * Initialise a new vio_sock structure.
*
- * Requires that: the vio_file structure is not currently in use.
+ * Requires that: the vio_sock structure is not currently in use.
*
- * if fd >= 0 then: file is open and ready read and write
- * otherwise: file is not open
+ * if fd >= 0 then: sock is open and ready read and write
+ * otherwise: sock is not open
*
* there are no errors, yet.
*
* Sets timeout to no timeout at all -- timeout is optional.
+ *
+ * NB: MUST be in the CLI thread if the fd is >= 0 !
*/
static void
-uty_file_init_new(vio_file file, int fd, void* info)
+uty_sock_init_new(vio_sock sock, int fd, void* info)
{
- memset(file, 0, sizeof(struct vio_file)) ;
+ VTY_ASSERT_LOCKED() ;
+
+ if (fd >= 0)
+ VTY_ASSERT_CLI_THREAD() ;
+
+ memset(sock, 0, sizeof(struct vio_sock)) ;
/* Zeroising the structure has set:
*
@@ -718,17 +945,16 @@ uty_file_init_new(vio_file file, int fd, void* info)
* t_timer = NULL -- no timer thread, yet
* qtr = NULL -- no qtimer, yet
*/
- file->fd = fd ;
- file->info = info ;
+ sock->fd = fd ;
+ sock->info = info ;
- file->read_open = (fd >= 0) ;
- file->write_open = (fd >= 0) ;
+ sock->read_open = (fd >= 0) ;
+ sock->write_open = (fd >= 0) ;
- if (vty_cli_nexus)
+ if ((fd >= 0) && vty_cli_nexus)
{
- file->qf = qps_file_init_new(NULL, NULL);
- if (fd >= 0)
- qps_add_file(vty_cli_nexus->selection, file->qf, file->fd, file->info);
+ sock->qf = qps_file_init_new(NULL, NULL);
+ qps_add_file(vty_cli_nexus->selection, sock->qf, sock->fd, sock->info);
} ;
} ;
@@ -740,250 +966,368 @@ uty_file_init_new(vio_file file, int fd, void* info)
* If no timeout time is set, and the timer is running, unset it.
*/
static void
-uty_file_restart_timer(vio_file file)
+uty_sock_restart_timer(vio_sock sock)
{
- if (file->v_timeout != 0)
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->v_timeout != 0)
{
- assert(file->action.timer.anon != NULL) ;
+ assert(sock->action.timer.anon != NULL) ;
if (vty_cli_nexus)
{
- if (file->qtr == NULL) /* allocate qtr if required */
- file->qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
- NULL, file->info) ;
- qtimer_set(file->qtr, qt_add_monotonic(QTIME(file->v_timeout)),
- file->action.timer.qnexus) ;
+ if (sock->qtr == NULL) /* allocate qtr if required */
+ sock->qtr = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ NULL, sock->info) ;
+ qtimer_set(sock->qtr, qt_add_monotonic(QTIME(sock->v_timeout)),
+ sock->action.timer.qnexus) ;
}
else
{
- if (file->t_timer != NULL)
- thread_cancel (file->t_timer);
- file->t_timer = thread_add_timer (vty_master,
- file->action.timer.thread, file->info, file->v_timeout) ;
+ if (sock->t_timer != NULL)
+ thread_cancel (sock->t_timer);
+ sock->t_timer = thread_add_timer (vty_master,
+ sock->action.timer.thread, sock->info, sock->v_timeout) ;
} ;
- file->timer_running = 1 ;
+ sock->timer_running = 1 ;
}
- else if (file->timer_running)
+ else if (sock->timer_running)
{
if (vty_cli_nexus)
{
- if (file->qtr != NULL)
- qtimer_unset(file->qtr) ;
+ if (sock->qtr != NULL)
+ qtimer_unset(sock->qtr) ;
}
else
{
- if (file->t_timer != NULL)
- thread_cancel (file->t_timer) ;
+ if (sock->t_timer != NULL)
+ thread_cancel (sock->t_timer) ;
} ;
- file->timer_running = 0 ;
+ sock->timer_running = 0 ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Set a new timer value.
+ * Set read on/off
+ *
+ * Returns: the on/off state set
*/
-extern void
-uty_file_set_timer(vio_file file, unsigned long timeout)
+static bool
+uty_sock_set_read(vio_sock sock, bool on)
{
- file->v_timeout = timeout ;
- if (file->timer_running)
- uty_file_restart_timer(file) ;
-} ;
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
-/*------------------------------------------------------------------------------
- * Set read on/off -- restart timer.
- */
-extern void
-uty_file_set_read(vio_file file, bool on)
-{
- if (file->fd < 0)
- return ;
+ if (sock->fd < 0)
+ return 0 ;
if (on)
{
- assert(file->action.read.anon != NULL) ;
+ assert(sock->action.read.anon != NULL) ;
if (vty_cli_nexus)
- {
- qps_enable_mode(file->qf, qps_read_mnum, file->action.read.qnexus) ;
- }
+ qps_enable_mode(sock->qf, qps_read_mnum, sock->action.read.qnexus) ;
else
{
- if (file->t_read != NULL)
- thread_cancel(file->t_read) ;
+ if (sock->t_read != NULL)
+ thread_cancel(sock->t_read) ;
- file->t_read = thread_add_read(vty_master,
- file->action.read.thread, file->info, file->fd) ;
+ sock->t_read = thread_add_read(vty_master,
+ sock->action.read.thread, sock->info, sock->fd) ;
} ;
}
else
{
if (vty_cli_nexus)
- {
- qps_disable_modes(file->qf, qps_read_mbit) ;
- }
+ qps_disable_modes(sock->qf, qps_read_mbit) ;
else
{
- if (file->t_read != NULL)
- thread_cancel (file->t_read) ;
+ if (sock->t_read != NULL)
+ thread_cancel (sock->t_read) ;
} ;
} ;
- uty_file_restart_timer(file) ;
+ return on ;
} ;
/*------------------------------------------------------------------------------
- * Set write on/off -- restart timer.
+ * Set write on/off
+ *
+ * Returns: the on/off state set
*/
-extern void
-uty_file_set_write(vio_file file, bool on)
+static bool
+uty_sock_set_write(vio_sock sock, bool on)
{
- if (file->fd < 0)
- return ;
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ if (sock->fd < 0)
+ return 0 ;
if (on)
{
- assert(file->action.write.anon != NULL) ;
+ assert(sock->action.write.anon != NULL) ;
if (vty_cli_nexus)
- {
- qps_enable_mode(file->qf, qps_write_mnum, file->action.write.qnexus) ;
- }
+ qps_enable_mode(sock->qf, qps_write_mnum, sock->action.write.qnexus) ;
else
{
- if (file->t_write != NULL)
- thread_cancel(file->t_write) ;
+ if (sock->t_write != NULL)
+ thread_cancel(sock->t_write) ;
- file->t_write = thread_add_write(vty_master,
- file->action.write.thread, file->info, file->fd) ;
+ sock->t_write = thread_add_write(vty_master,
+ sock->action.write.thread, sock->info, sock->fd) ;
} ;
}
else
{
if (vty_cli_nexus)
- {
- qps_disable_modes(file->qf, qps_write_mbit) ;
- }
+ qps_disable_modes(sock->qf, qps_write_mbit) ;
else
{
- if (file->t_write != NULL)
- thread_cancel (file->t_write) ;
+ if (sock->t_write != NULL)
+ thread_cancel (sock->t_write) ;
} ;
} ;
- uty_file_restart_timer(file) ;
+ return on ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set read/write readiness -- for VTY_TERM
+ *
+ * Note that for VTY_TERM, set only one of read or write, and sets write for
+ * preference.
+ */
+extern void
+uty_sock_set_readiness(vio_sock sock, enum vty_readiness ready)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ uty_sock_set_read(sock, (ready == read_ready)) ;
+ uty_sock_set_write(sock, (ready >= write_ready)) ;
} ;
/*------------------------------------------------------------------------------
- * Close given vty file for reading.
+ * Set a new timer value.
+ */
+extern void
+uty_sock_set_timer(vio_sock sock, unsigned long timeout)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
+
+ sock->v_timeout = timeout ;
+ if (sock->timer_running)
+ uty_sock_restart_timer(sock) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close given vty sock for reading.
*
* Sets timer to timeout for clearing any pending output.
+ *
+ * NB: if there is a socket, MUST be in the CLI thread
*/
static void
-uty_file_half_close(vio_file file)
+uty_sock_half_close(vio_sock sock)
{
VTY_ASSERT_LOCKED() ;
- if (file->fd >= 0)
- {
- shutdown(file->fd, SHUT_RD) ; /* actual half close */
+ sock->read_open = 0 ; /* make sure */
- file->v_timeout = 30 ; /* for output to clear */
- uty_file_set_read(file, off) ;
- } ;
+ if (sock->fd < 0)
+ return ; /* nothing more if no socket */
+
+ VTY_ASSERT_CLI_THREAD() ;
- file->read_open = 0 ;
+ shutdown(sock->fd, SHUT_RD) ; /* actual half close */
+
+ uty_sock_set_read(sock, off) ;
+ uty_sock_set_write(sock, on) ;
+ sock->v_timeout = 30 ; /* for output to clear */
+ uty_sock_restart_timer(sock) ;
} ;
/*------------------------------------------------------------------------------
- * Close given vio_file, completely -- shut down any timer.
+ * Close given vio_sock, completely -- shut down any timer.
*
* Structure is cleared of everything except the last error !
+ *
+ * NB: if there is a socket, MUST be in the CLI thread
*/
static void
-uty_file_close(vio_file file)
+uty_sock_close(vio_sock sock)
{
VTY_ASSERT_LOCKED() ;
- if (file->fd >= 0)
- close(file->fd) ;
+ sock->read_open = 0 ; /* make sure */
+ sock->write_open = 0 ;
- if (vty_cli_nexus && (file->fd >= 0))
- qps_remove_file(file->qf) ;
+ if (sock->fd < 0)
+ {
+ assert( (sock->qf == NULL)
+ && (sock->qtr == NULL)
+ && (sock->t_read == NULL)
+ && (sock->t_write == NULL)
+ && (sock->t_timer == NULL) ) ;
+ return ; /* no more to be done here */
+ } ;
- if (file->qf != NULL)
- qps_file_free(file->qf) ;
+ VTY_ASSERT_CLI_THREAD() ;
+ close(sock->fd) ;
- if (file->t_read != NULL)
- thread_cancel(file->t_write) ;
- if (file->t_write != NULL)
- thread_cancel(file->t_write) ;
+ if (vty_cli_nexus)
+ {
+ assert((sock->qf != NULL) && (sock->fd == qps_file_fd(sock->qf))) ;
+ qps_remove_file(sock->qf) ;
+ qps_file_free(sock->qf) ;
+ sock->qf = NULL ;
+ } ;
- file->fd = -1 ;
- file->qf = NULL ;
- file->t_read = NULL ;
- file->t_write = NULL ;
+ sock->fd = -1 ;
- file->info = NULL ;
- file->action.read.anon = NULL ;
- file->action.write.anon = NULL ;
- file->action.timer.anon = NULL ;
+ if (sock->t_read != NULL)
+ thread_cancel(sock->t_write) ;
+ if (sock->t_write != NULL)
+ thread_cancel(sock->t_write) ;
- file->read_open = 0 ;
- file->write_open = 0 ;
+ sock->t_read = NULL ;
+ sock->t_write = NULL ;
- if (file->qtr != NULL)
- qtimer_free(file->qtr) ;
- if (file->t_timer != NULL)
- thread_cancel(file->t_timer) ;
+ sock->info = NULL ;
+ sock->action.read.anon = NULL ;
+ sock->action.write.anon = NULL ;
+ sock->action.timer.anon = NULL ;
- file->v_timeout = 0 ;
- file->qtr = NULL ;
- file->t_timer = NULL ;
+ if (sock->qtr != NULL)
+ qtimer_free(sock->qtr) ;
+ if (sock->t_timer != NULL)
+ thread_cancel(sock->t_timer) ;
+
+ sock->v_timeout = 0 ;
+ sock->qtr = NULL ;
+ sock->t_timer = NULL ;
} ;
-/*==============================================================================
- * Reading from the VTY_TERM type file.
+/*------------------------------------------------------------------------------
+ * Dealing with an I/O error on VTY socket
*
- * The select/pselect call-back ends up in uty_read_ready().
+ * If this is the first error for this VTY, produce suitable log message.
*
- * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
- * current CLI.
+ * If is a "monitor", turn that off, *before* issuing log message.
*/
+static int
+uty_sock_error(vty_io vio, const char* what)
+{
+ VTY_ASSERT_LOCKED() ;
+ VTY_ASSERT_CLI_THREAD() ;
-/*------------------------------------------------------------------------------
- * Ready to read -> kicking CLI
+ /* can no longer be a monitor ! *before* any logging ! */
+ uty_set_monitor(vio, 0) ;
+
+ /* if this is the first error, log it */
+ if (vio->sock.error_seen == 0)
+ {
+ const char* type ;
+ switch (vio->type)
+ {
+ case VTY_TERM:
+ type = "VTY Terminal" ;
+ break ;
+ case VTY_SHELL_SERV:
+ type = "VTY Shell Server" ;
+ break ;
+ default:
+ zabort("unknown VTY type for uty_sock_error()") ;
+ } ;
+
+ vio->sock.error_seen = errno ;
+ uzlog(NULL, LOG_WARNING, "%s: %s failed on fd %d: %s",
+ type, what, vio->sock.fd, safe_strerror(vio->sock.error_seen)) ;
+ } ;
+
+ return -1 ;
+} ;
+
+/*==============================================================================
+ * Readiness and the VTY_TERM type VTY.
*
- * Have two CLI: one (trivial one) when waiting on "--more--",
- * and the standard one.
+ * For VTY_TERM the driving force is write ready. This is used to prompt the
+ * VTY_TERM when there is outstanding output (obviously), but also if there
+ * is buffered input in the keystroke stream.
*
- * End up here when there is something ready to be read.
+ * The VTY_TERM uses read ready only when it doesn't set write ready. Does
+ * not set both at once.
*
- * Also ends up here when was write_ready and did not block in uty_write.
+ * So there is only one, common, uty_ready function, which:
*
- * Will also end up here if an error has occurred, the other end has closed,
- * this end has half closed, etc. This fact is used to kick the CLI even when
- * there is no data to be read.
+ * 1. attempts to clear any output it can.
*
- * Note that nothing is actually read here -- reading is done in the CLI itself,
- * if required.
+ * The state of the output affects the CLI, so must always do this before
+ * before invoking the CLI.
*
- * The CLI decides whether to re-enable read, or enable write, or both.
+ * If this write enters the "--more--" state, then will have tried to
+ * write away the prompt.
+ *
+ * 2. invokes the CLI
+ *
+ * Which will do either the standard CLI stuff or the special "--more--"
+ * stuff.
+ *
+ * 3. attempts to write any output there now is.
+ *
+ * If the CLI generated new output, as much as possible is written away
+ * now.
+ *
+ * If this write enters the "--more--" state, then it returns now_ready,
+ * if the prompt was written away, which loops back to the CLI.
+ *
+ * Note that this is arranging:
+ *
+ * a. to write away the "--more--" prompt as soon as the tranche of output to
+ * which it refers, completes
+ *
+ * b. to enter the cli_more_wait CLI for the first time immediately after the
+ * "--more--" prompt is written away.
+ *
+ * The loop limits itself to one trache of command output each time.
+ *
+ * Resets the timer because something happened.
*/
static void
-uty_read_ready(vty_io vio)
+uty_ready(vty_io vio)
{
- uty_file_set_read(&vio->file, off) ; /* restarts timer */
+ enum vty_readiness ready ;
- /* Execute the required command processor */
- if (vio->cmd_wait_more)
- uty_cli_wait_more(vio) ; /* run "--more--" CLI */
- else
- uty_cli(vio) ; /* run standard CLI */
+ VTY_ASSERT_LOCKED() ;
+
+ vio->cmd_out_done = 0 ; /* not done any command output yet */
+
+ uty_write(vio) ; /* try to clear outstanding stuff */
+ do
+ {
+ ready = uty_cli(vio) ; /* do any CLI work... */
+ ready |= uty_write(vio) ; /* ...and any output that generates */
+ } while (ready >= now_ready) ;
+
+ uty_sock_set_readiness(&vio->sock, ready) ;
+ uty_sock_restart_timer(&vio->sock) ;
} ;
+/*==============================================================================
+ * Reading from VTY_TERM.
+ *
+ * The select/pselect call-back ends up in uty_read_ready().
+ *
+ * Note that uty_write_ready() also calls uty_read_ready, in order to kick the
+ * current CLI.
+ */
+
/*------------------------------------------------------------------------------
* Callback -- qnexus: ready to read -> kicking CLI
*/
@@ -994,9 +1338,9 @@ vty_read_qnexus(qps_file qf, void* file_info)
VTY_LOCK() ;
- assert((vio->file.fd == qf->fd) && (vio == vio->file.info)) ;
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
- uty_read_ready(vio) ;
+ uty_ready(vio) ;
VTY_UNLOCK() ;
}
@@ -1011,10 +1355,10 @@ vty_read_thread(struct thread *thread)
VTY_LOCK() ;
- assert(vio->file.fd == THREAD_FD (thread) && (vio == vio->file.info)) ;
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
- vio->file.t_read = NULL ; /* implicitly */
- uty_read_ready(vio);
+ vio->sock.t_read = NULL ; /* implicitly */
+ uty_ready(vio);
VTY_UNLOCK() ;
return 0 ;
@@ -1035,18 +1379,18 @@ uty_read (vty_io vio, keystroke steal)
unsigned char buf[500] ;
int get ;
- if (!vio->file.read_open)
+ if (!vio->sock.read_open)
return -1 ; /* at EOF if not open */
- get = read_nb(vio->file.fd, buf, sizeof(buf)) ;
- if (get > 0)
+ get = read_nb(vio->sock.fd, buf, sizeof(buf)) ;
+ if (get >= 0)
keystroke_input(vio->key_stream, buf, get, steal) ;
else if (get < 0)
{
if (get == -1)
- uty_io_error(vio, "read") ;
+ uty_sock_error(vio, "read") ;
- vio->file.read_open = 0 ;
+ vio->sock.read_open = 0 ;
keystroke_input(vio->key_stream, NULL, 0, steal) ;
get = -1 ;
@@ -1056,14 +1400,14 @@ uty_read (vty_io vio, keystroke steal)
} ;
/*==============================================================================
- * The write file action for VTY_TERM type VTY
+ * The write sock action for VTY_TERM type VTY
*
* There are two sets of buffering:
*
* cli -- command line -- which reflects the status of the command line
*
- * cmd -- command output -- which is not written to the file while
- * cmd_in_progress.
+ * cmd -- command output -- which is written to the file only while
+ * cmd_out_enabled.
*
* The cli output takes precedence.
*
@@ -1071,41 +1415,8 @@ uty_read (vty_io vio, keystroke steal)
* "--more--" mechanism.
*/
-static bool uty_write(vty_io vio) ;
-static int uty_flush_fifo(vty_io vio, vio_fifo vf,
- struct vty_line_control* line_control) ;
-static void uty_empty_out_fifos(vty_io vio) ;
-
-/*------------------------------------------------------------------------------
- * Flush as much as possible of what there is.
- *
- * May end up with:
- *
- * * something in the buffers waiting to go, but output is currently
- * threatening to block.
- *
- * in this case will have set write on, and things will progress when next
- * write_ready.
- *
- * * otherwise:
- *
- * will be set write off, so does a read_ready in order t kick the CLI,
- * which may wish to set either read or write on.
- */
-static void
-uty_write_ready(vty_io vio)
-{
- bool blocked ;
-
- VTY_ASSERT_LOCKED() ;
-
- uty_file_set_write(&vio->file, off) ; /* restarts timer, too */
-
- blocked = uty_write(vio) ;
-
- if (!blocked)
- uty_read_ready(vio) ;
-} ;
+static int uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
+static int uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc) ;
/*------------------------------------------------------------------------------
* Callback -- qnexus: ready to write -> try to empty buffers
@@ -1117,9 +1428,9 @@ vty_write_qnexus(qps_file qf, void* file_info)
VTY_LOCK() ;
- assert((vio->file.fd == qf->fd) && (vio == vio->file.info)) ;
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
- uty_write_ready(vio) ;
+ uty_ready(vio) ;
VTY_UNLOCK() ;
}
@@ -1134,10 +1445,10 @@ vty_write_thread(struct thread *thread)
VTY_LOCK() ;
- assert(vio->file.fd == THREAD_FD (thread) && (vio == vio->file.info)) ;
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
- vio->file.t_write = NULL; /* implicitly */
- uty_write_ready(vio) ;
+ vio->sock.t_write = NULL; /* implicitly */
+ uty_ready(vio) ;
VTY_UNLOCK() ;
return 0 ;
@@ -1152,154 +1463,337 @@ vty_write_thread(struct thread *thread)
* Note that if !write_open, or becomes !write_open, then the FIFOs are empty
* and all output instantly successful.
*
- * Sets write on if prevented from outputting everything available for output
+ * Sets write on if prevented from writing everything available for output
* by write() threatening to block.
*
- * Sets read on if enters cmd_wait_more state.
- *
- * Returns: true <=> blocked by I/O
- *
- * Note that this means that returns true iff sets write on.
+ * Returns: write_ready if should now set write on
+ * now_ready if should loop back and try again
+ * not_ready otherwise
*/
-static bool
+static enum vty_readiness
uty_write(vty_io vio)
{
int ret ;
VTY_ASSERT_LOCKED() ;
- /* empty the CLI FIFO
- *
- * NB: if the file is !write_open, or if it fails during output here
- * and becomes !write_open, then ret == 0 -- as if everything
- * has been written.
- */
- ret = uty_flush_fifo(vio, &vio->cli_obuf, NULL) ;
- if (ret != 0)
- return 1 ; /* blocked by I/O */
+ ret = -1 ;
+ while (vio->sock.write_open)
+ {
+ /* Any outstanding line control output takes precedence */
+ if (vio->cmd_lc != NULL)
+ {
+ ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+ if (ret != 0)
+ break ;
+ }
- if ((vio->cmd_in_progress) || (vio->cmd_wait_more))
- return 0 ; /* not blocked by I/O */
+ /* Next: empty out the cli output */
+ ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ if (ret != 0)
+ break ;
- /* write from the command FIFO
- *
- * NB: if the file is !write_open, or if it fails during output here
- * and becomes !write_open, then ret == 0 -- as if everything
- * has been written.
- */
- ret = uty_flush_fifo(vio, &vio->cmd_obuf, &vio->line_control) ;
- if (ret == 1)
- return 1 ; /* blocked by I/O */
+ /* Finished now if not allowed to progress the command stuff */
+ if (!vio->cmd_out_enabled)
+ return not_ready ; /* done all can do */
- if (ret == 2)
- {
- /* Want now to wait for "--more--"
+ /* Last: if there is something in the command buffer, do that */
+ if (!vio_fifo_empty(&vio->cmd_obuf))
+ {
+ if (vio->cmd_out_done)
+ break ; /* ...but not if done once */
+
+ vio->cmd_out_done = 1 ; /* done this once */
+
+ assert(!vio->cli_more_wait) ;
+
+ if (vio->cmd_lc != NULL)
+ ret = uty_write_fifo_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+ else
+ ret = vio_fifo_write_nb(&vio->cmd_obuf, vio->sock.fd, true) ;
+
+ /* If moved into "--more--" state@
+ *
+ * * the "--more--" prompt is ready to be written, so do that now
+ *
+ * * if that completes, then want to run the CLI *now* to perform the
+ * first stage of the "--more--" process.
+ */
+ if (vio->cli_more_wait)
+ {
+ ret = vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
+ if (ret == 0)
+ return now_ready ;
+ } ;
+
+ if (ret != 0)
+ break ;
+ }
+
+ /* Exciting stuff: there is nothing left to output...
*
- * Note that this produces CLI output, which must deal with here.
+ * ... watch out for half closed state.
*/
- uty_cli_want_more(vio) ; /* NB: sets cmd_wait_more */
+ if (vio->half_closed)
+ {
+ if (vio->close_reason != NULL)
+ {
+ vio->cmd_in_progress = 1 ; /* TODO: not use vty_out ? */
+
+ struct vty* vty = vio->vty ;
+ if (vio->cli_drawn || vio->cli_dirty)
+ vty_out(vty, VTY_NEWLINE) ;
+ vty_out(vty, "%% %s%s", vio->close_reason, VTY_NEWLINE) ;
- ret = uty_flush_fifo(vio, &vio->cli_obuf, NULL) ;
- if (ret == 1)
- return 1 ; /* blocked by I/O */
+ vio->cmd_in_progress = 0 ;
+
+ vio->close_reason = NULL ; /* MUST discard now... */
+ continue ; /* ... and write away */
+ } ;
+
+ if (!vio->closed) /* avoid recursion */
+ uty_close(vio) ;
+
+ return not_ready ; /* it's all over */
+ } ;
- if (vio->file.write_open)
- return 0 ; /* not blocked by I/O */
+ /* For VTY_TERM: if the command line is not drawn, now is a good
+ * time to do that.
+ */
+ if (vio->type == VTY_TERM)
+ if (uty_cli_draw_if_required(vio))
+ continue ; /* do that now. */
+
+ /* There really is nothing left to output */
+ return not_ready ;
} ;
- /* Reach here iff both CLI and command FIFOs are empty and is not
- * cmd_in_progress
+ /* Arrives here if there is more to do, or failed (or was !write_open) */
+
+ if (ret >= 0)
+ return write_ready ;
+
+ /* If is write_open, then report the error
+ *
+ * If still read_open, let the reader pick up and report the error, when it
+ * has finished anything it has buffered.
*/
- vio->cli_blocked = 0 ;
+ if (vio->sock.write_open)
+ {
+ if (!vio->sock.read_open)
+ uty_sock_error(vio, "write") ;
- return 0 ;
+ vio->sock.write_open = 0 ; /* crash close write */
+ } ;
+
+ /* For whatever reason, is no longer write_open -- clear all buffers.
+ */
+ vio_fifo_clear(&vio->cli_obuf) ; /* throw away cli stuff */
+ uty_out_clear(vio) ; /* throw away cmd stuff */
+
+ vio->close_reason = NULL ; /* too late for this */
+
+ return not_ready ; /* NB: NOT blocked by I/O */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible -- for "monitor" output.
+ *
+ * Outputs only:
+ *
+ * a. outstanding line control stuff.
+ *
+ * b. contents of CLI buffer
+ *
+ * And:
+ *
+ * a. does not report any errors.
+ *
+ * b. does not change anything except the state of the buffers.
+ *
+ * In particular, for the qpthreaded world, does not attempt to change
+ * the state of the qfile or any other "thread private" structures.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone
+ * < 0 => failed (or !write_open)
+ */
+static int
+uty_write_monitor(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (!vio->sock.write_open)
+ return -1 ;
+
+ if (vio->cmd_lc != NULL)
+ {
+ int ret ;
+ ret = uty_write_lc(vio, &vio->cmd_obuf, vio->cmd_lc) ;
+
+ if (ret != 0)
+ return ret ;
+ } ;
+
+ return vio_fifo_write_nb(&vio->cli_obuf, vio->sock.fd, true) ;
} ;
/*------------------------------------------------------------------------------
- * Flush the given FIFO to output -- subject to possible line control.
+ * Write the given FIFO to output -- subject to possible line control.
+ *
+ * Note that even if no "--more--" is set, will have set some height, so
+ * that does not attempt to empty the FIFO completely all in one go.
+ *
+ * If the line control becomes "paused", it is time to enter "--more--" state
+ * -- unless the FIFO is empty (or "--more--" is not enabled).
*
- * If ends up needing to write more, sets write on.
+ * NB: expects that the sock is write_open
*
- * Returns: 0 => written everything there is -- or not (now) write_open
- * 1 => written everything that could -- needs to write more
- * 2 => written everything that could -- needs a "--more--"
+ * Returns: > 0 => blocked or completed one tranche
+ * 0 => all gone
+ * < 0 => failed
*/
static int
-uty_flush_fifo(vty_io vio, vio_fifo vf, struct vty_line_control* line_control)
+uty_write_fifo_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
{
+ int ret ;
char* src ;
size_t have ;
- int done ;
- bool wait_more ;
- if (!vio->file.write_open)
+ /* Collect another line_control height's worth of output.
+ *
+ * Expect the line control to be empty at this point, but it does not have
+ * to be.
+ */
+ vio_lc_set_pause(lc) ; /* clears lc->paused */
+
+ src = vio_fifo_get_rdr(vf, &have) ;
+
+ while ((src != NULL) && (!lc->paused))
{
- uty_empty_out_fifos(vio) ;
- return 0 ;
+ size_t take ;
+ take = vio_lc_append(lc, src, have) ;
+ src = vio_fifo_step_rdr(vf, &have, take) ;
} ;
- wait_more = 0 ;
+ vio->cli_dirty = (lc->col != 0) ;
- while ((src = vio_fifo_get_lump(vf, &have)) != NULL)
- {
- if (line_control != NULL) /* TODO: line control */
- {
- /* Account for what happens if output have bytes from src...
- * ... and if necessary reduce "have".
- *
- * set wait_more if now need to wait
- */
- } ;
+ /* Write the contents of the line control */
+ ret = uty_write_lc(vio, vf, lc) ;
- done = write_nb(vio->file.fd, src, have) ;
+ if (ret < 0)
+ return ret ; /* give up now if failed. */
- if (done < 0)
- {
- uty_io_error(vio, "write") ;
+ if ((ret == 0) && vio_fifo_empty(vf))
+ return 0 ; /* FIFO and line control empty */
- vio->file.write_open = 0 ;
- uty_empty_out_fifos(vio) ;
- return 0 ; /* no longer open */
- }
+ /* If should now do "--more--", now is the time to prepare for that.
+ *
+ * Entering more state issues a new prompt in the CLI buffer, which can
+ * be written once line control write completes.
+ *
+ * The "--more--" cli will not do anything until the CLI buffer has
+ * cleared.
+ */
+ if (lc->paused && vio->cli_more_enabled)
+ uty_cli_go_more_wait(vio) ;
- vio_fifo_got_upto(vf, src + done) ;
+ return 1 ; /* FIFO or line control, not empty */
+} ;
- if (done < (int)have)
- {
- if (line_control != NULL)
- {
- /* "put back" have - done bytes for next time */
- } ;
+/*------------------------------------------------------------------------------
+ * Write contents of line control (if any).
+ *
+ * NB: expects that the sock is write_open
+ *
+ * NB: does nothing other than write() and buffer management.
+ *
+ * Returns: > 0 => blocked
+ * 0 => all gone
+ * < 0 => failed
+ */
+static int
+uty_write_lc(vty_io vio, vio_fifo vf, vio_line_control lc)
+{
+ int ret ;
- uty_file_set_write(&vio->file, on) ;
- return 1 ; /* output is full */
- } ;
+ ret = vio_lc_write_nb(vio->sock.fd, lc) ;
- /* If now wants to wait for a "--more--", then exit
- *
- * Note that the line_control cannot tell if the place it wants to
- * stop is, in fact, the end of the FIFO -- can only tell that
- * now...
- */
- if (wait_more)
- return vio_fifo_empty(vf) ? 0 : 2 ;
- } ;
+ if (ret <= 0)
+ vio_fifo_sync_rdr(vf) ; /* finished with FIFO contents */
- return 0 ; /* all gone */
+ return ret ;
} ;
/*------------------------------------------------------------------------------
- * Empty the output FIFOs
+ * Start command output -- clears down the line control.
*
- * This is for use when the output has failed or is closed.
+ * Requires that that current line is empty -- restarts the line control
+ * on the basis that is at column 0.
*/
-static void
-uty_empty_out_fifos(vty_io vio)
+extern void
+uty_cmd_output_start(vty_io vio)
{
- vio_fifo_set_empty(&vio->cli_obuf) ;
- vio_fifo_set_empty(&vio->cmd_obuf) ;
+ if (vio->cmd_lc != NULL)
+ vio_lc_clear(vio->cmd_lc) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the effective height for line control (if any)
+ *
+ * If using line_control, may enable the "--more--" output handling.
+ *
+ * If not, want some limit on the amount of stuff output at a time.
+ *
+ * Sets the line control window width and height.
+ * Sets cli_more_enabled if "--more--" is enabled.
+ */
+extern void
+uty_set_height(vty_io vio)
+{
+ bool on ;
+
+ on = 0 ; /* default state */
+
+ if ((vio->cmd_lc != NULL) && !vio->half_closed)
+ {
+ int height ;
+
+ height = 0 ; /* default state */
- vio->cmd_wait_more = 0 ;
+ if ((vio->width) != 0)
+ {
+ /* If window size is known, use lines or given height */
+ if (vio->lines >= 0)
+ height = vio->lines ;
+ else
+ {
+ /* Window height, leaving one line from previous "page"
+ * and one line for the "--more--" -- if at all possible
+ */
+ height = vio->height - 2 ;
+ if (height < 1)
+ height = 1 ;
+ } ;
+ }
+ else
+ {
+ /* If window size not known, use lines if that has been set
+ * explicitly for this terminal.
+ */
+ if (vio->lines_set)
+ height = vio->lines ;
+ } ;
+
+ if (height > 0)
+ on = 1 ; /* have a defined height */
+ else
+ height = 200 ; /* but no "--more--" */
+
+ vio_lc_set_window(vio->cmd_lc, vio->width, height) ;
+ } ;
+
+ vio->cli_more_enabled = on ;
} ;
/*==============================================================================
@@ -1321,10 +1815,8 @@ uty_timer_expired (vty_io vio)
if (vio->half_closed)
return uty_close(vio) ; /* curtains */
- uty_half_close(vio) ; /* bring input side to a halt */
-
- vio->timed_out = 1 ; /* why stopped */
-} ;
+ uty_half_close(vio, "Timed out") ; /* bring input side to a halt */
+ } ;
/*------------------------------------------------------------------------------
* Callback -- qnexus: deal with timer timeout.
@@ -1351,7 +1843,7 @@ vty_timer_thread (struct thread *thread)
VTY_LOCK() ;
- vio->file.t_timer = NULL ; /* implicitly */
+ vio->sock.t_timer = NULL ; /* implicitly */
uty_timer_expired(vio) ;
@@ -1373,7 +1865,7 @@ struct vty_listener
enum vty_type type ;
- struct vio_file file ;
+ struct vio_sock sock ;
};
/* List of listeners so can tidy up. */
@@ -1450,7 +1942,7 @@ uty_close_listeners(void)
while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
{
- uty_file_close(&listener->file) ; /* no ceremony, no flowers */
+ uty_sock_close(&listener->sock) ; /* no ceremony, no flowers */
XFREE(MTYPE_VTY, listener) ;
} ;
} ;
@@ -1537,6 +2029,8 @@ uty_serv_sock(const char* addr, unsigned short port)
VTY_ASSERT_LOCKED() ;
+ n = 0 ; /* nothing opened yet */
+
/* If have an address, see what kind and whether valid */
sa = NULL ;
@@ -1620,6 +2114,19 @@ uty_serv_sock_open(sa_family_t family, int type, int protocol,
if (ret >= 0)
ret = set_nonblocking(sock);
+#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)
+ /* Want only IPV6 on ipv6 socket (not mapped addresses)
+ *
+ * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the
+ * attempt to bind to :: after binding to 0.0.0.0.
+ */
+ if ((ret >= 0) && (sa->sa_family == AF_INET6))
+ {
+ int on = 1;
+ ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
+ }
+#endif
+
if (ret >= 0)
ret = sockunion_bind (sock, &su, port, sa) ;
@@ -1673,7 +2180,6 @@ uty_serv_vtysh(const char *path)
{
uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
safe_strerror(errno));
- umask (old_mask);
return -1 ;
}
@@ -1711,10 +2217,8 @@ uty_serv_vtysh(const char *path)
{
/* set group of socket */
if ( chown (path, -1, ids.gid_vty) )
- {
- uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
+ uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
safe_strerror (errno) );
- }
}
umask (old_mask);
@@ -1745,16 +2249,16 @@ uty_serv_start_listener(int fd, enum vty_type type)
listener = XCALLOC(MTYPE_VTY, sizeof (struct vty_listener));
ssl_push(vty_listeners_list, listener, next) ;
- uty_file_init_new(&listener->file, fd, listener) ;
+ uty_sock_init_new(&listener->sock, fd, listener) ;
listener->type = type ;
if (vty_cli_nexus)
- listener->file.action.read.qnexus = vty_accept_qnexus ;
+ listener->sock.action.read.qnexus = vty_accept_qnexus ;
else
- listener->file.action.read.thread = vty_accept_thread ;
+ listener->sock.action.read.thread = vty_accept_thread ;
- uty_file_set_read(&listener->file, on) ;
+ uty_sock_set_read(&listener->sock, on) ;
} ;
/*------------------------------------------------------------------------------
@@ -1770,7 +2274,7 @@ vty_accept_thread(struct thread *thread)
result = uty_accept(listener, THREAD_FD(thread));
- uty_file_set_read(&listener->file, on) ;
+ uty_sock_set_read(&listener->sock, on) ;
VTY_UNLOCK() ;
return result ;
@@ -1797,7 +2301,7 @@ uty_accept(vty_listener listener, int listen_sock)
{
VTY_ASSERT_LOCKED() ;
- assert(listener->file.fd == listen_sock) ;
+ assert(listener->sock.fd == listen_sock) ;
switch (listener->type)
{
@@ -1828,9 +2332,9 @@ uty_accept_term(vty_listener listener)
VTY_ASSERT_LOCKED() ;
/* We can handle IPv4 or IPv6 socket. */
- sockunion_init_new(&su, 0) ;
+ sockunion_init_new(&su, AF_UNSPEC) ;
- sock = sockunion_accept (listener->file.fd, &su);
+ sock = sockunion_accept (listener->sock.fd, &su);
if (sock < 0)
{
@@ -1918,7 +2422,7 @@ uty_accept_shell_serv (vty_listener listener)
client_len = sizeof(client);
memset (&client, 0, client_len);
- sock = accept(listener->file.fd, (struct sockaddr *) &client,
+ sock = accept(listener->sock.fd, (struct sockaddr *) &client,
(socklen_t *) &client_len) ;
if (sock < 0)
@@ -1948,7 +2452,7 @@ uty_accept_shell_serv (vty_listener listener)
}
/*==============================================================================
- * Reading from the VTY_SHELL_SERV type file.
+ * Reading from the VTY_SHELL_SERV type sock.
*
* The select/pselect call-back ends up in utysh_read_ready().
*/
@@ -1970,7 +2474,7 @@ uty_accept_shell_serv (vty_listener listener)
static void
utysh_read_ready(vty_io vio)
{
- uty_file_set_read(&vio->file, off) ;
+ uty_sock_set_read(&vio->sock, off) ;
/* TODO: need minimal "CLI" for VTY_SHELL_SERV
* NB: when output from command is flushed out, must append the
@@ -1989,7 +2493,7 @@ vtysh_read_qnexus(qps_file qf, void* file_info)
VTY_LOCK() ;
- assert((vio->file.fd == qf->fd) && (vio == vio->file.info)) ;
+ assert((vio->sock.fd == qf->fd) && (vio == vio->sock.info)) ;
utysh_read_ready(vio) ;
@@ -2006,9 +2510,9 @@ vtysh_read_thread(struct thread *thread)
VTY_LOCK() ;
- assert(vio->file.fd == THREAD_FD (thread) && (vio == vio->file.info)) ;
+ assert(vio->sock.fd == THREAD_FD (thread) && (vio == vio->sock.info)) ;
- vio->file.t_read = NULL ; /* implicitly */
+ vio->sock.t_read = NULL ; /* implicitly */
utysh_read_ready(vio);
VTY_UNLOCK() ;
@@ -2025,7 +2529,7 @@ vtysh_read_thread(struct thread *thread)
* Moves stuff from the "buf" qstring and appends to "cl" qstring, stopping
* when get '\0' or empties the "buf".
*
- * When empties "buf", reads a lump from the file.
+ * When empties "buf", reads a lump from the sock.
*
* Returns: 0 => command line is incomplete
* 1 => have a complete command line
@@ -2070,13 +2574,13 @@ utysh_read (vty_io vio, qstring cl, qstring buf)
/* buffer is empty -- try and get some more stuff */
assert(buf->len == buf->cp) ;
- if (!vio->file.read_open)
+ if (!vio->sock.read_open)
return -1 ; /* at EOF if not open <<<<<<<<<<<<< */
qs_need(buf, 500) ; /* need a reasonable lump */
- qs_set_empty(buf) ; /* set cp = len = 0 */
+ qs_clear(buf) ; /* set cp = len = 0 */
- get = read_nb(vio->file.fd, buf->body, buf->size) ;
+ get = read_nb(vio->sock.fd, buf->body, buf->size) ;
if (get > 0)
buf->len = get ;
else if (get == 0)
@@ -2084,9 +2588,9 @@ utysh_read (vty_io vio, qstring cl, qstring buf)
else
{
if (get == -1)
- uty_io_error(vio, "read") ;
+ uty_sock_error(vio, "read") ;
- vio->file.read_open = 0 ;
+ vio->sock.read_open = 0 ;
return -1 ; /* at EOF or failed <<<<<<<<<<<<< */
} ;
@@ -2096,13 +2600,65 @@ utysh_read (vty_io vio, qstring cl, qstring buf)
/*==============================================================================
* Output to vty which are set to "monitor".
*
- * If there is something in the command FIFO and command is not in progress,
- * then throw logging away -- console is busy dealing with the output from
- * some command.
+ * This is VERY TRICKY.
+ *
+ * If not running qpthreaded, then the objective is to get the message away
+ * immediately -- do not wish it to be delayed in any way by the thread
+ * system.
+ *
+ * So proceed as follows:
+ *
+ * a. wipe command line -- which adds output to the CLI buffer
+ *
+ * b. write the CLI buffer to the sock and any outstanding line control.
+ *
+ * c. write the monitor output.
+ *
+ * If that does not complete, put the tail end to the CLI buffer.
+ *
+ * d. restore any command line -- which adds output to the CLI buffer
+ *
+ * e. write the CLI buffer to the sock
+ *
+ * If that all succeeds, nothing has changed as far as the VTY stuff is
+ * concerned -- except that possibly some CLI output was sent before it got
+ * round to it.
+ *
+ * Note that step (b) will deal with any output hanging around from an
+ * earlier step (e). If cannot complete that, then does not add fuel to the
+ * fire -- but the message will be discarded.
+ *
+ * If that fails, or does not complete, then can set write on, to signal that
+ * there is some output in the CLI buffer that needs to be sent, or some
+ * error to be dealt with.
+ *
+ * The output should be tidy.
+ *
+ * To cut down the clutter, step (d) is performed only if the command line
+ * is not empty (or if in cli_more_wait). Once a the user has started to enter
+ * a command, the prompt and the command will remain visible.
*
- * Wipes the command line and flushes the output. If the CLI FIFO is now
- * empty, add the logging line to it and flush. Enable read, so that the
- * CLI will be reentered, and the command line restored in due course.
+ * When logging an I/O error for a vty that happens to be a monitor, the
+ * monitor-ness has already been turned off. The monitor output code does not
+ * attempt to log any errors, sets write on so that the error will be picked
+ * up that way.
+ *
+ * However, in the event of an assertion failure, it is possible that an
+ * assertion will fail inside the monitor output. The monitor_busy flag
+ * prevents disaster. It is also left set if I/O fails in monitor output, so
+ * will not try to use the monitor again.
+ *
+ * Note that an assertion which is false for all vty monitors will recurse
+ * through all the monitors, setting each one busy, in turn !
+ *
+
+
+ * TODO: sort out write on in the qpthreads world ??
+ *
+ * The problem is that the qpselect structure is designed to be accessed ONLY
+ * within the thread to which it belongs. This makes it impossible for the
+ * monitor output to set/clear read/write on the vty sock... so some way
+ * around this is required.
*/
/*------------------------------------------------------------------------------
@@ -2113,67 +2669,52 @@ uty_log(struct logline* ll, struct zlog *zl, int priority,
const char *format, va_list va)
{
vty_io vio ;
- vty_io next ;
VTY_ASSERT_LOCKED() ;
- next = sdl_head(vio_monitors_base) ;
+ vio = sdl_head(vio_monitors_base) ;
- if (next == NULL)
+ if (vio == NULL)
return ; /* go no further if no "monitor" vtys */
/* Prepare line for output. */
- uvzlog_line(ll, zl, priority, format, va, 1) ; /* with crlf */
+ uvzlog_line(ll, zl, priority, format, va, llt_crlf) ; /* with crlf */
/* write to all known "monitor" vty
*
- * While writing to a given "monitor" the monitor flag is cleared. This
- * means that if the write fails, and logs a message, then will recurse
- * through here -- but won't log to the monitor that has failed.
- *
- * If one of the other monitors fails during this process, will recurse
- * again, now with two monitors with their monitor flags cleared.
- *
- * Once the output (and any recursed output) has completed, then the
- * monitor flag is restored -- but only if the vty is still write_open.
- *
- * A monitor that is not write_open at the end of this, is removed from the
- * monitors list. The current vio *cannot* be the current vio at a higher
- * level in any recursion stack, because... if anything higher up the stack
- * will have their monitor flag cleared, and therefore have been stepped
- * over at the current level.
*/
- while (next != NULL)
+ while (vio != NULL)
{
- vio = next ;
-
- if ( vio->monitor /* may be temporarily not a monitor */
- && (vio->cmd_in_progress || vio_fifo_empty(&vio->cmd_obuf)) )
+ if (!vio->monitor_busy)
{
- vio->monitor = 0 ; /* avoid recursion */
+ int ret ;
- uty_cli_wipe(vio) ;
- uty_write(vio) ;
+ vio->monitor_busy = 1 ; /* close the door */
- if (vio_fifo_empty(&vio->cli_obuf) && vio->file.write_open)
+ uty_cli_pre_monitor(vio, ll->len - 2) ; /* claim the console */
+
+ ret = uty_write_monitor(vio) ;
+ if (ret == 0)
{
- vio_fifo_put(&vio->cli_obuf, ll->line, ll->len) ;
- uty_write(vio) ;
+ ret = write_nb(vio->sock.fd, ll->line, ll->len) ;
+
+ if (ret >= 0)
+ {
+ ret = uty_cli_post_monitor(vio, ll->line + ret,
+ ll->len - ret) ;
+ if (ret > 0)
+ ret = uty_write_monitor(vio) ;
+ } ;
} ;
- uty_file_set_read(&vio->file, on) ;
+ if (ret != 0)
+ /* need to prod */ ;
- /* It is possible that something failed, so that is no longer
- * write_open, and should no longer be a monitor.
- */
- vio->monitor = vio->file.write_open ;
+ if (ret >= 0)
+ vio->monitor_busy = 0 ;
} ;
- next = sdl_next(vio, mon_list) ;
-
- /* take self off list if no onger a monitor */
- if (!vio->monitor)
- sdl_del(vio_monitors_base, vio, mon_list) ;
+ vio = sdl_next(vio, mon_list) ;
} ;
} ;
@@ -2194,8 +2735,8 @@ vty_log_fixed (const char *buf, size_t len)
vio = sdl_head(vio_monitors_base) ;
while (vio != NULL)
{
- write(vio->file.fd, buf, len) ;
- write(vio->file.fd, "\r\n", 2) ;
+ write(vio->sock.fd, buf, len) ;
+ write(vio->sock.fd, "\r\n", 2) ;
vio = sdl_next(vio, mon_list) ;
} ;
diff --git a/lib/vty_io.h b/lib/vty_io.h
index 06cefe06..19689853 100644
--- a/lib/vty_io.h
+++ b/lib/vty_io.h
@@ -31,6 +31,7 @@
#include "uty.h"
#include "vty.h"
#include "vio_fifo.h"
+#include "vio_lines.h"
#include "keystroke.h"
#include "thread.h"
#include "command.h"
@@ -62,14 +63,17 @@
*/
/*------------------------------------------------------------------------------
- * VTY file structure
+ * VTY sock structure
*
- * Used
+ * Used for VTY_TERM and VTY_SHELL_SERV VTY types, which are attached to TCP
+ * and UNIX sockets, respectively.
+ *
+ * Also used for the associated listeners.
*/
typedef int thread_action(struct thread *) ;
-union file_action
+union sock_action
{
qps_action* qnexus ;
thread_action* thread ;
@@ -83,21 +87,21 @@ union timer_action
void* anon ;
} ;
-struct vio_file_actions
+struct vio_sock_actions
{
- union file_action read ;
- union file_action write ;
+ union sock_action read ;
+ union sock_action write ;
union timer_action timer ;
};
-typedef struct vio_file* vio_file ;
-struct vio_file
+typedef struct vio_sock* vio_sock ;
+struct vio_sock
{
int fd ;
void* info ; /* for action routines */
- struct vio_file_actions action ;
+ struct vio_sock_actions action ;
bool read_open ; /* read returns 0 if not open */
bool write_open ; /* write completes instantly if not open */
@@ -116,56 +120,67 @@ struct vio_file
} ;
-struct vty_line_control
+enum
{
- int tba ;
+ on = true,
+ off = false
} ;
-enum
+enum vty_readiness /* bit significant */
{
- off = false,
- on = true
-};
+ not_ready = 0,
+ read_ready = 1,
+ write_ready = 2, /* takes precedence */
+ now_ready = 4
+} ;
/*------------------------------------------------------------------------------
- *
+ * The vty_io structure
*/
struct vty_io {
+ struct vty* vty ; /* the related vty */
+ char *name ; /* for VTY_TERM is IP address) */
+
/* List of all vty_io objects */
struct dl_list_pair(vty_io) vio_list ;
- bool half_closed ; /* => on death watch list */
- bool timed_out ; /* closed by timer */
-
/* List of all vty_io that are in monitor state */
struct dl_list_pair(vty_io) mon_list ;
- /* The attached to this vty */
- struct vty* vty ;
-
- /* Type of VTY */
+ /* VTY type and sock stuff */
enum vty_type type;
- /* File level stuff */
- struct vio_file file ;
+ struct vio_sock sock ; /* for VTY_TERM and VTY_SHELL_SERV */
- /* "name" of the VTY (for VTY_TERM is IP address) */
- char *name ;
+ bool half_closed ; /* => on death watch list */
+ bool closed ; /* => all I/O terminated
+ will also be half_closed */
- /* Keystroke stream and raw input buffer */
- keystroke_stream key_stream ;
- qstring_t ibuf ;
+ const 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 ;
/*--------------------------------------------------------------------*/
/* Command line and related state */
+ keystroke_stream key_stream ;
+
/* cli_drawn <=> the current prompt and user input occupy the current
* line on the screen.
*
+ * cli_dirty <=> the last command output did not end with a newline.
+ *
* If cli_drawn is true, the following are valid:
*
* cli_prompt_len -- the length of the prompt part.
+ * (will be the "--more--" prompt in cli_more_wait)
*
* cli_extra_len -- the length of any ^X at the cursor position
* (for when blocked waiting for queued command)
@@ -174,28 +189,38 @@ struct vty_io {
*
* NB: cli_echo_suppress is only used for password entry.
*/
- int cli_drawn ;
+ bool cli_drawn ;
+ bool cli_dirty ;
- int cli_prompt_len ; /* for drawn line (if any) */
- int cli_extra_len ; /* for for drawn line (if any) */
+ int cli_prompt_len ;
+ int cli_extra_len ;
- bool cli_echo_suppress ; /* non-zero => suppress cli echo */
+ 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 ;
+ enum node_type cli_prompt_node ;
+ bool cli_prompt_set ;
+ qstring_t cli_prompt_for_node ;
/* State of the CLI
*
* cli_blocked -- blocked from processing keystrokes
* cmd_in_progress -- command dispatched (may be queued)
- *
- * cli_wait_more -- is in "--more--" wait state
- *
+ * 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)
*
@@ -211,87 +236,75 @@ struct vty_io {
*/
enum cli_do cli_do ;
- qstring_t cl ;
- qstring_t clx ;
+ qstring_t cl ;
+ qstring_t clx ;
/* CLI output buffering */
- qstring_t cli_vbuf ; /* for uty_cli_out */
vio_fifo_t cli_obuf ;
/* Command output buffering */
- qstring_t cmd_vbuf ; /* for uty_vout() */
vio_fifo_t cmd_obuf ;
- bool cmd_wait_more ;
+ vio_line_control cmd_lc ;
- struct vty_line_control line_control ;
/* Failure count for login attempts */
- int fail;
+ int fail;
/* History of commands */
vector_t hist ;
int hp ; /* History lookup current point */
int hindex; /* History insert end point */
- /* Window width/height. */
- int width;
- int height;
+ /* Window width/height as reported by Telnet. 0 => unknown */
+ int width;
+ int height;
/* Configure lines. */
- int lines;
+ int lines;
+ bool lines_set ; /* true <=> explicitly set */
/* Terminal monitor. */
- bool monitor ;
+ bool monitor ;
+ bool monitor_busy ;
/* In configure mode. */
- bool config;
+ bool config;
} ;
/*==============================================================================
* Functions
*/
-extern struct vty*
-uty_new (int fd, enum vty_type type) ;
-
-extern void
-uty_open_listeners(const char *addr, unsigned short port, const char *path) ;
-extern void
-uty_close_listeners(void) ;
-
-extern void
-uty_half_close (vty_io vio) ;
-extern void
-uty_close (vty_io vio) ;
-extern void
-uty_full_close (vty_io vio) ;
-extern void
-uty_watch_dog_stop(void) ;
-
-extern int
-uty_out (struct vty *vty, const char *format, ...) PRINTF_ATTRIBUTE(2, 3) ;
-extern int
-uty_vout(struct vty *vty, const char *format, va_list args) ;
-extern void
-uty_out_discard(vty_io vio) ;
-
-extern void
-uty_file_set_read(vio_file file, bool on) ;
-extern void
-uty_file_set_write(vio_file file, bool on) ;
-extern void
-uty_file_set_timer(vio_file file, 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) ;
+extern struct vty* uty_new (enum vty_type type, int sock_fd) ;
+
+extern void uty_open_listeners(const char *addr, unsigned short port,
+ const char *path) ;
+extern void uty_close_listeners(void) ;
+
+extern void uty_watch_dog_start(void) ;
+extern void uty_watch_dog_stop(void) ;
+
+extern void uty_half_close (vty_io vio, const char* reason) ;
+extern void uty_close (vty_io vio) ;
+
+extern int uty_out (struct vty *vty, const char *format, ...)
+ PRINTF_ATTRIBUTE(2, 3) ;
+extern int uty_vout(struct vty *vty, const char *format, va_list args) ;
+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_sock_set_readiness(vio_sock sock, enum vty_readiness ready) ;
+extern void uty_sock_set_timer(vio_sock sock, 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) ;
#endif /* _ZEBRA_VTY_IO_H */
diff --git a/lib/zassert.h b/lib/zassert.h
index a766eb7b..8ca2203f 100644
--- a/lib/zassert.h
+++ b/lib/zassert.h
@@ -49,11 +49,8 @@ extern void _zlog_abort_err (const char *mess, int err, const char *file,
#define dassert(EX)
#endif
-/* TODO: implement _zlog_abort() to give required messages */
-
/* Abort with message */
-#define zabort(MS) _zlog_assert_failed(MS, __FILE__, __LINE__, \
- __ASSERT_FUNCTION)
+#define zabort(MS) _zlog_abort_mess(MS, __FILE__, __LINE__, __ASSERT_FUNCTION)
/* Abort with message and errno and strerror() thereof */
#define zabort_errno(MS) _zlog_abort_errno(MS, __FILE__, __LINE__, \
diff --git a/lib/zclient.c b/lib/zclient.c
index 8cfa5d52..f365a1aa 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -116,7 +116,7 @@ void
zclient_init (struct zclient *zclient, int redist_default)
{
int i;
-
+
/* Enable zebra client connection by default. */
zclient->enable = 1;
@@ -202,8 +202,8 @@ zclient_socket(void)
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
return -1;
-
- /* Make server socket. */
+
+ /* Make server socket. */
memset (&serv, 0, sizeof (struct sockaddr_in));
serv.sin_family = AF_INET;
serv.sin_port = htons (ZEBRA_PORT);
@@ -235,8 +235,8 @@ zclient_socket_un (const char *path)
sock = socket (AF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
return -1;
-
- /* Make server socket. */
+
+ /* Make server socket. */
memset (&addr, 0, sizeof (struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy (addr.sun_path, path, strlen (path));
@@ -370,7 +370,7 @@ zebra_message_send (struct zclient *zclient, int command)
/* Send very simple command only Zebra message. */
zclient_create_header (s, command);
-
+
return zclient_send_message(zclient);
}
@@ -396,7 +396,7 @@ zclient_start (struct zclient *zclient)
return 0;
/* Check timer */
- if (zclient->qtr && qtr_is_active(zclient->qtr))
+ if (zclient->qtr && zclient->qtr->active)
return 0;
/* Make socket. */
@@ -421,7 +421,7 @@ zclient_start (struct zclient *zclient)
zclient->fail = 0;
if (zclient_debug)
zlog_debug ("zclient connect success with socket [%d]", zclient->sock);
-
+
if (zclient_nexus)
qps_add_file(zclient_nexus->selection, zclient->qf, zclient->sock, zclient);
@@ -521,13 +521,13 @@ zlookup_connect_r (qtimer qtr, void* timer_info, qtime_t when)
}
- /*
+ /*
* "xdr_encode"-like interface that allows daemon (client) to send
* a message to zebra server for a route that needs to be
* added/deleted to the kernel. Info about the route is specified
* by the caller in a struct zapi_ipv4. zapi_ipv4_read() then writes
* the info down the zclient socket using the stream_* functions.
- *
+ *
* The corresponding read ("xdr_decode") function on the server
* side is zread_ipv4_add()/zread_ipv4_delete().
*
@@ -539,11 +539,11 @@ zlookup_connect_r (qtimer qtr, void* timer_info, qtime_t when)
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Destination IPv4 Prefix for route |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Nexthop count |
+ * | Nexthop count |
* +-+-+-+-+-+-+-+-+
*
- *
- * A number of IPv4 nexthop(s) or nexthop interface index(es) are then
+ *
+ * A number of IPv4 nexthop(s) or nexthop interface index(es) are then
* described, as per the Nexthop count. Each nexthop described as:
*
* +-+-+-+-+-+-+-+-+
@@ -553,18 +553,18 @@ zlookup_connect_r (qtimer qtr, void* timer_info, qtime_t when)
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Alternatively, if the flags field has ZEBRA_FLAG_BLACKHOLE or
- * ZEBRA_FLAG_REJECT is set then Nexthop count is set to 1, then _no_
+ * ZEBRA_FLAG_REJECT is set then Nexthop count is set to 1, then _no_
* nexthop information is provided, and the message describes a prefix
* to blackhole or reject route.
*
* If ZAPI_MESSAGE_DISTANCE is set, the distance value is written as a 1
* byte value.
- *
+ *
* If ZAPI_MESSAGE_METRIC is set, the metric value is written as an 8
* byte value.
*
* XXX: No attention paid to alignment.
- */
+ */
int
zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p,
struct zapi_ipv4 *api)
@@ -576,9 +576,9 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p,
/* Reset stream. */
s = zclient->obuf;
stream_reset (s);
-
+
zclient_create_header (s, cmd);
-
+
/* Put type and nexthop. */
stream_putc (s, api->type);
stream_putc (s, api->flags);
@@ -644,7 +644,7 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p,
stream_putc (s, api->type);
stream_putc (s, api->flags);
stream_putc (s, api->message);
-
+
/* Put prefix information. */
psize = PSIZE (p->prefixlen);
stream_putc (s, p->prefixlen);
@@ -679,10 +679,10 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p,
}
#endif /* HAVE_IPV6 */
-/*
+/*
* send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE
* for the route type (ZEBRA_ROUTE_KERNEL etc.). The zebra server will
- * then set/unset redist[type] in the client handle (a struct zserv) for the
+ * then set/unset redist[type] in the client handle (a struct zserv) for the
* sending client
*/
int
@@ -692,12 +692,12 @@ zebra_redistribute_send (int command, struct zclient *zclient, int type)
s = zclient->obuf;
stream_reset(s);
-
+
zclient_create_header (s, command);
stream_putc (s, type);
-
+
stream_putw_at (s, 0, stream_get_endp (s));
-
+
return zclient_send_message(zclient);
}
@@ -716,7 +716,7 @@ zebra_router_id_update_read (struct stream *s, struct prefix *rid)
}
/* Interface addition from zebra daemon. */
-/*
+/*
* The format of the message sent with type ZEBRA_INTERFACE_ADD or
* ZEBRA_INTERFACE_DELETE from zebra to the client is:
* 0 1 2 3
@@ -776,11 +776,11 @@ zebra_interface_add_read (struct stream *s)
if (ifp->hw_addr_len)
stream_get (ifp->hw_addr, s, ifp->hw_addr_len);
#endif /* HAVE_STRUCT_SOCKADDR_DL */
-
+
return ifp;
}
-/*
+/*
* Read interface up/down msg (ZEBRA_INTERFACE_UP/ZEBRA_INTERFACE_DOWN)
* from zebra server. The format of this message is the same as
* that sent for ZEBRA_INTERFACE_ADD/ZEBRA_INTERFACE_DELETE (see
@@ -818,7 +818,7 @@ zebra_interface_state_read (struct stream *s)
return ifp;
}
-/*
+/*
* format of message for address additon is:
* 0
* 0 1 2 3 4 5 6 7
@@ -918,7 +918,7 @@ zebra_interface_address_read (int type, struct stream *s)
stream_get (&d.u.prefix, s, plen);
d.family = family;
- if (type == ZEBRA_INTERFACE_ADDRESS_ADD)
+ if (type == ZEBRA_INTERFACE_ADDRESS_ADD)
{
/* N.B. NULL destination pointers are encoded as all zeroes */
ifc = connected_add_by_prefix(ifp, &p,(memconstant(&d.u.prefix,0,plen) ?
@@ -995,15 +995,15 @@ zclient_read (struct zclient *zclient)
marker = stream_getc (zclient->ibuf);
version = stream_getc (zclient->ibuf);
command = stream_getw (zclient->ibuf);
-
+
if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION)
{
zlog_err("%s: socket %d version mismatch, marker %d, version %d",
__func__, zclient->sock, marker, version);
return zclient_failed(zclient);
}
-
- if (length < ZEBRA_HEADER_SIZE)
+
+ if (length < ZEBRA_HEADER_SIZE)
{
zlog_err("%s: socket %d message length %u is less than %d ",
__func__, zclient->sock, length, ZEBRA_HEADER_SIZE);
@@ -1112,7 +1112,7 @@ void
zclient_redistribute (int command, struct zclient *zclient, int type)
{
- if (command == ZEBRA_REDISTRIBUTE_ADD)
+ if (command == ZEBRA_REDISTRIBUTE_ADD)
{
if (zclient->redist[type])
return;
@@ -1140,7 +1140,7 @@ zclient_redistribute_default (int command, struct zclient *zclient)
return;
zclient->default_information = 1;
}
- else
+ else
{
if (!zclient->default_information)
return;
@@ -1169,20 +1169,20 @@ zclient_event_r (enum event event, struct zclient *zclient)
switch (event)
{
case ZLOOKUP_SCHEDULE:
- if (!qtr_is_active(zclient->qtr))
+ if (!zclient->qtr->active)
qtimer_set(zclient->qtr, qt_get_monotonic(), zlookup_connect_r) ;
break;
case ZCLIENT_SCHEDULE:
- if (!qtr_is_active(zclient->qtr))
+ if (!zclient->qtr->active)
qtimer_set(zclient->qtr, qt_get_monotonic(), zclient_connect_r) ;
break;
case ZCLIENT_CONNECT:
if (zclient->fail >= 10)
return;
if (zclient_debug)
- zlog_debug ("zclient connect schedule interval is %d",
+ zlog_debug ("zclient connect schedule interval is %d",
zclient->fail < 3 ? 10 : 60);
- if (!qtr_is_active(zclient->qtr))
+ if (!zclient->qtr->active)
qtimer_set(zclient->qtr,
qt_add_monotonic(QTIME(zclient->fail < 3 ? 10 : 60)), zclient_connect_r) ;
break;