summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am6
-rw-r--r--lib/buffer.c43
-rw-r--r--lib/buffer.h17
-rw-r--r--lib/command.c1383
-rw-r--r--lib/command.h146
-rw-r--r--lib/command_queue.c132
-rw-r--r--lib/command_queue.h5
-rw-r--r--lib/if.c50
-rw-r--r--lib/keychain.c44
-rw-r--r--lib/keystroke.c1007
-rw-r--r--lib/keystroke.h187
-rw-r--r--lib/list_util.c59
-rw-r--r--lib/list_util.h729
-rw-r--r--lib/log.c406
-rw-r--r--lib/log.h71
-rw-r--r--lib/mem_tracker.c583
-rw-r--r--lib/memory.c422
-rw-r--r--lib/memory.h56
-rw-r--r--lib/memtypes.awk2
-rw-r--r--lib/memtypes.c8
-rw-r--r--lib/mqueue.c93
-rw-r--r--lib/mqueue.h3
-rw-r--r--lib/network.c163
-rw-r--r--lib/network.h6
-rw-r--r--lib/node_type.h81
-rw-r--r--lib/plist.c8
-rw-r--r--lib/qlib_init.c3
-rw-r--r--lib/qpnexus.c26
-rw-r--r--lib/qpnexus.h37
-rw-r--r--lib/qpselect.c332
-rw-r--r--lib/qpselect.h27
-rw-r--r--lib/qstring.c227
-rw-r--r--lib/qstring.h365
-rw-r--r--lib/qtimers.c26
-rw-r--r--lib/routemap.c74
-rw-r--r--lib/sockunion.c653
-rw-r--r--lib/sockunion.h13
-rw-r--r--lib/uty.h205
-rw-r--r--lib/vector.c46
-rw-r--r--lib/vector.h40
-rw-r--r--lib/vio_fifo.c572
-rw-r--r--lib/vio_fifo.h185
-rw-r--r--lib/vty.c3837
-rw-r--r--lib/vty.c.x4414
-rw-r--r--lib/vty.h278
-rw-r--r--lib/vty_cli.c2308
-rw-r--r--lib/vty_cli.h43
-rw-r--r--lib/vty_io.c2202
-rw-r--r--lib/vty_io.h297
-rw-r--r--lib/workqueue.h23
50 files changed, 17287 insertions, 4656 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 9f21a377..17940f8d 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -14,7 +14,8 @@ libzebra_la_SOURCES = \
zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c privs.c \
sigevent.c pqueue.c jhash.c memtypes.c workqueue.c symtab.c heap.c \
qtime.c qpthreads.c mqueue.c qpselect.c qtimers.c qpnexus.c \
- command_queue.c qlib_init.c pthread_safe.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
BUILT_SOURCES = memtypes.h route_types.h
@@ -32,7 +33,8 @@ pkginclude_HEADERS = \
workqueue.h route_types.h symtab.h heap.h \
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
+ 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
EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.awk route_types.txt
diff --git a/lib/buffer.c b/lib/buffer.c
index f19a9e0c..b81924ec 100644
--- a/lib/buffer.c
+++ b/lib/buffer.c
@@ -1,5 +1,5 @@
/*
- * Buffering of output and input.
+ * Buffering of output and input.
* Copyright (C) 1998 Kunihiro Ishiguro
*
* This file is part of GNU Zebra.
@@ -8,7 +8,7 @@
* 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
@@ -17,7 +17,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.
+ * Boston, MA 02111-1307, USA.
*/
#include <zebra.h>
@@ -29,18 +29,6 @@
#include <stddef.h>
-
-/* Buffer master. */
-struct buffer
-{
- /* Data list. */
- struct buffer_data *head;
- struct buffer_data *tail;
-
- /* Size of each buffer_data chunk. */
- size_t size;
-};
-
/* Data container. */
struct buffer_data
{
@@ -67,11 +55,12 @@ struct buffer_data
/* Make new buffer. */
struct buffer *
-buffer_new (size_t size)
+buffer_init_new (struct buffer* b, size_t size)
{
- struct buffer *b;
-
- b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer));
+ if (b == NULL)
+ b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer));
+ else
+ memset(b, 0, sizeof (struct buffer)) ;
if (size)
b->size = size;
@@ -89,6 +78,13 @@ buffer_new (size_t size)
return b;
}
+/* Make new buffer. */
+struct buffer *
+buffer_new (size_t size)
+{
+ return buffer_init_new(NULL, size);
+}
+
/* Free buffer. */
void
buffer_free (struct buffer *b)
@@ -133,7 +129,7 @@ buffer_reset (struct buffer *b)
{
struct buffer_data *data;
struct buffer_data *next;
-
+
for (data = b->head; data; data = next)
{
next = data->next;
@@ -148,7 +144,8 @@ buffer_add (struct buffer *b)
{
struct buffer_data *d;
- d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data[b->size]));
+ typedef struct buffer_data buffer_data_t ; /* stop Eclipse whinging */
+ d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(buffer_data_t, data[b->size]));
d->cp = d->sp = 0;
d->next = NULL;
@@ -169,7 +166,7 @@ buffer_put(struct buffer *b, const void *p, size_t size)
const char *ptr = p;
/* We use even last one byte of data buffer. */
- while (size)
+ while (size)
{
size_t chunk;
@@ -226,7 +223,7 @@ buffer_flush_all (struct buffer *b, int fd)
/* Flush enough data to fill a terminal window of the given scene (used only
by vty telnet interface). */
buffer_status_t
-buffer_flush_window (struct buffer *b, int fd, int width, int height,
+buffer_flush_window (struct buffer *b, int fd, int width, int height,
int erase_flag, int no_more_flag)
{
int nbytes;
diff --git a/lib/buffer.h b/lib/buffer.h
index 6c3dc76a..132fe155 100644
--- a/lib/buffer.h
+++ b/lib/buffer.h
@@ -1,5 +1,5 @@
/*
- * Buffering to output and input.
+ * Buffering to output and input.
* Copyright (C) 1998 Kunihiro Ishiguro
*
* This file is part of GNU Zebra.
@@ -23,11 +23,22 @@
#ifndef _ZEBRA_BUFFER_H
#define _ZEBRA_BUFFER_H
+/* Buffer master. */
+struct buffer
+{
+ /* Data list. */
+ struct buffer_data *head;
+ struct buffer_data *tail;
+
+ /* Size of each buffer_data chunk. */
+ size_t size;
+};
/* Create a new buffer. Memory will be allocated in chunks of the given
size. If the argument is 0, the library will supply a reasonable
default size suitable for buffering socket I/O. */
extern struct buffer *buffer_new (size_t);
+struct buffer *buffer_init_new (struct buffer* b, size_t size) ;
/* Free all data in the buffer. */
extern void buffer_reset (struct buffer *);
@@ -73,7 +84,7 @@ typedef enum
extern buffer_status_t buffer_write(struct buffer *, int fd,
const void *, size_t);
-/* This function attempts to flush some (but perhaps not all) of
+/* This function attempts to flush some (but perhaps not all) of
the queued data to the given file descriptor. */
extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
@@ -88,7 +99,7 @@ extern buffer_status_t buffer_flush_all (struct buffer *, int fd);
/* Attempt to write enough data to the given fd to fill a window of the
given width and height (and remove the data written from the buffer).
- If !no_more, then a message saying " --More-- " is appended.
+ If !no_more, then a message saying " --More-- " is appended.
If erase is true, then first overwrite the previous " --More-- " message
with spaces.
diff --git a/lib/command.c b/lib/command.c
index 2c9ca64a..251c8963 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -30,6 +30,8 @@ Boston, MA 02111-1307, USA. */
#include "thread.h"
#include "vector.h"
#include "vty.h"
+#include "uty.h"
+#include "qstring.h"
#include "command.h"
#include "workqueue.h"
#include "command_queue.h"
@@ -242,10 +244,29 @@ sort_node ()
}
}
-/* Breaking up string into each command piece. I assume given
- character is separated by a space character. Return value is a
- vector which includes char ** data element. */
-vector
+/*------------------------------------------------------------------------------
+ * 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)
{
const char *cp, *start;
@@ -258,58 +279,69 @@ cmd_make_strvec (const char *string)
cp = string;
- /* Skip white spaces. */
- while (isspace ((int) *cp) && *cp != '\0')
+ /* Skip white spaces. */
+ while (isspace((int) *cp))
cp++;
- /* Return if there is only white spaces */
- if (*cp == '\0')
+ /* Return if line is empty or effectively empty */
+ if ((*cp == '\0') || (*cp == '!') || (*cp == '#'))
return NULL;
- if (*cp == '!' || *cp == '#')
- return NULL;
+ /* Prepare return vector -- expect some reasonable number of tokens. */
+ strvec = vector_init (10) ;
- /* Prepare return vector. */
- strvec = vector_init (0);
-
- /* Copy each command piece and set into vector. */
+ /* Copy each command piece and set into vector. */
while (1)
{
start = cp;
- while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
- *cp != '\0')
- 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_set (strvec, token);
+ vector_push_item(strvec, token);
- while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
- *cp != '\0')
- cp++;
+ while (isspace((int) *cp))
+ cp++ ; /* skip white-space */
if (*cp == '\0')
return strvec;
}
}
-/* Free allocated string vector. */
-void
-cmd_free_strvec (vector v)
+/*------------------------------------------------------------------------------
+ * Add given string to vector of strings.
+ *
+ * Create vector if required.
+ */
+extern vector
+cmd_add_to_strvec (vector strvec, const char* str)
{
- unsigned int i;
- char *cp;
+ if (strvec == NULL)
+ strvec = vector_init(1) ;
- if (!v)
- return;
+ vector_push_item(strvec, XSTRDUP(MTYPE_STRVEC, str));
- for (i = 0; i < vector_active (v); i++)
- if ((cp = vector_slot (v, i)) != NULL)
- XFREE (MTYPE_STRVEC, cp);
+ return strvec ;
+} ;
- vector_free (v);
-}
+/*------------------------------------------------------------------------------
+ * Free allocated string vector (if any) and all its contents.
+ *
+ * Note that this is perfectly happy with strvec == NULL.
+ */
+extern void
+cmd_free_strvec (vector strvec)
+{
+ char *cp;
+
+ /* Note that vector_ream_free() returns NULL if strvec == NULL */
+ while((cp = vector_ream_free(strvec)) != NULL)
+ XFREE (MTYPE_STRVEC, cp);
+} ;
+
+/*----------------------------------------------------------------------------*/
/* Fetch next description. Used in cmd_make_descvec(). */
static char *
@@ -472,7 +504,19 @@ cmd_prompt (enum node_type node)
{
struct cmd_node *cnode;
- cnode = vector_slot (cmdvec, node);
+ assert(cmdvec != NULL) ;
+ assert(cmdvec->p_items != NULL) ;
+
+ cnode = NULL ;
+ if (node < cmdvec->limit)
+ cnode = vector_slot (cmdvec, node);
+
+ if (cnode == NULL)
+ {
+ zlog_err("Could not find prompt for node %d for", node) ;
+ return NULL ;
+ } ;
+
return cnode->prompt;
}
@@ -683,21 +727,41 @@ cmd_filter_by_symbol (char *command, char *symbol)
}
#endif
+/*==============================================================================
+ * Match functions.
+ *
+ * Is the given string a, possibly incomplete, value of the required kind ?
+ */
+
/* Completion match types. */
enum match_type
{
- no_match,
+ no_match, /* nope */
extend_match,
+
ipv4_prefix_match,
ipv4_match,
ipv6_prefix_match,
ipv6_match,
range_match,
vararg_match,
- partly_match,
- exact_match
+
+ partly_match, /* OK as far as it went */
+ exact_match /* Syntactically complete */
};
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Address:
+ *
+ * 999.999.999.999 -- where no part may be > 255
+ *
+ * TODO: cmd_ipv4_match() seems to accept leading '.' ?
+ * TODO: cmd_ipv4_match() seems to accept leading zeros ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ */
static enum match_type
cmd_ipv4_match (const char *str)
{
@@ -755,6 +819,22 @@ cmd_ipv4_match (const char *str)
return exact_match;
}
+/*------------------------------------------------------------------------------
+ * Is this an IPv4 Prefix:
+ *
+ * 999.999.999.999/99 -- where no part may be > 255,
+ * and prefix length may not be > 32
+ *
+ * TODO: cmd_ipv4_prefix_match() seems to accept leading '.' ?
+ * TODO: cmd_ipv4_prefix_match() seems to accept leading zeros ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
static enum match_type
cmd_ipv4_prefix_match (const char *str)
{
@@ -834,6 +914,16 @@ cmd_ipv4_prefix_match (const char *str)
return exact_match;
}
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Address:
+ *
+ * TODO: cmd_ipv6_match() only returns "partly_match" for empty string ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ */
+
#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
#define STATE_START 1
@@ -952,6 +1042,19 @@ cmd_ipv6_match (const char *str)
return exact_match;
}
+/*------------------------------------------------------------------------------
+ * Is this an IPv6 Prefix:
+ *
+ * TODO: cmd_ipv6_prefix_match() hardly returns "partly_match" ?
+ * TODO: cmd_ipv6_prefix_match() possibly accepts invalid address before '/' ?
+ *
+ * Returns: no_match -- improperly formed
+ * partly_match -- accepts empty string
+ * exact_match -- syntactically complete
+ *
+ * NB: partly_match is returned for anything valid before the '/', but which
+ * has no '/' or no number after the '/'.
+ */
static enum match_type
cmd_ipv6_prefix_match (const char *str)
{
@@ -1085,6 +1188,14 @@ cmd_ipv6_prefix_match (const char *str)
#endif /* HAVE_IPV6 */
+/*------------------------------------------------------------------------------
+ * Is this a decimal number in the allowed range:
+ *
+ * Returns: 1 => OK -- *including* empty string
+ * 0 => not a valid number, or not in required range
+ * (or invalid range !!)
+ */
+
#define DECIMAL_STRLEN_MAX 10
static int
@@ -1132,124 +1243,182 @@ cmd_range_match (const char *range, const char *str)
return 1;
}
-/* Make completion match and return match type flag. */
+/*==============================================================================
+ * Command "filtering".
+ *
+ * The command parsing process starts with a (shallow) copy of the cmd_vector
+ * entry for the current "node".
+ *
+ * So cmd_v contains pointers to struct cmd_element values. When match fails,
+ * the pointer is set NULL -- so parsing is a process of reducing the cmd_v
+ * down to just the entries that match.
+ *
+ * Each cmd_element has a vector "strvec", which contains an entry for each
+ * "token" position. That entry is a vector containing the possible values at
+ * that position.
+ *
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * Make completion match and return match type flag.
+ *
+ * Takes: command -- address of candidate token
+ * cmd_v -- vector of commands that is being reduced/filtered
+ * index -- index of token (position in line -- 0 == first)
+ *
+ * Returns: any of the enum match_type values:
+ *
+ * no_match => no match of any kind
+ *
+ * extend_match => saw an optional token
+ * ipv4_prefix_match )
+ * ipv4_match )
+ * ipv6_prefix_match ) saw full or partial match for this
+ * ipv6_match )
+ * range_match )
+ * vararg_match )
+ *
+ * partly_match => saw partial match for a keyword
+ * exact_match => saw exact match for a keyword
+ *
+ * Note that these return values are in ascending order of preference. So,
+ * if there are multiple possibilities at this position, will return the one
+ * furthest down this list.
+ */
static enum match_type
-cmd_filter_by_completion (char *command, vector v, unsigned int index)
+cmd_filter_by_completion (char *command, vector cmd_v, unsigned int index)
{
unsigned int i;
- const char *str;
- struct cmd_element *cmd_element;
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 (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
- {
- if (index >= vector_active (cmd_element->strvec))
- vector_slot (v, i) = NULL;
- else
- {
- unsigned int j;
- int matched = 0;
+ for (i = 0; i < vector_active (cmd_v); i++)
+ {
+ const char *str;
+ struct cmd_element *cmd_element;
+ vector descvec;
+ struct desc *desc;
+ unsigned int j;
+ int matched ;
- descvec = vector_slot (cmd_element->strvec, index);
+ /* Skip past cmd_v entries that have already been set NULL */
+ if ((cmd_element = vector_slot (cmd_v, i)) == NULL)
+ continue ;
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- str = desc->cmd;
+ /* 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 ;
+ } ;
- 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;
+ /* See if get any sort of match at current position */
+ matched = 0 ;
+ descvec = vector_slot (cmd_element->strvec, index);
- matched++;
- }
- }
+ 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;
+
+ matched++;
+ }
+ }
#ifdef HAVE_IPV6
- 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 (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++;
+ }
+ }
#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++;
+ } ;
+ } ;
+
+ /* Discard cmd_v entry that has no match at this position */
+ if (!matched)
+ vector_slot (cmd_v, i) = NULL;
+ }
- 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
- /* Check is this point's argument optional ? */
- if (CMD_OPTION (str) || CMD_VARIABLE (str))
- {
- 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++;
- }
- }
- if (!matched)
- vector_slot (v, i) = NULL;
- }
- }
return match_type;
}
-/* Filter vector by command character with index. */
+/*------------------------------------------------------------------------------
+ * Filter vector by command character with index.
+ *
+ * This appears to be identical to cmd_filter_by_completion(), except that
+ * when matching keywords, requires an exact match.
+ *
+ * TODO: see if can merge cmd_filter_by_completion() & cmd_filter_by_string()
+ */
static enum match_type
-cmd_filter_by_string (char *command, vector v, unsigned int index)
+cmd_filter_by_string (char *command, vector cmd_v, unsigned int index)
{
unsigned int i;
const char *str;
@@ -1261,13 +1430,13 @@ cmd_filter_by_string (char *command, vector v, unsigned int index)
match_type = no_match;
/* If command and cmd_element string does not match set NULL to vector */
- for (i = 0; i < vector_active (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
+ 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 (v, i) = NULL;
+ vector_slot (cmd_v, i) = NULL;
else
{
unsigned int j;
@@ -1349,15 +1518,49 @@ cmd_filter_by_string (char *command, vector v, unsigned int index)
}
}
if (!matched)
- vector_slot (v, i) = NULL;
+ vector_slot (cmd_v, i) = NULL;
}
}
return match_type;
}
-/* Check ambiguous match */
+/*------------------------------------------------------------------------------
+ * Check for ambiguous match
+ *
+ * Given the best that cmd_filter_by_completion() or cmd_filter_by_string()
+ * found, check as follows:
+ *
+ * 1. discard all commands for which do not have the type of match selected.
+ *
+ * See above for the ranking of matches.
+ *
+ * 2. for "partial match", look out for matching more than one keyword, and
+ * return 1 if finds that.
+ *
+ * 3. for "range match", look out for matching more than one range, and
+ * return 1 if finds that.
+ *
+ * 4. for ipv4_prefix_match and ipv6_prefix_match, if get a "partial match",
+ * return 2.
+ *
+ * This appears to catch things which are supposed to be prefixes, but
+ * do not have a '/' or do not have any digits after the '/'.
+ *
+ * Takes: command -- address of candidate token
+ * cmd_v -- vector of commands that is being reduced/filtered
+ * index -- index of token (position in line -- 0 == first)
+ * type -- as returned by cmd_filter_by_completion()
+ * or cmd_filter_by_string()
+ *
+ * Returns: 0 => not ambiguous
+ * 1 => ambiguous -- the candidate token matches more than one
+ * keyword, or the candidate number matches more
+ * than one number range.
+ * 2 => partial match for ipv4_prefix or ipv6_prefix
+ * (missing '/' or no digits after '/').
+ */
static int
-is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
+is_cmd_ambiguous (char *command, vector cmd_v, int index, enum match_type type)
{
unsigned int i;
unsigned int j;
@@ -1367,8 +1570,8 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
vector descvec;
struct desc *desc;
- for (i = 0; i < vector_active (v); i++)
- if ((cmd_element = vector_slot (v, i)) != NULL)
+ for (i = 0; i < vector_active (cmd_v); i++)
+ if ((cmd_element = vector_slot (cmd_v, i)) != NULL)
{
int match = 0;
@@ -1447,25 +1650,27 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type)
}
}
if (!match)
- vector_slot (v, i) = NULL;
+ vector_slot (cmd_v, i) = NULL;
}
return 0;
}
-/* If src matches dst return dst string, otherwise return NULL */
+/*------------------------------------------------------------------------------
+ * If src matches dst return dst string, otherwise return NULL
+ *
+ * Returns NULL if dst is an option, variable of vararg.
+ *
+ * NULL or empty src are deemed to match.
+ */
static const char *
cmd_entry_function (const char *src, const char *dst)
{
- /* Skip variable arguments. */
- if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) ||
- CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst))
+ if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst))
return NULL;
- /* In case of 'command \t', given src is NULL string. */
- if (src == NULL)
+ if ((src == NULL) || (*src == '\0'))
return dst;
- /* Matched with input string. */
if (strncmp (src, dst, strlen (src)) == 0)
return dst;
@@ -1537,8 +1742,12 @@ cmd_entry_function_desc (const char *src, const char *dst)
return NULL;
}
-/* Check same string element existence. If it isn't there return
- 1. */
+/*------------------------------------------------------------------------------
+ * Check same string element existence.
+ *
+ * Returns: 0 => found same string in the vector
+ * 1 => NOT found same string in the vector
+ */
static int
cmd_unique_string (vector v, const char *str)
{
@@ -1569,15 +1778,9 @@ desc_unique_string (vector v, const char *str)
static int
cmd_try_do_shortcut (enum node_type node, char* first_word) {
- if ( first_word != NULL &&
- node != AUTH_NODE &&
- node != VIEW_NODE &&
- node != AUTH_ENABLE_NODE &&
- node != ENABLE_NODE &&
- node != RESTRICTED_NODE &&
- 0 == strcmp( "do", first_word ) )
- return 1;
- return 0;
+ return (node >= MIN_DO_SHORTCUT_NODE)
+ && (first_word != NULL)
+ && (strcmp( "do", first_word) == 0) ? 1 : 0 ;
}
/* '?' describe command support. */
@@ -1717,6 +1920,19 @@ cmd_describe_command_real (vector vline, int node, int *status)
return matchvec;
}
+/*------------------------------------------------------------------------------
+ * Get description of current (partial) command
+ *
+ * Returns: NULL => no description available
+ *
+ * status set to CMD_ERR_NO_MATCH or CMD_ERR_AMBIGUOUS
+ *
+ * or: address of vector of "struct desc" values available.
+ *
+ * NB: when a vector is returned it is the caller's responsibility to
+ * vector_free() it. (The contents are all effectively const, so do not
+ * themselves need to be freed.)
+ */
vector
cmd_describe_command (vector vline, int node, int *status)
{
@@ -1745,134 +1961,166 @@ cmd_describe_command (vector vline, int node, int *status)
return cmd_describe_command_real (vline, node, status);
}
-
-/* Check LCD of matched command. */
+/*------------------------------------------------------------------------------
+ * Check LCD of matched command.
+ *
+ * Scan list of matched keywords, and by comparing them pair-wise, find the
+ * longest common leading substring.
+ *
+ * Returns: 0 if zero or one matched keywords
+ * length of longest common leading substring, otherwise.
+ */
static int
-cmd_lcd (char **matched)
+cmd_lcd (vector matchvec)
{
- int i;
- int j;
- int lcd = -1;
- char *s1, *s2;
- char c1, c2;
+ int n ;
+ int i ;
+ int lcd ;
+ char *sp, *sq, *ss ;
- if (matched[0] == NULL || matched[1] == NULL)
- return 0;
+ n = vector_end(matchvec) ;
+ if (n < 2)
+ return 0 ;
- for (i = 1; matched[i] != NULL; i++)
+ ss = vector_get_item(matchvec, 0) ;
+ lcd = strlen(ss) ;
+
+ for (i = 1 ; i < n ; i++)
{
- s1 = matched[i - 1];
- s2 = matched[i];
+ sq = ss ;
+ ss = vector_get_item(matchvec, i) ;
+ sp = ss ;
- for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
- if (c1 != c2)
- break;
+ while ((*sp == *sq) && (*sp != '\0'))
+ {
+ ++sp ;
+ ++sq ;
+ } ;
- if (lcd < 0)
- lcd = j;
- else
- {
- if (lcd > j)
- lcd = j;
- }
+ if (lcd > (sp - ss))
+ lcd = (sp - ss) ;
}
return lcd;
}
-/* Command line completion support. */
-static char **
+/*------------------------------------------------------------------------------
+ * Command line completion support.
+ */
+static vector
cmd_complete_command_real (vector vline, int node, int *status)
{
unsigned int i;
- vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
+ unsigned int ivl ;
+ unsigned int last_ivl ;
+ vector cmd_v ;
#define INIT_MATCHVEC_SIZE 10
vector matchvec;
struct cmd_element *cmd_element;
unsigned int index;
- char **match_str;
struct desc *desc;
vector descvec;
- char *command;
- int lcd;
+ char *token;
+ int n ;
+ /* Stop immediately if the vline is empty. */
if (vector_active (vline) == 0)
{
- vector_free (cmd_vector);
*status = CMD_ERR_NO_MATCH;
return NULL;
}
- else
- index = vector_active (vline) - 1;
- /* First, filter by preceeding command string */
- for (i = 0; i < index; i++)
- if ((command = vector_slot (vline, i)))
- {
- enum match_type match;
- int ret;
+ /* Take (shallow) copy of cmdvec for given node. */
+ cmd_v = vector_copy (cmd_node_vector (cmdvec, node));
- /* First try completion match, if there is exactly match return 1 */
- match = cmd_filter_by_completion (command, cmd_vector, i);
+ /* First, filter upto, but excluding last token */
+ last_ivl = vector_active (vline) - 1;
- /* If there is exact match then filter ambiguous match else check
- ambiguousness. */
- if ((ret = is_cmd_ambiguous (command, cmd_vector, i, match)) == 1)
- {
- vector_free (cmd_vector);
- *status = CMD_ERR_AMBIGUOUS;
- return NULL;
- }
- /*
- else if (ret == 2)
- {
- vector_free (cmd_vector);
+ for (ivl = 0; ivl < last_ivl; ivl++)
+ {
+ enum match_type match;
+ int ret;
+
+ /* TODO: does this test make any sense ? */
+ if ((token = vector_slot (vline, ivl)) == NULL)
+ continue ;
+
+ /* First try completion match, return best kind of match */
+ index = ivl ;
+ match = cmd_filter_by_completion (token, cmd_v, index) ;
+
+ /* Eliminate all but the selected kind of match */
+ ret = is_cmd_ambiguous (token, cmd_v, index, match) ;
+
+ if (ret == 1)
+ {
+ /* ret == 1 => either token matches more than one keyword
+ * or token matches more than one number range
+ */
+ vector_free (cmd_v);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ }
+#if 0
+ /* For command completion purposes do not appear to care about
+ * incomplete ipv4 or ipv6 prefixes (missing '/' or digits after).
+ */
+ else if (ret == 2)
+ {
+ vector_free (cmd_v);
*status = CMD_ERR_NO_MATCH;
return NULL;
- }
- */
+ }
+#endif
}
- /* Prepare match vector. */
+ /* Prepare match vector. */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
- /* Now we got into completion */
- for (i = 0; i < vector_active (cmd_vector); i++)
- if ((cmd_element = vector_slot (cmd_vector, i)))
- {
- const char *string;
- vector strvec = cmd_element->strvec;
+ /* Now we got into completion */
+ index = last_ivl ;
+ token = vector_slot(vline, last_ivl) ; /* is now the last token */
- /* Check field length */
- if (index >= vector_active (strvec))
- vector_slot (cmd_vector, i) = NULL;
- else
- {
- unsigned int j;
+ for (i = 0; i < vector_active (cmd_v); i++)
+ {
+ unsigned int j;
+ const char *string;
- descvec = vector_slot (strvec, index);
- for (j = 0; j < vector_active (descvec); j++)
- if ((desc = vector_slot (descvec, j)))
- {
- if ((string =
- cmd_entry_function (vector_slot (vline, index),
- desc->cmd)))
- if (cmd_unique_string (matchvec, string))
- vector_set (matchvec, XSTRDUP (MTYPE_TMP, string));
- }
- }
- }
+ if ((cmd_element = vector_slot (cmd_v, i)) == NULL)
+ continue ;
- /* We don't need cmd_vector any more. */
- vector_free (cmd_vector);
+ vector strvec = cmd_element->strvec;
+
+ /* Check field length */
+ if (index >= vector_active (strvec))
+ {
+ vector_slot (cmd_v, i) = NULL;
+ continue ;
+ }
+
+ descvec = vector_slot (strvec, index);
+
+ 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) ;
+ }
+ } ;
+
+ n = vector_end(matchvec) ; /* number of entries in the matchvec */
+
+ /* We don't need cmd_v any more. */
+ vector_free (cmd_v);
/* No matched command */
- if (vector_slot (matchvec, 0) == NULL)
+ if (n == 0)
{
vector_free (matchvec);
/* In case of 'command \t' pattern. Do you need '?' command at
the end of the line. */
- if (vector_slot (vline, index) == '\0')
+ if (*token == '\0')
*status = CMD_ERR_NOTHING_TODO;
else
*status = CMD_ERR_NO_MATCH;
@@ -1880,66 +2128,50 @@ cmd_complete_command_real (vector vline, int node, int *status)
}
/* XXX: TODO: stop poking around inside vector */
- /* Only one matched */
- if (vector_slot (matchvec, 1) == NULL)
+ /* Only one matched */
+ if (n == 1)
{
- match_str = (char **) matchvec->VECTOR_INDEX;
- vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_FULL_MATCH;
- return match_str;
+ return matchvec ;
}
- /* Make it sure last element is NULL. */
- vector_set (matchvec, NULL);
- /* Check LCD of matched strings. */
- if (vector_slot (vline, index) != NULL)
+ /* Check LCD of matched strings. */
+ if (token != NULL)
{
- lcd = cmd_lcd ((char **) matchvec->VECTOR_INDEX);
+ unsigned lcd = cmd_lcd (matchvec) ;
- if (lcd)
+ if (lcd != 0)
{
- int len = strlen (vector_slot (vline, index));
-
- if (len < lcd)
+ if (strlen(token) < lcd)
{
char *lcdstr;
lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1);
- memcpy (lcdstr, matchvec->VECTOR_INDEX[0], lcd);
+ memcpy (lcdstr, vector_get_item(matchvec, 0), lcd) ;
lcdstr[lcd] = '\0';
- /* match_str = (char **) &lcdstr; */
+ cmd_free_strvec(matchvec) ; /* discard the match vector */
- /* Free matchvec. */
- for (i = 0; i < vector_active (matchvec); i++)
- {
- if (vector_slot (matchvec, i))
- XFREE (MTYPE_STRVEC, vector_slot (matchvec, i));
- }
- vector_free (matchvec);
-
- /* Make new matchvec. */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
- vector_set (matchvec, lcdstr);
- match_str = (char **) matchvec->VECTOR_INDEX;
- vector_only_wrapper_free (matchvec);
+ vector_push_item(matchvec, lcdstr) ;
*status = CMD_COMPLETE_MATCH;
- return match_str;
+ return matchvec ;
}
}
}
- match_str = (char **) matchvec->VECTOR_INDEX;
- vector_only_wrapper_free (matchvec);
*status = CMD_COMPLETE_LIST_MATCH;
- return match_str;
+ return matchvec ;
}
-char **
+/*------------------------------------------------------------------------------
+ * Can the current command be completed ?
+ */
+extern vector
cmd_complete_command (vector vline, int node, int *status)
{
- char **ret;
+ vector ret;
if ( cmd_try_do_shortcut(node, vector_slot(vline, 0) ) )
{
@@ -1964,13 +2196,14 @@ cmd_complete_command (vector vline, int node, int *status)
return cmd_complete_command_real (vline, node, status);
}
-/* return parent node */
-/* MUST eventually converge on CONFIG_NODE */
+/*------------------------------------------------------------------------------
+ * Return parent node
+ *
+ * All nodes > CONFIG_NODE are descended from CONFIG_NODE
+ */
enum node_type
node_parent ( enum node_type node )
{
- enum node_type ret;
-
assert (node > CONFIG_NODE);
switch (node)
@@ -1980,69 +2213,101 @@ node_parent ( enum node_type node )
case BGP_IPV4M_NODE:
case BGP_IPV6_NODE:
case BGP_IPV6M_NODE:
- ret = BGP_NODE;
- break;
+ return BGP_NODE;
+
case KEYCHAIN_KEY_NODE:
- ret = KEYCHAIN_NODE;
- break;
- default:
- ret = CONFIG_NODE;
- }
+ return KEYCHAIN_NODE;
- return ret;
-}
+ default:
+ return CONFIG_NODE;
+ }
+}
+
+/*------------------------------------------------------------------------------
+ * 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()
+ *
+ * Takes the node from parsed->cnode.
+ *
+ * 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.
+ *
+ * otherwise => some failure to parse
+ *
+ * NB: the argv[] in the parsed structure contains *copies* of the char*
+ * pointers in the given vline.
+ *
+ * The vline may not be released until the parsed structure is.
+ *
+ * The parsed structure may be released without worrying about the contents
+ * of the argv[].
+ */
+enum cmd_parse_type
+{
+ cmd_parse_completion = 0,
+ cmd_parse_strict = 1,
+};
-/* Execute command by argument vline vector. */
static int
-cmd_execute_command_real (vector vline, struct vty *vty,
- struct cmd_element **cmd, qpn_nexus dest_nexus)
+cmd_parse_command(vector vline, int first, struct cmd_parsed* parsed,
+ enum cmd_parse_type type)
{
- unsigned int i;
- unsigned int index;
- vector cmd_vector;
+ unsigned int i ;
+ unsigned int ivl ;
+ unsigned index ;
+ vector cmd_v;
struct cmd_element *cmd_element;
struct cmd_element *matched_element;
unsigned int matched_count, incomplete_count;
int argc;
- const char *argv[CMD_ARGC_MAX];
enum match_type match = 0;
int varflag;
char *command;
- /* Make copy of command elements. */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty_get_node(vty)));
+ int strict = (type == cmd_parse_strict) ;
- for (index = 0; index < vector_active (vline); index++)
- if ((command = vector_slot (vline, index)))
+ parsed->cmd = NULL ; /* return this if parsing fails */
+ parsed->argc = 0 ;
+
+ /* 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;
+ int ret ;
- match = cmd_filter_by_completion (command, cmd_vector, index);
+ index = ivl - first ;
+ match = strict ? cmd_filter_by_string(command, cmd_v, index)
+ : cmd_filter_by_completion(command, cmd_v, index) ;
if (match == vararg_match)
break;
- ret = is_cmd_ambiguous (command, cmd_vector, index, match);
+ ret = is_cmd_ambiguous (command, cmd_v, index, match);
- if (ret == 1)
+ if (ret != 0)
{
- vector_free (cmd_vector);
- return CMD_ERR_AMBIGUOUS;
- }
- else if (ret == 2)
- {
- vector_free (cmd_vector);
- return CMD_ERR_NO_MATCH;
+ 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;
+ /* Check matched count. */
+ matched_element = NULL;
+ matched_count = 0;
incomplete_count = 0;
- for (i = 0; i < vector_active (cmd_vector); i++)
- if ((cmd_element = vector_slot (cmd_vector, i)))
+ 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)
{
@@ -2058,133 +2323,255 @@ cmd_execute_command_real (vector vline, struct vty *vty,
}
}
- /* Finish of using cmd_vector. */
- vector_free (cmd_vector);
+ /* Finished with cmd_v. */
+ vector_free (cmd_v);
- /* To execute command, matched_count must be 1. */
- if (matched_count == 0)
+ /* To execute command, matched_count must be 1. */
+ if (matched_count != 1)
{
- if (incomplete_count)
- return CMD_ERR_INCOMPLETE;
+ if (matched_count == 0)
+ return (incomplete_count) ? CMD_ERR_INCOMPLETE : CMD_ERR_NO_MATCH ;
else
- return CMD_ERR_NO_MATCH;
- }
-
- if (matched_count > 1)
- return CMD_ERR_AMBIGUOUS;
+ return CMD_ERR_AMBIGUOUS ;
+ } ;
- /* Argument treatment */
- varflag = 0;
- argc = 0;
+ /* Found command -- process the arguments ready for execution */
+ varflag = 0 ;
+ argc = 0 ;
- for (i = 0; i < vector_active (vline); i++)
+ for (ivl = first; ivl < vector_active (vline); ivl++)
{
- if (varflag)
- argv[argc++] = vector_slot (vline, i);
- else
+ int take = varflag ;
+
+ if (!varflag)
{
- vector descvec = vector_slot (matched_element->strvec, i);
+ int index = ivl - first ;
+ vector descvec = vector_slot (matched_element->strvec, index);
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);
+ take = varflag = 1 ;
+ else
+ take = (CMD_VARIABLE (desc->cmd) || CMD_OPTION (desc->cmd)) ;
}
else
- argv[argc++] = vector_slot (vline, i);
+ take = 1 ;
}
- if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXEED_ARGC_MAX;
- }
+ if (take)
+ {
+ if (argc >= CMD_ARGC_MAX)
+ return CMD_ERR_EXCEED_ARGC_MAX ;
+ parsed->argv[argc++] = vector_slot (vline, ivl);
+ } ;
+ } ;
- /* For vtysh execution. */
- if (cmd)
- *cmd = matched_element;
+ /* Everything checks out... ready to execute command */
+ parsed->cmd = matched_element ;
+ parsed->argc = argc ;
- if (matched_element->daemon)
+ if (parsed->cmd->daemon)
return CMD_SUCCESS_DAEMON;
- /* Execute matched command. */
- if (qpthreads_enabled && !(matched_element->attr & CMD_ATTR_CALL))
- {
- /* Don't do it now, but send to bgp qpthread */
- cq_enqueue(matched_element, vty, argc, argv, dest_nexus);
- return CMD_QUEUED;
- }
- else
- {
- return (*matched_element->func) (matched_element, vty, argc, argv);
- }
-}
+ 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.
+ *
+ * CMD_SUCCESS_DAEMON => as CMD_SUCCESS, and the command has a
+ * "daemon" value.
+ *
+ * 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.
+ */
+static int
+cmd_parse_command_tree(vector vline, int first, struct cmd_parsed* parsed,
+ enum cmd_parse_type type)
+{
+ int ret ;
+ int first_ret ;
+ enum node_type first_node ;
-int
-cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
- qpn_nexus dest_nexus, int vtysh) {
- int ret, saved_ret, tried = 0;
- enum node_type onode, try_node;
+ /* Try in the current node */
+ ret = cmd_parse_command(vline, first, parsed, type) ;
- onode = try_node = vty_get_node(vty);
+ if ((ret == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON))
+ return ret ; /* done if found command */
- if ( cmd_try_do_shortcut(vty_get_node(vty), vector_slot(vline, 0) ) )
- {
- vector shifted_vline;
- unsigned int index;
+ /* Try in parent node(s) */
+ first_node = parsed->cnode ;
+ first_ret = ret ;
- vty_set_node(vty, ENABLE_NODE);
- /* We can try it on enable node, cos' the vty is authenticated */
+ while (parsed->cnode <= CONFIG_NODE)
+ {
+ parsed->cnode = node_parent(parsed->cnode) ;
+ ret = cmd_parse_command(vline, first, parsed, type) ;
- shifted_vline = vector_init (vector_count(vline));
- /* use memcpy? */
- for (index = 1; index < vector_active (vline); index++)
- {
- vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
- }
+ if ((ret == CMD_SUCCESS) || (ret == CMD_SUCCESS_DAEMON))
+ return ret ; /* done if found command */
+ } ;
- ret = cmd_execute_command_real (shifted_vline, vty, cmd, dest_nexus);
+ parsed->cnode = first_node ; /* restore node state */
+ return first_ret ; /* return original result */
+}
- vector_free(shifted_vline);
- vty_set_node(vty, onode);
- return ret;
- }
+/*------------------------------------------------------------------------------
+ * 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) ;
- saved_ret = ret = cmd_execute_command_real (vline, vty, cmd, dest_nexus);
+ 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 ;
+ }
+ else
+ {
+ parsed->do_shortcut = 0 ;
+ first = 0 ;
+ } ;
+
+ return first ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Tidy up after executing command.
+ *
+ * This is separated out so that can be called when queued command completes.
+ *
+ * If have just processed a "do" shortcut command, and it has not set the
+ * vty->node to something other than ENABLE_NODE, then restore to the original
+ * state.
+ *
+ * Arguments: ret = CMD_XXXX -- NB: CMD_QUEUED => command revoked
+ * action = true unless command return is being revoked
+ */
+extern void
+cmd_post_command(struct vty* vty, struct cmd_parsed* parsed, int ret,
+ int action)
+{
+ if (parsed->do_shortcut)
+ {
+ if (vty_get_node(vty) == ENABLE_NODE)
+ vty_set_node(vty, parsed->onode) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Execute command:
+ *
+ * -- use cmd_parse_completion type parsing.
+ *
+ * -- unless vtysh: if does not parse in current node, try ancestors
+ *
+ * If does not find in any ancestor, return error from current node.
+ *
+ * -- implement the "do" shortcut
+ *
+ * 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.
+ */
+extern int
+cmd_execute_command (vector vline, struct vty *vty,
+ struct cmd_element **cmd, qpn_nexus to_nexus,
+ qpn_nexus from_nexus, int vtysh)
+{
+ 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)
- return saved_ret;
+ ret = cmd_parse_command(vline, first, &parsed, cmd_parse_completion) ;
+ else
+ ret = cmd_parse_command_tree(vline, first, &parsed, cmd_parse_completion) ;
- /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
- while ( ret != CMD_SUCCESS && ret != CMD_WARNING
- && vty_get_node(vty) > CONFIG_NODE )
+ if (cmd)
+ *cmd = parsed.cmd ; /* for vtysh */
+
+ if (ret != CMD_SUCCESS)
+ return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */
+
+ /* Execute the parsed command */
+
+ vty_set_node(vty, parsed.cnode) ;
+
+ if (qpthreads_enabled && !(parsed.cmd->attr & CMD_ATTR_CALL))
{
- try_node = node_parent(try_node);
- vty_set_node(vty, try_node);
- ret = cmd_execute_command_real (vline, vty, cmd, dest_nexus);
- tried = 1;
- if (ret == CMD_SUCCESS || ret == CMD_WARNING)
- {
- /* succesfull command, leave the node as is */
- return ret;
- }
+ /* Don't do it now, but send to bgp qpthread */
+ cq_enqueue(vty, &parsed, to_nexus, from_nexus) ;
+ ret = CMD_QUEUED ;
}
- /* no command succeeded, reset the vty to the original node and
- return the error for this node */
- if ( tried )
- vty_set_node(vty, onode);
- return saved_ret;
-}
-
-/* Execute command by argument readline. */
-int
+ else
+ {
+ ret = (*(parsed.cmd->func))(parsed.cmd, vty, parsed.argc, parsed.argv) ;
+
+ cmd_post_command(vty, &parsed, ret, 1) ;
+ } ;
+
+ return ret ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Execute command by argument readline.
+ *
+ * -- use cmd_parse_strict type parsing.
+ *
+ * -- unless vtysh: if does not parse in current node, try ancestors
+ *
+ * If does not find in any ancestor, return error from current node.
+ *
+ * -- does NOT implement the "do" shortcut
+ *
+ * At all times executes the command immediately (no queueing, even if
+ * qpthreads_enabled).
+ *
+ * The vty->node may be changed either when parsing or executing the command.
+ */
+extern int
cmd_execute_command_strict (vector vline, struct vty *vty,
- struct cmd_element **cmd)
+ struct cmd_element **cmd, int vtysh)
{
+#if 0 /* replaced by cmd_parse_command() */
unsigned int i;
unsigned int index;
vector cmd_vector;
@@ -2285,62 +2672,74 @@ cmd_execute_command_strict (vector vline, struct vty *vty,
}
if (argc >= CMD_ARGC_MAX)
- return CMD_ERR_EXEED_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) ;
- /* For vtysh execution. */
if (cmd)
- *cmd = matched_element;
+ *cmd = parsed.cmd ; /* for vtysh */
- if (matched_element->daemon)
- return CMD_SUCCESS_DAEMON;
+ if (ret != CMD_SUCCESS)
+ return ret ; /* NB: CMD_SUCCESS_DAEMON exits here */
- /* Now execute matched command */
- return (*matched_element->func) (matched_element, vty, argc, argv);
+ /* Now execute matched command */
+ vty_set_node(vty, parsed.cnode) ;
+
+ return (*(parsed.cmd->func)) (parsed.cmd, vty, parsed.argc, parsed.argv);
}
-/* Configration make from file. */
+/*------------------------------------------------------------------------------
+ * Read configuration from file.
+ *
+ */
int
-config_from_file (struct vty *vty, FILE *fp, void (*after_first_cmd)(void))
+config_from_file (struct vty *vty, FILE *fp, void (*after_first_cmd)(void),
+ qstring buf)
{
int ret;
vector vline;
int first_cmd = 1;
- while (fgets (vty->buf, VTY_BUFSIZ, fp))
+ while (fgets (buf->body, buf->size, fp))
{
- vline = cmd_make_strvec (vty->buf);
+ vline = cmd_make_strvec (buf->body);
- /* In case of comment line */
+ /* In case of comment line */
if (vline == NULL)
continue;
- /* Execute configuration command : this is strict match */
- ret = cmd_execute_command_strict (vline, vty, NULL);
- /* special handling for after the first command */
+ /* Execute configuration command : this is strict match */
+ ret = cmd_execute_command_strict (vline, vty, NULL, 0);
+
+ /* special handling for after the first command */
if (first_cmd && after_first_cmd)
{
after_first_cmd();
first_cmd = 0;
}
- /* Try again with setting node to CONFIG_NODE */
- while (ret != CMD_SUCCESS && ret != CMD_WARNING
- && ret != CMD_ERR_NOTHING_TODO && vty_get_node(vty) != CONFIG_NODE)
- {
- vty_set_node(vty, node_parent(vty_get_node(vty)));
- ret = cmd_execute_command_strict (vline, vty, NULL);
- }
-
cmd_free_strvec (vline);
+ /* TODO: why does config file not stop on CMD_WARNING ?? */
if (ret != CMD_SUCCESS && ret != CMD_WARNING
- && ret != CMD_ERR_NOTHING_TODO)
+ && ret != CMD_ERR_NOTHING_TODO)
return ret;
- }
+ } ;
+
return CMD_SUCCESS;
}
+/*----------------------------------------------------------------------------*/
+
/* Configration from terminal */
DEFUN_CALL (config_terminal,
config_terminal_cmd,
@@ -2348,14 +2747,11 @@ DEFUN_CALL (config_terminal,
"Configuration from vty interface\n"
"Configuration terminal\n")
{
- if (vty_config_lock (vty))
- vty_set_node(vty, CONFIG_NODE);
- else
- {
- vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
- return CMD_SUCCESS;
+ if (vty_config_lock (vty, CONFIG_NODE))
+ return CMD_SUCCESS;
+
+ vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
+ return CMD_WARNING;
}
/* Enable command */
@@ -2391,47 +2787,7 @@ DEFUN_CALL (config_exit,
"exit",
"Exit current mode and down to previous mode\n")
{
- switch (vty_get_node(vty))
- {
- case VIEW_NODE:
- case ENABLE_NODE:
- case RESTRICTED_NODE:
- if (vty_shell (vty))
- exit (0);
- else
- vty_set_status(vty, VTY_CLOSE);
- break;
- case CONFIG_NODE:
- vty_set_node(vty, ENABLE_NODE);
- vty_config_unlock (vty);
- break;
- case INTERFACE_NODE:
- case ZEBRA_NODE:
- case BGP_NODE:
- case RIP_NODE:
- case RIPNG_NODE:
- case OSPF_NODE:
- case OSPF6_NODE:
- case ISIS_NODE:
- case KEYCHAIN_NODE:
- case MASC_NODE:
- case RMAP_NODE:
- case VTY_NODE:
- vty_set_node(vty, CONFIG_NODE);
- break;
- case BGP_VPNV4_NODE:
- case BGP_IPV4_NODE:
- case BGP_IPV4M_NODE:
- case BGP_IPV6_NODE:
- case BGP_IPV6M_NODE:
- vty_set_node(vty, BGP_NODE);
- break;
- case KEYCHAIN_KEY_NODE:
- vty_set_node(vty, KEYCHAIN_NODE);
- break;
- default:
- break;
- }
+ vty_cmd_exit(vty) ;
return CMD_SUCCESS;
}
@@ -2447,38 +2803,7 @@ DEFUN_CALL (config_end,
"end",
"End current mode and change to enable mode.")
{
- switch (vty_get_node(vty))
- {
- 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:
- vty_config_unlock (vty);
- vty_set_node(vty, ENABLE_NODE);
- break;
- default:
- break;
- }
+ vty_cmd_end(vty) ;
return CMD_SUCCESS;
}
@@ -2539,7 +2864,7 @@ DEFUN_CALL (config_list,
}
/* Write current configuration into file. */
-DEFUN_CALL (config_write_file,
+DEFUN (config_write_file,
config_write_file_cmd,
"write file",
"Write running configuration to memory, network, or terminal\n"
@@ -2645,18 +2970,18 @@ finished:
return ret;
}
-ALIAS_CALL (config_write_file,
+ALIAS (config_write_file,
config_write_cmd,
"write",
"Write running configuration to memory, network, or terminal\n")
-ALIAS_CALL (config_write_file,
+ALIAS (config_write_file,
config_write_memory_cmd,
"write memory",
"Write running configuration to memory, network, or terminal\n"
"Write configuration to the file (same as write file)\n")
-ALIAS_CALL (config_write_file,
+ALIAS (config_write_file,
copy_runningconfig_startupconfig_cmd,
"copy running-config startup-config",
"Copy configuration\n"
@@ -2664,7 +2989,7 @@ ALIAS_CALL (config_write_file,
"Copy running config to startup config (same as write file)\n")
/* Write current configuration into the terminal. */
-DEFUN_CALL (config_write_terminal,
+DEFUN (config_write_terminal,
config_write_terminal_cmd,
"write terminal",
"Write running configuration to memory, network, or terminal\n"
@@ -2700,14 +3025,14 @@ DEFUN_CALL (config_write_terminal,
}
/* Write current configuration into the terminal. */
-ALIAS_CALL (config_write_terminal,
+ALIAS (config_write_terminal,
show_running_config_cmd,
"show running-config",
SHOW_STR
"running configuration\n")
/* Write startup configuration into the terminal. */
-DEFUN_CALL (show_startup_config,
+DEFUN (show_startup_config,
show_startup_config_cmd,
"show startup-config",
SHOW_STR
@@ -2753,10 +3078,16 @@ DEFUN_CALL (config_hostname,
return CMD_WARNING;
}
+ VTY_LOCK() ;
+
if (host.name)
XFREE (MTYPE_HOST, host.name);
host.name = XSTRDUP (MTYPE_HOST, argv[0]);
+ uty_set_host_name(host.name) ;
+
+ VTY_UNLOCK() ;
+
return CMD_SUCCESS;
}
@@ -2767,9 +3098,15 @@ DEFUN_CALL (config_no_hostname,
"Reset system's network name\n"
"Host name of this router\n")
{
+ VTY_LOCK() ;
+
if (host.name)
XFREE (MTYPE_HOST, host.name);
host.name = NULL;
+ uty_set_host_name(host.name) ;
+
+ VTY_UNLOCK() ;
+
return CMD_SUCCESS;
}
diff --git a/lib/command.h b/lib/command.h
index b1cf18a4..0e8b3630 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -8,7 +8,7 @@
* 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
@@ -23,8 +23,11 @@
#ifndef _ZEBRA_COMMAND_H
#define _ZEBRA_COMMAND_H
+#include "node_type.h"
#include "vector.h"
+#include "uty.h"
#include "vty.h"
+#include "qstring.h"
#include "lib/route_types.h"
/* Host configuration variable */
@@ -59,69 +62,24 @@ struct host
char *motdfile;
};
-/* There are some command levels which called from command node. */
-enum node_type
-{
- AUTH_NODE, /* Authentication mode of vty interface. */
- RESTRICTED_NODE, /* Restricted view mode */
- VIEW_NODE, /* View node. Default mode of vty interface. */
- AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
- ENABLE_NODE, /* Enable node. */
- CONFIG_NODE, /* Config node. Default mode of config file. */
- SERVICE_NODE, /* Service node. */
- DEBUG_NODE, /* Debug node. */
- AAA_NODE, /* AAA node. */
- KEYCHAIN_NODE, /* Key-chain node. */
- KEYCHAIN_KEY_NODE, /* Key-chain key node. */
- INTERFACE_NODE, /* Interface mode node. */
- ZEBRA_NODE, /* zebra connection node. */
- TABLE_NODE, /* rtm_table selection node. */
- RIP_NODE, /* RIP protocol mode node. */
- RIPNG_NODE, /* RIPng protocol mode node. */
- BGP_NODE, /* BGP protocol mode which includes BGP4+ */
- BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */
- BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */
- BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */
- BGP_IPV6_NODE, /* BGP IPv6 address family */
- BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */
- OSPF_NODE, /* OSPF protocol mode */
- OSPF6_NODE, /* OSPF protocol for IPv6 mode */
- ISIS_NODE, /* ISIS protocol mode */
- MASC_NODE, /* MASC for multicast. */
- IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
- IP_NODE, /* Static ip route node. */
- ACCESS_NODE, /* Access list node. */
- PREFIX_NODE, /* Prefix list node. */
- ACCESS_IPV6_NODE, /* Access list node. */
- PREFIX_IPV6_NODE, /* Prefix list node. */
- AS_LIST_NODE, /* AS list node. */
- COMMUNITY_LIST_NODE, /* Community list node. */
- RMAP_NODE, /* Route map node. */
- SMUX_NODE, /* SNMP configuration node. */
- DUMP_NODE, /* Packet dump node. */
- FORWARDING_NODE, /* IP forwarding node. */
- PROTOCOL_NODE, /* protocol filtering node */
- VTY_NODE, /* Vty node. */
-};
-
/* Node which has some commands and prompt string and configuration
function pointer . */
-struct cmd_node
+struct cmd_node
{
/* Node index. */
- enum node_type node;
+ enum node_type node;
/* Prompt character at vty interface. */
- const char *prompt;
+ const char *prompt;
/* Is this node's configuration goes to vtysh ? */
int vtysh;
-
+
/* Node's configuration write function */
int (*func) (struct vty *);
/* Vector of this node's command list. */
- vector cmd_vector;
+ vector cmd_vector;
};
enum
@@ -132,8 +90,8 @@ enum
CMD_ATTR_CALL = 0x04,
};
-/* Structure of command element. */
-struct cmd_element
+/* Structure of command element. */
+struct cmd_element
{
const char *string; /* Command specification by string. */
int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
@@ -146,32 +104,64 @@ struct cmd_element
u_char attr; /* Command attributes */
};
-/* Command description structure. */
+/* Command description structure. */
struct desc
{
char *cmd; /* Command string. */
char *str; /* Command's description. */
};
-/* Return value of the commands. */
-#define CMD_SUCCESS 0
-#define CMD_WARNING 1
-#define CMD_ERR_NO_MATCH 2
-#define CMD_ERR_AMBIGUOUS 3
-#define CMD_ERR_INCOMPLETE 4
-#define CMD_ERR_EXEED_ARGC_MAX 5
-#define CMD_ERR_NOTHING_TODO 6
-#define CMD_COMPLETE_FULL_MATCH 7
-#define CMD_COMPLETE_MATCH 8
-#define CMD_COMPLETE_LIST_MATCH 9
-#define CMD_SUCCESS_DAEMON 10
-#define CMD_QUEUED 11
-
/* Argc max counts. */
#define CMD_ARGC_MAX 25
+/* Parsed command */
+struct cmd_parsed
+{
+ struct cmd_element *cmd ;
+
+ enum node_type cnode ; /* node command is in */
+
+ int do_shortcut ; /* true => is "do" command */
+ enum node_type onode ; /* vty->node before "do" */
+
+ int argc ;
+ const char* argv[CMD_ARGC_MAX] ;
+} ;
+
+/* 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
+} ;
+
+#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"
+
/* Turn off these macros when uisng cpp with extract.pl */
-#ifndef VTYSH_EXTRACT_PL
+#ifndef VTYSH_EXTRACT_PL
/* helper defines for end-user DEFUN* macros */
#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
@@ -352,15 +342,21 @@ extern void sort_node (void);
extern char *argv_concat (const char **argv, int argc, int shift);
extern vector cmd_make_strvec (const char *);
+extern vector cmd_add_to_strvec (vector v, const char* str) ;
extern void cmd_free_strvec (vector);
extern vector cmd_describe_command (vector, int, int *status);
-extern char **cmd_complete_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));
+extern int config_from_file (struct vty *, FILE *, void (*)(void), qstring buf);
extern enum node_type node_parent (enum node_type);
-extern int cmd_execute_command (vector, struct vty *, struct cmd_element **,
- qpn_nexus, int);
-extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **);
+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 void config_replace_string (struct cmd_element *, char *, ...);
extern void cmd_init (int);
extern void cmd_terminate (void);
@@ -377,7 +373,7 @@ extern void host_config_set (char *);
extern void print_version (const char *);
/* struct host global, ick */
-extern struct host host;
+extern struct host host;
/* "<cr>" global */
extern char *command_cr;
diff --git a/lib/command_queue.c b/lib/command_queue.c
index b14bcd20..b6aab580 100644
--- a/lib/command_queue.c
+++ b/lib/command_queue.c
@@ -26,56 +26,130 @@
#include "memory.h"
#include "command_queue.h"
-/* Prototypes */
+/*------------------------------------------------------------------------------
+ * Form of message passed with command to be executed
+ */
+
+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 */
+} ;
+MQB_ARGS_SIZE_OK(cq_command_args) ;
+
+/*------------------------------------------------------------------------------
+ * Prototypes
+ */
static void cq_action(mqueue_block mqb, mqb_flag_t flag);
+static void cq_return(mqueue_block mqb, mqb_flag_t flag);
+/*------------------------------------------------------------------------------
+ * Enqueue vty and argv[] for execution in given nexus.
+ */
void
-cq_enqueue(struct cmd_element *matched_element, struct vty *vty,
- int argc, const char *argv[], qpn_nexus bgp_nexus)
+cq_enqueue(struct vty *vty, struct cmd_parsed* parsed, qpn_nexus to_nexus,
+ qpn_nexus from_nexus)
{
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 ;
- /* all parameters are pointers so use the queue's argv */
- mqb_push_argv_p(mqb, matched_element);
- for (i = 0; i < argc; ++i)
- mqb_push_argv_p(mqb, XSTRDUP(MTYPE_MARSHAL, argv[i]));
+ args->ret_nexus = from_nexus ;
+ args->ret = CMD_SUCCESS ;
- mqueue_enqueue(bgp_nexus->queue, mqb, 0) ;
+ for (i = 0; i < parsed->argc; ++i)
+ mqb_push_argv_p(mqb, XSTRDUP(MTYPE_MARSHAL, parsed->argv[i]));
+
+ mqueue_enqueue(to_nexus->queue, mqb, 0) ;
}
-/* dispatch a command from the message queue block */
+/*------------------------------------------------------------------------------
+ * Dispatch a command from the message queue block
+ *
+ * When done (or revoked/deleted) return the message, so that the sender knows
+ * that the command has been dealt with (one way or another).
+ */
static void
cq_action(mqueue_block mqb, mqb_flag_t flag)
{
- int result;
- int i;
- struct cmd_element *matched_element;
struct vty *vty;
- void **argv;
- int argc;
+ struct cq_command_args* args ;
- vty = mqb_get_arg0(mqb);
- argc = mqb_get_argv_count(mqb);
- argv = mqb_get_argv(mqb) ;
-
- matched_element = argv[0];
- argv++;
- argc--;
+ vty = mqb_get_arg0(mqb);
+ args = mqb_get_args(mqb) ;
if (flag == mqb_action)
{
- /* Execute matched command. */
- result = (matched_element->func)
- (matched_element, vty, argc, (const char **)argv);
+ const char** argv = mqb_get_argv(mqb) ;
- /* report */
- vty_queued_result(vty, result);
+ args->ret = (args->cmd->func)(args->cmd, vty, args->argc, argv) ;
}
+ else
+ args->ret = CMD_QUEUED ;
+
+ mqb_set_action(mqb, cq_return) ;
+ mqueue_enqueue(args->ret_nexus->queue, mqb, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept return from command executed in another thread.
+ *
+ * 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.
+ */
+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)) ;
+
+ /* update the state of the vty -- passing the "action" state */
+ vty_queued_result(vty, args->ret, (flag == mqb_action));
- /* clean up */
- for (i = 0; i < argc; ++i)
- XFREE(MTYPE_MARSHAL, argv[i]);
+ if (qpthreads_enabled)
+ qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
mqb_free(mqb);
}
diff --git a/lib/command_queue.h b/lib/command_queue.h
index 90a4f7b6..257c9552 100644
--- a/lib/command_queue.h
+++ b/lib/command_queue.h
@@ -25,7 +25,8 @@
#include "command.h"
#include "qpnexus.h"
-extern void cq_enqueue(struct cmd_element *matched_element, struct vty *vty,
- int argc, const char *argv[], qpn_nexus);
+extern void
+cq_enqueue(struct vty *vty, struct cmd_parsed* parsed, qpn_nexus to_nexus,
+ qpn_nexus from_nexus) ;
#endif /* COMMAND_QUEUE_H_ */
diff --git a/lib/if.c b/lib/if.c
index c9a9b335..b1d0d49a 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -1,10 +1,10 @@
-/*
+/*
* Interface functions.
* 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
@@ -35,7 +35,7 @@
#include "buffer.h"
#include "str.h"
#include "log.h"
-
+
/* Master list of interfaces. */
struct list *iflist;
@@ -45,7 +45,7 @@ struct if_master
int (*if_new_hook) (struct interface *);
int (*if_delete_hook) (struct interface *);
} if_master;
-
+
/* Compare interface names, returning an integer greater than, equal to, or
* less than 0, (following the strcmp convention), according to the
* relationship between ifp1 and ifp2. Interface names consist of an
@@ -53,7 +53,7 @@ struct if_master
* lexicographic by name, and then numeric by number. No number sorts
* before all numbers. Examples: de0 < de1, de100 < fxp0 < xl0, devpty <
* devpty0, de0 < del0
- */
+ */
int
if_cmp_func (struct interface *ifp1, struct interface *ifp2)
{
@@ -87,9 +87,9 @@ if_cmp_func (struct interface *ifp1, struct interface *ifp2)
p1 += l1;
p2 += l1;
- if (!*p1)
+ if (!*p1)
return -1;
- if (!*p2)
+ if (!*p2)
return 1;
x1 = strtol(p1, &p1, 10);
@@ -119,7 +119,7 @@ if_create (const char *name, int namelen)
ifp = XCALLOC (MTYPE_IF, sizeof (struct interface));
ifp->ifindex = IFINDEX_INTERNAL;
-
+
assert (name);
assert (namelen <= INTERFACE_NAMSIZ); /* Need space for '\0' at end. */
strncpy (ifp->name, name, namelen);
@@ -215,7 +215,7 @@ if_lookup_by_name (const char *name)
{
struct listnode *node;
struct interface *ifp;
-
+
if (name)
for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
{
@@ -262,7 +262,7 @@ if_lookup_exact_address (struct in_addr src)
{
if (IPV4_ADDR_SAME (&p->u.prefix4, &src))
return ifp;
- }
+ }
}
}
return NULL;
@@ -434,12 +434,12 @@ if_dump (const struct interface *ifp)
"mtu6 %d "
#endif /* HAVE_IPV6 */
"%s",
- ifp->name, ifp->ifindex, ifp->metric, ifp->mtu,
+ ifp->name, ifp->ifindex, ifp->metric, ifp->mtu,
#ifdef HAVE_IPV6
ifp->mtu6,
#endif /* HAVE_IPV6 */
if_flag_dump (ifp->flags));
-
+
for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, c))
;
}
@@ -455,7 +455,7 @@ if_dump_all (void)
if_dump (p);
}
-DEFUN (interface_desc,
+DEFUN (interface_desc,
interface_desc_cmd,
"description .LINE",
"Interface specific description\n"
@@ -474,7 +474,7 @@ DEFUN (interface_desc,
return CMD_SUCCESS;
}
-DEFUN (no_interface_desc,
+DEFUN (no_interface_desc,
no_interface_desc_cmd,
"no description",
NO_STR
@@ -489,7 +489,7 @@ DEFUN (no_interface_desc,
return CMD_SUCCESS;
}
-
+
#ifdef SUNOS_5
/* Need to handle upgrade from SUNWzebra to Quagga. SUNWzebra created
* a seperate struct interface for each logical interface, so config
@@ -519,11 +519,11 @@ if_sunwzebra_get (const char *name, size_t nlen)
if ( (ifp = if_lookup_by_name_len(name, nlen)) != NULL)
return ifp;
-
+
/* hunt the primary interface name... */
while (seppos < nlen && name[seppos] != ':')
seppos++;
-
+
/* Wont catch seperator as last char, e.g. 'foo0:' but thats invalid */
if (seppos < nlen)
return if_get_by_name_len (name, seppos);
@@ -531,7 +531,7 @@ if_sunwzebra_get (const char *name, size_t nlen)
return if_get_by_name_len (name, nlen);
}
#endif /* SUNOS_5 */
-
+
DEFUN (interface,
interface_cmd,
"interface IFNAME",
@@ -556,7 +556,7 @@ DEFUN (interface,
#endif /* SUNOS_5 */
vty->index = ifp;
- vty->node = INTERFACE_NODE;
+ vty_set_node(vty, INTERFACE_NODE) ;
return CMD_SUCCESS;
}
@@ -579,7 +579,7 @@ DEFUN_NOSH (no_interface,
return CMD_WARNING;
}
- if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
+ if (CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
{
vty_out (vty, "%% Only inactive interfaces can be deleted%s",
VTY_NEWLINE);
@@ -649,11 +649,11 @@ connected_log (struct connected *connected, char *str)
struct interface *ifp;
char logbuf[BUFSIZ];
char buf[BUFSIZ];
-
+
ifp = connected->ifp;
p = connected->address;
- snprintf (logbuf, BUFSIZ, "%s interface %s %s %s/%d ",
+ snprintf (logbuf, BUFSIZ, "%s interface %s %s %s/%d ",
str, ifp->name, prefix_family_str (p),
inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
p->prefixlen);
@@ -734,7 +734,7 @@ connected_lookup_address (struct interface *ifp, struct in_addr dst)
}
struct connected *
-connected_add_by_prefix (struct interface *ifp, struct prefix *p,
+connected_add_by_prefix (struct interface *ifp, struct prefix *p,
struct prefix *destination)
{
struct connected *ifc;
@@ -782,7 +782,7 @@ if_indextoname (unsigned int ifindex, char *name)
return ifp->name;
}
#endif
-
+
#if 0 /* this route_table of struct connected's is unused
* however, it would be good to use a route_table rather than
* a list..
@@ -852,7 +852,7 @@ ifaddr_ipv4_lookup (struct in_addr *addr, unsigned int ifindex)
rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
if (! rn)
return NULL;
-
+
ifp = rn->info;
route_unlock_node (rn);
return ifp;
diff --git a/lib/keychain.c b/lib/keychain.c
index 41c463f9..2f8a0b77 100644
--- a/lib/keychain.c
+++ b/lib/keychain.c
@@ -74,7 +74,7 @@ key_cmp_func (void *arg1, void *arg2)
{
const struct key *k1 = arg1;
const struct key *k2 = arg2;
-
+
if (k1->index > k2->index)
return 1;
if (k1->index < k2->index)
@@ -226,7 +226,7 @@ key_delete (struct keychain *keychain, struct key *key)
free (key->string);
key_free (key);
}
-
+
DEFUN (key_chain,
key_chain_cmd,
"key chain WORD",
@@ -238,7 +238,7 @@ DEFUN (key_chain,
keychain = keychain_get (argv[0]);
vty->index = keychain;
- vty->node = KEYCHAIN_NODE;
+ vty_set_node(vty, KEYCHAIN_NODE) ;
return CMD_SUCCESS;
}
@@ -281,8 +281,8 @@ DEFUN (key,
VTY_GET_INTEGER ("key identifier", index, argv[0]);
key = key_get (keychain, index);
vty->index_sub = key;
- vty->node = KEYCHAIN_KEY_NODE;
-
+ vty_set_node(vty, KEYCHAIN_KEY_NODE) ;
+
return CMD_SUCCESS;
}
@@ -296,7 +296,7 @@ DEFUN (no_key,
struct keychain *keychain;
struct key *key;
u_int32_t index;
-
+
keychain = vty->index;
VTY_GET_INTEGER ("key identifier", index, argv[0]);
@@ -309,7 +309,7 @@ DEFUN (no_key,
key_delete (keychain, key);
- vty->node = KEYCHAIN_NODE;
+ vty_set_node(vty, KEYCHAIN_NODE) ;
return CMD_SUCCESS;
}
@@ -353,7 +353,7 @@ DEFUN (no_key_string,
/* Convert HH:MM:SS MON DAY YEAR to time_t value. -1 is returned when
given string is malformed. */
-static time_t
+static time_t
key_str2time (const char *time_str, const char *day_str, const char *month_str,
const char *year_str)
{
@@ -364,7 +364,7 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
unsigned int sec, min, hour;
unsigned int day, month, year;
- const char *month_name[] =
+ const char *month_name[] =
{
"January",
"February",
@@ -392,7 +392,7 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
return -1; \
(V) = tmpl; \
}
-
+
/* Check hour field of time_str. */
colon = strchr (time_str, ':');
if (colon == NULL)
@@ -416,10 +416,10 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
time_str = colon + 1;
if (*time_str == '\0')
return -1;
-
+
/* Sec must be between 0 and 59. */
GET_LONG_RANGE (sec, time_str, 0, 59);
-
+
/* Check day_str. Day must be <1-31>. */
GET_LONG_RANGE (day, day_str, 1, 31);
@@ -437,7 +437,7 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
/* Check year_str. Year must be <1993-2035>. */
GET_LONG_RANGE (year, year_str, 1993, 2035);
-
+
memset (&tm, 0, sizeof (struct tm));
tm.tm_sec = sec;
tm.tm_min = min;
@@ -445,9 +445,9 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str,
tm.tm_mon = month;
tm.tm_mday = day;
tm.tm_year = year - 1900;
-
+
time = mktime (&tm);
-
+
return time;
#undef GET_LONG_RANGE
}
@@ -461,7 +461,7 @@ key_lifetime_set (struct vty *vty, struct key_range *krange,
{
time_t time_start;
time_t time_end;
-
+
time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str);
if (time_start < 0)
{
@@ -496,7 +496,7 @@ key_lifetime_duration_set (struct vty *vty, struct key_range *krange,
{
time_t time_start;
u_int32_t duration;
-
+
time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str);
if (time_start < 0)
{
@@ -518,7 +518,7 @@ key_lifetime_infinite_set (struct vty *vty, struct key_range *krange,
const char *smonth_str, const char *syear_str)
{
time_t time_start;
-
+
time_start = key_str2time (stime_str, sday_str, smonth_str, syear_str);
if (time_start < 0)
{
@@ -531,7 +531,7 @@ key_lifetime_infinite_set (struct vty *vty, struct key_range *krange,
return CMD_SUCCESS;
}
-
+
DEFUN (accept_lifetime_day_month_day_month,
accept_lifetime_day_month_day_month_cmd,
"accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>",
@@ -689,7 +689,7 @@ DEFUN (accept_lifetime_duration_month_day,
return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[2],
argv[1], argv[3], argv[4]);
}
-
+
DEFUN (send_lifetime_day_month_day_month,
send_lifetime_day_month_day_month_cmd,
"send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>",
@@ -847,7 +847,7 @@ DEFUN (send_lifetime_duration_month_day,
return key_lifetime_duration_set (vty, &key->send, argv[0], argv[2], argv[1],
argv[3], argv[4]);
}
-
+
static struct cmd_node keychain_node =
{
KEYCHAIN_NODE,
@@ -887,7 +887,7 @@ keychain_config_write (struct vty *vty)
for (ALL_LIST_ELEMENTS_RO (keychain_list, node, keychain))
{
vty_out (vty, "key chain %s%s", keychain->name, VTY_NEWLINE);
-
+
for (ALL_LIST_ELEMENTS_RO (keychain->key, knode, key))
{
vty_out (vty, " key %d%s", key->index, VTY_NEWLINE);
diff --git a/lib/keystroke.c b/lib/keystroke.c
new file mode 100644
index 00000000..09f0fed0
--- /dev/null
+++ b/lib/keystroke.c
@@ -0,0 +1,1007 @@
+/* Keystroke Buffering
+ * 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 "string.h"
+
+#include "keystroke.h"
+
+#include "list_util.h"
+
+#include "memory.h"
+#include "mqueue.h"
+#include "zassert.h"
+
+/*==============================================================================
+ */
+
+/*------------------------------------------------------------------------------
+ * Parsing of incoming keystrokes.
+ *
+ * At present this code only handles 8-bit characters, which one assumes are
+ * ISO8859-1 (or similar). The encoding of keystrokes allows for characters
+ * of up to 32-bits -- so could parse UTF-8 (or similar).
+ *
+ * Handles:
+ *
+ * 0. Null, returned as:
+ *
+ * type = ks_null
+ * value = knull_eof
+ * knull_not_eof
+ *
+ * len = 0
+ * truncated = false
+ * broken = false
+ * buf -- not used
+ *
+ * This is returned when there is nothing else available.
+ *
+ * 1. Characters, returned as:
+ *
+ * type = ks_char
+ * value = 0x0.. -- the character value
+ * '\0' if truncated, malformed or EOF met
+ *
+ * len = 1..n => length of character representation, or
+ * number of bytes in raw form if no good.
+ * truncated => too long for buffers
+ * broken => malformed or EOF met
+ * buf -- if OK, the representation for the character (UTF-8 ?)
+ * if truncated or broken, the raw bytes
+ *
+ * 2. ESC X -- where X is single character, other than '['.
+ *
+ * Returned as:
+ *
+ * type = ks_esc
+ * value = 0x0.. -- the character value X
+ * ('\0' if hit EOF)
+ *
+ * len = 1 (or 0 if EOF met)
+ * truncated = false
+ * broken => EOF met
+ * buf = copy of X (unless EOF met)
+ *
+ * 3. ESC [ ... X or CSI ... X -- ANSI escape
+ *
+ * Returned as:
+ *
+ * type = ks_csi
+ * value = 0x0.. -- the character value X
+ * ('\0' if hit EOF or malformed)
+ *
+ * len = number of bytes in buf (excluding '\0' terminator)
+ * truncated => too long for buffers
+ * broken => malformed or EOF met
+ * buf -- bytes between ESC [ or CSI and terminating X
+ * NB: '\0' terminated.
+ *
+ * Note: an ANSI escape is malformed if a byte outside the range
+ * 0x20..0x7F is found before the terminating byte. The illegal
+ * byte is deemed not to be part of the sequence.
+ *
+ * 4. Telnet command -- IAC X ...
+ *
+ * IAC IAC is treated as character 0xFF, so appears as a ks_char, or
+ * possibly as a component of an ESC or other sequence.
+ *
+ * Returned as:
+ *
+ * type = ks_iac
+ * value = 0x0.. -- the value of X
+ * ('\0' if hit EOF)
+ *
+ * len = number of bytes in buf (0 if hit EOF after IAC)
+ * truncated => too long for buffers
+ * broken => malformed or EOF met
+ * buf -- the X and any further bytes,
+ * but *excluding* any terminating IAC SE
+ *
+ * Note: a Telnet command may appear at any time, including in the
+ * middle of any of the above "real" keystrokes.
+ *
+ * This is made invisible to the "real" keystrokes, and the Telnet
+ * command(s) will appear before the incomplete real keystroke in
+ * the keystroke stream.
+ *
+ * Note: there are three forms of Telnet command, and one escape.
+ *
+ * 1) IAC IAC is an escape, and maps simply to the value 0xFF,
+ * wherever it appears (except when reading an <option>).
+ *
+ * 2) IAC X -- two bytes, where X < 250 (SB)
+ *
+ * 3) IAC X O -- three bytes, where X is 251..254 (WILL/WONT/DO/DONT)
+ *
+ * the O may be 0x00..0xFF (where 0xFF is *NOT* escaped)
+ *
+ * 4) IAC SB O ... IAC SE -- many bytes.
+ *
+ * the O may be 0x00..0xFF (where 0xFF is *NOT* escaped)
+ *
+ * the data ... is subject to IAC IAC escaping (so that
+ * the terminating IAC SE is unambiguous).
+ *
+ * This implementation treats IAC X (where X is not IAC
+ * and not SE) as an error -- terminating the command
+ * before the IAC, and marking it malformed. The IAC X
+ * will then be taken as a new command.
+ *
+ * Extended Option objects (O = 0xFF) are exotic, but the above will
+ * parse them.
+ */
+
+/*------------------------------------------------------------------------------
+ * Encoding of keystrokes in the keystroke FIFO.
+ *
+ * The first byte of a keystroke is as follows:
+ *
+ * 1. 0x00..0x7F -- simple character -- complete keystroke
+ *
+ * 2. 0x80..0xFF -- compound keystroke -- further bytes (may) follow
+ *
+ * A compound keystroke comprises:
+ *
+ * 1. type of keystroke -- see enum keystroke_type
+ *
+ * 2. number of bytes that make up the keystroke
+ *
+ * This is limited to keystroke_max_len. Any keystroke longer than that
+ * is marked as truncated.
+ *
+ * 3. the bytes (0..keystroke_max_len of them)
+ *
+ * This is encoded as <first> <length> [ <bytes> ]
+ *
+ * Where:
+ *
+ * <first> is 0x80..0xFF (as above):
+ *
+ * b7 = 1
+ * b6 = 0 : "reserved"
+ * b5 : 0 => OK
+ * 1 => broken object
+ * b4 : 0 => OK
+ * 1 => truncated
+ *
+ * b3..0 : type of keystroke -- so 16 types of keystroke
+ *
+ * <length> is 0..keystroke_max_len
+ *
+ * <bytes> (if present) are the bytes:
+ *
+ * ks_null -- should not appear in the FIFO
+ *
+ * ks_char -- character value, in network order, length 1..4
+ *
+ * BUT, if broken or truncated, the raw bytes received (must
+ * be at least one).
+ *
+ * ks_esc -- byte that followed the ESC, length = 0..1
+ *
+ * Length 0 => EOF met => broken
+ *
+ * ks_csi -- bytes that followed the ESC [ or CSI, length 1..n
+ *
+ * The last byte is *always* the byte that terminated the
+ * sequence, or '\0' if is badly formed, or hit EOF.
+ *
+ * If the sequence is truncated, then the last byte of the
+ * sequence is written over the last buffered byte.
+ *
+ * ks_iac -- bytes of the telnet command, excluding the leading
+ * IAC and the trailing IAC SE, length 1..n.
+ *
+ * IAC IAC pairs are not telnet commands.
+ *
+ * IAC IAC pairs between SB X O and IAC SE are reduced to
+ * 0xFF.
+ *
+ * Telnet commands are broken if EOF is met before the end
+ * of the command, or get IAC X between SB X O and IAC SE
+ * (where X is not IAC or SE).
+ */
+
+enum stream_state
+{
+ kst_null, /* nothing special (but see iac) */
+
+ kst_char, /* collecting a multi-byte character */
+ kst_esc, /* collecting an ESC sequence */
+ kst_csi, /* collecting an ESC '[' or CSI sequence */
+
+ kst_iac_option, /* waiting for option (just seen IAC X) */
+ kst_iac_sub, /* waiting for IAC SE */
+} ;
+
+struct keystroke_state
+{
+ enum stream_state state ;
+ unsigned len ;
+ uint8_t raw[keystroke_max_len] ;
+} ;
+
+struct keystroke_stream
+{
+ vio_fifo_t fifo ; /* the keystrokes */
+
+ uint8_t CSI ; /* CSI character value (if any) */
+
+ bool eof_met ; /* nothing more to come */
+
+ bool steal_this ; /* steal current keystroke when complete */
+
+ bool iac ; /* last character was an IAC */
+
+ struct keystroke_state in ; /* current keystroke being collected */
+
+ struct keystroke_state pushed_in ;
+ /* keystroke interrupted by IAC */
+} ;
+
+/* Buffering of keystrokes */
+
+enum { keystroke_buffer_len = 2000 } ; /* should be plenty ! */
+
+/*------------------------------------------------------------------------------
+ * Prototypes
+ */
+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) ;
+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_steal_char(keystroke steal, keystroke_stream stream,
+ uint8_t u) ;
+static void keystroke_steal_esc(keystroke steal, keystroke_stream stream,
+ uint8_t u) ;
+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) ;
+
+/*==============================================================================
+ * Creating and freeing keystroke streams and keystroke stream buffers.
+ */
+
+/*------------------------------------------------------------------------------
+ * Create and initialise a keystroke stream.
+ *
+ * Can set CSI character value. '\0' => none. (As does '\x1B' !)
+ */
+extern keystroke_stream
+keystroke_stream_new(uint8_t csi_char)
+{
+ keystroke_stream stream ;
+
+ stream = XCALLOC(MTYPE_KEY_STREAM, sizeof(struct keystroke_stream)) ;
+
+ /* Zeroising the structure sets:
+ *
+ * eof_met = false -- no EOF yet
+ * steal = false -- no stealing set
+ * iac = false -- last character was not an IAC
+ *
+ * in.state = kst_null
+ * in.len = 0 -- nothing in the buffer
+ *
+ * pushed_in.state ) ditto
+ * pushed_in.len )
+ */
+ confirm(kst_null == 0) ;
+
+ vio_fifo_init_new(&stream->fifo, keystroke_buffer_len) ;
+
+ stream->CSI = (csi_char != '\0') ? csi_char : 0x1B ;
+
+ return stream ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free keystroke stream and all associated buffers.
+ */
+extern void
+keystroke_stream_free(keystroke_stream stream)
+{
+ if (stream == NULL)
+ return ;
+
+ vio_fifo_reset_keep(&stream->fifo) ;
+
+ XFREE(MTYPE_KEY_STREAM, stream) ;
+} ;
+
+/*==============================================================================
+ * Keystroke stream state
+ */
+
+/*------------------------------------------------------------------------------
+ * See if given keystroke stream is empty
+ *
+ * May or may not be at "EOF", see below.
+ *
+ * Returns: true <=> is empty
+ */
+extern bool
+keystroke_stream_empty(keystroke_stream stream)
+{
+ return vio_fifo_empty(&stream->fifo) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * See if given keystroke stream is at "EOF", that is:
+ *
+ * * keystroke stream is empty
+ *
+ * * there is no partial keystroke in construction
+ *
+ * * EOF has been signalled by a suitable call of keystroke_input().
+ *
+ * Returns: true <=> is at EOF
+ */
+extern bool
+keystroke_stream_eof(keystroke_stream stream)
+{
+ /* Note that when EOF is signalled, any partial keystroke in construction
+ * 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set keystroke stream to "EOF", that is:
+ *
+ * * discard contents of the stream, including any partial keystroke.
+ *
+ * * set the stream "eof_met".
+ */
+extern void
+keystroke_stream_set_eof(keystroke_stream stream)
+{
+ vio_fifo_reset_keep(&stream->fifo) ;
+
+ stream->eof_met = 1 ; /* essential information */
+
+ stream->steal_this = 0 ; /* keep tidy */
+ stream->iac = 0 ;
+ stream->in.state = kst_null ;
+ stream->pushed_in.state = kst_null ;
+} ;
+
+
+/*==============================================================================
+ * Input raw bytes to given keyboard stream.
+ *
+ * To steal the next keystroke, pass 'steal' = address of a keystroke structure.
+ * Otherwise, pass NULL.
+ *
+ * Note: when trying to steal, will complete any partial keystroke before
+ * stealing the next one. May exit from here:
+ *
+ * a. without having completed the partial keystroke.
+ *
+ * b. without having completed the keystroke to be stolen.
+ *
+ * State (b) is remembered by the keystroke_stream.
+ *
+ * Caller may have to call several times with steal != NULL to get a
+ * keystroke.
+ *
+ * If steal != NULL the keystroke will be set to the stolen keystroke. That
+ * will be type ks_null if nothing was available, and may be knull_eof.
+ *
+ * Note that never steals broken or truncated keystrokes.
+ *
+ * Passing len == 0 and ptr == NULL signals EOF to the keystroke_stream.
+ *
+ * Updates the stream and returns updated raw
+ */
+extern void
+keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
+ keystroke steal)
+{
+ uint8_t* end ;
+
+ /* Deal with EOF if required
+ *
+ * Any partial keystroke is converted into a broken keystroke and placed
+ * at the end of the stream.
+ *
+ * Note that this occurs before any attempt to steal a keystroke -- so can
+ * never steal a broken keystroke.
+ */
+ if ((len == 0) && (ptr == NULL))
+ {
+ stream->eof_met = 1 ;
+ stream->steal_this = 0 ;
+
+ 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.
+ */
+ while (stream->in.state != kst_null)
+ {
+ switch (stream->in.state)
+ {
+ case kst_esc: /* expecting rest of escape */
+ keystroke_put_esc(stream, '\0', 0) ;
+ stream->in.state = kst_null ;
+ break ;
+
+ case kst_csi:
+ keystroke_put_csi(stream, '\0') ;
+ stream->in.state = kst_null ;
+ break ;
+
+ case kst_iac_option: /* expecting rest of IAC */
+ case kst_iac_sub:
+ keystroke_put_iac_long(stream, 1) ;
+ /* pops the stream->pushed_in */
+ break ;
+
+ case kst_char: /* TBD */
+ zabort("impossible keystroke stream state") ;
+
+ default:
+ zabort("unknown keystroke stream state") ;
+ } ;
+ } ;
+ } ;
+
+ /* Update the stealing state
+ *
+ * steal != NULL => want to steal a keystroke
+ *
+ * If do not wish to steal now, must clear any
+ * remembered steal_this state.
+ *
+ * stream->steal_this => steal the next keystroke to complete.
+ *
+ * If want to steal a keystroke, this is set if
+ * currently "between" keystrokes, or later when
+ * reach that condition.
+ *
+ * Once set, this is remembered across calls to
+ * keystroke_input(), while still wish to steal.
+ */
+ if (steal == NULL)
+ stream->steal_this = 0 ; /* clear as not now required */
+ else
+ stream->steal_this = (stream->in.state == kst_null) ;
+ /* want to and can can steal the next
+ keystroke that completes */
+
+ /* Once EOF has been signalled, do not expect to receive any further input.
+ *
+ * However, keystroke_stream_set_eof() can set the stream artificially at
+ * EOF, and that is honoured here.
+ */
+ if (stream->eof_met)
+ len = 0 ;
+
+ /* Normal processing
+ *
+ * Note that when manages to steal a keystroke sets steal == NULL, and
+ * proceeds to collect any following keystrokes.
+ */
+ end = ptr + len ;
+ while (ptr < end)
+ {
+ uint8_t u = *ptr++ ;
+
+ /* IAC handling takes precedence over everything, except the <option>
+ * byte, which may be EXOPL, which happens to be 255 as well !
+ */
+ if ((u == tn_IAC) && (stream->in.state != kst_iac_option))
+ {
+ if (stream->iac)
+ stream->iac = 0 ; /* IAC IAC => single IAC byte value */
+ else
+ {
+ stream->iac = 1 ; /* seen an IAC */
+ continue ; /* wait for next character */
+ } ;
+ } ;
+
+ /* If stream->iac, then need to worry about IAC XX
+ *
+ * Note that IAC sequences are entirely invisible to the general
+ * stream of keystrokes. So... IAC sequences may appear in the middle
+ * of multi-byte general keystroke objects.
+ *
+ * If this is not a simple 2 byte IAC, then must put whatever was
+ * collecting to one side, and deal with the IAC.
+ *
+ * Note: not interested in stealing an IAC object.
+ */
+ if (stream->iac)
+ {
+ stream->iac = 0 ; /* assume will eat the IAC XX */
+
+ switch (stream->in.state)
+ {
+ case kst_null:
+ case kst_esc:
+ case kst_csi:
+ if (u < tn_SB)
+ keystroke_put_iac(stream, u, 1) ;
+ else
+ {
+ stream->pushed_in = stream->in ;
+
+ stream->in.len = 1 ;
+ stream->in.raw[0] = u ;
+
+ stream->in.state = kst_iac_option ;
+ }
+ break ;
+
+ case kst_iac_sub:
+ assert(stream->in.raw[0] == tn_SB) ;
+
+ if (u != tn_SE)
+ {
+ --ptr ; /* put back the XX */
+ stream->iac = 1 ; /* put back the IAC */
+ } ;
+
+ keystroke_put_iac_long(stream, (u != tn_SE)) ;
+ /* pops the stream->pushed_in */
+ break ;
+
+ case kst_char: /* TBD */
+ case kst_iac_option:
+ zabort("impossible keystroke stream state") ;
+
+ default:
+ zabort("unknown keystroke stream state") ;
+ } ;
+
+ continue ;
+ } ;
+
+ /* No IAC complications... proceed per current state */
+ switch (stream->in.state)
+ {
+ case kst_null: /* Expecting anything */
+ stream->steal_this = (steal != NULL) ;
+
+ if (u == 0x1B)
+ stream->in.state = kst_esc ;
+ else if (u == stream->CSI) /* NB: CSI == 0x1B => no CSI */
+ {
+ stream->in.len = 0 ;
+ stream->in.state = kst_csi ;
+ }
+ else
+ {
+ if (!stream->steal_this)
+ keystroke_put_char(stream, u) ;
+ else
+ {
+ keystroke_steal_char(steal, stream, u) ;
+ stream->steal_this = 0 ;
+ steal = NULL ;
+ } ;
+
+ stream->in.state = kst_null ;
+ } ;
+ break ;
+
+ case kst_char: /* TBD */
+ zabort("impossible keystroke stream state") ;
+
+ case kst_esc: /* Expecting XX after ESC */
+ if (u == '[')
+ {
+ stream->in.len = 0 ;
+ stream->in.state = kst_csi ;
+ }
+ else
+ {
+ if (!stream->steal_this)
+ keystroke_put_esc(stream, u, 1) ;
+ else
+ {
+ keystroke_steal_esc(steal, stream, u) ;
+ stream->steal_this = 0 ;
+ steal = NULL ;
+ } ;
+
+ stream->in.state = kst_null ;
+ } ;
+ break ;
+
+ case kst_csi: /* Expecting ... after ESC [ or CSI */
+ if ((u >= 0x20) && (u <= 0x3F))
+ keystroke_add_raw(stream, u) ;
+ else
+ {
+ int ok = 1 ;
+ int l ;
+
+ if ((u < 0x40) || (u > 0x7F))
+ {
+ --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 */
+ } ;
+ 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 ;
+ steal = NULL ;
+ } ;
+ stream->in.state = kst_null ;
+ } ;
+ break ;
+
+ case kst_iac_option: /* Expecting <option> after IAC XX */
+ assert(stream->in.len == 1) ;
+ keystroke_add_raw(stream, u) ;
+
+ 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 */
+ break ;
+
+ case kst_iac_sub: /* Expecting sub stuff */
+ assert(stream->in.raw[0]== tn_SB) ;
+
+ keystroke_add_raw(stream, u) ;
+ break ;
+
+ default:
+ zabort("unknown keystroke stream state") ;
+ }
+ } ;
+ assert(ptr == end) ;
+
+ /* If did not steal a keystroke, return a ks_null -- which may be
+ * a knull_eof.
+ */
+ if (steal != NULL)
+ keystroke_set_null(stream, steal) ;
+} ;
+
+/*==============================================================================
+ * Fetch next keystroke from keystroke stream
+ *
+ * Returns: 1 => have a stroke type != ks_null
+ * 0 => stroke type is ks_null (may be EOF).
+ */
+extern int
+keystroke_get(keystroke_stream stream, keystroke stroke)
+{
+ int b ;
+ uint8_t* p ;
+ uint8_t* e ;
+
+ /* Get first byte and deal with FIFO empty response */
+ b = vio_fifo_get_byte(&stream->fifo) ;
+
+ if (b < 0)
+ return keystroke_set_null(stream, stroke) ;
+
+ /* Fetch first byte and deal with the simple character case */
+ if ((b & kf_compound) == 0) /* Simple character ? */
+ {
+ stroke->type = ks_char ;
+ stroke->value = b ;
+
+ stroke->flags = 0 ;
+ stroke->len = 1 ;
+ stroke->buf[0] = b ;
+
+ return 1 ;
+ } ;
+
+ /* Sex the compound keystroke */
+
+ stroke->type = b & kf_type_mask ;
+ stroke->value = 0 ;
+ stroke->flags = b & (kf_broken | kf_truncated) ;
+ stroke->len = keystroke_get_byte(stream) ;
+
+ /* Fetch what we need to the stroke buffer */
+ p = stroke->buf ;
+ e = p + stroke->len ;
+
+ while (p < e)
+ *p++ = keystroke_get_byte(stream) ;
+
+ p = stroke->buf ;
+
+ /* Complete the process, depending on the type */
+ switch (stroke->type)
+ {
+ case ks_null:
+ zabort("ks_null found in FIFO") ;
+
+ case ks_char:
+ /* If character is well formed, set its value */
+ if (stroke->flags == 0)
+ {
+ assert((stroke->len > 0) && (stroke->len <= 4)) ;
+ while (p < e)
+ stroke->value = (stroke->value << 8) + *p++ ;
+
+ /* NB: to do UTF-8 would need to create UTF form here */
+
+ } ;
+ break ;
+
+ case ks_esc:
+ /* If have ESC X, set value = X */
+ if (stroke->len == 1)
+ stroke->value = *p ;
+ else
+ assert(stroke->len == 0) ;
+ break ;
+
+ case ks_csi:
+ /* If have the final X, set value = X */
+ /* Null terminate the parameters */
+ if (stroke->len != 0)
+ {
+ --e ;
+ stroke->value = *e ;
+ --stroke->len ;
+ } ;
+ *e = '\0' ;
+ break ;
+
+ case ks_iac:
+ /* If have the command byte after IAC, set value */
+ if (stroke->len > 0)
+ stroke->value = *p ;
+ break ;
+
+ default:
+ zabort("unknown keystroke type") ;
+ } ;
+
+ return 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set given keystroke to ks_null -- and set to knull_eof if stream->eof_met
+ *
+ * Returns: 0
+ */
+inline static int
+keystroke_set_null(keystroke_stream stream, keystroke stroke)
+{
+ stroke->type = ks_null ;
+ stroke->value = stream->eof_met ? knull_eof : knull_not_eof ;
+
+ stroke->flags = 0 ;
+ stroke->len = 0 ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Fetch 2nd or subsequent byte of keystroke.
+ *
+ * NB: it is impossible for partial keystrokes to be written, so this treats
+ * buffer empty as a FATAL error.
+ */
+inline static uint8_t
+keystroke_get_byte(keystroke_stream stream)
+{
+ int b = vio_fifo_get_byte(&stream->fifo) ;
+
+ passert(b >= 0) ;
+
+ return b ;
+} ;
+
+/*==============================================================================
+ * Functions to support keystroke_input.
+ */
+
+/*------------------------------------------------------------------------------
+ * If possible, add character to the stream->in.raw[] buffer
+ */
+static inline void
+keystroke_add_raw(keystroke_stream stream, uint8_t u)
+{
+ if (stream->in.len < keystroke_max_len)
+ stream->in.raw[stream->in.len] = u ;
+
+ ++stream->in.len ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store simple character value
+ */
+static void
+keystroke_put_char(keystroke_stream stream, uint32_t u)
+{
+ if (u < 0x80)
+ vio_fifo_put_byte(&stream->fifo, (uint8_t)u) ;
+ else
+ {
+ uint8_t buf[4] ;
+ uint8_t* p ;
+
+ p = buf + 4 ;
+
+ do
+ {
+ *(--p) = u & 0xFF ;
+ u >>= 8 ;
+ }
+ while (u != 0) ;
+
+ keystroke_put(stream, ks_char, 0, p, (buf + 4) - p) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store simple ESC. Is broken if length (after ESC) == 0 !
+ */
+inline static void
+keystroke_put_esc(keystroke_stream stream, uint8_t u, int len)
+{
+ keystroke_put(stream, ks_esc, (len == 0), &u, 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.
+ *
+ * Is broken if u == '\0'. May also be truncated !
+ */
+inline static void
+keystroke_put_csi(keystroke_stream stream, uint8_t u)
+{
+ 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
+ */
+inline static void
+keystroke_put_iac(keystroke_stream stream, uint8_t u, int len)
+{
+ keystroke_put(stream, ks_iac, (len == 0), &u, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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)
+{
+ keystroke_put(stream, ks_iac, broken, stream->in.raw, stream->in.len) ;
+
+ stream->in = stream->pushed_in ;
+ stream->pushed_in.state = kst_null ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Store <first> <len> [<bytes>]
+ */
+static void
+keystroke_put(keystroke_stream stream, enum keystroke_type type, int broken,
+ uint8_t* p, int len)
+{
+ if (len > keystroke_max_len)
+ {
+ len = keystroke_max_len ;
+ type |= kf_truncated ;
+ } ;
+
+ vio_fifo_put_byte(&stream->fifo,
+ kf_compound | (broken ? kf_broken : 0) | type) ;
+ vio_fifo_put_byte(&stream->fifo, len) ;
+
+ if (len > 0)
+ vio_fifo_put(&stream->fifo, (void*)p, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Steal character value -- cannot be broken
+ */
+static void
+keystroke_steal_char(keystroke steal, keystroke_stream stream, uint8_t u)
+{
+ steal->type = ks_char ;
+ steal->value = u ;
+ steal->flags = 0 ;
+ steal->len = 1 ;
+ steal->buf[0] = u ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Steal simple escape -- cannot be broken
+ */
+static void
+keystroke_steal_esc(keystroke steal, keystroke_stream stream, uint8_t u)
+{
+ steal->type = ks_esc ;
+ steal->value = u ;
+ steal->flags = 0 ;
+ steal->len = 1 ;
+ steal->buf[0] = u ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Steal CSI escape.
+ *
+ * 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) ;
+
+ steal->type = ks_esc ;
+ steal->value = u ;
+ steal->flags = 0 ;
+ steal->len = len - 1 ;
+
+ memcpy(steal->buf, stream->in.raw, len - 1) ;
+ steal->buf[len] = '\0' ;
+} ;
+
+/*==============================================================================
+ */
diff --git a/lib/keystroke.h b/lib/keystroke.h
new file mode 100644
index 00000000..4dc94d12
--- /dev/null
+++ b/lib/keystroke.h
@@ -0,0 +1,187 @@
+/* Keystroke Buffering -- 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_KEYSTROKE_H
+#define _ZEBRA_KEYSTROKE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <arpa/telnet.h>
+
+#include "zassert.h"
+#include "vio_fifo.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Keystroke buffering
+ */
+
+enum { keystroke_max_len = 100 } ;
+
+enum keystroke_type
+{
+ ks_null = 0, /* nothing, nada, bupkis... */
+ ks_char, /* character -- uint32_t */
+ ks_esc, /* ESC xx */
+ ks_csi, /* ESC [ ... or CSI ... */
+ ks_iac, /* Telnet command */
+
+ ks_type_count,
+
+ ks_type_reserved = 0x0F,
+} ;
+CONFIRM(ks_type_count <= ks_type_reserved) ;
+
+enum keystroke_null
+{
+ knull_not_eof,
+ knull_eof
+};
+
+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_type_mask = 0x0F, /* extraction of type */
+} ;
+
+CONFIRM(ks_type_reserved == kf_type_mask) ;
+
+typedef struct keystroke* keystroke ;
+typedef struct keystroke_stream* keystroke_stream ;
+
+struct keystroke
+{
+ enum keystroke_type type ;
+ uint8_t flags ;
+
+ uint32_t value ;
+
+ unsigned len ;
+ uint8_t buf[keystroke_max_len] ;
+} ;
+
+/* Telnet commands/options */
+enum tn_Command
+{
+ tn_IAC = IAC, /* IAC IAC interpret as command: */
+ tn_DONT = DONT, /* IAC DONT <opt> you are not to use option */
+ tn_DO = DO, /* IAC DO <opt> please, you use option */
+ tn_WONT = WONT, /* IAC WONT <opt> I won't use option */
+ tn_WILL = WILL, /* IAC WILL <opt> I will use option */
+ tn_SB = SB, /* IAC SB <opt> interpret as subnegotiation */
+ tn_GA = GA, /* IAC GA you may reverse the line */
+ tn_EL = EL, /* IAC EL erase the current line */
+ tn_EC = EC, /* IAC EC erase the current character */
+ tn_AYT = AYT, /* IAC AYT are you there */
+ tn_AO = AO, /* IAC AO abort output--but let prog finish */
+ tn_IP = IP, /* IAC IP interrupt process--permanently */
+ tn_BREAK = BREAK, /* IAC BREAK break */
+ tn_DM = DM, /* IAC DM data mark--for connect. cleaning */
+ tn_NOP = NOP, /* IAC NOP nop */
+ tn_SE = SE, /* IAC SE end sub negotiation */
+ tn_EOR = EOR, /* IAC EOR end of record (transparent mode) */
+ tn_ABORT = ABORT, /* IAC ABORT Abort process */
+ tn_SUSP = SUSP, /* IAC SUSP Suspend process */
+ tn_EOF = xEOF, /* IAC xEOF End of file: EOF is already used... */
+
+ tn_SYNCH = SYNCH, /* IAC SYNCH for telfunc calls */
+} ;
+
+enum tn_Option
+{
+ to_BINARY = TELOPT_BINARY, /* 8-bit data path */
+ to_ECHO = TELOPT_ECHO, /* echo */
+ to_RCP = TELOPT_RCP, /* prepare to reconnect */
+ to_SGA = TELOPT_SGA, /* suppress go ahead */
+ to_NAMS = TELOPT_NAMS, /* approximate message size */
+ to_STATUS = TELOPT_STATUS, /* give status */
+ to_TM = TELOPT_TM, /* timing mark */
+ to_RCTE = TELOPT_RCTE, /* remote controlled tx and echo */
+ to_NAOL = TELOPT_NAOL, /* neg. about output line width */
+ to_NAOP = TELOPT_NAOP, /* neg. about output page size */
+ to_NAOCRD = TELOPT_NAOCRD, /* neg. about CR disposition */
+ to_NAOHTS = TELOPT_NAOHTS, /* neg. about horizontal tabstops */
+ to_NAOHTD = TELOPT_NAOHTD, /* neg. about horizontal tab disp. */
+ to_NAOFFD = TELOPT_NAOFFD, /* neg. about formfeed disposition */
+ to_NAOVTS = TELOPT_NAOVTS, /* neg. about vertical tab stops */
+ to_NAOVTD = TELOPT_NAOVTD, /* neg. about vertical tab disp. */
+ to_NAOLFD = TELOPT_NAOLFD, /* neg. about output LF disposition */
+ to_XASCII = TELOPT_XASCII, /* extended ascii character set */
+ to_LOGOUT = TELOPT_LOGOUT, /* force logout */
+ to_BM = TELOPT_BM, /* byte macro */
+ to_DET = TELOPT_DET, /* data entry terminal */
+ to_SUPDUP = TELOPT_SUPDUP, /* supdup protocol */
+ to_SUPDUPOUTPUT = TELOPT_SUPDUPOUTPUT,/* supdup output */
+ to_SNDLOC = TELOPT_SNDLOC, /* send location */
+ to_TTYPE = TELOPT_TTYPE, /* terminal type */
+ to_EOR = TELOPT_EOR, /* end or record */
+ to_TUID = TELOPT_TUID, /* TACACS user identification */
+ to_OUTMRK = TELOPT_OUTMRK, /* output marking */
+ to_TTYLOC = TELOPT_TTYLOC, /* terminal location number */
+ to_3270REGIME = TELOPT_3270REGIME, /* 3270 regime */
+ to_X3PAD = TELOPT_X3PAD, /* X.3 PAD */
+ to_NAWS = TELOPT_NAWS, /* window size */
+ to_TSPEED = TELOPT_TSPEED, /* terminal speed */
+ to_LFLOW = TELOPT_LFLOW, /* remote flow control */
+ to_LINEMODE = TELOPT_LINEMODE, /* Linemode option */
+ to_XDISPLOC = TELOPT_XDISPLOC, /* X Display Location */
+ to_OLD_ENVIRON = TELOPT_OLD_ENVIRON, /* Old - Environment variables */
+ to_AUTHENTICATION = TELOPT_AUTHENTICATION, /* Authenticate */
+ to_ENCRYPT = TELOPT_ENCRYPT, /* Encryption option */
+ to_NEW_ENVIRON = TELOPT_NEW_ENVIRON, /* New - Environment variables */
+ to_EXOPL = TELOPT_EXOPL, /* extended-options-list */
+} ;
+
+
+/*==============================================================================
+ * Functions
+ */
+extern keystroke_stream
+keystroke_stream_new(uint8_t csi_char) ;
+
+extern void
+keystroke_stream_set_eof(keystroke_stream stream) ;
+
+extern void
+keystroke_stream_free(keystroke_stream stream) ;
+
+extern bool
+keystroke_stream_empty(keystroke_stream stream) ;
+extern bool
+keystroke_stream_eof(keystroke_stream stream) ;
+
+extern void
+keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
+ keystroke steal) ;
+extern int
+keystroke_get(keystroke_stream stream, keystroke stroke) ;
+
+#endif /* _ZEBRA_KEYSTROKE_H */
diff --git a/lib/list_util.c b/lib/list_util.c
new file mode 100644
index 00000000..0d61a67a
--- /dev/null
+++ b/lib/list_util.c
@@ -0,0 +1,59 @@
+/* List Utilities
+ * 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.
+ */
+
+#include <list_util.h>
+
+
+/*==============================================================================
+ * Single Base, Single Link
+ */
+
+/*------------------------------------------------------------------------------
+ * Deleting item
+ *
+ * Have to chase down list to find item.
+ *
+ * Returns: 0 => OK -- removed item from list (OR item == NULL)
+ * -1 => item not found on list
+ */
+extern int
+ssl_del_func(void** p_this, void* item, size_t link_offset)
+{
+ if (item == NULL)
+ return 0 ;
+
+ while (*p_this != item)
+ {
+ if (*p_this == NULL)
+ return -1 ;
+
+ p_this = _sl_p_next(*p_this, link_offset) ;
+ } ;
+
+ *p_this = _sl_next(item, link_offset) ;
+
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Single Base, Double Link
+ */
+
diff --git a/lib/list_util.h b/lib/list_util.h
new file mode 100644
index 00000000..b658c7ce
--- /dev/null
+++ b/lib/list_util.h
@@ -0,0 +1,729 @@
+/* List Utilities -- 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_LIST_UTIL_H
+#define _ZEBRA_LIST_UTIL_H
+
+#include <stddef.h>
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/*------------------------------------------------------------------------------
+ * Note that the following fell foul of "strict-aliasing":
+ *
+ * #define ssl_del_head(base, next) \
+ * ssl_del_head_func_i((void**)&(base), _lu_off(base, next))
+ *
+ * Inline void*
+ * ssl_del_head_func_i(void** p_base, size_t link_offset)
+ * {
+ * void* item = *p_base ;
+ *
+ * if (item != NULL)
+ * *p_base = _sl_next(item, link_offset) ;
+ *
+ * return item ;
+ * } ;
+ *
+ * the assignment to *p_base is, apparently, unacceptable. This works
+ * perfectly well as am ordinary function. Using a GNUC extension it is
+ * possible to avoid the function call... hence the ugly skips.
+ */
+#ifdef __GNUC__
+#define __GNUC__LIST_UTIL
+#endif
+
+/*==============================================================================
+ * These utilities provide for linked lists of items, where the list pointers
+ * are fields in the items.
+ *
+ * This is a little less general that the linklist stuff, but carries less
+ * overhead.
+ *
+ * The items will be structures of some sort, and are described here as being
+ * of type "struct item". Pointers to those items will be of type
+ * "struct item*".
+ *
+ * Most of these utilities are implemented as macros.
+ *
+ *------------------------------------------------------------------------------
+ * Links and Bases.
+ *
+ * For a singly linked list, the item declaration is straightforward:
+ *
+ * struct item
+ * {
+ * ....
+ * struct item* foo_next ;
+ * ....
+ * }
+ *
+ * The item can live on more than one list, all that is required is that each
+ * list has its next pointer.
+ *
+ * For double linked lists, the item may be declared:
+ *
+ * struct item
+ * {
+ * ....
+ * struct list_pair(struct item*) foo_list ;
+ * ....
+ * } ;
+ *
+ * A single base is straighforward:
+ *
+ * struct item* foo_base ;
+ *
+ * and that may be a variable or a structure field.
+ *
+ * A double base may be declared:
+ *
+ * struct base_pair(struct item*) foo_base ;
+ *
+ * Various ways to construct structures or structure types:
+ *
+ * typedef struct list_pair(struct foo*) foo_list ;
+ *
+ * struct foo_list list_pair(struct foo*) ;
+ *
+ * struct foo_base base_pair(struct foo*) ;
+ */
+
+#define dl_list_pair(ptr_t) { ptr_t next ; ptr_t prev ; }
+
+#define dl_base_pair(ptr_t) { ptr_t head ; ptr_t tail ; }
+
+struct dl_void_list_pair list_pair(void*) ;
+struct dl_void_base_pair base_pair(void*) ;
+
+#define _lu_off(obj, field) ((char*)&((obj)->field) - (char*)(obj))
+
+/*==============================================================================
+ * Single Base, Single Link
+ *
+ * To delete entry must chase down list to find it.
+ *
+ * Supports:
+ *
+ * ssl_init(base) -- initialise base
+ *
+ * An empty list has a NULL base.
+ *
+ * ssl_push(base, item, next) -- add at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * ssl_del(base, item, next) -- delete from list
+ *
+ * Treat as function returning int. Does nothing if the item is NULL.
+ *
+ * Returns: 0 => OK -- removed item from list (OR item == NULL)
+ * -1 => item not found on list
+ *
+ * ssl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * ssl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * ssl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * ssl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if item is NULL.
+ *
+ * Note that ssl_del() and ssl_pop() do NOT affect the item->next pointer.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type struct item*
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "next" to be the name of a field in struct item, with type struct item*
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct item* bar_next ;
+ * ...
+ * } ;
+ *
+ * static struct item* bar_base ; // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * ssl_push(bar_base, q, bar_next) ;
+ *
+ * // remove item from list
+ * ssl_del(bar_base, q, bar_next) ;
+ *
+ * // walk a list
+ * struct item* t = ssl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = ssl_next(t, bar_next) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (ssl_pop(&t, bar_base, bar_next) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = ssl_head(bar_base) != NULL)
+ * {
+ * ....
+ * ssl_del_head(bar_base, bar_next) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct item* bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * ssl_push(parent->bar_base, item, bar_next) ;
+ * ....
+ * }
+ */
+
+#define ssl_init(base) \
+ ((base) = NULL)
+
+#define ssl_push(base, item, next) \
+ do { confirm(_lu_off(base, next) == _lu_off(item, next)) ; \
+ (item)->next = (base) ; \
+ (base) = item ; \
+ } while (0)
+
+extern int ssl_del_func(void** p_this, void* obj, size_t link_offset) ;
+
+#define ssl_del(base, item, next) \
+ ssl_del_func((void**)&(base), item, _lu_off(base, next))
+
+#define ssl_del_head(base, next) \
+ do { if ((base) != NULL) \
+ (base) = (base)->next ; \
+ } while (0)
+
+#define ssl_pop(dst, base, next) \
+ ((*(dst) = (base)) != NULL ? ((base) = (base)->next, *(dst)) : NULL)
+
+#define ssl_head(base) (base)
+
+#define ssl_next(item, next) \
+ ((item) != NULL ? (item)->next : NULL)
+
+/* _sl_p_next(item, off) -- pointer to next pointer at given offset
+ * _sl_next(item, off) -- contents of next pointer at given offset
+ */
+
+#define _sl_p_next(item, off) \
+ ( (void**)( (char*)(item) + (off) ) )
+
+#define _sl_next(item, off) \
+ *_sl_p_next(item, off)
+
+/*==============================================================================
+ * Single Base, Double Link
+ *
+ * Can delete entry directly.
+ *
+ * Supports:
+ *
+ * sdl_init(base) -- initialise base
+ *
+ * An empty list has a NULL base.
+ *
+ * sdl_push(base, item, list) -- add at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * sdl_del(base, item, list) -- delete from list
+ *
+ * Treat as void function. Does nothing if the item is NULL.
+ *
+ * Undefined if item is not on the list.
+ *
+ * sdl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * sdl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * sdl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * sdl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * sdl_prev(item, next) -- step to prev item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * Note that sdl_del() and sdl_pop() do NOT affect the item->list.next
+ * or item->list.prev pointers.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type: struct base_pair(struct item*)*
+ *
+ * That is... a variable or field which is a pointer to
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "list" to be the name of a field in struct item
+ * of type: struct list_pair(struct item*)
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct list_pair(struct item*) bar_list ;
+ * ...
+ * } ;
+ *
+ * static struct base_pair(struct item*) bar_base ;
+ * // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * sdl_push(bar_base, q, bar_list) ;
+ *
+ * // remove item from list
+ * sdl_del(bar_base, q, bar_list) ;
+ *
+ * // walk a list
+ * struct item* t = sdl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = sdl_next(t, bar_list) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (sdl_pop(&t, bar_base, bar_list) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = sdl_head(bar_base) != NULL)
+ * {
+ * ....
+ * sdl_del_head(bar_base, bar_list) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct base_pair(struct item*) bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * sdl_push(parent->bar_base, item, bar_list) ;
+ * ....
+ * }
+ */
+
+#define sdl_init(base) \
+ ((base) = NULL)
+
+#define sdl_push(base, item, list) \
+ do { confirm(_lu_off(base, list.next) == _lu_off(item, list.next)) ; \
+ confirm(_lu_off(base, list.prev) == _lu_off(item, list.prev)) ; \
+ (item)->list.next = (base) ; \
+ (item)->list.prev = NULL ; \
+ if ((base) != NULL) \
+ (base)->list.prev = (item) ; \
+ (base) = (item) ; \
+ } while (0)
+
+#define sdl_del(base, item, list) \
+ do { confirm(_lu_off(base, list.next) == _lu_off(item, list.next)) ; \
+ confirm(_lu_off(base, list.prev) == _lu_off(item, list.prev)) ; \
+ if ((item) != NULL) \
+ { \
+ if ((item)->list.next != NULL) \
+ (item)->list.next->list.prev = (item)->list.prev ; \
+ if ((item)->list.prev != NULL) \
+ (item)->list.prev->list.next = (item)->list.next ; \
+ else \
+ (base) = (item)->list.next ; \
+ } ; \
+ } while (0)
+
+#define sdl_del_head(base, list) \
+ do { if ((base) != NULL) \
+ { \
+ (base) = (base)->list.next ; \
+ if ((base) != NULL) \
+ (base)->list.prev = NULL ; \
+ } \
+ } while (0)
+
+#define sdl_pop(dst, base, list) \
+ ((*(dst) = (base)) != NULL \
+ ? ( ((base) = (base)->list.next) != NULL \
+ ? ( (base)->list.prev = NULL, *(dst) ) : *(dst) ) : NULL)
+
+#define sdl_head(base) (base)
+
+#define sdl_next(item, list) \
+ ((item) != NULL ? (item)->list.next : NULL)
+
+#define sdl_prev(item, list) \
+ ((item) != NULL ? (item)->list.prev : NULL)
+
+/* _dl_p_next(obj, off) -- pointer to next pointer at given offset
+ * _dl_next(obj, off) -- contents of next pointer at given offset
+ * _dl_p_prev(obj, off) -- pointer to prev pointer at given offset
+ * _dl_prev(obj, off) -- contents of prev pointer at given offset
+ */
+#define _dl_p_next(obj, off) \
+ ( (void**)( (char*)(obj) + (off) + 0 ) )
+
+#define _dl_next(obj, off) \
+ *_dl_p_next(obj, off)
+
+#define _dl_p_prev(obj, off) \
+ ( (void**)( (char*)(obj) + (off) _ sizeof(void*) ) )
+
+#define _dl_prev(obj, off) \
+ *_dl_p_next(obj, off)
+
+/*==============================================================================
+ * Double Base, Double Link
+ *
+ * Can delete entry directly. Can insert and remove at tail.
+ *
+ * Supports:
+ *
+ * ddl_init(base) -- initialise base
+ *
+ * An empty list has *both* head and tail pointers NULL.
+ *
+ * NB: confusion will arise if only one of these pointers is NULL.
+ *
+ * ddl_push(base, item, list) -- insert at head of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * ddl_append(base, item, list) -- insert at tail of list
+ *
+ * Treat as void function. The item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one).
+ *
+ * ddl_in_after(after, base, item, list) -- insert after
+ *
+ * Treat as void function. The after & item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one), or if
+ * after is not on the list.
+ *
+ * ddl_in_before(before, base, item, list) -- insert before
+ *
+ * Treat as void function. The before & item may *not* be NULL.
+ *
+ * Undefined if item is already on any list (including this one), or if
+ * before is not on the list.
+ *
+ * ddl_pop(&dst, base, next) -- pop head of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * ddl_crop(&dst, base, next) -- crop tail of list, if any
+ *
+ * Treat as function returning void*.
+ *
+ * Returns old head in dst and as return from "function".
+ *
+ * Returns NULL and sets dst == NULL if list is empty.
+ *
+ * ddl_del(base, item, list) -- delete from list
+ *
+ * Treat as void function. Does nothing if the item is NULL.
+ *
+ * Undefined if item is not on the list.
+ *
+ * ddl_del_head(base, next) -- delete head of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * ddl_del_tail(base, next) -- delete tail of list
+ *
+ * Treat as void function. Does nothing if the list is empty.
+ *
+ * ddl_head(base) -- return head of list
+ *
+ * Treat as function returning void*.
+ *
+ * ddl_tail(base) -- return tail of list
+ *
+ * Treat as function returning void*.
+ *
+ * ddl_next(item, next) -- step to next item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * ddl_prev(item, next) -- step to prev item, if any
+ *
+ * Treat as function returning void*. Returns NULL if the item is NULL.
+ *
+ * Note that ddl_del() and ddl_pop() do NOT affect the item->list.next
+ * or item->list.prev pointers.
+ *
+ * Where:
+ *
+ * "base" to be an r-value of type: struct base_pair(struct item*)*
+ *
+ * That is... a variable or field which is a pointer to
+ *
+ * "item" to be an l-value of type struct item*
+ *
+ * "dst" to be an r-value of type struct item*
+ *
+ * "list" to be the name of a field in struct item
+ * of type: struct list_pair(struct item*)
+ *
+ *
+ *
+ *------------------------------------------------------------------------------
+ * For example:
+ *
+ * struct item // definition for list items
+ * {
+ * ...
+ * struct list_pair(struct item*) bar_list ;
+ * ...
+ * } ;
+ *
+ * static struct base_pair(struct item*) bar_base ;
+ * // declaration of the list base
+ *
+ * // create item and add to list (adds at front)
+ * struct item* q = calloc(1, sizeof(struct item)) ;
+ * ddl_push(bar_base, q, bar_list) ;
+ *
+ * // remove item from list
+ * ddl_del(bar_base, q, bar_list) ;
+ *
+ * // walk a list
+ * struct item* t = ddl_head(bar_base) ;
+ * while (t != NULL)
+ * {
+ * ....
+ * t = ddl_next(t, bar_list) ;
+ * }
+ *
+ * // walk and empty out a list -- removing item before processing
+ * struct item* t ;
+ * while (ddl_pop(&t, bar_base, bar_list) != NULL)
+ * {
+ * .... // t points to old head of list
+ * }
+ *
+ * // walk and empty out a list -- removing after processing
+ * struct item* t ;
+ * while ((t = ddl_head(bar_base) != NULL)
+ * {
+ * ....
+ * ddl_del_head(bar_base, bar_list) ;
+ * }
+ *
+ * And for example:
+ *
+ * struct parent_item // parent structure containing list
+ * {
+ * ....
+ * struct base_pair(struct item*) bar_base ;
+ * ....
+ * }
+ *
+ * void footle(struct parent_item* parent, struct item* item)
+ * {
+ * ....
+ * ddl_push(parent->bar_base, item, bar_list) ;
+ * ....
+ * }
+ */
+
+#define ddl_init(base) \
+ ((base).head = (base).tail = NULL)
+
+#define ddl_push(base, item, list) \
+ do { (item)->list.next = (base).head ; \
+ (item)->list.prev = NULL ; \
+ if ((base).head != NULL) \
+ (base).head->list.prev = (item) ; \
+ else \
+ (base).tail = (item) ; \
+ (base).head = (item) ; \
+ } while (0)
+
+#define ddl_append(base, item, list) \
+ do { (item)->list.next = NULL ; \
+ (item)->list.prev = (base).tail ; \
+ if ((base).tail != NULL) \
+ (base).tail->list.next = (item) ; \
+ else \
+ (base).head = (item) ; \
+ (base).tail = (item) ; \
+ } while (0)
+
+#define ddl_in_after(after, base, item, list) \
+ do { (item)->list.next = (after)->list.next ; \
+ (item)->list.prev = (after) ; \
+ if ((after)->list.next != NULL) \
+ (after)->list.next->list.prev = (item) ; \
+ else \
+ (base).tail = (item) ; \
+ (after)->list.next = (item) ; \
+ } while (0)
+
+#define ddl_in_before(before, base, item, list) \
+ do { (item)->list.next = (before) ; \
+ (item)->list.prev = (before)->list.prev ; \
+ if ((before)->list.prev != NULL) \
+ (before)->list.prev->list.next = (item) ; \
+ else \
+ (base).head = (item) ; \
+ (before)->list.prev = (item) ; \
+ } while (0)
+
+#define ddl_del(base, item, list) \
+ do { if ((item) != NULL) \
+ { \
+ if ((item)->list.next != NULL) \
+ (item)->list.next->list.prev = (item)->list.prev ; \
+ else \
+ (base).tail = (item)->list.prev ; \
+ if ((item)->list.prev != NULL) \
+ (item)->list.prev->list.next = (item)->list.next ; \
+ else \
+ (base).head = (item)->list.next ; \
+ } ; \
+ } while (0)
+
+#define ddl_del_head(base, list) \
+ do { if ((base).head != NULL) \
+ { \
+ (base).head = (base).head->list.next ; \
+ if ((base).head != NULL) \
+ (base).head->list.prev = NULL ; \
+ else \
+ (base).tail = NULL ; \
+ } \
+ } while (0)
+
+#define ddl_del_tail(base, list) \
+ do { if ((base).tail != NULL) \
+ { \
+ (base).tail = (base).tail->list.prev ; \
+ if ((base).tail != NULL) \
+ (base).tail->list.next = NULL ; \
+ else \
+ (base).head = NULL ; \
+ } \
+ } while (0)
+
+#define ddl_pop(dst, base, list) \
+ ((*(dst) = (base).head) != NULL \
+ ? ( ((base).head = (base).head->list.next) != NULL \
+ ? ( (base).head->list.prev = NULL, *(dst) ) \
+ : ( (base).tail = NULL, *(dst) ) ) \
+ : NULL)
+
+#define ddl_crop(dst, base, list) \
+ ((*(dst) = (base).tail) != NULL \
+ ? ( ((base).tail = (base).tail->list.prev) != NULL \
+ ? ( (base).tail->list.next = NULL, *(dst) ) \
+ : ( (base).head = NULL, *(dst) ) ) \
+ : NULL)
+
+#define ddl_head(base) ((base).head)
+
+#define ddl_tail(base) ((base).tail)
+
+#define ddl_next(item, list) \
+ ((item) != NULL ? (item)->list.next : NULL)
+
+#define ddl_prev(item, list) \
+ ((item) != NULL ? (item)->list.prev : NULL)
+
+#endif /* _ZEBRA_LIST_UTIL_H */
diff --git a/lib/log.c b/lib/log.c
index 2d636599..3db6fa81 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -25,6 +25,8 @@
#include <zebra.h>
#include "log.h"
+#include "vty.h"
+#include "uty.h"
#include "memory.h"
#include "command.h"
#ifndef SUNOS_5
@@ -36,15 +38,6 @@
#endif
#include "qpthreads.h"
-#ifdef NDEBUG
-#define LOCK qpt_mutex_lock(&vty_mutex);
-#define UNLOCK qpt_mutex_unlock(&vty_mutex);
-#else
-#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
-
/* log is protected by the same mutext as vty, see comments in vty.c */
/* prototypes */
@@ -86,30 +79,55 @@ const char *zlog_priority[] =
NULL,
};
-/* For time string format. */
+/*==============================================================================
+ * Time stamp handling -- gettimeofday(), localtime() and strftime().
+ *
+ * Maintains a cached form of the current time (under the vty/log mutex), so
+ * that can avoid multiple calls of localtime() and strftime() per second.
+ *
+ * The value from gettimeofday() is in micro-seconds. Can provide timestamp
+ * with any number of decimal digits, but at most 6 will be significant.
+ */
+/*------------------------------------------------------------------------------
+ * Fill buffer with current time, to given number of decimal digits.
+ *
+ * If given buffer is too small, provides as many characters as possible.
+ *
+ * Returns: number of characters in buffer, not including trailing '\0'.
+ *
+ * NB: does no rounding.
+ *
+ * NB: buflen MUST be > 1 and buf MUST NOT be NULL.
+ */
size_t
quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
{
size_t result;
- LOCK
+ VTY_LOCK() ;
result = uquagga_timestamp(timestamp_precision, buf, buflen);
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
-/* unprotected version for when mutex already held */
+/*------------------------------------------------------------------------------
+ * unprotected version for when mutex already held
+ */
size_t
uquagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
{
static struct {
time_t last;
size_t len;
- char buf[28];
+ char buf[timestamp_buffer_len];
} cache;
+
struct timeval clock;
- size_t result = 0;
+ 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);
@@ -121,149 +139,233 @@ uquagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
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);
+ "%Y/%m/%d %H:%M:%S", &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 */
- if (buflen > cache.len)
+ 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 */
+
+ memcpy(buf, cache.buf, len) ;
+
+ /* Can do decimal part if there is room for the '.' character */
+ if ((timestamp_precision > 0) && (left > 0))
{
- memcpy(buf, cache.buf, cache.len);
- if ((timestamp_precision > 0) &&
- (buflen > cache.len+1+timestamp_precision))
- {
- /* should we worry about locale issues? */
- static const int divisor[] = {0, 100000, 10000, 1000, 100, 10, 1};
- int prec;
- char *p = buf+cache.len+1+(prec = timestamp_precision);
- *p-- = '\0';
- while (prec > 6)
- /* this is unlikely to happen, but protect anyway */
- {
- *p-- = '0';
- prec--;
- }
- clock.tv_usec /= divisor[prec];
- do
- {
- *p-- = '0'+(clock.tv_usec % 10);
- clock.tv_usec /= 10;
- }
- while (--prec > 0);
- *p = '.';
- result = cache.len+1+timestamp_precision;
- }
- else
+ /* 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 */
{
- buf[cache.len] = '\0';
- result = cache.len;
- }
- } else {
- if (buflen > 0)
- buf[0] = '\0';
- }
+ *p-- = '0';
+ --prec ;
+ } ;
- return result;
-}
+ clock.tv_usec /= divisor[prec];
-/* Utility routine for current time printing. */
-static void
-time_print(FILE *fp, struct timestamp_control *ctl)
-{
- if (!ctl->already_rendered)
- {
- ctl->len = uquagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
- ctl->already_rendered = 1;
- }
- fprintf(fp, "%s ", ctl->buf);
-}
+ while (prec > 0) /* could have been reduced to 0 */
+ {
+ *p-- = '0'+(clock.tv_usec % 10);
+ clock.tv_usec /= 10;
+ --prec ;
+ } ;
+
+ *p = '.';
+ } ;
-/* va_list version of zlog. */
+ buf[len] = '\0';
+
+ return len ;
+} ;
+
+/*==============================================================================
+ * va_list version of zlog
+ */
static void
vzlog (struct zlog *zl, int priority, const char *format, va_list args)
{
- LOCK
+ VTY_LOCK() ;
uvzlog(zl, priority, format, args);
- UNLOCK
+ VTY_UNLOCK() ;
}
/* va_list version of zlog. Unprotected assumes mutex already held*/
static void
-uvzlog (struct zlog *zl, int priority, const char *format, va_list args)
+uvzlog (struct zlog *zl, int priority, const char *format, va_list va)
{
- struct timestamp_control tsctl;
- tsctl.already_rendered = 0;
+ struct logline ll ; /* prepares line for output, here */
- ASSERTLOCKED
+ VTY_ASSERT_LOCKED() ;
- /* If zlog is not specified, use default one. */
+ ll.p_nl = NULL ; /* Nothing generated, yet */
+
+ /* If zlog is not specified, use default one. */
if (zl == NULL)
- zl = zlog_default;
+ zl = zlog_default ;
- /* When zlog_default is also NULL, use stderr for logging. */
+ /* When zlog_default is also NULL, use stderr for logging. */
if (zl == NULL)
{
- tsctl.precision = 0;
- time_print(stderr, &tsctl);
- fprintf (stderr, "%s: ", "unknown");
- vfprintf (stderr, format, args);
- fprintf (stderr, "\n");
- fflush (stderr);
+ uvzlog_line(&ll, zl, priority, format, va, 0) ;
+ write(fileno(stderr), ll.line, ll.len) ;
}
else
{
- tsctl.precision = zl->timestamp_precision;
-
- /* Syslog output */
+ /* Syslog output */
if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
{
va_list ac;
- va_copy(ac, args);
+ va_copy(ac, va);
vsyslog (priority|zlog_default->facility, format, ac);
va_end(ac);
}
- /* File output. */
+ /* File output. */
if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp)
{
- va_list ac;
- time_print (zl->fp, &tsctl);
- if (zl->record_priority)
- fprintf (zl->fp, "%s: ", zlog_priority[priority]);
- fprintf (zl->fp, "%s: ", zlog_proto_names[zl->protocol]);
- va_copy(ac, args);
- vfprintf (zl->fp, format, ac);
- va_end(ac);
- fprintf (zl->fp, "\n");
- fflush (zl->fp);
+ uvzlog_line(&ll, zl, priority, format, va, 0) ;
+ write(fileno(zl->fp), ll.line, ll.len) ;
}
/* stdout output. */
if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT])
{
- va_list ac;
- time_print (stdout, &tsctl);
- if (zl->record_priority)
- fprintf (stdout, "%s: ", zlog_priority[priority]);
- fprintf (stdout, "%s: ", zlog_proto_names[zl->protocol]);
- va_copy(ac, args);
- vfprintf (stdout, format, ac);
- va_end(ac);
- fprintf (stdout, "\n");
- fflush (stdout);
+ uvzlog_line(&ll, zl, priority, format, va, 0) ;
+ write(fileno(zl->fp), ll.line, ll.len) ;
}
/* Terminal monitor. */
if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
- {
- const char *priority_name = (zl->record_priority ? zlog_priority[priority] : NULL);
- const char *proto_name = zlog_proto_names[zl->protocol];
- vty_log (priority_name, proto_name, format, &tsctl, args);
- }
+ uty_log(&ll, zl, priority, format, va) ;
}
}
+/*------------------------------------------------------------------------------
+ * Preparation of line to send to logging: file, stdout or "monitor" terminals.
+ *
+ * Takes copy of va_list before using it, so the va_list is unchanged.
+ */
+extern void
+uvzlog_line(struct logline* ll, struct zlog *zl, int priority,
+ const char *format, va_list va, int crlf)
+{
+ char* p ;
+
+ 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))
+ return ; /* exit here if all set */
+ }
+ else
+ {
+ /* must construct the line */
+ const char* q ;
+ char* e ;
+ size_t len ;
+ 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 */
+
+ if (p < e)
+ *p++ = ' ' ;
+
+ /* "<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++ = ' ' ;
+ } ;
+
+ /* "<protocol>: " or "unknown: " */
+ if (zl != NULL)
+ q = zlog_proto_names[zl->protocol] ;
+ 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++ = ' ' ;
+
+ /* 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 */
+
+ if (p > e)
+ p = e ; /* actual end */
+ } ;
+
+ ll->p_nl = p ; /* set end pointer */
+
+ assert(p <= e) ;
+ } ;
+
+ /* finish off with '\r''\n''\0' or '\n''\0' as required */
+ if (crlf)
+ *p++ = '\r' ;
+
+ *p++ = '\n' ;
+ *p = '\0' ;
+
+ ll->len = p - ll->line ;
+ ll->crlf = crlf ;
+} ;
+
+/*============================================================================*/
+
static char *
str_append(char *dst, int len, const char *src)
{
@@ -566,9 +668,9 @@ zlog_backtrace_sigsafe(int priority, void *program_counter)
void
zlog_backtrace(int priority)
{
- LOCK
+ VTY_LOCK() ;
uzlog_backtrace(priority);
- UNLOCK
+ VTY_UNLOCK() ;
}
static void
@@ -720,9 +822,9 @@ _zlog_abort_err (const char *mess, int err, const char *file,
static void
zlog_abort (const char *mess)
{
-#ifndef NDEBUG
- /* don't work about being unlocked */
- vty_lock_asserted = 1;
+#if VTY_DEBUG
+ /* May not be locked -- but that doesn't matter any more */
+ ++vty_lock_count ;
#endif
/* Force fallback file logging? */
@@ -778,7 +880,7 @@ closezlog (struct zlog *zl)
void
zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level)
{
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -788,7 +890,7 @@ zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level)
zl->maxlvl[dest] = log_level;
}
- UNLOCK
+ VTY_UNLOCK() ;
}
int
@@ -798,7 +900,7 @@ zlog_set_file (struct zlog *zl, const char *filename, int log_level)
mode_t oldumask;
int result = 1;
- LOCK
+ VTY_LOCK() ;
/* There is opend file. */
uzlog_reset_file (zl);
@@ -825,7 +927,7 @@ zlog_set_file (struct zlog *zl, const char *filename, int log_level)
}
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
@@ -834,9 +936,9 @@ int
zlog_reset_file (struct zlog *zl)
{
int result;
- LOCK
+ VTY_LOCK() ;
result = uzlog_reset_file(zl);
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
@@ -869,7 +971,7 @@ zlog_rotate (struct zlog *zl)
int level;
int result = 1;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -908,7 +1010,7 @@ zlog_rotate (struct zlog *zl)
}
}
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
@@ -917,7 +1019,7 @@ zlog_get_default_lvl (struct zlog *zl)
{
int result = LOG_DEBUG;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -927,14 +1029,14 @@ zlog_get_default_lvl (struct zlog *zl)
result = zl->default_lvl;
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
void
zlog_set_default_lvl (struct zlog *zl, int level)
{
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -944,7 +1046,7 @@ zlog_set_default_lvl (struct zlog *zl, int level)
zl->default_lvl = level;
}
- UNLOCK
+ VTY_UNLOCK() ;
}
/* Set logging level and default for all destinations */
@@ -953,7 +1055,7 @@ zlog_set_default_lvl_dest (struct zlog *zl, int level)
{
int i;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -967,7 +1069,7 @@ zlog_set_default_lvl_dest (struct zlog *zl, int level)
zl->maxlvl[i] = level;
}
- UNLOCK
+ VTY_UNLOCK() ;
}
int
@@ -975,7 +1077,7 @@ zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest)
{
int result = ZLOG_DISABLED;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -985,7 +1087,7 @@ zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest)
result = zl->maxlvl[dest];
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
@@ -994,7 +1096,7 @@ zlog_get_facility (struct zlog *zl)
{
int result = LOG_DAEMON;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -1004,14 +1106,14 @@ zlog_get_facility (struct zlog *zl)
result = zl->facility;
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
void
zlog_set_facility (struct zlog *zl, int facility)
{
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -1021,7 +1123,7 @@ zlog_set_facility (struct zlog *zl, int facility)
zl->facility = facility;
}
- UNLOCK
+ VTY_UNLOCK() ;
}
int
@@ -1029,7 +1131,7 @@ zlog_get_record_priority (struct zlog *zl)
{
int result = 0;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -1039,14 +1141,14 @@ zlog_get_record_priority (struct zlog *zl)
result = zl->record_priority;
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
void
zlog_set_record_priority (struct zlog *zl, int record_priority)
{
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -1055,7 +1157,7 @@ zlog_set_record_priority (struct zlog *zl, int record_priority)
{
zl->record_priority = record_priority;
}
- UNLOCK
+ VTY_UNLOCK() ;
}
int
@@ -1063,7 +1165,7 @@ zlog_get_timestamp_precision (struct zlog *zl)
{
int result = 0;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -1072,14 +1174,14 @@ zlog_get_timestamp_precision (struct zlog *zl)
{
result = zl->timestamp_precision;
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
void
zlog_set_timestamp_precision (struct zlog *zl, int timestamp_precision)
{
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -1089,7 +1191,7 @@ zlog_set_timestamp_precision (struct zlog *zl, int timestamp_precision)
zl->timestamp_precision = timestamp_precision;
}
- UNLOCK
+ VTY_UNLOCK() ;
}
/* returns name of ZLOG_NONE if no zlog given and no default set */
@@ -1097,9 +1199,9 @@ const char *
zlog_get_proto_name (struct zlog *zl)
{
const char * result;
- LOCK
+ VTY_LOCK() ;
result = uzlog_get_proto_name(zl);
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
@@ -1126,7 +1228,7 @@ zlog_get_filename (struct zlog *zl)
{
char * result = NULL;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -1136,7 +1238,7 @@ zlog_get_filename (struct zlog *zl)
result = strdup(zl->filename);
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
@@ -1145,7 +1247,7 @@ zlog_get_ident (struct zlog *zl)
{
const char * result = NULL;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -1155,7 +1257,7 @@ zlog_get_ident (struct zlog *zl)
result = zl->ident;
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
@@ -1165,7 +1267,7 @@ zlog_is_file (struct zlog *zl)
{
int result = 0;
- LOCK
+ VTY_LOCK() ;
if (zl == NULL)
zl = zlog_default;
@@ -1175,7 +1277,7 @@ zlog_is_file (struct zlog *zl)
result = (zl->fp != NULL);
}
- UNLOCK;
+ VTY_UNLOCK() ;;
return result;
}
@@ -1346,7 +1448,3 @@ proto_name2num(const char *s)
return -1;
}
#undef RTSIZE
-
-#undef LOCK
-#undef UNLOCK
-#undef ASSERTLOCKED
diff --git a/lib/log.h b/lib/log.h
index ae573804..b2934ac3 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -19,7 +19,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_LOG_H
@@ -44,7 +44,7 @@
* please use LOG_ERR instead.
*/
-typedef enum
+typedef enum
{
ZLOG_NONE,
ZLOG_DEFAULT,
@@ -52,7 +52,7 @@ typedef enum
ZLOG_RIP,
ZLOG_BGP,
ZLOG_OSPF,
- ZLOG_RIPNG,
+ ZLOG_RIPNG,
ZLOG_OSPF6,
ZLOG_ISIS,
ZLOG_MASC
@@ -71,7 +71,7 @@ typedef enum
} zlog_dest_t;
#define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1)
-struct zlog
+struct zlog
{
const char *ident; /* daemon name (first arg to openlog) */
zlog_proto_t protocol;
@@ -176,7 +176,7 @@ extern const char * uzlog_get_proto_name (struct zlog *zl);
#define LOOKUP(x, y) mes_lookup(x, x ## _max, y, "(no item found)")
extern const char *lookup (const struct message *, int);
-extern const char *mes_lookup (const struct message *meslist,
+extern const char *mes_lookup (const struct message *meslist,
int max, int index,
const char *no_item);
@@ -203,23 +203,58 @@ extern void zlog_backtrace(int priority);
extern void zlog_backtrace_sigsafe(int priority, void *program_counter);
/* Puts a current timestamp in buf and returns the number of characters
- written (not including the terminating NUL). The purpose of
- this function is to avoid calls to localtime appearing all over the code.
- It caches the most recent localtime result and can therefore
- avoid multiple calls within the same second. If buflen is too small,
- *buf will be set to '\0', and 0 will be returned. */
+ * written (not including the terminating NUL). The purpose of
+ * this function is to avoid calls to localtime appearing all over the code.
+ * It caches the most recent localtime result and can therefore
+ * avoid multiple calls within the same second.
+ *
+ * The buflen MUST be > 1 and the buffer address MUST NOT be NULL.
+ *
+ * If buflen is too small, writes buflen-1 characters followed by '\0'.
+ *
+ * Time stamp is rendered in the form: %Y/%m/%d %H:%M:%S
+ *
+ * This has a fixed length (leading zeros are included) of 19 characters
+ * (unless this code is still in use beyond the year 9999 !)
+ *
+ * Which may be followed by "." and a number of decimal digits, usually 1..6.
+ *
+ * So the maximum time stamp is 19 + 1 + 6 = 26. Adding the trailing '\n', and
+ * rounding up for good measure -- buffer size = 32.
+ */
+#define TIMESTAMP_FORM "%Y/%m/%d %H:%M:%S"
+
+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 */
+/* unprotected version for when mutex already held */
extern size_t uquagga_timestamp(int timestamp_precision /* # subsecond digits */,
char *buf, size_t buflen);
-/* structure useful for avoiding repeated rendering of the same timestamp */
-struct timestamp_control {
- size_t len; /* length of rendered timestamp */
- int precision; /* configuration parameter */
- int already_rendered; /* should be initialized to 0 */
- char buf[40]; /* will contain the rendered timestamp */
-};
+
+/* Generate line to be logged
+ *
+ * Structure used to hold line for log output -- so that need be generated
+ * just once even if output to multiple destinations.
+ *
+ * Note that the buffer length is a hard limit (including terminating '\n''\0'
+ * or '\r''\n''\0'). Do not wish to malloc any larger buffer while logging.
+ */
+enum { logline_buffer_len = 1008 } ;
+struct logline {
+ char* p_nl ; /* address of the first byte of "\n" or "\r\n" */
+ /* NULL => not filled in yet */
+
+ 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" */
+
+ char buf[logline_buffer_len]; /* buffer */
+} ;
+
+extern void
+uvzlog_line(struct logline* ll, struct zlog *zl, int priority,
+ const char *format, va_list va, int crlf) ;
/* Defines for use in command construction: */
diff --git a/lib/mem_tracker.c b/lib/mem_tracker.c
new file mode 100644
index 00000000..a7ee430f
--- /dev/null
+++ b/lib/mem_tracker.c
@@ -0,0 +1,583 @@
+/* Memory Allocation Tracker
+ * 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 "vty.h"
+
+/*==============================================================================
+ * Memory Tracker
+ */
+typedef struct mem_descriptor* mem_descriptor ;
+struct mem_descriptor
+{
+ void* addr ;
+ const char* name ;
+
+ uint32_t next ; /* MS Type is encoded as MS 4 bits */
+ uint32_t size ; /* LS Type is encoded as MS 4 bits */
+} ;
+
+typedef uint32_t md_index ;
+
+enum
+{
+ md_next_bits = 28, /* up to 256M allocated objects */
+ md_next_mask = (1 << md_next_bits) - 1,
+
+ md_index_max = md_next_mask + 1,
+
+ md_size_bits = 28, /* up to 256M individual item */
+ md_size_mask = (1 << md_size_bits) - 1,
+
+ md_size_max = md_size_mask,
+
+ md_next_type_bits = 32 - md_next_bits,
+ md_next_type_mask = (1 << md_next_type_bits) - 1,
+ md_size_type_bits = 32 - md_size_bits,
+ md_size_type_mask = (1 << md_size_type_bits) - 1,
+
+ md_i_index_bits = 16,
+ md_i_index_count = 1 << md_i_index_bits,
+ md_i_index_mask = md_i_index_count - 1,
+
+ md_page_bits = md_next_bits - md_i_index_bits,
+ md_page_count = 1 << md_page_bits,
+ md_page_mask = md_page_count - 1,
+} ;
+
+CONFIRM(MTYPE_MAX < (1 << (md_next_type_bits + md_size_type_bits))) ;
+
+static struct mem_type_tracker
+{
+ struct mem_tracker mt[MTYPE_MAX] ;
+} mem_type_tracker ;
+
+static mem_descriptor mem_page_table[md_page_count] ;
+
+static mem_descriptor mem_free_descriptors ;
+static md_index mem_next_index ;
+
+static struct mem_tracker mem ;
+
+uint32_t mem_base_count ;
+
+md_index* mem_bases ;
+
+inline static void
+mem_md_set_type(mem_descriptor md, enum MTYPE mtype)
+{
+ uint32_t t_ms ;
+ uint32_t t_ls ;
+
+ t_ms = mtype >> md_size_type_bits ;
+ t_ls = mtype ;
+
+ t_ms = (t_ms & md_next_type_mask) << md_next_bits ;
+ t_ls = (t_ls & md_size_type_mask) << md_size_bits ;
+
+ md->next = (md->next & md_next_mask) | t_ms ;
+ md->size = (md->size & md_size_mask) | t_ls ;
+} ;
+
+inline static void
+mem_md_set_next(mem_descriptor md, md_index next)
+{
+ md->next = (md->next & ~md_next_mask) | (next & md_next_mask) ;
+} ;
+
+inline static void
+mem_md_set_size(mem_descriptor md, size_t size)
+{
+ md->size = (md->size & ~md_size_mask) | (size & md_size_mask) ;
+} ;
+
+inline static uint8_t
+mem_md_type(mem_descriptor md)
+{
+ return ( (md->next >> (md_next_bits - md_size_type_bits))
+ & (md_next_type_mask << md_size_type_bits) )
+ | ( (md->size >> md_size_bits) & md_size_type_mask ) ;
+} ;
+
+inline static md_index
+mem_md_next(mem_descriptor md)
+{
+ return md->next & md_next_mask ;
+} ;
+
+inline static size_t
+mem_md_size(mem_descriptor md)
+{
+ return md->size & md_size_mask ;
+} ;
+
+inline static mem_descriptor
+mem_md_ptr(md_index mdi)
+{
+ mem_descriptor page ;
+
+ if (mdi == 0)
+ return NULL ;
+
+ page = mem_page_table[(mdi >> md_i_index_bits) & md_page_mask] ;
+ passert(page != NULL) ;
+ return page + (mdi & md_i_index_mask) ;
+} ;
+
+static void mem_md_make_bases(void) ;
+
+inline static md_index*
+mem_md_base(void* address)
+{
+ if (mem_bases == NULL)
+ mem_md_make_bases() ;
+
+ return mem_bases + ((uintptr_t)address % mem_base_count) ;
+} ;
+
+static void
+mem_md_make_bases(void)
+{
+ md_index* bases_was = mem_bases ;
+ uint32_t count_was = mem_base_count ;
+
+ mem_base_count += 256 * 1024 ;
+ mem_base_count |= 1 ;
+ mem_bases = calloc(mem_base_count, sizeof(md_index)) ;
+
+ if (bases_was == NULL)
+ passert(count_was == 0) ;
+ else
+ {
+ md_index* base = bases_was ;
+ md_index* new_base ;
+ md_index this ;
+ md_index next ;
+ mem_descriptor md ;
+
+ while (count_was)
+ {
+ next = *base++ ;
+ while (next != 0)
+ {
+ this = next ;
+ md = mem_md_ptr(this) ;
+ next = mem_md_next(md) ;
+
+ new_base = mem_md_base(md->addr) ;
+ mem_md_set_next(md, *new_base) ;
+ *new_base = this ;
+ } ;
+ --count_was ;
+ } ;
+
+ free(bases_was) ;
+ } ;
+} ;
+
+static void
+mem_md_make_descriptors(void)
+{
+ mem_descriptor md ;
+ md_index mdi ;
+
+ mdi = mem_next_index ;
+ passert(mdi < md_index_max) ;
+
+ mem_free_descriptors
+ = mem_page_table[(mdi >> md_i_index_bits) & md_page_mask]
+ = calloc(md_i_index_count, sizeof(struct mem_descriptor)) ;
+
+ mem_next_index += md_i_index_count ;
+
+ if (mdi == 0)
+ {
+ ++mem_free_descriptors ; /* don't use index == 0 */
+ ++mdi ;
+ } ;
+
+ md = mem_free_descriptors ;
+ while (mdi < mem_next_index)
+ {
+ md->addr = md + 1 ; /* point at next entry */
+ md->next = mdi ; /* set to point at self */
+ ++md ;
+ ++mdi ;
+ } ;
+ (md-1)->addr = NULL ; /* set end of list */
+} ;
+
+inline static void
+mem_md_malloc(enum MTYPE mtype, void* address, size_t size, const char* name)
+{
+ mem_tracker mtt ;
+ md_index* base ;
+ mem_descriptor md ;
+ md_index mdi ;
+
+ passert(size <= md_size_max) ;
+
+ if (mem_free_descriptors == NULL)
+ mem_md_make_descriptors() ;
+
+ md = mem_free_descriptors ;
+ mem_free_descriptors = md->addr ;
+ mdi = md->next ;
+
+ if (mem.tracked_count >= (mem_base_count * 4))
+ mem_md_make_bases() ;
+
+ base = mem_md_base(address) ;
+
+ md->addr = address ;
+ md->name = name ;
+ md->size = size ;
+ md->next = *base ;
+ mem_md_set_type(md, mtype) ;
+
+ *base = mdi ;
+
+ ++mem.malloc_count ;
+ ++mem.tracked_count ;
+
+ mem.tracked_size += size ;
+
+ if (mem.tracked_max_count < mem.tracked_count)
+ mem.tracked_max_count = mem.tracked_count ;
+
+ if (mem.tracked_max_size < mem.tracked_size)
+ mem.tracked_max_size = mem.tracked_size ;
+
+ mtt = &(mem_type_tracker.mt[mtype]) ;
+
+ ++(mtt->malloc_count) ;
+ ++(mtt->tracked_count) ;
+ mtt->tracked_size += size ;
+
+ if (mtt->tracked_max_count < mtt->tracked_count)
+ mtt->tracked_max_count = mtt->tracked_count ;
+
+ if (mtt->tracked_max_size < mtt->tracked_size)
+ mtt->tracked_max_size = mtt->tracked_size ;
+} ;
+
+inline static void
+mem_md_free(enum MTYPE mtype, void* address)
+{
+ mem_tracker mtt ;
+ md_index* base ;
+ mem_descriptor md, prev_md ;
+ md_index this, next ;
+
+ base = mem_md_base(address) ;
+
+ prev_md = NULL ;
+ this = *base ;
+ while (this != 0)
+ {
+ md = mem_md_ptr(this) ;
+ next = mem_md_next(md) ;
+
+ if (md->addr == address)
+ {
+ if (mem_md_type(md) != mtype)
+ zabort("memory type mismatch in free") ;
+
+ ++mem.free_count ;
+ --mem.tracked_count ;
+
+ mem.tracked_size -= mem_md_size(md) ;
+
+ mtt = &(mem_type_tracker.mt[mtype]) ;
+
+ ++(mtt->free_count) ;
+ --(mtt->tracked_count) ;
+ mtt->tracked_size -= mem_md_size(md) ;
+
+ if (prev_md == NULL)
+ *base = next ;
+ else
+ mem_md_set_next(prev_md, next) ;
+
+ md->addr = mem_free_descriptors ;
+ mem_free_descriptors = md ;
+ md->next = this ;
+
+ return ;
+ }
+ else
+ {
+ prev_md = md ;
+ this = next ;
+ } ;
+ } ;
+
+ zabort("Failed to find memory being freed") ;
+} ;
+
+inline static void
+mem_md_realloc(enum MTYPE mtype, void* old_address, void* new_address,
+ size_t size, const char* name)
+{
+ mem_tracker mtt ;
+ md_index* base ;
+ mem_descriptor md, prev_md ;
+ md_index this, next ;
+
+ if (old_address == NULL)
+ {
+ mem_md_malloc(mtype, new_address, size, name) ;
+ return ;
+ } ;
+
+ passert(size <= md_size_max) ;
+
+ base = mem_md_base(old_address) ;
+
+ prev_md = NULL ;
+ this = *base ;
+ while (this != 0)
+ {
+ md = mem_md_ptr(this) ;
+ next = mem_md_next(md) ;
+
+ if (md->addr == old_address)
+ {
+ if (mem_md_type(md) != mtype)
+ zabort("memory type mismatch in realloc") ;
+
+ ++mem.realloc_count ;
+
+ mem.tracked_size += size - mem_md_size(md) ;
+
+ if (mem.tracked_max_size < mem.tracked_size)
+ mem.tracked_max_size = mem.tracked_size ;
+
+ mtt = &(mem_type_tracker.mt[mtype]) ;
+
+ ++(mtt->realloc_count) ;
+ mtt->tracked_size += size - mem_md_size(md) ;
+
+ if (mtt->tracked_max_size < mtt->tracked_size)
+ mtt->tracked_max_size = mtt->tracked_size ;
+
+ md->name = name ;
+ mem_md_set_size(md, size) ;
+
+ if (old_address == new_address)
+ return ;
+
+ if (prev_md == NULL)
+ *base = next ;
+ else
+ mem_md_set_next(prev_md, next) ;
+
+ base = mem_md_base(new_address) ;
+ mem_md_set_next(md, *base) ;
+ *base = this ;
+
+ md->addr = new_address ;
+
+ return ;
+ }
+ else
+ {
+ prev_md = md ;
+ this = next ;
+ } ;
+ } ;
+
+ zabort("Failed to find memory being realloced") ;
+} ;
+
+/*==============================================================================
+ * Memory Tracker Display
+ */
+
+static const char* scale_d_tags [] =
+{
+ [0] = " " ,
+ [1] = "k",
+ [2] = "m",
+ [3] = "g",
+} ;
+
+static const char* scale_b_tags [] =
+{
+ [0] = " " ,
+ [1] = "KiB",
+ [2] = "MiB",
+ [3] = "GiB",
+} ;
+
+static char*
+mem_show_commas(char* buff, size_t size, uint64_t val, const char* tag)
+{
+ char* p ;
+ const char* q ;
+ int n ;
+
+ passert(size > 10) ;
+
+ p = buff + size ;
+ *(--p) = '\0' ;
+
+ q = tag + strlen(tag) ;
+ while ((p > buff) && (q > tag))
+ *(--p) = *(--q) ;
+
+ n = 3 ;
+ while (p > buff)
+ {
+ *(--p) = '0' + (val % 10) ;
+ val /= 10 ;
+ if (val == 0)
+ break ;
+
+ if ((--n == 0) && (p > buff))
+ {
+ *(--p) = ',' ;
+ n = 3 ;
+ } ;
+ } ;
+
+ return p ;
+} ;
+
+static char*
+mem_show_count(char* buff, size_t size, uint64_t val, int scale)
+{
+ int i, r ;
+
+ i = 0 ;
+ if (scale)
+ {
+ r = 0 ;
+ while ((i < 3) && (val >= 10000))
+ {
+ r = (val % 1000) ;
+ val /= 1000 ;
+ ++i ;
+ } ;
+ if (r >= 500) {
+ val += 1 ;
+ if ((val == 10000) && (i < 3))
+ {
+ val /= 1000 ;
+ ++i ;
+ } ;
+ } ;
+ } ;
+
+ return mem_show_commas(buff, size, val, scale_d_tags[i]) ;
+} ;
+
+static char*
+mem_show_byte_count(char* buff, size_t size, uint64_t val, int scale)
+{
+ int i, r ;
+
+ i = 0 ;
+ if (scale)
+ {
+ r = 0 ;
+ while ((i < 3) && (val >= 10000))
+ {
+ r = (val % 1024) ;
+ val /= 1024 ;
+ ++i ;
+ } ;
+ if (r >= 512) {
+ val += 1 ;
+ if ((val == 10000) && (i < 3))
+ {
+ val /= 1024 ;
+ ++i ;
+ } ;
+ } ;
+ } ;
+
+ return mem_show_commas(buff, size, val, scale_b_tags[i]) ;
+} ;
+
+static int
+show_memory_tracker_summary(struct vty *vty)
+{
+ struct mem_tracker mt ;
+ enum { sbs = 100 } ;
+ char buf[sbs];
+ size_t overhead ;
+
+ LOCK ;
+ overhead = (sizeof(struct mem_descriptor) * mem_next_index)
+ + (sizeof(md_index) * mem_base_count)
+ + (sizeof(mem_descriptor) * md_page_count) ;
+
+ mt = mem ; /* copy the overall memory information */
+ UNLOCK ;
+
+ vty_out (vty, "Memory Tracker Statistics:%s", VTY_NEWLINE);
+ vty_out (vty, " Current memory allocated: %10s%s",
+ mem_show_byte_count(buf, sbs, mt.tracked_size, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Current allocated objects: %8s%s",
+ mem_show_count (buf, sbs, mt.tracked_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Maximum memory allocated: %10s%s",
+ mem_show_byte_count(buf, sbs, mt.tracked_max_size, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Maximum allocated objects: %8s%s",
+ mem_show_count (buf, sbs, mt.tracked_max_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " malloc/calloc call count: %8s%s",
+ mem_show_count (buf, sbs, mt.malloc_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " realloc_call_count: %8s%s",
+ mem_show_count (buf, sbs, mt.realloc_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " free call count: %8s%s",
+ mem_show_count (buf, sbs, mt.free_count, 1),
+ VTY_NEWLINE);
+ vty_out (vty, " Memory Tracker overhead: %10s%s",
+ mem_show_byte_count(buf, sbs, overhead, 1),
+ VTY_NEWLINE);
+ return 1;
+} ;
+
+static int
+show_memory_tracker_detail(struct vty *vty, struct mem_tracker* mt,
+ unsigned long alloc)
+{
+ enum { sbs = 100 } ;
+ char buf[sbs];
+
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->tracked_count, 1)) ;
+ vty_out(vty, "%10s", mem_show_byte_count(buf, sbs, mt->tracked_size, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->tracked_max_count, 1)) ;
+ vty_out(vty, "%10s", mem_show_byte_count(buf, sbs, mt->tracked_max_size, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->malloc_count, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->realloc_count, 1)) ;
+ vty_out(vty, "%8s", mem_show_count(buf, sbs, mt->free_count, 1)) ;
+
+ if (alloc != mt->tracked_count)
+ vty_out(vty, " %8s!!", mem_show_count(buf, sbs, alloc, 1)) ;
+
+ return 1;
+} ;
diff --git a/lib/memory.c b/lib/memory.c
index e11a5e4a..f68dd298 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -31,13 +31,13 @@
#include "qpthreads.h"
/* Needs to be qpthread safe. The system malloc etc are already
- * thread safe, but we need to protect the stats */
+ * thread safe, but we need to protect the stats
+ */
static qpt_mutex_t memory_mutex;
-#define LOCK qpt_mutex_lock(&memory_mutex);
+
+#define LOCK qpt_mutex_lock(&memory_mutex);
#define UNLOCK qpt_mutex_unlock(&memory_mutex);
-static void alloc_inc (int);
-static void alloc_dec (int);
static void log_memstats(int log_priority);
static const struct message mstr [] =
@@ -50,12 +50,57 @@ static const struct message mstr [] =
{ 0, NULL },
};
+/* If using the mem_tracker, include it now. */
+
+typedef struct mem_tracker* mem_tracker ;
+struct mem_tracker
+{
+ uint64_t malloc_count ;
+ uint64_t realloc_count ;
+ uint64_t free_count ;
+
+ uint32_t tracked_count ;
+ size_t tracked_size ;
+
+ uint32_t tracked_max_count ;
+ size_t tracked_max_size ;
+} ;
+
+static void
+mem_tracker_zeroise(struct mem_tracker* mem)
+{
+ memset(mem, 0, sizeof(struct mem_tracker)) ;
+} ;
+
+#ifdef MEMORY_TRACKER
+#include "mem_tracker.c"
+#endif
+
+/*==============================================================================
+ * Keeping track of number of allocated objects of given type
+ */
+
+static struct mstat
+{
+ struct
+ {
+ char *name ;
+ long alloc ;
+ } mt[MTYPE_MAX] ;
+} mstat ;
+
+/*==============================================================================
+ * Memory allocation functions.
+ *
+ * NB: failure to allocate is FATAL -- so no need to test return value.
+ */
+
/* Fatal memory allocation error occured. */
static void __attribute__ ((noreturn))
zerror (const char *fname, int type, size_t size)
{
zlog_err ("%s : can't allocate memory for `%s' size %d: %s\n",
- fname, lookup (mstr, type), (int) size, safe_strerror(errno));
+ fname, lookup (mstr, type), (int) size, safe_strerror(errno));
log_memstats(LOG_WARNING);
/* N.B. It might be preferable to call zlog_backtrace_sigsafe here, since
that function should definitely be safe in an OOM condition. But
@@ -65,72 +110,145 @@ zerror (const char *fname, int type, size_t size)
abort();
}
-/* Memory allocation. */
+/*------------------------------------------------------------------------------
+ * Memory allocation.
+ */
void *
-zmalloc (int type, size_t size)
+zmalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
{
void *memory;
+ LOCK ;
+
memory = malloc (size);
if (memory == NULL)
- zerror ("malloc", type, size);
-
- alloc_inc (type);
+ {
+ UNLOCK ;
+ zerror ("malloc", mtype, size); /* NO RETURN ! */
+ }
+ else
+ {
+ mstat.mt[mtype].alloc++;
+#ifdef MEMORY_TRACKER
+ mem_md_malloc(mtype, memory, size, name) ;
+#endif
+ UNLOCK ;
+ } ;
return memory;
}
-/* Memory allocation with num * size with cleared. */
+/*------------------------------------------------------------------------------
+ * Memory allocation zeroising the allocated area.
+ */
void *
-zcalloc (int type, size_t size)
+zcalloc (enum MTYPE mtype, size_t size MEMORY_TRACKER_NAME)
{
void *memory;
+ LOCK ;
+
memory = calloc (1, size);
if (memory == NULL)
- zerror ("calloc", type, size);
-
- alloc_inc (type);
+ {
+ UNLOCK ;
+ zerror ("calloc", mtype, size); /* NO RETURN ! */
+ }
+ else
+ {
+ mstat.mt[mtype].alloc++;
+#ifdef MEMORY_TRACKER
+ mem_md_malloc(mtype, memory, size, name) ;
+#endif
+ UNLOCK ;
+ } ;
return memory;
}
-/* Memory reallocation. */
+/*------------------------------------------------------------------------------
+ * Memory reallocation.
+ */
void *
-zrealloc (int type, void *ptr, size_t size)
+zrealloc (enum MTYPE mtype, void *ptr, size_t size MEMORY_TRACKER_NAME)
{
void *memory;
+ LOCK ;
+
memory = realloc (ptr, size);
if (memory == NULL)
- zerror ("realloc", type, size);
+ {
+ UNLOCK ;
+ zerror ("realloc", mtype, size); /* NO RETURN ! */
+ }
+ else
+ {
+ if (ptr == NULL)
+ mstat.mt[mtype].alloc++;
+#ifdef MEMORY_TRACKER
+ mem_md_realloc(mtype, ptr, memory, size, name) ;
+#endif
+ UNLOCK ;
+ } ;
+
return memory;
-}
+} ;
-/* Memory free. */
+/*------------------------------------------------------------------------------
+ * Memory free.
+ */
void
-zfree (int type, void *ptr)
+zfree (enum MTYPE mtype, void *ptr)
{
- alloc_dec (type);
+ LOCK ;
+
free (ptr);
-}
-/* String duplication. */
+ mstat.mt[mtype].alloc--;
+#ifdef MEMORY_TRACKER
+ mem_md_free(mtype, ptr) ;
+#endif
+
+ UNLOCK ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * String duplication.
+ */
char *
-zstrdup (int type, const char *str)
+zstrdup (enum MTYPE mtype, const char *str MEMORY_TRACKER_NAME)
{
void *dup;
+ LOCK ;
+
dup = strdup (str);
if (dup == NULL)
- zerror ("strdup", type, strlen (str));
- alloc_inc (type);
+ {
+ UNLOCK ;
+ zerror ("strdup", mtype, strlen (str)); /* NO RETURN ! */
+ }
+ else
+ {
+ mstat.mt[mtype].alloc++;
+#ifdef MEMORY_TRACKER
+ mem_md_malloc(mtype, dup, strlen(str)+1, name) ;
+#endif
+ UNLOCK ;
+ } ;
+
return dup;
}
+/*==============================================================================
+ * Memory allocation with built in logging
+ */
+
#ifdef MEMORY_LOG
+
static struct
{
const char *name;
@@ -142,10 +260,11 @@ static struct
unsigned long t_realloc;
unsigned long t_free;
unsigned long c_strdup;
-} mstat [MTYPE_MAX];
+} mlog_stat [MTYPE_MAX];
static void
-mtype_log (char *func, void *memory, const char *file, int line, int type)
+mtype_log (char *func, void *memory, const char *file, int line,
+ enum MTYPE type)
{
zlog_debug ("%s: %s %p %s %d", func, lookup (mstr, type), memory, file, line);
}
@@ -156,8 +275,8 @@ mtype_zmalloc (const char *file, int line, int type, size_t size)
void *memory;
LOCK
- mstat[type].c_malloc++;
- mstat[type].t_malloc++;
+ mlog_stat[type].c_malloc++;
+ mlog_stat[type].t_malloc++;
UNLOCK
memory = zmalloc (type, size);
@@ -167,13 +286,13 @@ mtype_zmalloc (const char *file, int line, int type, size_t size)
}
void *
-mtype_zcalloc (const char *file, int line, int type, size_t size)
+mtype_zcalloc (const char *file, int line, enum MTYPE type, size_t size)
{
void *memory;
LOCK
- mstat[type].c_calloc++;
- mstat[type].t_calloc++;
+ mlog_stat[type].c_calloc++;
+ mlog_stat[type].t_calloc++;
UNLOCK
memory = zcalloc (type, size);
@@ -183,13 +302,14 @@ mtype_zcalloc (const char *file, int line, int type, size_t size)
}
void *
-mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size)
+mtype_zrealloc (const char *file, int line, enum MTYPE type, void *ptr,
+ size_t size)
{
void *memory;
/* Realloc need before allocated pointer. */
LOCK
- mstat[type].t_realloc++;
+ mlog_stat[type].t_realloc++;
UNLOCK
memory = zrealloc (type, ptr, size);
@@ -201,10 +321,10 @@ mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size)
/* Important function. */
void
-mtype_zfree (const char *file, int line, int type, void *ptr)
+mtype_zfree (const char *file, int line, enum MTYPE type, void *ptr)
{
LOCK
- mstat[type].t_free++;
+ mlog_stat[type].t_free++;
UNLOCK
mtype_log ("xfree", ptr, file, line, type);
@@ -213,12 +333,12 @@ mtype_zfree (const char *file, int line, int type, void *ptr)
}
char *
-mtype_zstrdup (const char *file, int line, int type, const char *str)
+mtype_zstrdup (const char *file, int line, enum MTYPE type, const char *str)
{
char *memory;
LOCK
- mstat[type].c_strdup++;
+ mlog_stat[type].c_strdup++;
UNLOCK
memory = zstrdup (type, str);
@@ -227,31 +347,11 @@ mtype_zstrdup (const char *file, int line, int type, const char *str)
return memory;
}
-#else
-static struct
-{
- char *name;
- long alloc;
-} mstat [MTYPE_MAX];
-#endif /* MEMORY_LOG */
-
-/* Increment allocation counter. */
-static void
-alloc_inc (int type)
-{
- LOCK
- mstat[type].alloc++;
- UNLOCK
-}
+#endif
-/* Decrement allocation counter. */
-static void
-alloc_dec (int type)
-{
- LOCK
- mstat[type].alloc--;
- UNLOCK
-}
+/*==============================================================================
+ * Showing memory allocation
+ */
/* Looking up memory status from vty interface. */
#include "vector.h"
@@ -261,8 +361,13 @@ alloc_dec (int type)
static void
log_memstats(int pri)
{
+ struct mstat mst ;
struct mlist *ml;
+ LOCK ;
+ mst = mstat ;
+ UNLOCK ;
+
for (ml = mlists; ml->list; ml++)
{
struct memory_list *m;
@@ -270,7 +375,7 @@ log_memstats(int pri)
zlog (NULL, pri, "Memory utilization in module %s:", ml->name);
for (m = ml->list; m->index >= 0; m++)
{
- unsigned long alloc = mtype_stats_alloc(m->index);
+ unsigned long alloc = mst.mt[m->index].alloc ;
if (m->index && alloc)
zlog (NULL, pri, " %-30s: %10ld", m->format, alloc);
}
@@ -280,17 +385,22 @@ log_memstats(int pri)
void
log_memstats_stderr (const char *prefix)
{
+ struct mstat mst ;
struct mlist *ml;
struct memory_list *m;
int i;
int j = 0;
+ LOCK ;
+ mst = mstat ;
+ UNLOCK ;
+
for (ml = mlists; ml->list; ml++)
{
i = 0;
for (m = ml->list; m->index >= 0; m++)
{
- unsigned long alloc = mtype_stats_alloc(m->index);
+ unsigned long alloc = mst.mt[m->index].alloc ;
if (m->index && alloc)
{
if (!i)
@@ -321,37 +431,99 @@ log_memstats_stderr (const char *prefix)
}
static void
-show_separator(struct vty *vty)
+show_memory_type_vty (struct vty *vty, const char* name,
+ struct mem_tracker* mt, long int alloc, int sep)
{
- vty_out (vty, "-----------------------------\r\n");
-}
+ if (sep)
+ vty_out (vty, "-----------------------------%s", VTY_NEWLINE) ;
+
+ vty_out (vty, "%-30s:", name) ;
+#ifdef MEMORY_TRACKER
+ show_memory_tracker_detail(vty, mt, alloc) ;
+#else
+ vty_out (vty, " %10ld", alloc) ;
+#endif
+ vty_out (vty, "%s", VTY_NEWLINE);
+} ;
static int
-show_memory_vty (struct vty *vty, struct memory_list *list)
+show_memory_vty (struct vty *vty, struct memory_list *m, struct mlist* ml,
+ int needsep)
{
- struct memory_list *m;
- int needsep = 0;
+ int notempty = 0 ;
- for (m = list; m->index >= 0; m++)
- if (m->index == 0)
- {
- if (needsep)
- {
- show_separator (vty);
- needsep = 0;
- }
- }
- else
- {
- unsigned long alloc = mtype_stats_alloc(m->index);
- if (alloc)
- {
- vty_out (vty, "%-30s: %10ld\r\n", m->format, alloc);
- needsep = 1;
- }
- }
- return needsep;
-}
+ long int alloc ;
+
+ struct mstat mst ;
+ struct mem_tracker mem_tot ;
+ struct mem_tracker mem_one ;
+ struct mem_tracker* mt ;
+
+#ifdef MEMORY_TRACKER
+ struct mem_type_tracker mem_tt ;
+#endif
+
+ LOCK ;
+ mst = mstat ;
+#ifdef MEMORY_TRACKER
+ mem_tt = mem_type_tracker ;
+#endif
+ UNLOCK ;
+
+ mem_tracker_zeroise(&mem_tot) ;
+ mem_tracker_zeroise(&mem_one) ;
+
+ if ((m == NULL) && (ml != NULL))
+ m = (ml++)->list ;
+
+ while (m != NULL)
+ {
+ if (m->index <= 0)
+ {
+ needsep = notempty ;
+ if (m->index < 0)
+ {
+ if (ml == NULL)
+ m = NULL ;
+ else
+ m = (ml++)->list ;
+ }
+ else
+ ++m ;
+ }
+ else
+ {
+ alloc = mst.mt[m->index].alloc ;
+#ifdef MEMORY_TRACKER
+ mt = &(mem_tt.mt[m->index]) ;
+#else
+ mt = &mem_one ;
+ mt->tracked_count = alloc ;
+#endif
+
+ mem_tot.malloc_count += mt->malloc_count ;
+ mem_tot.free_count += mt->free_count ;
+ mem_tot.realloc_count += mt->realloc_count ;
+ mem_tot.tracked_count += mt->tracked_count ;
+ mem_tot.tracked_max_count += mt->tracked_max_count ;
+ mem_tot.tracked_size += mt->tracked_size ;
+ mem_tot.tracked_max_size += mt->tracked_max_size ;
+
+ if (alloc || mt->tracked_count)
+ {
+ show_memory_type_vty(vty, m->format, mt, alloc, needsep) ;
+ needsep = 0 ;
+ notempty = 1 ;
+ } ;
+
+ ++m ;
+ } ;
+ } ;
+
+ show_memory_type_vty(vty, "Total", &mem_tot, mem_tot.tracked_count, notempty);
+
+ return 1 ;
+} ;
#ifdef HAVE_MALLINFO
static int
@@ -390,10 +562,40 @@ show_memory_mallinfo (struct vty *vty)
VTY_NEWLINE);
vty_out (vty, "(see system documentation for 'mallinfo' for meaning)%s",
VTY_NEWLINE);
+
return 1;
}
#endif /* HAVE_MALLINFO */
+
+DEFUN_CALL (show_memory_summary,
+ show_memory_summary_cmd,
+ "show memory summary",
+ "Show running system information\n"
+ "Memory statistics\n"
+ "Summary memory statistics\n")
+{
+#ifdef MEMORY_TRACKER
+ show_memory_tracker_summary(vty) ;
+#else
+ long alloc = 0 ;
+ int mtype ;
+
+# ifdef HAVE_MALLINFO
+ show_memory_mallinfo (vty);
+# endif /* HAVE_MALLINFO */
+
+ LOCK ;
+ for (mtype = 1 ; mtype < MTYPE_MAX ; ++mtype)
+ alloc += mstat[mtype] ;
+ UNLOCK
+ vty_out(vty, "%ld items allocated%s", alloc, VTY_NEWLINE) ;
+
+#endif /* MEMORY_TRACKER */
+
+ return CMD_SUCCESS;
+}
+
DEFUN_CALL (show_memory_all,
show_memory_all_cmd,
"show memory all",
@@ -401,19 +603,16 @@ DEFUN_CALL (show_memory_all,
"Memory statistics\n"
"All memory statistics\n")
{
- struct mlist *ml;
int needsep = 0;
#ifdef HAVE_MALLINFO
- needsep = show_memory_mallinfo (vty);
+ needsep |= show_memory_mallinfo (vty);
#endif /* HAVE_MALLINFO */
+#ifdef MEMORY_TRACKER
+ needsep |= show_memory_tracker_summary(vty) ;
+#endif
- for (ml = mlists; ml->list; ml++)
- {
- if (needsep)
- show_separator (vty);
- needsep = show_memory_vty (vty, ml->list);
- }
+ show_memory_vty (vty, NULL, mlists, needsep);
return CMD_SUCCESS;
}
@@ -431,7 +630,7 @@ DEFUN_CALL (show_memory_lib,
"Memory statistics\n"
"Library memory\n")
{
- show_memory_vty (vty, memory_list_lib);
+ show_memory_vty (vty, memory_list_lib, NULL, 0);
return CMD_SUCCESS;
}
@@ -442,7 +641,7 @@ DEFUN_CALL (show_memory_zebra,
"Memory statistics\n"
"Zebra memory\n")
{
- show_memory_vty (vty, memory_list_zebra);
+ show_memory_vty (vty, memory_list_zebra, NULL, 0);
return CMD_SUCCESS;
}
@@ -453,7 +652,7 @@ DEFUN_CALL (show_memory_rip,
"Memory statistics\n"
"RIP memory\n")
{
- show_memory_vty (vty, memory_list_rip);
+ show_memory_vty (vty, memory_list_rip, NULL, 0);
return CMD_SUCCESS;
}
@@ -464,7 +663,7 @@ DEFUN_CALL (show_memory_ripng,
"Memory statistics\n"
"RIPng memory\n")
{
- show_memory_vty (vty, memory_list_ripng);
+ show_memory_vty (vty, memory_list_ripng, NULL, 0);
return CMD_SUCCESS;
}
@@ -475,7 +674,7 @@ DEFUN_CALL (show_memory_bgp,
"Memory statistics\n"
"BGP memory\n")
{
- show_memory_vty (vty, memory_list_bgp);
+ show_memory_vty (vty, memory_list_bgp, NULL, 0);
return CMD_SUCCESS;
}
@@ -486,7 +685,7 @@ DEFUN_CALL (show_memory_ospf,
"Memory statistics\n"
"OSPF memory\n")
{
- show_memory_vty (vty, memory_list_ospf);
+ show_memory_vty (vty, memory_list_ospf, NULL, 0);
return CMD_SUCCESS;
}
@@ -497,7 +696,7 @@ DEFUN_CALL (show_memory_ospf6,
"Memory statistics\n"
"OSPF6 memory\n")
{
- show_memory_vty (vty, memory_list_ospf6);
+ show_memory_vty (vty, memory_list_ospf6, NULL, 0);
return CMD_SUCCESS;
}
@@ -508,7 +707,7 @@ DEFUN_CALL (show_memory_isis,
"Memory statistics\n"
"ISIS memory\n")
{
- show_memory_vty (vty, memory_list_isis);
+ show_memory_vty (vty, memory_list_isis, NULL, 0);
return CMD_SUCCESS;
}
@@ -529,6 +728,7 @@ memory_finish (void)
void
memory_init (void)
{
+ install_element (RESTRICTED_NODE, &show_memory_summary_cmd);
install_element (RESTRICTED_NODE, &show_memory_cmd);
install_element (RESTRICTED_NODE, &show_memory_all_cmd);
install_element (RESTRICTED_NODE, &show_memory_lib_cmd);
@@ -539,6 +739,7 @@ memory_init (void)
install_element (RESTRICTED_NODE, &show_memory_ospf6_cmd);
install_element (RESTRICTED_NODE, &show_memory_isis_cmd);
+ install_element (VIEW_NODE, &show_memory_summary_cmd);
install_element (VIEW_NODE, &show_memory_cmd);
install_element (VIEW_NODE, &show_memory_all_cmd);
install_element (VIEW_NODE, &show_memory_lib_cmd);
@@ -549,6 +750,7 @@ memory_init (void)
install_element (VIEW_NODE, &show_memory_ospf6_cmd);
install_element (VIEW_NODE, &show_memory_isis_cmd);
+ install_element (ENABLE_NODE, &show_memory_summary_cmd);
install_element (ENABLE_NODE, &show_memory_cmd);
install_element (ENABLE_NODE, &show_memory_all_cmd);
install_element (ENABLE_NODE, &show_memory_lib_cmd);
@@ -625,11 +827,11 @@ mtype_memstr (char *buf, size_t len, unsigned long bytes)
}
unsigned long
-mtype_stats_alloc (int type)
+mtype_stats_alloc (enum MTYPE type)
{
unsigned long result;
LOCK
- result = mstat[type].alloc;
+ result = mstat.mt[type].alloc;
UNLOCK
return result;
}
diff --git a/lib/memory.h b/lib/memory.h
index 09fddf85..5fa5c5ac 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -52,45 +52,63 @@ extern struct mlist mlists[];
#define XSTRDUP(mtype, str) \
mtype_zstrdup (__FILE__, __LINE__, (mtype), (str))
#else
-#define XMALLOC(mtype, size) zmalloc ((mtype), (size))
-#define XCALLOC(mtype, size) zcalloc ((mtype), (size))
-#define XREALLOC(mtype, ptr, size) zrealloc ((mtype), (ptr), (size))
+
+#define MEMORY_TRACKER 1
+
+#ifdef MEMORY_TRACKER
+#define MEMORY_TRACKER_NAME , const char* name
+#define MEMORY_TRACKER_FUNC , __func__
+#else
+#define MEMORY_TRACKER_NAME
+#define MEMORY_TRACKER_FUNC
+#endif
+
+#define XMALLOC(mtype, size) zmalloc ((mtype), (size) \
+ MEMORY_TRACKER_FUNC)
+#define XCALLOC(mtype, size) zcalloc ((mtype), (size) \
+ MEMORY_TRACKER_FUNC)
+#define XREALLOC(mtype, ptr, size) zrealloc ((mtype), (ptr), (size) \
+ MEMORY_TRACKER_FUNC)
#define XFREE(mtype, ptr) do { \
zfree ((mtype), (ptr)); \
ptr = NULL; } \
while (0)
-#define XSTRDUP(mtype, str) zstrdup ((mtype), (str))
+#define XSTRDUP(mtype, str) zstrdup ((mtype), (str) \
+ MEMORY_TRACKER_FUNC)
+
#endif /* MEMORY_LOG */
#define SIZE(t,n) (sizeof(t) * (n))
/* Prototypes of memory function. */
-extern void *zmalloc (int type, size_t size);
-extern void *zcalloc (int type, size_t size);
-extern void *zrealloc (int type, void *ptr, size_t size);
-extern void zfree (int type, void *ptr);
-extern char *zstrdup (int type, const char *str);
+extern void *zmalloc (enum MTYPE type, size_t size MEMORY_TRACKER_NAME);
+extern void *zcalloc (enum MTYPE type, size_t size MEMORY_TRACKER_NAME);
+extern void *zrealloc (enum MTYPE type, void *ptr, size_t size
+ MEMORY_TRACKER_NAME);
+extern void zfree (enum MTYPE type, void *ptr);
+extern char *zstrdup (enum MTYPE type, const char *str MEMORY_TRACKER_NAME);
-extern void *mtype_zmalloc (const char *file, int line, int type, size_t size);
+extern void *mtype_zmalloc (const char *file, int line, enum MTYPE type,
+ size_t size);
-extern void *mtype_zcalloc (const char *file, int line, int type,
- size_t num, size_t size);
+extern void *mtype_zcalloc (const char *file, int line, enum MTYPE type,
+ size_t num, size_t size);
-extern void *mtype_zrealloc (const char *file, int line, int type, void *ptr,
- size_t size);
+extern void *mtype_zrealloc (const char *file, int line, enum MTYPE type,
+ void *ptr, size_t size);
-extern void mtype_zfree (const char *file, int line, int type,
- void *ptr);
+extern void mtype_zfree (const char *file, int line, enum MTYPE type,
+ void *ptr);
-extern char *mtype_zstrdup (const char *file, int line, int type,
- const char *str);
+extern char *mtype_zstrdup (const char *file, int line, enum MTYPE type,
+ const char *str);
extern void memory_init (void);
extern void memory_init_r (void);
extern void memory_finish (void);
extern void log_memstats_stderr (const char *);
/* return number of allocations outstanding for the type */
-extern unsigned long mtype_stats_alloc (int);
+extern unsigned long mtype_stats_alloc (enum MTYPE);
/* Human friendly string for given byte count */
#define MTYPE_MEMSTR_LEN 20
diff --git a/lib/memtypes.awk b/lib/memtypes.awk
index 5429f6e8..a8004977 100644
--- a/lib/memtypes.awk
+++ b/lib/memtypes.awk
@@ -54,7 +54,7 @@ BEGIN {
}
END {
- printf("enum\n{\n MTYPE_TMP = 1,\n");
+ printf("enum MTYPE\n{\n MTYPE_TMP = 1,\n");
for (i = 0; i < tcount; i++) {
if (mtype[i] != "" && mtype[i] != "MTYPE_TMP")
printf (" %s,\n", mtype[i]);
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 2f2ac239..4ed5cbc0 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -45,7 +45,13 @@ struct memory_list memory_list_lib[] =
{ MTYPE_TSD, "Thread specific data" },
{ MTYPE_VTY, "VTY" },
{ MTYPE_VTY_OUT_BUF, "VTY output buffer" },
- { MTYPE_VTY_HIST, "VTY history" },
+ { 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_QSTRING, "qstring structure" },
+ { MTYPE_QSTRING_BODY, "qstring body" },
{ MTYPE_IF, "Interface" },
{ MTYPE_CONNECTED, "Connected" },
{ MTYPE_CONNECTED_LABEL, "Connected interface label" },
diff --git a/lib/mqueue.c b/lib/mqueue.c
index a6dca32f..8b557dfe 100644
--- a/lib/mqueue.c
+++ b/lib/mqueue.c
@@ -129,9 +129,67 @@
*/
/*==============================================================================
- * Initialisation etc. for Message Queues.
+ * Message Block allocation statics
+ *
+ * Once a message block is allocated it is not deallocated, but kept ready
+ * for future use.
+ *
+ * Keeps a count of free message blocks. (Could at some later date reduce the
+ * number of free message blocks if it is known that some burst of messages has
+ * now passed.)
+ */
+
+static pthread_mutex_t mqb_mutex ; /* for allocation of mqueue blocks */
+
+static mqueue_block mqb_free_list = NULL ;
+static unsigned mqb_free_count = 0 ;
+
+/*==============================================================================
+ * Initialise and shut down Message Queue and Message Block handling
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise Message Queue handling.
+ *
+ * Must be called before any qpt_threads are started.
+ *
+ * Freezes qpthreads_enabled.
+ */
+extern void
+mqueue_initialise(void)
+{
+ if (qpthreads_enabled_freeze)
+ qpt_mutex_init_new(&mqb_mutex, qpt_mutex_quagga) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Shut down Message Queue handling.
+ *
+ * Release all resources used.
+ *
+ * NB: all pthreads must have stopped -- mutex must be free and no further
+ * uses may be made.
+ */
+extern void
+mqueue_finish(void)
+{
+ mqueue_block mqb ;
+
+ while ((mqb = mqb_free_list) != NULL)
+ {
+ assert(mqb_free_count != 0) ;
+ mqb_free_list = mqb->next ;
+ XFREE(MTYPE_MQUEUE_BLOCK, mqb) ;
+ } ;
+
+ assert(mqb_free_count == 0) ;
+
+ qpt_mutex_destroy_keep(&mqb_mutex) ;
+} ;
+
+/*==============================================================================
+ * Initialisation etc. for Message Queue
*
- * TODO: how to shut down a message queue... for reset/exit ?
*/
/*------------------------------------------------------------------------------
@@ -328,11 +386,6 @@ mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval)
* mqueue_initialise MUST be called before the first message block is allocated.
*/
-static pthread_mutex_t mqb_mutex ;
-
-static mqueue_block mqb_free_list = NULL ;
-static unsigned mqb_free_count = 0 ;
-
inline static size_t mqb_argv_size(mqb_index_t alloc)
{
return alloc * sizeof(mqb_arg_t) ;
@@ -476,7 +529,7 @@ static void mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig) ;
* NB: this works perfectly well if !qpthreads enabled. Of course, there can
* never be any waiters... so no kicking is ever done.
*/
-void
+extern void
mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority)
{
qpt_mutex_lock(&mq->mutex) ;
@@ -582,7 +635,7 @@ mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority)
*
* Returns a message block if one is available. (And not otherwise.)
*/
-mqueue_block
+extern mqueue_block
mqueue_dequeue(mqueue_queue mq, int wait, void* arg)
{
mqueue_block mqb ;
@@ -750,7 +803,7 @@ mqueue_revoke(mqueue_queue mq, void* arg0)
*
* (Signal will never be kicked if !qpthreads_enabled.)
*/
-int
+extern int
mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig)
{
int kicked ;
@@ -834,7 +887,7 @@ mqueue_local_dequeue(mqueue_local_queue lmq)
*
* Returns address of the structure.
*/
-mqueue_thread_signal
+extern mqueue_thread_signal
mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
int signum)
{
@@ -860,7 +913,7 @@ mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
* Frees the structure if required, and returns NULL.
* Otherwise zeroises the structure, and returns address of same.
*/
-mqueue_thread_signal
+extern mqueue_thread_signal
mqueue_thread_signal_reset(mqueue_thread_signal mqt, int free_structure)
{
passert(mqt->prev == NULL) ;
@@ -1248,19 +1301,3 @@ mqb_argv_extend(mqueue_block mqb, mqb_index_t iv)
mqb->argv_alloc = need ;
} ;
-
-/*==============================================================================
- * Initialise Message Queue handling
- *
- * Must be called before any qpt_threads are started.
- *
- * Freezes qpthreads_enabled.
- *
- * TODO: how do we shut down message queue handling ?
- */
-void
-mqueue_initialise(void)
-{
- if (qpthreads_enabled_freeze)
- qpt_mutex_init_new(&mqb_mutex, qpt_mutex_quagga) ;
-} ;
diff --git a/lib/mqueue.h b/lib/mqueue.h
index 355aec23..f22ea022 100644
--- a/lib/mqueue.h
+++ b/lib/mqueue.h
@@ -198,6 +198,9 @@ struct mqueue_local_queue
extern void
mqueue_initialise(void) ;
+extern void
+mqueue_finish(void) ;
+
extern mqueue_queue
mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type) ;
diff --git a/lib/network.c b/lib/network.c
index 3373983b..61d98717 100644
--- a/lib/network.c
+++ b/lib/network.c
@@ -17,14 +17,23 @@
* 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.
*/
#include <zebra.h>
#include "log.h"
#include "network.h"
-/* Read nbytes from fd and store into ptr. */
+/*------------------------------------------------------------------------------
+ * Read nbytes from fd and store into ptr -- BLOCKING
+ *
+ * Loops internally if gets EINTR.
+ *
+ * Returns: >= 0 -- number of bytes read
+ * < 0 => error
+ *
+ * NB: if applied to a NON-BLOCKING fd, may return EAGAIN or EWOULDBLOCK
+ */
int
readn (int fd, u_char *ptr, int nbytes)
{
@@ -33,24 +42,86 @@ readn (int fd, u_char *ptr, int nbytes)
nleft = nbytes;
- while (nleft > 0)
+ while (nleft > 0)
{
nread = read (fd, ptr, nleft);
- if (nread < 0)
- return (nread);
+ if (nread > 0)
+ {
+ nleft -= nread;
+ ptr += nread;
+ }
+ else if (nread == 0)
+ break;
else
- if (nread == 0)
- break;
-
- nleft -= nread;
- ptr += nread;
+ {
+ if (errno != EINTR)
+ return (nread);
+ }
}
return nbytes - nleft;
-}
+}
+
+/*------------------------------------------------------------------------------
+ * Read up to nbyte bytes into buf -- assuming NON-BLOCKING.
+ *
+ * Loops internally if gets EINTR -- so if does not read everything asked for,
+ * that must be because the read would otherwise block.
+ *
+ * Returns: 0..n -- number of bytes read
+ * -1 => failed -- see errno
+ * -2 => EOF met immediately
+ *
+ * NB: if asked to write zero bytes, does nothing and will return 0.
+ *
+ * Reading zero bytes is defined for all types of files, and may be used
+ * to probe for error state.
+ */
+int
+read_nb(int fd, void* buf, size_t nbyte)
+{
+ size_t nleft = nbyte ;
+
+ do
+ {
+ int ret = read(fd, buf, nleft);
+
+ if (ret > 0)
+ {
+ buf = (char*)buf + ret ;
+ nleft -= ret ;
+ }
+ else if (ret == 0)
+ {
+ if (nleft < nbyte)
+ break ; /* if read something before EOF */
+
+ return -2 ; /* hit EOF immediately */
+ }
+ else
+ {
+ int err = errno ;
+ if ((err == EAGAIN) || (err == EWOULDBLOCK))
+ break ;
+ if (err != EINTR)
+ return -1 ; /* failed */
+ } ;
+ } while (nleft > 0) ;
+
+ return (nbyte - nleft) ;
+} ;
-/* Write nbytes from ptr to fd. */
+/*------------------------------------------------------------------------------
+ * Write nbytes to fd from ptr -- BLOCKING
+ *
+ * Loops internally if gets EINTR.
+ *
+ * Returns: >= 0 -- number of bytes written
+ * < 0 => error
+ *
+ * NB: if applied to a NON-BLOCKING fd, may return EAGAIN or EWOULDBLOCK
+ */
int
writen(int fd, const u_char *ptr, int nbytes)
{
@@ -59,19 +130,75 @@ writen(int fd, const u_char *ptr, int nbytes)
nleft = nbytes;
- while (nleft > 0)
+ while (nleft > 0)
{
nwritten = write(fd, ptr, nleft);
-
- if (nwritten <= 0)
- return (nwritten);
- nleft -= nwritten;
- ptr += nwritten;
+ if (nwritten > 0)
+ {
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+ else if (nwritten == 0)
+ break ;
+ else
+ {
+ if (errno != EINTR)
+ return (nwritten);
+ }
}
return nbytes - nleft;
}
+/*------------------------------------------------------------------------------
+ * Write up to nbyte bytes from buf -- assuming NON-BLOCKING.
+ *
+ * Loops internally if gets EINTR.
+ *
+ * Returns: 0..n -- number of bytes written
+ * -1 => failed -- see errno
+ *
+ * NB: if asked to write zero bytes, does nothing and will return 0.
+ *
+ * Writing zero bytes is defined for "regular files", but not for anything
+ * else.
+ */
+int
+write_nb(int fd, void* buf, size_t nbyte)
+{
+ size_t nleft = nbyte ;
+
+ while (nleft > 0)
+ {
+ int ret = write(fd, buf, nleft);
+
+ if (ret > 0)
+ {
+ buf = (char*)buf + ret ;
+ nleft -= ret ;
+ }
+ 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 (nbyte - nleft) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set fd to non-blocking
+ *
+ * Returns: 0 => OK
+ * -1 => failed
+ */
int
set_nonblocking(int fd)
{
diff --git a/lib/network.h b/lib/network.h
index 4d9c2284..72d38b52 100644
--- a/lib/network.h
+++ b/lib/network.h
@@ -17,7 +17,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_NETWORK_H
@@ -33,6 +33,10 @@ extern int writen (int, const u_char *, int);
-1 on error. */
extern int set_nonblocking(int fd);
+/* Non-Blocking versions of read/write */
+int read_nb(int fd, void* buf, size_t nbyte) ;
+int write_nb(int fd, void* buf, size_t nbyte) ;
+
/* Does the I/O error indicate that the operation should be retried later? */
#define ERRNO_IO_RETRY(EN) \
(((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
diff --git a/lib/node_type.h b/lib/node_type.h
new file mode 100644
index 00000000..7ec1107d
--- /dev/null
+++ b/lib/node_type.h
@@ -0,0 +1,81 @@
+/* Command handler node_type stuff -- header
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_NODE_TYPE_H
+#define _ZEBRA_NODE_TYPE_H
+
+/* There are some command levels which called from command node. */
+enum node_type
+{
+ AUTH_NODE, /* Authentication mode of vty interface. */
+ RESTRICTED_NODE, /* Restricted view mode */
+ VIEW_NODE, /* View node. Default mode of vty interface. */
+ AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
+ ENABLE_NODE, /* Enable node. */
+
+ MIN_DO_SHORTCUT_NODE = ENABLE_NODE,
+ /* May not "do xxx" at any node lower */
+ MAX_NON_CONFIG_NODE = ENABLE_NODE,
+ /* May not be higher than this without owning
+ * the configuration symbol of power */
+
+ CONFIG_NODE, /* Config node. Default mode of config file. */
+
+ MIN_CONTEXT_NODE = CONFIG_NODE,
+ /* May not change context to any node lower */
+
+ SERVICE_NODE, /* Service node. */
+ DEBUG_NODE, /* Debug node. */
+ AAA_NODE, /* AAA node. */
+ KEYCHAIN_NODE, /* Key-chain node. */
+ KEYCHAIN_KEY_NODE, /* Key-chain key node. */
+ INTERFACE_NODE, /* Interface mode node. */
+ ZEBRA_NODE, /* zebra connection node. */
+ TABLE_NODE, /* rtm_table selection node. */
+ RIP_NODE, /* RIP protocol mode node. */
+ RIPNG_NODE, /* RIPng protocol mode node. */
+ BGP_NODE, /* BGP protocol mode which includes BGP4+ */
+ BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */
+ BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */
+ BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */
+ BGP_IPV6_NODE, /* BGP IPv6 address family */
+ BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */
+ OSPF_NODE, /* OSPF protocol mode */
+ OSPF6_NODE, /* OSPF protocol for IPv6 mode */
+ ISIS_NODE, /* ISIS protocol mode */
+ MASC_NODE, /* MASC for multicast. */
+ IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
+ IP_NODE, /* Static ip route node. */
+ ACCESS_NODE, /* Access list node. */
+ PREFIX_NODE, /* Prefix list node. */
+ ACCESS_IPV6_NODE, /* Access list node. */
+ PREFIX_IPV6_NODE, /* Prefix list node. */
+ AS_LIST_NODE, /* AS list node. */
+ COMMUNITY_LIST_NODE, /* Community list node. */
+ RMAP_NODE, /* Route map node. */
+ SMUX_NODE, /* SNMP configuration node. */
+ DUMP_NODE, /* Packet dump node. */
+ FORWARDING_NODE, /* IP forwarding node. */
+ PROTOCOL_NODE, /* protocol filtering node */
+ VTY_NODE, /* Vty node. */
+};
+
+#endif /* _ZEBRA_NODE_TYPE_H */
diff --git a/lib/plist.c b/lib/plist.c
index 9cf099dd..41868e96 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -1074,7 +1074,7 @@ vty_prefix_list_value_print(struct vty* vty, struct prefix_list_entry* pe,
vty_out(vty, "%s ", prefix_list_type_str(pe)) ;
if (pe->flags & PREFIX_ANY)
- vty_puts(vty, "any");
+ vty_out(vty, "any");
else
{
struct prefix *p = &pe->prefix ;
@@ -1092,7 +1092,7 @@ vty_prefix_list_value_print(struct vty* vty, struct prefix_list_entry* pe,
if (with_stats)
vty_out (vty, " (hit count: %lu, refcount: %lu)", pe->hitcnt, pe->refcnt);
- vty_puts(vty, post) ;
+ vty_out(vty, post) ;
}
static void __attribute__ ((unused))
@@ -1391,7 +1391,7 @@ vty_show_prefix_entry (struct vty *vty, struct prefix_list *plist,
struct prefix_list_entry* p_l = vector_get_last_item(&plist->list) ;
vty_prefix_list_name_print(vty, plist, ":") ;
- vty_out_newline(vty) ;
+ vty_out(vty, VTY_NEWLINE) ;
vty_prefix_list_desc_print(vty, plist, 3, VTY_NEWLINE) ;
@@ -2798,7 +2798,7 @@ config_write_prefix_afi (afi_t afi, struct vty *vty)
}
else
{
- vty_puts(vty, "!! ") ;
+ vty_out(vty, "!! ") ;
vty_prefix_list_undefined_print(vty, afi, sym, VTY_NEWLINE) ;
write++ ;
} ;
diff --git a/lib/qlib_init.c b/lib/qlib_init.c
index 400b34fc..c44de575 100644
--- a/lib/qlib_init.c
+++ b/lib/qlib_init.c
@@ -23,6 +23,7 @@
#include "zassert.h"
#include "memory.h"
#include "qpthreads.h"
+#include "qpselect.h"
#include "thread.h"
#include "privs.h"
#include "mqueue.h"
@@ -68,6 +69,7 @@
void
qlib_init_first_stage(void)
{
+ qps_start_up() ;
}
void
@@ -86,6 +88,7 @@ void
qexit(int exit_code)
{
safe_finish();
+ mqueue_finish();
zprivs_finish();
thread_finish();
memory_finish();
diff --git a/lib/qpnexus.c b/lib/qpnexus.c
index cb0bd12c..6fc9129d 100644
--- a/lib/qpnexus.c
+++ b/lib/qpnexus.c
@@ -172,6 +172,10 @@ qpn_start(void* arg)
/* now in our thread, complete initialisation */
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]))() ;
+
/* Until required to terminate, loop */
done = 1 ;
while (!qpn->terminate)
@@ -226,11 +230,11 @@ qpn_start(void* arg)
if (wait)
mqueue_done_waiting(qpn->queue, qpn->mts);
- /* process I/O actions */
+ /* process I/O actions */
while (actions)
actions = qps_dispatch_next(qpn->selection) ;
- /* process timers */
+ /* process timers */
now = qt_get_monotonic() ;
while (qtimer_pile_dispatch_next(qpn->pile, now))
done = 1 ;
@@ -241,14 +245,16 @@ qpn_start(void* arg)
done |= ((qpn_hook_function*)(qpn->background.hooks[i]))() ;
} ;
- /* last bit of code to run in this thread */
- if (qpn->in_thread_final != NULL)
- qpn->in_thread_final();
+ /* custom in-thread finalization */
+ for (i = qpn->in_thread_final.count - 1; i > 0 ; --i)
+ ((qpn_init_function*)(qpn->in_thread_final.hooks[i]))() ;
return NULL;
}
-/* Now running in our thread, complete initialisation */
+/*------------------------------------------------------------------------------
+ * Now running in our thread, do common initialisation
+ */
static void
qpn_in_thread_init(qpn_nexus qpn)
{
@@ -295,13 +301,11 @@ qpn_in_thread_init(qpn_nexus qpn)
qpn->mts = mqueue_thread_signal_init(qpn->mts, qpn->thread_id, SIGMQUEUE);
if (qpn->selection != NULL)
qps_set_signal(qpn->selection, SIGMQUEUE, newmask);
-
- /* custom in-thread initialization */
- if (qpn->in_thread_init != NULL)
- qpn->in_thread_init();
}
-/* Ask the thread to terminate itself quickly and cleanly */
+/*------------------------------------------------------------------------------
+ * Ask the thread to terminate itself quickly and cleanly
+ */
void
qpn_terminate(qpn_nexus qpn)
{
diff --git a/lib/qpnexus.h b/lib/qpnexus.h
index d5b7c5a6..c2cc6463 100644
--- a/lib/qpnexus.h
+++ b/lib/qpnexus.h
@@ -47,20 +47,21 @@
*
*/
-/* maximum time in seconds to sit in a pselect */
+/* maximum time in seconds to sit in a pselect */
#define MAX_PSELECT_WAIT 10
-/* signal for message queues */
+/* signal for message queues */
#define SIGMQUEUE SIGUSR2
-/* number of event hooks */
+/* number of hooks per hook list */
enum { qpn_hooks_max = 4 } ;
/*==============================================================================
* Data Structures.
*/
-typedef int qpn_hook_function(void) ;
+typedef int qpn_hook_function(void) ; /* dispatch of tasks */
+typedef int qpn_init_function(void) ; /* start/stop work */
typedef struct qpn_hook_list* qpn_hook_list ;
struct qpn_hook_list
@@ -95,14 +96,24 @@ struct qpn_nexus
/* qpthread routine, can override */
void* (*start)(void*);
- /* in-thread initialise, can override. Called within the thread
- * after all other initialisation just before thread loop */
- void (*in_thread_init)(void);
+ /* in-thread initialise, can override. Called within the thread after all
+ * other initialisation just before thread loop
+ *
+ * These are typedef int qpn_init_function(void).
+ *
+ * These are executed in the order given.
+ */
+ struct qpn_hook_list in_thread_init ;
- /* in-thread finalise, can override. Called within thread
- * just before thread dies. Nexus components all exist but
- * thread loop is no longer executed */
- void (*in_thread_final)(void);
+ /* in-thread finalise, can override. Called within thread just before
+ * thread dies. Nexus components all exist but thread loop is no longer
+ * executed
+ *
+ * These are typedef int qpn_init_function(void).
+ *
+ * These are executed in the reverse of the order given.
+ */
+ struct qpn_hook_list in_thread_final ;
/* in-thread queue(s) of events or other work.
*
@@ -110,6 +121,8 @@ struct qpn_nexus
* loop. So in addition to the mqueue, I/O, timers and any background stuff,
* the thread may have other queue(s) of things to be done.
*
+ * These are typedef int qpn_hook_function(void).
+ *
* Hook function can process some queue(s) of things to be done. It does not
* have to empty its queues, but it MUST only return 0 if all queues are now
* empty.
@@ -121,6 +134,8 @@ struct qpn_nexus
* The hook functions are called at the bottom of the qpnexus loop, but only
* when there is absolutely nothing else to do.
*
+ * These are typedef int qpn_hook_function(void).
+ *
* The hook function should do some unit of background work (if there is any)
* and return. MUST return 0 iff there is no more work to do.
*/
diff --git a/lib/qpselect.c b/lib/qpselect.c
index d3f8e5ad..882f4173 100644
--- a/lib/qpselect.c
+++ b/lib/qpselect.c
@@ -29,6 +29,14 @@
#include "memory.h"
#include "vector.h"
+enum { qdebug =
+#ifdef QDEBUG
+ 1
+#else
+ 0
+#endif
+};
+
/*==============================================================================
* Quagga pselect -- qps_xxxx
*
@@ -92,38 +100,65 @@
* the file removed from the selection... there are no restrictions.
*/
-static int qps_super_set_map_made = 0 ;
-
-static void qps_make_super_set_map(void) ;
-
/*==============================================================================
* qps_selection handling
*/
+/* See qps_make_super_set_map() below. */
+static short fd_byte_count[FD_SETSIZE] ; /* number of bytes for fds 0..fd */
+
/* Forward references */
+static void qps_make_super_set_map(void) ;
+static void qps_selection_re_init(qps_selection qps) ;
static qps_file qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert) ;
static void qps_file_remove(qps_selection qps, qps_file qf) ;
static void qps_super_set_zero(fd_super_set* p_set, int n) ;
+static int qps_super_set_cmp(fd_super_set* p_a, fd_super_set* p_b, int n) ;
static int qps_next_fd_pending(fd_super_set* pending, int fd, int fd_last) ;
static void qps_selection_validate(qps_selection qps) ;
-/* See qps_make_super_set_map() and qps_pselect() below. */
-static short fd_byte_count[FD_SETSIZE] ; /* number of bytes for fds 0..fd */
+/*------------------------------------------------------------------------------
+ * Initialise a selection -- allocating it if required.
+ *
+ * Returns the qps_selection.
+ */
+
+extern void
+qps_start_up(void)
+{
+ qps_make_super_set_map() ; /* map the fd_super_set */
+} ;
-/* Initialise a selection -- allocating it if required.
+/*------------------------------------------------------------------------------
+ * Initialise a selection -- allocating it if required.
*
* Returns the qps_selection.
+ *
+ * NB: when initialising an existing selection which has been used before, it
+ * is the caller's responsibility to have dealt with its contents before
+ * calling this.
*/
qps_selection
qps_selection_init_new(qps_selection qps)
{
- if (!qps_super_set_map_made)
- qps_make_super_set_map() ; /* map the fd_super_set */
-
if (qps == NULL)
qps = XCALLOC(MTYPE_QPS_SELECTION, sizeof(struct qps_selection)) ;
- else
- memset(qps, 0, sizeof(struct qps_selection)) ;
+
+ qps_selection_re_init(qps) ;
+
+ return qps ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Re-initialise a selection.
+ *
+ * It is the caller's responsibility to have dealt with any active files before
+ * calling this.
+ */
+static void
+qps_selection_re_init(qps_selection qps)
+{
+ memset(qps, 0, sizeof(struct qps_selection)) ;
/* Zeroising initialises:
*
@@ -146,24 +181,13 @@ qps_selection_init_new(qps_selection qps)
* signum -- no signal to be enabled
* sigmask -- unset
*
- * So nothing much else to do -- see also qps_selection_re_init(), below.
+ * So nothing much else to do:
*/
qps->fd_last = -1 ; /* not an fd in sight. */
-
- return qps ;
-} ;
-
-/* Re-initialise a selection.
- */
-static void
-qps_selection_re_init(qps_selection qps)
-{
- memset(qps, 0, sizeof(struct qps_selection)) ;
-
- qps->fd_last = -1 ; /* not an fd in sight. */
} ;
-/* Add given file to the selection, setting its fd and pointer to further
+/*------------------------------------------------------------------------------
+ * Add given file to the selection, setting its fd and pointer to further
* file information. All modes are disabled.
*
* This initialises most of the qps_file structure, but not the actions.
@@ -187,7 +211,8 @@ qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info)
qps_file_lookup_fd(qps, fd, qf) ; /* Add. */
} ;
-/* Remove given file from its selection, if any.
+/*------------------------------------------------------------------------------
+ * Remove given file from its selection, if any.
*
* It is the callers responsibility to ensure that the file is in a suitable
* state to be removed from the selection.
@@ -201,7 +226,8 @@ qps_remove_file(qps_file qf)
qps_file_remove(qf->selection, qf) ;
} ;
-/* Ream (another) file out of the selection.
+/*------------------------------------------------------------------------------
+ * Ream (another) file out of the selection.
*
* If selection is empty, release the qps_selection structure, if required.
*
@@ -243,7 +269,8 @@ qps_selection_ream(qps_selection qps, int free_structure)
return qf ;
} ;
-/* Set the signal mask for the selection.
+/*------------------------------------------------------------------------------
+ * Set the signal mask for the selection.
*
* This supports the unmasking of a single signal for the duration of the
* pselect operation.
@@ -270,7 +297,8 @@ qps_set_signal(qps_selection qps, int signum, sigset_t sigmask)
} ;
} ;
-/* Execute a pselect for the given selection -- subject to the given maximum
+/*------------------------------------------------------------------------------
+ * Execute a pselect for the given selection -- subject to the given maximum
* time to wait.
*
* There is no support for an infinite timeout.
@@ -291,8 +319,8 @@ qps_pselect(qps_selection qps, qtime_t max_wait)
fd_set* p_fds[qps_mnum_count] ;
int n ;
- /* TODO: put this under a debug skip */
- qps_selection_validate(qps) ;
+ if (qdebug)
+ qps_selection_validate(qps) ;
/* If there is stuff still pending, tidy up by zeroising the result */
/* vectors. This is to make sure that when bits are copied from */
@@ -361,7 +389,8 @@ qps_pselect(qps_selection qps, qtime_t max_wait)
zabort_errno("Failed in pselect") ;
} ;
-/* Dispatch the next errored/readable/writeable file, as returned by the
+/*------------------------------------------------------------------------------
+ * Dispatch the next errored/readable/writeable file, as returned by the
* most recent qps_pselect().
*
* Processes the errored files, then the readable and lastly the writeable.
@@ -386,8 +415,8 @@ qps_dispatch_next(qps_selection qps)
qps_file qf ;
qps_mnum_t mnum ;
- /* TODO: put this under a debug skip */
- qps_selection_validate(qps) ;
+ if (qdebug)
+ qps_selection_validate(qps) ;
if (qps->pend_count == 0)
return 0 ; /* quit immediately of nothing to do. */
@@ -435,7 +464,8 @@ qps_dispatch_next(qps_selection qps)
* qps_file structure handling
*/
-/* Initialise qps_file structure -- allocating one if required.
+/*------------------------------------------------------------------------------
+ * Initialise qps_file structure -- allocating one if required.
*
* If a template is given, then the action functions are copied from there to
* the new structure. See above for discussion of action functions.
@@ -472,7 +502,8 @@ qps_file_init_new(qps_file qf, qps_file template)
return qf ;
} ;
-/* Free dynamically allocated qps_file structure.
+/*------------------------------------------------------------------------------
+ * Free dynamically allocated qps_file structure.
*
* It is the caller's responsibility to have removed it from any selection it
* may have been in.
@@ -485,7 +516,8 @@ qps_file_free(qps_file qf)
XFREE(MTYPE_QPS_FILE, qf) ;
} ;
-/* Enable (or re-enable) file for the given mode.
+/*------------------------------------------------------------------------------
+ * Enable (or re-enable) file for the given mode.
*
* If the action argument is not NULL, set the action for the mode.
*
@@ -519,7 +551,8 @@ qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action)
} ;
} ;
-/* Set action for given mode -- does not enable/disable.
+/*------------------------------------------------------------------------------
+ * Set action for given mode -- does not enable/disable.
*
* May unset an action by setting it NULL !
*
@@ -538,7 +571,8 @@ qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action)
qf->actions[mnum] = action ;
} ;
-/* Disable file for one or more modes.
+/*------------------------------------------------------------------------------
+ * Disable file for one or more modes.
*
* If there are any pending pending results for the modes, those are discarded.
*
@@ -608,7 +642,9 @@ qps_disable_modes(qps_file qf, qps_mbit_t mbits)
* fd.
*/
-/* Comparison function for binary chop */
+/*------------------------------------------------------------------------------
+ * Comparison function for binary chop
+ */
static int
qps_fd_cmp(const int** pp_fd, const qps_file* p_qf)
{
@@ -619,7 +655,8 @@ qps_fd_cmp(const int** pp_fd, const qps_file* p_qf)
return 0 ;
}
-/* Lookup/Insert file by file-descriptor.
+/*------------------------------------------------------------------------------
+ * Lookup/Insert file by file-descriptor.
*
* Inserts if insert argument is not NULL.
*
@@ -696,7 +733,8 @@ qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert)
return qf ;
} ;
-/* Remove file from selection.
+/*------------------------------------------------------------------------------
+ * Remove file from selection.
*
* NB: FATAL error if file is not in the selection, or the file-descriptor
* is invalid (or refers to some other file !).
@@ -751,7 +789,7 @@ qps_file_remove(qps_selection qps, qps_file qf)
qf->selection = NULL ;
} ;
- /*==============================================================================
+/*==============================================================================
* fd_super_set support.
*
* For large sets of file descriptors something faster than testing for all
@@ -787,65 +825,100 @@ static uint8_t fd_bit_map [FD_SETSIZE] ; /* maps fd to bit in byte */
static int8_t fd_first_map[256] ; /* maps byte value to 0..7, where that */
/* is the lowest fd bit set in byte. */
-#define QPS_TESTING 0 /* true => testing */
-
-#if !QPS_TESTING
-
-/* Not testing, so map to the standard FD_SET etc. functions. */
-# define qFD_SET FD_SET
-# define qFD_CLR FD_CLR
-# define qFD_ISSET FD_ISSET
-# define qFD_ZERO FD_ZERO
-
-#else
-
-/* Set up the testing */
+/*------------------------------------------------------------------------------
+ * Cross Check
+ *
+ * Where the shape of the bit map is known, this will test that the correct
+ * bit map has been deduced.
+ *
+ * Requires the following to be defined:
+ *
+ * QPS_CROSS_CHECK -- weebb
+ *
+ * where: w -- number of bytes per word, 1..
+ * ee -- 10 => big-endian bytes in word
+ * 01 => little-endian
+ * bb -- 70 => b7 is MS bit, b0 is LS bit
+ * 07 => b0 is MS bit, b7 is LS bit
+ *
+ * So:
+ *
+ * 10170 => a bit map handled as bytes
+ *
+ * 40170 => a bit map handled as little-endian 32-bit words
+ *
+ * ...though this is actually no different to handling the bit map
+ * as bytes.
+ *
+ * 41070 => a bit map handled as big-endian 32-bit words
+ *
+ * 10107 => a bit map handled as bytes, where the "leftmost" bit is the first
+ * bit in the bitmap:
+ *
+ * ...a big-endian machine, where the bit map is handled as n-bit
+ * words, with the "leftmost" bit being the first would be like
+ * this too.
+ */
-# define QPS_TEST_WORD 4 /* Wordsize */
-# define QPS_TEST_BE 1 /* true => big-endian */
-# define QPS_TEST_B_ORD 07 /* 07 => bits 0..7, 70 => bits 7..0 */
+#define QPS_CROSS_CHECK 40170
-# define QPS_TEST_WORD_BITS (QPS_TEST_WORD * 8)
-# if QPS_TEST_BE
-# define QPS_BYTE(fd) ( ((fd / QPS_TEST_WORD_BITS) * QPS_TEST_WORD) \
- + (QPS_TEST_WORD - 1) - ((fd % QPS_TEST_WORD_BITS) / 8) )
+enum {
+#ifdef QPS_CROSS_CHECK
+ qps_cross_check = 1,
+ qps_cc_word_bytes = QPS_CROSS_CHECK / 10000,
+ qps_cc_byte_ord = (QPS_CROSS_CHECK / 100) % 100,
+ qps_cc_bit_ord = QPS_CROSS_CHECK % 100,
#else
-# define QPS_BYTE(fd) ( fd / 8 )
+ qps_cross_check = 0, /* no cross check */
+ qps_cc_word_bytes = 1, /* byte_wise */
+ qps_cc_byte_ord = 1, /* little-endian */
+ qps_cc_bit_ord = 70, /* standard bit order */
#endif
+ qps_cc_word_bits = qps_cc_word_bytes * 8
+} ;
-# if QPS_TEST_B_ORD == 07
-# define QPS_BIT(fd) (0x01 << (fd & 0x7))
-# else
-# define QPS_BIT(fd) (0x80 >> (fd & 0x7))
-# endif
+CONFIRM((qps_cc_word_bytes == 16) || (qps_cc_word_bytes == 8)
+ || (qps_cc_word_bytes == 4)
+ || (qps_cc_word_bytes == 2)
+ || (qps_cc_word_bytes == 1)) ;
+CONFIRM((qps_cc_byte_ord == 10) || (qps_cc_byte_ord == 1)) ;
+CONFIRM((qps_cc_bit_ord == 70) || (qps_cc_bit_ord == 7)) ;
- static void
- qFD_SET(int fd, fd_set* set)
- {
- *((uint8_t*)set + QPS_BYTE(fd)) |= QPS_BIT(fd) ;
- } ;
+/* Functions required for the cross check. */
- static void
- qFD_CLR(int fd, fd_set* set)
- {
- *((uint8_t*)set + QPS_BYTE(fd)) &= ~QPS_BIT(fd) ;
- } ;
+static inline int
+qpd_cc_word(int fd)
+{
+ return fd / qps_cc_word_bits ;
+} ;
- static int
- qFD_ISSET(int fd, fd_set* set)
- {
- return (*((uint8_t*)set + QPS_BYTE(fd)) & QPS_BIT(fd)) != 0 ;
- } ;
+static inline int
+qps_cc_byte(int fd)
+{
+ if (qps_cc_byte_ord == 10)
+ return (qpd_cc_word(fd) * qps_cc_word_bytes)
+ + qps_cc_word_bytes - 1 - ((fd % qps_cc_word_bits) / 8) ;
+ else
+ return fd / 8 ;
+} ;
- static void
- qFD_ZERO(fd_set* set)
- {
- memset(set, 0, sizeof(fd_set)) ;
- } ;
+static inline uint8_t
+qps_cc_bit(int fd)
+{
+ if (qps_cc_bit_ord == 70)
+ return 0x01 << (fd & 0x7) ;
+ else
+ return 0x80 >> (fd & 0x7) ;
+} ;
-#endif
+static int
+ccFD_ISSET(int fd, fd_set* set)
+{
+ return (*((uint8_t*)set + qps_cc_byte(fd)) & qps_cc_bit(fd)) != 0 ;
+} ;
-/* Scan for next fd in given fd set, and clear it.
+/*------------------------------------------------------------------------------
+ * Scan for next fd in given fd set, and clear it.
*
* Starts at the given fd, will not consider anything above fd_last.
*
@@ -886,7 +959,8 @@ qps_next_fd_pending(fd_super_set* pending, int fd, int fd_last)
return fd ;
} ;
-/* Make a map of the fd_super_set.
+/*------------------------------------------------------------------------------
+ * Make a map of the fd_super_set.
*
* The form of an fd_set is not defined. This code verifies that it is, in
* fact a bit vector, and hence that the fd_super_set works here !
@@ -903,11 +977,11 @@ qps_make_super_set_map(void)
qps_super_set_zero(&test, 1) ;
for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
- if (qFD_ISSET(fd, &test.fdset))
+ if (FD_ISSET(fd, &test.fdset))
zabort("Zeroised fd_super_set is not empty") ;
/* (2) check that zeroising the fd_set doesn't change things */
- qFD_ZERO(&test.fdset) ;
+ FD_ZERO(&test.fdset) ;
for (iw = 0 ; iw < FD_SUPER_SET_WORD_SIZE ; ++iw)
if (test.words[iw] != 0)
zabort("Zeroised fd_super_set is not all zero words") ;
@@ -918,7 +992,7 @@ qps_make_super_set_map(void)
{
fd_word_t w ;
- qFD_SET(fd, &test.fdset) ;
+ FD_SET(fd, &test.fdset) ;
w = 0 ;
for (iw = 0 ; iw < FD_SUPER_SET_WORD_SIZE ; ++iw)
@@ -949,7 +1023,7 @@ qps_make_super_set_map(void)
if (w == 0)
zabort("FD_SET did not set any bit in any word") ;
- qFD_CLR(fd, &test.fdset) ;
+ FD_CLR(fd, &test.fdset) ;
for (iw = 0 ; iw < FD_SUPER_SET_WORD_SIZE ; ++iw)
if (test.words[iw] != 0)
@@ -1006,6 +1080,9 @@ qps_make_super_set_map(void)
fd_first_map[i] = fd ;
} ;
+ if (fd_first_map[0] != -1)
+ zabort("Broken fd_first_map -- invalid result for 0") ;
+
for (i = 1 ; i < 256 ; ++i)
if (fd_first_map[i] == -1)
zabort("Broken fd_first_map -- missing bits") ;
@@ -1026,31 +1103,31 @@ qps_make_super_set_map(void)
fd_byte_count[fd] = c ;
} ;
-#if QPS_TESTING
-
- /* Checking that the maps have been correctly deduced */
+ if (!qps_cross_check)
+ return ;
+ /*----------------------------------------------------------------------------
+ * Checking that the maps have been correctly deduced -- where know what
+ * the mapping really is !
+ */
for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
{
uint8_t b ;
short c ;
- iw = fd / QPS_TEST_WORD_BITS ;
- if (QPS_TEST_BE)
- ib = ( ((fd / QPS_TEST_WORD_BITS) * QPS_TEST_WORD) +
- (QPS_TEST_WORD - 1) - ((fd % QPS_TEST_WORD_BITS) / 8) ) ;
- else
- ib = ( fd / 8 ) ;
+ FD_ZERO(&test.fdset) ;
+ FD_SET(fd, &test.fdset) ;
+ if (!ccFD_ISSET(fd, &test.fdset))
+ zabort("FD_SET and ccFD_ISSET differ") ;
- if (QPS_TEST_B_ORD == 07)
- b = 0x01 << (fd % 8) ;
- else
- b = 0x80 >> (fd % 8) ;
+ iw = qpd_cc_word(fd) ;
+ ib = qps_cc_byte(fd) ;
+ b = qps_cc_bit(fd) ;
- if (QPS_TEST_BE)
- c = (iw + 1) * QPS_TEST_WORD ;
+ if (qps_cc_byte_ord == 10)
+ c = (iw + 1) * 4 ;
else
- c = (ib + 1) ;
+ c = ib + 1 ;
if (fd_word_map[fd] != iw)
zabort("Broken fd_word_map") ;
@@ -1066,7 +1143,7 @@ qps_make_super_set_map(void)
{
uint8_t b = i ;
fd = 0 ;
- if (QPS_TEST_B_ORD == 07)
+ if (qps_cc_bit_ord == 70)
{
while ((b & 1) == 0)
{
@@ -1087,14 +1164,11 @@ qps_make_super_set_map(void)
zabort("Broken fd_first_map") ;
} ;
- zabort("OK fd mapping") ;
-#endif
-
- /* Phew -- we're all set now */
- qps_super_set_map_made = 1 ;
+ return ;
} ;
-/* Zeroise 'n' contiguous fd_super_sets
+/*------------------------------------------------------------------------------
+ * Zeroise 'n' contiguous fd_super_sets
*
* NB: this MUST be used in place of FD_ZERO because the fd_set may be shorter
* than the overlayed words/bytes vectors.
@@ -1107,17 +1181,10 @@ qps_super_set_zero(fd_super_set* p_set, int n)
memset(p_set, 0, SIZE(fd_super_set, n)) ;
} ;
-#if 0 /* Mask unused function */
-/* Copy 'n' contiguous fd_super_sets
- */
-static void
-qps_super_set_copy(fd_super_set* p_dst, fd_super_set* p_src, int n)
-{
- memcpy(p_dst, p_src, SIZE(fd_super_set, n)) ;
-} ;
-#endif
-
-/* Compare 'n' contiguous fd_super_sets
+/*------------------------------------------------------------------------------
+ * Compare 'n' contiguous fd_super_sets
+ *
+ * Returns 0 <=> equal
*/
static int
qps_super_set_cmp(fd_super_set* p_a, fd_super_set* p_b, int n)
@@ -1125,7 +1192,8 @@ qps_super_set_cmp(fd_super_set* p_a, fd_super_set* p_b, int n)
return memcmp(p_a, p_b, SIZE(fd_super_set, n)) ;
} ;
-/* Count the number of bits set in 'n' contiguous fd_super_sets.
+/*------------------------------------------------------------------------------
+ * Count the number of bits set in 'n' contiguous fd_super_sets.
*/
static int
qps_super_set_count(fd_super_set* p_set, int n)
diff --git a/lib/qpselect.h b/lib/qpselect.h
index 538ebf68..561eebb2 100644
--- a/lib/qpselect.h
+++ b/lib/qpselect.h
@@ -163,16 +163,19 @@ struct qps_file
* qps_selection handling
*/
-qps_selection
+extern void
+qps_start_up(void) ;
+
+extern qps_selection
qps_selection_init_new(qps_selection qps) ;
-void
+extern void
qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info) ;
-void
+extern void
qps_remove_file(qps_file qf) ;
-qps_file
+extern qps_file
qps_selection_ream(qps_selection qps, int free_structure) ;
/* Ream out selection and free the selection structure. */
@@ -180,32 +183,32 @@ qps_selection_ream(qps_selection qps, int free_structure) ;
/* Ream out selection but keep the selection structure. */
#define qps_selection_ream_keep(qps) qps_selection_ream(qps, 0)
-void
+extern void
qps_set_signal(qps_selection qps, int signum, sigset_t sigmask) ;
-int
+extern int
qps_pselect(qps_selection qps, qtime_mono_t timeout) ;
-int
+extern int
qps_dispatch_next(qps_selection qps) ;
/*==============================================================================
* qps_file structure handling
*/
-qps_file
+extern qps_file
qps_file_init_new(qps_file qf, qps_file template) ;
-void
+extern void
qps_file_free(qps_file qf) ;
-void
+extern void
qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action) ;
-void
+extern void
qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action) ;
-void
+extern void
qps_disable_modes(qps_file qf, qps_mbit_t mbits) ;
Inline void*
diff --git a/lib/qstring.c b/lib/qstring.c
new file mode 100644
index 00000000..f847e0b0
--- /dev/null
+++ b/lib/qstring.c
@@ -0,0 +1,227 @@
+/* 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 "qstring.h"
+
+#include "memory.h"
+#include "zassert.h"
+
+/*==============================================================================
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise qstring -- allocate if required.
+ *
+ * If non-zero len is given, a body is allocated (for at least len + 1).
+ *
+ * Returns: address of qstring
+ *
+ * NB: assumes initialising a new structure. If not, then caller should
+ * use qs_reset() or qs_set_empty().
+ */
+extern qstring
+qs_init_new(qstring qs, size_t len)
+{
+ if (qs == NULL)
+ qs = XCALLOC(MTYPE_QSTRING, sizeof(qstring_t)) ;
+ 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
+ */
+
+ if (len != 0)
+ qs_alloc(qs, len) ;
+
+ return qs ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate or reallocate so that string is big enough for the given length.
+ *
+ * Allocates to 16 byte boundaries.
+ *
+ * Returns: the number of bytes *allocated*, which includes the byte for
+ * possible trailing '\0'.
+ *
+ * NB: allocates EXTRA space for trailing '\0' beyond given length.
+ */
+extern size_t
+qs_alloc(qstring qs, size_t len)
+{
+ len = (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
+ {
+ assert(qs->size > 0) ;
+ qs->size *= 2 ;
+ if (qs->size < len)
+ qs->size = len ;
+ qs->body = XREALLOC(MTYPE_QSTRING_BODY, qs->body, qs->size) ;
+ } ;
+
+ return qs->size ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free body of qstring -- zeroise size, len and cp
+ */
+extern void
+qs_free_body(qstring qs)
+{
+ if (qs->body != NULL)
+ XFREE(MTYPE_QSTRING_BODY, qs->body) ; /* sets qs->body = NULL */
+
+ qs->size = 0 ;
+ qs->len = 0 ;
+ qs->cp = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset qstring -- free body and, if required, free the structure.
+ *
+ * If not freeing the structure, zeroise size, len and cp -- qs_free_body()
+ *
+ * Returns: NULL if freed the structure
+ * address of structure, otherwise
+ */
+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
+ {
+ qs->size = 0 ;
+ qs->len = 0 ;
+ qs->cp = 0 ;
+ } ;
+
+ return qs ;
+} ;
+
+/*==============================================================================
+ * printf(0 and vprintf() type functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Formatted print to qstring -- cf printf()
+ */
+extern int
+qs_printf(qstring qs, const char* format, ...)
+{
+ va_list args;
+ int result ;
+
+ va_start (args, format);
+ result = qs_vprintf(qs, format, args);
+ va_end (args);
+
+ return result;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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'.
+ *
+ * Also note that given a zero length the string address may be NULL, and the
+ * result is still the length required.
+ */
+extern int
+qs_vprintf(qstring qs, const char *format, va_list args)
+{
+ va_list ac ;
+ int len ;
+
+ while (1)
+ {
+ va_copy(ac, args);
+ qs->len = len = vsnprintf (qs->body, qs->size, format, ac) ;
+ va_end(ac);
+
+ if (len < (int)qs->size)
+ return len ; /* quit if done (or error) */
+
+ qs_alloc(qs, len) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Other operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be copy of the given string.
+ *
+ * Sets qs->len to the length of the string (excluding trailing '\0')
+ *
+ * NB: if stc == NULL, sets qstring to be zero length string.
+ */
+extern size_t
+qs_set(qstring qs, const char* src)
+{
+ 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 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set qstring to be leading 'n' bytes of given string.
+ *
+ * NB: src string MUST be at least that long.
+ *
+ * NB: src may not be NULL unless len == 0.
+ */
+extern size_t
+qs_set_n(qstring qs, const char* src, size_t n)
+{
+ qs_need(qs, n) ; /* sets qs->len */
+ if (n != 0)
+ memcpy(qs->body, src, n) ;
+
+ *((char*)qs->body + n) = '\0' ;
+
+ return n ;
+} ;
diff --git a/lib/qstring.h b/lib/qstring.h
new file mode 100644
index 00000000..1841657e
--- /dev/null
+++ b/lib/qstring.h
@@ -0,0 +1,365 @@
+/* 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_QSTRING_H
+#define _ZEBRA_QSTRING_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 "qstrings" address the ...
+ *
+ *
+ *
+ */
+
+typedef struct qstring qstring_t ;
+typedef struct qstring* qstring ;
+
+struct qstring
+{
+ void* body ;
+ size_t size ;
+
+ size_t len ;
+ size_t cp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Access functions for body of qstring -- to take care of casting pointers
+ *
+ * NB: if the body has not yet been allocated, these functions will return
+ * NULL or NULL + the offset.
+ */
+Inline char* /* pointer to body of qstring */
+qs_chars(qstring qs)
+{
+ return (char*)qs->body ;
+} ;
+
+Inline unsigned char* /* pointer to body of qstring */
+qs_bytes(qstring qs)
+{
+ return (unsigned char*)qs->body ;
+} ;
+
+Inline char* /* pointer to given offset in qstring */
+qs_chars_at(qstring qs, size_t off)
+{
+ return qs_chars(qs) + off ;
+} ;
+
+Inline unsigned char* /* pointer to given offset in qstring */
+qs_bytes_at(qstring qs, size_t off)
+{
+ return qs_bytes(qs) + off ;
+} ;
+
+Inline char* /* pointer to 'cp' offset in qstring */
+qs_cp_char(qstring qs)
+{
+ return qs_chars_at(qs, qs->cp) ;
+} ;
+
+Inline unsigned char* /* pointer to 'cp' offset in qstring */
+qs_cp_byte(qstring qs)
+{
+ return qs_bytes_at(qs, qs->cp) ;
+} ;
+
+Inline char* /* pointer to 'len' offset in qstring */
+qs_ep_char(qstring qs)
+{
+ return qs_chars_at(qs, qs->len) ;
+} ;
+
+Inline unsigned char* /* pointer to 'len' offset in qstring */
+qs_ep_byte(qstring qs)
+{
+ return qs_bytes_at(qs, qs->len) ;
+} ;
+
+/*==============================================================================
+ * 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) ;
+
+#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) ;
+
+extern int
+qs_vprintf(qstring qs, const char *format, va_list args) ;
+
+extern size_t
+qs_set(qstring qs, const char* s) ;
+
+extern size_t
+qs_set_n(qstring qs, const char* s, size_t len) ;
+
+Inline size_t
+qs_need(qstring qs, size_t len) ;
+
+Inline size_t
+qs_set_len(qstring qs, size_t len) ;
+
+Inline void
+qs_set_empty(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_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) ;
+
+/*==============================================================================
+ * The Inline functions.
+ */
+
+/*------------------------------------------------------------------------------
+ * 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')
+ *
+ * NB: asking for 0 bytes will cause a body to be allocated, ready for any
+ * '\0' !
+ *
+ * NB: has no effect on 'cp' or 'len'.
+ */
+Inline size_t
+qs_need(qstring qs, size_t len)
+{
+ if (len < qs->size)
+ {
+ assert(qs->body != NULL) ;
+ return qs->size ;
+ }
+ else
+ return qs_alloc(qs, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set 'len' -- allocate or extend body as required.
+ *
+ * Returns: size of the qstring body
+ * (which includes the extra space allowed for '\0')
+ *
+ * NB: asking for 0 bytes will cause a body to be allocated, ready for any
+ * '\0' !
+ *
+ * NB: has no effect on 'cp' -- even if 'cp' > 'len'.
+ */
+Inline size_t
+qs_set_len(qstring qs, size_t len)
+{
+ qs->len = len ;
+ return qs_need(qs, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reset contents of qstring.
+ *
+ * Sets 'cp' = 'len' = 0. Sets first byte of body (if any) to NULL.
+ */
+Inline void
+qs_set_empty(qstring qs)
+{
+ qs->len = 0 ;
+ qs->cp = 0 ;
+ if (qs->body != NULL)
+ *((char*)qs->body) = '\0' ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get length of qstring -- by doing strlen() -- and record it in qs->len.
+ *
+ * Returns: the string length
+ *
+ * NB: if no body has been allocated, length = 0
+ */
+Inline size_t
+qs_len(qstring qs)
+{
+ return qs->len = (qs->body != NULL) ? strlen(qs_chars(qs)) : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get size of qstring body.
+ *
+ * NB: if no body has been allocated, size = 0
+ */
+Inline size_t
+qs_size(qstring qs)
+{
+ return qs->size ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set '\0' at qs->len -- allocate or extend body as required.
+ *
+ * Returns address of body.
+ */
+Inline void*
+qs_term(qstring qs)
+{
+ size_t len ;
+ if ((len = qs->len) >= qs->size)
+ qs_alloc(qs, len) ;
+
+ *qs_chars_at(qs, len) = '\0' ;
+
+ return qs->body ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert 'n' bytes at 'cp' -- moves anything cp..len up.
+ *
+ * Increases 'len'. but does not affect 'cp'.
+ *
+ * Returns: number of bytes beyond 'cp' that were moved before insert.
+ *
+ * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * NB: the string is NOT re-terminated.
+ */
+Inline size_t
+qs_insert(qstring qs, const void* src, size_t n)
+{
+ size_t after ;
+ char* p ;
+
+ if (qs->len < qs->cp)
+ qs->len = qs->cp ;
+ after = qs->len - qs->cp ;
+
+ qs_set_len(qs, qs->len + n) ; /* set len and ensure have space */
+
+ p = qs_cp_char(qs) ;
+ if (after > 0)
+ memmove (p + n, p, after) ;
+
+ if (n > 0)
+ memmove(p, src, n) ;
+
+ return after ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Replace 'n' bytes at 'cp' -- extending if required.
+ *
+ * May increase 'len'. but does not affect 'cp'.
+ *
+ * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * NB: the string is NOT re-terminated.
+ */
+Inline void
+qs_replace(qstring qs, const void* src, size_t n)
+{
+ if (qs->len < qs->cp + n)
+ qs_set_len(qs, qs->cp + n) ; /* set len and ensure have space */
+
+ if (n > 0)
+ memmove(qs_cp_char(qs), src, n) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Remove 'n' bytes at 'cp' -- extending if required.
+ *
+ * May change 'len'. but does not affect 'cp'.
+ *
+ * Returns: number of bytes beyond 'cp' that were moved before insert.
+ *
+ * NB: if 'cp' > 'len', then sets 'len' = 'cp' first -- which will introduce
+ * one or more undefined bytes.
+ *
+ * NB: the string is NOT re-terminated.
+ */
+Inline size_t
+qs_delete(qstring qs, size_t n)
+{
+ size_t after ;
+ char* p ;
+
+ /* If deleting up to or beyond len, then simply set len == cp */
+ if ((qs->cp + n) >= qs->len)
+ {
+ qs_set_len(qs, qs->cp) ; /* set len, looks after cp > len */
+ return 0 ; /* nothing after */
+ }
+
+ /* There is at least one byte after cp (so body must exist) */
+ after = qs->len - (qs->cp + n) ;
+
+ if (n > 0)
+ {
+ p = qs_cp_char(qs) ;
+ memmove (p, p + n, after) ;
+
+ qs->len -= n ;
+ } ;
+
+ return after ;
+} ;
+
+#endif /* _ZEBRA_QSTRING_H */
diff --git a/lib/qtimers.c b/lib/qtimers.c
index 0aef52a4..508fc7d7 100644
--- a/lib/qtimers.c
+++ b/lib/qtimers.c
@@ -27,6 +27,14 @@
#include "memory.h"
#include "heap.h"
+enum { qdebug =
+#ifdef QDEBUG
+ 1
+#else
+ 0
+#endif
+};
+
/*==============================================================================
* Quagga Timers -- qtimer_xxxx
*
@@ -153,7 +161,8 @@ qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto)
{
qtimer qtr ;
- qtimer_pile_verify(qtp) ; /* TODO: remove after debuggery */
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
qtr = heap_top_item(&qtp->timers) ;
if ((qtr != NULL) && (qtr->time <= upto))
@@ -320,7 +329,8 @@ qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
qtp = qtr->pile ;
dassert(qtp != NULL) ;
- qtimer_pile_verify(qtp) ; /* TODO: remove after debuggery */
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
qtr->time = when ;
@@ -336,9 +346,10 @@ qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
if (action != NULL)
qtr->action = action ;
else
- dassert(qtr->action != NULL) ;
+ assert(qtr->action != NULL) ;
- qtimer_pile_verify(qtp) ; /* TODO: remove after debuggery */
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
} ;
/* Unset given timer
@@ -353,12 +364,13 @@ qtimer_unset(qtimer qtr)
qtimer_pile qtp = qtr->pile ;
dassert(qtp != NULL) ;
- qtimer_pile_verify(qtp) ; /* TODO: remove after debuggery */
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
heap_delete_item(&qtp->timers, qtr) ;
- assert(qtp == qtr->pile);
- qtimer_pile_verify(qtp) ; /* TODO: remove after debuggery */
+ if (qdebug)
+ qtimer_pile_verify(qtp) ;
qtr->state = qtr_state_inactive ; /* overrides any unset pending */
} ;
diff --git a/lib/routemap.c b/lib/routemap.c
index 2dfa5a46..b142e99c 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -28,7 +28,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "command.h"
#include "vty.h"
#include "log.h"
-
+
/* Vector for route match rules. */
static vector route_match_vec;
@@ -60,7 +60,7 @@ struct route_map_list
void (*add_hook) (const char *);
void (*delete_hook) (const char *);
- void (*event_hook) (route_map_event_t, const char *);
+ void (*event_hook) (route_map_event_t, const char *);
};
/* Master list of route map. */
@@ -72,7 +72,7 @@ route_map_rule_delete (struct route_map_rule_list *,
static void
route_map_index_delete (struct route_map_index *, int);
-
+
/* New route map allocation. Please note route map's name must be
specified. */
static struct route_map *
@@ -94,7 +94,7 @@ route_map_add (const char *name)
map = route_map_new (name);
list = &route_map_master;
-
+
map->next = NULL;
map->prev = list->tail;
if (list->tail)
@@ -117,7 +117,7 @@ route_map_delete (struct route_map *map)
struct route_map_list *list;
struct route_map_index *index;
char *name;
-
+
while ((index = map->head) != NULL)
route_map_index_delete (index, 0);
@@ -220,23 +220,23 @@ vty_show_route_map_entry (struct vty *vty, struct route_map *map)
if (index->description)
vty_out (vty, " Description:%s %s%s", VTY_NEWLINE,
index->description, VTY_NEWLINE);
-
+
/* Match clauses */
vty_out (vty, " Match clauses:%s", VTY_NEWLINE);
for (rule = index->match_list.head; rule; rule = rule->next)
- vty_out (vty, " %s %s%s",
+ vty_out (vty, " %s %s%s",
rule->cmd->str, rule->rule_str, VTY_NEWLINE);
-
+
vty_out (vty, " Set clauses:%s", VTY_NEWLINE);
for (rule = index->set_list.head; rule; rule = rule->next)
vty_out (vty, " %s %s%s",
rule->cmd->str, rule->rule_str, VTY_NEWLINE);
-
+
/* Call clause */
vty_out (vty, " Call clause:%s", VTY_NEWLINE);
if (index->nextrm)
vty_out (vty, " Call %s%s", index->nextrm, VTY_NEWLINE);
-
+
/* Exit Policy */
vty_out (vty, " Action:%s", VTY_NEWLINE);
if (index->exitpolicy == RMAP_GOTO)
@@ -353,7 +353,7 @@ route_map_index_add (struct route_map *map, enum route_map_type type,
index->map = map;
index->type = type;
index->pref = pref;
-
+
/* Compare preference. */
for (point = map->head; point; point = point->next)
if (point->pref >= pref)
@@ -394,7 +394,7 @@ route_map_index_add (struct route_map *map, enum route_map_type type,
/* Get route map index. */
static struct route_map_index *
-route_map_index_get (struct route_map *map, enum route_map_type type,
+route_map_index_get (struct route_map *map, enum route_map_type type,
int pref)
{
struct route_map_index *index;
@@ -420,7 +420,7 @@ route_map_rule_new (void)
new = XCALLOC (MTYPE_ROUTE_MAP_RULE, sizeof (struct route_map_rule));
return new;
}
-
+
/* Install rule command to the match list. */
void
route_map_install_match (struct route_map_rule_cmd *cmd)
@@ -552,7 +552,7 @@ route_map_add_match (struct route_map_index *index, const char *match_name,
{
next = rule->next;
if (rule->cmd == cmd)
- {
+ {
route_map_rule_delete (&index->match_list, rule);
replaced = 1;
}
@@ -591,9 +591,9 @@ route_map_delete_match (struct route_map_index *index, const char *match_name,
cmd = route_map_lookup_match (match_name);
if (cmd == NULL)
return 1;
-
+
for (rule = index->match_list.head; rule; rule = rule->next)
- if (rule->cmd == cmd &&
+ if (rule->cmd == cmd &&
(rulecmp (rule->rule_str, match_arg) == 0 || match_arg == NULL))
{
route_map_rule_delete (&index->match_list, rule);
@@ -677,7 +677,7 @@ route_map_delete_set (struct route_map_index *index, const char *set_name,
cmd = route_map_lookup_set (set_name);
if (cmd == NULL)
return 1;
-
+
for (rule = index->set_list.head; rule; rule = rule->next)
if ((rule->cmd == cmd) &&
(rulecmp (rule->rule_str, set_arg) == 0 || set_arg == NULL))
@@ -698,7 +698,7 @@ route_map_delete_set (struct route_map_index *index, const char *set_name,
The matrix for a route-map looks like this:
(note, this includes the description for the "NEXT"
and "GOTO" frobs now
-
+
Match | No Match
|
permit action | cont
@@ -707,7 +707,7 @@ route_map_delete_set (struct route_map_index *index, const char *set_name,
|
deny deny | cont
|
-
+
action)
-Apply Set statements, accept route
-If Call statement is present jump to the specified route-map, if it
@@ -719,10 +719,10 @@ route_map_delete_set (struct route_map_index *index, const char *set_name,
-Route is denied by route-map.
cont)
-Goto Next index
-
+
If we get no matches after we've processed all updates, then the route
is dropped too.
-
+
Some notes on the new "CALL", "NEXT" and "GOTO"
call WORD - If this clause is matched, then the set statements
are executed and then we jump to route-map 'WORD'. If
@@ -735,7 +735,7 @@ route_map_delete_set (struct route_map_index *index, const char *set_name,
first clause greater than this. In order to ensure
route-maps *always* exit, you cannot jump backwards.
Sorry ;)
-
+
We need to make sure our route-map processing matches the above
*/
@@ -757,7 +757,7 @@ route_map_apply_match (struct route_map_rule_list *match_list,
for (match = match_list->head; match; match = match->next)
{
/* Try each match statement in turn, If any do not return
- RMAP_MATCH, return, otherwise continue on to next match
+ RMAP_MATCH, return, otherwise continue on to next match
statement. All match statements must match for end-result
to be a match. */
ret = (*match->cmd->func_apply) (match->value, prefix,
@@ -827,7 +827,7 @@ route_map_apply (struct route_map *map, struct prefix *prefix,
if (ret == RMAP_DENYMATCH)
return ret;
}
-
+
switch (index->exitpolicy)
{
case RMAP_EXIT:
@@ -898,7 +898,7 @@ route_map_finish (void)
vector_free (route_set_vec);
route_set_vec = NULL;
}
-
+
/* VTY related functions. */
DEFUN (route_map,
route_map_cmd,
@@ -945,7 +945,7 @@ DEFUN (route_map,
index = route_map_index_get (map, permit, pref);
vty->index = index;
- vty->node = RMAP_NODE;
+ vty_set_node(vty, RMAP_NODE) ;
return CMD_SUCCESS;
}
@@ -1025,7 +1025,7 @@ DEFUN (no_route_map,
index = route_map_index_lookup (map, permit, pref);
if (index == NULL)
{
- vty_out (vty, "%% Could not find route-map entry %s %s%s",
+ vty_out (vty, "%% Could not find route-map entry %s %s%s",
argv[0], argv[2], VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1066,7 +1066,7 @@ DEFUN (no_rmap_onmatch_next,
struct route_map_index *index;
index = vty->index;
-
+
if (index)
index->exitpolicy = RMAP_EXIT;
@@ -1089,11 +1089,11 @@ DEFUN (rmap_onmatch_goto,
VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65536);
else
d = index->pref + 1;
-
+
if (d <= index->pref)
{
/* Can't allow you to do that, Dave */
- vty_out (vty, "can't jump backwards in route-maps%s",
+ vty_out (vty, "can't jump backwards in route-maps%s",
VTY_NEWLINE);
return CMD_WARNING;
}
@@ -1119,7 +1119,7 @@ DEFUN (no_rmap_onmatch_goto,
if (index)
index->exitpolicy = RMAP_EXIT;
-
+
return CMD_SUCCESS;
}
@@ -1259,7 +1259,7 @@ route_map_config_write (struct vty *vty)
else
first = 0;
- vty_out (vty, "route-map %s %s %d%s",
+ vty_out (vty, "route-map %s %s %d%s",
map->name,
route_map_type_str (index->type),
index->pref, VTY_NEWLINE);
@@ -1268,7 +1268,7 @@ route_map_config_write (struct vty *vty)
vty_out (vty, " description %s%s", index->description, VTY_NEWLINE);
for (rule = index->match_list.head; rule; rule = rule->next)
- vty_out (vty, " match %s %s%s", rule->cmd->str,
+ vty_out (vty, " match %s %s%s", rule->cmd->str,
rule->rule_str ? rule->rule_str : "",
VTY_NEWLINE);
@@ -1282,7 +1282,7 @@ route_map_config_write (struct vty *vty)
vty_out (vty, " on-match goto %d%s", index->nextpref, VTY_NEWLINE);
if (index->exitpolicy == RMAP_NEXT)
vty_out (vty," on-match next%s", VTY_NEWLINE);
-
+
write++;
}
return write;
@@ -1315,12 +1315,12 @@ route_map_init_vty (void)
install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd);
install_element (RMAP_NODE, &rmap_onmatch_goto_cmd);
install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd);
-
+
/* Install the continue stuff (ALIAS of on-match). */
install_element (RMAP_NODE, &rmap_continue_cmd);
install_element (RMAP_NODE, &no_rmap_continue_cmd);
install_element (RMAP_NODE, &rmap_continue_index_cmd);
-
+
/* Install the call stuff. */
install_element (RMAP_NODE, &rmap_call_cmd);
install_element (RMAP_NODE, &no_rmap_call_cmd);
@@ -1328,7 +1328,7 @@ route_map_init_vty (void)
/* Install description commands. */
install_element (RMAP_NODE, &rmap_description_cmd);
install_element (RMAP_NODE, &no_rmap_description_cmd);
-
+
/* Install show command */
install_element (ENABLE_NODE, &rmap_show_name_cmd);
}
diff --git a/lib/sockunion.c b/lib/sockunion.c
index 479adc3e..4043783c 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -117,50 +117,164 @@ inet_ntop (int family, const void *addrptr, char *strptr, size_t len)
}
#endif /* ! HAVE_INET_NTOP */
-const char *
-inet_sutop (union sockunion *su, char *str)
+/*------------------------------------------------------------------------------
+ * Set the sockunion size (sin_len or sin6_len), if required.
+ *
+ * NB: POSIX does not require this and Stevens et al say that even where it
+ * is supported, the application need not worry about it.
+ *
+ * However... the code as found does this.
+ *
+ * TODO: is it *really* necessary to set sin_len or sin6_len ??
+ *
+ * Returns: the sockunion size
+ */
+inline static int
+sockunion_sin_len(sockunion su)
+{
+ return
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ su->sin.sin_len =
+#endif
+ sizeof(struct sockaddr_in);
+} ;
+
+#if HAVE_IPV6
+inline static int
+sockunion_sin6_len(sockunion su)
+{
+ return
+#ifdef SIN6_LEN
+ su->sin6.sin6_len =
+#endif
+ sizeof(struct sockaddr_in6);
+} ;
+#endif
+
+/*------------------------------------------------------------------------------
+ * Set the address family for the given sockunion.
+ *
+ * If sin_len or sin6_len entry is present, fill that in too.
+ *
+ * Assumes the address family is valid !
+ *
+ * Returns: 0
+ */
+inline static int
+sockunion_set_family(sockunion su, sa_family_t family)
+{
+ su->sa.sa_family = family ;
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ if (family == AF_INET)
+ su->sin.sin_len = sizeof(struct sockaddr_in);
+#endif
+#if defined(HAVE_IPV6) && defined(SIN6_LEN)
+ if (family == AF_INET6)
+ su->sin6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the given sockunion address to "any"
+ */
+static void
+sockunion_set_addr_any(sockunion su)
{
switch (su->sa.sa_family)
- {
+ {
case AF_INET:
- inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN);
- break;
+ su->sin.sin_addr.s_addr = htonl (INADDR_ANY);
+ return ;
+
#ifdef HAVE_IPV6
case AF_INET6:
- inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN);
- break;
-#endif /* HAVE_IPV6 */
- }
- return str;
-}
+# if defined(LINUX_IPV6) || defined(NRL)
+ memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr));
+# else
+ su->sin6.sin6_addr = in6addr_any;
+# endif /* LINUX_IPV6 || defined(NRL) */
+ return ;
+#endif
+
+ default:
+ return ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set the port number in the given sockunion.
+ *
+ * For good measure, set the size (if that's required) and return same.
+ */
+static int
+sockunion_set_port(sockunion su, in_port_t port)
+{
+ switch (su->sa.sa_family)
+ {
+ case AF_INET:
+ su->sin.sin_port = htons(port) ;
+ return sockunion_sin_len(su) ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ su->sin6.sin6_port = htons(port) ;
+ return sockunion_sin6_len(su) ;
+#endif
+
+ default:
+ return 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialise a new sockunion -- for the given address family (if any)
+ *
+ * Allocates a sockunion if required.
+ *
+ * Advice is to zeroize sockaddr_in6, in particular.
+ */
+extern sockunion
+sockunion_init_new(sockunion su, sa_family_t family)
+{
+ if (su == NULL)
+ su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion)) ;
+ else
+ memset(su, 0, sizeof(union sockunion)) ;
+
+ if (family != 0)
+ sockunion_set_family(su, family) ;
+ return su ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * From the given string, fill in the given sockunion.
+ *
+ * Returns: 0 => OK -- sockunion filled in
+ * -1 => not a valid address (or not a known address family)
+ */
int
str2sockunion (const char *str, union sockunion *su)
{
int ret;
- memset (su, 0, sizeof (union sockunion));
+ assert(su != NULL) ;
+
+ sockunion_init_new(su, 0) ;
ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
if (ret > 0) /* Valid IPv4 address format. */
- {
- su->sin.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- su->sin.sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- return 0;
- }
+ return sockunion_set_family(su, AF_INET) ;
+
#ifdef HAVE_IPV6
ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
if (ret > 0) /* Valid IPv6 address format. */
- {
- su->sin6.sin6_family = AF_INET6;
-#ifdef SIN6_LEN
- su->sin6.sin6_len = sizeof(struct sockaddr_in6);
-#endif /* SIN6_LEN */
- return 0;
- }
+ return sockunion_set_family(su, AF_INET6) ;
#endif /* HAVE_IPV6 */
+
return -1;
}
@@ -170,72 +284,65 @@ str2sockunion (const char *str, union sockunion *su)
* Requires buffer of at least SU_ADDRSTRLEN characters.
*/
const char *
-sockunion2str (union sockunion *su, char *buf, size_t len)
+sockunion2str (union sockunion *su, char *buf, size_t size)
{
- assert(len >= SU_ADDRSTRLEN) ;
+ assert(size >= SU_ADDRSTRLEN) ;
- if (su->sa.sa_family == AF_INET)
- return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len);
+ switch (su->sa.sa_family)
+ {
+ case AF_INET:
+ inet_ntop (AF_INET, &su->sin.sin_addr, buf, size);
+ break;
#ifdef HAVE_IPV6
- else if (su->sa.sa_family == AF_INET6)
- return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len);
+ case AF_INET6:
+ inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, size);
+ break;
#endif /* HAVE_IPV6 */
- return NULL;
+ default:
+ snprintf (buf, size, "?af=%d?", (int)su->sa.sa_family) ;
+ } ;
+
+ return buf;
}
+/*------------------------------------------------------------------------------
+ * From the given string, construct and fill in a sockunion.
+ *
+ * Returns: NULL => not a valid address (or not a known address family)
+ * otherwise is address of new sockunion.
+ *
+ * NB: the caller is responsible for freeing the sockunion created.
+ */
union sockunion *
sockunion_str2su (const char *str)
{
- int ret;
union sockunion *su;
- su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
+ su = XMALLOC (MTYPE_SOCKUNION, sizeof(union sockunion));
- ret = inet_pton (AF_INET, str, &su->sin.sin_addr);
- if (ret > 0) /* Valid IPv4 address format. */
- {
- su->sin.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- su->sin.sin_len = sizeof(struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- return su;
- }
-#ifdef HAVE_IPV6
- ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr);
- if (ret > 0) /* Valid IPv6 address format. */
- {
- su->sin6.sin6_family = AF_INET6;
-#ifdef SIN6_LEN
- su->sin6.sin6_len = sizeof(struct sockaddr_in6);
-#endif /* SIN6_LEN */
- return su;
- }
-#endif /* HAVE_IPV6 */
+ if (str2sockunion (str, su) != 0)
+ XFREE (MTYPE_SOCKUNION, su); /* sets su = NULL */
- XFREE (MTYPE_SOCKUNION, su);
- return NULL;
+ return su ;
}
-char *
-sockunion_su2str (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Convert given sockunion to string, and return a new piece of memory
+ * containing same.
+ *
+ * It is the callers responsibility to free the memory in due course.
+ */
+extern char *
+sockunion_su2str (union sockunion *su, enum MTYPE type)
{
- char str[SU_ADDRSTRLEN];
+ char buf[SU_ADDRSTRLEN];
- switch (su->sa.sa_family)
- {
- case AF_INET:
- inet_ntop (AF_INET, &su->sin.sin_addr, str, sizeof (str));
- break;
-#ifdef HAVE_IPV6
- case AF_INET6:
- inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, sizeof (str));
- break;
-#endif /* HAVE_IPV6 */
- }
- return XSTRDUP (MTYPE_TMP, str);
+ return XSTRDUP (type, sockunion2str(su, buf, sizeof(buf))) ;
}
-/* Convert IPv4 compatible IPv6 address to IPv4 address. */
+/*------------------------------------------------------------------------------
+ * Convert IPv4 compatible IPv6 address to IPv4 address.
+ */
static void
sockunion_normalise_mapped (union sockunion *su)
{
@@ -277,7 +384,7 @@ sockunion_accept (int sock, union sockunion *su)
len = sizeof(union sockunion);
memset(su, 0, len) ;
- ret = accept(sock, (struct sockaddr *)su, &len) ;
+ ret = accept(sock, &su->sa, &len) ;
if (ret >= 0)
{
@@ -292,188 +399,175 @@ sockunion_accept (int sock, union sockunion *su)
|| (ret == EINTR) ) ? -2 : -1 ;
} ;
-/* Return sizeof union sockunion. */
-static int
-sockunion_sizeof (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * Make socket for given family, type and protocol
+ *
+ * Returns: -1 : failed -- see errno
+ * otherwise : socket
+ *
+ * Logs a LOG_WARNING message if fails.
+ */
+extern int
+sockunion_socket(sa_family_t family, int type, int protocol)
{
- int ret;
+ int sockfd ;
- ret = 0;
- switch (su->sa.sa_family)
+ sockfd = socket(family, type, protocol);
+ if (sockfd < 0)
{
- case AF_INET:
- ret = sizeof (struct sockaddr_in);
- break;
-#ifdef HAVE_IPV6
- case AF_INET6:
- ret = sizeof (struct sockaddr_in6);
- break;
-#endif /* AF_INET6 */
+ zlog (NULL, LOG_WARNING,
+ "Can't make socket family=%d, type=%d, protocol=%d : %s",
+ (int)family, type, protocol, safe_strerror(errno)) ;
+ return -1;
}
- return ret;
-}
-
-/* return sockunion structure : this function should be revised. */
-static char *
-sockunion_log (union sockunion *su)
-{
- static char buf[SU_ADDRSTRLEN];
- switch (su->sa.sa_family)
- {
- case AF_INET:
- snprintf (buf, SU_ADDRSTRLEN, "%s", safe_inet_ntoa (su->sin.sin_addr));
- break;
-#ifdef HAVE_IPV6
- case AF_INET6:
- snprintf (buf, SU_ADDRSTRLEN, "%s",
- inet_ntop (AF_INET6, &(su->sin6.sin6_addr), buf, SU_ADDRSTRLEN));
- break;
-#endif /* HAVE_IPV6 */
- default:
- snprintf (buf, SU_ADDRSTRLEN, "af_unknown %d ", su->sa.sa_family);
- break;
- }
- return (XSTRDUP (MTYPE_TMP, buf));
+ return sockfd ;
}
-/*==============================================================================
- * Return socket of sockunion. (only used in bgpd)
+/*------------------------------------------------------------------------------
+ * Make socket for family from given sockunion, type=SOCK_STREAM, protocol=0.
*
* Returns: -1 : failed -- see errno
* otherwise : socket
+ *
+ * Logs a LOG_WARNING message if fails.
*/
int
-sockunion_socket (union sockunion *su)
+sockunion_stream_socket (union sockunion *su)
{
- int sockfd ;
-
- sockfd = socket(su->sa.sa_family, SOCK_STREAM, 0);
- if (sockfd < 0)
- {
- zlog (NULL, LOG_WARNING, "Can't make socket : %s", safe_strerror(errno)) ;
- return -1;
- }
+ if (su->sa.sa_family == 0)
+ su->sa.sa_family = AF_INET_UNION;
- return sockfd ;
+ return sockunion_socket (su->sa.sa_family, SOCK_STREAM, 0);
}
-/*==============================================================================
- * Initiate a connection (only used in bgpd)
+/*------------------------------------------------------------------------------
+ * Initiate a connection
*
* Reports EINPROGRESS as success.
*
+ * TODO: discover how the ifindex thing is supposed to work !!
+ *
* Returns: 0 : OK (so far so good)
- * != 0 : error number (from errno or otherwise)
+ * < 0 : failed -- see errno
+ *
+ * Logs a LOG_INFO message if fails.
*/
extern int
sockunion_connect(int fd, union sockunion* peer_su, unsigned short port,
unsigned int ifindex)
{
+ char buf[SU_ADDRSTRLEN] ;
union sockunion su ;
int ret ;
+ int sa_len ;
memcpy(&su, peer_su, sizeof(union sockunion)) ;
- switch (su.sa.sa_family)
- {
- case AF_INET:
- su.sin.sin_port = htons(port) ;
- break;
+ sa_len = sockunion_set_port(&su, port) ;
+
#ifdef HAVE_IPV6
- case AF_INET6:
- su.sin6.sin6_port = htons(port) ;
-#ifdef KAME
+# ifdef KAME
+ if (su.sa.sa_family == AF_INET6)
+ {
if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
{
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
+# ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
/* su.sin6.sin6_scope_id = ifindex; */
-#ifdef MUSICA
+# ifdef MUSICA
su.sin6.sin6_scope_id = ifindex;
-#endif
-#endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
-#ifndef MUSICA
+# endif
+# endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
+# ifndef MUSICA
SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
-#endif
+# endif
}
-#endif /* KAME */
- break;
+ } ;
+# endif /* KAME */
#endif /* HAVE_IPV6 */
- }
- ret = connect(fd, (struct sockaddr *)&su, sockunion_sizeof(&su)) ;
+ ret = connect(fd, &su.sa, sa_len) ;
- if ((ret == 0) || ((ret = errno) == EINPROGRESS))
+ if ((ret == 0) || (errno == EINPROGRESS))
return 0 ; /* instant success or EINPROGRESS as expected */
- zlog_info("can't connect to %s fd %d : %s",
- sockunion_log (&su), fd, safe_strerror(ret)) ;
+ zlog_info("can't connect to %s port %d fd %d : %s",
+ sockunion2str(&su, buf, sizeof(buf)), port, fd, safe_strerror(errno)) ;
return ret ;
} ;
-/* Make socket from sockunion union. */
-int
-sockunion_stream_socket (union sockunion *su)
+/*------------------------------------------------------------------------------
+ * 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)
{
- int sock;
-
- if (su->sa.sa_family == 0)
- su->sa.sa_family = AF_INET_UNION;
+ int ret ;
- sock = socket (su->sa.sa_family, SOCK_STREAM, 0);
+ ret = listen(fd, backlog) ;
- if (sock < 0)
- zlog (NULL, LOG_WARNING, "can't make socket sockunion_stream_socket");
+ if (ret == 0)
+ return 0 ;
- return sock;
-}
+ zlog (NULL, LOG_WARNING, "can't listen on fd %d : %s",
+ fd, safe_strerror(errno)) ;
+ return ret ;
+} ;
-/* Bind socket to specified address. */
+/*------------------------------------------------------------------------------
+ * Bind socket to address/port.
+ *
+ * Sets the given port into the sockunion su.
+ *
+ * If the 'any' parameter is NULL, set the address part of sockunion to
+ * INADDR_ANY or the family equivalent. Note that for IPv6 this does not
+ * affect the flow/scope in the su.
+ *
+ * For good measure, sets sin_len or family equivalent if required.
+ *
+ * Performs bind() and logs a LOG_WARNING message if fails.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ */
int
-sockunion_bind (int sock, union sockunion *su, unsigned short port,
- union sockunion *su_addr)
+sockunion_bind (int sock, union sockunion *su, unsigned short port, void* any)
{
- int size = 0;
+ int sa_len ;
int ret;
+ char buf[SU_ADDRSTRLEN] ;
- if (su->sa.sa_family == AF_INET)
- {
- size = sizeof (struct sockaddr_in);
- su->sin.sin_port = htons (port);
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- su->sin.sin_len = size;
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
- if (su_addr == NULL)
- su->sin.sin_addr.s_addr = htonl (INADDR_ANY);
- }
-#ifdef HAVE_IPV6
- else if (su->sa.sa_family == AF_INET6)
- {
- size = sizeof (struct sockaddr_in6);
- su->sin6.sin6_port = htons (port);
-#ifdef SIN6_LEN
- su->sin6.sin6_len = size;
-#endif /* SIN6_LEN */
- if (su_addr == NULL)
- {
-#if defined(LINUX_IPV6) || defined(NRL)
- memset (&su->sin6.sin6_addr, 0, sizeof (struct in6_addr));
-#else
- su->sin6.sin6_addr = in6addr_any;
-#endif /* LINUX_IPV6 */
- }
- }
-#endif /* HAVE_IPV6 */
+ if (any == NULL)
+ sockunion_set_addr_any(su) ;
+ sa_len = sockunion_set_port(su, port) ;
- ret = bind (sock, (struct sockaddr *)su, size);
+ ret = bind (sock, &su->sa, sa_len);
if (ret < 0)
- zlog (NULL, LOG_WARNING, "can't bind socket : %s", safe_strerror (errno));
+ zlog (NULL, LOG_WARNING, "can't bind to %s port %d fd %d : %s",
+ sockunion2str(su, buf, sizeof(buf)), port, sock, safe_strerror(ret)) ;
return ret;
}
+/*------------------------------------------------------------------------------
+ * Set socket SO_REUSEADDR option
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_WARNING message if fails.
+ */
int
sockopt_reuseaddr (int sock)
{
@@ -484,16 +578,25 @@ sockopt_reuseaddr (int sock)
(void *) &on, sizeof (on));
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock);
+ zlog (NULL, LOG_WARNING,
+ "can't set sockopt SO_REUSEADDR to socket %d", sock);
return -1;
}
return 0;
}
-#ifdef SO_REUSEPORT
+/*------------------------------------------------------------------------------
+ * Set socket SO_REUSEPORT option -- if it is locally supported.
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed -- see errno
+ *
+ * Logs a LOG_WARNING message if fails.
+ */
int
sockopt_reuseport (int sock)
{
+#ifdef SO_REUSEPORT
int ret;
int on = 1;
@@ -501,20 +604,18 @@ sockopt_reuseport (int sock)
(void *) &on, sizeof (on));
if (ret < 0)
{
- zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEPORT to socket %d", sock);
+ zlog (NULL, LOG_WARNING,
+ "can't set sockopt SO_REUSEPORT to socket %d", sock);
return -1;
}
+#endif
+
return 0;
-}
-#else
-int
-sockopt_reuseport (int sock)
-{
- return 0;
-}
-#endif /* 0 */
+} ;
-/* If same family and same prefix return 1. */
+/*------------------------------------------------------------------------------
+ * If same family and same prefix return 1.
+ */
int
sockunion_same (union sockunion *su1, union sockunion *su2)
{
@@ -526,9 +627,8 @@ sockunion_same (union sockunion *su1, union sockunion *su2)
switch (su1->sa.sa_family)
{
case AF_INET:
- ret = memcmp (&su1->sin.sin_addr, &su2->sin.sin_addr,
- sizeof (struct in_addr));
- break;
+ return (su1->sin.sin_addr.s_addr == su2->sin.sin_addr.s_addr) ;
+
#ifdef HAVE_IPV6
case AF_INET6:
ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
@@ -631,8 +731,9 @@ sockunion_getpeername (int fd, union sockunion* su_remote)
return sockunion_get_name(fd, su_remote, 0) ;
} ;
-
-/* Print sockunion structure */
+/*------------------------------------------------------------------------------
+ * Print sockunion structure to stdout
+ */
static void __attribute__ ((unused))
sockunion_print (union sockunion *su)
{
@@ -671,27 +772,9 @@ sockunion_print (union sockunion *su)
}
}
-#ifdef HAVE_IPV6
-static int
-in6addr_cmp (struct in6_addr *addr1, struct in6_addr *addr2)
-{
- unsigned int i;
- u_char *p1, *p2;
-
- p1 = (u_char *)addr1;
- p2 = (u_char *)addr2;
-
- for (i = 0; i < sizeof (struct in6_addr); i++)
- {
- if (p1[i] > p2[i])
- return 1;
- else if (p1[i] < p2[i])
- return -1;
- }
- return 0;
-}
-#endif /* HAVE_IPV6 */
-
+/*------------------------------------------------------------------------------
+ * Compare two sockunion values
+ */
int
sockunion_cmp (union sockunion *su1, union sockunion *su2)
{
@@ -700,23 +783,33 @@ sockunion_cmp (union sockunion *su1, union sockunion *su2)
if (su1->sa.sa_family < su2->sa.sa_family)
return -1;
- if (su1->sa.sa_family == AF_INET)
- {
- if (ntohl (su1->sin.sin_addr.s_addr) == ntohl (su2->sin.sin_addr.s_addr))
- return 0;
- if (ntohl (su1->sin.sin_addr.s_addr) > ntohl (su2->sin.sin_addr.s_addr))
- return 1;
+ switch (su1->sa.sa_family)
+ {
+ case AF_INET:
+ if (su1->sin.sin_addr.s_addr == su2->sin.sin_addr.s_addr)
+ return 0;
+ if (ntohl(su1->sin.sin_addr.s_addr) > ntohl(su2->sin.sin_addr.s_addr))
+ return +1;
else
return -1;
- }
+
#ifdef HAVE_IPV6
- if (su1->sa.sa_family == AF_INET6)
- return in6addr_cmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr);
+ case AF_INET6:
+ return memcmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
+ sizeof(struct in6_addr)) ;
#endif /* HAVE_IPV6 */
- return 0;
+
+ default:
+ return 0 ;
+ } ;
}
-/* Duplicate sockunion. */
+/*------------------------------------------------------------------------------
+ * Create copy of existing sockunion.
+ *
+ * It is the caller's responsibility to free the sockunion at some point --see
+ * sockunion_free()
+ */
union sockunion *
sockunion_dup (union sockunion *su)
{
@@ -725,37 +818,47 @@ sockunion_dup (union sockunion *su)
return dup;
}
+/*------------------------------------------------------------------------------
+ * Free given sockunion (if any).
+ */
void
sockunion_free (union sockunion *su)
{
- XFREE (MTYPE_SOCKUNION, su);
+ if (su != NULL)
+ XFREE (MTYPE_SOCKUNION, su);
}
/*==============================================================================
* Sockunion reference utilities
*/
+/*------------------------------------------------------------------------------
+ * Set sockunion from given prefix -- allocate new sockunion, if required.
+ *
+ * It is the caller's responsibility to free the sockunion at some point.
+ * (See sockunion_free() or sockunion_unset().)
+ */
extern sockunion
-sockunion_new(struct prefix* p)
+sockunion_new_prefix(sockunion su, struct prefix* p)
{
- sockunion nsu = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)) ;
+ sa_family_t family ;
- if (p == NULL)
- return NULL ;
+ family = (p != NULL) ? p->family : 0 ;
- switch (p->family)
+ su = sockunion_init_new(su, family) ;
+
+ switch (family)
{
+ case 0:
+ break ;
+
case AF_INET:
- nsu->sin.sin_family = AF_INET ;
- nsu->sin.sin_port = 0 ;
- nsu->sin.sin_addr = p->u.prefix4 ;
+ su->sin.sin_addr = p->u.prefix4 ;
break ;
#ifdef HAVE_IPV6
case AF_INET6:
- nsu->sin6.sin6_family = AF_INET ;
- nsu->sin6.sin6_port = 0 ;
- nsu->sin6.sin6_addr = p->u.prefix6 ;
+ su->sin6.sin6_addr = p->u.prefix6 ;
break ;
#endif
@@ -763,7 +866,44 @@ sockunion_new(struct prefix* p)
break ;
} ;
- return nsu ;
+ return su ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new sockunion from given sockaddr.
+ *
+ * It is the caller's responsibility to free the sockunion at some point.
+ * (See sockunion_free() or sockunion_unset().)
+ */
+extern sockunion
+sockunion_new_sockaddr(sockunion su, struct sockaddr* sa)
+{
+ sa_family_t family ;
+
+ family = (sa != NULL) ? sa->sa_family : 0 ;
+
+ su = sockunion_init_new(su, family) ;
+
+ switch (family)
+ {
+ case 0:
+ break ;
+
+ case AF_INET:
+ su->sin = *(struct sockaddr_in*)sa ;
+ break ;
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ su->sin6 = *(struct sockaddr_in6*)sa ;
+ break ;
+#endif
+
+ default:
+ break ;
+ } ;
+
+ return su ;
} ;
/*------------------------------------------------------------------------------
@@ -825,7 +965,6 @@ sockunion_set_mov(sockunion* p_dst, sockunion* p_src)
/*==============================================================================
* Symbol Table Hash function -- for symbols whose name is an address.
*/
-
extern void
sockunion_symbol_hash(symbol_hash p_hash, const void* name)
{
diff --git a/lib/sockunion.h b/lib/sockunion.h
index ea76a955..d1f29b13 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -26,6 +26,7 @@
#include "zebra.h"
#include "symtab.h"
#include "prefix.h"
+#include "memory.h"
#if 0
union sockunion {
@@ -99,12 +100,13 @@ CONFIRM(SU_ADDRSTRLEN >= INET6_ADDRSTRLEN) ;
#define sockunion_family(X) (X)->sa.sa_family
/* Prototypes. */
+extern sockunion sockunion_init_new(sockunion su, sa_family_t family) ;
extern int str2sockunion (const char *, union sockunion *);
extern const char *sockunion2str (union sockunion *, char *, size_t);
extern int sockunion_cmp (union sockunion *, union sockunion *);
extern int sockunion_same (union sockunion *, union sockunion *);
-extern char *sockunion_su2str (union sockunion *su);
+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 *);
@@ -112,18 +114,19 @@ 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 *,
- unsigned short, union sockunion *);
+ unsigned short, void* any);
extern int sockopt_ttl (int family, int sock, int ttl);
-extern int sockunion_socket (union sockunion *su);
-extern const char *inet_sutop (union sockunion *su, char *str);
+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_getsockname (int, union sockunion*);
extern int sockunion_getpeername (int, union sockunion*);
extern union sockunion *sockunion_dup (union sockunion *);
extern void sockunion_free (union sockunion *);
-extern sockunion sockunion_new(prefix p) ;
+extern sockunion sockunion_new_prefix(sockunion su, prefix p) ;
+extern sockunion sockunion_new_sockaddr(sockunion su, struct sockaddr* sa) ;
extern void sockunion_unset(sockunion* p_su) ;
extern void sockunion_set(sockunion* p_dst, sockunion su) ;
extern void sockunion_set_dup(sockunion* p_dst, sockunion su) ;
diff --git a/lib/uty.h b/lib/uty.h
new file mode 100644
index 00000000..80a7cae3
--- /dev/null
+++ b/lib/uty.h
@@ -0,0 +1,205 @@
+/* VTY internal stuff -- header
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_UTY_H
+#define _ZEBRA_UTY_H
+
+#include <stdbool.h>
+
+#include "qpthreads.h"
+#include "qpnexus.h"
+#include "thread.h"
+#include "list_util.h"
+#include "vty.h"
+#include "node_type.h"
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/*==============================================================================
+ * This is stuff which is used by the close family of:
+ *
+ * vty
+ * command
+ * 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.)
+ */
+
+/*==============================================================================
+ * To make vty qpthread safe we use a single mutex.
+ *
+ * vty and log recurse through each other, so the same mutex is used
+ * for both, i.e. they are treated as being part of the same monitor.
+ *
+ * A recursive mutex is used. This simplifies the calling from log to vty and
+ * back again. It also allows for the vty internals to call each other.
+ *
+ * There are some "uty" functions which assume the mutex is locked.
+ *
+ * vty is closely bound to the command handling -- the main vty structure
+ * contains the context in which commands are parsed and executed.
+ */
+
+extern qpt_mutex_t vty_mutex ;
+
+#ifdef NDEBUG
+# define VTY_DEBUG 0 /* NDEBUG override */
+#else
+# ifndef VTY_DEBUG
+# define VTY_DEBUG 1 /* Set to 1 to turn on debug checks */
+# endif
+#endif
+
+#if VTY_DEBUG
+
+extern int vty_lock_count ;
+extern int vty_lock_assert_fail ;
+
+#endif
+
+Inline void
+VTY_LOCK(void)
+{
+ qpt_mutex_lock(&vty_mutex) ;
+ if (VTY_DEBUG)
+ ++vty_lock_count ;
+} ;
+
+Inline void
+VTY_UNLOCK(void)
+{
+ if (VTY_DEBUG)
+ --vty_lock_count ;
+ qpt_mutex_lock(&vty_mutex) ;
+} ;
+
+#if VTY_DEBUG
+
+Inline void
+VTY_ASSERT_LOCKED(void)
+{
+ if (vty_lock_count == 0 && !vty_lock_assert_fail)
+ {
+ vty_lock_assert_fail = 1;
+ assert(0);
+ }
+} ;
+
+#else
+
+#define VTY_ASSERT_LOCKED()
+
+#endif
+
+/*==============================================================================
+ * Shared definitions
+ */
+
+enum cli_do
+{
+ cli_do_nothing = 0, /* no action required */
+
+ cli_do_command, /* dispatch the current command line */
+ cli_do_ctrl_c, /* received ^c */
+ cli_do_ctrl_d, /* received ^d on empty line */
+ cli_do_ctrl_z, /* received ^z */
+
+ cli_do_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 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
+ */
+extern void
+vty_queued_result(struct vty* vty, int ret, int action);
+
+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.
+ */
+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);
+
+#endif /* _ZEBRA_UTY_H */
diff --git a/lib/vector.c b/lib/vector.c
index b1ec160d..3fb4cbd9 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -124,20 +124,6 @@ vector_init (unsigned int size)
return vector_init_new(NULL, size ? size : 1) ; /* at least 1 entry */
} ;
-/* Basic: free the given vector structure. NB: Orphans any existing body !! */
-void
-vector_only_wrapper_free (vector v)
-{
- XFREE (MTYPE_VECTOR, v);
-}
-
-/* Basic: free the vector body. */
-void
-vector_only_index_free (void *body)
-{
- XFREE (MTYPE_VECTOR_BODY, body);
-}
-
/* Basic: free the vector body and the vector structure. */
void
vector_free (vector v)
@@ -201,6 +187,38 @@ vector_reset(vector v, int free_structure)
return vector_init_new(v, 0) ;
} ;
+/* Set vector length to be (at least) the given fixed length.
+ *
+ * There must be a vector.
+ *
+ * Does nothing if the vector is already as long or longer than the given
+ * length.
+ *
+ * If the body is not big enough for the new length, allocates or extends to
+ * exactly the new length. Otherwise, leaves body as it is.
+ *
+ * Appends NULLs as required to extend to the required length.
+ *
+ * Note that the existing contents of the vector are preserved in all cases.
+ */
+extern void
+vector_set_new_min_length(vector v, unsigned int len)
+{
+ assert (v != NULL) ;
+
+ if (len > v->limit)
+ {
+ if (v->p_items == NULL)
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(len)) ;
+ else
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(len));
+ v->limit = len ;
+ } ;
+
+ if (v->end < len)
+ vector_extend(v, len) ;
+} ;
+
/* Pop item from vector, stepping past any NULLs.
*
* If vector is empty, free the body and, if required, the vector structure.
diff --git a/lib/vector.h b/lib/vector.h
index 08abdcf7..727d2626 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -30,22 +30,24 @@
#define Inline static inline
#endif
-/* types and struct for vector */
-/* */
-/* NB: an entirely zero structure represents an entirely empty vector. */
-/* */
-/* TODO: could force vector_index to be 32 bits ? */
-
+/*------------------------------------------------------------------------------
+ * types and struct for vector
+ *
+ * NB: an entirely zero structure represents an entirely empty vector.
+ *
+ * TODO: could force vector_index to be 32 bits ?
+ */
typedef void* p_vector_item ;
typedef unsigned int vector_index ;
+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 */
};
-typedef struct vector *vector;
/* Values that control the allocation of the vector body. */
/* NB: these must all be powers of 2. */
@@ -76,9 +78,6 @@ typedef struct vector *vector;
/* include any NULL items. */
#define vector_active(V) ((V)->end)
-/* TODO: fix where this is used to poke around inside a vector */
-#define VECTOR_INDEX p_items
-
/* To walk all items in a vector:
*
* vector_index i ;
@@ -109,8 +108,6 @@ 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)
extern vector_index vector_count (vector v);
-extern void vector_only_wrapper_free (vector v);
-extern void vector_only_index_free (void *index);
extern void vector_free (vector v);
extern vector vector_copy (vector v);
@@ -131,7 +128,11 @@ extern p_vector_item vector_ream(vector v, int free_structure) ;
/* Ream out vector but keep the vector structure. */
#define vector_ream_keep(v) vector_ream(v, 0)
-Inline vector_index vector_end(vector v) ;
+Inline void vector_set_min_length(vector v, unsigned int len) ;
+extern void vector_set_new_min_length(vector v, unsigned int len) ;
+
+Inline vector_index vector_length(vector v) ;
+#define vector_end(v) vector_length(v)
Inline int vector_is_empty(vector v) ;
Inline p_vector_item vector_get_item(vector v, vector_index i) ;
@@ -226,9 +227,20 @@ vector_ensure(vector v, vector_index i)
vector_extend(v, i + 1) ; /* do it the hard way */
} ;
+/* 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 */
+ vector_set_new_min_length(v, len) ;
+} ;
+
/* Return index of end of vector (index of last item + 1) */
Inline vector_index
-vector_end(vector v)
+vector_length(vector v)
{
return v->end ;
} ;
diff --git a/lib/vio_fifo.c b/lib/vio_fifo.c
new file mode 100644
index 00000000..fb62f192
--- /dev/null
+++ b/lib/vio_fifo.c
@@ -0,0 +1,572 @@
+/* VTY I/O FIFO
+ * 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 <stddef.h>
+#include <string.h>
+
+#include "vio_fifo.h"
+#include "list_util.h"
+
+#include "memory.h"
+#include "zassert.h"
+
+/*==============================================================================
+ * VTY I/O FIFO manages an arbitrary length byte-wise FIFO buffer.
+ *
+ * The FIFO is arranged as lumps of some given size. Lumps are allocated
+ * as and when necessary, and released once emptied.
+ *
+ * The last lump is never released. So, it may be that only one lump is
+ * ever needed.
+ *
+ *------------------------------------------------------------------------------
+ * Implementation notes:
+ *
+ * The FIFO is initialised with all pointers NULL -- so with no lumps at all.
+ *
+ * Once a lump has been allocated there is always one lump in the FIFO.
+ *
+ * The following are expected to be true:
+ *
+ * * put_ptr == get_ptr => FIFO empty
+ *
+ * * put_ptr == tail->end -- at all times (NULL when no lumps)
+ *
+ * put_ptr >= tail->data ) otherwise something is broken
+ * put_ptr <= tail->end )
+ *
+ * * get_ptr == head->end -- when there is more than one lump
+ * get_ptr <= put_ptr -- when there is only one lump
+ *
+ * get_ptr >= head->data ) otherwise something is broken
+ * get_ptr <= head->end )
+ *
+ * * put_ptr == put_end => tail lump is full
+ * put_ptr < put_end => space exists in the tail lump
+ * put_ptr > put_end => broken
+ *
+ * * get_ptr == get_end => head lump is empty
+ * BUT if there is only one lump, make sure that
+ * get_end == put_ptr.
+ * get_ptr < get_end => data exists in the head lump
+ * get_ptr > get_end => broken
+ *
+ * Note that:
+ *
+ * * when the get_ptr reaches the put_ptr the pointers are reset to the
+ * start of the one and only lump.
+ *
+ * Everywhere that the get_ptr is moved, must check for meeting the
+ * put_ptr and reset pointers. At the same time, when reaches the end of
+ * a lump, gets rid of it.
+ *
+ * * when advancing the put_ptr does not check for advancing the get_end.
+ *
+ * The one exception to this, is that when the put_ptr advances to a new
+ * block, if there was one lump, sets the get_end to the end of that block.
+ *
+ * Everywhere that the get_end is used, must check for there being one
+ * lump and the possibility that put_ptr has changed.
+ */
+
+/*==============================================================================
+ * Initialisation, allocation and freeing of FIFO and lumps thereof.
+ */
+
+/* Return default size, or given size rounded up to 16 byte boundary */
+static size_t
+vio_fifo_size(size_t size)
+{
+ if (size == 0)
+ return 4096 ;
+ else
+ return ((size + 16 - 1) / 16) * 16 ;
+} ;
+
+/*==============================================================================
+ * Initialise VTY I/O FIFO -- allocating if required.
+ */
+extern vio_fifo
+vio_fifo_init_new(vio_fifo vf, size_t size)
+{
+ if (vf == NULL)
+ vf = XCALLOC(MTYPE_VIO_FIFO, sizeof(vio_fifo_t)) ;
+ else
+ memset(vf, 0, sizeof(vio_fifo_t)) ;
+
+ /* Zeroising the the vio_fifo_t has set:
+ *
+ * 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
+ *
+ * ALSO put_ptr == get_ptr => FIFO is empty !
+ */
+
+ vf->size = vio_fifo_size(size) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return vf ;
+}
+
+/*------------------------------------------------------------------------------
+ * Free contents of given FIFO, and free FIFO structure as well, if required.
+ *
+ * Does nothing if given a NULL pointer -- must already have been freed !
+ *
+ * If does not free the FIFO structure, resets it all empty.
+ *
+ * See also: vio_fifo_reset_keep(vio_fifo)
+ * vio_fifo_reset_free(vio_fifo)
+ */
+extern vio_fifo
+vio_fifo_reset(vio_fifo vf, int free_structure)
+{
+ vio_fifo_lump lump ;
+
+ if (vf == NULL)
+ return NULL ;
+
+ while (ddl_pop(&lump, vf->base, list) != NULL)
+ XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
+
+ if (free_structure)
+ XFREE(MTYPE_VIO_FIFO, vf) ; /* sets vf = NULL */
+ else
+ vio_fifo_init_new(vf, vf->size) ;
+
+ return vf ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set FIFO empty, discarding current contents -- will continue to use the FIFO.
+ */
+extern void
+vio_fifo_set_empty(vio_fifo vf)
+{
+ vio_fifo_lump lump ;
+
+ assert(vf != NULL) ;
+
+ while (ddl_head(vf->base) != ddl_tail(vf->base))
+ {
+ ddl_pop(&lump, vf->base, list) ;
+ XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
+ } ;
+
+ lump = ddl_head(vf->base) ;
+ if (lump != NULL)
+ {
+ vf->get_ptr = vf->get_end = vf->put_ptr = lump->data ;
+ vf->put_end = lump->end ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Allocate another lump for putting into.
+ *
+ * Call when (vf->put_ptr >= vf->put_end) -- asserts that the pointers are equal.
+ *
+ * Set the put_ptr/put_end pointers to point at the new lump.
+ *
+ * If this is the first lump allocated, set the get_ptr/get_end pointers too.
+ *
+ * If have just filled the first lump on the list, update the get_end pointer
+ * to reflect the fact that the out lump is now full.
+ */
+extern void
+vio_fifo_lump_new(vio_fifo vf)
+{
+ 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 ;
+
+ first_alloc = (lump == NULL) ; /* extra initialisation needed */
+
+ if (first_alloc)
+ assert(vf->put_ptr == NULL) ; /* must all be NULL together */
+ 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 ;
+
+ ddl_append(vf->base, lump, list) ;
+
+ vf->one = first_alloc ;
+
+ vf->put_ptr = lump->data ;
+ vf->put_end = lump->end ;
+
+ if (first_alloc)
+ {
+ vf->get_ptr = vf->put_ptr ; /* get_ptr == put_ptr => empty */
+ vf->get_end = vf->put_ptr ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*==============================================================================
+ * Put data to the FIFO.
+ */
+
+/*------------------------------------------------------------------------------
+ * Store 'n' bytes -- allocate new lump if current is exhausted.
+ */
+extern void
+vio_fifo_put(vio_fifo vf, const char* src, size_t n)
+{
+ size_t take ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ while (n > 0)
+ {
+ if (vf->put_ptr >= vf->put_end)
+ vio_fifo_lump_new(vf) ; /* traps broken vf->put_ptr > vf->put_end */
+
+ take = (vf->put_end - vf->put_ptr) ;
+ if (take > n)
+ take = n ;
+
+ memcpy(vf->put_ptr, src, take) ;
+ vf->put_ptr += take ;
+
+ src += take ;
+ n -= take ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+} ;
+
+/*==============================================================================
+ * Get data from the FIFO.
+ */
+
+static bool vio_fifo_get_next_lump(vio_fifo vf) ;
+
+/*------------------------------------------------------------------------------
+ * Get ready to read something out of the FIFO.
+ *
+ * Makes sure vf->get_end is up to date (if required) and if the FIFO is not
+ * empty, makes sure vf->get_ptr points at the next byte to be read.
+ *
+ * Returns: true <=> there is something in the FIFO.
+ */
+static inline bool
+vio_fifo_get_ready(vio_fifo vf)
+{
+ if (vf->one)
+ vf->get_end = vf->put_ptr ; /* make sure have everything */
+
+ if (vf->get_ptr >= vf->get_end)
+ if (!vio_fifo_get_next_lump(vf))
+ return 0 ; /* quit now if nothing there */
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return 1 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get upto 'n' bytes.
+ *
+ * Returns: number of bytes got -- may be zero.
+ */
+extern size_t
+vio_fifo_get(vio_fifo vf, void* dst, size_t n)
+{
+ size_t have ;
+ void* dst_in ;
+
+ if (!vio_fifo_get_ready(vf))
+ return 0 ; /* quit now if nothing there */
+
+ dst_in = dst ;
+ while (n > 0)
+ {
+ have = vf->get_end - vf->get_ptr ;
+
+ if (have > n)
+ have = n ;
+
+ memcpy(dst, vf->get_ptr, have) ;
+ vf->get_ptr += have ;
+ dst = (char*)dst + have ;
+
+ if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
+ if (!vio_fifo_get_next_lump(vf))
+ break ; /* quit if nothing more to come */
+
+ n -= have ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return (char*)dst - (char*)dst_in ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get byte -- the long winded way.
+ *
+ * See the inline vio_fifo_get_byte().
+ *
+ * The version is used when the get_ptr is at or just before the end of the
+ * current lump. Looks after all the necessary pointer updates associated with
+ * hitting end of lump, or hitting end of FIFO.
+ *
+ * Returns: 0x00..0xFF -- byte value (as an int)
+ * -1 => FIFO is empty.
+ */
+
+extern int
+vio_fifo_get_next_byte(vio_fifo vf)
+{
+ unsigned char u ;
+
+ if (!vio_fifo_get_ready(vf))
+ return -1 ; /* quit now if nothing there */
+
+ u = *vf->get_ptr++ ;
+
+ /* As soon as reach the end want either to discard empty lump, or reset
+ * the pointers.
+ */
+ if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
+ vio_fifo_get_next_lump(vf) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return u ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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
+ *
+ * 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.
+ */
+extern void*
+vio_fifo_get_lump(vio_fifo vf, size_t* have)
+{
+ if (!vio_fifo_get_ready(vf))
+ {
+ *have = 0 ;
+ return NULL ;
+ } ;
+
+ *have = (vf->get_end - vf->get_ptr) ;
+ return vf->get_ptr ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Advance FIFO to position reached.
+ *
+ * Having done vio_fifo_get_lump(), can take any number of bytes (up to the
+ * number that "have"), then call this function to advance the pointers.
+ *
+ * The "here" argument must the the address returned by vio_fifo_get_lump()
+ * plus the number of bytes taken.
+ */
+extern void
+vio_fifo_got_upto(vio_fifo vf, void* here)
+{
+ vf->get_ptr = here ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ if (vf->get_ptr >= vf->get_end)
+ vio_fifo_get_next_lump(vf) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move on to next lump to get stuff from.
+ *
+ * Advance pointers etc. so that have at least one byte available, unless
+ * the FIFO is entirely empty.
+ *
+ * This should be called if (vf->get_ptr >= vf->get_end) -- asserts that
+ * these are equal !
+ *
+ * NB: when there is only one block, it may be that get_end is out of date,
+ * and should be advanced to the current put_ptr position.
+ *
+ * That is done here, but may be worth updating get_end before testing
+ * against get_ptr.
+ *
+ * Returns: true <=> at least one byte in FIFO.
+ *
+ * NB: if finds that the FIFO is empty, resets the pointers to the start
+ * of the last lump.
+ */
+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 */
+
+ /* Deal with case of one lump only */
+ if (vf->one)
+ {
+ assert( (head != NULL)
+ && (head == tail) ) ;
+
+ 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) ;
+
+ return 0 ; /* FIFO 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 */
+ } ;
+
+ /* Deal with case of not yet allocated */
+ if (head == NULL)
+ {
+ assert( (tail == NULL)
+ && (vf->put_ptr == vf->get_ptr) );
+
+ return 0 ; /* 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) ;
+
+ return (vf->get_ptr < vf->get_end) ;
+} ;
+
+/*==============================================================================
+ * For debug purposes -- verify the state of the given FIFO
+ */
+Private void
+vio_fifo_verify(vio_fifo vf)
+{
+ vio_fifo_lump head ;
+ vio_fifo_lump tail ;
+
+ head = ddl_head(vf->base) ;
+ tail = ddl_tail(vf->base) ;
+
+ /* If nothing allocated, should all be NULL & !vf->one */
+ /* If something allocated, tail must not be NULL */
+ if (head == NULL)
+ {
+ if ( (tail != NULL)
+ || (vf->put_ptr != NULL)
+ || (vf->put_end != NULL)
+ || (vf->get_ptr != NULL)
+ || (vf->get_end != NULL)
+ || (vf->one) )
+ zabort("nothing allocated, but not all NULL") ;
+ return ;
+ }
+ else
+ {
+ if (tail == NULL)
+ zabort("head pointer not NULL, but tail pointer is") ;
+ } ;
+
+ /* 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) )
+ zabort("put pointers outside the tail lump") ;
+
+ if ( (head->data > vf->get_ptr)
+ || (vf->get_ptr > vf->get_end)
+ || (vf->get_end > head->end) )
+ zabort("get pointers outside the head lump") ;
+
+ /* If head == tail, should be vf->one, etc. */
+ if (head == tail)
+ {
+ if (!vf->one)
+ zabort("have one lump, but !vf->one") ;
+
+ if (vf->get_end > vf->put_ptr)
+ zabort("get_end is greater than put_ptr when vf->one") ;
+ }
+ else
+ {
+ if (vf->one)
+ zabort("have two or more lumps, but vf->one is true") ;
+
+ if (vf->get_end != head->end)
+ zabort("get_end is not head->end when !vf->one") ;
+ }
+} ;
diff --git a/lib/vio_fifo.h b/lib/vio_fifo.h
new file mode 100644
index 00000000..6d99afe5
--- /dev/null
+++ b/lib/vio_fifo.h
@@ -0,0 +1,185 @@
+/* VTY I/O FIFO -- 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_VIO_FIFO_H
+#define _ZEBRA_VIO_FIFO_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "list_util.h"
+#include "zassert.h"
+
+#ifndef Inline /* in case of compiler issues */
+#define Inline static inline
+#endif
+
+#ifndef Private /* extern, but for "friends" only */
+#define Private extern
+#endif
+
+/*==============================================================================
+ * VTY I/O FIFO -- buffering of arbitrary amounts of I/O.
+ */
+
+#ifdef NDEBUG
+# define VIO_FIFO_DEBUG 0 /* NDEBUG override */
+#else
+# ifndef VIO_FIFO_DEBUG
+# define VIO_FIFO_DEBUG 1 /* Set to 1 to turn on debug checks */
+# endif
+#endif
+
+/*==============================================================================
+ * Data Structures
+ */
+typedef struct vio_fifo vio_fifo_t ;
+typedef struct vio_fifo* vio_fifo ;
+
+typedef struct vio_fifo_lump vio_fifo_lump_t ;
+typedef struct vio_fifo_lump* vio_fifo_lump ;
+
+struct vio_fifo
+{
+ struct dl_base_pair(vio_fifo_lump) base ;
+
+ bool one ;
+
+ char* put_ptr ;
+ char* put_end ;
+
+ char* get_ptr ;
+ char* get_end ;
+
+ size_t size ;
+} ;
+
+struct vio_fifo_lump
+{
+ struct dl_list_pair(vio_fifo_lump) list ;
+
+ char* end ; /* end of this particular lump */
+ char data[] ;
+} ;
+
+/*==============================================================================
+ * 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) ;
+
+#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) ;
+
+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) ;
+
+
+Private void
+vio_fifo_lump_new(vio_fifo vf) ;
+
+Private int
+vio_fifo_get_next_byte(vio_fifo vf) ;
+
+/*==============================================================================
+ * Debug -- verification function
+ */
+
+Private void
+vio_fifo_verify(vio_fifo vf) ;
+
+#if VIO_FIFO_DEBUG
+# define VIO_FIFO_DEBUG_VERIFY(vf) vio_fifo_verify(vf)
+#else
+# define VIO_FIFO_DEBUG_VERIFY(vf)
+#endif
+
+/*==============================================================================
+ * Inline Functions
+ */
+
+/*------------------------------------------------------------------------------
+ * Returns true <=> FIFO is empty
+ */
+Inline bool
+vio_fifo_empty(vio_fifo vf)
+{
+ return (vf->get_ptr == vf->put_ptr) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Put one byte to the FIFO
+ */
+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_DEBUG_VERIFY(vf) ;
+
+ *vf->put_ptr++ = b ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Get one byte from the FIFO.
+ *
+ * Returns: 0x00..0xFF -- byte value (as an int)
+ * -1 => FIFO is empty.
+ */
+Inline int
+vio_fifo_get_byte(vio_fifo vf)
+{
+ if (vf->get_end <= (vf->get_ptr + 1))
+ return vio_fifo_get_next_byte(vf) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vf) ;
+
+ return (unsigned char)*vf->get_ptr++ ;
+} ;
+
+#endif /* _ZEBRA_VIO_FIFO_H */
diff --git a/lib/vty.c b/lib/vty.c
index e154a575..8acd1403 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -1,7 +1,8 @@
-/*
- * Virtual terminal [aka TeletYpe] interface routine.
+/* VTY top level
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
+ * Revisions: 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
@@ -20,327 +21,348 @@
* 02111-1307, USA.
*/
-#include <zebra.h>
-#include "miyagi.h"
+#include "zebra.h"
+#include <stdbool.h>
+
+#include "vty_io.h"
+#include "vty.h"
+#include "uty.h"
+#include "vty_cli.h"
+
+#include "list_util.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);}
+
+/*==============================================================================
+ * Variables etc.
+ */
+
+/*------------------------------------------------------------------------------
+ * Static and Global (see uty.h) Variables
+ */
+
+/* The mutex and related debug counters */
+qpt_mutex_t vty_mutex ;
+
+#if VTY_DEBUG
+
+int vty_lock_count = 0 ;
+int vty_lock_assert_fail = 0 ;
+
#endif
-/*
- * To make vty qpthread safe we use a single mutex. In general external
- * routines have explicit locks, static routines assume that they are being
- * called with the mutex already locked. There are a few exceptions, e.g.
- * callbacks where static routines are being invoked from outside the module.
- *
- * There are a few cases where both external and static versions of a
- * routine exist. The former for use outside, the latter for use inside
- * the module (and lock). In these cases the internal static versions
- * starts uty_. This is not strictly necessary as we are using a recursive
- * mutex but it avoids unnecessary recursive calls. The recursive mutex
- * is used so that we can call zlog and friends from anywhere.
- *
- * 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.
+/* For thread handling -- initialised in vty_init */
+struct thread_master* vty_master = NULL ;
+
+/* In the qpthreads world, have nexus for the CLI and one for the Routeing
+ * Engine. Some commands are processed directly in the CLI, most have to
+ * be sent to the Routeing Engine.
*/
+qpn_nexus vty_cli_nexus = NULL ;
+qpn_nexus vty_cmd_nexus = NULL ;
-/* Vty events */
-enum event
-{
- VTY_SERV,
- VTY_READ,
- VTY_WRITE,
- VTY_TIMEOUT_RESET,
-#ifdef VTYSH
- VTYSH_SERV,
- VTYSH_READ,
- VTYSH_WRITE
-#endif /* VTYSH */
-};
+/* List of all known vio */
+vty_io vio_list_base = NULL ;
+
+/* List of all vty which are in monitor state. */
+vty_io vio_monitors_base = NULL ;
+
+/* List of all vty which are on death watch */
+vty_io vio_death_watch = NULL ;
+
+/* Vty timeout value -- see "exec timeout" command */
+unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
+
+/* Vty access-class command */
+char *vty_accesslist_name = NULL;
-/* Prototypes */
-static int uty_out (struct vty *vty, const char *format, ...);
-static int uty_vout(struct vty *vty, const char *format, va_list args);
-static void vty_event (enum event, int, struct vty *);
-static void uty_hello (struct vty *vty);
-static void uty_close (struct vty *vty);
-static int uty_config_unlock (struct vty *vty);
-static int uty_shell (struct vty *vty);
-static int uty_read (struct vty *vty, int vty_sock);
-static int uty_flush (struct vty *vty, int vty_sock);
-static void vty_event_t (enum event event, int sock, struct vty *vty);
-static void vty_event_r (enum 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;
-
-/* Vector which store each vty structure. */
-static vector vtyvec;
-
-/* Vty timeout value. */
-static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
-
-/* Vty access-class command */
-static char *vty_accesslist_name = NULL;
-
-/* Vty access-calss for IPv6. */
-static char *vty_ipv6_accesslist_name = NULL;
-
-/* VTY server thread. */
-static vector Vvty_serv_thread;
-
-/* Current directory. */
+/* Vty access-class for IPv6. */
+char *vty_ipv6_accesslist_name = NULL;
+
+/* Current directory -- initialised in vty_init() */
static char *vty_cwd = NULL;
-/* Configure lock. */
-static int vty_config;
+/* Configure lock -- only one vty may be in CONFIG_NODE or above ! */
+bool vty_config = 0 ;
-/* Login password check. */
-static int no_password_check = 0;
+/* Login password check override. */
+bool no_password_check = 0;
-/* Restrict unauthenticated logins? */
-static const u_char restricted_mode_default = 0;
-static u_char restricted_mode = 0;
+/* Restrict unauthenticated logins? */
+const bool restricted_mode_default = 0 ;
+ bool restricted_mode = 0 ;
-/* Integrated configuration file path */
-char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
+/* Watch-dog timer. */
+union vty_watch_dog vty_watch_dog = { NULL } ;
-/* Master of the threads. */
-static struct thread_master *master = NULL;
-static qpn_nexus cli_nexus = NULL;
-static qpn_nexus routing_nexus = NULL;
+/*------------------------------------------------------------------------------
+ * VTYSH stuff
+ */
-/* VTY standard output function. vty == NULL or VTY_SHELL => stdout */
-int
-vty_out (struct vty *vty, const char *format, ...)
-{
- int result;
+/* Integrated configuration file path -- for VTYSH */
+char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG ;
- LOCK
- va_list args;
- va_start (args, format);
- result = uty_vout(vty, format, args);
- va_end (args);
- UNLOCK
- return result;
-}
+/*------------------------------------------------------------------------------
+ * Prototypes
+ */
+static void uty_reset (bool final) ;
+static void uty_init_commands (void) ;
+static void vty_save_cwd (void) ;
-/* internal VTY standard output function. vty == NULL or VTY_SHELL => stdout */
-static int
-uty_out (struct vty *vty, const char *format, ...)
-{
- int result;
- ASSERTLOCKED
- va_list args;
- va_start (args, format);
- result = uty_vout(vty, format, args);
- va_end (args);
- return result;
-}
+/*==============================================================================
+ * Public Interface
+ */
-/* internal VTY standard output function. vty == NULL or VTY_SHELL => stdout */
-static int
-uty_vout(struct vty *vty, const char *format, va_list args)
+/*------------------------------------------------------------------------------
+ * Initialise vty handling (threads and pthreads)
+ *
+ * Install vty's own commands like `who' command.
+ */
+extern void
+vty_init (struct thread_master *master_thread)
{
- int len = 0;
- int size = 1024;
- char buf[1024];
- char *p = NULL;
- va_list ac;
+ VTY_LOCK() ;
- ASSERTLOCKED
+ vty_master = master_thread; /* Local pointer to the master thread */
- if (uty_shell (vty))
- {
- vprintf (format, args);
- }
- else
- {
- /* Try to write to initial buffer. */
- va_copy(ac, args);
- len = vsnprintf (buf, sizeof buf, format, ac);
- va_end(ac);
+ vty_save_cwd (); /* need cwd for config reading */
- /* Initial buffer is not enough. */
- if (len < 0 || len >= size)
- {
- while (1)
- {
- if (len > -1)
- size = len + 1;
- else
- size = size * 2;
+ vio_list_base = NULL ; /* no VTYs yet */
+ vio_monitors_base = NULL ;
+ vio_death_watch = NULL ;
- p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size);
- if (! p)
- return -1;
+ vty_cli_nexus = NULL ; /* not running qnexus-wise */
+ vty_cmd_nexus = NULL ;
- va_copy(ac, args);
- len = vsnprintf (p, size, format, ac);
- va_end(ac);
+ vty_watch_dog.anon = NULL ; /* no watch dog */
- if (len > -1 && len < size)
- break;
- }
- }
+ uty_init_commands() ; /* install nodes */
- /* When initial buffer is enough to store all output. */
- if (! p)
- p = buf;
+ VTY_UNLOCK() ;
+}
- /* Pointer p must point out buffer. */
- buffer_put (vty->obuf, (u_char *) p, len);
+/*------------------------------------------------------------------------------
+ * 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.
+ */
+extern void
+vty_init_r (qpn_nexus cli, qpn_nexus cmd)
+{
+ vty_cli_nexus = cli ;
+ vty_cmd_nexus = cmd ;
- /* If p is not different with buf, it is allocated buffer. */
- if (p != buf)
- XFREE (MTYPE_VTY_OUT_BUF, p);
- }
+ qpt_mutex_init(&vty_mutex, qpt_mutex_recursive);
+} ;
- return len;
-}
+/*------------------------------------------------------------------------------
+ * 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() ;
+} ;
-int
-vty_puts(struct vty *vty, const char* str)
+/*------------------------------------------------------------------------------
+ * Initialisation for vtysh application.
+ *
+ * TODO: work out what this needs to do ! (If anything.)
+ */
+extern void
+vty_init_vtysh (void)
{
- return vty_out(vty, "%s", str) ;
-}
+ VTY_LOCK() ;
-int
-vty_out_newline(struct vty *vty)
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create a new VTY of the given type
+ */
+extern struct vty *
+vty_new (int fd, enum vty_type type)
+{
+ struct vty* vty ;
+
+ VTY_LOCK() ;
+ vty = uty_new(fd, type);
+ VTY_UNLOCK() ;
+
+ return vty ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the given VTY completely
+ */
+extern void
+vty_close (struct vty *vty)
{
- return vty_out(vty, "%s", VTY_NEWLINE) ;
+ VTY_LOCK() ;
+ uty_close(vty->vio);
+ VTY_UNLOCK() ;
}
-/* 123456789012345678901234 */
-const char* vty_spaces_string = " " ;
+/*------------------------------------------------------------------------------
+ * Reset all VTY status
+ *
+ * This is done just before the configuration file is re-read (SIGHUP).
+ *
+ * Half closes all VTY, leaving the death watch to tidy up once all output has
+ * completed.
+ *
+ * NB: old code discarded all output and hard closed all the VTY...
+ *
+ * TODO: ...SIGHUP while a command is queued ?
+ *
+ * Closes all listening sockets.
+ */
+extern void
+vty_reset(void)
+{
+ VTY_LOCK() ;
+ uty_reset(0) ; /* not final ! */
+ VTY_UNLOCK() ;
+}
-int
-vty_out_indent(struct vty *vty, int indent)
+/*------------------------------------------------------------------------------
+ * System shut-down
+ *
+ * Reset all known vty and release all memory.
+ */
+extern void
+vty_terminate (void)
{
- return vty_puts(vty, VTY_SPACES(indent)) ;
+ VTY_LOCK() ;
+ uty_reset(1) ; /* final reset */
+ VTY_UNLOCK() ;
+
+ qpt_mutex_destroy(&vty_mutex, 0);
}
-static int
-vty_log_out (struct vty *vty, const char *level, const char *proto_str,
- const char *format, struct timestamp_control *ctl, va_list va)
+/*------------------------------------------------------------------------------
+ * Reset -- final or for SIGHUP
+ */
+static void
+uty_reset (bool curtains)
{
- int ret;
- int len;
- char buf[1024];
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
- ASSERTLOCKED
+ uty_close_listeners() ;
- if (!ctl->already_rendered)
+ while ((vio = sdl_pop(&vio, vio_list_base, vio_list)) != NULL)
{
- ctl->len = uquagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
- ctl->already_rendered = 1;
- }
- if (ctl->len+1 >= sizeof(buf))
- return -1;
- memcpy(buf, ctl->buf, len = ctl->len);
- buf[len++] = ' ';
- buf[len] = '\0';
-
- if (level)
- ret = snprintf(buf+len, sizeof(buf)-len, "%s: %s: ", level, proto_str);
- else
- ret = snprintf(buf+len, sizeof(buf)-len, "%s: ", proto_str);
- if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
- return -1;
+ uty_half_close(vio) ; /* TODO: reason for close */
- if (((ret = vsnprintf(buf+len, sizeof(buf)-len, format, va)) < 0) ||
- ((size_t)((len += ret)+2) > sizeof(buf)))
- return -1;
+ if (curtains)
+ uty_full_close(vio) ;
+ } ;
- buf[len++] = '\r';
- buf[len++] = '\n';
+ vty_timeout_val = VTY_TIMEOUT_DEFAULT;
- if (write(vty->fd, buf, len) < 0)
+ if (vty_accesslist_name)
{
- 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->monitor = 0; /* disable monitoring to avoid infinite recursion */
- uzlog(NULL, LOG_WARNING, "%s: write failed to vty client fd %d, closing: %s",
- __func__, vty->fd, safe_strerror(errno));
- buffer_reset(vty->obuf);
- /* cannot call vty_close, because a parent routine may still try
- to access the vty struct */
- vty->status = VTY_CLOSE;
- shutdown(vty->fd, SHUT_RDWR);
- return -1;
+ XFREE(MTYPE_VTY, vty_accesslist_name);
+ vty_accesslist_name = NULL;
}
- return 0;
-}
-/* Output current time to the vty. */
-void
-vty_time_print (struct vty *vty, int cr)
+ if (vty_ipv6_accesslist_name)
+ {
+ XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
+ vty_ipv6_accesslist_name = NULL;
+ }
+
+ if (curtains && vty_cwd)
+ XFREE (MTYPE_TMP, vty_cwd);
+} ;
+
+/*==============================================================================
+ * 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 vty_io.
+ */
+
+/*------------------------------------------------------------------------------
+ * VTY output -- cf fprintf !
+ */
+extern int
+vty_out (struct vty *vty, const char *format, ...)
{
- char buf [25];
+ int result;
- if (quagga_timestamp(0, buf, sizeof(buf)) == 0)
+ VTY_LOCK() ;
+ va_list args;
+ va_start (args, format);
+ result = uty_vout(vty, format, args);
+ va_end (args);
+ VTY_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)
{
- zlog (NULL, LOG_INFO, "quagga_timestamp error");
- return;
+ 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);
+ vty_out (vty, "%s%s", buf, VTY_NEWLINE);
else
vty_out (vty, "%s ", buf);
return;
}
-/* Say hello to vty interface. */
+/*------------------------------------------------------------------------------
+ * Say hello to vty interface.
+ */
void
vty_hello (struct vty *vty)
{
- LOCK
- uty_hello(vty);
- UNLOCK
-}
+ VTY_LOCK() ;
-static void
-uty_hello (struct vty *vty)
-{
- ASSERTLOCKED
#ifdef QDEBUG
uty_out (vty, "%s%s", debug_banner, VTY_NEWLINE);
#endif
@@ -351,126 +373,147 @@ uty_hello (struct vty *vty)
f = fopen (host.motdfile, "r");
if (f)
- {
- while (fgets (buf, sizeof (buf), f))
- {
- char *s;
- /* work backwards to ignore trailling 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);
- }
+ {
+ 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);
+ uty_out (vty, "MOTD file %s not found%s", host.motdfile, VTY_NEWLINE);
}
else if (host.motd)
uty_out (vty, "%s", host.motd);
+
+ VTY_UNLOCK() ;
}
-/* Put out prompt and wait input from user. */
-static void
-vty_prompt (struct vty *vty)
+/*==============================================================================
+ * Command Execution
+ */
+
+/*------------------------------------------------------------------------------
+ * Execute command, adding it to the history if not empty or comment
+ *
+ * Outputs diagnostics if fails to parse.
+ *
+ * Returns: CMD_xxxx result.
+ */
+extern int
+uty_command(struct vty *vty, const char *buf)
{
- struct utsname names;
- const char*hostname;
+ int ret;
+ vector vline;
+ const char *protocolname;
- ASSERTLOCKED
+ VTY_ASSERT_LOCKED() ;
- if (vty->type == VTY_TERM)
- {
- hostname = host.name;
- if (!hostname)
- {
- uname (&names);
- hostname = names.nodename;
- }
- uty_out (vty, cmd_prompt (vty->node), hostname);
- }
-}
+ /* Split readline string up into the vector */
+ vline = cmd_make_strvec (buf);
-/* Send WILL TELOPT_ECHO to remote server. */
-static void
-vty_will_echo (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
- ASSERTLOCKED
- uty_out (vty, "%s", cmd);
-}
+ if (vline == NULL)
+ return CMD_SUCCESS; /* quit if empty or comment */
-/* Make suppress Go-Ahead telnet option. */
-static void
-vty_will_suppress_go_ahead (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
- ASSERTLOCKED
- uty_out (vty, "%s", cmd);
-}
+ uty_cli_hist_add (vty->vio, buf) ;
-/* Make don't use linemode over telnet. */
-static void
-vty_dont_linemode (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
- ASSERTLOCKED
- uty_out (vty, "%s", cmd);
-}
+#ifdef CONSUMED_TIME_CHECK
+ {
+ RUSAGE_T before;
+ RUSAGE_T after;
+ unsigned long realtime, cputime;
-/* Use window size. */
-static void
-vty_do_window_size (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
- ASSERTLOCKED
- uty_out (vty, "%s", cmd);
-}
+ GETRUSAGE(&before);
+#endif /* CONSUMED_TIME_CHECK */
-#if 0 /* Currently not used. */
-/* Make don't use lflow vty interface. */
-static void
-vty_dont_lflow_ahead (struct vty *vty)
-{
- unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
- ASSERTLOCKED
- uty_out (vty, "%s", cmd);
-}
-#endif /* 0 */
+//VTY_UNLOCK() ;
+ ret = cmd_execute_command (vline, vty, NULL, vty_cmd_nexus, vty_cli_nexus, 0);
+//VTY_LOCK() ;
-/* Allocate new vty struct. */
-struct vty *
-vty_new (int fd, int type)
-{
- struct vty *vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
+ /* Get the name of the protocol if any */
+ protocolname = uzlog_get_proto_name(NULL);
- vty->obuf = buffer_new(0); /* Use default buffer size. */
- vty->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
- vty->max = VTY_BUFSIZ;
- vty->fd = fd;
- vty->type = type;
+#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 (cli_nexus)
- {
- vty->qf = qps_file_init_new(vty->qf, NULL);
- qps_add_file(cli_nexus->selection, vty->qf, vty->fd, vty);
- vty->qtr = qtimer_init_new(vty->qtr, cli_nexus->pile, vty_timeout_r, vty);
- }
+ 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 vty;
+ return ret;
}
-/* Authentication of vty */
-static void
-vty_auth (struct vty *vty, char *buf)
+/*------------------------------------------------------------------------------
+ * Authentication of vty
+ *
+ * During AUTH_NODE and AUTH_ENABLE_NODE, when a command line is dispatched by
+ * any means this function is called.
+ *
+ * Note that if the AUTH_NODE password fails too many times, the terminal is
+ * closed.
+ *
+ * Returns: 0 <=> not queued.
+ */
+extern int
+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 *);
- ASSERTLOCKED
+ vty_io vio = vty->vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* What to do ?
+ *
+ * In fact, all the exotic command terminators simply discard any input
+ * and return.
+ */
+ switch (cli_do)
+ {
+ case cli_do_nothing:
+ case cli_do_ctrl_c:
+ case cli_do_ctrl_d:
+ case cli_do_ctrl_z:
+ return 0 ;
+
+ case cli_do_command:
+ break ;
+
+ default:
+ zabort("unknown or invalid cli_do") ;
+ } ;
+ /* Ordinary command dispatch -- see if password is OK. */
switch (vty->node)
{
case AUTH_NODE:
@@ -504,368 +547,102 @@ vty_auth (struct vty *vty, char *buf)
if (! fail)
{
- vty->fail = 0;
+ vio->fail = 0;
vty->node = next_node; /* Success ! */
}
else
{
- vty->fail++;
- if (vty->fail >= 3)
+ vio->fail++;
+ if (vio->fail >= 3)
{
if (vty->node == AUTH_NODE)
{
- uty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
- vty->status = VTY_CLOSE;
+ uty_out (vty, "%% Bad passwords, too many failures!%s",
+ VTY_NEWLINE);
+ uty_half_close(vio) ;
}
else
{
/* AUTH_ENABLE_NODE */
- vty->fail = 0;
- uty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
+ vio->fail = 0;
+ uty_out (vty, "%% Bad enable passwords, too many failures!%s",
+ VTY_NEWLINE);
vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
}
}
}
-}
-
-/* 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, 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->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 */
-void
-vty_queued_result(struct vty *vty, int result)
-{
- LOCK
-
- vty_prompt(vty);
-
- /* Wake up */
- if (cli_nexus)
- {
- vty_event (VTY_WRITE, vty->fd, vty);
- if (qpthreads_enabled)
- qpt_thread_signal(cli_nexus->thread_id, SIGMQUEUE);
- }
-
- UNLOCK
-}
-
-static const char telnet_backward_char = 0x08;
-static const char telnet_space_char = ' ';
-
-/* Basic function to write buffer to vty. */
-static void
-vty_write (struct vty *vty, const char *buf, size_t nbytes)
-{
- ASSERTLOCKED
- if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
- return;
-
- /* Should we do buffering here ? And make vty_flush (vty) ? */
- buffer_put (vty->obuf, buf, nbytes);
-}
-
-/* Ensure length of input buffer. Is buffer is short, double it. */
-static void
-vty_ensure (struct vty *vty, int length)
-{
- ASSERTLOCKED
- if (vty->max <= length)
- {
- vty->max *= 2;
- vty->buf = XREALLOC (MTYPE_VTY, vty->buf, vty->max);
- }
-}
-
-/* Basic function to insert character into vty. */
-static void
-vty_self_insert (struct vty *vty, char c)
-{
- int i;
- int length;
-
- ASSERTLOCKED
-
- vty_ensure (vty, vty->length + 1);
- length = vty->length - vty->cp;
- memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
- vty->buf[vty->cp] = c;
- vty_write (vty, &vty->buf[vty->cp], length + 1);
- for (i = 0; i < length; i++)
- vty_write (vty, &telnet_backward_char, 1);
+ return 0 ;
+} ;
- vty->cp++;
- vty->length++;
-}
-
-/* Self insert character 'c' in overwrite mode. */
-static void
-vty_self_insert_overwrite (struct vty *vty, char c)
-{
- ASSERTLOCKED
- vty_ensure (vty, vty->length + 1);
- vty->buf[vty->cp++] = c;
-
- if (vty->cp > vty->length)
- vty->length++;
-
- if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
- return;
-
- vty_write (vty, &c, 1);
-}
-
-/* Insert a word into vty interface with overwrite mode. */
-static void
-vty_insert_word_overwrite (struct vty *vty, char *str)
+/*------------------------------------------------------------------------------
+ * Command line "exit" command -- aka "quit"
+ *
+ * Falls back one NODE level.
+ *
+ * Returns: 0 <=> not queued.
+ */
+extern int
+vty_cmd_exit(struct vty* vty)
{
+ VTY_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
- int len = strlen (str);
-
- ASSERTLOCKED
-
- vty_write (vty, str, len);
- strcpy (&vty->buf[vty->cp], str);
- vty->cp += len;
- vty->length = vty->cp;
-}
-
-/* Forward character. */
-static void
-vty_forward_char (struct vty *vty)
-{
- ASSERTLOCKED
- if (vty->cp < vty->length)
- {
- vty_write (vty, &vty->buf[vty->cp], 1);
- vty->cp++;
- }
-}
-
-/* Backward character. */
-static void
-vty_backward_char (struct vty *vty)
-{
- ASSERTLOCKED
- if (vty->cp > 0)
+ switch (vty->node)
{
- vty->cp--;
- vty_write (vty, &telnet_backward_char, 1);
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ case RESTRICTED_NODE:
+ if (vty_shell (vty))
+ exit (0);
+// else
+// vty_set_status(vty, VTY_CLOSE);
+ break;
+ case CONFIG_NODE:
+ uty_config_unlock (vty, ENABLE_NODE);
+ break;
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case BGP_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case MASC_NODE:
+ case RMAP_NODE:
+ case VTY_NODE:
+ vty->node = CONFIG_NODE ;
+ break;
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ case BGP_IPV6M_NODE:
+ vty->node = BGP_NODE ;
+ break;
+ case KEYCHAIN_KEY_NODE:
+ vty->node = KEYCHAIN_NODE ;
+ break;
+ default:
+ break;
}
-}
-
-/* Move to the beginning of the line. */
-static void
-vty_beginning_of_line (struct vty *vty)
-{
- ASSERTLOCKED
- while (vty->cp)
- vty_backward_char (vty);
-}
-
-/* Move to the end of the line. */
-static void
-vty_end_of_line (struct vty *vty)
-{
- ASSERTLOCKED
- while (vty->cp < vty->length)
- vty_forward_char (vty);
-}
-
-static void vty_kill_line_from_beginning (struct vty *);
-static void vty_redraw_line (struct vty *);
-/* Print command line history. This function is called from
- vty_next_line and vty_previous_line. */
-static void
-vty_history_print (struct vty *vty)
-{
- int length;
-
- ASSERTLOCKED
-
- vty_kill_line_from_beginning (vty);
-
- /* Get previous line from history buffer */
- length = strlen (vty->hist[vty->hp]);
- memcpy (vty->buf, vty->hist[vty->hp], length);
- vty->cp = vty->length = length;
-
- /* Redraw current line */
- vty_redraw_line (vty);
-}
-
-/* Show next command line history. */
-static void
-vty_next_line (struct vty *vty)
-{
- int try_index;
-
- ASSERTLOCKED
-
- if (vty->hp == vty->hindex)
- return;
-
- /* Try is there history exist or not. */
- try_index = vty->hp;
- if (try_index == (VTY_MAXHIST - 1))
- try_index = 0;
- else
- try_index++;
-
- /* If there is not history return. */
- if (vty->hist[try_index] == NULL)
- return;
- else
- vty->hp = try_index;
-
- vty_history_print (vty);
+ VTY_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ return 0 ;
}
-/* Show previous command line history. */
-static void
-vty_previous_line (struct vty *vty)
-{
- int try_index;
-
- ASSERTLOCKED
-
- try_index = vty->hp;
- if (try_index == 0)
- try_index = VTY_MAXHIST - 1;
- else
- try_index--;
-
- if (vty->hist[try_index] == NULL)
- return;
- else
- vty->hp = try_index;
-
- vty_history_print (vty);
-}
-
-/* This function redraw all of the command line character. */
-static void
-vty_redraw_line (struct vty *vty)
-{
- ASSERTLOCKED
- vty_write (vty, vty->buf, vty->length);
- vty->cp = vty->length;
-}
-
-/* Forward word. */
-static void
-vty_forward_word (struct vty *vty)
-{
- ASSERTLOCKED
- while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
- vty_forward_char (vty);
-
- while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
- vty_forward_char (vty);
-}
-
-/* Backward word without skipping training space. */
-static void
-vty_backward_pure_word (struct vty *vty)
-{
- ASSERTLOCKED
- while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
- vty_backward_char (vty);
-}
-
-/* Backward word. */
-static void
-vty_backward_word (struct vty *vty)
-{
- ASSERTLOCKED
- while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
- vty_backward_char (vty);
-
- while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
- vty_backward_char (vty);
-}
-
-/* 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);
- vty->cp = 0;
-}
-
-/* When '^Z' is received from vty, move down to the enable mode. */
-static void
-vty_end_config (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Command line "end" command
+ *
+ * Falls back to ENABLE_NODE.
+ *
+ * Returns: 0 <=> not queued.
+ */
+extern int
+vty_cmd_end(struct vty* vty)
{
- ASSERTLOCKED
- uty_out (vty, "%s", VTY_NEWLINE);
+ VTY_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
switch (vty->node)
{
@@ -893,406 +670,76 @@ vty_end_config (struct vty *vty)
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
case VTY_NODE:
- uty_config_unlock (vty);
- vty->node = ENABLE_NODE;
+ uty_config_unlock (vty, ENABLE_NODE);
break;
default:
- /* Unknown node, we have to ignore it. */
break;
}
- vty_prompt (vty);
- vty->cp = 0;
-}
-
-/* Delete a charcter at the current point. */
-static void
-vty_delete_char (struct vty *vty)
-{
- int i;
- int size;
-
- ASSERTLOCKED
-
- if (vty->length == 0)
- {
- vty_down_level (vty);
- return;
- }
-
- if (vty->cp == vty->length)
- return; /* completion need here? */
-
- size = vty->length - vty->cp;
-
- vty->length--;
- memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
- vty->buf[vty->length] = '\0';
-
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
- return;
-
- vty_write (vty, &vty->buf[vty->cp], size - 1);
- vty_write (vty, &telnet_space_char, 1);
-
- for (i = 0; i < size; i++)
- vty_write (vty, &telnet_backward_char, 1);
-}
-
-/* Delete a character before the point. */
-static void
-vty_delete_backward_char (struct vty *vty)
-{
- ASSERTLOCKED
- if (vty->cp == 0)
- return;
-
- vty_backward_char (vty);
- vty_delete_char (vty);
-}
-
-/* Kill rest of line from current point. */
-static void
-vty_kill_line (struct vty *vty)
-{
- int i;
- int size;
-
- ASSERTLOCKED
-
- size = vty->length - vty->cp;
-
- if (size == 0)
- return;
-
- for (i = 0; i < size; i++)
- vty_write (vty, &telnet_space_char, 1);
- for (i = 0; i < size; i++)
- vty_write (vty, &telnet_backward_char, 1);
-
- memset (&vty->buf[vty->cp], 0, size);
- vty->length = vty->cp;
-}
-
-/* Kill line from the beginning. */
-static void
-vty_kill_line_from_beginning (struct vty *vty)
-{
- ASSERTLOCKED
- vty_beginning_of_line (vty);
- vty_kill_line (vty);
-}
-
-/* Delete a word before the point. */
-static void
-vty_forward_kill_word (struct vty *vty)
-{
- ASSERTLOCKED
- while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
- vty_delete_char (vty);
- while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
- vty_delete_char (vty);
-}
-
-/* Delete a word before the point. */
-static void
-vty_backward_kill_word (struct vty *vty)
-{
- ASSERTLOCKED
- while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
- vty_delete_backward_char (vty);
- while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
- vty_delete_backward_char (vty);
-}
-
-/* Transpose chars before or at the point. */
-static void
-vty_transpose_chars (struct vty *vty)
-{
- char c1, c2;
-
- ASSERTLOCKED
-
- /* If length is short or point is near by the beginning of line then
- return. */
- if (vty->length < 2 || vty->cp < 1)
- return;
-
- /* In case of point is located at the end of the line. */
- if (vty->cp == vty->length)
- {
- c1 = vty->buf[vty->cp - 1];
- c2 = vty->buf[vty->cp - 2];
-
- vty_backward_char (vty);
- vty_backward_char (vty);
- vty_self_insert_overwrite (vty, c1);
- vty_self_insert_overwrite (vty, c2);
- }
- else
- {
- c1 = vty->buf[vty->cp];
- c2 = vty->buf[vty->cp - 1];
-
- vty_backward_char (vty);
- vty_self_insert_overwrite (vty, c1);
- vty_self_insert_overwrite (vty, c2);
- }
-}
+ VTY_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
+ return 0 ;
+} ;
-/* Do completion at vty interface. */
-static void
-vty_complete_command (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Command line ^C action.
+ *
+ * Ignores contents of command line (including not adding to history).
+ *
+ * Fall back to ENABLE_NODE if in any one of a number of nodes.
+ *
+ * Resets the history pointer.
+ *
+ * Returns: 0 <=> not queued.
+ */
+extern int
+uty_stop_input(struct vty *vty)
{
+ vty_io vio = vty->vio ;
- int i;
- int ret;
- char **matched = NULL;
- vector vline;
-
- ASSERTLOCKED
-
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
- return;
-
- vline = cmd_make_strvec (vty->buf);
- if (vline == NULL)
- return;
-
- /* In case of 'help \t'. */
- if (isspace ((int) vty->buf[vty->length - 1]))
- vector_set (vline, '\0');
-
- matched = cmd_complete_command (vline, vty->node, &ret);
+ VTY_ASSERT_LOCKED() ;
- cmd_free_strvec (vline);
-
- uty_out (vty, "%s", VTY_NEWLINE);
- switch (ret)
+ switch (vty->node)
{
- case CMD_ERR_AMBIGUOUS:
- uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
- vty_prompt (vty);
- vty_redraw_line (vty);
- break;
- case CMD_ERR_NO_MATCH:
- /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
- vty_prompt (vty);
- vty_redraw_line (vty);
- break;
- case CMD_COMPLETE_FULL_MATCH:
- vty_prompt (vty);
- vty_redraw_line (vty);
- vty_backward_pure_word (vty);
- vty_insert_word_overwrite (vty, matched[0]);
- vty_self_insert (vty, ' ');
- XFREE (MTYPE_TMP, matched[0]);
- break;
- case CMD_COMPLETE_MATCH:
- vty_prompt (vty);
- vty_redraw_line (vty);
- vty_backward_pure_word (vty);
- vty_insert_word_overwrite (vty, matched[0]);
- XFREE (MTYPE_TMP, matched[0]);
- vector_only_index_free (matched);
- return;
- break;
- case CMD_COMPLETE_LIST_MATCH:
- for (i = 0; matched[i] != NULL; i++)
- {
- if (i != 0 && ((i % 6) == 0))
- uty_out (vty, "%s", VTY_NEWLINE);
- uty_out (vty, "%-10s ", matched[i]);
- XFREE (MTYPE_TMP, matched[i]);
- }
- uty_out (vty, "%s", VTY_NEWLINE);
-
- vty_prompt (vty);
- vty_redraw_line (vty);
- break;
- case CMD_ERR_NOTHING_TODO:
- vty_prompt (vty);
- vty_redraw_line (vty);
+ case CONFIG_NODE:
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case BGP_NODE:
+ case RMAP_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case KEYCHAIN_KEY_NODE:
+ case MASC_NODE:
+ case VTY_NODE:
+ uty_config_unlock (vty, ENABLE_NODE) ;
break;
default:
+ /* Unknown node, we have to ignore it. */
break;
}
- if (matched)
- vector_only_index_free (matched);
-}
-
-static void
-vty_describe_fold (struct vty *vty, 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_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_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_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
-
- cmd = "";
- }
-
- uty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
-
- XFREE (MTYPE_TMP, buf);
-}
-
-/* Describe matched command function. */
-static void
-vty_describe_command (struct vty *vty)
-{
- int ret;
- vector vline;
- vector describe;
- unsigned int i, width, desc_width;
- struct desc *desc, *desc_cr = NULL;
-
- ASSERTLOCKED
-
- vline = cmd_make_strvec (vty->buf);
-
- /* In case of '> ?'. */
- if (vline == NULL)
- {
- vline = vector_init (1);
- vector_set (vline, '\0');
- }
- else
- if (isspace ((int) vty->buf[vty->length - 1]))
- vector_set (vline, '\0');
-
- describe = cmd_describe_command (vline, vty->node, &ret);
-
- uty_out (vty, "%s", VTY_NEWLINE);
-
- /* Ambiguous error. */
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
- goto out;
- break;
- case CMD_ERR_NO_MATCH:
- uty_out (vty, "%% There is no matched command.%s", VTY_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 = vty->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_out (vty, " %-s%s",
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- VTY_NEWLINE);
- else if (desc_width >= strlen (desc->str))
- uty_out (vty, " %-*s %s%s", width,
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str, VTY_NEWLINE);
- else
- vty_describe_fold (vty, width, desc_width, desc);
-
-#if 0
- uty_out (vty, " %-*s %s%s", width
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str ? desc->str : "", VTY_NEWLINE);
-#endif /* 0 */
- }
-
- if ((desc = desc_cr))
- {
- if (!desc->str)
- uty_out (vty, " %-s%s",
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- VTY_NEWLINE);
- else if (desc_width >= strlen (desc->str))
- uty_out (vty, " %-*s %s%s", width,
- desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
- desc->str, VTY_NEWLINE);
- else
- vty_describe_fold (vty, width, desc_width, desc);
- }
-
-out:
- cmd_free_strvec (vline);
- if (describe)
- vector_free (describe);
-
- vty_prompt (vty);
- vty_redraw_line (vty);
-}
+ /* Set history pointer to the latest one. */
+ vio->hp = vio->hindex;
-static void
-vty_clear_buf (struct vty *vty)
-{
- ASSERTLOCKED
- memset (vty->buf, 0, vty->max);
-}
+ return 0 ;
+} ;
-/* ^C stop current input and do not add command line to the history. */
-static void
-vty_stop_input (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Command ^Z action.
+ *
+ * Ignores contents of command line (including not adding to history).
+ *
+ * Fall back to ENABLE_NODE if in any one of a number of nodes.
+ *
+ * Returns: 0 <=> not queued.
+ */
+extern int
+uty_end_config (struct vty *vty)
{
- ASSERTLOCKED
- vty->cp = vty->length = 0;
- vty_clear_buf (vty);
- uty_out (vty, "%s", VTY_NEWLINE);
+ VTY_ASSERT_LOCKED() ;
switch (vty->node)
{
@@ -1307,6 +754,11 @@ vty_stop_input (struct vty *vty)
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:
@@ -1315,1354 +767,258 @@ vty_stop_input (struct vty *vty)
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
case VTY_NODE:
- uty_config_unlock (vty);
- vty->node = ENABLE_NODE;
+ uty_config_unlock (vty, ENABLE_NODE) ;
break;
default:
/* Unknown node, we have to ignore it. */
break;
}
- vty_prompt (vty);
- /* Set history pointer to the latest one. */
- vty->hp = vty->hindex;
+ return 0 ;
}
-/* Add current command line to the history buffer. */
-static void
-vty_hist_add (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Command ^D action -- when nothing else on command line.
+ *
+ * Same as "exit" command.
+ *
+ * Returns: 0 <=> not queued.
+ */
+extern int
+uty_down_level (struct vty *vty)
{
- int index;
-
- ASSERTLOCKED
+ return vty_cmd_exit(vty) ;
+} ;
- if (vty->length == 0)
- return;
-
- index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
-
- /* Ignore the same string as previous one. */
- if (vty->hist[index])
- if (strcmp (vty->buf, vty->hist[index]) == 0)
- {
- vty->hp = vty->hindex;
- return;
- }
-
- /* Insert history entry. */
- if (vty->hist[vty->hindex])
- XFREE (MTYPE_VTY_HIST, vty->hist[vty->hindex]);
- vty->hist[vty->hindex] = XSTRDUP (MTYPE_VTY_HIST, vty->buf);
-
- /* History index rotation. */
- vty->hindex++;
- if (vty->hindex == VTY_MAXHIST)
- vty->hindex = 0;
-
- vty->hp = vty->hindex;
-}
+/*==============================================================================
+ * Reading of configuration file
+ */
-/* #define TELNET_OPTION_DEBUG */
+static FILE * vty_use_backup_config (char *fullpath) ;
+static void vty_read_file (FILE *confp, void (*after_first_cmd)(void)) ;
-/* Get telnet window size. */
-static int
-vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
+/*------------------------------------------------------------------------------
+ * Read the given configuration file.
+ */
+extern void
+vty_read_config (char *config_file,
+ char *config_default)
{
-#ifdef TELNET_OPTION_DEBUG
- int i;
-
- ASSERTLOCKED
-
- for (i = 0; i < nbytes; i++)
- {
- switch (buf[i])
- {
- case IAC:
- uty_out (vty, "IAC ");
- break;
- case WILL:
- uty_out (vty, "WILL ");
- break;
- case WONT:
- uty_out (vty, "WONT ");
- break;
- case DO:
- uty_out (vty, "DO ");
- break;
- case DONT:
- uty_out (vty, "DONT ");
- break;
- case SB:
- uty_out (vty, "SB ");
- break;
- case SE:
- uty_out (vty, "SE ");
- break;
- case TELOPT_ECHO:
- uty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
- break;
- case TELOPT_SGA:
- uty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
- break;
- case TELOPT_NAWS:
- uty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
- break;
- default:
- uty_out (vty, "%x ", buf[i]);
- break;
- }
- }
- uty_out (vty, "%s", VTY_NEWLINE);
-
-#endif /* TELNET_OPTION_DEBUG */
-
- switch (buf[0])
- {
- case SB:
- vty->sb_len = 0;
- vty->iac_sb_in_progress = 1;
- return 0;
- break;
- case SE:
- {
- if (!vty->iac_sb_in_progress)
- return 0;
-
- if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
- {
- vty->iac_sb_in_progress = 0;
- return 0;
- }
- switch (vty->sb_buf[0])
- {
- case TELOPT_NAWS:
- if (vty->sb_len != TELNET_NAWS_SB_LEN)
- uzlog(NULL, LOG_WARNING, "RFC 1073 violation detected: telnet NAWS option "
- "should send %d characters, but we received %lu",
- TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
- else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
- uzlog(NULL, LOG_ERR, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
- "too small to handle the telnet NAWS option",
- (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
- else
- {
- vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
- vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
-#ifdef TELNET_OPTION_DEBUG
- uty_out(vty, "TELNET NAWS window size negotiation completed: "
- "width %d, height %d%s",
- vty->width, vty->height, VTY_NEWLINE);
-#endif
- }
- break;
- }
- vty->iac_sb_in_progress = 0;
- return 0;
- break;
- }
- default:
- break;
- }
- return 1;
+ vty_read_config_first_cmd_special(config_file, config_default, NULL);
}
-/* Execute current command line. */
-static int
-vty_execute (struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Read the given configuration file.
+ *
+ * The config_file (-f argument) is used if specified.
+ *
+ * If config_file is NULL, use the config_default.
+ *
+ * If using the config_default, if VTYSH_ENABLED, look for "vtysh" in the name.
+ * If find "vtysh" and find the "integrate_default" file, then do nothing
+ * now -- expect vtysh to connect in due course and provide the configuration.
+ *
+ * The config_file or config_default may be relative file names.
+ *
+ * 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.
+ */
+extern void
+vty_read_config_first_cmd_special(char *config_file,
+ char *config_default,
+ void (*after_first_cmd)(void))
{
- int ret;
-
- ret = CMD_SUCCESS;
-
- switch (vty->node)
- {
- case AUTH_NODE:
- case AUTH_ENABLE_NODE:
- vty_auth (vty, vty->buf);
- break;
- default:
- ret = vty_command (vty, vty->buf);
- if (vty->type == VTY_TERM)
- vty_hist_add (vty);
- break;
- }
-
- /* Clear command line buffer. */
- vty->cp = vty->length = 0;
- vty_clear_buf (vty);
-
- if (vty->status != VTY_CLOSE && ret != CMD_QUEUED)
- vty_prompt (vty);
-
- return ret;
-}
-
-#define CONTROL(X) ((X) - '@')
-#define VTY_NORMAL 0
-#define VTY_PRE_ESCAPE 1
-#define VTY_ESCAPE 2
+ char cwd[MAXPATHLEN];
+ FILE *confp = NULL;
+ char *fullpath;
+ char *tmp = NULL;
-/* Escape character command map. */
-static void
-vty_escape_map (unsigned char c, struct vty *vty)
-{
- ASSERTLOCKED
- switch (c)
+ /* Deal with VTYSH_ENABLED magic */
+ if (VTYSH_ENABLED && (config_file == NULL))
{
- case ('A'):
- vty_previous_line (vty);
- break;
- case ('B'):
- vty_next_line (vty);
- break;
- case ('C'):
- vty_forward_char (vty);
- break;
- case ('D'):
- vty_backward_char (vty);
- break;
- default:
- break;
- }
-
- /* Go back to normal mode. */
- vty->escape = VTY_NORMAL;
-}
-
-/* Quit print out to the buffer. */
-static void
-vty_buffer_reset (struct vty *vty)
-{
- ASSERTLOCKED
- buffer_reset (vty->obuf);
- vty_prompt (vty);
- vty_redraw_line (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
-
- /* is this necessary? */
- qps_disable_modes(qf, qps_read_mbit);
- uty_read(vty, vty_sock);
-
- 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
+ int ret;
+ struct stat conf_stat;
- vty->t_read = NULL;
- result = uty_read(vty, vty_sock);
+ /* !!!!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
+ */
- UNLOCK
- return result;
-}
+ /* Stat for vtysh Zebra.conf, if found startup and wait for
+ * boot configuration
+ */
-static int
-uty_read (struct vty *vty, int vty_sock)
-{
- int i;
- int nbytes;
- unsigned char buf[VTY_READ_BUFSIZ];
+ if ( strstr(config_default, "vtysh") == NULL)
+ {
+ ret = stat (integrate_default, &conf_stat);
+ if (ret >= 0)
+ return;
+ }
+ } ;
- /* Read raw data from socket */
- if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
- {
- if (nbytes < 0)
- {
- if (ERRNO_IO_RETRY(errno))
- {
- vty_event (VTY_READ, vty_sock, vty);
- return 0;
- }
- vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- uzlog(NULL, LOG_WARNING, "%s: read error on vty client fd %d, closing: %s",
- __func__, vty->fd, safe_strerror(errno));
- }
- buffer_reset(vty->obuf);
- vty->status = VTY_CLOSE;
- }
+ /* Use default if necessary, and deal with constructing full path */
+ if (config_file == NULL)
+ config_file = config_default ;
- for (i = 0; i < nbytes; i++)
+ if (! IS_DIRECTORY_SEP (config_file[0]))
{
- if (buf[i] == IAC)
- {
- if (!vty->iac)
- {
- vty->iac = 1;
- continue;
- }
- else
- {
- vty->iac = 0;
- }
- }
-
- if (vty->iac_sb_in_progress && !vty->iac)
- {
- if (vty->sb_len < sizeof(vty->sb_buf))
- vty->sb_buf[vty->sb_len] = buf[i];
- vty->sb_len++;
- continue;
- }
-
- if (vty->iac)
- {
- /* In case of telnet command */
- int ret = 0;
- ret = vty_telnet_option (vty, buf + i, nbytes - i);
- vty->iac = 0;
- i += ret;
- continue;
- }
-
-
- if (vty->status == VTY_MORE)
- {
- switch (buf[i])
- {
- case CONTROL('C'):
- case 'q':
- case 'Q':
- vty_buffer_reset (vty);
- break;
-#if 0 /* More line does not work for "show ip bgp". */
- case '\n':
- case '\r':
- vty->status = VTY_MORELINE;
- break;
-#endif
- default:
- break;
- }
- continue;
- }
-
- /* Escape character. */
- if (vty->escape == VTY_ESCAPE)
- {
- vty_escape_map (buf[i], vty);
- continue;
- }
-
- /* Pre-escape status. */
- if (vty->escape == VTY_PRE_ESCAPE)
- {
- switch (buf[i])
- {
- case '[':
- vty->escape = VTY_ESCAPE;
- break;
- case 'b':
- vty_backward_word (vty);
- vty->escape = VTY_NORMAL;
- break;
- case 'f':
- vty_forward_word (vty);
- vty->escape = VTY_NORMAL;
- break;
- case 'd':
- vty_forward_kill_word (vty);
- vty->escape = VTY_NORMAL;
- break;
- case CONTROL('H'):
- case 0x7f:
- vty_backward_kill_word (vty);
- vty->escape = VTY_NORMAL;
- break;
- default:
- vty->escape = VTY_NORMAL;
- break;
- }
- continue;
- }
-
- switch (buf[i])
- {
- case CONTROL('A'):
- vty_beginning_of_line (vty);
- break;
- case CONTROL('B'):
- vty_backward_char (vty);
- break;
- case CONTROL('C'):
- vty_stop_input (vty);
- break;
- case CONTROL('D'):
- vty_delete_char (vty);
- break;
- case CONTROL('E'):
- vty_end_of_line (vty);
- break;
- case CONTROL('F'):
- vty_forward_char (vty);
- break;
- case CONTROL('H'):
- case 0x7f:
- vty_delete_backward_char (vty);
- break;
- case CONTROL('K'):
- vty_kill_line (vty);
- break;
- case CONTROL('N'):
- vty_next_line (vty);
- break;
- case CONTROL('P'):
- vty_previous_line (vty);
- break;
- case CONTROL('T'):
- vty_transpose_chars (vty);
- break;
- case CONTROL('U'):
- vty_kill_line_from_beginning (vty);
- break;
- case CONTROL('W'):
- vty_backward_kill_word (vty);
- break;
- case CONTROL('Z'):
- vty_end_config (vty);
- break;
- case '\n':
- case '\r':
- uty_out (vty, "%s", VTY_NEWLINE);
- vty_execute (vty);
- break;
- case '\t':
- vty_complete_command (vty);
- break;
- case '?':
- if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
- vty_self_insert (vty, buf[i]);
- else
- vty_describe_command (vty);
- break;
- case '\033':
- if (i + 1 < nbytes && buf[i + 1] == '[')
- {
- vty->escape = VTY_ESCAPE;
- i++;
- }
- else
- vty->escape = VTY_PRE_ESCAPE;
- break;
- default:
- if (buf[i] > 31 && buf[i] < 127)
- vty_self_insert (vty, buf[i]);
- break;
- }
+ getcwd (cwd, sizeof(cwd)) ;
+ tmp = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (config_file) + 2) ;
+ sprintf (tmp, "%s/%s", cwd, config_file);
+ fullpath = tmp;
}
-
- /* Check status. */
- if (vty->status == VTY_CLOSE)
- uty_close (vty);
else
{
- vty_event (VTY_WRITE, vty_sock, vty);
- vty_event (VTY_READ, vty_sock, vty);
- }
+ tmp = NULL ;
+ fullpath = config_file;
+ } ;
- 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);
+ /* try to open the configuration file */
+ confp = fopen (fullpath, "r");
- /* Temporary disable read thread. */
- if ((vty->lines == 0))
+ if (confp == NULL)
{
- 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->t_write = NULL;
-
- /* Temporary disable read thread. */
- if ((vty->lines == 0) && vty->t_read)
- {
- thread_cancel (vty->t_read);
- vty->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->status == VTY_MORE || vty->status == VTY_MORELINE));
-
- /* N.B. if width is 0, that means we don't know the window size. */
- if ((vty->lines == 0) || (vty->width == 0))
- flushrc = buffer_flush_available(vty->obuf, vty->fd);
- else if (vty->status == VTY_MORELINE)
- flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
- 1, erase, 0);
- else
- flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width,
- vty->lines >= 0 ? vty->lines :
- vty->height,
- erase, 0);
- switch (flushrc)
- {
- case BUFFER_ERROR:
- vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- uzlog(NULL, LOG_WARNING, "buffer_flush failed on vty client fd %d, closing",
- vty->fd);
- buffer_reset(vty->obuf);
- uty_close(vty);
- break;
- case BUFFER_EMPTY:
- if (vty->status == VTY_CLOSE)
- uty_close (vty);
- else
- {
- vty->status = VTY_NORMAL;
- if (vty->lines == 0)
- vty_event (VTY_READ, vty_sock, vty);
- }
- break;
- case BUFFER_PENDING:
- /* There is more data waiting to be written. */
- vty->status = VTY_MORE;
- if (vty->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;
+ fprintf (stderr, "%s: failed to open configuration file %s: %s\n",
+ __func__, fullpath, safe_strerror (errno));
- ASSERTLOCKED
-
- /* Allocate new vty structure and set up default values. */
- vty = vty_new (vty_sock, VTY_TERM);
- vty->address = sockunion_su2str (su);
- if (no_password_check)
- {
- if (restricted_mode)
- vty->node = RESTRICTED_NODE;
- else if (host.advanced)
- vty->node = ENABLE_NODE;
+ confp = vty_use_backup_config (fullpath);
+ if (confp)
+ fprintf (stderr, "WARNING: using backup configuration file!\n");
else
- vty->node = VIEW_NODE;
- }
- else
- vty->node = AUTH_NODE;
- vty->fail = 0;
- vty->cp = 0;
- vty_clear_buf (vty);
- vty->length = 0;
- memset (vty->hist, 0, sizeof (vty->hist));
- vty->hp = 0;
- vty->hindex = 0;
- vector_set_index (vtyvec, vty_sock, vty);
- vty->status = VTY_NORMAL;
- vty->v_timeout = vty_timeout_val;
- if (host.lines >= 0)
- vty->lines = host.lines;
- else
- vty->lines = -1;
- vty->iac = 0;
- vty->iac_sb_in_progress = 0;
- vty->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_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
- vty->status = VTY_CLOSE;
- uty_close (vty);
- return NULL;
- }
- }
-
- /* Say hello to the world. */
- uty_hello (vty);
- if (! no_password_check)
- uty_out (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",
- (bufp = sockunion_su2str (&su)));
- 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;
+ fprintf (stderr, "can't open backup configuration file [%s%s]\n",
+ fullpath, CONF_BACKUP_EXT);
+ exit(1);
}
+ } ;
- 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;
+#ifdef QDEBUG
+ fprintf(stderr, "Reading config file: %s\n", fullpath);
#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->fd = sock;
- vty->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->obuf, vty->fd))
- {
- case BUFFER_PENDING:
- vty_event(VTYSH_WRITE, vty->fd, vty);
- break;
- case BUFFER_ERROR:
- vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- uzlog(NULL, LOG_WARNING, "%s: write error to fd %d, closing", __func__, vty->fd);
- buffer_reset(vty->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;
+ vty_read_file (confp, after_first_cmd);
+ fclose (confp);
- LOCK
+ host_config_set (fullpath);
- /* is this necessary? */
- qps_disable_modes(qf, qps_read_mbit);
- utysh_read(vty, vty_soc);
+#ifdef QDEBUG
+ fprintf(stderr, "Finished reading config file\n");
+#endif
- UNLOCK
+ if (tmp)
+ XFREE (MTYPE_TMP, tmp);
}
-/* Callback: threads. Read data via vty socket. */
-static int
-vtysh_read (struct thread *thread)
+/*------------------------------------------------------------------------------
+ * Try to use a backup configuration file.
+ *
+ * Having failed to open the file "<fullpath>", if there is a file called
+ * "<fullpath>.sav" that can be opened for reading, then:
+ *
+ * - make a copy of that file
+ * - call it "<fullpath>"
+ * - return an open FILE
+ *
+ * Returns: NULL => no "<fullpath>.sav", or faild doing any of the above
+ * otherwise, returns FILE* for open file.
+ */
+static FILE *
+vty_use_backup_config (char *fullpath)
{
- int vty_sock = THREAD_FD (thread);
- struct vty *vty = THREAD_ARG (thread);
- int result;
-
- LOCK
-
- vty->t_read = NULL;
- result = uty_read(vty, vty_soc);
-
- UNLOCK
- return result;
-}
+ char *tmp_path ;
+ struct stat buf;
+ int ret, tmp, sav;
+ int c;
+ char buffer[4096] ;
-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};
+ enum { xl = 32 } ;
+ tmp_path = malloc(strlen(fullpath) + xl) ;
- 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->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->obuf);
- uty_close (vty);
-#ifdef VTYSH_DEBUG
- printf ("close vtysh\n");
-#endif /* VTYSH_DEBUG */
- return 0;
- }
+ /* construct the name "<fullname>.sav", and try to open it. */
+ confirm(xl > sizeof(CONF_BACKUP_EXT)) ;
+ sprintf (tmp_path, "%s%s", fullpath, CONF_BACKUP_EXT) ;
-#ifdef VTYSH_DEBUG
- printf ("line: %.*s\n", nbytes, buf);
-#endif /* VTYSH_DEBUG */
+ sav = -1 ;
+ if (stat (tmp_path, &buf) != -1)
+ sav = open (tmp_path, O_RDONLY);
- for (p = buf; p < buf+nbytes; p++)
+ if (sav < 0)
{
- vty_ensure(vty, vty->length+1);
- vty->buf[vty->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->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->obuf, header, 4);
-
- if (!vty->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->t_write = NULL;
- vtysh_flush(vty);
-
- UNLOCK
- return 0;
-}
+ free (tmp_path);
+ return NULL;
+ } ;
-#endif /* VTYSH */
+ /* construct a temporary file and copy "<fullpath.sav>" to it. */
+ confirm(xl > sizeof(".XXXXXX"))
+ sprintf (tmp_path, "%s%s", fullpath, ".XXXXXX") ;
-/* 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)
+ /* Open file to configuration write. */
+ tmp = mkstemp (tmp_path);
+ if (tmp < 0)
{
-
-#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 */
+ free (tmp_path);
+ close(sav);
+ return NULL;
}
-#ifdef VTYSH
- vty_serv_un (path);
-#endif /* VTYSH */
-
- UNLOCK
-}
+ while((c = read (sav, buffer, sizeof(buffer))) > 0)
+ write (tmp, buffer, c);
-/* 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
-}
+ close (sav);
+ close (tmp);
-static void
-uty_close (struct vty *vty)
-{
- int i;
-
- ASSERTLOCKED
-
- /* Cancel threads.*/
- if (vty->t_read)
- thread_cancel (vty->t_read);
- if (vty->t_write)
- thread_cancel (vty->t_write);
- if (vty->t_timeout)
- thread_cancel (vty->t_timeout);
- if (vty->qf)
- {
- qps_remove_file(vty->qf);
- qps_file_free(vty->qf);
- vty->qf = NULL;
- }
- if (vty->qtr)
+ /* Make sure that have the required file status */
+ if (chmod(tmp_path, CONFIGFILE_MASK) != 0)
{
- qtimer_free(vty->qtr);
- vty->qtr = NULL;
+ unlink (tmp_path);
+ free (tmp_path);
+ return NULL;
}
- /* Flush buffer. */
- buffer_flush_all (vty->obuf, vty->fd);
-
- /* Free input buffer. */
- buffer_free (vty->obuf);
-
- /* Free command history. */
- for (i = 0; i < VTY_MAXHIST; i++)
- if (vty->hist[i])
- XFREE (MTYPE_VTY_HIST, vty->hist[i]);
-
- /* Unset vector. */
- vector_unset (vtyvec, vty->fd);
-
- /* Close socket. */
- if (vty->fd > 0)
- close (vty->fd);
-
- if (vty->address)
- XFREE (MTYPE_TMP, vty->address);
- if (vty->buf)
- XFREE (MTYPE_VTY, vty->buf);
-
- /* 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->t_timeout = NULL;
- result = uty_timeout(vty);
- UNLOCK
- return result;
-}
-
-static int
-uty_timeout (struct vty *vty)
-{
- vty->v_timeout = 0;
-
- /* Clear buffer*/
- buffer_reset (vty->obuf);
- uty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
+ /* Make <fullpath> be a name for the new file just created. */
+ ret = link (tmp_path, fullpath) ;
- /* Close connection. */
- vty->status = VTY_CLOSE;
- uty_close (vty);
+ /* Discard the temporary, now */
+ unlink (tmp_path) ;
+ free (tmp_path) ;
- return 0;
-}
+ /* If link was successful, try to open -- otherwise, failed. */
+ return (ret == 0) ? fopen (fullpath, "r") : NULL ;
+} ;
-/* Read up configuration file from file_name. */
+/*------------------------------------------------------------------------------
+ * Read the given configuration file.
+ *
+ * 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.
+ */
static void
vty_read_file (FILE *confp, void (*after_first_cmd)(void))
{
int ret;
struct vty *vty;
+ /* TODO: sort out what VTY Type should use for reading config file */
vty = vty_new (0, VTY_TERM); /* stdout */
vty->node = CONFIG_NODE;
- /* Execute configuration file */
- ret = config_from_file (vty, confp, after_first_cmd);
+ /* Make sure we have a suitable buffer, and set vty->buf to point at
+ * it -- same like other command execution.
+ */
+ qs_need(&vty->vio->clx, VTY_BUFSIZ) ;
+ vty->buf = qs_chars(&vty->vio->clx) ;
+
+ /* Execute configuration file */
+ ret = config_from_file (vty, confp, after_first_cmd, &vty->vio->clx) ;
- LOCK
+ VTY_LOCK() ;
if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
{
@@ -2675,219 +1031,13 @@ vty_read_file (FILE *confp, void (*after_first_cmd)(void))
fprintf (stderr, "There is no such command.\n");
break;
}
- fprintf (stderr, "Error occured during reading below line.\n%s\n",
- vty->buf);
- 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
- */
+ fprintf (stderr, "Error occurred while processing:\n%s\n", vty->buf);
- 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;
+ exit (1);
}
- 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);
-}
-
-/* Small utility function which output log to the VTY. */
-void
-vty_log (const char *level, const char *proto_str,
- const char *format, struct timestamp_control *ctl, va_list va)
-{
- unsigned int i;
- struct vty *vty;
-
- ASSERTLOCKED
-
- if (!vtyvec)
- return;
-
- for (i = 0; i < vector_active (vtyvec); i++)
- if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
- {
- va_list ac;
- va_copy(ac, va);
- vty_log_out (vty, level, proto_str, format, ctl, ac);
- va_end(ac);
- }
+ uty_half_close (vty->vio);
+ VTY_UNLOCK() ;
}
#ifdef QDEBUG
@@ -2898,238 +1048,120 @@ vty_goodbye (void)
unsigned int i;
struct vty *vty;
- LOCK
+ VTY_LOCK() ;
if (vtyvec)
{
for (i = 0; i < vector_active (vtyvec); i++)
{
- if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->type == VTY_TERM)
+ if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->vio->type == VTY_TERM)
{
- uty_out(vty, QUAGGA_PROGNAME " is shutting down%s", VTY_NEWLINE);
+ uty_cout(vty, QUAGGA_PROGNAME " is shutting down%s", VTY_NEWLINE);
/* Wake up */
- if (cli_nexus)
- vty_event (VTY_WRITE, vty->fd, vty);
+ if (vty_cli_nexus)
+ vty_event (VTY_WRITE, vty->vio->fd, vty);
}
}
if (qpthreads_enabled)
- qpt_thread_signal(cli_nexus->thread_id, SIGMQUEUE);
+ qpt_thread_signal(vty_cli_nexus->thread_id, SIGMQUEUE);
}
- UNLOCK
+ VTY_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;
+/*==============================================================================
+ * Configuration node/state handling
+ *
+ * At most one VTY may hold the configuration symbol of power at any time.
+ */
- iov[0].iov_base = miyagi(buf) ;
- iov[0].iov_len = len;
- iov[1].iov_base = miyagi("\r\n") ;
- iov[1].iov_len = 2;
+/*------------------------------------------------------------------------------
+ * Attempt to gain the configuration symbol of power
+ *
+ * If succeeds, set the given node.
+ *
+ * Returns: true <=> now own the symbol of power.
+ */
+extern bool
+vty_config_lock (struct vty *vty, enum node_type node)
+{
+ bool result;
- for (i = 0; i < vector_active (vtyvec); i++)
- {
- struct vty *vty;
- if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor)
- /* N.B. We don't care about the return code, since process is
- most likely just about to die anyway. */
- writev(vty->fd, iov, 2);
- }
-}
+ VTY_LOCK() ;
-int
-vty_config_lock (struct vty *vty)
-{
- int result;
- LOCK
if (vty_config == 0)
{
- vty->config = 1;
- vty_config = 1;
- }
- result = vty->config;
- UNLOCK
- return result;
-}
+ vty->vio->config = 1 ;
+ vty_config = 1 ;
+ } ;
+
+ result = vty->vio->config;
+
+ if (result)
+ vty->node = node ;
+
+ VTY_UNLOCK() ;
-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)
+/*------------------------------------------------------------------------------
+ * Give back the configuration symbol of power -- if own it.
+ *
+ * Set the given node -- which must be <= MAX_NON_CONFIG_NODE
+ */
+extern void
+vty_config_unlock (struct vty *vty, enum node_type node)
{
- ASSERTLOCKED
- if (vty_config == 1 && vty->config == 1)
- {
- vty->config = 0;
- vty_config = 0;
- }
- return vty->config;
+ VTY_LOCK() ;
+ uty_config_unlock(vty, node);
+ VTY_UNLOCK() ;
}
-static void
-vty_event (enum event event, int sock, struct vty *vty)
+/*------------------------------------------------------------------------------
+ * Give back the configuration symbol of power -- if own it.
+ *
+ * Set the given node -- which must be <= MAX_NON_CONFIG_NODE
+ */
+extern void
+uty_config_unlock (struct vty *vty, enum node_type node)
{
- 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 event event, int sock, struct vty *vty)
- {
- struct thread *vty_serv_thread;
-
- ASSERTLOCKED
-
- switch (event)
+ VTY_ASSERT_LOCKED() ;
+ if ((vty_config == 1) && (vty->vio->config == 1))
{
- 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->t_read = thread_add_read (master, vtysh_read, vty, sock);
- break;
- case VTYSH_WRITE:
- vty->t_write = thread_add_write (master, vtysh_write, vty, sock);
- break;
-#endif /* VTYSH */
- case VTY_READ:
- vty->t_read = thread_add_read (master, vty_read, vty, sock);
-
- /* Time out treatment. */
- if (vty->v_timeout)
- {
- if (vty->t_timeout)
- thread_cancel (vty->t_timeout);
- vty->t_timeout =
- thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
- }
- break;
- case VTY_WRITE:
- if (! vty->t_write)
- vty->t_write = thread_add_write (master, vty_flush, vty, sock);
- break;
- case VTY_TIMEOUT_RESET:
- if (vty->t_timeout)
- {
- thread_cancel (vty->t_timeout);
- vty->t_timeout = NULL;
- }
- if (vty->v_timeout)
- {
- vty->t_timeout =
- thread_add_timer (master, vty_timeout, vty, vty->v_timeout);
- }
- break;
+ vty->vio->config = 0;
+ vty_config = 0;
}
-}
-
-/* qpthreads event setter */
-static void
-vty_event_r (enum event event, int sock, struct vty *vty)
- {
- qps_file accept_file = NULL;
-
- ASSERTLOCKED
-
- switch (event)
- {
- case VTY_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(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->file, qps_read_mnum, vtysh_read_r) ;
- break;
- case VTYSH_WRITE:
- qps_enable_mode(vty->file, qps_write_mnum, vtysh_write_r) ;
- break;
-#endif /* VTYSH */
- case VTY_READ:
- qps_enable_mode(vty->qf, qps_read_mnum, vty_read_r) ;
-
- /* Time out treatment. */
- if (vty->v_timeout)
- {
- qtimer_set(vty->qtr, qt_add_monotonic(QTIME(vty->v_timeout)), NULL) ;
- }
- break;
- case VTY_WRITE:
- qps_enable_mode(vty->qf, qps_write_mnum, vty_flush_r) ;
- break;
- case VTY_TIMEOUT_RESET:
- if (vty->qtr == NULL)
- break;
- if (vty->v_timeout)
- {
- qtimer_set(vty->qtr, qt_add_monotonic(QTIME(vty->v_timeout)), NULL) ;
- }
- else
- {
- qtimer_unset(vty->qtr);
- }
- break;
- }
-}
+ assert(node <= MAX_NON_CONFIG_NODE) ;
+ vty->node = node ;
+} ;
+/*==============================================================================
+ * Commands
+ *
+ */
DEFUN_CALL (config_who,
config_who_cmd,
"who",
"Display who is on vty\n")
{
- unsigned int i;
- struct vty *v;
+ unsigned int i = 0;
+ vty_io vio ;
- LOCK
- for (i = 0; i < vector_active (vtyvec); i++)
- if ((v = vector_slot (vtyvec, i)) != NULL)
+ VTY_LOCK() ;
+
+ vio = vio_list_base ;
+ while (vio != NULL) /* TODO: show only VTY_TERM ??? */
+ {
uty_out (vty, "%svty[%d] connected from %s.%s",
- v->config ? "*" : " ",
- i, v->address, VTY_NEWLINE);
- UNLOCK
+ vio->config ? "*" : " ",
+ i, uty_get_name(vio), VTY_NEWLINE);
+ vio = sdl_next(vio, vio_list) ;
+ } ;
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3140,9 +1172,9 @@ DEFUN_CALL (line_vty,
"Configure a terminal line\n"
"Virtual terminal\n")
{
- LOCK
+ VTY_LOCK() ;
vty->node = VTY_NODE;
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3152,7 +1184,7 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
{
unsigned long timeout = 0;
- LOCK
+ VTY_LOCK() ;
/* min_str and sec_str are already checked by parser. So it must be
all digit string. */
@@ -3165,10 +1197,10 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
timeout += strtol (sec_str, NULL, 10);
vty_timeout_val = timeout;
- vty->v_timeout = timeout;
- vty_event (VTY_TIMEOUT_RESET, 0, vty);
+ vty->vio->file.v_timeout = timeout;
+// vty_event (VTY_TIMEOUT_RESET, 0, vty);
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3207,14 +1239,14 @@ DEFUN_CALL (vty_access_class,
"Filter connections based on an IP access list\n"
"IP access list\n")
{
- LOCK
+ VTY_LOCK() ;
if (vty_accesslist_name)
XFREE(MTYPE_VTY, vty_accesslist_name);
vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3228,7 +1260,7 @@ DEFUN_CALL (no_vty_access_class,
{
int result = CMD_SUCCESS;
- LOCK
+ VTY_LOCK() ;
if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0])))
{
uty_out (vty, "Access-class is not currently applied to vty%s",
@@ -3241,7 +1273,7 @@ DEFUN_CALL (no_vty_access_class,
vty_accesslist_name = NULL;
}
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
@@ -3254,13 +1286,13 @@ DEFUN_CALL (vty_ipv6_access_class,
"Filter connections based on an IP access list\n"
"IPv6 access list\n")
{
- LOCK
+ VTY_LOCK() ;
if (vty_ipv6_accesslist_name)
XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]);
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3275,7 +1307,7 @@ DEFUN_CALL (no_vty_ipv6_access_class,
{
int result = CMD_SUCCESS;
- LOCK
+ VTY_LOCK() ;
if (! vty_ipv6_accesslist_name ||
(argc && strcmp(vty_ipv6_accesslist_name, argv[0])))
@@ -3291,7 +1323,7 @@ DEFUN_CALL (no_vty_ipv6_access_class,
vty_ipv6_accesslist_name = NULL;
}
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
#endif /* HAVE_IPV6 */
@@ -3302,9 +1334,9 @@ DEFUN_CALL (vty_login,
"login",
"Enable password checking\n")
{
- LOCK
+ VTY_LOCK() ;
no_password_check = 0;
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3314,9 +1346,9 @@ DEFUN_CALL (no_vty_login,
NO_STR
"Enable password checking\n")
{
- LOCK
+ VTY_LOCK() ;
no_password_check = 1;
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3326,9 +1358,9 @@ DEFUN_CALL (vty_restricted_mode,
"anonymous restricted",
"Restrict view commands available in anonymous, unauthenticated vty\n")
{
- LOCK
+ VTY_LOCK() ;
restricted_mode = 1;
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3338,9 +1370,9 @@ DEFUN_CALL (vty_no_restricted_mode,
NO_STR
"Enable password checking\n")
{
- LOCK
+ VTY_LOCK() ;
restricted_mode = 0;
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3350,9 +1382,9 @@ DEFUN_CALL (service_advanced_vty,
"Set up miscellaneous service\n"
"Enable advanced mode vty interface\n")
{
- LOCK
+ VTY_LOCK() ;
host.advanced = 1;
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3363,9 +1395,9 @@ DEFUN_CALL (no_service_advanced_vty,
"Set up miscellaneous service\n"
"Enable advanced mode vty interface\n")
{
- LOCK
+ VTY_LOCK() ;
host.advanced = 0;
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3375,9 +1407,9 @@ DEFUN_CALL (terminal_monitor,
"Set terminal line parameters\n"
"Copy debug output to the current terminal line\n")
{
- LOCK
- vty->monitor = 1;
- UNLOCK
+ VTY_LOCK() ;
+ uty_set_monitor(vty->vio, true);
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3388,9 +1420,9 @@ DEFUN_CALL (terminal_no_monitor,
NO_STR
"Copy debug output to the current terminal line\n")
{
- LOCK
- vty->monitor = 0;
- UNLOCK
+ VTY_LOCK() ;
+ uty_set_monitor(vty->vio, false);
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
@@ -3409,27 +1441,34 @@ DEFUN_CALL (show_history,
{
int index;
- LOCK
+ VTY_LOCK() ;
- for (index = vty->hindex + 1; index != vty->hindex;)
+ for (index = vty->vio->hindex + 1; index != vty->vio->hindex;)
{
+ const char* line ;
+
if (index == VTY_MAXHIST)
{
index = 0;
continue;
}
- if (vty->hist[index] != NULL)
- uty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
+ line = vector_get_item(&vty->vio->hist, index) ;
+ if (line != NULL)
+ uty_out (vty, " %s%s", line, VTY_NEWLINE);
index++;
}
- UNLOCK
+ VTY_UNLOCK() ;
return CMD_SUCCESS;
}
-/* Display current configuration. */
+/*==============================================================================
+ * Output the current configuration
+ *
+ * Returns: CMD_SUCCESS
+ */
static int
vty_config_write (struct vty *vty)
{
@@ -3466,76 +1505,16 @@ vty_config_write (struct vty *vty)
return CMD_SUCCESS;
}
-struct cmd_node vty_node =
-{
- VTY_NODE,
- "%s(config-line)# ",
- 1,
-};
-
-/* Reset all VTY status. */
-void
-vty_reset ()
-{
- LOCK
- uty_reset();
- UNLOCK
-}
-
-void
-uty_reset ()
-{
- unsigned int i;
- struct vty *vty;
- struct thread *vty_serv_thread;
- qps_file qf;
-
- for (i = 0; i < vector_active (vtyvec); i++)
- if ((vty = vector_slot (vtyvec, i)) != NULL)
- {
- buffer_reset (vty->obuf);
- vty->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;
- }
-}
+/*==============================================================================
+ * The cwd at start-up.
+ */
+/*------------------------------------------------------------------------------
+ * Save cwd
+ *
+ * This is done early in the morning so that any future operations on files
+ * can use the original cwd if required.
+ */
static void
vty_save_cwd (void)
{
@@ -3550,140 +1529,107 @@ vty_save_cwd (void)
getcwd (cwd, MAXPATHLEN);
}
- vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1);
- strcpy (vty_cwd, cwd);
-}
+ vty_cwd = XSTRDUP(MTYPE_TMP, cwd) ;
+} ;
+/*------------------------------------------------------------------------------
+ * Get cwd as at start-up. Never changed -- so no locking required.
+ */
char *
vty_get_cwd ()
{
return vty_cwd;
}
+/*==============================================================================
+ * Access functions for VTY values, where locking is or might be required.
+ */
+
int
vty_shell (struct vty *vty)
{
- LOCK
+ VTY_LOCK() ;
int result;
- result = uty_shell (vty);
- UNLOCK
+ result = (vty->vio->type == VTY_SHELL) ? 1 : 0 ;
+ VTY_UNLOCK() ;
return result;
}
-static int
-uty_shell (struct vty *vty)
-{
- return ((vty == NULL) || (vty->type == VTY_SHELL)) ? 1 : 0;
-}
-
int
vty_shell_serv (struct vty *vty)
{
- LOCK
+ VTY_LOCK() ;
int result;
- result = ((vty->type == VTY_SHELL_SERV) ? 1 : 0);
- UNLOCK
+ result = ((vty->vio->type == VTY_SHELL_SERV) ? 1 : 0);
+ VTY_UNLOCK() ;
return result;
}
-void
-vty_init_vtysh ()
-{
- LOCK
- vtyvec = vector_init (0);
- UNLOCK
-}
-
int
vty_get_node(struct vty *vty)
{
int result;
- LOCK
+ VTY_LOCK() ;
result = vty->node;
- UNLOCK
+ VTY_UNLOCK() ;
return result;
}
void
vty_set_node(struct vty *vty, int node)
{
- LOCK
+ VTY_LOCK() ;
vty->node = node;
- UNLOCK
+ VTY_UNLOCK() ;
}
int
vty_get_type(struct vty *vty)
{
int result;
- LOCK
- result = vty->type;
- UNLOCK
+ VTY_LOCK() ;
+ result = vty->vio->type;
+ VTY_UNLOCK() ;
return result;
}
int
-vty_get_status(struct vty *vty)
-{
- int result;
- LOCK
- result = vty->status;
- UNLOCK
- return result;
-}
-
-void
-vty_set_status(struct vty *vty, int status)
-{
- LOCK
- vty->status = status;
- UNLOCK
-}
-
-int
vty_get_lines(struct vty *vty)
{
int result;
- LOCK
- result = vty->lines;
- UNLOCK
+ VTY_LOCK() ;
+ result = vty->vio->lines;
+ VTY_UNLOCK() ;
return result;
}
void
vty_set_lines(struct vty *vty, int lines)
{
- LOCK
- vty->lines = lines;
- UNLOCK
+ VTY_LOCK() ;
+ vty->vio->lines = lines;
+ VTY_UNLOCK() ;
}
-/* qpthreads: Install vty's own commands like `who' command. */
-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);
-}
+/*==============================================================================
+ * The VTY command nodes
+ */
-/* threads: Install vty's own commands like `who' command. */
-void
-vty_init (struct thread_master *master_thread)
+struct cmd_node vty_node =
{
- LOCK
-
- /* For further configuration read, preserve current directory. */
- vty_save_cwd ();
-
- vtyvec = vector_init (0);
-
- master = master_thread;
+ VTY_NODE,
+ "%s(config-line)# ",
+ 1,
+};
- /* Initilize server thread vector. */
- Vvty_serv_thread = vector_init (0);
+/*------------------------------------------------------------------------------
+ * Install vty's own commands like `who' command.
+ */
+static void
+uty_init_commands (void)
+{
+ VTY_ASSERT_LOCKED() ;
- /* Install bgp top node. */
install_node (&vty_node, vty_config_write);
install_element (RESTRICTED_NODE, &config_who_cmd);
@@ -3714,29 +1660,4 @@ vty_init (struct thread_master *master_thread)
install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
#endif /* HAVE_IPV6 */
-
- UNLOCK
-}
-
-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.c.x b/lib/vty.c.x
new file mode 100644
index 00000000..bed6fc28
--- /dev/null
+++ b/lib/vty.c.x
@@ -0,0 +1,4414 @@
+/*
+ * 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 ccb325d1..f43ad59e 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -1,141 +1,162 @@
-/* Virtual terminal [aka TeletYpe] interface routine
- Copyright (C) 1997 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. */
+/* VTY top level
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: 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_VTY_H
#define _ZEBRA_VTY_H
+#include <stdbool.h>
+
#include "thread.h"
#include "log.h"
#include "qpthreads.h"
#include "qpselect.h"
#include "qtimers.h"
#include "qpnexus.h"
+#include "list_util.h"
+#include "node_type.h"
-#define VTY_BUFSIZ 512
-#define VTY_MAXHIST 20
-
-/* VTY struct. */
-struct vty
-{
- /* File descriptor of this vty. */
- int fd;
-
- /* Is this vty connect to file or not */
- enum {VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV} type;
-
- /* Node status of this vty */
- int node;
-
- /* What address is this vty coming from. */
- char *address;
-
- /* Failure count */
- int fail;
-
- /* Output buffer. */
- struct buffer *obuf;
-
- /* Command input buffer */
- char *buf;
-
- /* Command cursor point */
- int cp;
-
- /* Command length */
- int length;
-
- /* Command max length. */
- int max;
-
- /* Histry of command */
- char *hist[VTY_MAXHIST];
-
- /* History lookup current point */
- int hp;
-
- /* History insert end point */
- int hindex;
-
- /* For current referencing point of interface, route-map,
- access-list etc... */
- void *index;
-
- /* For multiple level index treatment such as key chain and key. */
- void *index_sub;
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
- /* For escape character. */
- unsigned char escape;
+/*==============================================================================
+ * The VTYSH uses a unix socket to talk to the daemon.
+ *
+ * The ability to respond to a connection from VTYSH appears to be a *compile*
+ * time option. In the interests of keeping the code up to date, the VTYSH
+ * option is turned into a testable constant.
+ */
+#ifdef VTYSH
+# define VTYSH_DEFINED 1
+#else
+# define VTYSH_DEFINED 0
+#endif
- /* Current vty status. */
- enum {VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE} status;
+enum { VTYSH_ENABLED = VTYSH_DEFINED } ;
- /* IAC handling: was the last character received the
- IAC (interpret-as-command) escape character (and therefore the next
- character will be the command code)? Refer to Telnet RFC 854. */
- unsigned char iac;
+#undef VTYSH_DEFINED
- /* IAC SB (option subnegotiation) handling */
- unsigned char iac_sb_in_progress;
- /* At the moment, we care only about the NAWS (window size) negotiation,
- and that requires just a 5-character buffer (RFC 1073):
- <NAWS char> <16-bit width> <16-bit height> */
-#define TELNET_NAWS_SB_LEN 5
- unsigned char sb_buf[TELNET_NAWS_SB_LEN];
- /* How many subnegotiation characters have we received? We just drop
- those that do not fit in the buffer. */
- size_t sb_len;
+/*==============================================================================
+ * VTY struct.
+ */
- /* Window width/height. */
- int width;
- int height;
+typedef struct vty_io* vty_io ; /* private to vty.c */
- /* Configure lines. */
- int lines;
+struct vty
+{
+ /*----------------------------------------------------------------------
+ * The following are used outside vty.c, and represent the context
+ * in which commands are executed.
+ */
+
+ /* Node status of this vty
+ *
+ * NB: when using qpthreads should lock VTY to access this -- so use
+ * vty_get_node() and vty_set_node().
+ */
+ 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 ;
+
+ /* For current referencing point of interface, route-map, access-list
+ * etc...
+ *
+ * NB: this value is private to the command execution, which is assumed
+ * to all be in the one thread... so no lock required.
+ */
+ void *index ;
+
+ /* For multiple level index treatment such as key chain and key.
+ *
+ * NB: this value is private to the command execution, which is assumed
+ * to all be in the one thread... so no lock required.
+ */
+ void *index_sub ;
+
+ /* String which is newline... read only -- no locking */
+ const char* newline ;
+
+ /*----------------------------------------------------------------------
+ * The following are used inside vty.c only.
+ */
+
+ /* Pointer to related vty_io structure -- if any. */
+ vty_io vio ;
+};
- /* Terminal monitor. */
- int monitor;
+/*------------------------------------------------------------------------------
+ * VTY events
+ */
+enum vty_event
+{
+ VTY_SERV,
+ VTY_READ,
+ VTY_WRITE,
+ VTY_TIMEOUT_RESET,
+
+ VTYSH_SERV,
+ VTYSH_READ,
+ VTYSH_WRITE
+};
- /* In configure mode. */
- int config;
+/*------------------------------------------------------------------------------
+ * VTY Types
+ */
+enum vty_type
+{
+ VTY_TERM, /* a telnet session -- input and output */
+ VTY_FILE, /* writing config file -- output is to file
+ -- no input */
- /* Read and write thread. */
+ VTY_STDOUT, /* reading config file -- output is to stdout
+ -- no input */
- qps_file qf;
- struct thread *t_read;
- struct thread *t_write;
+ VTY_SHELL, /* vty in vtysh -- output is to stdout */
+ VTY_SHELL_SERV /* vty in daemon -- input and output */
+} ;
- /* Timeout seconds and thread. */
- unsigned long v_timeout;
- qtimer qtr;
- struct thread *t_timeout;
-};
+/*------------------------------------------------------------------------------
+ *
+ */
+#define VTY_BUFSIZ 512
+#define VTY_MAXHIST 20
/* Integrated configuration file. */
#define INTEGRATE_DEFAULT_CONFIG "Quagga.conf"
/* Small macro to determine newline is newline only or linefeed needed. */
-#define VTY_NEWLINE (((vty != NULL) && (vty->type == VTY_TERM)) ? "\r\n" : "\n")
+#define VTY_NEWLINE (((vty) != NULL) ? (vty)->newline : "\n")
/* For indenting, mostly. */
-extern const char* vty_spaces_string ;
-#define VTY_MAX_SPACES 24
+extern const char vty_spaces_string[] ;
+enum { VTY_MAX_SPACES = 40 } ;
#define VTY_SPACES(n) (vty_spaces_string + ((n) < VTY_MAX_SPACES \
? VTY_MAX_SPACES - (n) : 0))
@@ -210,40 +231,37 @@ do {
} \
} while (0)
-/* Exported variables */
+/*------------------------------------------------------------------------------
+ * Exported variables
+ */
extern char integrate_default[];
-extern qpt_mutex_t vty_mutex;
-#ifndef NDEBUG
-extern int vty_lock_count;
-extern int vty_lock_asserted;
-#endif
-/* Prototypes. */
-extern void vty_init_r (qpn_nexus, qpn_nexus);
-extern void vty_exec_r(void);
+/*------------------------------------------------------------------------------
+ * Prototypes.
+ */
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,
+ const char *path) ;
+extern struct vty* vty_new (int fd, 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 struct vty *vty_new (int, int);
+
extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
-extern int vty_puts(struct vty* vty, const char* str) ;
-extern int vty_out_newline(struct vty *vty) ;
extern int vty_out_indent(struct vty *vty, int indent) ;
+
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 void vty_serv_sock (const char *, unsigned short, const char *);
-extern void vty_close (struct vty *);
+
extern char *vty_get_cwd (void);
-extern void vty_log (const char *level, const char *proto,
- const char *fmt, struct timestamp_control *, va_list);
-extern int vty_config_lock (struct vty *);
-extern int vty_config_unlock (struct vty *);
+
extern int vty_shell (struct vty *);
extern int vty_shell_serv (struct vty *);
extern void vty_hello (struct vty *);
-extern void vty_queued_result(struct vty *, int);
extern int vty_get_node(struct vty *);
extern void vty_set_node(struct vty *, int);
extern int vty_get_type(struct vty *);
@@ -252,10 +270,6 @@ extern void vty_set_status(struct vty *, int);
extern int vty_get_lines(struct vty *);
extern void vty_set_lines(struct vty *, int);
-/* Send a fixed-size message to all vty terminal monitors; this should be
- an async-signal-safe function. */
-extern void vty_log_fixed (const char *buf, size_t len);
-
#ifdef QDEBUG
extern void vty_goodbye (void);
#endif
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
new file mode 100644
index 00000000..de9bb53c
--- /dev/null
+++ b/lib/vty_cli.c
@@ -0,0 +1,2308 @@
+/* VTY Command Line Handler
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: 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 "keystroke.h"
+#include "vty.h"
+#include "uty.h"
+#include "vty_io.h"
+#include "vty_cli.h"
+
+#include "command.h"
+
+#include "memory.h"
+
+/*==============================================================================
+ * Host name handling
+ *
+ * The host name is used in the command line prompt. The name used is either
+ * the name set by "hostname" command, or the current machine host name.
+ *
+ * Static variables -- under the VTY_LOCK !
+ */
+
+static char* vty_host_name = NULL ;
+int vty_host_name_set = 0 ;
+
+static void uty_new_host_name(const char* name) ;
+
+/*------------------------------------------------------------------------------
+ * Update vty_host_name as per "hostname" or "no hostname" command
+ */
+extern void
+uty_set_host_name(const char* name)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ vty_host_name_set = (name != NULL) ;
+
+ if (vty_host_name_set)
+ uty_new_host_name(name) ;
+ else
+ uty_check_host_name() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If vty_host_name is set, free it.
+ */
+extern void
+uty_free_host_name(void)
+{
+ if (vty_host_name != NULL)
+ XFREE (MTYPE_HOST, vty_host_name) ; /* sets vty_host_name = NULL */
+} ;
+
+/*------------------------------------------------------------------------------
+ * If the host name is not set by command, see if the actual host name has
+ * changed, and if so change it.
+ *
+ * This is done periodically in case the actual host name changes !
+ */
+extern void
+uty_check_host_name(void)
+{
+ struct utsname names ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vty_host_name_set)
+ return ; /* nothing to do if set by command */
+
+ uname (&names) ;
+
+ if ((vty_host_name == NULL) || (strcmp(vty_host_name, names.nodename) != 0))
+ uty_new_host_name(names.nodename) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set new vty_host_name and run along list of VTYs to mark the change.
+ */
+static void
+uty_new_host_name(const char* name)
+{
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ uty_free_host_name() ;
+ vty_host_name = XSTRDUP(MTYPE_HOST, name) ;
+
+ vio = vio_list_base ;
+ while (vio != NULL)
+ {
+ vio->cli_prompt_set = 0 ;
+ vio = sdl_next(vio, vio_list) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * General mechanism for command execution.
+ *
+ * Command execution is driven by select/pselect -- which means that the
+ * processing of commands is multiplexed with all other activity. In the
+ * following:
+ *
+ * -- read_ready and write_ready are events signalled by select/pselect
+ *
+ * -- setting read or write on, means enabling the file for select/pselect to
+ * consider it for read_ready or write_ready, respectively.
+ *
+ * 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.
+ *
+ * * on write_ready:
+ *
+ * - empty out the CLI buffer
+ *
+ * - if ! cmd_in_progress:
+ *
+ * * empty out the command buffer
+ *
+ * * if the command buffer is empty, clear cli_blocked (if was set)
+ *
+ * - generate a read_ready event unless write() would block.
+ *
+ * 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
+ *
+ * * clear cmd_in_progress
+ *
+ * * set cli_blocked
+ *
+ * - set write on (or read on) as required.
+ *
+ * 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
+ * invokes read_ready.
+ *
+ * Note also that after each command dispatch the CLI processor exits, to be
+ * re-entered again on write_ready/read_ready -- so does one command line at
+ * a time, yielding the processor after each one.
+ *
+ * Note that select/pselect treat a socket which is at "EOF", or has seen an
+ * error, or has been half closed, etc. as readable and writable. This means
+ * that the CLI will continue to move forward even after the socket is no
+ * longer delivering any data.
+ *
+ *------------------------------------------------------------------------------
+ * The "--more--" handling.
+ *
+ * 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:
+ *
+ * * cmd_in_progress is cleared
+ *
+ * * cli_blocked will be set
+ *
+ * * the line_control structure is reset
+ *
+ * * the output process is kicked off by setting write on
+ *
+ * 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.)
+ *
+ * When all the output has completed, cli_blocked is cleared and the CLI will
+ * be kicked.
+ *
+ * It is expected that the output will end with a newline -- so that when the
+ * CLI is kicked, the cursor will be at the start of an empty line.
+ *
+ * This mechanism means that the command output FIFO only ever contains the
+ * output from the last command executed.
+ *
+ * If the user decides to abandon output at the "--more--" prompt, then the
+ * contents of the command output FIFO are discarded.
+ *
+ *------------------------------------------------------------------------------
+ * The qpthreads/qpnexus extension.
+ *
+ * When running in qnexus mode, many commands are not executed directly in the
+ * CLI, but are queued for execution by the main "routeing" nexus.
+ *
+ * The parsing of commands is context sensitive. The context depends may
+ * change during the execution of a command. So... it is not possible to
+ * dispatch a command until the previous one has completed.
+ *
+ * In qnexus mode, when a command is queued, the CLI does not go cli_blocked,
+ * even if some output has already been generated. This allows a further
+ * command to be entered while the previous one is executed. However, if the
+ * command is dispatched before the previous one completes, then the cli will
+ * block.
+ *
+ * While the previous command is executing, the current command line has a
+ * minimal prompt -- to show that the context is not known. When the previous
+ * command completes, the command line is redrawn in the new context.
+ *
+ *------------------------------------------------------------------------------
+ * Command line drawn state.
+ *
+ * When the cli_drawn flag is set, the current console line contains the
+ * current prompt and the user input to date. The cursor is positioned where
+ * the user last placed it.
+ *
+ * The command line can be "wiped" -- see uty_cli_wipe() -- which removes all
+ * output and prompt, and leaves the console at the start of an empty line
+ * where the command line used to be.
+ *
+ * On entry to the CLI, it will draw the command line again if it has been
+ * wiped.
+ *
+ * This mechanism is used to support the partial command line that may be
+ * entered while a queued command executes.
+ *
+ * It is also used for the command help/completion system.
+ *
+ * It is also used to support the "monitor" output.
+ */
+
+/*==============================================================================
+ * Command Line Interface
+ *
+ * State of the CLI:
+ *
+ * cli_blocked -- a command has been dispatched, and now waiting
+ * for it and/or its output to complete.
+ *
+ * cmd_in_progress -- a command has been dispatched (and may have been
+ * queued).
+ *
+ * Can continue in the CLI until another command is
+ * ready to be executed.
+ */
+
+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) ;
+
+/*------------------------------------------------------------------------------
+ * Run the CLI 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 !
+ */
+extern void
+uty_cli(vty_io vio)
+{
+ bool won ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* cli_blocked is set when is waiting for a command, or its output to
+ * complete.
+ *
+ * There is no good reason to arrive here in that state, and nothing to be
+ * done if that happens.
+ */
+ if (vio->cli_blocked)
+ return ;
+
+ /* 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
+ * something to do, or runs out of input.
+ */
+ if (vio->cli_do != cli_do_nothing)
+ uty_cli_draw(vio, vio->vty->node) ;
+ else
+ vio->cli_do = uty_cli_process(vio, vio->vty->node) ;
+
+ /* If have something to do, do it. */
+ if (vio->cli_do != cli_do_nothing)
+ {
+ /* Reflect immediate response */
+ uty_cli_response(vio, vio->cli_do) ;
+
+ /* If command not already in progress, dispatch this one, which may
+ * set the CLI blocked.
+ *
+ * Otherwise is now blocked until queued command completes.
+ */
+ if (!vio->cmd_in_progress)
+ vio->cli_blocked = uty_cli_dispatch(vio) ;
+ else
+ vio->cli_blocked = 1 ;
+ } ;
+
+ /* If there is anything in the CLI output FIFO, must set write on to clear
+ * it.
+ *
+ * If there is anything in the command output FIFO *and* is !cmd_in_progress,
+ * must set write on to clear it.
+ *
+ * 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.
+ */
+ 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) ;
+} ;
+
+/* 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.
+ *
+ * Requires that are NOT blocked and NO command is queued.
+ *
+ * Expects to be on new blank line, and when returns will be on new, blank
+ * line.
+ *
+ * Returns: true <=> output is pending and command completed
+ * false => no output pending or command was queued
+ *
+ * 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
+uty_cli_dispatch(vty_io vio)
+{
+ qstring_t tmp ;
+ enum cli_do cli_do ;
+ int queued ;
+
+ struct vty* vty = vio->vty ;
+
+ VTY_ASSERT_LOCKED() ;
+ assert(!vio->cli_blocked && !vio->cmd_in_progress) ;
+
+ /* Set vio->clx to the command about to execute.
+ *
+ * Clear vio->cl and vio->cl_do.
+ */
+ vio->cmd_in_progress = 1 ; /* => vty->buf is valid */
+
+ tmp = vio->clx ; /* swap clx and cl */
+ vio->clx = vio->cl ;
+ vio->cl = tmp ;
+
+ qs_term(&vio->clx) ; /* ensure string is terminated */
+ vty->buf = qs_chars(&vio->clx) ; /* terminated command line */
+ cli_do = vio->cli_do ; /* current operation */
+
+ vio->cli_do = cli_do_nothing ; /* clear */
+ qs_set_empty(&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 */
+
+ /* 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) ;
+ }
+ else
+ {
+ /* All other nodes... */
+ switch (cli_do)
+ {
+ case cli_do_nothing:
+ break ;
+
+ case cli_do_command:
+ queued = uty_command(vty, vty->buf) ;
+ break ;
+
+ case cli_do_ctrl_c:
+ queued = uty_stop_input(vty) ;
+ break ;
+
+ case cli_do_ctrl_d:
+ queued = 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 ;
+ else
+ queued = uty_end_config(vty) ;
+ break ;
+
+ default:
+ zabort("unknown cli_do_xxx value") ;
+ } ;
+ } ;
+
+ if (!queued)
+ {
+ vio->cmd_in_progress = 0 ; /* command complete */
+ vty->buf = NULL ; /* finished with command line */
+ } ;
+
+ return ! (vio->cmd_in_progress || vio_fifo_empty(&vio->cmd_obuf)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Queued command has completed.
+ *
+ * Note that sets write on whether there is anything in the output buffer
+ * or not... write_ready will kick read_ready.
+ */
+extern void
+vty_queued_result(struct vty *vty, int result, int action)
+{
+ vty_io vio ;
+
+ VTY_LOCK() ;
+
+ vio = vty->vio ;
+
+ vio->cmd_in_progress = 0 ; /* command complete */
+ vty->buf = NULL ; /* finished with command line */
+
+ vio->cli_blocked = !vio_fifo_empty(&vio->cmd_obuf) ;
+ /* blocked if output is now pending */
+
+ uty_cli_wipe(vio) ; /* wipe any partly constructed line */
+
+ uty_file_set_write(&vty->vio->file, on) ;
+ /* flush any output -- which will do a
+ * read_ready when all finished */
+ VTY_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.
+ *
+ * No actual I/O takes place here-- all "output" is to vio->cli_obuf
+ * and/or vio->cli_ex_obuf
+ *
+ * The "cli_echo" functions discard the output if vio->cli_echo_suppress != 0.
+ * This is used while passwords are entered and to allow command line changes
+ * to be made while the line is not visible.
+ */
+
+enum { cli_rep_count = 32 } ;
+
+typedef const char cli_rep_char[cli_rep_count] ;
+
+static const char telnet_backspaces[] =
+ { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+ } ;
+CONFIRM(sizeof(telnet_backspaces) == sizeof(cli_rep_char)) ;
+
+static const char telnet_spaces[] =
+ { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
+ } ;
+CONFIRM(sizeof(telnet_spaces) == sizeof(cli_rep_char)) ;
+
+static const char* telnet_newline = "\r\n" ;
+
+static void uty_cli_write(vty_io vio, const char *this, int len) ;
+static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- cf fprintf()
+ */
+static void
+uty_cli_out (vty_io vio, const char *format, ...)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->file.write_open)
+ {
+ va_list args;
+ int len ;
+
+ va_start (args, format);
+ len = qs_vprintf(&vio->cli_vbuf, format, args) ;
+ va_end(args);
+
+ if (len > 0)
+ uty_cli_write(vio, qs_chars(&vio->cli_vbuf), len) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- echo user input
+ *
+ * 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)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->cli_echo_suppress || !vio->file.write_open)
+ return ;
+
+ uty_cli_write(vio, this, len) ;
+}
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- echo 'n' characters using a cli_rep_char string
+ *
+ * Do nothing if echo suppressed (eg in AUTH_NODE)
+ */
+static void
+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)
+ return ;
+
+ uty_cli_write_n(vio, chars, n) ;
+}
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- cf write()
+ */
+inline static void
+uty_cli_write(vty_io vio, const char *this, int len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->file.write_open)
+ vio_fifo_put(&vio->cli_obuf, this, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- write 'n' characters using a cli_rep_char string
+ */
+static void
+uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
+{
+ int len ;
+
+ len = sizeof(cli_rep_char) ;
+ while (n > 0)
+ {
+ if (n < len)
+ len = n ;
+ uty_cli_write(vio, chars, len) ;
+ n -= len ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI VTY output -- write given string
+ *
+ * Returns length of string.
+ */
+static int
+uty_cli_write_s(vty_io vio, const char *str)
+{
+ int len ;
+
+ len = strlen(str) ;
+ if (len != 0)
+ uty_cli_write(vio, str, len) ;
+
+ return len ;
+} ;
+
+/*==============================================================================
+ * Standard Messages
+ */
+
+/*------------------------------------------------------------------------------
+ * Send newline to the console.
+ *
+ * Clears the cli_drawn flag.
+ */
+static void
+uty_cli_out_newline(vty_io vio)
+{
+ uty_cli_write(vio, telnet_newline, 2) ;
+ vio->cli_drawn = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Wipe 'n' characters.
+ *
+ * If 'n' < 0, wipes characters backwards and moves cursor back.
+ * 'n' > 0, wipes characters forwards, leaving cursor where it is
+ */
+static void
+uty_cli_out_wipe_n(vty_io vio, int n)
+{
+ if (n < 0)
+ {
+ n = abs(n) ;
+ uty_cli_write_n(vio, telnet_backspaces, n);
+ } ;
+
+ if (n > 0)
+ {
+ uty_cli_write_n(vio, telnet_spaces, n) ;
+ uty_cli_write_n(vio, telnet_backspaces, n) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send response to the given cli_do
+ *
+ * If no command is in progress, then will send newline to signal that the
+ * command is about to be dispatched.
+ *
+ * If command is in progress, then leaves cursor on '^' to signal that is now
+ * waiting for previous command to complete.
+ */
+static const char* cli_response [2][cli_do_count] =
+{
+ { /* 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",
+ },
+ { /* 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",
+ }
+} ;
+
+static void
+uty_cli_response(vty_io vio, enum cli_do cli_do)
+{
+ const char* str ;
+ int len ;
+
+ if (cli_do == cli_do_nothing)
+ return ;
+
+ str = (cli_do < cli_do_count)
+ ? cli_response[vio->cmd_in_progress ? 1 : 0][cli_do] : NULL ;
+ assert(str != NULL) ;
+
+ len = uty_cli_write_s(vio, str) ;
+
+ if (vio->cmd_in_progress)
+ {
+ vio->cli_extra_len = len ;
+ uty_cli_write_n(vio, telnet_backspaces, len) ;
+ }
+ else
+ {
+ uty_cli_out_newline(vio) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send various messages with trailing newline.
+ */
+
+static void
+uty_cli_out_CMD_ERR_AMBIGUOUS(vty_io vio)
+{
+ uty_cli_write_s(vio, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
+ uty_cli_out_newline(vio) ;
+} ;
+
+static void
+uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio)
+{
+ uty_cli_write_s(vio, "% " MSG_CMD_ERR_NO_MATCH ".") ;
+ uty_cli_out_newline(vio) ;
+} ;
+
+/*==============================================================================
+ * Command line draw and wipe
+ */
+
+/*------------------------------------------------------------------------------
+ * Wipe the current console line -- if any.
+ */
+extern void
+uty_cli_wipe(vty_io vio)
+{
+ int a ;
+ int b ;
+
+ if (!vio->cli_drawn == 0)
+ 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
+ {
+ b = vio->cl.cp ; /* behind cursor */
+ a = vio->cl.len - b ; /* ahead of cursor */
+ }
+
+ /* Stuff ahead of the current position */
+ uty_cli_out_wipe_n(vio, a + vio->cli_extra_len) ;
+
+ /* Stuff behind the current position */
+ uty_cli_out_wipe_n(vio, vio->cli_prompt_len + b) ;
+
+ vio->cli_drawn = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Draw prompt and entire command line, leaving current position where it
+ * should be.
+ *
+ * If command line is currently drawn, this wipes and redraws.
+ *
+ * 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.
+ *
+ * Sets: cli_drawn = true
+ * 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)
+{
+ const char* prompt ;
+
+ 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) ;
+
+ /* Sort out what the prompt is. */
+
+ if (vio->cmd_in_progress)
+ {
+ /* If there is a queued command, the prompt is a minimal affair. */
+ prompt = "~ " ;
+ vio->cli_prompt_len = strlen(prompt) ;
+ }
+ else
+ {
+ /* The prompt depends on the node, and is expected to include the
+ * host name.
+ *
+ * Caches the prompt so doesn't re-evaluate it every time.
+ *
+ * If the host name changes, the cli_prompt_set flag is cleared.
+ */
+ if (!vio->cli_prompt_set || (node != vio->cli_prompt_node))
+ {
+ const char* prompt ;
+
+ if (vty_host_name == NULL)
+ uty_check_host_name() ; /* should never be required */
+
+ prompt = cmd_prompt(node) ;
+ if (prompt == NULL)
+ {
+ zlog_err("vty %s has node %d", uty_get_name(vio), node) ;
+ prompt = "%s ???: " ;
+ } ;
+
+ qs_printf(&vio->cli_prompt_for_node, prompt, vty_host_name);
+
+ vio->cli_prompt_node = node ;
+ vio->cli_prompt_set = 1 ;
+ } ;
+
+ prompt = qs_chars(&vio->cli_prompt_for_node) ;
+ vio->cli_prompt_len = vio->cli_prompt_for_node.len ;
+ } ;
+
+ uty_cli_write(vio, prompt, vio->cli_prompt_len) ;
+
+ if ((vio->cl.len != 0) && !vio->cli_echo_suppress)
+ {
+ 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) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Command line processing loop
+ */
+
+#define CONTROL(X) ((X) - '@')
+
+static void uty_telnet_command(vty_io vio, keystroke stroke) ;
+static int uty_cli_insert (vty_io vio, const char* chars, int n) ;
+static int uty_cli_overwrite (vty_io vio, char* chars, int n) ;
+static int uty_cli_word_overwrite (vty_io vio, char *str) ;
+static int uty_cli_forwards(vty_io vio, int n) ;
+static int uty_cli_backwards(vty_io vio, int n) ;
+static int uty_cli_del_forwards(vty_io vio, int n) ;
+static int uty_cli_del_backwards(vty_io vio, int n) ;
+static int uty_cli_bol (vty_io vio) ;
+static int uty_cli_eol (vty_io vio) ;
+static int uty_cli_word_forwards_delta(vty_io vio) ;
+static int uty_cli_word_forwards(vty_io vio) ;
+static int uty_cli_word_backwards_delta(vty_io vio, int eat_spaces) ;
+static int uty_cli_word_backwards_pure (vty_io vio) ;
+static int uty_cli_word_backwards (vty_io vio) ;
+static int uty_cli_del_word_forwards(vty_io vio) ;
+static int uty_cli_del_word_backwards(vty_io vio) ;
+static int uty_cli_del_to_eol (vty_io vio) ;
+static int uty_cli_clear_line(vty_io vio) ;
+static int uty_cli_transpose_chars(vty_io vio) ;
+static void uty_cli_history_use(vty_io vio, int step) ;
+static void uty_cli_next_line(vty_io vio) ;
+static void uty_cli_previous_line (vty_io vio) ;
+static void uty_cli_complete_command (vty_io vio, enum node_type node) ;
+static void uty_cli_describe_command (vty_io vio, enum node_type node) ;
+
+/*------------------------------------------------------------------------------
+ * Fetch next keystroke, reading from the file if required.
+ */
+static inline bool
+uty_cli_get_keystroke(vty_io vio, keystroke stroke)
+{
+ if (keystroke_get(vio->key_stream, stroke))
+ return 1 ;
+
+ uty_read(vio, NULL) ; /* not stealing */
+
+ return keystroke_get(vio->key_stream, stroke) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Process keystrokes until run out of input, or get something to cli_do.
+ *
+ * If required, draw the prompt and command line.
+ *
+ * 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_do_xxxx
+ *
+ * When returns the cl is '\0' terminated.
+ */
+static enum cli_do
+uty_cli_process(vty_io vio, enum node_type node)
+{
+ struct keystroke stroke ;
+ uint8_t u ;
+ int auth ;
+ enum cli_do ret ;
+
+ auth = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+
+ /* Now process as much as possible of what there is */
+ ret = cli_do_nothing ;
+ while (1)
+ {
+ if (!vio->cli_drawn)
+ uty_cli_draw(vio, node) ;
+
+ if (!uty_cli_get_keystroke(vio, &stroke))
+ break ;
+
+ 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_cli_bol (vio);
+ break;
+
+ case CONTROL('B'):
+ uty_cli_backwards(vio, 1);
+ break;
+
+ case CONTROL('C'):
+ ret = cli_do_ctrl_c ; /* Exit on ^C ..................*/
+ break ;
+
+ case CONTROL('D'):
+ if (vio->cl.len == 0) /* if at start of empty line */
+ ret = cli_do_ctrl_d ; /* Exit on ^D ..................*/
+ else
+ uty_cli_del_forwards(vio, 1);
+ break;
+
+ case CONTROL('E'):
+ uty_cli_eol (vio);
+ break;
+
+ case CONTROL('F'):
+ uty_cli_forwards(vio, 1);
+ break;
+
+ case CONTROL('H'):
+ case 0x7f:
+ uty_cli_del_backwards(vio, 1);
+ break;
+
+ case CONTROL('K'):
+ uty_cli_del_to_eol (vio);
+ break;
+
+ case CONTROL('N'):
+ uty_cli_next_line (vio);
+ break;
+
+ case CONTROL('P'):
+ uty_cli_previous_line (vio);
+ break;
+
+ case CONTROL('T'):
+ uty_cli_transpose_chars (vio);
+ break;
+
+ case CONTROL('U'):
+ uty_cli_clear_line(vio);
+ break;
+
+ case CONTROL('W'):
+ uty_cli_del_word_backwards (vio);
+ break;
+
+ case CONTROL('Z'):
+ ret = cli_do_ctrl_z ; /* Exit on ^Z ..................*/
+ break;
+
+ case '\n':
+ case '\r':
+ ret = cli_do_command ; /* Exit on CR or LF.............*/
+ break ;
+
+ case '\t':
+ if (auth)
+ break ;
+ else
+ uty_cli_complete_command (vio, node);
+ break;
+
+ case '?':
+ if (auth)
+ uty_cli_insert (vio, (char*)&u, 1);
+ else
+ uty_cli_describe_command (vio, node);
+ break;
+
+ default:
+ if ((stroke.value >= 0x20) && (stroke.value < 0x7F))
+ uty_cli_insert (vio, (char*)&u, 1) ;
+ break;
+ }
+ break ;
+
+ /* ESC X -------------------------------------------------------------*/
+ case ks_esc:
+ switch (stroke.value)
+ {
+ case 'b':
+ uty_cli_word_backwards (vio);
+ break;
+
+ case 'f':
+ uty_cli_word_forwards (vio);
+ break;
+
+ case 'd':
+ uty_cli_del_word_forwards (vio);
+ break;
+
+ case CONTROL('H'):
+ case 0x7f:
+ uty_cli_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_cli_previous_line (vio);
+ break;
+
+ case ('B'):
+ uty_cli_next_line (vio);
+ break;
+
+ case ('C'):
+ uty_cli_forwards(vio, 1);
+ break;
+
+ case ('D'):
+ uty_cli_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..... */
+
+ if (ret != cli_do_nothing)
+ {
+ uty_cli_eol (vio) ; /* go to the end of the line */
+ break ; /* stop processing */
+ } ;
+ } ;
+
+ /* Tidy up and return where got to. */
+
+ qs_term(&vio->cl) ; /* add '\0' */
+
+ return ret ;
+} ;
+
+/*==============================================================================
+ * 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
+ */
+
+/*------------------------------------------------------------------------------
+ * Insert 'n' characters at current position in the command line
+ *
+ * Returns number of characters inserted -- ie 'n'
+ */
+static int
+uty_cli_insert (vty_io vio, const char* chars, int n)
+{
+ int after ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ assert((vio->cl.cp <= vio->cl.len)&& (n >= 0)) ;
+
+ if (n <= 0)
+ return n ; /* avoid trouble */
+
+ after = qs_insert(&vio->cl, chars, n) ;
+
+ uty_cli_echo(vio, qs_cp_char(&vio->cl), after + n) ;
+
+ if (after != 0)
+ uty_cli_echo_n(vio, telnet_backspaces, after) ;
+
+ vio->cl.cp += 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_cli_overwrite (vty_io vio, char* chars, int n)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
+
+ if (n > 0)
+ {
+ qs_replace(&vio->cl, chars, n) ;
+ uty_cli_echo(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_cli_word_overwrite (vty_io vio, char *str)
+{
+ int n ;
+ VTY_ASSERT_LOCKED() ;
+
+ n = uty_cli_overwrite(vio, str, strlen(str)) ;
+
+ vio->cl.len = vio->cl.cp ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Forward 'n' characters -- stop at end of line.
+ *
+ * Returns number of characters actually moved
+ */
+static int
+uty_cli_forwards(vty_io vio, int n)
+{
+ int c ;
+ VTY_ASSERT_LOCKED() ;
+
+ c = vio->cl.len - vio->cl.cp ;
+ if (n > c)
+ n = c ;
+
+ assert(n >= 0) ;
+
+ if (n > 0)
+ {
+ uty_cli_echo(vio, qs_cp_char(&vio->cl), n) ;
+ vio->cl.cp += n ;
+ } ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Backwards 'n' characters -- stop at start of line.
+ *
+ * Returns number of characters actually moved
+ */
+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 ;
+
+ assert(n >= 0) ;
+
+ uty_cli_echo_n(vio, telnet_backspaces, 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_cli_del_forwards(vty_io vio, int n)
+{
+ int after ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ assert((vio->cl.len - vio->cl.cp) && (n >= 0)) ;
+
+ if (n <= 0)
+ return 0 ;
+
+ after = qs_delete(&vio->cl, n) ;
+
+ if (after > 0)
+ uty_cli_echo(vio, qs_cp_char(&vio->cl), after) ;
+
+ uty_cli_echo_n(vio, telnet_spaces, n) ;
+ uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
+
+ vio->cl.len -= n ;
+
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Delete 'n' characters before the point.
+ *
+ * Returns number of characters actually deleted.
+ */
+static int
+uty_cli_del_backwards(vty_io vio, int n)
+{
+ return uty_cli_del_forwards(vio, uty_cli_backwards(vio, n)) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Move to the beginning of the line.
+ *
+ * Returns number of characters moved over.
+ */
+static int
+uty_cli_bol (vty_io vio)
+{
+ return uty_cli_backwards(vio, vio->cl.cp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move to the end of the line.
+ *
+ * Returns number of characters moved over
+ */
+static int
+uty_cli_eol (vty_io vio)
+{
+ return uty_cli_forwards(vio, vio->cl.len - 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_cli_word_forwards_delta(vty_io vio)
+{
+ char* cp ;
+ char* tp ;
+ char* ep ;
+
+ VTY_ASSERT_LOCKED() ; ;
+
+ assert(vio->cl.cp <= vio->cl.len) ;
+
+ cp = qs_cp_char(&vio->cl) ;
+ ep = qs_ep_char(&vio->cl) ;
+
+ tp = cp ;
+
+ while ((tp < ep) && (*tp != ' '))
+ ++tp ;
+
+ while ((tp < ep) && (*tp == ' '))
+ ++tp ;
+
+ return tp - cp ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Forward word -- move to start of next word.
+ *
+ * Moves past any non-spaces, then past any spaces.
+ */
+static int
+uty_cli_word_forwards(vty_io vio)
+{
+ return uty_cli_forwards(vio, uty_cli_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_cli_word_backwards_delta(vty_io vio, int eat_spaces)
+{
+ char* cp ;
+ char* tp ;
+ char* sp ;
+
+ VTY_ASSERT_LOCKED() ; ;
+
+ assert(vio->cl.cp <= vio->cl.len) ;
+
+ cp = qs_cp_char(&vio->cl) ;
+ sp = qs_chars(&vio->cl) ;
+
+ tp = cp ;
+
+ if (eat_spaces)
+ while ((tp > sp) && (*(tp - 1) == ' '))
+ --tp ;
+
+ while ((tp > sp) && (*(tp - 1) != ' '))
+ --tp ;
+
+ return 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_cli_word_backwards_pure (vty_io vio)
+{
+ return uty_cli_backwards(vio, uty_cli_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_cli_word_backwards (vty_io vio)
+{
+ return uty_cli_backwards(vio, uty_cli_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_cli_del_word_forwards(vty_io vio)
+{
+ return uty_cli_del_forwards(vio, uty_cli_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_cli_del_word_backwards(vty_io vio)
+{
+ return uty_cli_del_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Kill rest of line from current point.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_del_to_eol (vty_io vio)
+{
+ return uty_cli_del_forwards(vio, vio->cl.len - vio->cl.cp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Kill line from the beginning.
+ *
+ * Returns number of characters deleted.
+ */
+static int
+uty_cli_clear_line(vty_io vio)
+{
+ uty_cli_bol(vio) ;
+ return uty_cli_del_to_eol(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Transpose chars before or at the point.
+ *
+ * Return number of characters affected.
+ */
+static int
+uty_cli_transpose_chars(vty_io vio)
+{
+ char chars[2] ;
+ char* cp ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Give up if < 2 characters or at start of line. */
+ if ((vio->cl.len < 2) || (vio->cl.cp < 1))
+ return 0 ;
+
+ /* Move back to first of characters to exchange */
+ if (vio->cl.cp == vio->cl.len)
+ uty_cli_backwards(vio, 2) ;
+ else
+ uty_cli_backwards(vio, 1) ;
+
+ /* Pick up in the new order */
+ cp = qs_cp_char(&vio->cl) ;
+ chars[1] = *cp++ ;
+ chars[0] = *cp ;
+
+ /* And overwrite */
+ return uty_cli_overwrite(vio, chars, 2) ;
+} ;
+
+/*==============================================================================
+ * Command line history handling
+ */
+
+/*------------------------------------------------------------------------------
+ * Add given command line to the history buffer.
+ *
+ * This is inserting the vty->buf line into the history.
+ */
+extern void
+uty_cli_hist_add (vty_io vio, const char* cmd_line)
+{
+ char* prev_line ;
+ char* line ;
+ char* e ;
+ int prev_index ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* 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)
+ prev_index = VTY_MAXHIST - 1 ;
+
+ 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) ;
+
+ vector_set_item(&vio->hist, vio->hindex, line) ;
+
+ /* History index rotation. */
+ vio->hindex++;
+ if (vio->hindex == VTY_MAXHIST)
+ vio->hindex = 0;
+ }
+ else
+ {
+ XFREE(MTYPE_VTY_HIST, line) ;
+ } ;
+
+ 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_cli_history_use(vty_io vio, int step)
+{
+ int index ;
+ unsigned new_len ;
+ unsigned old_len ;
+ char* hist ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* 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 = vector_get_item(&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_cli_bol(vio) ;
+
+ /* 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 ;
+
+ uty_cli_echo(vio, hist, new_len) ;
+
+ if (old_len < new_len)
+ uty_cli_del_to_eol(vio) ;
+
+ return ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Use next history line, if any.
+ */
+static void
+uty_cli_next_line(vty_io vio)
+{
+ uty_cli_history_use(vio, +1) ;
+}
+
+/*------------------------------------------------------------------------------
+ * Use previous history line, if any.
+ */
+static void
+uty_cli_previous_line (vty_io vio)
+{
+ uty_cli_history_use(vio, -1) ;
+}
+
+/*==============================================================================
+ * Command Completion and Command Description
+ *
+ */
+static void uty_cli_describe_show(vty_io vio, vector describe) ;
+static void uty_cli_describe_fold (vty_io vio, int cmd_width,
+ unsigned int desc_width, struct desc *desc) ;
+static void uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
+ const char* str) ;
+
+static vector uty_cli_cmd_prepare(vty_io vio, int help) ;
+
+/*------------------------------------------------------------------------------
+ * Command completion
+ */
+static void
+uty_cli_complete_command (vty_io vio, enum node_type node)
+{
+ unsigned i ;
+ int ret ;
+ vector matched ;
+ vector vline ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Try and match the tokenised command line */
+ vline = uty_cli_cmd_prepare(vio, 1) ;
+ matched = cmd_complete_command (vline, node, &ret);
+ cmd_free_strvec (vline);
+
+ /* Show the result. */
+ switch (ret)
+ {
+ case CMD_ERR_AMBIGUOUS:
+ uty_cli_out_newline(vio) ; /* clears cli_drawn */
+ uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
+ break ;
+
+ case CMD_ERR_NO_MATCH:
+ uty_cli_out_newline(vio) ; /* clears cli_drawn */
+ uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
+ break ;
+
+ case CMD_COMPLETE_FULL_MATCH:
+ uty_cli_eol (vio) ;
+ uty_cli_word_backwards_pure (vio);
+ uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
+ uty_cli_insert(vio, " ", 1);
+ break ;
+
+ case CMD_COMPLETE_MATCH:
+ uty_cli_eol (vio) ;
+ uty_cli_word_backwards_pure (vio);
+ uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
+ break ;
+
+ case CMD_COMPLETE_LIST_MATCH:
+ for (i = 0; i < vector_end(matched); i++)
+ {
+ if ((i % 6) == 0)
+ uty_cli_out_newline(vio) ; /* clears cli_drawn */
+ uty_cli_out (vio, "%-10s ", (char*)vector_get_item(matched, i));
+ }
+
+ break;
+
+ case CMD_ERR_NOTHING_TODO:
+ default:
+ break;
+ } ;
+
+ cmd_free_strvec(matched);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Command Description
+ */
+static void
+uty_cli_describe_command (vty_io vio, enum node_type node)
+{
+ int ret;
+ vector vline;
+ vector describe;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Try and match the tokenised command line */
+ vline = uty_cli_cmd_prepare(vio, 1) ;
+ describe = cmd_describe_command (vline, node, &ret);
+ cmd_free_strvec (vline);
+
+ uty_cli_out_newline(vio); /* clears cli_drawn */
+
+ /* Deal with result. */
+ switch (ret)
+ {
+ case CMD_ERR_AMBIGUOUS:
+ uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
+ break ;
+
+ case CMD_ERR_NO_MATCH:
+ uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
+ break ;
+
+ default:
+ uty_cli_describe_show(vio, describe) ;
+ break ;
+ } ;
+
+ if (describe != NULL)
+ vector_free (describe);
+}
+
+/*------------------------------------------------------------------------------
+ * Show the command description.
+ *
+ * Generates lines of the form:
+ *
+ * word description text
+ *
+ * Where the word field is adjusted to suit the longest word, and the
+ * description text is wrapped if required (if the width of the console is
+ * known) so that get:
+ *
+ * word description ..................................
+ * .............text
+ *
+ * If one of the options is '<cr>', that is always shown last.
+ */
+static void
+uty_cli_describe_show(vty_io vio, vector describe)
+{
+ unsigned int i, cmd_width, desc_width;
+ struct desc *desc, *desc_cr ;
+
+ /* Get width of the longest "word" */
+ cmd_width = 0;
+ for (i = 0; i < vector_active (describe); i++)
+ if ((desc = vector_slot (describe, i)) != NULL)
+ {
+ unsigned int len;
+
+ if (desc->cmd[0] == '\0')
+ continue;
+
+ len = strlen (desc->cmd);
+ if (desc->cmd[0] == '.')
+ len--;
+
+ if (cmd_width < len)
+ cmd_width = len;
+ }
+
+ /* Set width of description string. */
+ desc_width = vio->width - (cmd_width + 6);
+
+ /* Print out description. */
+ desc_cr = NULL ; /* put <cr> last if it appears */
+
+ 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;
+ }
+
+ uty_cli_describe_fold (vio, cmd_width, desc_width, desc);
+ }
+
+ if (desc_cr != NULL)
+ uty_cli_describe_fold (vio, cmd_width, desc_width, desc_cr);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Show one word and the description, folding the description as required.
+ */
+static void
+uty_cli_describe_fold (vty_io vio, int cmd_width,
+ unsigned int desc_width, struct desc *desc)
+{
+ char *buf;
+ const char *cmd, *p;
+ int pos;
+
+ VTY_ASSERT_LOCKED() ;
+
+ cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
+ p = desc->str ;
+
+ /* If have a sensible description width */
+ if (desc_width > 20)
+ {
+ buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
+
+ while (strlen (p) > desc_width)
+ {
+ /* move back to first space */
+ for (pos = desc_width; pos > 0; pos--)
+ if (*(p + pos) == ' ')
+ break;
+
+ /* if did not find a space, break at width */
+ if (pos == 0)
+ pos = desc_width ;
+
+ strncpy (buf, p, pos);
+ buf[pos] = '\0';
+ uty_cli_describe_line(vio, cmd_width, cmd, buf) ;
+
+ cmd = ""; /* for 2nd and subsequent lines */
+
+ p += pos ; /* step past what just wrote */
+ while (*p == ' ')
+ ++p ; /* skip spaces */
+ } ;
+
+ XFREE (MTYPE_TMP, buf);
+ } ;
+
+ uty_cli_describe_line(vio, cmd_width, cmd, p) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Show one description line.
+ */
+static void
+uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
+ const char* str)
+{
+ if (str != NULL)
+ uty_cli_out (vio, " %-*s %s", cmd_width, cmd, str) ;
+ else
+ uty_cli_out (vio, " %-s", cmd) ;
+ uty_cli_out_newline(vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Prepare "vline" token array for command handler.
+ *
+ * For "help" (command completion/description), if the command line is empty,
+ * or ends in ' ', adds an empty token to the end of the token array.
+ */
+static vector
+uty_cli_cmd_prepare(vty_io vio, int help)
+{
+ vector vline ;
+
+ vline = cmd_make_strvec(qs_term(&vio->cl)) ;
+
+ /* Note that if there is a vector of tokens, then there is at least one
+ * token, so can guarantee that vio->cl.len >= 1 !
+ */
+ if (help)
+ if ((vline == NULL) || isspace(*qs_chars_at(&vio->cl, vio->cl.len - 1)))
+ vline = cmd_add_to_strvec(vline, "") ;
+
+ return vline ;
+} ;
+
+/*==============================================================================
+ * VTY telnet stuff
+ */
+
+#define TELNET_OPTION_DEBUG 1 /* 0 to turn off */
+
+static const char* telnet_commands[256] =
+{
+ [tn_IAC ] = "IAC",
+ [tn_DONT ] = "DONT",
+ [tn_DO ] = "DO",
+ [tn_WONT ] = "WONT",
+ [tn_WILL ] = "WILL",
+ [tn_SB ] = "SB",
+ [tn_GA ] = "GA",
+ [tn_EL ] = "EL",
+ [tn_EC ] = "EC",
+ [tn_AYT ] = "AYT",
+ [tn_AO ] = "AO",
+ [tn_IP ] = "IP",
+ [tn_BREAK] = "BREAK",
+ [tn_DM ] = "DM",
+ [tn_NOP ] = "NOP",
+ [tn_SE ] = "SE",
+ [tn_EOR ] = "EOR",
+ [tn_ABORT] = "ABORT",
+ [tn_SUSP ] = "SUSP",
+ [tn_EOF ] = "EOF",
+} ;
+
+static const char* telnet_options[256] =
+{
+ [to_BINARY] = "BINARY", /* 8-bit data path */
+ [to_ECHO] = "ECHO", /* echo */
+ [to_RCP] = "RCP", /* prepare to reconnect */
+ [to_SGA] = "SGA", /* suppress go ahead */
+ [to_NAMS] = "NAMS", /* approximate message size */
+ [to_STATUS] = "STATUS", /* give status */
+ [to_TM] = "TM", /* timing mark */
+ [to_RCTE] = "RCTE", /* remote controlled tx and echo */
+ [to_NAOL] = "NAOL", /* neg. about output line width */
+ [to_NAOP] = "NAOP", /* neg. about output page size */
+ [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
+ [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
+ [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
+ [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
+ [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
+ [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
+ [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
+ [to_XASCII] = "XASCII", /* extended ascii character set */
+ [to_LOGOUT] = "LOGOUT", /* force logout */
+ [to_BM] = "BM", /* byte macro */
+ [to_DET] = "DET", /* data entry terminal */
+ [to_SUPDUP] = "SUPDUP", /* supdup protocol */
+ [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
+ [to_SNDLOC] = "SNDLOC", /* send location */
+ [to_TTYPE] = "TTYPE", /* terminal type */
+ [to_EOR] = "EOR", /* end or record */
+ [to_TUID] = "TUID", /* TACACS user identification */
+ [to_OUTMRK] = "OUTMRK", /* output marking */
+ [to_TTYLOC] = "TTYLOC", /* terminal location number */
+ [to_3270REGIME] = "3270REGIME", /* 3270 regime */
+ [to_X3PAD] = "X3PAD", /* X.3 PAD */
+ [to_NAWS] = "NAWS", /* window size */
+ [to_TSPEED] = "TSPEED", /* terminal speed */
+ [to_LFLOW] = "LFLOW", /* remote flow control */
+ [to_LINEMODE] = "LINEMODE", /* Linemode option */
+ [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
+ [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
+ [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
+ [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
+ [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
+ [to_EXOPL] = "EXOPL", /* extended-options-list */
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as decimal.
+ */
+static void
+uty_cli_out_dec(vty_io vio, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(vio, "%s ", str) ;
+ else
+ uty_cli_out(vio, "%d ", (int)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * For debug. Put string or value as hex.
+ */
+static void
+uty_cli_out_hex(vty_io vio, const char* str, unsigned char u)
+{
+ if (str != NULL)
+ uty_cli_out(vio, "%s ", str) ;
+ else
+ uty_cli_out(vio, "0x%02x ", (unsigned)u) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "WILL TELOPT_ECHO"
+ */
+extern void
+uty_will_echo (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "suppress Go-Ahead"
+ */
+extern void
+uty_will_suppress_go_ahead (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use linemode"
+ */
+extern void
+uty_dont_linemode (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "Use window size"
+ */
+extern void
+uty_do_window_size (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * Send telnet: "don't use lflow" -- not currently used
+ */
+extern void
+uty_dont_lflow_ahead (vty_io vio)
+{
+ unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
+ VTY_ASSERT_LOCKED() ;
+ uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
+}
+
+/*------------------------------------------------------------------------------
+ * 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 ;
+
+ /* Echo to the other end if required */
+ if (TELNET_OPTION_DEBUG)
+ {
+ p = stroke->buf ;
+ left = stroke->len ;
+
+ uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(vio, telnet_commands[*p], *p) ;
+ ++p ;
+
+ if (left-- > 0)
+ uty_cli_out_dec(vio, telnet_options[*p], *p) ;
+ ++p ;
+
+ if (left > 0)
+ {
+ while(left-- > 0)
+ uty_cli_out_hex(vio, NULL, *p++) ;
+
+ if (stroke->flags & kf_truncated)
+ uty_cli_out(vio, "... ") ;
+
+ if (!(stroke->flags & kf_broken))
+ {
+ uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
+ uty_cli_out_hex(vio, telnet_commands[tn_SE], tn_SE) ;
+ }
+ } ;
+
+ if (!(stroke->flags & kf_broken))
+ uty_cli_out (vio, "BROKEN") ;
+
+ uty_cli_out (vio, "\r\n") ;
+
+ } ;
+
+ /* Process the telnet command */
+ 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_NAWS:
+ if (left != 4)
+ {
+ uzlog(NULL, LOG_WARNING,
+ "RFC 1073 violation detected: telnet NAWS option "
+ "should send %d characters, but we received %d",
+ (3 + 4 + 2), (3 + left + 2)) ;
+ }
+ else
+ {
+ vio->width = *p++ << 8 ;
+ vio->width += *p++ ;
+ vio->height = *p++ << 8 ;
+ vio->height += *p ;
+
+ if (TELNET_OPTION_DEBUG)
+ uty_cli_out(vio, "TELNET NAWS window size received: "
+ "width %d, height %d%s",
+ vio->width, vio->height, telnet_newline) ;
+ } ;
+ break ;
+
+ default: /* no other IAC SB <option> */
+ break ;
+ } ;
+ break ;
+
+ default: /* no other IAC X */
+ break ;
+ } ;
+} ;
diff --git a/lib/vty_cli.h b/lib/vty_cli.h
new file mode 100644
index 00000000..4fda2db8
--- /dev/null
+++ b/lib/vty_cli.h
@@ -0,0 +1,43 @@
+/* VTY Command Line Handler
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * Revisions: 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_VTY_CLI_H
+#define _ZEBRA_VTY_CLI_H
+
+#include "vty_io.h"
+
+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_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) ;
+
+#endif /* _ZEBRA_VTY_CLI_H */
diff --git a/lib/vty_io.c b/lib/vty_io.c
new file mode 100644
index 00000000..15f90219
--- /dev/null
+++ b/lib/vty_io.c
@@ -0,0 +1,2202 @@
+/* VTY IO Functions
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: 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 "vty.h"
+#include "vty_io.h"
+#include "vty_cli.h"
+#include "qstring.h"
+#include "keystroke.h"
+
+#include "memory.h"
+
+#include "prefix.h"
+#include "filter.h"
+#include "privs.h"
+#include "sockunion.h"
+#include "network.h"
+
+#include <arpa/telnet.h>
+#include <sys/un.h> /* for VTYSH */
+#include <sys/socket.h>
+
+#define VTYSH_DEBUG 0
+
+/*==============================================================================
+ * 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.
+ */
+
+/*------------------------------------------------------------------------------
+ * VTY output function -- cf fprintf
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
+ */
+extern int
+uty_out (struct vty *vty, const char *format, ...)
+{
+ int result;
+ VTY_ASSERT_LOCKED() ;
+ va_list args;
+ va_start (args, format);
+ result = uty_vout(vty, format, args);
+ va_end (args);
+ return result;
+}
+
+/*------------------------------------------------------------------------------
+ * VTY output function -- cf vfprintf
+ *
+ * Returns: >= 0 => OK
+ * < 0 => failed (see errno)
+ */
+extern int
+uty_vout(struct vty *vty, const char *format, va_list args)
+{
+ enum vty_type type ;
+ vty_io vio ;
+ int len ;
+
+ 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 ;
+ } ;
+
+ /* Output -- process depends on type */
+ switch (type)
+ {
+ case VTY_STDOUT:
+ case VTY_SHELL:
+ len = vprintf (format, args);
+ break ;
+
+ case VTY_FILE:
+ case VTY_TERM:
+ case VTY_SHELL_SERV:
+
+ 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) ;
+ } ;
+ break ;
+
+ default:
+ zabort("impossible VTY type") ;
+ } ;
+
+ return len;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Discard the current contents of the command FIFO
+ *
+ * TODO: worry about line control ??
+ */
+extern void
+uty_out_discard(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ vio_fifo_set_empty(&vio->cmd_obuf) ;
+} ;
+
+/*==============================================================================
+ * The watch dog.
+ *
+ * The watch dog starts up every now and checks:
+ *
+ * * for changes to the host name, which should be reflected in the
+ * prompt for any terminals.
+ *
+ * * the death watch list
+ */
+
+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) ;
+
+/*------------------------------------------------------------------------------
+ * Watch dog action
+ */
+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
+uty_watch_dog_start()
+{
+ if (vty_cli_nexus)
+ vty_watch_dog.qnexus = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ NULL, NULL) ;
+
+ uty_watch_dog_bark() ; /* start up by barking the first time */
+}
+
+extern void
+uty_watch_dog_stop(void)
+{
+ if (vty_watch_dog.anon != NULL)
+ {
+ if (vty_cli_nexus)
+ qtimer_free(vty_watch_dog.qnexus) ;
+ else
+ thread_cancel(vty_watch_dog.thread) ;
+ } ;
+}
+
+/*------------------------------------------------------------------------------
+ * qnexus watch dog action
+ */
+static void
+vty_watch_dog_qnexus(qtimer qtr, void* timer_info, qtime_t when)
+{
+ VTY_LOCK() ;
+ uty_watch_dog_bark() ;
+ VTY_UNLOCK() ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * thread watch dog action
+ */
+static int
+vty_watch_dog_thread(struct thread *thread)
+{
+ VTY_LOCK() ;
+ uty_watch_dog_bark() ;
+ VTY_UNLOCK() ;
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * 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 vty_read_qnexus (qps_file qf, void* file_info) ;
+static void vty_write_qnexus (qps_file qf, void* file_info) ;
+static void vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when) ;
+
+static int vty_read_thread (struct thread *thread) ;
+static int vty_write_thread (struct thread *thread) ;
+static int vty_timer_thread (struct thread *thread) ;
+
+static void vtysh_read_qnexus (qps_file qf, void* file_info) ;
+static int vtysh_read_thread (struct thread *thread) ;
+
+/*==============================================================================
+ * Creation and destruction of VTY objects
+ */
+
+/*------------------------------------------------------------------------------
+ * 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.
+ *
+ * Returns: new vty
+ */
+extern struct vty *
+uty_new (int fd, enum vty_type type)
+{
+ struct vty *vty ;
+ struct vty_io* vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vty_watch_dog.anon == NULL)
+ uty_watch_dog_start() ;
+
+ 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:
+ *
+ * vio_list both pointers NULL
+ *
+ * half_closed = 0 -- NOT half closed (important !)
+ * timed_out = 0 -- NOT timed out
+ *
+ * mon_list both pointers NULL
+ *
+ * name = NULL -- no name, yet
+ *
+ * cli_drawn = 0 -- not drawn
+ * cli_prompt_len = 0 )
+ * cli_extra_len = 0 ) not material
+ * cli_echo_suppress = 0 )
+ *
+ * cli_prompt_node = 0 -- not material
+ * cli_prompt_set = 0 -- so prompt needs to be constructed
+ *
+ * cli_blocked = 0 -- not blocked
+ * cmd_in_progress = 0 -- no command in progress
+ *
+ * cli_do = 0 = cli_do_nothing
+ *
+ * cmd_wait_more = 0 -- not waiting for response to "--more--"
+ *
+ * fail = 0 -- no login failures yet
+ *
+ * hist = empty vector
+ * hp = 0 -- at the beginning
+ * hindex = 0 -- the beginning
+ *
+ * width = 0 -- unknown console width
+ * height = 0 -- unknown console height
+ *
+ * lines = 0 -- unset
+ *
+ * monitor = 0 -- not a monitor
+ *
+ * config = 0 -- not holder of "config" mode
+ */
+ confirm(cli_do_nothing == 0) ;
+
+ vio->type = type ;
+
+ uty_file_init_new(&vio->file, fd, vio) ;
+
+ vio->key_stream = keystroke_stream_new('\0') ; /* TODO: CSI ?? */
+
+ qs_init_new(&vio->ibuf, 0) ;
+
+ 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 */
+
+ qs_init_new(&vio->cmd_vbuf, 0) ;
+ vio_fifo_init_new(&vio->cmd_obuf, 16 * 1024) ;
+
+ /* Place on list of known vio/vty */
+ sdl_push(vio_list_base, vio, vio_list) ;
+
+ return vty;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new vty of type VTY_TERM -- ie attached to a telnet session.
+ *
+ * Returns: new vty
+ */
+static struct vty *
+uty_new_term(int vty_sock, union sockunion *su)
+{
+ struct vty *vty ;
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new (vty_sock, VTY_TERM) ;
+ vio = vty->vio ;
+
+ /* Set the 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 ;
+ }
+ 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 ;
+ } ;
+
+ /* The text form of the address identifies the VTY */
+ vio->name = sockunion_su2str (su, MTYPE_VTY_NAME);
+
+ /* Set the initial node */
+ if (no_password_check)
+ {
+ if (restricted_mode)
+ vty->node = RESTRICTED_NODE;
+ else if (host.advanced)
+ vty->node = ENABLE_NODE;
+ else
+ vty->node = VIEW_NODE;
+ }
+ else
+ vty->node = AUTH_NODE;
+
+ /* Pick up current timeout setting */
+ vio->file.v_timeout = vty_timeout_val;
+
+ /* Use global 'lines' setting, otherwise is unset */
+ if (host.lines >= 0)
+ vio->lines = host.lines;
+ else
+ vio->lines = -1;
+
+ /* 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) ;
+
+ /* Set CLI into state waiting for output to complete. */
+ vio->cli_blocked = 1 ;
+ uty_file_set_write(&vio->file, on) ;
+
+ /* 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;
+ }
+
+ /* Say hello to the world. */
+ vty_hello (vty);
+
+ if (! no_password_check)
+ uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE,
+ VTY_NEWLINE, VTY_NEWLINE);
+
+ return vty;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create new vty of type VTY_SHELL_SERV -- ie attached to a vtysh session.
+ *
+ * Returns: new vty
+ */
+static struct vty *
+uty_new_shell_serv(int vty_sock)
+{
+ struct vty *vty ;
+ vty_io vio ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Allocate new vty structure and set up default values. */
+ vty = uty_new (vty_sock, VTY_SHELL_SERV) ;
+ 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 ;
+ }
+ else
+ {
+ vio->file.action.read.thread = vtysh_read_thread ;
+ vio->file.action.write.thread = vty_write_thread ;
+ vio->file.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) ;
+
+ return vty;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set/Clear "monitor" state:
+ *
+ * set: if VTY_TERM and not already "monitor" (and write_open !)
+ * clear: if is "monitor"
+ */
+extern void
+uty_set_monitor(vty_io vio, bool on)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (on && !vio->monitor)
+ {
+ if ((vio->type == VTY_TERM) && vio->file.write_open)
+ {
+ vio->monitor = 1 ;
+ sdl_push(vio_monitors_base, vio, mon_list) ;
+ } ;
+ }
+ else if (!on && vio->monitor)
+ {
+ vio->monitor = 0 ;
+ sdl_del(vio_monitors_base, vio, mon_list) ;
+ }
+} ;
+
+/*------------------------------------------------------------------------------
+ * Return "name" of VTY
+ *
+ * For VTY_TERM this is the IP address of the far end of the telnet connection.
+ */
+extern const char*
+uty_get_name(vty_io vio)
+{
+ return (vio->name != NULL) ? vio->name : "?" ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Closing down VTY for reading.
+ *
+ * Shuts the read side and discards any buffered input.
+ *
+ * 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.
+ */
+extern void
+uty_half_close (vty_io vio)
+{
+ char* line ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->half_closed)
+ return ; /* cannot do it again */
+
+ vio->half_closed = 1 ;
+
+ uzlog (NULL, LOG_INFO, "Vty connection (fd %d) close", vio->file.fd) ;
+
+ uty_file_half_close(&vio->file) ;
+ uty_set_monitor(vio, 0) ;
+
+ keystroke_stream_free(vio->key_stream) ;
+ qs_free_body(&vio->ibuf) ;
+
+ uty_cli_wipe(vio) ;
+
+ while ((line = vector_ream_keep(&vio->hist)) != NULL)
+ XFREE(MTYPE_VTY_HIST, line) ;
+
+ /* Hit the width, height and lines so that all output clears without
+ * interruption.
+ */
+ vio->width = 0 ;
+ vio->height = 0 ;
+ vio->lines = 0 ;
+
+ /* Make sure no longer holding the config symbol of power */
+ uty_config_unlock(vio->vty, AUTH_NODE) ;
+
+ /* Move to the death watch list */
+ sdl_del(vio_list_base, vio, vio_list) ;
+ sdl_push(vio_death_watch, vio, vio_list) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Closing down VTY.
+ *
+ * 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.
+ */
+extern void
+uty_close (vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_file_close(&vio->file) ; /* bring the file to a complete stop */
+
+ uty_half_close(vio) ; /* deal with the input side, and place on
+ death watch -- if not already done */
+
+ 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) ;
+
+ vio->vty->buf = NULL ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * 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.
+ */
+extern void
+uty_full_close (vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_file_close(&vio->file) ; /* bring the file to a complete stop */
+
+ uty_half_close(vio) ; /* deal with the input side, and place on
+ death watch -- if not already done */
+
+ 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) ;
+
+ vio->vty->buf = NULL ;
+} ;
+
+/*==============================================================================
+ * Dealing with am I/O error on VTY
+ *
+ * If this is the first error for this VTY, produce suitable log message.
+ *
+ * If is a "monitor", turn that off, *before* issuing log message.
+ */
+static int
+uty_io_error(vty_io vio, const char* what)
+{
+ /* can no longer be a monitor ! */
+ uty_set_monitor(vio, 0) ;
+
+ /* if this is the first error, log it */
+ if (vio->file.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_io_error()") ;
+ } ;
+
+ 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)) ;
+ } ;
+
+ return -1 ;
+} ;
+
+/*==============================================================================
+ * vio_file level operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a new vio_file structure.
+ *
+ * Requires that: the vio_file structure is not currently in use.
+ *
+ * if fd >= 0 then: file is open and ready read and write
+ * otherwise: file is not open
+ *
+ * there are no errors, yet.
+ *
+ * Sets timeout to no timeout at all -- timeout is optional.
+ */
+static void
+uty_file_init_new(vio_file file, int fd, void* info)
+{
+ memset(file, 0, sizeof(struct vio_file)) ;
+
+ /* Zeroising the structure has set:
+ *
+ * action = all the actions set NULL
+ *
+ * error_seen = 0 -- no error, yet
+ *
+ * qf = NULL -- no qfile, yet
+ * t_read = NULL ) no threads, yet
+ * t_write = NULL )
+ *
+ * v_timeout = 0 -- no timeout set
+ * timer_runing = 0 -- not running, yet
+ * t_timer = NULL -- no timer thread, yet
+ * qtr = NULL -- no qtimer, yet
+ */
+ file->fd = fd ;
+ file->info = info ;
+
+ file->read_open = (fd >= 0) ;
+ file->write_open = (fd >= 0) ;
+
+ if (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);
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Restart the timer.
+ *
+ * If a timeout time is set, then start or restart the timer with that value.
+ *
+ * If no timeout time is set, and the timer is running, unset it.
+ */
+static void
+uty_file_restart_timer(vio_file file)
+{
+ if (file->v_timeout != 0)
+ {
+ assert(file->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) ;
+ }
+ 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) ;
+ } ;
+
+ file->timer_running = 1 ;
+ }
+ else if (file->timer_running)
+ {
+ if (vty_cli_nexus)
+ {
+ if (file->qtr != NULL)
+ qtimer_unset(file->qtr) ;
+ }
+ else
+ {
+ if (file->t_timer != NULL)
+ thread_cancel (file->t_timer) ;
+ } ;
+
+ file->timer_running = 0 ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set a new timer value.
+ */
+extern void
+uty_file_set_timer(vio_file file, unsigned long timeout)
+{
+ file->v_timeout = timeout ;
+ if (file->timer_running)
+ uty_file_restart_timer(file) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set read on/off -- restart timer.
+ */
+extern void
+uty_file_set_read(vio_file file, bool on)
+{
+ if (file->fd < 0)
+ return ;
+
+ if (on)
+ {
+ assert(file->action.read.anon != NULL) ;
+
+ if (vty_cli_nexus)
+ {
+ qps_enable_mode(file->qf, qps_read_mnum, file->action.read.qnexus) ;
+ }
+ else
+ {
+ if (file->t_read != NULL)
+ thread_cancel(file->t_read) ;
+
+ file->t_read = thread_add_read(vty_master,
+ file->action.read.thread, file->info, file->fd) ;
+ } ;
+ }
+ else
+ {
+ if (vty_cli_nexus)
+ {
+ qps_disable_modes(file->qf, qps_read_mbit) ;
+ }
+ else
+ {
+ if (file->t_read != NULL)
+ thread_cancel (file->t_read) ;
+ } ;
+ } ;
+
+ uty_file_restart_timer(file) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set write on/off -- restart timer.
+ */
+extern void
+uty_file_set_write(vio_file file, bool on)
+{
+ if (file->fd < 0)
+ return ;
+
+ if (on)
+ {
+ assert(file->action.write.anon != NULL) ;
+
+ if (vty_cli_nexus)
+ {
+ qps_enable_mode(file->qf, qps_write_mnum, file->action.write.qnexus) ;
+ }
+ else
+ {
+ if (file->t_write != NULL)
+ thread_cancel(file->t_write) ;
+
+ file->t_write = thread_add_write(vty_master,
+ file->action.write.thread, file->info, file->fd) ;
+ } ;
+ }
+ else
+ {
+ if (vty_cli_nexus)
+ {
+ qps_disable_modes(file->qf, qps_write_mbit) ;
+ }
+ else
+ {
+ if (file->t_write != NULL)
+ thread_cancel (file->t_write) ;
+ } ;
+ } ;
+
+ uty_file_restart_timer(file) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close given vty file for reading.
+ *
+ * Sets timer to timeout for clearing any pending output.
+ */
+static void
+uty_file_half_close(vio_file file)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (file->fd >= 0)
+ {
+ shutdown(file->fd, SHUT_RD) ; /* actual half close */
+
+ file->v_timeout = 30 ; /* for output to clear */
+ uty_file_set_read(file, off) ;
+ } ;
+
+ file->read_open = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close given vio_file, completely -- shut down any timer.
+ *
+ * Structure is cleared of everything except the last error !
+ */
+static void
+uty_file_close(vio_file file)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (file->fd >= 0)
+ close(file->fd) ;
+
+ if (vty_cli_nexus && (file->fd >= 0))
+ qps_remove_file(file->qf) ;
+
+ if (file->qf != NULL)
+ qps_file_free(file->qf) ;
+
+ if (file->t_read != NULL)
+ thread_cancel(file->t_write) ;
+ if (file->t_write != NULL)
+ thread_cancel(file->t_write) ;
+
+ file->fd = -1 ;
+ file->qf = NULL ;
+ file->t_read = NULL ;
+ file->t_write = NULL ;
+
+ file->info = NULL ;
+ file->action.read.anon = NULL ;
+ file->action.write.anon = NULL ;
+ file->action.timer.anon = NULL ;
+
+ file->read_open = 0 ;
+ file->write_open = 0 ;
+
+ if (file->qtr != NULL)
+ qtimer_free(file->qtr) ;
+ if (file->t_timer != NULL)
+ thread_cancel(file->t_timer) ;
+
+ file->v_timeout = 0 ;
+ file->qtr = NULL ;
+ file->t_timer = NULL ;
+} ;
+
+/*==============================================================================
+ * Reading from the VTY_TERM type file.
+ *
+ * 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.
+ */
+
+/*------------------------------------------------------------------------------
+ * Ready to read -> kicking CLI
+ *
+ * Have two CLI: one (trivial one) when waiting on "--more--",
+ * and the standard one.
+ *
+ * End up here when there is something ready to be read.
+ *
+ * Also ends up here when was write_ready and did not block in uty_write.
+ *
+ * Will also end up here if an error has occurred, the other end has closed,
+ * this end has half closed, etc. This fact is used to kick the CLI even when
+ * there is no data to be read.
+ *
+ * Note that nothing is actually read here -- reading is done in the CLI itself,
+ * if required.
+ *
+ * The CLI decides whether to re-enable read, or enable write, or both.
+ */
+static void
+uty_read_ready(vty_io vio)
+{
+ uty_file_set_read(&vio->file, off) ; /* restarts timer */
+
+ /* 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 */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to read -> kicking CLI
+ */
+static void
+vty_read_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info;
+
+ VTY_LOCK() ;
+
+ assert((vio->file.fd == qf->fd) && (vio == vio->file.info)) ;
+
+ uty_read_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- threads: ready to read -> kicking CLI
+ */
+static int
+vty_read_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->file.fd == THREAD_FD (thread) && (vio == vio->file.info)) ;
+
+ vio->file.t_read = NULL ; /* implicitly */
+ uty_read_ready(vio);
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Read a lump of bytes and shovel into the keystroke stream
+ *
+ * Steal keystroke if required -- see keystroke_input()
+ *
+ * Returns: 0 => nothing available
+ * > 0 => read at least one byte
+ * -1 => EOF (or not open, or failed)
+ */
+extern int
+uty_read (vty_io vio, keystroke steal)
+{
+ unsigned char buf[500] ;
+ int get ;
+
+ if (!vio->file.read_open)
+ return -1 ; /* at EOF if not open */
+
+ get = read_nb(vio->file.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") ;
+
+ vio->file.read_open = 0 ;
+ keystroke_input(vio->key_stream, NULL, 0, steal) ;
+
+ get = -1 ;
+ } ;
+
+ return get ;
+} ;
+
+/*==============================================================================
+ * The write file 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.
+ *
+ * The cli output takes precedence.
+ *
+ * Output of command stuff is subject to line_control, and may go through the
+ * "--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) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to write -> try to empty buffers
+ */
+static void
+vty_write_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info ;
+
+ VTY_LOCK() ;
+
+ assert((vio->file.fd == qf->fd) && (vio == vio->file.info)) ;
+
+ uty_write_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: ready to write -> try to empty buffers
+ */
+static int
+vty_write_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->file.fd == THREAD_FD (thread) && (vio == vio->file.info)) ;
+
+ vio->file.t_write = NULL; /* implicitly */
+ uty_write_ready(vio) ;
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Write as much as possible of what there is.
+ *
+ * If not cmd_in_progress, clears cli_blocked if both FIFOs are, or become,
+ * empty.
+ *
+ * Note that if !write_open, or becomes !write_open, then the FIFOs are empty
+ * and all output instantly successful.
+ *
+ * Sets write on if prevented from outputting 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.
+ */
+static bool
+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 */
+
+ if ((vio->cmd_in_progress) || (vio->cmd_wait_more))
+ return 0 ; /* not blocked by I/O */
+
+ /* 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 */
+
+ if (ret == 2)
+ {
+ /* Want now to wait for "--more--"
+ *
+ * Note that this produces CLI output, which must deal with here.
+ */
+ uty_cli_want_more(vio) ; /* NB: sets cmd_wait_more */
+
+ ret = uty_flush_fifo(vio, &vio->cli_obuf, NULL) ;
+ if (ret == 1)
+ return 1 ; /* blocked by I/O */
+
+ if (vio->file.write_open)
+ return 0 ; /* not blocked by I/O */
+ } ;
+
+ /* Reach here iff both CLI and command FIFOs are empty and is not
+ * cmd_in_progress
+ */
+ vio->cli_blocked = 0 ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Flush the given FIFO to output -- subject to possible line control.
+ *
+ * If ends up needing to write more, sets write on.
+ *
+ * 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--"
+ */
+static int
+uty_flush_fifo(vty_io vio, vio_fifo vf, struct vty_line_control* line_control)
+{
+ char* src ;
+ size_t have ;
+ int done ;
+ bool wait_more ;
+
+ if (!vio->file.write_open)
+ {
+ uty_empty_out_fifos(vio) ;
+ return 0 ;
+ } ;
+
+ wait_more = 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
+ */
+ } ;
+
+ done = write_nb(vio->file.fd, src, have) ;
+
+ if (done < 0)
+ {
+ uty_io_error(vio, "write") ;
+
+ vio->file.write_open = 0 ;
+ uty_empty_out_fifos(vio) ;
+ return 0 ; /* no longer open */
+ }
+
+ vio_fifo_got_upto(vf, src + done) ;
+
+ if (done < (int)have)
+ {
+ if (line_control != NULL)
+ {
+ /* "put back" have - done bytes for next time */
+ } ;
+
+ uty_file_set_write(&vio->file, on) ;
+ return 1 ; /* output is full */
+ } ;
+
+ /* 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 ;
+ } ;
+
+ return 0 ; /* all gone */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Empty the output FIFOs
+ *
+ * This is for use when the output has failed or is closed.
+ */
+static void
+uty_empty_out_fifos(vty_io vio)
+{
+ vio_fifo_set_empty(&vio->cli_obuf) ;
+ vio_fifo_set_empty(&vio->cmd_obuf) ;
+
+ vio->cmd_wait_more = 0 ;
+} ;
+
+/*==============================================================================
+ * Timer for VTY_TERM (and VTY_SHELL_SERV).
+ */
+
+/*------------------------------------------------------------------------------
+ * Timer has expired.
+ *
+ * If half_closed, then this is curtains -- have waited long enough !
+ *
+ * Otherwise, half close the VTY and leave it to the death-watch to sweep up.
+ */
+static void
+uty_timer_expired (vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (vio->half_closed)
+ return uty_close(vio) ; /* curtains */
+
+ uty_half_close(vio) ; /* bring input side to a halt */
+
+ vio->timed_out = 1 ; /* why stopped */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: deal with timer timeout.
+ */
+static void
+vty_timer_qnexus (qtimer qtr, void* timer_info, qtime_t when)
+{
+ vty_io vio = timer_info ;
+
+ VTY_LOCK() ;
+
+ uty_timer_expired(vio);
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- thread: deal with timer timeout.
+ */
+static int
+vty_timer_thread (struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ vio->file.t_timer = NULL ; /* implicitly */
+
+ uty_timer_expired(vio) ;
+
+ VTY_UNLOCK() ;
+ return 0;
+}
+
+/*==============================================================================
+ * VTY Listener(s)
+ *
+ * Have listeners for VTY_TERM and VTY_SHELL_SERV types of VTY.
+ */
+
+typedef struct vty_listener* vty_listener ;
+
+struct vty_listener
+{
+ vty_listener next ; /* ssl type list */
+
+ enum vty_type type ;
+
+ struct vio_file file ;
+};
+
+/* List of listeners so can tidy up. */
+static vty_listener vty_listeners_list = NULL ;
+
+/* Prototypes for listener stuff */
+static int uty_serv_sock_addrinfo (const char *hostname, unsigned short port) ;
+static int uty_serv_sock(const char* addr, unsigned short port) ;
+static int uty_serv_sock_open(sa_family_t family, int type, int protocol,
+ struct sockaddr* sa, unsigned short port) ;
+static int uty_serv_vtysh(const char *path) ;
+static int vty_accept_thread(struct thread *thread) ;
+static void vty_accept_qnexus(qps_file qf, void* listener) ;
+static int uty_accept(vty_listener listener, int listen_sock) ;
+static int uty_accept_term(vty_listener listener) ;
+static int uty_accept_shell_serv (vty_listener listener) ;
+
+static void uty_serv_start_listener(int fd, enum vty_type type) ;
+
+/*------------------------------------------------------------------------------
+ * If possible, will use getaddrinfo() to find all the things to listen on.
+ */
+
+#if defined(HAVE_IPV6) && !defined(NRL)
+# define VTY_USE_ADDRINFO 1
+#else
+# define VTY_USE_ADDRINFO 0
+#endif
+
+/*------------------------------------------------------------------------------
+ * Open VTY listener(s)
+ *
+ * addr -- address ) to listen for VTY_TERM connections
+ * port -- port )
+ * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ */
+extern void
+uty_open_listeners(const char *addr, unsigned short port, const char *path)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ /* If port is set to 0, do not listen on TCP/IP at all! */
+ if (port)
+ {
+ int n ;
+
+ if (VTY_USE_ADDRINFO)
+ n = uty_serv_sock_addrinfo(addr, port);
+ else
+ n = uty_serv_sock(addr, port);
+
+ if (n == 0)
+ uzlog(NULL, LOG_ERR, "could not open any VTY listeners") ;
+ }
+
+ /* If want to listen for vtysh, set up listener now */
+ if (VTYSH_ENABLED && (path != NULL))
+ uty_serv_vtysh(path) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close VTY listener
+ *
+ * addr -- address ) to listen for VTY_TERM connections
+ * port -- port )
+ * path -- path for VTYSH connections -- if VTYSH_ENABLED
+ */
+extern void
+uty_close_listeners(void)
+{
+ vty_listener listener ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ while ((listener = ssl_pop(&listener, vty_listeners_list, next)) != NULL)
+ {
+ uty_file_close(&listener->file) ; /* no ceremony, no flowers */
+ XFREE(MTYPE_VTY, listener) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERM -- using getaddrinfo().
+ *
+ * Returns: number of listeners successfully opened.
+ */
+static int
+uty_serv_sock_addrinfo (const char *hostname, unsigned short port)
+{
+#if VTY_USE_ADDRINFO
+
+# ifndef HAVE_IPV6
+# error Using getaddrinfo() but HAVE_IPV6 is not defined ??
+# endif
+
+ int ret;
+ int n ;
+ struct addrinfo req;
+ struct addrinfo *ainfo;
+ struct addrinfo *ainfo_save;
+ char port_str[16];
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Want to listen, TCP-wise, on all available address families, on the
+ * given port.
+ */
+ memset (&req, 0, sizeof (struct addrinfo));
+ req.ai_flags = AI_PASSIVE;
+ req.ai_family = AF_UNSPEC;
+ req.ai_socktype = SOCK_STREAM;
+ snprintf(port_str, sizeof(port_str), "%d", port);
+
+ ret = getaddrinfo (hostname, port_str, &req, &ainfo);
+
+ if (ret != 0)
+ {
+ fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
+ exit (1);
+ }
+
+ /* Open up sockets on all AF_INET and AF_INET6 addresses */
+ ainfo_save = ainfo;
+
+ n = 0 ;
+ do
+ {
+ if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
+ continue;
+
+ assert(ainfo->ai_family == ainfo->ai_addr->sa_family) ;
+
+ ret = uty_serv_sock_open(ainfo->ai_family, ainfo->ai_socktype,
+ ainfo->ai_protocol, ainfo->ai_addr, port) ;
+ if (ret >= 0)
+ ++n ;
+ }
+ while ((ainfo = ainfo->ai_next) != NULL);
+
+ freeaddrinfo (ainfo_save);
+
+ return n ;
+
+#else
+ zabort("uty_serv_sock_addrinfo not implemented") ;
+#endif /* VTY_USE_ADDRINFO */
+}
+
+/*------------------------------------------------------------------------------
+ * Open listener(s) for VTY_TERM -- not using getaddrinfo() !
+ *
+ * Returns: number of listeners successfully opened.
+ */
+static int
+uty_serv_sock(const char* addr, unsigned short port)
+{
+ int ret;
+ int n ;
+ union sockunion su_addr ;
+ struct sockaddr* sa ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* If have an address, see what kind and whether valid */
+ sa = NULL ;
+
+ if (addr != NULL)
+ {
+ ret = str2sockunion (addr, &su_addr) ;
+ if (ret == 0)
+ sa = &su_addr.sa ;
+ else
+ uzlog(NULL, LOG_ERR, "bad address %s, cannot listen for VTY", addr);
+ } ;
+
+ /* Try for AF_INET */
+ ret = uty_serv_sock_open(AF_INET, SOCK_STREAM, 0, sa, port) ;
+ if (ret >= 0)
+ ++n ; /* opened socket */
+ if (ret == 1)
+ sa = NULL ; /* used the address */
+
+#if HAVE_IPV6
+ /* Try for AF_INET6 */
+ ret = uty_serv_sock_open(AF_INET6, SOCK_STREAM, 0, sa, port) ;
+ if (ret >= 0)
+ ++n ; /* opened socket */
+ if (ret == 1)
+ sa = NULL ; /* used the address */
+#endif
+
+ /* If not used the address... something wrong */
+ if (sa != NULL)
+ uzlog(NULL, LOG_ERR, "could not use address %s, to listen for VTY", addr);
+
+ /* Done */
+ return n ;
+}
+
+/*------------------------------------------------------------------------------
+ * Open a VTY_TERM listener socket.
+ *
+ * The sockaddr 'sa' may be NULL or of a different address family, in which
+ * case "any" address is used.
+ *
+ * If the sockaddr 'sa' is used, only the address portion is used.
+ *
+ * Returns: < 0 => failed
+ * == 0 => OK -- did not use the sockaddr 'sa'.
+ * > 1 => OK -- and did use the sockaddr 'sa'
+ */
+static int
+uty_serv_sock_open(sa_family_t family, int type, int protocol,
+ struct sockaddr* sa, unsigned short port)
+{
+ union sockunion su ;
+ int sock ;
+ int ret ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Is there an address and is it for this family ? */
+ if ((sa != NULL) || (sa->sa_family == family))
+ /* Set up sockunion containing required family and address */
+ sockunion_new_sockaddr(&su, sa) ;
+ else
+ {
+ /* no address or wrong family -- set up empty sockunion of
+ * required family */
+ sockunion_init_new(&su, family) ;
+ sa = NULL ;
+ } ;
+
+ /* Open the socket and set its properties */
+ sock = sockunion_socket(family, type, protocol) ;
+ if (sock < 0)
+ return -1 ;
+
+ ret = sockopt_reuseaddr (sock);
+
+ if (ret >= 0)
+ ret = sockopt_reuseport (sock);
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock);
+
+ if (ret >= 0)
+ ret = sockunion_bind (sock, &su, port, sa) ;
+
+ if (ret >= 0)
+ ret = sockunion_listen (sock, 3);
+
+ if (ret < 0)
+ {
+ close (sock);
+ return -1 ;
+ }
+
+ /* Socket is open -- set VTY Term listener going */
+ uty_serv_start_listener(sock, VTY_TERM) ;
+
+ /* Return OK and signal whether used address or not */
+ return (sa != NULL) ? 1 : 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Open a VTY_SHEL_SERV listener socket (UNIX domain).
+ *
+ * Returns: < 0 => failed
+ * >= 0 => OK
+ */
+static int
+uty_serv_vtysh(const char *path)
+{
+ int ret;
+ int sock, sa_len, path_len ;
+ struct sockaddr_un sa_un ;
+ mode_t old_mask;
+ struct zprivs_ids_t ids;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* worry about the path length */
+ path_len = strlen(path) + 1 ;
+ if (path_len >= (int)sizeof(sa_un.sun_path))
+ {
+ uzlog(NULL, LOG_ERR, "path too long for unix stream socket: '%s'", path);
+ return -1 ;
+ } ;
+
+ /* First of all, unlink existing socket */
+ unlink (path);
+
+ /* Make UNIX domain socket. */
+ sock = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0)
+ {
+ uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s",
+ safe_strerror(errno));
+ umask (old_mask);
+ return -1 ;
+ }
+
+ /* Bind to the required path */
+ memset (&sa_un, 0, sizeof(sa_un));
+ sa_un.sun_family = AF_UNIX;
+ strncpy (sa_un.sun_path, path, sizeof(sa_un.sun_path) - 1);
+
+ sa_len = SUN_LEN(&sa_un) ;
+
+#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+ sa_un.sun_len = sa_len ;
+#endif
+
+ old_mask = umask (0007);
+
+ ret = bind (sock, (struct sockaddr *) &sa_un, sa_len) ;
+ if (ret < 0)
+ uzlog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, safe_strerror(errno));
+
+ if (ret >= 0)
+ ret = set_nonblocking(sock);
+
+ if (ret >= 0)
+ {
+ ret = listen (sock, 5);
+ if (ret < 0)
+ uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock,
+ safe_strerror(errno));
+ } ;
+
+ zprivs_get_ids(&ids);
+
+ if (ids.gid_vty > 0)
+ {
+ /* set group of socket */
+ if ( chown (path, -1, ids.gid_vty) )
+ {
+ uzlog (NULL, LOG_ERR, "uty_serv_vtysh: could chown socket, %s",
+ safe_strerror (errno) );
+ }
+ }
+
+ umask (old_mask);
+
+ /* Give up now if failed along the way */
+ if (ret < 0)
+ {
+ close (sock) ;
+ return -1 ;
+ } ;
+
+ /* Socket is open -- set VTY Term listener going */
+ uty_serv_start_listener(sock, VTY_SHELL_SERV) ;
+
+ return 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Socket is open -- set a VTY listener going
+ *
+ * Note that the vyt_listener structure is passed to the accept action function.
+ */
+static void
+uty_serv_start_listener(int fd, enum vty_type type)
+{
+ vty_listener listener ;
+
+ listener = XCALLOC(MTYPE_VTY, sizeof (struct vty_listener));
+
+ ssl_push(vty_listeners_list, listener, next) ;
+ uty_file_init_new(&listener->file, fd, listener) ;
+
+ listener->type = type ;
+
+ if (vty_cli_nexus)
+ listener->file.action.read.qnexus = vty_accept_qnexus ;
+ else
+ listener->file.action.read.thread = vty_accept_thread ;
+
+ uty_file_set_read(&listener->file, on) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action for the thread world -- create and dispatch VTY
+ */
+static int
+vty_accept_thread(struct thread *thread)
+{
+ vty_listener listener = THREAD_ARG(thread) ;
+ int result ;
+
+ VTY_LOCK() ;
+
+ result = uty_accept(listener, THREAD_FD(thread));
+
+ uty_file_set_read(&listener->file, on) ;
+
+ VTY_UNLOCK() ;
+ return result ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action for the qnexus world -- create and dispatch VTY
+ */
+static void
+vty_accept_qnexus(qps_file qf, void* listener)
+{
+ VTY_LOCK() ;
+
+ uty_accept(listener, qf->fd);
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_TERM or VTY_SHELL_SERV
+ */
+static int
+uty_accept(vty_listener listener, int listen_sock)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(listener->file.fd == listen_sock) ;
+
+ switch (listener->type)
+ {
+ case VTY_TERM:
+ return uty_accept_term(listener) ;
+
+ case VTY_SHELL_SERV:
+ return uty_accept_shell_serv(listener) ;
+
+ default:
+ zabort("unknown vty type") ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_TERM
+ */
+static int
+uty_accept_term(vty_listener listener)
+{
+ int sock;
+ union sockunion su;
+ int ret;
+ unsigned int on;
+ struct prefix *p ;
+ char buf[SU_ADDRSTRLEN] ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* We can handle IPv4 or IPv6 socket. */
+ sockunion_init_new(&su, 0) ;
+
+ sock = sockunion_accept (listener->file.fd, &su);
+
+ if (sock < 0)
+ {
+ if (sock == -1)
+ uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s",
+ safe_strerror (errno));
+ return -1;
+ }
+
+ /* Really MUST have non-blocking */
+ ret = set_nonblocking(sock) ; /* issues WARNING if fails */
+ if (ret < 0)
+ {
+ close(sock) ;
+ return -1 ;
+ } ;
+
+ /* New socket is open... worry about access lists */
+ p = sockunion2hostprefix (&su);
+ ret = 0 ; /* so far, so good */
+
+ if ((p->family == AF_INET) && vty_accesslist_name)
+ {
+ /* VTY's accesslist apply. */
+ struct access_list* acl ;
+
+ if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) &&
+ (access_list_apply (acl, p) == FILTER_DENY))
+ ret = -1 ;
+ }
+
+#ifdef HAVE_IPV6
+ if ((p->family == AF_INET6) && vty_ipv6_accesslist_name)
+ {
+ /* VTY's ipv6 accesslist apply. */
+ struct access_list* acl ;
+
+ if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) &&
+ (access_list_apply (acl, p) == FILTER_DENY))
+ ret = -1 ;
+ }
+#endif /* HAVE_IPV6 */
+
+ prefix_free (p);
+
+ if (ret != 0)
+ {
+ uzlog (NULL, LOG_INFO, "Vty connection refused from %s",
+ sockunion2str (&su, buf, sizeof(buf)));
+ close (sock);
+ return 0;
+ } ;
+
+ /* Final options (optional) */
+ on = 1 ;
+ ret = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY,
+ (void*)&on, sizeof (on));
+ if (ret < 0)
+ uzlog (NULL, LOG_INFO, "can't set sockopt to sock %d: %s",
+ (int)sock, safe_strerror (errno));
+
+ /* All set -- create the VTY_TERM */
+ uty_new_term(sock, &su);
+
+ /* Log new VTY */
+ uzlog (NULL, LOG_INFO, "Vty connection from %s (fd %d)",
+ sockunion2str (&su, buf, sizeof(buf)), sock);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------------
+ * Accept action -- create and dispatch VTY_SHELL_SERV
+ */
+static int
+uty_accept_shell_serv (vty_listener listener)
+{
+ int sock ;
+ int ret ;
+ int client_len ;
+ struct sockaddr_un client ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ client_len = sizeof(client);
+ memset (&client, 0, client_len);
+
+ sock = accept(listener->file.fd, (struct sockaddr *) &client,
+ (socklen_t *) &client_len) ;
+
+ if (sock < 0)
+ {
+ uzlog (NULL, LOG_WARNING, "can't accept vty shell socket : %s",
+ safe_strerror (errno));
+ return -1;
+ }
+
+ /* Really MUST have non-blocking */
+ ret = set_nonblocking(sock) ; /* issues WARNING if fails */
+ if (ret < 0)
+ {
+ close(sock) ;
+ return -1 ;
+ } ;
+
+ /* All set -- create the VTY_SHELL_SERV */
+ if (VTYSH_DEBUG)
+ printf ("VTY shell accept\n");
+
+ uty_new_shell_serv(sock) ;
+
+ /* Log new VTY */
+ uzlog (NULL, LOG_INFO, "Vty shell connection (fd %d)", sock);
+ return 0;
+}
+
+/*==============================================================================
+ * Reading from the VTY_SHELL_SERV type file.
+ *
+ * The select/pselect call-back ends up in utysh_read_ready().
+ */
+
+/*------------------------------------------------------------------------------
+ * Ready to read -> kicking the "SHELL_SERV CLI"
+ *
+ * End up here when there is something ready to be read.
+ *
+ * Will also end up here if an error has occurred, the other end has closed,
+ * this end has half closed, etc. This fact is used to kick the CLI even when
+ * there is no data to be read.
+ *
+ * Note that nothing is actually read here -- reading is done in the CLI itself,
+ * if required.
+ *
+ * The CLI decides whether to re-enable read, or enable write, or both.
+ */
+static void
+utysh_read_ready(vty_io vio)
+{
+ uty_file_set_read(&vio->file, off) ;
+
+ /* TODO: need minimal "CLI" for VTY_SHELL_SERV
+ * NB: when output from command is flushed out, must append the
+ * following four bytes: '\0' '\0' '\0' <ret>
+ * Where <ret> is the command return code.
+ */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Callback -- qnexus: ready to read -> kicking the "SHELL_SERV CLI"
+ */
+static void
+vtysh_read_qnexus(qps_file qf, void* file_info)
+{
+ vty_io vio = file_info;
+
+ VTY_LOCK() ;
+
+ assert((vio->file.fd == qf->fd) && (vio == vio->file.info)) ;
+
+ utysh_read_ready(vio) ;
+
+ VTY_UNLOCK() ;
+}
+
+/*------------------------------------------------------------------------------
+ * Callback -- threads: ready to read -> kicking the "SHELL_SERV CLI"
+ */
+static int
+vtysh_read_thread(struct thread *thread)
+{
+ vty_io vio = THREAD_ARG (thread);
+
+ VTY_LOCK() ;
+
+ assert(vio->file.fd == THREAD_FD (thread) && (vio == vio->file.info)) ;
+
+ vio->file.t_read = NULL ; /* implicitly */
+ utysh_read_ready(vio);
+
+ VTY_UNLOCK() ;
+ return 0 ;
+}
+
+/*------------------------------------------------------------------------------
+ * Read a lump of bytes and shovel into the command line buffer
+ *
+ * Lines coming in are terminated by '\0'.
+ *
+ * Assumes that the incoming command line is empty or otherwise incomplete.
+ *
+ * Moves stuff from the "buf" qstring and appends to "cl" qstring, stopping
+ * when get '\0' or empties the "buf".
+ *
+ * When empties "buf", reads a lump from the file.
+ *
+ * Returns: 0 => command line is incomplete
+ * 1 => have a complete command line
+ * -1 => EOF (or not open, or failed)
+ */
+extern int
+utysh_read (vty_io vio, qstring cl, qstring buf)
+{
+ int get ;
+ char* cp ;
+ char* ep ;
+ size_t have ;
+
+ while (1)
+ {
+ /* process what there is in the buffer */
+ if (buf->len > buf->cp)
+ {
+ cp = qs_cp_char(buf) ;
+ ep = qs_ep_char(buf) ;
+ have = ep - cp ;
+
+ ep = memchr(cp, '\0', have) ;
+ if (ep != NULL)
+ have = ep - cp ; /* have upto, but excluding '\0' */
+
+ if (have > 0) /* take what have */
+ {
+ qs_insert(cl, cp, have) ;
+ cl->cp += have ;
+ buf->cp += have ;
+ } ;
+
+ if (ep != NULL) /* if found '\0' */
+ {
+ qs_term(cl) ; /* '\0' terminate */
+ ++buf->cp ; /* step past it */
+ return 1 ; /* have a complete line <<<<<<<<<<<<< */
+ }
+ } ;
+
+ /* buffer is empty -- try and get some more stuff */
+ assert(buf->len == buf->cp) ;
+
+ if (!vio->file.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 */
+
+ get = read_nb(vio->file.fd, buf->body, buf->size) ;
+ if (get > 0)
+ buf->len = get ;
+ else if (get == 0)
+ return 0 ; /* have an incomplete line <<<<<<<<<<<< */
+ else
+ {
+ if (get == -1)
+ uty_io_error(vio, "read") ;
+
+ vio->file.read_open = 0 ;
+
+ return -1 ; /* at EOF or failed <<<<<<<<<<<<< */
+ } ;
+ } ;
+} ;
+
+/*==============================================================================
+ * 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.
+ *
+ * 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.
+ */
+
+/*------------------------------------------------------------------------------
+ * 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)
+{
+ vty_io vio ;
+ vty_io next ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ next = sdl_head(vio_monitors_base) ;
+
+ if (next == 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 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)
+ {
+ vio = next ;
+
+ if ( vio->monitor /* may be temporarily not a monitor */
+ && (vio->cmd_in_progress || vio_fifo_empty(&vio->cmd_obuf)) )
+ {
+ vio->monitor = 0 ; /* avoid recursion */
+
+ uty_cli_wipe(vio) ;
+ uty_write(vio) ;
+
+ if (vio_fifo_empty(&vio->cli_obuf) && vio->file.write_open)
+ {
+ vio_fifo_put(&vio->cli_obuf, ll->line, ll->len) ;
+ uty_write(vio) ;
+ } ;
+
+ uty_file_set_read(&vio->file, on) ;
+
+ /* 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 ;
+ } ;
+
+ 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) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Async-signal-safe version of vty_log for fixed strings.
+ *
+ * This is last gasp operation.
+ */
+void
+vty_log_fixed (const char *buf, size_t len)
+{
+ vty_io vio ;
+
+ /* Write to all known "monitor" vty
+ *
+ * Forget all the niceties -- about to die in any case.
+ */
+ vio = sdl_head(vio_monitors_base) ;
+ while (vio != NULL)
+ {
+ write(vio->file.fd, buf, len) ;
+ write(vio->file.fd, "\r\n", 2) ;
+
+ vio = sdl_next(vio, mon_list) ;
+ } ;
+} ;
diff --git a/lib/vty_io.h b/lib/vty_io.h
new file mode 100644
index 00000000..06cefe06
--- /dev/null
+++ b/lib/vty_io.h
@@ -0,0 +1,297 @@
+/* VTY IO Structure and Functions -- header
+ * Virtual terminal [aka TeletYpe] interface routine.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * Revisions: 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_VTY_IO_H
+#define _ZEBRA_VTY_IO_H
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include "uty.h"
+#include "vty.h"
+#include "vio_fifo.h"
+#include "keystroke.h"
+#include "thread.h"
+#include "command.h"
+#include "qstring.h"
+
+/*==============================================================================
+ * Here are structures and other definitions which are shared by:
+ *
+ * vty.c -- the main vty handler
+ * vty_cli.c -- which handles the command line stuff
+ * vty_io.c -- ....
+ *
+ * The "struct vty" is used extensively across the Quagga daemons, where it
+ * has two functions relating to command handling as:
+ *
+ * 1) a "file handle" for output produced by commands
+ *
+ * 2) the holder of some context -- notably the current command "node" -- for
+ * command execution to use
+ *
+ * The bulk of "struct vty" is, therefore, private to vty.c and is factored
+ * out into the "struct vty_io".
+ *
+ * To reduce the size of vty.c, some groups of functions are separated into:
+ *
+ * vty_cli.c -- which looks after the keystroke by keystroke handling
+ * of the command line.
+ *
+ */
+
+/*------------------------------------------------------------------------------
+ * VTY file structure
+ *
+ * Used
+ */
+
+typedef int thread_action(struct thread *) ;
+
+union file_action
+{
+ qps_action* qnexus ;
+ thread_action* thread ;
+ void* anon ;
+} ;
+
+union timer_action
+{
+ qtimer_action* qnexus ;
+ thread_action* thread ;
+ void* anon ;
+} ;
+
+struct vio_file_actions
+{
+ union file_action read ;
+ union file_action write ;
+ union timer_action timer ;
+};
+
+typedef struct vio_file* vio_file ;
+struct vio_file
+{
+ int fd ;
+
+ void* info ; /* for action routines */
+
+ struct vio_file_actions action ;
+
+ bool read_open ; /* read returns 0 if not open */
+ bool write_open ; /* write completes instantly if not open */
+ int error_seen ; /* non-zero => failed */
+
+ qps_file qf ; /* when running qnexus */
+
+ struct thread *t_read; /* when running threads */
+ struct thread *t_write;
+
+ unsigned long v_timeout; /* time-out in seconds -- 0 => none */
+ bool timer_running ; /* true when timer is running */
+
+ qtimer qtr; /* when running qnexus */
+ struct thread *t_timer; /* when running threads */
+
+} ;
+
+struct vty_line_control
+{
+ int tba ;
+} ;
+
+enum
+{
+ off = false,
+ on = true
+};
+
+/*------------------------------------------------------------------------------
+ *
+ */
+
+struct vty_io {
+ /* 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 */
+ enum vty_type type;
+
+ /* File level stuff */
+ struct vio_file file ;
+
+ /* "name" of the VTY (for VTY_TERM is IP address) */
+ char *name ;
+
+ /* Keystroke stream and raw input buffer */
+ keystroke_stream key_stream ;
+ qstring_t ibuf ;
+
+ /*--------------------------------------------------------------------*/
+ /* Command line and related state */
+
+ /* cli_drawn <=> the current prompt and user input occupy the current
+ * line on the screen.
+ *
+ * If cli_drawn is true, the following are valid:
+ *
+ * cli_prompt_len -- the length of the prompt part.
+ *
+ * cli_extra_len -- the length of any ^X at the cursor position
+ * (for when blocked waiting for queued command)
+ *
+ * cli_echo_suppress -- the user part of the command line is suppressed
+ *
+ * NB: cli_echo_suppress is only used for password entry.
+ */
+ int cli_drawn ;
+
+ int cli_prompt_len ; /* for drawn line (if any) */
+ int cli_extra_len ; /* for for drawn line (if any) */
+
+ bool cli_echo_suppress ; /* non-zero => suppress cli echo */
+
+ /* "cache" for prompt -- when node or host name changes, prompt does */
+ enum node_type cli_prompt_node ;
+ bool cli_prompt_set ;
+ qstring_t cli_prompt_for_node ;
+
+ /* State of the CLI
+ *
+ * cli_blocked -- blocked from processing keystrokes
+ * cmd_in_progress -- command dispatched (may be queued)
+ *
+ * cli_wait_more -- is in "--more--" wait state
+ *
+ */
+ bool cli_blocked ;
+ bool cmd_in_progress ;
+
+ /* Command Line(s)
+ *
+ * cli_do -- when current command being prepared is completed (by
+ * CR/LF or otherwise) this says what there now is to be done.
+ *
+ * cl -- current command line being prepared.
+ *
+ * clx -- current command line being executed.
+ *
+ * NB: during command execution vty->buf is set to point at the '\0'
+ * terminated current command line being executed.
+ */
+ enum cli_do cli_do ;
+
+ 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 ;
+
+ struct vty_line_control line_control ;
+ /* Failure count for login attempts */
+ int fail;
+
+ /* History of commands */
+ vector_t hist ;
+ int hp ; /* History lookup current point */
+ int hindex; /* History insert end point */
+
+ /* Window width/height. */
+ int width;
+ int height;
+
+ /* Configure lines. */
+ int lines;
+
+ /* Terminal monitor. */
+ bool monitor ;
+
+ /* In configure mode. */
+ 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) ;
+
+#endif /* _ZEBRA_VTY_IO_H */
diff --git a/lib/workqueue.h b/lib/workqueue.h
index 5d2f2da2..9ff7cdb5 100644
--- a/lib/workqueue.h
+++ b/lib/workqueue.h
@@ -78,6 +78,13 @@ CONFIRM(offsetof(work_queue_item_t, args) == 0) ;
#define WQ_UNPLUGGED (1 << 0) /* available for draining */
+typedef struct work_queue* work_queue ;
+
+typedef wq_item_status wq_workfunc(work_queue, work_queue_item);
+typedef void wq_errorfunc(work_queue, work_queue_item);
+typedef void wq_del_item_data(work_queue, work_queue_item);
+typedef void wq_completion_func(work_queue);
+
struct work_queue
{
/* Everything but the specification struct is private
@@ -91,23 +98,23 @@ struct work_queue
* Public, must be set before use by caller. May be modified at will.
*/
struct {
- /* optional opaque user data, global to the queue. */
+ /* optional opaque user data, global to the queue. */
void *data;
/* work function to process items with:
* First argument is the workqueue queue.
* Second argument is the item data
*/
- wq_item_status (*workfunc) (struct work_queue *, work_queue_item);
+ wq_workfunc* workfunc ;
- /* error handling function, optional */
- void (*errorfunc) (struct work_queue *, work_queue_item);
+ /* error handling function -- optional */
+ wq_errorfunc* errorfunc ;
- /* callback to delete user specific item data */
- void (*del_item_data) (struct work_queue *, work_queue_item);
+ /* callback to delete user specific item data -- optional */
+ wq_del_item_data* del_item_data ;
- /* completion callback, called when queue is emptied, optional */
- void (*completion_func) (struct work_queue *);
+ /* completion callback, called when queue is emptied -- optional */
+ wq_completion_func* completion_func ;
/* max number of retries to make for item that errors */
unsigned int max_retries;