summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_main.c69
-rwxr-xr-xconfigure.ac1
-rw-r--r--lib/Makefile.am8
-rw-r--r--lib/command.c352
-rw-r--r--lib/command.h30
-rw-r--r--lib/command_queue.c87
-rw-r--r--lib/command_queue.h31
-rw-r--r--lib/filter.c2
-rw-r--r--lib/heap.c517
-rw-r--r--lib/heap.h160
-rw-r--r--lib/log.c654
-rw-r--r--lib/log.h30
-rw-r--r--lib/memory.c114
-rw-r--r--lib/memory.h8
-rw-r--r--lib/memtypes.c19
-rw-r--r--lib/mqueue.c615
-rw-r--r--lib/mqueue.h282
-rw-r--r--lib/plist.c2
-rw-r--r--lib/privs.c202
-rw-r--r--lib/privs.h4
-rw-r--r--lib/qlib_init.c84
-rw-r--r--lib/qlib_init.h40
-rw-r--r--lib/qpnexus.c230
-rw-r--r--lib/qpnexus.h97
-rw-r--r--lib/qpselect.c1321
-rw-r--r--lib/qpselect.h208
-rw-r--r--lib/qpthreads.c713
-rw-r--r--lib/qpthreads.h417
-rw-r--r--lib/qtime.c194
-rw-r--r--lib/qtime.h307
-rw-r--r--lib/qtimers.c347
-rw-r--r--lib/qtimers.h181
-rw-r--r--lib/routemap.c2
-rw-r--r--lib/thread.c35
-rw-r--r--lib/vector.c103
-rw-r--r--lib/vector.h5
-rw-r--r--lib/vty.c1055
-rw-r--r--lib/vty.h28
-rw-r--r--lib/zassert.h50
-rw-r--r--tests/test-privs.c28
-rw-r--r--vtysh/vtysh.c13
-rw-r--r--vtysh/vtysh_config.c4
42 files changed, 8001 insertions, 648 deletions
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 9d14683c..69767a9f 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -35,6 +35,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "routemap.h"
#include "filter.h"
#include "plist.h"
+#include "qpnexus.h"
+#include "qlib_init.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h"
@@ -65,6 +67,7 @@ static const struct option longopts[] =
{ "version", no_argument, NULL, 'v'},
{ "dryrun", no_argument, NULL, 'C'},
{ "help", no_argument, NULL, 'h'},
+ { "threaded", no_argument, NULL, 't'},
{ 0 }
};
@@ -72,6 +75,7 @@ static const struct option longopts[] =
void sighup (void);
void sigint (void);
void sigusr1 (void);
+void sigusr2 (void);
static void bgp_exit (int);
@@ -86,6 +90,10 @@ static struct quagga_signal_t bgp_signals[] =
.handler = &sigusr1,
},
{
+ .signal = SIGUSR2,
+ .handler = &sigusr2,
+ },
+ {
.signal = SIGINT,
.handler = &sigint,
},
@@ -103,6 +111,8 @@ static int retain_mode = 0;
/* Master of threads. */
struct thread_master *master;
+qpn_nexus cli_nexus = NULL;
+qpn_nexus bgp_nexus = NULL;
/* Manually specified configuration file name. */
char *config_file = NULL;
@@ -160,6 +170,7 @@ redistribution between different routing protocols.\n\n\
-v, --version Print program version\n\
-C, --dryrun Check configuration for validity and exit\n\
-h, --help Display this help and exit\n\
+-t, --threaded Use pthreads\n\
\n\
Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
}
@@ -206,6 +217,18 @@ sigusr1 (void)
zlog_rotate (NULL);
}
+/* SIGUSR2 handler. */
+void
+sigusr2 (void)
+{
+ /* Used to signal message queues */
+ if (qpthreads_enabled)
+ return;
+ else
+ exit(1);
+}
+
+
/*
Try to free up allocations we know about so that diagnostic tools such as
valgrind are able to better illuminate leaks.
@@ -296,7 +319,15 @@ bgp_exit (int status)
if (CONF_BGP_DEBUG (normal, NORMAL))
log_memstats_stderr ("bgpd");
- exit (status);
+ if (bgp_nexus)
+ bgp_nexus->terminate = 1;
+
+ if (cli_nexus)
+ cli_nexus->terminate = 1; /* waste of time, its the main thread */
+
+ /* TODO: join threads ? */
+
+ qexit (status);
}
/* Main routine of bgpd. Treatment of argument and start bgp finite
@@ -311,10 +342,13 @@ main (int argc, char **argv)
char *progname;
struct thread thread;
int tmp_port;
+ int threaded = 0;
/* Set umask before anything for security */
umask (0027);
+ qlib_init_first_stage();
+
/* Preserve name of myself. */
progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
@@ -327,7 +361,7 @@ main (int argc, char **argv)
/* Command line argument treatment. */
while (1)
{
- opt = getopt_long (argc, argv, "df:i:hp:l:A:P:rnu:g:vC", longopts, 0);
+ opt = getopt_long (argc, argv, "df:i:hp:l:A:P:rnu:g:vCt", longopts, 0);
if (opt == EOF)
break;
@@ -392,6 +426,9 @@ main (int argc, char **argv)
case 'h':
usage (progname, 0);
break;
+ case 't':
+ threaded = 1;
+ break;
default:
usage (progname, 1);
break;
@@ -429,10 +466,21 @@ main (int argc, char **argv)
return (1);
}
-
/* Process ID file creation. */
pid_output (pid_file);
+ /* stage 2 initialisation */
+ qlib_init_second_stage(threaded) ;
+
+ if (qpthreads_enabled)
+ {
+ cli_nexus = qpn_init_main(cli_nexus); /* main thread */
+ bgp_nexus = qpn_init_bgp(bgp_nexus);
+
+ zprivs_init_r ();
+ vty_init_r(cli_nexus, bgp_nexus);
+ }
+
/* Make bgp vty socket. */
vty_serv_sock (vty_addr, vty_port, BGP_VTYSH_PATH);
@@ -442,9 +490,18 @@ main (int argc, char **argv)
(bm->address ? bm->address : "<all>"),
bm->port);
- /* Start finite state machine, here we go! */
- while (thread_fetch (master, &thread))
- thread_call (&thread);
+ /* Launch finite state machines */
+ if (qpthreads_enabled)
+ {
+ qpn_exec(bgp_nexus);
+ qpn_exec(cli_nexus); /* must be last to start - on main thread */
+ }
+ else
+ {
+ /* Start finite state machine, here we go! */
+ while (thread_fetch (master, &thread))
+ thread_call (&thread);
+ }
/* Not reached. */
return (0);
diff --git a/configure.ac b/configure.ac
index 9c47b5b4..17ab4387 100755
--- a/configure.ac
+++ b/configure.ac
@@ -128,6 +128,7 @@ if test "x${cflags_specified}" = "x" ; then
CFLAGS="${CFLAGS} -Wbad-function-cast -Wwrite-strings"
CFLAGS="${CFLAGS} -Wmissing-prototypes -Wmissing-declarations"
CFLAGS="${CFLAGS} -Wchar-subscripts -Wcast-qual"
+ CFLAGS="${CFLAGS} -pthread"
# TODO: conditionally addd -Wpacked if handled
AC_MSG_RESULT([gcc default])
;;
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f655ac39..8f518f05 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -12,7 +12,9 @@ libzebra_la_SOURCES = \
sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
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
+ 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
BUILT_SOURCES = memtypes.h route_types.h
@@ -27,7 +29,9 @@ pkginclude_HEADERS = \
str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \
plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \
privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
- workqueue.h route_types.h symtab.h
+ 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
EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.awk route_types.txt
diff --git a/lib/command.c b/lib/command.c
index 60880f49..f1ec15c5 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -32,6 +32,7 @@ Boston, MA 02111-1307, USA. */
#include "vty.h"
#include "command.h"
#include "workqueue.h"
+#include "command_queue.h"
/* Command vector which includes some level of command lists. Normally
each daemon maintains each own cmdvec. */
@@ -548,57 +549,57 @@ config_write_host (struct vty *vty)
vty_out (vty, "enable password %s%s", host.enable, VTY_NEWLINE);
}
- if (zlog_default->default_lvl != LOG_DEBUG)
+ if (zlog_get_default_lvl(NULL) != LOG_DEBUG)
{
vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s",
VTY_NEWLINE);
vty_out (vty, "log trap %s%s",
- zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
+ zlog_priority[zlog_get_default_lvl(NULL)], VTY_NEWLINE);
}
- if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED))
+ if (host.logfile && (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != ZLOG_DISABLED))
{
vty_out (vty, "log file %s", host.logfile);
- if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) != zlog_get_default_lvl(NULL))
vty_out (vty, " %s",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)]);
vty_out (vty, "%s", VTY_NEWLINE);
}
- if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != ZLOG_DISABLED)
{
vty_out (vty, "log stdout");
- if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) != zlog_get_default_lvl(NULL))
vty_out (vty, " %s",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)]);
vty_out (vty, "%s", VTY_NEWLINE);
}
- if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) == ZLOG_DISABLED)
vty_out(vty,"no log monitor%s",VTY_NEWLINE);
- else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl)
+ else if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) != zlog_get_default_lvl(NULL))
vty_out(vty,"log monitor %s%s",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)],VTY_NEWLINE);
- if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != ZLOG_DISABLED)
{
vty_out (vty, "log syslog");
- if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) != zlog_get_default_lvl(NULL))
vty_out (vty, " %s",
- zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG)]);
vty_out (vty, "%s", VTY_NEWLINE);
}
- if (zlog_default->facility != LOG_DAEMON)
+ if (zlog_get_facility(NULL) != LOG_DAEMON)
vty_out (vty, "log facility %s%s",
- facility_name(zlog_default->facility), VTY_NEWLINE);
+ facility_name(zlog_get_facility(NULL)), VTY_NEWLINE);
- if (zlog_default->record_priority == 1)
+ if (zlog_get_record_priority(NULL) == 1)
vty_out (vty, "log record-priority%s", VTY_NEWLINE);
- if (zlog_default->timestamp_precision > 0)
+ if (zlog_get_timestamp_precision(NULL) > 0)
vty_out (vty, "log timestamp precision %d%s",
- zlog_default->timestamp_precision, VTY_NEWLINE);
+ zlog_get_timestamp_precision(NULL), VTY_NEWLINE);
if (host.advanced)
vty_out (vty, "service advanced-vty%s", VTY_NEWLINE);
@@ -1573,7 +1574,7 @@ cmd_try_do_shortcut (enum node_type node, char* first_word) {
/* '?' describe command support. */
static vector
-cmd_describe_command_real (vector vline, struct vty *vty, int *status)
+cmd_describe_command_real (vector vline, int node, int *status)
{
unsigned int i;
vector cmd_vector;
@@ -1595,7 +1596,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
index = vector_active (vline) - 1;
/* Make copy vector of current node's command vector. */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+ cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
/* Prepare match vector */
matchvec = vector_init (INIT_MATCHVEC_SIZE);
@@ -1709,18 +1710,15 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status)
}
vector
-cmd_describe_command (vector vline, struct vty *vty, int *status)
+cmd_describe_command (vector vline, int node, int *status)
{
vector ret;
- if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
+ if ( cmd_try_do_shortcut(node, vector_slot(vline, 0) ) )
{
- enum node_type onode;
- vector shifted_vline;
+ vector shifted_vline;
unsigned int index;
- onode = vty->node;
- vty->node = ENABLE_NODE;
/* We can try it on enable node, cos' the vty is authenticated */
shifted_vline = vector_init (vector_count(vline));
@@ -1730,15 +1728,13 @@ cmd_describe_command (vector vline, struct vty *vty, int *status)
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
- ret = cmd_describe_command_real (shifted_vline, vty, status);
+ ret = cmd_describe_command_real (shifted_vline, ENABLE_NODE, status);
vector_free(shifted_vline);
- vty->node = onode;
return ret;
}
-
- return cmd_describe_command_real (vline, vty, status);
+ return cmd_describe_command_real (vline, node, status);
}
@@ -1777,10 +1773,10 @@ cmd_lcd (char **matched)
/* Command line completion support. */
static char **
-cmd_complete_command_real (vector vline, struct vty *vty, int *status)
+cmd_complete_command_real (vector vline, int node, int *status)
{
unsigned int i;
- vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+ vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, node));
#define INIT_MATCHVEC_SIZE 10
vector matchvec;
struct cmd_element *cmd_element;
@@ -1933,18 +1929,15 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status)
}
char **
-cmd_complete_command (vector vline, struct vty *vty, int *status)
+cmd_complete_command (vector vline, int node, int *status)
{
char **ret;
- if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
+ if ( cmd_try_do_shortcut(node, vector_slot(vline, 0) ) )
{
- enum node_type onode;
vector shifted_vline;
unsigned int index;
- onode = vty->node;
- vty->node = ENABLE_NODE;
/* We can try it on enable node, cos' the vty is authenticated */
shifted_vline = vector_init (vector_count(vline));
@@ -1954,15 +1947,13 @@ cmd_complete_command (vector vline, struct vty *vty, int *status)
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
- ret = cmd_complete_command_real (shifted_vline, vty, status);
+ ret = cmd_complete_command_real (shifted_vline, ENABLE_NODE, status);
vector_free(shifted_vline);
- vty->node = onode;
return ret;
}
-
- return cmd_complete_command_real (vline, vty, status);
+ return cmd_complete_command_real (vline, node, status);
}
/* return parent node */
@@ -1996,7 +1987,7 @@ node_parent ( enum node_type node )
/* Execute command by argument vline vector. */
static int
cmd_execute_command_real (vector vline, struct vty *vty,
- struct cmd_element **cmd)
+ struct cmd_element **cmd, qpn_nexus bgp_nexus)
{
unsigned int i;
unsigned int index;
@@ -2011,7 +2002,7 @@ cmd_execute_command_real (vector vline, struct vty *vty,
char *command;
/* Make copy of command elements. */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty_get_node(vty)));
for (index = 0; index < vector_active (vline); index++)
if ((command = vector_slot (vline, index)))
@@ -2112,23 +2103,32 @@ cmd_execute_command_real (vector vline, struct vty *vty,
return CMD_SUCCESS_DAEMON;
/* Execute matched command. */
- return (*matched_element->func) (matched_element, vty, argc, argv);
+ 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, bgp_nexus);
+ return CMD_QUEUED;
+ }
+ else
+ {
+ return (*matched_element->func) (matched_element, vty, argc, argv);
+ }
}
int
cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
- int vtysh) {
+ qpn_nexus bgp_nexus, int vtysh) {
int ret, saved_ret, tried = 0;
enum node_type onode, try_node;
- onode = try_node = vty->node;
+ onode = try_node = vty_get_node(vty);
- if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) )
+ if ( cmd_try_do_shortcut(vty_get_node(vty), vector_slot(vline, 0) ) )
{
vector shifted_vline;
unsigned int index;
- vty->node = ENABLE_NODE;
+ vty_set_node(vty, ENABLE_NODE);
/* We can try it on enable node, cos' the vty is authenticated */
shifted_vline = vector_init (vector_count(vline));
@@ -2138,26 +2138,26 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
}
- ret = cmd_execute_command_real (shifted_vline, vty, cmd);
+ ret = cmd_execute_command_real (shifted_vline, vty, cmd, bgp_nexus);
vector_free(shifted_vline);
- vty->node = onode;
+ vty_set_node(vty, onode);
return ret;
}
- saved_ret = ret = cmd_execute_command_real (vline, vty, cmd);
+ saved_ret = ret = cmd_execute_command_real (vline, vty, cmd, bgp_nexus);
if (vtysh)
return saved_ret;
/* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
while ( ret != CMD_SUCCESS && ret != CMD_WARNING
- && vty->node > CONFIG_NODE )
+ && vty_get_node(vty) > CONFIG_NODE )
{
try_node = node_parent(try_node);
- vty->node = try_node;
- ret = cmd_execute_command_real (vline, vty, cmd);
+ vty_set_node(vty, try_node);
+ ret = cmd_execute_command_real (vline, vty, cmd, bgp_nexus);
tried = 1;
if (ret == CMD_SUCCESS || ret == CMD_WARNING)
{
@@ -2168,7 +2168,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd,
/* no command succeeded, reset the vty to the original node and
return the error for this node */
if ( tried )
- vty->node = onode;
+ vty_set_node(vty, onode);
return saved_ret;
}
@@ -2190,7 +2190,7 @@ cmd_execute_command_strict (vector vline, struct vty *vty,
char *command;
/* Make copy of command element */
- cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node));
+ cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty_get_node(vty)));
for (index = 0; index < vector_active (vline); index++)
if ((command = vector_slot (vline, index)))
@@ -2310,9 +2310,9 @@ config_from_file (struct vty *vty, FILE *fp)
/* Try again with setting node to CONFIG_NODE */
while (ret != CMD_SUCCESS && ret != CMD_WARNING
- && ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE)
+ && ret != CMD_ERR_NOTHING_TODO && vty_get_node(vty) != CONFIG_NODE)
{
- vty->node = node_parent(vty->node);
+ vty_set_node(vty, node_parent(vty_get_node(vty)));
ret = cmd_execute_command_strict (vline, vty, NULL);
}
@@ -2326,14 +2326,14 @@ config_from_file (struct vty *vty, FILE *fp)
}
/* Configration from terminal */
-DEFUN (config_terminal,
+DEFUN_CALL (config_terminal,
config_terminal_cmd,
"configure terminal",
"Configuration from vty interface\n"
"Configuration terminal\n")
{
if (vty_config_lock (vty))
- vty->node = CONFIG_NODE;
+ vty_set_node(vty, CONFIG_NODE);
else
{
vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
@@ -2343,39 +2343,39 @@ DEFUN (config_terminal,
}
/* Enable command */
-DEFUN (enable,
+DEFUN_CALL (enable,
config_enable_cmd,
"enable",
"Turn on privileged mode command\n")
{
/* If enable password is NULL, change to ENABLE_NODE */
if ((host.enable == NULL && host.enable_encrypt == NULL) ||
- vty->type == VTY_SHELL_SERV)
- vty->node = ENABLE_NODE;
+ vty_get_type(vty) == VTY_SHELL_SERV)
+ vty_set_node(vty, ENABLE_NODE);
else
- vty->node = AUTH_ENABLE_NODE;
+ vty_set_node(vty, AUTH_ENABLE_NODE);
return CMD_SUCCESS;
}
/* Disable command */
-DEFUN (disable,
+DEFUN_CALL (disable,
config_disable_cmd,
"disable",
"Turn off privileged mode command\n")
{
- if (vty->node == ENABLE_NODE)
- vty->node = VIEW_NODE;
+ if (vty_get_node(vty) == ENABLE_NODE)
+ vty_set_node(vty, VIEW_NODE);
return CMD_SUCCESS;
}
/* Down vty node level. */
-DEFUN (config_exit,
+DEFUN_CALL (config_exit,
config_exit_cmd,
"exit",
"Exit current mode and down to previous mode\n")
{
- switch (vty->node)
+ switch (vty_get_node(vty))
{
case VIEW_NODE:
case ENABLE_NODE:
@@ -2383,10 +2383,10 @@ DEFUN (config_exit,
if (vty_shell (vty))
exit (0);
else
- vty->status = VTY_CLOSE;
+ vty_set_status(vty, VTY_CLOSE);
break;
case CONFIG_NODE:
- vty->node = ENABLE_NODE;
+ vty_set_node(vty, ENABLE_NODE);
vty_config_unlock (vty);
break;
case INTERFACE_NODE:
@@ -2401,17 +2401,17 @@ DEFUN (config_exit,
case MASC_NODE:
case RMAP_NODE:
case VTY_NODE:
- vty->node = CONFIG_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->node = BGP_NODE;
+ vty_set_node(vty, BGP_NODE);
break;
case KEYCHAIN_KEY_NODE:
- vty->node = KEYCHAIN_NODE;
+ vty_set_node(vty, KEYCHAIN_NODE);
break;
default:
break;
@@ -2420,18 +2420,18 @@ DEFUN (config_exit,
}
/* quit is alias of exit. */
-ALIAS (config_exit,
+ALIAS_CALL (config_exit,
config_quit_cmd,
"quit",
"Exit current mode and down to previous mode\n")
/* End of configuration. */
-DEFUN (config_end,
+DEFUN_CALL (config_end,
config_end_cmd,
"end",
"End current mode and change to enable mode.")
{
- switch (vty->node)
+ switch (vty_get_node(vty))
{
case VIEW_NODE:
case ENABLE_NODE:
@@ -2458,7 +2458,7 @@ DEFUN (config_end,
case MASC_NODE:
case VTY_NODE:
vty_config_unlock (vty);
- vty->node = ENABLE_NODE;
+ vty_set_node(vty, ENABLE_NODE);
break;
default:
break;
@@ -2467,7 +2467,7 @@ DEFUN (config_end,
}
/* Show version. */
-DEFUN (show_version,
+DEFUN_CALL (show_version,
show_version_cmd,
"show version",
SHOW_STR
@@ -2481,7 +2481,7 @@ DEFUN (show_version,
}
/* Help display function for all node. */
-DEFUN (config_help,
+DEFUN_CALL (config_help,
config_help_cmd,
"help",
"Description of the interactive help system\n")
@@ -2505,26 +2505,25 @@ argument.%s\
}
/* Help display function for all node. */
-DEFUN (config_list,
+DEFUN_CALL (config_list,
config_list_cmd,
"list",
"Print command list\n")
{
unsigned int i;
- struct cmd_node *cnode = vector_slot (cmdvec, vty->node);
+ struct cmd_node *cnode = vector_slot (cmdvec, vty_get_node(vty));
struct cmd_element *cmd;
for (i = 0; i < vector_active (cnode->cmd_vector); i++)
if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL
- && !(cmd->attr == CMD_ATTR_DEPRECATED
- || cmd->attr == CMD_ATTR_HIDDEN))
+ && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN)))
vty_out (vty, " %s%s", cmd->string,
VTY_NEWLINE);
return CMD_SUCCESS;
}
/* Write current configuration into file. */
-DEFUN (config_write_file,
+DEFUN_CALL (config_write_file,
config_write_file_cmd,
"write file",
"Write running configuration to memory, network, or terminal\n"
@@ -2569,9 +2568,7 @@ DEFUN (config_write_file,
}
/* Make vty for configuration file. */
- file_vty = vty_new ();
- file_vty->fd = fd;
- file_vty->type = VTY_FILE;
+ file_vty = vty_new (fd, VTY_FILE);
/* Config file header print. */
vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
@@ -2632,18 +2629,18 @@ finished:
return ret;
}
-ALIAS (config_write_file,
+ALIAS_CALL (config_write_file,
config_write_cmd,
"write",
"Write running configuration to memory, network, or terminal\n")
-ALIAS (config_write_file,
+ALIAS_CALL (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 (config_write_file,
+ALIAS_CALL (config_write_file,
copy_runningconfig_startupconfig_cmd,
"copy running-config startup-config",
"Copy configuration\n"
@@ -2651,7 +2648,7 @@ ALIAS (config_write_file,
"Copy running config to startup config (same as write file)\n")
/* Write current configuration into the terminal. */
-DEFUN (config_write_terminal,
+DEFUN_CALL (config_write_terminal,
config_write_terminal_cmd,
"write terminal",
"Write running configuration to memory, network, or terminal\n"
@@ -2660,7 +2657,7 @@ DEFUN (config_write_terminal,
unsigned int i;
struct cmd_node *node;
- if (vty->type == VTY_SHELL_SERV)
+ if (vty_get_type(vty) == VTY_SHELL_SERV)
{
for (i = 0; i < vector_active (cmdvec); i++)
if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
@@ -2687,14 +2684,14 @@ DEFUN (config_write_terminal,
}
/* Write current configuration into the terminal. */
-ALIAS (config_write_terminal,
+ALIAS_CALL (config_write_terminal,
show_running_config_cmd,
"show running-config",
SHOW_STR
"running configuration\n")
/* Write startup configuration into the terminal. */
-DEFUN (show_startup_config,
+DEFUN_CALL (show_startup_config,
show_startup_config_cmd,
"show startup-config",
SHOW_STR
@@ -2728,7 +2725,7 @@ DEFUN (show_startup_config,
}
/* Hostname configuration */
-DEFUN (config_hostname,
+DEFUN_CALL (config_hostname,
hostname_cmd,
"hostname WORD",
"Set system's network name\n"
@@ -2747,7 +2744,7 @@ DEFUN (config_hostname,
return CMD_SUCCESS;
}
-DEFUN (config_no_hostname,
+DEFUN_CALL (config_no_hostname,
no_hostname_cmd,
"no hostname [HOSTNAME]",
NO_STR
@@ -2761,7 +2758,7 @@ DEFUN (config_no_hostname,
}
/* VTY interface password set. */
-DEFUN (config_password, password_cmd,
+DEFUN_CALL (config_password, password_cmd,
"password (8|) WORD",
"Assign the terminal connection password\n"
"Specifies a HIDDEN password will follow\n"
@@ -2817,13 +2814,13 @@ DEFUN (config_password, password_cmd,
return CMD_SUCCESS;
}
-ALIAS (config_password, password_text_cmd,
+ALIAS_CALL (config_password, password_text_cmd,
"password LINE",
"Assign the terminal connection password\n"
"The UNENCRYPTED (cleartext) line password\n")
/* VTY enable password set. */
-DEFUN (config_enable_password, enable_password_cmd,
+DEFUN_CALL (config_enable_password, enable_password_cmd,
"enable password (8|) WORD",
"Modify enable password parameters\n"
"Assign the privileged level password\n"
@@ -2884,7 +2881,7 @@ DEFUN (config_enable_password, enable_password_cmd,
return CMD_SUCCESS;
}
-ALIAS (config_enable_password,
+ALIAS_CALL (config_enable_password,
enable_password_text_cmd,
"enable password LINE",
"Modify enable password parameters\n"
@@ -2892,7 +2889,7 @@ ALIAS (config_enable_password,
"The UNENCRYPTED (cleartext) 'enable' password\n")
/* VTY enable password delete. */
-DEFUN (no_config_enable_password, no_enable_password_cmd,
+DEFUN_CALL (no_config_enable_password, no_enable_password_cmd,
"no enable password",
NO_STR
"Modify enable password parameters\n"
@@ -2909,7 +2906,7 @@ DEFUN (no_config_enable_password, no_enable_password_cmd,
return CMD_SUCCESS;
}
-DEFUN (service_password_encrypt,
+DEFUN_CALL (service_password_encrypt,
service_password_encrypt_cmd,
"service password-encryption",
"Set up miscellaneous service\n"
@@ -2936,7 +2933,7 @@ DEFUN (service_password_encrypt,
return CMD_SUCCESS;
}
-DEFUN (no_service_password_encrypt,
+DEFUN_CALL (no_service_password_encrypt,
no_service_password_encrypt_cmd,
"no service password-encryption",
NO_STR
@@ -2959,7 +2956,7 @@ DEFUN (no_service_password_encrypt,
return CMD_SUCCESS;
}
-DEFUN (config_terminal_length, config_terminal_length_cmd,
+DEFUN_CALL (config_terminal_length, config_terminal_length_cmd,
"terminal length <0-512>",
"Set terminal line parameters\n"
"Set number of lines on a screen\n"
@@ -2974,22 +2971,22 @@ DEFUN (config_terminal_length, config_terminal_length_cmd,
vty_out (vty, "length is malformed%s", VTY_NEWLINE);
return CMD_WARNING;
}
- vty->lines = lines;
+ vty_set_lines(vty, lines);
return CMD_SUCCESS;
}
-DEFUN (config_terminal_no_length, config_terminal_no_length_cmd,
+DEFUN_CALL (config_terminal_no_length, config_terminal_no_length_cmd,
"terminal no length",
"Set terminal line parameters\n"
NO_STR
"Set number of lines on a screen\n")
{
- vty->lines = -1;
+ vty_set_lines(vty, -1);
return CMD_SUCCESS;
}
-DEFUN (service_terminal_length, service_terminal_length_cmd,
+DEFUN_CALL (service_terminal_length, service_terminal_length_cmd,
"service terminal-length <0-512>",
"Set up miscellaneous service\n"
"System wide terminal length configuration\n"
@@ -3009,7 +3006,7 @@ DEFUN (service_terminal_length, service_terminal_length_cmd,
return CMD_SUCCESS;
}
-DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
+DEFUN_CALL (no_service_terminal_length, no_service_terminal_length_cmd,
"no service terminal-length [<0-512>]",
NO_STR
"Set up miscellaneous service\n"
@@ -3020,7 +3017,7 @@ DEFUN (no_service_terminal_length, no_service_terminal_length_cmd,
return CMD_SUCCESS;
}
-DEFUN_HIDDEN (do_echo,
+DEFUN_HID_CALL (do_echo,
echo_cmd,
"echo .MESSAGE",
"Echo a message back to the vty\n"
@@ -3035,7 +3032,7 @@ DEFUN_HIDDEN (do_echo,
return CMD_SUCCESS;
}
-DEFUN (config_logmsg,
+DEFUN_CALL (config_logmsg,
config_logmsg_cmd,
"logmsg "LOG_LEVELS" .MESSAGE",
"Send a message to enabled logging destinations\n"
@@ -3048,76 +3045,79 @@ DEFUN (config_logmsg,
if ((level = level_match(argv[0])) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
- zlog(NULL, level, ((message = argv_concat(argv, argc, 1)) ? message : ""));
+ message = argv_concat(argv, argc, 1);
+ zlog(NULL, level, "%s", (message ? message : ""));
if (message)
XFREE(MTYPE_TMP, message);
return CMD_SUCCESS;
}
-DEFUN (show_logging,
+DEFUN_CALL (show_logging,
show_logging_cmd,
"show logging",
SHOW_STR
"Show current logging configuration\n")
{
- struct zlog *zl = zlog_default;
-
vty_out (vty, "Syslog logging: ");
- if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG) == ZLOG_DISABLED)
vty_out (vty, "disabled");
else
vty_out (vty, "level %s, facility %s, ident %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
- facility_name(zl->facility), zl->ident);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_SYSLOG)],
+ facility_name(zlog_get_facility(NULL)), zlog_get_ident(NULL));
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "Stdout logging: ");
- if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT) == ZLOG_DISABLED)
vty_out (vty, "disabled");
else
vty_out (vty, "level %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_STDOUT)]);
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "Monitor logging: ");
- if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+ if (zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR) == ZLOG_DISABLED)
vty_out (vty, "disabled");
else
vty_out (vty, "level %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_MONITOR)]);
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "File logging: ");
- if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
- !zl->fp)
+ if ((zlog_get_maxlvl(NULL, ZLOG_DEST_FILE) == ZLOG_DISABLED) ||
+ !zlog_is_file(NULL))
vty_out (vty, "disabled");
else
- vty_out (vty, "level %s, filename %s",
- zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
- zl->filename);
+ {
+ char * filename = zlog_get_filename(NULL);
+ vty_out (vty, "level %s, filename %s",
+ zlog_priority[zlog_get_maxlvl(NULL, ZLOG_DEST_FILE)],
+ filename);
+ free(filename);
+ }
vty_out (vty, "%s", VTY_NEWLINE);
vty_out (vty, "Protocol name: %s%s",
- zlog_proto_names[zl->protocol], VTY_NEWLINE);
+ zlog_get_proto_name(NULL), VTY_NEWLINE);
vty_out (vty, "Record priority: %s%s",
- (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
+ (zlog_get_record_priority(NULL) ? "enabled" : "disabled"), VTY_NEWLINE);
vty_out (vty, "Timestamp precision: %d%s",
- zl->timestamp_precision, VTY_NEWLINE);
+ zlog_get_timestamp_precision(NULL), VTY_NEWLINE);
return CMD_SUCCESS;
}
-DEFUN (config_log_stdout,
+DEFUN_CALL (config_log_stdout,
config_log_stdout_cmd,
"log stdout",
"Logging control\n"
"Set stdout logging level\n")
{
- zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
+ zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_get_default_lvl(NULL));
return CMD_SUCCESS;
}
-DEFUN (config_log_stdout_level,
+DEFUN_CALL (config_log_stdout_level,
config_log_stdout_level_cmd,
"log stdout "LOG_LEVELS,
"Logging control\n"
@@ -3132,7 +3132,7 @@ DEFUN (config_log_stdout_level,
return CMD_SUCCESS;
}
-DEFUN (no_config_log_stdout,
+DEFUN_CALL (no_config_log_stdout,
no_config_log_stdout_cmd,
"no log stdout [LEVEL]",
NO_STR
@@ -3144,17 +3144,17 @@ DEFUN (no_config_log_stdout,
return CMD_SUCCESS;
}
-DEFUN (config_log_monitor,
+DEFUN_CALL (config_log_monitor,
config_log_monitor_cmd,
"log monitor",
"Logging control\n"
"Set terminal line (monitor) logging level\n")
{
- zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
+ zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_get_default_lvl(NULL));
return CMD_SUCCESS;
}
-DEFUN (config_log_monitor_level,
+DEFUN_CALL (config_log_monitor_level,
config_log_monitor_level_cmd,
"log monitor "LOG_LEVELS,
"Logging control\n"
@@ -3169,7 +3169,7 @@ DEFUN (config_log_monitor_level,
return CMD_SUCCESS;
}
-DEFUN (no_config_log_monitor,
+DEFUN_CALL (no_config_log_monitor,
no_config_log_monitor_cmd,
"no log monitor [LEVEL]",
NO_STR
@@ -3231,17 +3231,17 @@ set_log_file(struct vty *vty, const char *fname, int loglevel)
return CMD_SUCCESS;
}
-DEFUN (config_log_file,
+DEFUN_CALL (config_log_file,
config_log_file_cmd,
"log file FILENAME",
"Logging control\n"
"Logging to file\n"
"Logging filename\n")
{
- return set_log_file(vty, argv[0], zlog_default->default_lvl);
+ return set_log_file(vty, argv[0], zlog_get_default_lvl(NULL));
}
-DEFUN (config_log_file_level,
+DEFUN_CALL (config_log_file_level,
config_log_file_level_cmd,
"log file FILENAME "LOG_LEVELS,
"Logging control\n"
@@ -3256,7 +3256,7 @@ DEFUN (config_log_file_level,
return set_log_file(vty, argv[0], level);
}
-DEFUN (no_config_log_file,
+DEFUN_CALL (no_config_log_file,
no_config_log_file_cmd,
"no log file [FILENAME]",
NO_STR
@@ -3274,7 +3274,7 @@ DEFUN (no_config_log_file,
return CMD_SUCCESS;
}
-ALIAS (no_config_log_file,
+ALIAS_CALL (no_config_log_file,
no_config_log_file_level_cmd,
"no log file FILENAME LEVEL",
NO_STR
@@ -3283,17 +3283,17 @@ ALIAS (no_config_log_file,
"Logging file name\n"
"Logging level\n")
-DEFUN (config_log_syslog,
+DEFUN_CALL (config_log_syslog,
config_log_syslog_cmd,
"log syslog",
"Logging control\n"
"Set syslog logging level\n")
{
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+ zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_get_default_lvl(NULL));
return CMD_SUCCESS;
}
-DEFUN (config_log_syslog_level,
+DEFUN_CALL (config_log_syslog_level,
config_log_syslog_level_cmd,
"log syslog "LOG_LEVELS,
"Logging control\n"
@@ -3308,7 +3308,7 @@ DEFUN (config_log_syslog_level,
return CMD_SUCCESS;
}
-DEFUN_DEPRECATED (config_log_syslog_facility,
+DEFUN_DEP_CALL (config_log_syslog_facility,
config_log_syslog_facility_cmd,
"log syslog facility "LOG_FACILITIES,
"Logging control\n"
@@ -3321,12 +3321,12 @@ DEFUN_DEPRECATED (config_log_syslog_facility,
if ((facility = facility_match(argv[0])) < 0)
return CMD_ERR_NO_MATCH;
- zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
- zlog_default->facility = facility;
+ zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_get_default_lvl(NULL));
+ zlog_set_facility(NULL, facility);
return CMD_SUCCESS;
}
-DEFUN (no_config_log_syslog,
+DEFUN_CALL (no_config_log_syslog,
no_config_log_syslog_cmd,
"no log syslog [LEVEL]",
NO_STR
@@ -3338,7 +3338,7 @@ DEFUN (no_config_log_syslog,
return CMD_SUCCESS;
}
-ALIAS (no_config_log_syslog,
+ALIAS_CALL (no_config_log_syslog,
no_config_log_syslog_facility_cmd,
"no log syslog facility "LOG_FACILITIES,
NO_STR
@@ -3347,7 +3347,7 @@ ALIAS (no_config_log_syslog,
"Facility parameter for syslog messages\n"
LOG_FACILITY_DESC)
-DEFUN (config_log_facility,
+DEFUN_CALL (config_log_facility,
config_log_facility_cmd,
"log facility "LOG_FACILITIES,
"Logging control\n"
@@ -3358,11 +3358,11 @@ DEFUN (config_log_facility,
if ((facility = facility_match(argv[0])) < 0)
return CMD_ERR_NO_MATCH;
- zlog_default->facility = facility;
+ zlog_set_facility(NULL, facility);
return CMD_SUCCESS;
}
-DEFUN (no_config_log_facility,
+DEFUN_CALL (no_config_log_facility,
no_config_log_facility_cmd,
"no log facility [FACILITY]",
NO_STR
@@ -3370,11 +3370,11 @@ DEFUN (no_config_log_facility,
"Reset syslog facility to default (daemon)\n"
"Syslog facility\n")
{
- zlog_default->facility = LOG_DAEMON;
+ zlog_set_facility(NULL, LOG_DAEMON);
return CMD_SUCCESS;
}
-DEFUN_DEPRECATED (config_log_trap,
+DEFUN_DEP_CALL (config_log_trap,
config_log_trap_cmd,
"log trap "LOG_LEVELS,
"Logging control\n"
@@ -3382,19 +3382,15 @@ DEFUN_DEPRECATED (config_log_trap,
LOG_LEVEL_DESC)
{
int new_level ;
- int i;
if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
return CMD_ERR_NO_MATCH;
- zlog_default->default_lvl = new_level;
- for (i = 0; i < ZLOG_NUM_DESTS; i++)
- if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
- zlog_default->maxlvl[i] = new_level;
+ zlog_set_default_lvl_dest (NULL, new_level);
return CMD_SUCCESS;
}
-DEFUN_DEPRECATED (no_config_log_trap,
+DEFUN_DEP_CALL (no_config_log_trap,
no_config_log_trap_cmd,
"no log trap [LEVEL]",
NO_STR
@@ -3402,32 +3398,32 @@ DEFUN_DEPRECATED (no_config_log_trap,
"Permit all logging information\n"
"Logging level\n")
{
- zlog_default->default_lvl = LOG_DEBUG;
+ zlog_set_default_lvl(NULL, LOG_DEBUG);
return CMD_SUCCESS;
}
-DEFUN (config_log_record_priority,
+DEFUN_CALL (config_log_record_priority,
config_log_record_priority_cmd,
"log record-priority",
"Logging control\n"
"Log the priority of the message within the message\n")
{
- zlog_default->record_priority = 1 ;
+ zlog_set_record_priority(NULL, 1) ;
return CMD_SUCCESS;
}
-DEFUN (no_config_log_record_priority,
+DEFUN_CALL (no_config_log_record_priority,
no_config_log_record_priority_cmd,
"no log record-priority",
NO_STR
"Logging control\n"
"Do not log the priority of the message within the message\n")
{
- zlog_default->record_priority = 0 ;
+ zlog_set_record_priority(NULL, 0) ;
return CMD_SUCCESS;
}
-DEFUN (config_log_timestamp_precision,
+DEFUN_CALL (config_log_timestamp_precision,
config_log_timestamp_precision_cmd,
"log timestamp precision <0-6>",
"Logging control\n"
@@ -3435,6 +3431,8 @@ DEFUN (config_log_timestamp_precision,
"Set the timestamp precision\n"
"Number of subsecond digits\n")
{
+ int timestamp_precision;
+
if (argc != 1)
{
vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
@@ -3442,11 +3440,13 @@ DEFUN (config_log_timestamp_precision,
}
VTY_GET_INTEGER_RANGE("Timestamp Precision",
- zlog_default->timestamp_precision, argv[0], 0, 6);
+ timestamp_precision, argv[0], 0, 6);
+ zlog_set_timestamp_precision(NULL, timestamp_precision);
+
return CMD_SUCCESS;
}
-DEFUN (no_config_log_timestamp_precision,
+DEFUN_CALL (no_config_log_timestamp_precision,
no_config_log_timestamp_precision_cmd,
"no log timestamp precision",
NO_STR
@@ -3454,11 +3454,11 @@ DEFUN (no_config_log_timestamp_precision,
"Timestamp configuration\n"
"Reset the timestamp precision to the default value of 0\n")
{
- zlog_default->timestamp_precision = 0 ;
+ zlog_set_timestamp_precision(NULL, 0);
return CMD_SUCCESS;
}
-DEFUN (banner_motd_file,
+DEFUN_CALL (banner_motd_file,
banner_motd_file_cmd,
"banner motd file [FILE]",
"Set banner\n"
@@ -3473,7 +3473,7 @@ DEFUN (banner_motd_file,
return CMD_SUCCESS;
}
-DEFUN (banner_motd_default,
+DEFUN_CALL (banner_motd_default,
banner_motd_default_cmd,
"banner motd default",
"Set banner string\n"
@@ -3484,7 +3484,7 @@ DEFUN (banner_motd_default,
return CMD_SUCCESS;
}
-DEFUN (no_banner_motd,
+DEFUN_CALL (no_banner_motd,
no_banner_motd_cmd,
"no banner motd",
NO_STR
diff --git a/lib/command.h b/lib/command.h
index 1275efee..9cc472a7 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -126,8 +126,10 @@ struct cmd_node
enum
{
- CMD_ATTR_DEPRECATED = 1,
- CMD_ATTR_HIDDEN,
+ /* bit significant */
+ CMD_ATTR_DEPRECATED = 0x01,
+ CMD_ATTR_HIDDEN = 0x02,
+ CMD_ATTR_CALL = 0x04,
};
/* Structure of command element. */
@@ -163,6 +165,7 @@ struct desc
#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
@@ -205,8 +208,17 @@ struct desc
#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+#define DEFUN_HID_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, (CMD_ATTR_CALL | CMD_ATTR_HIDDEN))
+
#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
+
+#define DEFUN_DEP_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, (CMD_ATTR_CALL | CMD_ATTR_DEPRECATED))
+
+#define DEFUN_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_CALL)
/* DEFUN_NOSH for commands that vtysh should ignore */
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
@@ -214,7 +226,7 @@ struct desc
/* DEFSH for vtysh. */
#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
- DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \
+ DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon)
/* DEFUN + DEFSH */
#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
@@ -247,6 +259,9 @@ struct desc
#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
+#define ALIAS_CALL(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_CALL, 0)
+
#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
@@ -338,12 +353,13 @@ extern char *argv_concat (const char **argv, int argc, int shift);
extern vector cmd_make_strvec (const char *);
extern void cmd_free_strvec (vector);
-extern vector cmd_describe_command (vector, struct vty *, int *status);
-extern char **cmd_complete_command (vector, struct vty *, int *status);
+extern vector cmd_describe_command (vector, int, int *status);
+extern char **cmd_complete_command (vector, int, int *status);
extern const char *cmd_prompt (enum node_type);
extern int config_from_file (struct vty *, FILE *);
extern enum node_type node_parent (enum node_type);
-extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int);
+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 void config_replace_string (struct cmd_element *, char *, ...);
extern void cmd_init (int);
diff --git a/lib/command_queue.c b/lib/command_queue.c
new file mode 100644
index 00000000..9ca32e19
--- /dev/null
+++ b/lib/command_queue.c
@@ -0,0 +1,87 @@
+/* Command Message Queue
+ * 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 <zebra.h>
+
+#include "mqueue.h"
+#include "qpnexus.h"
+#include "memory.h"
+#include "command_queue.h"
+
+/* Prototypes */
+static void cq_action(mqueue_block mqb);
+
+/* We have too many parameters for a message queue block so have to marshal */
+struct marshal
+{
+ struct cmd_element *matched_element;
+ struct vty *vty;
+ int argc;
+ char **argv;
+};
+
+void
+cq_enqueue(struct cmd_element *matched_element, struct vty *vty,
+ int argc, const char *argv[], qpn_nexus bgp_nexus)
+{
+ struct marshal *wyatt = XCALLOC(MTYPE_MARSHAL, sizeof(struct marshal)) ;
+ int i;
+ mqueue_block mqb = NULL;
+
+ wyatt->matched_element = matched_element;
+ wyatt->vty = vty;
+ wyatt->argc = argc;
+ wyatt->argv = argc ? XCALLOC(MTYPE_MARSHAL, sizeof (char*) * argc) : NULL;
+ for (i = 0; i < argc; ++i)
+ {
+ wyatt->argv[i] = XSTRDUP(MTYPE_MARSHAL, argv[i]);
+ }
+
+ mqb = mqb_init_new(mqb, cq_action, 0) ;
+ mqb_set_arg0_p(mqb, wyatt);
+ mqueue_enqueue(bgp_nexus->queue, mqb, 0) ;
+}
+
+/* dispatch a command from the message queue block */
+static void
+cq_action(mqueue_block mqb)
+{
+ int result;
+ int i;
+ struct marshal *wyatt = mqb_get_arg0_p(mqb);
+
+ /* Execute matched command. */
+ result = (*wyatt->matched_element->func)
+ (wyatt->matched_element, wyatt->vty, wyatt->argc, (const char **)wyatt->argv);
+
+ /* report */
+ vty_queued_result(wyatt->vty, result);
+
+ /* clean up */
+ for (i = 0; i< wyatt->argc; ++i)
+ {
+ XFREE(MTYPE_MARSHAL, wyatt->argv[i]);
+ }
+ if (wyatt->argv)
+ XFREE(MTYPE_MARSHAL, wyatt->argv);
+ XFREE(MTYPE_MARSHAL, wyatt);
+ mqb_free(mqb);
+}
diff --git a/lib/command_queue.h b/lib/command_queue.h
new file mode 100644
index 00000000..90a4f7b6
--- /dev/null
+++ b/lib/command_queue.h
@@ -0,0 +1,31 @@
+/* Command Message Queue -- 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 COMMAND_QUEUE_H_
+#define COMMAND_QUEUE_H_
+
+#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);
+
+#endif /* COMMAND_QUEUE_H_ */
diff --git a/lib/filter.c b/lib/filter.c
index af8d587f..57109854 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -1584,7 +1584,7 @@ filter_show (struct vty *vty, const char *name, afi_t afi)
/* Print the name of the protocol */
if (zlog_default)
vty_out (vty, "%s:%s",
- zlog_proto_names[zlog_default->protocol], VTY_NEWLINE);
+ zlog_get_proto_name(NULL), VTY_NEWLINE);
for (access = master->num.head; access; access = access->next)
{
diff --git a/lib/heap.c b/lib/heap.c
new file mode 100644
index 00000000..35a1b51d
--- /dev/null
+++ b/lib/heap.c
@@ -0,0 +1,517 @@
+/* Generic heap data structure -- functions.
+ * 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 <zebra.h>
+
+#include "zassert.h"
+#include "heap.h"
+#include "memory.h"
+
+/* Heaps are implemented as a structure which includes a vector structure.
+ * So items in the heap are items in a vector, which are pointers to the item
+ * values.
+ *
+ * The heap structure may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the heap operations
+ * require the address of the heap structure -- see typedef for heap.
+ *
+ * An essential component of a heap is its comparison function. So, a heap
+ * CANNOT be used before it has been initialised and the comparison function
+ * set. (This is unlike a vector, which may be implicitly initialised empty by
+ * zeroising the vector structure.)
+ *
+ * Items may be pushed onto or popped off the heap, which is organised so that
+ * the top item has the smallest value -- according to the heap's comparison
+ * function. (For equal values, the order is undefined.)
+ *
+ * The top item in the heap may be examined, and its value may be updated.
+ * (Updating the top item is more efficient than popping and then pushing it.)
+ *
+ * Items may be deleted from the heap. Items may have their value updated
+ * while they are in the heap. Both of these operations cause the heap to be
+ * reorganised to maintain the heap's partial ordering. Note: these operations
+ * require knowledge of where in the heap the item is -- which, by default, is
+ * done by a linear scan of the heap. For large heaps, there is the option to
+ * keep a "backlink" from the item to it's heap position.
+ *
+ * Vectors may be pushed onto a heap -- copying or moving the contents.
+ *
+ * Heaps may popped to a vector -- copying or moving the contents. The
+ * resulting vector is fully sorted.
+ *
+ * ----------------------------
+ * Comparison function for heap.
+ *
+ * int heap_cmp(...** a, ...** b) ...
+ *
+ * Must return -1, 0, +1 : where -1 => a < b, 0 => a == b & +1 => a > b
+ *
+ * Heap will sort "smallest" to the top. If you want the biggest at the top,
+ * return -1 * usual comparison. Note that the effective heap ordering for
+ * equal values is, essentially, random.
+ *
+ * NB: like other comparison functions (cf qsort) the parameters are pointers
+ * to pointers to the value.
+ *
+ * NB: there should never be NULL items in the heap.
+ */
+
+/*==============================================================================
+ * Initialisation, allocation, reset etc.
+ */
+
+static heap
+heap_setup(heap h, int new_vector, vector_index size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset) ;
+
+/* Initialize heap -- allocating heap structure if required.
+ *
+ * Does not allocate the underlying vector if the heap is initialised empty.
+ *
+ * eg:
+ *
+ * ... = heap_new_init_simple(NULL, 0, (heap_cmp*)my_cmp)
+ *
+ * See: #define heap_init_new_simple(h, size, cmp)
+ * #define heap_init_new_backlinked(h, size, cmp, offset)
+ *
+ * NB: when initialising an existing heap structure it is ESSENTIAL that
+ * any previous heap and its contents have been released, because this
+ * function simply discards whatever was there before. (This function may
+ * be called to initialise a heap structure which has never been
+ * initialised.)
+ *
+ * Backlink:
+ *
+ * The heap_delete_item and heap_update_item functions need the heap
+ * position of the item. The default way of finding that is to scan the
+ * underlying heap array, looking for the address of the item.
+ *
+ * If either of these functions is done often and on large heaps, it is
+ * possible to speed this up by implementing a 'backlink'. This requires
+ * a field of type heap_backlink_t in the item structure, and it is the
+ * offset of that which must be initialised here, eg:
+ *
+ * ... = heap_new_init_backlinked(NULL, 0, (heap_cmp*)my_cmp,
+ * offset_of(struct xxx_heap_item, backlink)) ;
+ *
+ * This adds a little extra work to every change in the heap -- keeping the
+ * backlink of any moved item up to date. But avoids a linear search for
+ * every heap_delete_item or heap_update_item.
+ *
+ * Returns the heap which has been initialised.
+ */
+heap
+heap_init_new(heap h, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset)
+{
+ if (h == NULL)
+ h = XCALLOC(MTYPE_HEAP, sizeof(struct heap)) ;
+ else
+ memset(h, 0, sizeof(struct heap)) ;
+
+ return heap_setup(h, 1, size, cmp, with_backlink, backlink_offset) ;
+} ;
+
+/* Reinitialise heap (or create a new one, if h == NULL).
+ *
+ * Allocates heap structure if none given -- allocating vector if size != 0.
+ * Otherwise, re-initialise the heap and any vector (reusing its memory).
+ *
+ * See: #define heap_re_init_simple(h, size, cmp)
+ * #define heap_re_init_backlinked(h, size, cmp, offset)
+ *
+ * NB: when reinitialising an existing heap it is the caller's
+ * responsibility to release any item values *before* doing this.
+ *
+ * Returns the heap that has been reinitialised.
+ */
+heap
+heap_re_init(heap h, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset)
+{
+ if (h == NULL)
+ return heap_init_new(h, size, cmp, with_backlink, backlink_offset) ;
+ else
+ return heap_setup(h, 0, size, cmp, with_backlink, backlink_offset) ;
+} ;
+
+/* Release heap contents (underlying vector), and (if required) release the
+ * heap structure.
+ *
+ * Returns NULL if releases heap, otherwise the reset heap.
+ *
+ * If does not release the heap, it retains the comparison function and any
+ * backlink setting -- so heap can be reused without reinitialising it.
+ *
+ * NB: it is the callers responsibility to release any heap item values
+ * *before* doing this.
+ */
+heap
+heap_reset(heap h, int free_structure)
+{
+ vector_reset_keep(&h->v) ; /* vector structure is embedded in the heap */
+
+ if (free_structure)
+ XFREE(MTYPE_VECTOR, h) ; /* sets h = NULL */
+
+ return h ;
+} ;
+
+/* Common set-up for heap_init_new() & heap_reset().
+ */
+static heap
+heap_setup(heap h, int new_vector, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset)
+{
+ assert(cmp != NULL) ; /* or there will be tears */
+
+ h->cmp = cmp ;
+ h->state = with_backlink ? Heap_Has_Backlink : 0 ;
+ h->backlink_offset = backlink_offset ;
+
+ if (new_vector)
+ vector_init_new(&h->v, size) ;
+ else
+ vector_re_init(&h->v, size) ;
+
+ return h ;
+} ;
+
+/* Ream (another) item out of the given heap.
+ *
+ * If heap is empty, release the underlying vector, and (if required) release
+ * the heap structure.
+ *
+ * See: #define heap_ream_free(h) heap_ream(h, 1)
+ * #define heap_ream_keep(h) heap_ream(h, 0)
+ *
+ * Useful for emptying out and resetting/discarding a heap:
+ *
+ * while ((p_v = heap_ream_free(h)))
+ * ... do what's required to release the item p_v
+ *
+ * Returns NULL when heap is empty (and structure has been freed, if required).
+ *
+ * If does not release the heap, it retains the comparison function and any
+ * backlink setting -- so heap can be reused without reinitialising it.
+ *
+ * NB: once the process of reaming a heap has started: (a) MUST NOT attempt to
+ * use the heap until process completes, and (b) MUST complete the process.
+ *
+ * NB: items are reamed out in no defined order.
+ */
+p_vector_item
+heap_ream(heap h, int free_structure)
+{
+ p_vector_item p_v ;
+
+ if (h == NULL)
+ return NULL ;
+
+ if ((p_v = vector_ream_keep(&h->v)) == NULL)
+ heap_reset(h, free_structure) ;
+
+ return p_v ;
+} ;
+
+/*==============================================================================
+ * Simple Heap Operations -- see also the Inline functions.
+ */
+
+/* Pop item off the heap.
+ *
+ * Returns the popped value, which is NULL if the heap was (and still is) empty.
+ */
+p_vector_item
+heap_pop_item(heap h)
+{
+ p_vector_item p_v ;
+ p_vector_item p_x ;
+
+ p_v = vector_pop_item(&h->v) ; /* extract last item, if any */
+ if ((p_v == NULL) || (h->v.end == 0))
+ return p_v ; /* done if empty or last was also first */
+
+ p_x = h->v.p_items[0] ; /* this is what we are popping */
+
+ heap_bubble_down(h, 0, p_v) ; /* reposition what was the last item */
+ /* updating any backlink */
+ return p_x ;
+} ;
+
+/* Pop one item off the heap and promptly push another.
+ *
+ * In this combination, the pop is essentially free.
+ *
+ * Returns the popped value, which is NULL if the heap was (and still is) empty.
+ */
+p_vector_item
+heap_pop_push_item(heap h, p_vector_item p_v)
+{
+ p_vector_item p_x ;
+
+ dassert(p_v != NULL) ; /* no NULLs, thank you. */
+
+ p_x = heap_top_item(h) ; /* what we are popping */
+
+ if (p_x == NULL)
+ heap_push_item(h, p_v) ; /* for empty heap, this deals with */
+ /* extending heap etc. */
+ else
+ heap_bubble_down(h, 0, p_v) ; /* position the replacement */
+ /* setting any backlink */
+ return p_x ;
+} ;
+
+/*==============================================================================
+ * Heap Operations which use 'backlink', if implemented.
+ */
+
+/* Delete given item from the heap.
+ *
+ * See notes on backlink, above.
+ *
+ * NB: do NOT try this on items which are not in the given heap !
+ */
+void
+heap_delete_item(heap h, p_vector_item p_v)
+{
+ p_vector_item p_x ;
+ vector_index i ;
+
+ i = heap_find_item(h, p_v) ; /* index of item to be deleted */
+
+ p_x = vector_pop_item(&h->v) ; /* extract last item, if any */
+
+ if (i < h->v.end) /* if not deleting the last item... */
+ heap_bubble(h, i, p_x) ; /* ...reinsert what was last, at the delete */
+ /* position, updating any backlink */
+} ;
+
+/*==============================================================================
+ * Other Heap Operations.
+ */
+
+/* Push entire vector onto heap copying or moving items as required.
+ *
+ * Copy or move vector to end of heap's vector, then move each
+ * (non-NULL) item into heap order (discarding any NULL items).
+ *
+ * See: #define heap_push_vector_copy(h, v)
+ * #define heap_push_vector_move(h, v)
+ */
+void
+heap_push_vector(heap h, vector v, int move_vector)
+{
+ vector_index i = h->v.end ;
+ vector_index e ;
+ vector_index n = v->end ;
+ p_vector_item p_v ;
+
+ if (move_vector)
+ vector_move_append(&h->v, v) ;
+ else
+ vector_copy_append(&h->v, v) ;
+
+ e = i ; /* old end of the heap. */
+ while (n--) {
+ p_v = h->v.p_items[i++] ;
+ if (p_v != NULL)
+ heap_bubble_up(h, e++, p_v) ; /* move new item into position in heap */
+ /* setting any backlink */
+ } ;
+
+ h->v.end = e ; /* new end of heap */
+} ;
+
+/* Pop given heap to vector -- creating vector if required (v == NULL).
+ *
+ * Resulting vector is fully sorted.
+ *
+ * Moves or copies the contents of the heap.
+ *
+ * See: #define heap_pop_vector_copy(v, h)
+ * #define heap_pop_vector_move(v, h)
+ *
+ * NB: when creating new vector, will be exactly the required size.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ */
+vector
+heap_pop_vector(vector v, heap h, int move_heap)
+{
+ vector_index n = h->v.end ;
+ vector_index i ;
+
+ v = vector_re_init(v, n) ; /* guarantees >= 'n' items in vector */
+ v->end = n ;
+
+ for (i = 0 ; i < n ; i++)
+ v->p_items[i] = heap_pop_item(h) ;
+
+ if (!move_heap)
+ vector_copy_here(&h->v, v) ; /* fully sorted is also heap ordered ! */
+
+ return v ;
+} ;
+
+/*==============================================================================
+ * The Heap internal mechanics.
+ */
+
+/* Returns pointer to backlink value in heap item: lvalue or rvalue */
+#define HEAP_BACKLINK(h, p_v) \
+ *(heap_backlink_t*)((char*)(p_v) + (h)->backlink_offset)
+/* Sets backlink, if required. */
+#define heap_set_backlink(h, p_v, i) \
+ if ((h)->state & Heap_Has_Backlink) HEAP_BACKLINK(h, p_v) = (i)
+
+/* Returns index of parent. */
+#define HEAP_UP(i) (((i) - 1) / 2)
+/* Returns index of left child. */
+#define HEAP_DOWN(i) (((i) * 2) + 1)
+
+/* Insert given item in the required place in heap, given that there is now
+ * a hole at the given position -- may move up or down the heap, or stay put.
+ *
+ * Bubbles up or down as required.
+ *
+ * Note that this sets the backlink on the given item.
+ */
+private void
+heap_bubble(heap h, vector_index i, p_vector_item p_v)
+{
+ /* If this is < parent, we bubble upwards. */
+ if ((i != 0) && (h->cmp(&p_v, &h->v.p_items[HEAP_UP(i)]) < 0))
+ heap_bubble_up(h, i, p_v) ;
+ /* Otherwise we try bubbling downwards. */
+ else
+ heap_bubble_down(h, i, p_v) ;
+} ;
+
+/* Insert given item in the required place in heap, given that there is now
+ * a hole at the given position -- where we know may *only* move up the heap.
+ *
+ * Note that this sets the backlink on the given item.
+ *
+ * NB: ignores anything in the heap beyond 'i' -- in particular does not use
+ * v.end at all. So this can be used to work along a vector and bring
+ * items into heap order.
+ */
+private void
+heap_bubble_up(heap h, vector_index i, p_vector_item p_v)
+{
+ p_vector_item* ha = h->v.p_items ; /* underlying array */
+ vector_index ip ; /* index of parent */
+ p_vector_item p_p ; /* pointer to parent item */
+
+ dassert(ha != NULL) ;
+
+ while (i != 0)
+ {
+ ip = HEAP_UP(i) ;
+ p_p = &ha[ip] ; /* get parent */
+
+ if (h->cmp(&p_v, &p_p) >= 0)
+ break ; /* stop when value >= parent */
+
+ ha[i] = p_p ; /* move parent down... */
+ heap_set_backlink(h, p_p, i) ; /* ...updating any backlink */
+
+ i = ip ; /* move up the heap */
+ } ;
+
+ ha[i] = p_v ; /* place in new position... */
+ heap_set_backlink(h, p_v, i) ; /* ...updating any backlink */
+} ;
+
+/* Insert given item in the required place in heap, given that there is now
+ * a hole at the given position -- where we know may *only* move down the heap.
+ *
+ * Note that this sets the backlink on the given item.
+ */
+private void
+heap_bubble_down(heap h, vector_index i, p_vector_item p_v)
+{
+ vector_index e = h->v.end ; /* end of heap */
+ vector_index ic ; /* index of child */
+ vector_index is ; /* index of sibling */
+ p_vector_item p_c ; /* pointer to child */
+ p_vector_item p_s ; /* pointer to sibling */
+
+ p_vector_item* ha = h->v.p_items ; /* underlying array */
+ dassert(ha != NULL) ;
+
+ while (1)
+ {
+ ic = HEAP_DOWN(i) ;
+ if (ic >= e)
+ break ; /* Quit if run out of heap ! */
+ p_c = &ha[ic] ; /* get left hand child */
+
+ is = ic + 1 ;
+ if (is < e) /* is there a right hand child ? */
+ {
+ p_s = &ha[is] ; /* get right hand child */
+ if (h->cmp(&p_s, &p_c) < 0)
+ {
+ ic = is ; /* select smaller sibling */
+ p_c = p_s ;
+ } ;
+ } ;
+
+ if (h->cmp(&p_v, &p_c) <= 0)
+ break ; /* stop when <= both children */
+
+ ha[i] = p_c ; /* move smaller child up */
+ heap_set_backlink(h, p_c, i) ; /* ...updating any backlink */
+
+ i = ic ; /* move down the heap */
+ } ;
+
+ ha[i] = p_v ; /* place in new position... */
+ heap_set_backlink(h, p_v, i) ; /* ...updating any backlink */
+} ;
+
+/* Find index of given item in the given heap. */
+private vector_index
+heap_find_item(heap h, p_vector_item p_v)
+{
+ vector_index i ;
+
+ if (h->state & Heap_Has_Backlink)
+ i = HEAP_BACKLINK(h, p_v) ;
+ else
+ {
+ for (i = 0 ; i < h->v.end ; ++i)
+ if (h->v.p_items[i] == p_v)
+ return i ;
+ } ;
+
+ assert((i < h->v.end) && (h->v.p_items[i] == p_v)) ;
+
+ return i ;
+} ;
diff --git a/lib/heap.h b/lib/heap.h
new file mode 100644
index 00000000..bd984398
--- /dev/null
+++ b/lib/heap.h
@@ -0,0 +1,160 @@
+/* Generic heap data structure -- header.
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *.
+ * This file is part of GNU Zebra.
+ *
+ * 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_HEAP_H
+#define _ZEBRA_HEAP_H
+
+#include "vector.h"
+
+/* Macro in case there are particular compiler issues. */
+#ifndef Inline
+ #define Inline static inline
+#endif
+
+/*==============================================================================
+ * Data structures etc.
+ */
+
+typedef int heap_cmp(p_vector_item* a, p_vector_item*) ;
+
+enum heap_state {
+ Heap_Has_Backlink = 0x01, /* Set if backlink set */
+} ;
+
+typedef vector_index heap_backlink_t ;
+
+typedef struct heap* heap ;
+
+struct heap
+{
+ heap_cmp* cmp ;
+
+ enum heap_state state ;
+ unsigned int backlink_offset ;
+
+ struct vector v ;
+} ;
+
+/*==============================================================================
+ * Prototypes.
+ */
+
+extern heap heap_init_new(heap h, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset) ;
+#define heap_init_new_simple(h, size, cmp) \
+ heap_init_new(h, size, cmp, 0, 0)
+#define heap_init_new_backlinked(h, size, cmp, offset) \
+ heap_init_new(h, size, cmp, 1, offset)
+
+extern heap heap_re_init(heap h, unsigned int size, heap_cmp* cmp,
+ int with_backlink, unsigned int backlink_offset) ;
+#define heap_re_init_simple(h, size, cmp) \
+ heap_re_init(h, size, cmp, 0, 0)
+#define heap_re_init_backlinked(h, size, cmp, offset) \
+ heap_re_init(h, size, cmp, 1, offset)
+
+extern heap heap_reset(heap h, int free_structure) ;
+extern p_vector_item heap_ream(heap h, int free_structure) ;
+
+/* Reset heap and free the heap structure. */
+#define heap_reset_free(h) heap_reset(h, 1)
+/* Reset heap but keep the heap structure. */
+#define heap_reset_keep(h) heap_reset(h, 0)
+/* Ream out heap and free the heap structure. */
+#define heap_ream_free(h) heap_ream(h, 1)
+/* Ream out heap but keep the heap structure. */
+#define heap_ream_keep(h) heap_ream(h, 0)
+
+Inline void heap_push_item(heap h, p_vector_item p_v) ;
+extern p_vector_item heap_pop_item(heap h) ;
+extern p_vector_item heap_pop_push_item(heap h, p_vector_item p_v) ;
+Inline p_vector_item heap_top_item(heap h) ;
+Inline void heap_update_top_item(heap h) ;
+
+extern void heap_delete_item(heap h, p_vector_item p_v) ;
+Inline void heap_update_item(heap h, p_vector_item p_v) ;
+
+extern void heap_push_vector(heap h, vector v, int move_vector) ;
+#define heap_push_vector_copy(h, v) \
+ heap_push_vector(h, v, 0)
+#define heap_push_vector_move(h, v) \
+ heap_push_vector(h, v, 1)
+extern vector heap_pop_vector(vector v, heap h, int move_heap) ;
+#define heap_pop_vector_copy(v, h) \
+ heap_pop_vector(v, h, 0)
+#define heap_pop_vector_move(v, h) \
+ heap_pop_vector(v, h, 1)
+
+/*==============================================================================
+ * This are extern only for use in Inline and other friends
+ */
+
+#ifndef private
+ #define private extern
+#endif
+
+private void
+heap_bubble(heap h, vector_index i, p_vector_item p_v) ;
+
+private void
+heap_bubble_up(heap h, vector_index i, p_vector_item p_v) ;
+
+private void
+heap_bubble_down(heap h, vector_index i, p_vector_item p_v) ;
+
+private vector_index
+heap_find_item(heap h, p_vector_item p_v) ;
+
+/*==============================================================================
+ * Inline Functions
+ */
+
+/* Push given item onto the heap
+ */
+Inline void
+heap_push_item(heap h, p_vector_item p_v)
+{
+ dassert(p_v != NULL) ; /* no NULLs, thank you. */
+ heap_bubble_up(h, vector_extend_by_1(&h->v), p_v) ;
+} ;
+
+/* Get copy of top heap item (does not pop).
+ *
+ * Returns NULL if heap is empty.
+ */
+Inline p_vector_item
+heap_top_item(heap h)
+{
+ return vector_get_first_item(&h->v) ; /* if any */
+} ;
+
+/* Update heap to reflect new value of top item.
+ */
+Inline void
+heap_update_top_item(heap h)
+{
+ heap_bubble_down(h, 0, heap_top_item(h)) ;
+} ;
+
+/* Update heap to reflect new value of given item.
+ */
+Inline void
+heap_update_item(heap h, p_vector_item p_v)
+{
+ heap_bubble(h, heap_find_item(h, p_v), p_v) ;
+} ;
+
+#endif /* _ZEBRA_HEAP_H */
diff --git a/lib/log.c b/lib/log.c
index 0c2f655b..592dbf49 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -34,6 +34,25 @@
#ifdef HAVE_UCONTEXT_H
#include <ucontext.h>
#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 */
+static int uzlog_reset_file (struct zlog *zl);
+static void zlog_abort (const char *mess) __attribute__ ((noreturn));
+static void vzlog (struct zlog *zl, int priority, const char *format, va_list args);
+static void uzlog_backtrace(int priority);
+static void uvzlog (struct zlog *zl, int priority, const char *format, va_list args);
static int logfile_fd = -1; /* Used in signal handler. */
@@ -66,14 +85,23 @@ const char *zlog_priority[] =
"debugging",
NULL,
};
-
-
/* For time string format. */
size_t
quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
{
+ size_t result;
+ LOCK
+ result = uquagga_timestamp(timestamp_precision, buf, buflen);
+ UNLOCK
+ return result;
+}
+
+/* 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;
@@ -81,6 +109,8 @@ quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
} cache;
struct timeval clock;
+ size_t result = 0;
+
/* would it be sufficient to use global 'recent_time' here? I fear not... */
gettimeofday(&clock, NULL);
@@ -122,14 +152,19 @@ quagga_timestamp(int timestamp_precision, char *buf, size_t buflen)
}
while (--prec > 0);
*p = '.';
- return cache.len+1+timestamp_precision;
+ result = cache.len+1+timestamp_precision;
}
- buf[cache.len] = '\0';
- return cache.len;
+ else
+ {
+ buf[cache.len] = '\0';
+ result = cache.len;
+ }
+ } else {
+ if (buflen > 0)
+ buf[0] = '\0';
}
- if (buflen > 0)
- buf[0] = '\0';
- return 0;
+
+ return result;
}
/* Utility routine for current time printing. */
@@ -138,20 +173,30 @@ time_print(FILE *fp, struct timestamp_control *ctl)
{
if (!ctl->already_rendered)
{
- ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
+ ctl->len = uquagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
ctl->already_rendered = 1;
}
fprintf(fp, "%s ", ctl->buf);
}
-
/* va_list version of zlog. */
static void
vzlog (struct zlog *zl, int priority, const char *format, va_list args)
{
+ LOCK
+ uvzlog(zl, priority, format, args);
+ 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)
+{
struct timestamp_control tsctl;
tsctl.already_rendered = 0;
+ ASSERTLOCKED
+
/* If zlog is not specified, use default one. */
if (zl == NULL)
zl = zlog_default;
@@ -165,55 +210,58 @@ vzlog (struct zlog *zl, int priority, const char *format, va_list args)
vfprintf (stderr, format, args);
fprintf (stderr, "\n");
fflush (stderr);
-
- /* In this case we return at here. */
- return;
}
- tsctl.precision = zl->timestamp_precision;
-
- /* Syslog output */
- if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
+ else
{
- va_list ac;
- va_copy(ac, args);
- vsyslog (priority|zlog_default->facility, format, ac);
- va_end(ac);
- }
+ tsctl.precision = zl->timestamp_precision;
- /* 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);
- }
+ /* Syslog output */
+ if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG])
+ {
+ va_list ac;
+ va_copy(ac, args);
+ vsyslog (priority|zlog_default->facility, format, ac);
+ va_end(ac);
+ }
- /* 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);
- }
+ /* 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);
+ }
- /* Terminal monitor. */
- if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR])
- vty_log ((zl->record_priority ? zlog_priority[priority] : NULL),
- zlog_proto_names[zl->protocol], format, &tsctl, args);
+ /* 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);
+ }
+
+ /* 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);
+ }
+ }
}
static char *
@@ -518,8 +566,16 @@ zlog_backtrace_sigsafe(int priority, void *program_counter)
void
zlog_backtrace(int priority)
{
+ LOCK
+ zlog_backtrace(priority);
+ UNLOCK
+}
+
+static void
+uzlog_backtrace(int priority)
+{
#ifndef HAVE_GLIBC_BACKTRACE
- zlog(NULL, priority, "No backtrace available on this platform.");
+ uzlog(NULL, priority, "No backtrace available on this platform.");
#else
void *array[20];
int size, i;
@@ -528,27 +584,38 @@ zlog_backtrace(int priority)
if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) ||
((size_t)size > sizeof(array)/sizeof(array[0])))
{
- zlog_err("Cannot get backtrace, returned invalid # of frames %d "
+ uzlog(NULL, LOG_ERR, "Cannot get backtrace, returned invalid # of frames %d "
"(valid range is between 1 and %lu)",
size, (unsigned long)(sizeof(array)/sizeof(array[0])));
return;
}
- zlog(NULL, priority, "Backtrace for %d stack frames:", size);
+ uzlog(NULL, priority, "Backtrace for %d stack frames:", size);
if (!(strings = backtrace_symbols(array, size)))
{
- zlog_err("Cannot get backtrace symbols (out of memory?)");
+ uzlog(NULL, LOG_ERR, "Cannot get backtrace symbols (out of memory?)");
for (i = 0; i < size; i++)
- zlog(NULL, priority, "[bt %d] %p",i,array[i]);
+ uzlog(NULL, priority, "[bt %d] %p",i,array[i]);
}
else
{
for (i = 0; i < size; i++)
- zlog(NULL, priority, "[bt %d] %s",i,strings[i]);
+ uzlog(NULL, priority, "[bt %d] %s",i,strings[i]);
free(strings);
}
#endif /* HAVE_GLIBC_BACKTRACE */
}
+/* unlocked version */
+void
+uzlog (struct zlog *zl, int priority, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ uvzlog (zl, priority, format, args);
+ va_end (args);
+}
+
void
zlog (struct zlog *zl, int priority, const char *format, ...)
{
@@ -605,16 +672,69 @@ PLOG_FUNC(plog_debug, LOG_DEBUG)
void
_zlog_assert_failed (const char *assertion, const char *file,
- unsigned int line, const char *function)
+ unsigned int line, const char *function)
+{
+ const static size_t buff_size = 1024;
+ char buff[buff_size];
+ snprintf(buff, buff_size,
+ "Assertion `%s' failed in file %s, line %u, function %s",
+ assertion, file, line, (function ? function : "?"));
+ zlog_abort(buff);
+}
+
+/* Abort with message */
+void
+_zlog_abort_mess (const char *mess, const char *file,
+ unsigned int line, const char *function)
+{
+ const static size_t buff_size = 1024;
+ char buff[buff_size];
+ snprintf(buff, buff_size, "%s, in file %s, line %u, function %s",
+ mess, file, line, (function ? function : "?"));
+ zlog_abort(buff);
+}
+
+/* Abort with message and errno and strerror() thereof */
+void
+_zlog_abort_errno (const char *mess, const char *file,
+ unsigned int line, const char *function)
+{
+ _zlog_abort_err(mess, errno, file, line, function);
+}
+
+/* Abort with message and given error and strerror() thereof */
+void
+_zlog_abort_err (const char *mess, int err, const char *file,
+ unsigned int line, const char *function)
{
+ const static size_t buff_size = 1024;
+ char buff[buff_size];
+ char err_mess[buff_size];
+ strerror_r(err, err_mess, buff_size);
+ snprintf(buff, buff_size,
+ "%s, in file %s, line %u, function %s, error %d \"%s\"",
+ mess, file, line, (function ? function : "?"),
+ err, err_mess);
+ zlog_abort(buff);
+}
+
+
+static void
+zlog_abort (const char *mess)
+{
+#ifndef NDEBUG
+ /* don't work about being unlocked */
+ vty_lock_asserted = 1;
+#endif
+
/* Force fallback file logging? */
if (zlog_default && !zlog_default->fp &&
((logfile_fd = open_crashlog()) >= 0) &&
((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL))
zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR;
- zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s",
- assertion,file,line,(function ? function : "?"));
- zlog_backtrace(LOG_CRIT);
+
+ uzlog(NULL, LOG_CRIT, "%s", mess);
+ uzlog_backtrace(LOG_CRIT);
abort();
}
@@ -660,10 +780,17 @@ closezlog (struct zlog *zl)
void
zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level)
{
+ LOCK
+
if (zl == NULL)
zl = zlog_default;
- zl->maxlvl[dest] = log_level;
+ if (zl != NULL)
+ {
+ zl->maxlvl[dest] = log_level;
+ }
+
+ UNLOCK
}
int
@@ -671,46 +798,68 @@ zlog_set_file (struct zlog *zl, const char *filename, int log_level)
{
FILE *fp;
mode_t oldumask;
+ int result = 1;
+
+ LOCK
/* There is opend file. */
- zlog_reset_file (zl);
+ uzlog_reset_file (zl);
/* Set default zl. */
if (zl == NULL)
zl = zlog_default;
- /* Open file. */
- oldumask = umask (0777 & ~LOGFILE_MASK);
- fp = fopen (filename, "a");
- umask(oldumask);
- if (fp == NULL)
- return 0;
-
- /* Set flags. */
- zl->filename = strdup (filename);
- zl->maxlvl[ZLOG_DEST_FILE] = log_level;
- zl->fp = fp;
- logfile_fd = fileno(fp);
+ if (zl != NULL)
+ {
+ /* Open file. */
+ oldumask = umask (0777 & ~LOGFILE_MASK);
+ fp = fopen (filename, "a");
+ umask(oldumask);
+ if (fp == NULL)
+ result = 0;
+ else
+ {
+ /* Set flags. */
+ zl->filename = strdup (filename);
+ zl->maxlvl[ZLOG_DEST_FILE] = log_level;
+ zl->fp = fp;
+ logfile_fd = fileno(fp);
+ }
+ }
- return 1;
+ UNLOCK
+ return result;
}
/* Reset opend file. */
int
zlog_reset_file (struct zlog *zl)
{
+ int result;
+ LOCK
+ result = uzlog_reset_file(zl);
+ UNLOCK
+ return result;
+}
+
+static int
+uzlog_reset_file (struct zlog *zl)
+ {
if (zl == NULL)
zl = zlog_default;
- if (zl->fp)
- fclose (zl->fp);
- zl->fp = NULL;
- logfile_fd = -1;
- zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
-
- if (zl->filename)
- free (zl->filename);
- zl->filename = NULL;
+ if (zl != NULL)
+ {
+ if (zl->fp)
+ fclose (zl->fp);
+ zl->fp = NULL;
+ logfile_fd = -1;
+ zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
+
+ if (zl->filename)
+ free (zl->filename);
+ zl->filename = NULL;
+ }
return 1;
}
@@ -720,39 +869,318 @@ int
zlog_rotate (struct zlog *zl)
{
int level;
+ int result = 1;
+
+ LOCK
if (zl == NULL)
zl = zlog_default;
- if (zl->fp)
- fclose (zl->fp);
- zl->fp = NULL;
- logfile_fd = -1;
- level = zl->maxlvl[ZLOG_DEST_FILE];
- zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
+ if (zl != NULL)
+ {
+ if (zl->fp)
+ fclose (zl->fp);
+ zl->fp = NULL;
+ logfile_fd = -1;
+ level = zl->maxlvl[ZLOG_DEST_FILE];
+ zl->maxlvl[ZLOG_DEST_FILE] = ZLOG_DISABLED;
+
+ if (zl->filename)
+ {
+ mode_t oldumask;
+ int save_errno;
+
+ oldumask = umask (0777 & ~LOGFILE_MASK);
+ zl->fp = fopen (zl->filename, "a");
+ save_errno = errno;
+ umask(oldumask);
+ if (zl->fp == NULL)
+ {
+ /* can't call logging while locked */
+ char *fname = strdup(zl->filename);
+ uzlog(NULL, LOG_ERR, "Log rotate failed: cannot open file %s for append: %s",
+ fname, safe_strerror(save_errno));
+ free(fname);
+ result = -1;
+ }
+ else
+ {
+ logfile_fd = fileno(zl->fp);
+ zl->maxlvl[ZLOG_DEST_FILE] = level;
+ }
+ }
+ }
+ UNLOCK
+ return result;
+}
- if (zl->filename)
+int
+zlog_get_default_lvl (struct zlog *zl)
+{
+ int result = LOG_DEBUG;
+
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
{
- mode_t oldumask;
- int save_errno;
+ result = zl->default_lvl;
+ }
- oldumask = umask (0777 & ~LOGFILE_MASK);
- zl->fp = fopen (zl->filename, "a");
- save_errno = errno;
- umask(oldumask);
- if (zl->fp == NULL)
- {
- zlog_err("Log rotate failed: cannot open file %s for append: %s",
- zl->filename, safe_strerror(save_errno));
- return -1;
- }
- logfile_fd = fileno(zl->fp);
- zl->maxlvl[ZLOG_DEST_FILE] = level;
+ UNLOCK
+ return result;
+}
+
+void
+zlog_set_default_lvl (struct zlog *zl, int level)
+{
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->default_lvl = level;
}
- return 1;
+ UNLOCK
}
-
+
+/* Set logging level and default for all destinations */
+void
+zlog_set_default_lvl_dest (struct zlog *zl, int level)
+{
+ int i;
+
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->default_lvl = level;
+
+ for (i = 0; i < ZLOG_NUM_DESTS; i++)
+ if (zl->maxlvl[i] != ZLOG_DISABLED)
+ zl->maxlvl[i] = level;
+ }
+
+ UNLOCK
+}
+
+int
+zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest)
+{
+ int result = ZLOG_DISABLED;
+
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->maxlvl[dest];
+ }
+
+ UNLOCK
+ return result;
+}
+
+int
+zlog_get_facility (struct zlog *zl)
+{
+ int result = LOG_DAEMON;
+
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->facility;
+ }
+
+ UNLOCK
+ return result;
+}
+
+void
+zlog_set_facility (struct zlog *zl, int facility)
+{
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->facility = facility;
+ }
+
+ UNLOCK
+}
+
+int
+zlog_get_record_priority (struct zlog *zl)
+{
+ int result = 0;
+
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->record_priority;
+ }
+
+ UNLOCK
+ return result;
+}
+
+void
+zlog_set_record_priority (struct zlog *zl, int record_priority)
+{
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->record_priority = record_priority;
+ }
+ UNLOCK
+}
+
+int
+zlog_get_timestamp_precision (struct zlog *zl)
+{
+ int result = 0;
+
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->timestamp_precision;
+ }
+ UNLOCK
+ return result;
+}
+
+void
+zlog_set_timestamp_precision (struct zlog *zl, int timestamp_precision)
+{
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ zl->timestamp_precision = timestamp_precision;
+ }
+
+ UNLOCK
+}
+
+/* returns name of ZLOG_NONE if no zlog given and no default set */
+const char *
+zlog_get_proto_name (struct zlog *zl)
+{
+ const char * result;
+ LOCK
+ result = uzlog_get_proto_name(zl);
+ UNLOCK
+ return result;
+}
+
+/* unprotected version, assumes mutex held */
+const char *
+uzlog_get_proto_name (struct zlog *zl)
+{
+ zlog_proto_t protocol = ZLOG_NONE;
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ protocol = zl->protocol;
+ }
+
+ return zlog_proto_names[protocol];
+}
+
+/* caller must free result */
+char *
+zlog_get_filename (struct zlog *zl)
+{
+ char * result = NULL;
+
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL && zl->filename != NULL)
+ {
+ result = strdup(zl->filename);
+ }
+
+ UNLOCK
+ return result;
+}
+
+const char *
+zlog_get_ident (struct zlog *zl)
+{
+ const char * result = NULL;
+
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = zl->ident;
+ }
+
+ UNLOCK
+ return result;
+}
+
+/* logging to a file? */
+int
+zlog_is_file (struct zlog *zl)
+{
+ int result = 0;
+
+ LOCK
+
+ if (zl == NULL)
+ zl = zlog_default;
+
+ if (zl != NULL)
+ {
+ result = (zl->fp != NULL);
+ }
+
+ UNLOCK;
+ return result;
+}
+
/* Message lookup function. */
const char *
lookup (const struct message *mes, int key)
@@ -928,3 +1356,7 @@ 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 2dd1d313..6ab1b37c 100644
--- a/lib/log.h
+++ b/lib/log.h
@@ -93,7 +93,11 @@ struct message
const char *str;
};
-/* Default logging strucutre. */
+/* module initialization */
+extern void zlog_init_r(void);
+extern void zlog_destroy_r(void);
+
+/* Default logging structure. */
extern struct zlog *zlog_default;
/* Open zlog function */
@@ -113,6 +117,9 @@ extern void closezlog (struct zlog *zl);
/* Generic function for zlog. */
extern void zlog (struct zlog *zl, int priority, const char *format, ...)
PRINTF_ATTRIBUTE(3, 4);
+/* assumed locked version for close friends */
+extern void uzlog (struct zlog *zl, int priority, const char *format, ...)
+ PRINTF_ATTRIBUTE(3, 4);
/* Handy zlog functions. */
extern void zlog_err (const char *format, ...) PRINTF_ATTRIBUTE(1, 2);
@@ -147,6 +154,23 @@ extern int zlog_reset_file (struct zlog *zl);
/* Rotate log. */
extern int zlog_rotate (struct zlog *);
+/* getters & setters */
+extern int zlog_get_default_lvl (struct zlog *zl);
+extern void zlog_set_default_lvl (struct zlog *zl, int level);
+extern void zlog_set_default_lvl_dest (struct zlog *zl, int level);
+extern int zlog_get_maxlvl (struct zlog *zl, zlog_dest_t dest);
+extern int zlog_get_facility (struct zlog *zl);
+extern void zlog_set_facility (struct zlog *zl, int facility);
+extern int zlog_get_record_priority (struct zlog *zl);
+extern void zlog_set_record_priority (struct zlog *zl, int record_priority);
+extern int zlog_get_timestamp_precision (struct zlog *zl);
+extern void zlog_set_timestamp_precision (struct zlog *zl, int timestamp_precision);
+extern const char * zlog_get_ident (struct zlog *zl);
+extern char * zlog_get_filename (struct zlog *zl);
+extern int zlog_is_file (struct zlog *zl);
+extern const char * zlog_get_proto_name (struct zlog *zl);
+extern const char * uzlog_get_proto_name (struct zlog *zl);
+
/* For hackey massage lookup and check */
#define LOOKUP(x, y) mes_lookup(x, x ## _max, y, "(no item found)")
@@ -185,7 +209,9 @@ extern void zlog_backtrace_sigsafe(int priority, void *program_counter);
*buf will be set to '\0', and 0 will be returned. */
extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */,
char *buf, size_t buflen);
-
+/* unprotected version for when mutex already held */
+extern size_t uquagga_timestamp(int timestamp_precision /* # subsecond digits */,
+ char *buf, size_t buflen);
/* structure useful for avoiding repeated rendering of the same timestamp */
struct timestamp_control {
size_t len; /* length of rendered timestamp */
diff --git a/lib/memory.c b/lib/memory.c
index 333f59ad..36599f18 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -28,6 +28,13 @@
#include "log.h"
#include "memory.h"
+#include "qpthreads.h"
+
+/* Needs to be qpthread safe. The system malloc etc are already
+ * thread safe, but we need to protect the stats */
+static qpt_mutex_t* memory_mutex = NULL;
+#define LOCK if(memory_mutex)qpt_mutex_lock(memory_mutex);
+#define UNLOCK if(memory_mutex)qpt_mutex_unlock(memory_mutex);
static void alloc_inc (int);
static void alloc_dec (int);
@@ -148,8 +155,10 @@ mtype_zmalloc (const char *file, int line, int type, size_t size)
{
void *memory;
+ LOCK
mstat[type].c_malloc++;
mstat[type].t_malloc++;
+ UNLOCK
memory = zmalloc (type, size);
mtype_log ("zmalloc", memory, file, line, type);
@@ -162,8 +171,10 @@ mtype_zcalloc (const char *file, int line, int type, size_t size)
{
void *memory;
+ LOCK
mstat[type].c_calloc++;
mstat[type].t_calloc++;
+ UNLOCK
memory = zcalloc (type, size);
mtype_log ("xcalloc", memory, file, line, type);
@@ -177,7 +188,9 @@ mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size)
void *memory;
/* Realloc need before allocated pointer. */
+ LOCK
mstat[type].t_realloc++;
+ UNLOCK
memory = zrealloc (type, ptr, size);
@@ -190,7 +203,9 @@ mtype_zrealloc (const char *file, int line, int type, void *ptr, size_t size)
void
mtype_zfree (const char *file, int line, int type, void *ptr)
{
+ LOCK
mstat[type].t_free++;
+ UNLOCK
mtype_log ("xfree", ptr, file, line, type);
@@ -202,7 +217,9 @@ mtype_zstrdup (const char *file, int line, int type, const char *str)
{
char *memory;
+ LOCK
mstat[type].c_strdup++;
+ UNLOCK
memory = zstrdup (type, str);
@@ -222,14 +239,18 @@ static struct
static void
alloc_inc (int type)
{
+ LOCK
mstat[type].alloc++;
+ UNLOCK
}
/* Decrement allocation counter. */
static void
alloc_dec (int type)
{
+ LOCK
mstat[type].alloc--;
+ UNLOCK
}
/* Looking up memory status from vty interface. */
@@ -248,8 +269,11 @@ log_memstats(int pri)
zlog (NULL, pri, "Memory utilization in module %s:", ml->name);
for (m = ml->list; m->index >= 0; m++)
- if (m->index && mstat[m->index].alloc)
- zlog (NULL, pri, " %-30s: %10ld", m->format, mstat[m->index].alloc);
+ {
+ unsigned long alloc = mtype_stats_alloc(m->index);
+ if (m->index && alloc)
+ zlog (NULL, pri, " %-30s: %10ld", m->format, alloc);
+ }
}
}
@@ -264,23 +288,25 @@ log_memstats_stderr (const char *prefix)
for (ml = mlists; ml->list; ml++)
{
i = 0;
-
for (m = ml->list; m->index >= 0; m++)
- if (m->index && mstat[m->index].alloc)
- {
- if (!i)
+ {
+ unsigned long alloc = mtype_stats_alloc(m->index);
+ if (m->index && alloc)
+ {
+ if (!i)
+ fprintf (stderr,
+ "%s: memstats: Current memory utilization in module %s:\n",
+ prefix,
+ ml->name);
fprintf (stderr,
- "%s: memstats: Current memory utilization in module %s:\n",
- prefix,
- ml->name);
- fprintf (stderr,
- "%s: memstats: %-30s: %10ld%s\n",
- prefix,
- m->format,
- mstat[m->index].alloc,
- mstat[m->index].alloc < 0 ? " (REPORT THIS BUG!)" : "");
- i = j = 1;
- }
+ "%s: memstats: %-30s: %10ld%s\n",
+ prefix,
+ m->format,
+ alloc,
+ alloc < 0 ? " (REPORT THIS BUG!)" : "");
+ i = j = 1;
+ }
+ }
}
if (j)
@@ -315,10 +341,14 @@ show_memory_vty (struct vty *vty, struct memory_list *list)
needsep = 0;
}
}
- else if (mstat[m->index].alloc)
+ else
{
- vty_out (vty, "%-30s: %10ld\r\n", m->format, mstat[m->index].alloc);
- needsep = 1;
+ unsigned long alloc = mtype_stats_alloc(m->index);
+ if (alloc)
+ {
+ vty_out (vty, "%-30s: %10ld\r\n", m->format, alloc);
+ needsep = 1;
+ }
}
return needsep;
}
@@ -364,7 +394,7 @@ show_memory_mallinfo (struct vty *vty)
}
#endif /* HAVE_MALLINFO */
-DEFUN (show_memory_all,
+DEFUN_CALL (show_memory_all,
show_memory_all_cmd,
"show memory all",
"Show running system information\n"
@@ -388,13 +418,13 @@ DEFUN (show_memory_all,
return CMD_SUCCESS;
}
-ALIAS (show_memory_all,
+ALIAS_CALL (show_memory_all,
show_memory_cmd,
"show memory",
"Show running system information\n"
"Memory statistics\n")
-DEFUN (show_memory_lib,
+DEFUN_CALL (show_memory_lib,
show_memory_lib_cmd,
"show memory lib",
SHOW_STR
@@ -405,7 +435,7 @@ DEFUN (show_memory_lib,
return CMD_SUCCESS;
}
-DEFUN (show_memory_zebra,
+DEFUN_CALL (show_memory_zebra,
show_memory_zebra_cmd,
"show memory zebra",
SHOW_STR
@@ -416,7 +446,7 @@ DEFUN (show_memory_zebra,
return CMD_SUCCESS;
}
-DEFUN (show_memory_rip,
+DEFUN_CALL (show_memory_rip,
show_memory_rip_cmd,
"show memory rip",
SHOW_STR
@@ -427,7 +457,7 @@ DEFUN (show_memory_rip,
return CMD_SUCCESS;
}
-DEFUN (show_memory_ripng,
+DEFUN_CALL (show_memory_ripng,
show_memory_ripng_cmd,
"show memory ripng",
SHOW_STR
@@ -438,7 +468,7 @@ DEFUN (show_memory_ripng,
return CMD_SUCCESS;
}
-DEFUN (show_memory_bgp,
+DEFUN_CALL (show_memory_bgp,
show_memory_bgp_cmd,
"show memory bgp",
SHOW_STR
@@ -449,7 +479,7 @@ DEFUN (show_memory_bgp,
return CMD_SUCCESS;
}
-DEFUN (show_memory_ospf,
+DEFUN_CALL (show_memory_ospf,
show_memory_ospf_cmd,
"show memory ospf",
SHOW_STR
@@ -460,7 +490,7 @@ DEFUN (show_memory_ospf,
return CMD_SUCCESS;
}
-DEFUN (show_memory_ospf6,
+DEFUN_CALL (show_memory_ospf6,
show_memory_ospf6_cmd,
"show memory ospf6",
SHOW_STR
@@ -471,7 +501,7 @@ DEFUN (show_memory_ospf6,
return CMD_SUCCESS;
}
-DEFUN (show_memory_isis,
+DEFUN_CALL (show_memory_isis,
show_memory_isis_cmd,
"show memory isis",
SHOW_STR
@@ -482,6 +512,21 @@ DEFUN (show_memory_isis,
return CMD_SUCCESS;
}
+/* Second state initialisation if qpthreaded */
+void
+memory_init_r (void)
+{
+ memory_mutex = qpt_mutex_init(memory_mutex, qpt_mutex_quagga);
+}
+
+/* Finished with module */
+void
+memory_finish (void)
+{
+ if (memory_mutex)
+ memory_mutex = qpt_mutex_destroy(memory_mutex, 1);
+}
+
void
memory_init (void)
{
@@ -583,5 +628,12 @@ mtype_memstr (char *buf, size_t len, unsigned long bytes)
unsigned long
mtype_stats_alloc (int type)
{
- return mstat[type].alloc;
+ unsigned long result;
+ LOCK
+ result = mstat[type].alloc;
+ UNLOCK
+ return result;
}
+
+#undef UNLOCK
+#undef LOCK
diff --git a/lib/memory.h b/lib/memory.h
index 037efef2..09fddf85 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#ifndef _ZEBRA_MEMORY_H
#define _ZEBRA_MEMORY_H
+#include <stddef.h>
+
/* For pretty printing of memory allocate information. */
struct memory_list
{
@@ -33,10 +35,10 @@ struct mlist {
const char *name;
};
-#include "lib/memtypes.h"
-
extern struct mlist mlists[];
+#include "lib/memtypes.h"
+
/* #define MEMORY_LOG */
#ifdef MEMORY_LOG
#define XMALLOC(mtype, size) \
@@ -83,6 +85,8 @@ extern void mtype_zfree (const char *file, int line, int type,
extern char *mtype_zstrdup (const char *file, int line, int 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 */
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 197fb88c..81acb205 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -16,18 +16,31 @@ struct memory_list memory_list_lib[] =
{
{ MTYPE_TMP, "Temporary memory" },
{ MTYPE_STRVEC, "String vector" },
- { MTYPE_VECTOR, "Vector structure" },
- { MTYPE_VECTOR_BODY, "Vector body" },
+ { MTYPE_VECTOR, "Vector structure" },
+ { MTYPE_VECTOR_BODY, "Vector body" },
{ MTYPE_SYMBOL_TABLE, "Symbol Table structure" },
{ MTYPE_SYMBOL_BASES, "Symbol Table chain bases" },
{ MTYPE_SYMBOL, "Symbol" },
{ MTYPE_SYMBOL_REF, "Symbol Reference" },
+ { MTYPE_HEAP, "Heap structure" },
{ MTYPE_LINK_LIST, "Link List" },
{ MTYPE_LINK_NODE, "Link Node" },
{ MTYPE_THREAD, "Thread" },
{ MTYPE_THREAD_MASTER, "Thread master" },
{ MTYPE_THREAD_STATS, "Thread stats" },
- { MTYPE_THREAD_FUNCNAME, "Thread function name" },
+ { MTYPE_THREAD_FUNCNAME, "Thread function name" },
+ { MTYPE_QPT_THREAD_ATTR, "qpt thread attributes" },
+ { MTYPE_QPT_MUTEX, "qpt mutex" },
+ { MTYPE_QPT_COND, "qpt condition variable" },
+ { MTYPE_MQUEUE_QUEUE, "Mqueue queue structure" },
+ { MTYPE_MQUEUE_BLOCKS, "Mqueue message blocks" },
+ { MTYPE_MQUEUE_THREAD_SIGNAL, "Mqueue thread signal" },
+ { MTYPE_QPS_SELECTION, "qpselect selection" },
+ { MTYPE_QPS_FILE, "qpselect file" },
+ { MTYPE_QTIMER_PILE, "qtimer pile structure" },
+ { MTYPE_QTIMER, "qtimer timer" },
+ { MTYPE_QPN_NEXUS, "qtn nexus" },
+ { MTYPE_MARSHAL, "marshalled commands" },
{ MTYPE_VTY, "VTY" },
{ MTYPE_VTY_OUT_BUF, "VTY output buffer" },
{ MTYPE_VTY_HIST, "VTY history" },
diff --git a/lib/mqueue.c b/lib/mqueue.c
new file mode 100644
index 00000000..055086ed
--- /dev/null
+++ b/lib/mqueue.c
@@ -0,0 +1,615 @@
+/* Message Queue data structure -- functions
+ * 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 <string.h>
+
+#include "memory.h"
+#include "mqueue.h"
+#include "zassert.h"
+
+/*==============================================================================
+ * These message queues are designed for inter-qpthread communication.
+ *
+ * A message queue carries messages from one or more qpthreads to one or more
+ * other qpthreads.
+ *
+ * If !qpthreads_enabled, then a message queue hold messages for the program
+ * to consume later. There are never any waiters. Timeouts are ignored.
+ *
+ * A message queue has one ordinary priority queue and one high priority
+ * queue.
+ *
+ * There are four types of queue, depending on how qpthreads wait and how they
+ * are woken up:
+ *
+ * mqt_cond_unicast -- wait on condition variable, one waiter kicked
+ * mqt_cond_broadcast -- wait on condition variable, all waiters kicked
+ * mqt_signal_unicast -- wait for signal, one waiter kicked
+ * mqt_signal_broadcast -- wait for signal, all waiters kicked
+ *
+ * For condition variables there is a timeout mechanism so that waiters
+ * are woken up at least every now and then. The message queue maintains
+ * a timeout time and a timeout interval. The timeout time is a qtime_mono_t
+ * time -- so is monotonic.
+ *
+ * When waiting, an explicit timeout may be given, otherwise the stored timeout
+ * will be used:
+ *
+ * wait until explicit/stored timeout
+ * if times out and there is a stored interval:
+ * new stored timeout = stored timeout + stored interval
+ * if new stored timeout < time now
+ * new stored timeout = time now + stored interval
+ *
+ * Left to its own devices, this will produce a regular timeout every interval,
+ * assuming that the queue is waited on within the interval. Otherwise the
+ * "clock" will slip.
+ *
+ * There is a default timeout period. The period may be set "infinite".
+ *
+ * For waiters kicked by signal, the wait does not occur within the message
+ * queue code, but the need for a signal is recorded in the message queue.
+ *
+ * Messages take the form of a small block of information which contains:
+ *
+ * * flags -- used by the message handler
+ * * context -- identifies the context of the message (see revoke)
+ *
+ * * action -- void action(mqueue_block) message dispatch
+ * * arg0 -- *void/uintptr_t/intptr_t ) standard arguments
+ * * arg1 -- *void/uintptr_t/intptr_t )
+ *
+ * There are set/get functions for action/arg0/arg1 -- users should not poke
+ * around inside the structure.
+ *
+ * To send a message, first allocate a message block (see mqb_init_new),
+ * then fill in the arguments and enqueue it.
+ *
+ *
+ */
+
+/*==============================================================================
+ * Initialisation etc. for Message Queues.
+ *
+ * TODO: how to shut down a message queue... for reset/exit ?
+ */
+
+/* Initialise new Message Queue, if required (mq == NULL) allocating it.
+ *
+ * For mqt_cond_xxx type queues, sets the default timeout interval and the
+ * initial timeout time to now + that interval.
+ *
+ * NB: once any message queue has been enabled, it is TOO LATE to enable
+ * qpthreads.
+ */
+
+mqueue_queue
+mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type)
+{
+ if (mq == NULL)
+ mq = XCALLOC(MTYPE_MQUEUE_QUEUE, sizeof(struct mqueue_queue)) ;
+ else
+ memset(mq, 0, sizeof(struct mqueue_queue)) ;
+
+ if (qpt_freeze_qpthreads_enabled())
+ qpt_mutex_init_new(&mq->mutex, qpt_mutex_quagga) ;
+
+ /* head, tail and tail_priority set NULL already */
+ /* waiters set zero already */
+
+ mq->type = type ;
+ switch (type)
+ {
+ case mqt_cond_unicast:
+ case mqt_cond_broadcast:
+ if (qpthreads_enabled)
+ qpt_cond_init_new(&mq->kick.cond.wait_here, qpt_cond_quagga) ;
+ if (MQUEUE_DEFAULT_INTERVAL != 0)
+ {
+ mq->kick.cond.interval = MQUEUE_DEFAULT_INTERVAL ;
+ mq->kick.cond.timeout = qt_get_monotonic()
+ + MQUEUE_DEFAULT_INTERVAL ;
+ } ;
+ break;
+
+ case mqt_signal_unicast:
+ case mqt_signal_broadcast:
+ /* head/tail pointers set NULL already */
+ break;
+
+ default:
+ zabort("Invalid mqueue queue type") ;
+ } ;
+
+ return mq ;
+} ;
+
+/* Set new timeout interval (or unset by setting <= 0)
+ *
+ * Sets the next timeout to be the time now + new interval (or never).
+ *
+ * This is a waste of time if !qpthreads_enabled, but does no harm. The
+ * timeout is ignored.
+ */
+void
+mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval)
+{
+ qpt_mutex_lock(&mq->mutex) ;
+
+ dassert( (mq->type == mqt_cond_unicast) ||
+ (mq->type == mqt_cond_broadcast) ) ;
+
+ mq->kick.cond.interval = interval ;
+ mq->kick.cond.timeout = (interval > 0) ? qt_add_monotonic(interval)
+ : 0 ;
+ qpt_mutex_unlock(&mq->mutex) ;
+} ;
+
+/*==============================================================================
+ * Message Block memory management.
+ *
+ * Allocates message_block structures in lots of 256. Uses first message_block
+ * in each lot to keep track of the lots.
+ *
+ * mqueue_initialise MUST be called before the first message block is allocated.
+ */
+
+static pthread_mutex_t mqb_mutex ;
+
+#define MQB_LOT_SIZE 256
+
+static mqueue_block mqb_lot_list = NULL ;
+static mqueue_block mqb_free_list = NULL ;
+
+static mqueue_block mqueue_block_new_lot(void) ;
+
+/* Initialise message block (allocate if required) and set action and context.
+ */
+mqueue_block
+mqb_init_new(mqueue_block mqb, mqueue_action action, mqb_context_t context)
+{
+ if (mqb == NULL)
+ {
+ qpt_mutex_lock(&mqb_mutex) ;
+
+ mqb = mqb_free_list ;
+ if (mqb == NULL)
+ mqb = mqueue_block_new_lot() ;
+
+ mqb_free_list = mqb->next ;
+
+ qpt_mutex_unlock(&mqb_mutex) ;
+ } ;
+
+ memset(mqb, 0, sizeof(struct mqueue_block)) ;
+
+ mqb->action = action ;
+ mqb->context = context ;
+
+ return mqb ;
+} ;
+
+/* Free message block when done with it.
+ */
+void
+mqb_free(mqueue_block mqb)
+{
+ qpt_mutex_lock(&mqb_mutex) ;
+
+ mqb->next = mqb_free_list ;
+ mqb_free_list = mqb ;
+
+ qpt_mutex_unlock(&mqb_mutex) ;
+} ;
+
+/* Make a new lot of empty message_block structures.
+ *
+ * NB: caller MUST hold the mqb_mutex.
+ */
+static mqueue_block
+mqueue_block_new_lot(void)
+{
+ mqueue_block first, last, this ;
+
+ mqueue_block new = XCALLOC(MTYPE_MQUEUE_BLOCKS,
+ SIZE(struct mqueue_block, MQB_LOT_SIZE)) ;
+ first = &new[1] ;
+ last = &new[MQB_LOT_SIZE - 1] ;
+
+ new->next = mqb_lot_list ; /* add to list of lots */
+ mqb_lot_list = new ;
+
+ /* String all the new message_blocks together. */
+ this = last ;
+ while (this > first)
+ {
+ mqueue_block prev = this-- ;
+ this->next = prev ;
+ } ;
+ assert(this == first) ;
+
+ last->next = mqb_free_list ; /* point last at old free list */
+ mqb_free_list = first ; /* new blocks at head of free list */
+
+ return mqb_free_list ;
+} ;
+
+/*==============================================================================
+ * Enqueue and dequeue messages.
+ */
+
+static void mqueue_kick_signal(mqueue_queue mq, unsigned n) ;
+static void mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig) ;
+
+/* Enqueue message.
+ *
+ * If priority != 0, will enqueue after any previously enqueued priority
+ * messages.
+ *
+ * If there are any waiters, then we kick one or all of them.
+ *
+ * Note that we decrement or zero the waiters count here -- because if the
+ * waiter did it, they might not run before something else is enqueued.
+ * Similarly, if the kick uses a signal, the signal block is dequeued here.
+ *
+ * The waiter count is only incremented when a dequeue is attempted and the
+ * queue is empty, then:
+ *
+ * for a broadcast type message queue, the first message that arrives will
+ * kick all the waiters into action.
+ *
+ * for a signal type message queue, each message that arrives will kick one
+ * waiter.
+ *
+ * NB: this works perfectly well if !qpthreads enabled. Of course, there can
+ * never be any waiters... so no kicking is ever done.
+ */
+
+void
+mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority)
+{
+ qpt_mutex_lock(&mq->mutex) ;
+
+ if (mq->head == NULL)
+ {
+ mqb->next = NULL ;
+ mq->head = mqb ;
+ mq->tail_priority = priority ? mqb : NULL ;
+ mq->tail = mqb ;
+ }
+ else if (priority)
+ {
+ mqueue_block after = mq->tail_priority ;
+ if (after == NULL)
+ {
+ mqb->next = mq->head ;
+ mq->head = mqb ;
+ }
+ else
+ {
+ mqb->next = after->next ;
+ after->next = mqb ;
+ }
+ mq->tail_priority = mqb ;
+ }
+ else
+ {
+ dassert(mq->tail != NULL) ;
+ mqb->next = NULL ;
+ mq->tail->next = mqb ;
+ mq->tail = mqb ;
+ } ;
+
+ if (mq->waiters != 0)
+ {
+ dassert(qpthreads_enabled) ; /* waiters == 0 if !qpthreads_enabled */
+
+ switch (mq->type)
+ {
+ case mqt_cond_unicast:
+ qpt_cond_signal(&mq->kick.cond.wait_here) ;
+ --mq->waiters ;
+ break ;
+
+ case mqt_cond_broadcast:
+ qpt_cond_broadcast(&mq->kick.cond.wait_here) ;
+ mq->waiters = 0 ;
+ break ;
+
+ case mqt_signal_unicast:
+ mqueue_kick_signal(mq, 1) ; /* pick off first and kick it (MUST be */
+ /* one) and decrement the waiters count */
+ break ;
+
+ case mqt_signal_broadcast:
+ mqueue_kick_signal(mq, mq->waiters) ;
+ dassert(mq->kick.signal.head == NULL) ;
+ break;
+
+ default:
+ zabort("Invalid mqueue queue type") ;
+ } ;
+ } ;
+
+ qpt_mutex_unlock(&mq->mutex) ;
+} ;
+
+/* Dequeue message.
+ *
+ * If the queue is empty and wait != 0 (and qpthreads_enabled), will wait for a
+ * message. In which case for:
+ *
+ * * mqt_cond_xxxx type message queues, will wait on the condition variable,
+ * and may timeout.
+ *
+ * If the argument is NULL, uses the already set up timeout, if there is
+ * one.
+ *
+ * If the argument is not NULL, it is a pointer to a qtime_mono_t time,
+ * to be used as the new timeout time.
+ *
+ * * mqt_signal_xxxx type message queues, will register the given signal
+ * (mtsig argument MUST be provided), and return immediately.
+ *
+ * NB: if !qpthreads_enabled, will not wait on the queue. No how.
+ *
+ * Note this means that waiters == 0 all the time if !qpthreads_enabled !
+ *
+ * NB: the argument is ignored if !wait or !qpthreads_enabled, so may be NULL.
+ *
+ * Returns a message block if one is available. (And not otherwise.)
+ */
+
+mqueue_block
+mqueue_dequeue(mqueue_queue mq, int wait, void* arg)
+{
+ mqueue_block mqb ;
+ mqueue_thread_signal last ;
+
+ mqueue_thread_signal mtsig ;
+ qtime_mono_t timeout_time ;
+
+ qpt_mutex_lock(&mq->mutex) ;
+
+ while (1)
+ {
+ mqb = mq->head ;
+ if (mqb != NULL)
+ break ; /* Easy if queue not empty */
+
+ if (!wait || !qpthreads_enabled)
+ goto done ; /* Easy if not waiting ! mqb == NULL */
+ /* Short circuit if !qpthreads_enabled */
+
+ ++mq->waiters ; /* Another waiter */
+
+ switch (mq->type)
+ {
+ case mqt_cond_unicast: /* Now wait here */
+ case mqt_cond_broadcast:
+ if ((arg == NULL) && (mq->kick.cond.interval <= 0))
+ qpt_cond_wait(&mq->kick.cond.wait_here, &mq->mutex) ;
+ else
+ {
+ timeout_time = (arg != NULL) ? *(qtime_mono_t*)arg
+ : mq->kick.cond.timeout ;
+
+ if (qpt_cond_timedwait(&mq->kick.cond.wait_here, &mq->mutex,
+ timeout_time) == 0)
+ {
+ /* Timed out -- update timeout time, if required */
+ if (mq->kick.cond.interval > 0)
+ {
+ qtime_mono_t now = qt_get_monotonic() ;
+ timeout_time = mq->kick.cond.timeout
+ + mq->kick.cond.interval ;
+ if (timeout_time < now)
+ timeout_time = now + mq->kick.cond.interval ;
+
+ mq->kick.cond.timeout = timeout_time ;
+ } ;
+
+ goto done ; /* immediate return. mqb == NULL */
+ } ;
+ } ;
+ break ;
+
+ case mqt_signal_unicast: /* Register desire for signal */
+ case mqt_signal_broadcast:
+ mtsig = arg ;
+ dassert(mtsig != NULL) ;
+
+ if (mq->kick.signal.head == NULL)
+ {
+ mq->kick.signal.head = mtsig ;
+ mtsig->prev = (void*)mq ;
+ }
+ else
+ {
+ last = mq->kick.signal.tail ;
+ last->next = mtsig ;
+ mtsig->prev = last ;
+ }
+ mtsig->next = NULL ;
+ mq->kick.signal.tail = mtsig ;
+
+ goto done ; /* BUT do not wait ! mqb == NULL */
+
+ default:
+ zabort("Invalid mqueue queue type") ;
+ } ;
+ } ;
+
+ /* Have something to pull off the queue */
+
+ mq->head = mqb->next ;
+ if (mqb == mq->tail_priority)
+ mq->tail_priority = NULL ;
+
+done:
+ qpt_mutex_unlock(&mq->mutex) ;
+
+ return mqb ;
+} ;
+
+/* No longer waiting for a signal -- does nothing if !qpthreads_enabled.
+ *
+ * Returns true <=> signal has been kicked
+ *
+ * (Signal will never be kicked if !qpthreads_enabled.)
+ */
+int
+mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig)
+{
+ int kicked ;
+
+ if (!qpthreads_enabled)
+ return 0 ;
+
+ qpt_mutex_lock(&mq->mutex) ;
+
+ dassert( (mq->type == mqt_signal_unicast) ||
+ (mq->type == mqt_signal_broadcast) ) ;
+ dassert(mtsig != NULL) ;
+
+ /* When the thread is signalled, the prev entry is set NULL and the */
+ /* waiters count is decremented. */
+ /* */
+ /* So, only need to do something here if the prev is not NULL (ie the */
+ /* mqueue_thread_signal is still on the list. */
+
+ kicked = (mtsig->prev == NULL) ;
+
+ if (!kicked)
+ mqueue_dequeue_signal(mq, mtsig) ;
+
+ qpt_mutex_unlock(&mq->mutex) ;
+
+ return kicked ;
+} ;
+
+/*==============================================================================
+ * Message queue signal handling
+ */
+
+/* Initialise a message queue signal structure (struct mqueue_thread_signal).
+ * Allocate one if required.
+ *
+ * If !pthreads_enabled, then this structure is entirely redundant, but there
+ * is no harm in creating it -- but the signal will never be used.
+ *
+ * Returns address of the structure.
+ */
+mqueue_thread_signal
+mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
+ int signum)
+{
+ if (mqt == NULL)
+ mqt = XCALLOC(MTYPE_MQUEUE_THREAD_SIGNAL,
+ sizeof(struct mqueue_thread_signal)) ;
+ else
+ memset(mqt, 0, sizeof(struct mqueue_thread_signal)) ;
+
+ /* next and prev fields set to NULL already. */
+
+ mqt->qpthread = thread ;
+ mqt->signum = signum ;
+
+ return mqt ;
+} ;
+
+/* Signal the first 'n' threads on the to be signalled list.
+ *
+ * Removes the threads from the list and reduces the waiters count.
+ *
+ * NB: must be qpthreads_enabled with at least 'n' waiters.
+ *
+ * NB: sets the prev entry in the mqueue_thread_signal block to NULL, so that
+ * the thread can tell that its signal has been kicked.
+ *
+ * NB: *** MUST own the mqueue_queue mutex. ***
+ */
+static void
+mqueue_kick_signal(mqueue_queue mq, unsigned n)
+{
+ mqueue_thread_signal mtsig ;
+
+ dassert( (qpthreads_enabled) && (mq->waiters >= n) ) ;
+ while (n--)
+ {
+ mqueue_dequeue_signal(mq, mtsig = mq->kick.signal.head) ;
+ qpt_thread_signal(mtsig->qpthread, mtsig->signum) ;
+ } ;
+} ;
+
+/* Remove given signal from given message queue.
+ *
+ * NB: sets the prev entry in the mqueue_thread_signal block to NULL, so that
+ * the thread can tell that its signal has been kicked.
+ *
+ * NB: *** MUST own the mqueue_queue mutex. ***
+ */
+static void
+mqueue_dequeue_signal(mqueue_queue mq, mqueue_thread_signal mtsig)
+{
+ mqueue_thread_signal next ;
+ mqueue_thread_signal prev ;
+
+ next = mtsig->next ;
+ prev = mtsig->prev ;
+
+ if (prev == (void*)mq) /* marker for head of list */
+ {
+ dassert(mq->kick.signal.head == mtsig) ;
+ mq->kick.signal.head = next ;
+ }
+ else
+ {
+ dassert((prev != NULL) && (prev->next == mtsig)) ;
+ prev->next = next ;
+ } ;
+
+ if (next != NULL)
+ next->prev = prev ;
+
+ mtsig->next = NULL ;
+ mtsig->prev = NULL ; /* essential to show signal kicked */
+ --mq->waiters ; /* one fewer waiter */
+
+ dassert( ((mq->kick.signal.head == NULL) && (mq->waiters == 0)) ||
+ ((mq->kick.signal.head != NULL) && (mq->waiters != 0)) ) ;
+} ;
+
+/*==============================================================================
+ * 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
new file mode 100644
index 00000000..6fb519d4
--- /dev/null
+++ b/lib/mqueue.h
@@ -0,0 +1,282 @@
+/* Message Queue data structure -- 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_MQUEUE_H
+#define _ZEBRA_MQUEUE_H
+
+#include "qpthreads.h"
+#include "qtime.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ */
+
+typedef struct mqueue_block* mqueue_block ;
+
+typedef uint32_t mqb_flags_t ;
+typedef uint32_t mqb_context_t ;
+
+typedef void* mqb_ptr_t ;
+typedef intptr_t mqb_int_t ;
+typedef uintptr_t mqb_uint_t ;
+
+typedef union
+{
+ mqb_ptr_t p ;
+ mqb_int_t i ;
+ mqb_uint_t u ;
+} mqb_arg_t ;
+
+typedef void mqueue_action(mqueue_block mqb) ;
+
+struct mqueue_block
+{
+ mqueue_block next ; /* single linked list */
+
+ mqueue_action* action ; /* for message dispatch */
+
+ mqb_flags_t flags ; /* for message handler */
+
+ mqb_context_t context ; /* for message revoke */
+
+ mqb_arg_t arg0 ; /* may be pointer or integer */
+ mqb_arg_t arg1 ; /* may be pointer or integer */
+} ;
+
+typedef struct mqueue_thread_signal* mqueue_thread_signal ;
+
+struct mqueue_thread_signal {
+ mqueue_thread_signal next ; /* NULL => last on list */
+ mqueue_thread_signal prev ; /* NULL => NOT on list -- vital ! */
+
+ qpt_thread_t qpthread ; /* qpthread to kick */
+ int signum ; /* signal to kick with */
+} ;
+
+enum mqueue_queue_type {
+ mqt_cond_unicast, /* use qpt_cond_signal to kick the queue */
+ mqt_cond_broadcast, /* use qpt_cond_broadcast to kick the queue */
+ mqt_signal_unicast, /* use single qpt_signal to kick the queue */
+ mqt_signal_broadcast, /* use multiple qpt_signal to kick the queue */
+};
+
+#ifndef MQUEUE_DEFAULT_INTERVAL
+# define MQUEUE_DEFAULT_INTERVAL QTIME(5)
+#endif
+
+struct mqueue_queue_cond {
+ qpt_cond_t wait_here ;
+ qtime_mono_t timeout ;
+ qtime_t interval ;
+} ;
+
+struct mqueue_queue_signal {
+ mqueue_thread_signal head ; /* NULL => list is empty */
+ mqueue_thread_signal tail ;
+};
+
+typedef struct mqueue_queue* mqueue_queue ;
+
+struct mqueue_queue
+{
+ qpt_mutex_t mutex ;
+
+ mqueue_block head ; /* NULL => list is empty */
+ mqueue_block tail_priority ; /* last priority message (if any & not empty) */
+ mqueue_block tail ; /* last message (if not empty) */
+
+ enum mqueue_queue_type type ;
+
+ unsigned waiters ;
+
+ union {
+ struct mqueue_queue_cond cond ;
+ struct mqueue_queue_signal signal ;
+ } kick ;
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+void
+mqueue_initialise(void) ;
+
+mqueue_queue
+mqueue_init_new(mqueue_queue mq, enum mqueue_queue_type type) ;
+
+void
+mqueue_set_timeout_interval(mqueue_queue mq, qtime_t interval) ;
+
+mqueue_thread_signal
+mqueue_thread_signal_init(mqueue_thread_signal mqt, qpt_thread_t thread,
+ int signum) ;
+mqueue_block
+mqb_init_new(mqueue_block mqb, mqueue_action action, mqb_context_t context) ;
+
+#define mqb_new(action, context) mqb_init_new(NULL, action, context)
+
+void
+mqb_free(mqueue_block mqb) ;
+
+void
+mqueue_enqueue(mqueue_queue mq, mqueue_block mqb, int priority) ;
+
+mqueue_block
+mqueue_dequeue(mqueue_queue mq, int wait, void* arg) ;
+
+int
+mqueue_done_waiting(mqueue_queue mq, mqueue_thread_signal mtsig) ;
+
+/*==============================================================================
+ * Access functions for mqueue_block fields -- mqb_set_xxx/mqb_get_xxx
+ *
+ * Users should not poke around inside the mqueue_block structure.
+ */
+
+Inline void mqb_set_action(mqueue_block mqb, mqueue_action action) ;
+Inline void mqb_set_context(mqueue_block mqb, mqb_context_t context) ;
+
+Inline void mqb_set_arg0_p(mqueue_block mqb, mqb_ptr_t p) ;
+Inline void mqb_set_arg0_i(mqueue_block mqb, mqb_int_t i) ;
+Inline void mqb_set_arg0_u(mqueue_block mqb, mqb_uint_t u) ;
+Inline void mqb_set_arg1_p(mqueue_block mqb, mqb_ptr_t p) ;
+Inline void mqb_set_arg1_i(mqueue_block mqb, mqb_int_t i) ;
+Inline void mqb_set_arg1_u(mqueue_block mqb, mqb_uint_t u) ;
+
+Inline void mqb_dispatch(mqueue_block mqb) ;
+Inline mqb_context_t mqb_qet_context(mqueue_block mqb) ;
+
+Inline mqb_ptr_t mqb_get_arg0_p(mqueue_block mqb) ;
+Inline mqb_int_t mqb_get_arg0_i(mqueue_block mqb) ;
+Inline mqb_uint_t mqb_get_arg0_u(mqueue_block mqb) ;
+Inline mqb_ptr_t mqb_get_arg1_p(mqueue_block mqb) ;
+Inline mqb_int_t mqb_get_arg1_i(mqueue_block mqb) ;
+Inline mqb_uint_t mqb_get_arg1_u(mqueue_block mqb) ;
+
+/*==============================================================================
+ * The Inline functions.
+ */
+
+/* Set operations. */
+
+Inline void
+mqb_set_action(mqueue_block mqb, mqueue_action action)
+{
+ mqb->action = action ;
+} ;
+
+Inline void
+mqb_set_context(mqueue_block mqb, mqb_context_t context)
+{
+ mqb->context = context ;
+} ;
+
+Inline void
+mqb_set_arg0_p(mqueue_block mqb, mqb_ptr_t p)
+{
+ mqb->arg0.p = p ;
+} ;
+
+Inline void
+mqb_set_arg0_i(mqueue_block mqb, mqb_int_t i)
+{
+ mqb->arg0.i = i ;
+} ;
+
+Inline void
+mqb_set_arg0_u(mqueue_block mqb, mqb_uint_t u)
+{
+ mqb->arg0.u = u ;
+} ;
+
+Inline void
+mqb_set_arg1_p(mqueue_block mqb, mqb_ptr_t p)
+{
+ mqb->arg1.p = p ;
+} ;
+
+Inline void
+mqb_set_arg1_i(mqueue_block mqb, mqb_int_t i)
+{
+ mqb->arg1.i = i ;
+} ;
+
+Inline void
+mqb_set_arg1_u(mqueue_block mqb, mqb_uint_t u)
+{
+ mqb->arg1.u = u ;
+} ;
+
+/* Get operations */
+
+Inline void
+mqb_dispatch(mqueue_block mqb)
+{
+ mqb->action(mqb) ;
+} ;
+
+Inline mqb_context_t
+mqb_qet_context(mqueue_block mqb)
+{
+ return mqb->context ;
+} ;
+
+Inline mqb_ptr_t
+mqb_get_arg0_p(mqueue_block mqb)
+{
+ return mqb->arg0.p ;
+} ;
+
+Inline mqb_int_t
+mqb_get_arg0_i(mqueue_block mqb)
+{
+ return mqb->arg0.i ;
+} ;
+
+Inline mqb_uint_t
+mqb_get_arg0_u(mqueue_block mqb)
+{
+ return mqb->arg0.u ;
+} ;
+
+Inline mqb_ptr_t
+mqb_get_arg1_p(mqueue_block mqb)
+{
+ return mqb->arg1.p ;
+} ;
+
+Inline mqb_int_t
+mqb_get_arg1_i(mqueue_block mqb)
+{
+ return mqb->arg1.i ;
+} ;
+
+Inline mqb_uint_t
+mqb_get_arg1_u(mqueue_block mqb)
+{
+ return mqb->arg1.u ;
+} ;
+
+#endif /* _ZEBRA_MQUEUE_H */
diff --git a/lib/plist.c b/lib/plist.c
index 10d5e31c..b01c48f6 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -1378,7 +1378,7 @@ vty_show_prefix_entry (struct vty *vty, struct prefix_list *plist,
{
/* Print the name of the protocol */
if (zlog_default)
- vty_out (vty, "%s: ", zlog_proto_names[zlog_default->protocol]);
+ vty_out (vty, "%s: ", zlog_get_proto_name(NULL));
if (dtype == normal_display)
{
diff --git a/lib/privs.c b/lib/privs.c
index 69606f57..9d8cf79c 100644
--- a/lib/privs.c
+++ b/lib/privs.c
@@ -25,6 +25,10 @@
#include "log.h"
#include "privs.h"
#include "memory.h"
+#include "qpthreads.h"
+
+/* Needs to be qpthread safe */
+static qpt_mutex_t* mx = NULL;
#ifdef HAVE_CAPABILITIES
/* sort out some generic internal types for:
@@ -61,6 +65,7 @@ typedef priv_set_t *pstorage_t;
* zprivs_terminate is called and the NULL handler is installed.
*/
static zebra_privs_current_t zprivs_null_state = ZPRIVS_RAISED;
+static int raise_count = 0; /* keep raised until all pthreads have lowered */
/* internal privileges state */
static struct _zprivs_t
@@ -187,25 +192,42 @@ int
zprivs_change_caps (zebra_privs_ops_t op)
{
cap_flag_value_t cflag;
-
+ int result = 0;
+ int change = 0;
+
+ qpt_mutex_lock(mx);
+
/* should be no possibility of being called without valid caps */
assert (zprivs_state.syscaps_p && zprivs_state.caps);
if (! (zprivs_state.syscaps_p && zprivs_state.caps))
- exit (1);
-
+ {
+ qpt_mutex_unlock(mx);
+ exit (1);
+ }
+
if (op == ZPRIVS_RAISE)
- cflag = CAP_SET;
+ {
+ cflag = CAP_SET;
+ change = (raise_count++ == 0);
+ }
else if (op == ZPRIVS_LOWER)
- cflag = CAP_CLEAR;
+ {
+ cflag = CAP_CLEAR;
+ change = (--raise_count == 0);
+ }
else
- return -1;
+ {
+ result = -1;
+ }
- if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE,
+ if ( change && !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE,
zprivs_state.syscaps_p->num,
zprivs_state.syscaps_p->caps,
cflag))
- return cap_set_proc (zprivs_state.caps);
- return -1;
+ result = cap_set_proc (zprivs_state.caps);
+
+ qpt_mutex_unlock(mx);
+ return result;
}
zebra_privs_current_t
@@ -213,11 +235,17 @@ zprivs_state_caps (void)
{
int i;
cap_flag_value_t val;
+ zebra_privs_current_t result = ZPRIVS_LOWERED;
+
+ qpt_mutex_lock(mx);
/* should be no possibility of being called without valid caps */
assert (zprivs_state.syscaps_p && zprivs_state.caps);
if (! (zprivs_state.syscaps_p && zprivs_state.caps))
- exit (1);
+ {
+ qpt_mutex_unlock(mx);
+ exit (1);
+ }
for (i=0; i < zprivs_state.syscaps_p->num; i++)
{
@@ -226,12 +254,18 @@ zprivs_state_caps (void)
{
zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s",
safe_strerror (errno) );
- return ZPRIVS_UNKNOWN;
+ result = ZPRIVS_UNKNOWN;
+ break;
}
if (val == CAP_SET)
- return ZPRIVS_RAISED;
+ {
+ result = ZPRIVS_RAISED;
+ break;
+ }
}
- return ZPRIVS_LOWERED;
+
+ qpt_mutex_unlock(mx);
+ return result;
}
static void
@@ -376,12 +410,16 @@ zcaps2sys (zebra_capabilities_t *zcaps, int num)
int
zprivs_change_caps (zebra_privs_ops_t op)
{
+ int result = 0;
+ qpt_mutex_lock(mx);
+
/* should be no possibility of being called without valid caps */
assert (zprivs_state.syscaps_p);
if (!zprivs_state.syscaps_p)
{
fprintf (stderr, "%s: Eek, missing caps!", __func__);
+ qpt_mutex_unlock(mx);
exit (1);
}
@@ -389,49 +427,66 @@ zprivs_change_caps (zebra_privs_ops_t op)
* to lower: just clear the working effective set
*/
if (op == ZPRIVS_RAISE)
- priv_copyset (zprivs_state.syscaps_p, zprivs_state.caps);
+ {
+ if (raise_count++ == 0)
+ {
+ priv_copyset (zprivs_state.syscaps_p, zprivs_state.caps);
+ if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0)
+ result = -1;
+ }
+ }
else if (op == ZPRIVS_LOWER)
- priv_emptyset (zprivs_state.caps);
+ {
+ if (--raise_count == 0)
+ {
+ priv_emptyset (zprivs_state.caps);
+ if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0)
+ result = -1;
+ }
+ }
else
- return -1;
+ result = -1;
- if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0)
- return -1;
+ qpt_mutex_unlock(mx);
- return 0;
+ return result;
}
/* Retrieve current privilege state, is it RAISED or LOWERED? */
zebra_privs_current_t
zprivs_state_caps (void)
{
- zebra_privs_current_t result;
+ zebra_privs_current_t result = ZPRIVS_UNKNOWN;
pset_t *effective;
+ qpt_mutex_lock(mx);
+
if ( (effective = priv_allocset()) == NULL)
{
fprintf (stderr, "%s: failed to get priv_allocset! %s\n", __func__,
safe_strerror (errno));
- return ZPRIVS_UNKNOWN;
- }
-
- if (getppriv (PRIV_EFFECTIVE, effective))
- {
- fprintf (stderr, "%s: failed to get state! %s\n", __func__,
- safe_strerror (errno));
- result = ZPRIVS_UNKNOWN;
}
else
{
- if (priv_isemptyset (effective) == B_TRUE)
- result = ZPRIVS_LOWERED;
+
+ if (getppriv (PRIV_EFFECTIVE, effective))
+ {
+ fprintf (stderr, "%s: failed to get state! %s\n", __func__,
+ safe_strerror (errno));
+ }
else
- result = ZPRIVS_RAISED;
+ {
+ if (priv_isemptyset (effective) == B_TRUE)
+ result = ZPRIVS_LOWERED;
+ else
+ result = ZPRIVS_RAISED;
+ }
+
+ if (effective)
+ priv_freeset (effective);
}
- if (effective)
- priv_freeset (effective);
-
+ qpt_mutex_unlock(mx);
return result;
}
@@ -560,19 +615,42 @@ zprivs_caps_terminate (void)
int
zprivs_change_uid (zebra_privs_ops_t op)
{
+ int result = 0;
+
+ qpt_mutex_lock(mx);
if (op == ZPRIVS_RAISE)
- return seteuid (zprivs_state.zsuid);
+ {
+ if (raise_count++ == 0)
+ {
+ result = seteuid (zprivs_state.zsuid);
+ }
+ }
else if (op == ZPRIVS_LOWER)
- return seteuid (zprivs_state.zuid);
+ {
+ if (--raise_count == 0)
+ {
+ result = seteuid (zprivs_state.zuid);
+ }
+ }
else
- return -1;
+ {
+ result = -1;
+ }
+
+ qpt_mutex_unlock(mx);
+ return result;
}
zebra_privs_current_t
zprivs_state_uid (void)
{
- return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
+ zebra_privs_current_t result;
+
+ qpt_mutex_lock(mx);
+ result = ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
+ qpt_mutex_unlock(mx);
+ return result;
}
int
@@ -584,7 +662,24 @@ zprivs_change_null (zebra_privs_ops_t op)
zebra_privs_current_t
zprivs_state_null (void)
{
- return zprivs_null_state;
+ int result;
+
+ qpt_mutex_lock(mx);
+ result = zprivs_null_state;
+ qpt_mutex_unlock(mx);
+ return result;
+}
+
+void
+zprivs_init_r()
+{
+ mx = qpt_mutex_init(mx, qpt_mutex_quagga);
+}
+
+void
+zprivs_destroy_r(void)
+{
+ mx = qpt_mutex_destroy(mx, 1);
}
void
@@ -599,12 +694,15 @@ zprivs_init(struct zebra_privs_t *zprivs)
exit (1);
}
+ qpt_mutex_lock(mx);
+
/* NULL privs */
if (! (zprivs->user || zprivs->group
|| zprivs->cap_num_p || zprivs->cap_num_i) )
{
zprivs->change = zprivs_change_null;
zprivs->current_state = zprivs_state_null;
+ qpt_mutex_unlock(mx);
return;
}
@@ -619,6 +717,7 @@ zprivs_init(struct zebra_privs_t *zprivs)
/* cant use log.h here as it depends on vty */
fprintf (stderr, "privs_init: could not lookup user %s\n",
zprivs->user);
+ qpt_mutex_unlock(mx);
exit (1);
}
}
@@ -635,6 +734,7 @@ zprivs_init(struct zebra_privs_t *zprivs)
{
fprintf (stderr, "privs_init: could not setgroups, %s\n",
safe_strerror (errno) );
+ qpt_mutex_unlock(mx);
exit (1);
}
}
@@ -642,6 +742,7 @@ zprivs_init(struct zebra_privs_t *zprivs)
{
fprintf (stderr, "privs_init: could not lookup vty group %s\n",
zprivs->vty_group);
+ qpt_mutex_unlock(mx);
exit (1);
}
}
@@ -656,6 +757,7 @@ zprivs_init(struct zebra_privs_t *zprivs)
{
fprintf (stderr, "privs_init: could not lookup group %s\n",
zprivs->group);
+ qpt_mutex_unlock(mx);
exit (1);
}
/* change group now, forever. uid we do later */
@@ -663,6 +765,7 @@ zprivs_init(struct zebra_privs_t *zprivs)
{
fprintf (stderr, "zprivs_init: could not setregid, %s\n",
safe_strerror (errno) );
+ qpt_mutex_unlock(mx);
exit (1);
}
}
@@ -671,7 +774,7 @@ zprivs_init(struct zebra_privs_t *zprivs)
zprivs_caps_init (zprivs);
#else /* !HAVE_CAPABILITIES */
/* we dont have caps. we'll need to maintain rid and saved uid
- * and change euid back to saved uid (who we presume has all neccessary
+ * and change euid back to saved uid (who we presume has all necessary
* privileges) whenever we are asked to raise our privileges.
*
* This is not worth that much security wise, but all we can do.
@@ -683,6 +786,7 @@ zprivs_init(struct zebra_privs_t *zprivs)
{
fprintf (stderr, "privs_init (uid): could not setreuid, %s\n",
safe_strerror (errno));
+ qpt_mutex_unlock(mx);
exit (1);
}
}
@@ -690,6 +794,8 @@ zprivs_init(struct zebra_privs_t *zprivs)
zprivs->change = zprivs_change_uid;
zprivs->current_state = zprivs_state_uid;
#endif /* HAVE_CAPABILITIES */
+
+ qpt_mutex_unlock(mx);
}
void
@@ -701,6 +807,8 @@ zprivs_terminate (struct zebra_privs_t *zprivs)
exit (0);
}
+ qpt_mutex_lock(mx);
+
#ifdef HAVE_CAPABILITIES
zprivs_caps_terminate();
#else /* !HAVE_CAPABILITIES */
@@ -710,6 +818,7 @@ zprivs_terminate (struct zebra_privs_t *zprivs)
{
fprintf (stderr, "privs_terminate: could not setreuid, %s",
safe_strerror (errno) );
+ qpt_mutex_unlock(mx);
exit (1);
}
}
@@ -718,20 +827,25 @@ zprivs_terminate (struct zebra_privs_t *zprivs)
zprivs->change = zprivs_change_null;
zprivs->current_state = zprivs_state_null;
zprivs_null_state = ZPRIVS_LOWERED;
+ raise_count = 0;
+
+ qpt_mutex_unlock(mx);
return;
}
void
zprivs_get_ids(struct zprivs_ids_t *ids)
{
+ qpt_mutex_lock(mx);
- ids->uid_priv = getuid();
- (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid)
+ ids->uid_priv = getuid();
+ (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid)
: (ids->uid_normal = -1);
- (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid)
+ (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid)
: (ids->gid_normal = -1);
- (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp)
+ (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp)
: (ids->gid_vty = -1);
+ qpt_mutex_unlock(mx);
return;
}
diff --git a/lib/privs.h b/lib/privs.h
index 46d614e0..9c789d16 100644
--- a/lib/privs.h
+++ b/lib/privs.h
@@ -81,9 +81,13 @@ struct zprivs_ids_t
};
/* initialise zebra privileges */
+extern void zprivs_init_r (void);
extern void zprivs_init (struct zebra_privs_t *zprivs);
+extern void zprivs_destroy_r (void);
+
/* drop all and terminate privileges */
extern void zprivs_terminate (struct zebra_privs_t *);
+
/* query for runtime uid's and gid's, eg vty needs this */
extern void zprivs_get_ids(struct zprivs_ids_t *);
diff --git a/lib/qlib_init.c b/lib/qlib_init.c
new file mode 100644
index 00000000..8f46c610
--- /dev/null
+++ b/lib/qlib_init.c
@@ -0,0 +1,84 @@
+/* Quagga library initialise/closedown -- functions
+ * 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 "qlib_init.h"
+#include "zassert.h"
+#include "memory.h"
+#include "qpthreads.h"
+
+/*==============================================================================
+ * Quagga Library Initialise/Closedown
+ *
+ * This gathers together the essential initialisation and closedown for the
+ * library. This ensures that any changes in the library are contained here,
+ * and do not require changes in all users of the library.
+ *
+ * There are two stages of initialisation:
+ *
+ * 1) first stage
+ *
+ * this is expected to be called before the program does anything at all.
+ *
+ * This performs all initialisation required to support asserts, logging,
+ * basic I/O (but not the remote console), trap signals... and so on.
+ *
+ * After this has been done, the system is in good shape to deal with
+ * command line options, configuration files and so on.
+ *
+ * 2) second stage
+ *
+ * this is expected to be called before the program does any serious work.
+ *
+ * This performs all initialisation required to support socket I/O,
+ * thread handling, timers, and so on.
+ *
+ * In particular, at this stage the system is set into Pthread Mode, if
+ * required. No pthreads may be started before this. Up to this point
+ * the system operates in non-Pthread Mode -- all mutexes are implicitly
+ * free.
+ *
+ * There is one stage of closedown. This is expected to be called last, and
+ * is passed the exit code.
+ *
+ *
+ */
+
+void
+qlib_init_first_stage(void)
+{
+}
+
+void
+qlib_init_second_stage(int pthreads)
+{
+ qpt_set_qpthreads_enabled(pthreads);
+ memory_init_r();
+}
+
+
+void
+qexit(int exit_code)
+{
+ memory_finish();
+ exit (exit_code);
+}
+
+
diff --git a/lib/qlib_init.h b/lib/qlib_init.h
new file mode 100644
index 00000000..0d5fbd7e
--- /dev/null
+++ b/lib/qlib_init.h
@@ -0,0 +1,40 @@
+/* Quagga library initialise/closedown -- 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_QLIB_INIT_H
+#define _ZEBRA_QLIB_INIT_H
+
+/*==============================================================================
+ * Quagga Library Initialise/Closedown
+ *
+ * This gathers together the essential initialisation and closedown for the
+ * library.
+ *
+ * See qlib_init.c.
+ */
+
+extern void qlib_init_first_stage(void) ;
+
+extern void qlib_init_second_stage(int pthreads) ;
+
+extern void qexit(int exit_code) ;
+
+#endif /* _ZEBRA_QLIB_INIT_H */
diff --git a/lib/qpnexus.c b/lib/qpnexus.c
new file mode 100644
index 00000000..5a5c5417
--- /dev/null
+++ b/lib/qpnexus.c
@@ -0,0 +1,230 @@
+/* Quagga Pthreads support -- 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.
+ */
+
+#include <zebra.h>
+
+#include "qpnexus.h"
+#include "memory.h"
+#include "thread.h"
+#include "sigevent.h"
+
+/* prototypes */
+static void* qpn_start_main(void* arg);
+static void* qpn_start_bgp(void* arg);
+
+/* Master of the threads. */
+extern struct thread_master *master;
+
+/*==============================================================================
+ * Quagga Nexus Interface -- qpt_xxxx
+ *
+
+ */
+
+/* Initialise a nexus -- allocating it if required.
+ *
+ * If main_thread is set then no new thread will be created
+ * when qpn_exec() is called, instead the finite state machine will be
+ * run in the calling thread. The main thread will only block the
+ * message queue's signal. Non main threads will block all signals.
+ *
+ * Returns the qtn_nexus.
+ */
+qpn_nexus
+qpn_init_new(qpn_nexus qpn)
+{
+ if (qpn == NULL)
+ qpn = XCALLOC(MTYPE_QPN_NEXUS, sizeof(struct qpn_nexus)) ;
+ else
+ memset(qpn, 0, sizeof(struct qpn_nexus)) ;
+
+ return qpn;
+}
+
+/* Initialize main qpthread, no queue */
+qpn_nexus
+qpn_init_main(qpn_nexus qpn)
+{
+ qpn = qpn_init_new(qpn);
+
+ qpn->selection = qps_selection_init_new(qpn->selection);
+ qpn->pile = qtimer_pile_init_new(qpn->pile);
+ qpn->main_thread = 1;
+ qpn->start = qpn_start_main;
+
+ return qpn;
+}
+
+/* Initialize bgp qpthread */
+qpn_nexus
+qpn_init_bgp(qpn_nexus qpn)
+{
+ qpn = qpn_init_new(qpn);
+ qpn->queue = mqueue_init_new(qpn->queue, mqt_signal_unicast);
+ qpn->start = qpn_start_bgp;
+
+ return qpn;
+}
+
+/* free timers, selection, message queue and nexus */
+static void
+qpn_free(qpn_nexus qpn)
+{
+ qps_file qf;
+ qtimer qtr;
+
+ /* timers and the pile */
+ if (qpn->pile != NULL)
+ {
+ while ((qtr = qtimer_pile_ream(qpn->pile, 1)))
+ {
+ qtimer_free(qtr);
+ }
+ }
+
+ /* files and selection */
+ if (qpn->selection != NULL)
+ {
+ while ((qf = qps_selection_ream(qpn->selection, 1)))
+ {
+ qps_file_free(qf);
+ }
+ }
+
+ /* TODO: free qtn->queue */
+
+ XFREE(MTYPE_QPN_NEXUS, qpn) ;
+}
+
+/* If not main thread create new qpthread.
+ * Execute the state machine */
+void
+qpn_exec(qpn_nexus qpn)
+{
+ if (qpn->main_thread)
+ {
+ /* Run the state machine in calling thread */
+ qpn->start(qpn);
+ }
+ else
+ {
+ /* create a qpthread and run the state machine in it */
+ qpn->thread_id = qpt_thread_create(qpn->start, qpn, NULL) ;
+ }
+}
+
+/* Main qpthread, prep signals, then run finite state machine
+ * using qps_selection and qtimer
+*/
+static void*
+qpn_start_main(void* arg)
+{
+ qpn_nexus qpn = arg;
+ int actions;
+ qtime_mono_t now;
+ sigset_t newmask;
+
+ qpn->thread_id = qpt_thread_self();
+
+ /* Main thread, block the message queue's signal */
+ sigemptyset (&newmask);
+ sigaddset (&newmask, SIGMQUEUE);
+ qpt_thread_sigmask(SIG_BLOCK, &newmask, NULL);
+ qps_set_signal(qpn->selection, SIGMQUEUE, newmask);
+
+ while (!qpn->terminate)
+ {
+ /* Signals are highest priority.
+ * only execute on the main thread */
+ quagga_sigevent_process ();
+
+ /* process timers */
+ now = qt_get_monotonic();
+ while (qtimer_pile_dispatch_next(qpn->pile, now))
+ {
+ }
+
+ /* block for some input, output or timeout */
+ actions = qps_pselect( qpn->selection,
+ qtimer_pile_top_time(qpn->pile, now + QTIME(MAX_PSELECT_TIMOUT)) );
+
+ /* process I/O actions */
+ while (actions)
+ {
+ actions = qps_dispatch_next(qpn->selection) ;
+ }
+ }
+
+ qpn_free(qpn);
+
+ return NULL;
+}
+
+/* Bgp prep signals, then run finite state machine
+ * using legacy threads
+*/
+static void*
+qpn_start_bgp(void* arg)
+{
+ qpn_nexus qpn = arg;
+ struct thread thread;
+ mqueue_block mqb;
+ sigset_t newmask;
+
+ qpn->thread_id = qpt_thread_self();
+
+ /*
+ * Not main thread. Block most signals, but be careful not to
+ * defer SIGTRAP because doing so breaks gdb, at least on
+ * NetBSD 2.0. Avoid asking to block SIGKILL, just because
+ * we shouldn't be able to do so.
+ */
+ sigfillset (&newmask);
+ sigdelset (&newmask, SIGTRAP);
+ sigdelset (&newmask, SIGKILL);
+
+ qpt_thread_sigmask(SIG_BLOCK, &newmask, NULL);
+ qpn->mts = mqueue_thread_signal_init(qpn->mts, qpn->thread_id, SIGMQUEUE);
+
+ while (!qpn->terminate)
+ {
+
+ /* drain the message queue, will be waiting when it's empty */
+ for (;;)
+ {
+ mqb = mqueue_dequeue(qpn->queue, 1, qpn->mts) ;
+ if (mqb == NULL)
+ break;
+
+ mqb_dispatch(mqb);
+ }
+
+ /* TODO: use qpselect stuff */
+ if (thread_fetch (master, &thread))
+ thread_call (&thread);
+
+ mqueue_done_waiting(qpn->queue, qpn->mts);
+ }
+
+ qpn_free(qpn);
+
+ return NULL;
+}
diff --git a/lib/qpnexus.h b/lib/qpnexus.h
new file mode 100644
index 00000000..bda5234f
--- /dev/null
+++ b/lib/qpnexus.h
@@ -0,0 +1,97 @@
+/* Quagga Pthreads support -- 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_QPNEXUS_H
+#define _ZEBRA_QPNEXUS_H
+
+#include <stdint.h>
+#include <time.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "zassert.h"
+#include "qpthreads.h"
+#include "qtimers.h"
+#include "mqueue.h"
+#include "qpselect.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Quagga Nexus Interface -- qpn_xxxx
+ *
+ * Object to hold, a qpthread, a qps_selection, a qtimer_pile, a mqueue_queue
+ * together with the thread routine to poll and dispatch their respective
+ * action routines.
+ *
+ */
+
+/* maximum time in seconds to sit in a pselect */
+#define MAX_PSELECT_TIMOUT 10
+
+/* signal for message queues */
+#define SIGMQUEUE SIGUSR2
+
+/*==============================================================================
+ * Data Structures.
+ */
+
+typedef struct qpn_nexus* qpn_nexus ;
+
+struct qpn_nexus
+{
+ /* set true to terminate the thread (eventually) */
+ int terminate;
+
+ /* true if this is the main thread */
+ int main_thread;
+
+ /* thread ID */
+ qpt_thread_t thread_id;
+
+ /* pselect handler */
+ qps_selection selection;
+
+ /* timer pile */
+ qtimer_pile pile;
+
+ /* message queue */
+ mqueue_queue queue;
+ mqueue_thread_signal mts;
+
+ /* qpthread routine */
+ void* (*start)(void*);
+
+};
+
+/*==============================================================================
+ * Functions
+ */
+
+extern qpn_nexus qpn_init_new(qpn_nexus qtn);
+extern qpn_nexus qpn_init_main(qpn_nexus qtn);
+extern qpn_nexus qpn_init_bgp(qpn_nexus qtn);
+extern void qpn_exec(qpn_nexus qtn);
+
+#endif /* _ZEBRA_QPNEXUS_H */
diff --git a/lib/qpselect.c b/lib/qpselect.c
new file mode 100644
index 00000000..fb02f0fa
--- /dev/null
+++ b/lib/qpselect.c
@@ -0,0 +1,1321 @@
+/* Quagga pselect support -- 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.
+ */
+
+#include <signal.h>
+#include <string.h>
+
+#include "zassert.h"
+#include "qpselect.h"
+#include "qpthreads.h"
+#include "qtime.h"
+#include "memory.h"
+#include "vector.h"
+
+/*==============================================================================
+ * Quagga pselect -- qps_xxxx
+ *
+ * Here and in qpselect.h is a data structure for managing multiple file
+ * descriptors and running pselect to wait for I/O activity and to multiplex
+ * between the file descriptors.
+ *
+ * The qps_selection structure manages a collection of file descriptors which
+ * are to be waited on together in a pselect statement.
+ *
+ * NB: it is ASSUMED that a qps_selection will be private to the thread in
+ * which it is created and used.
+ *
+ * There is NO mutex handling here.
+ *
+ * This supports pselect, so supports:
+ *
+ * * waiting for file descriptors, which may each be expecting any combination
+ * of error/read/write events.
+ *
+ * Files may be added or removed from the selection. Files in the selection
+ * may then be enabled/disabled for any combination of error/read/write
+ * "mode" events.
+ *
+ * * a timeout *time*
+ *
+ * This is a qtime monotonic time at which to time out. (This is unlike
+ * pselect() itself, which takes a timeout interval.)
+ *
+ * Infinite timeouts are not supported.
+ *
+ * * an optional signal number and sigmask
+ *
+ * So that a signal may be used to interrupt a waiting pselect.
+ *
+ * For this to work there must be a signal which is generally masked, and
+ * is unmasked for the duration of the pselect.
+ *
+ * When a pselect returns there may be a number of files with events pending.
+ * The qps_dispatch_next() calls the action routine for the next event to be
+ * dealt with. Events are dispatched in the order: error, read and write, and
+ * then in file descriptor order. (So all error events in fd order, then all
+ * read events, and so on.)
+ *
+ * Note that at no time are any modes automatically disabled. So the system is
+ * level triggered. So, for example, a read event that is not dealt with will
+ * be triggered again on the next pselect -- unless the read mode is explicitly
+ * disabled for the file.
+ *
+ * Action Functions
+ * ----------------
+ *
+ * There is a separate action function for each mode. Each file has its own
+ * set of action functions -- so these may be used to implement a form of
+ * state machine for the file.
+ *
+ * When the action function is called it is passed the qps_file structure and
+ * the file_info pointer from that structure.
+ *
+ * During an action function modes may be enabled/disabled, actions changed,
+ * 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
+ */
+
+/* Forward references */
+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_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.
+ */
+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)) ;
+
+ /* Zeroising initialises:
+ *
+ * fd_count -- no fd's yet
+ * fd_direct -- not direct lookup
+ *
+ * files -- empty vector
+ *
+ * fd_last -- unset
+ * enabled_count -- no fd's enabled in any mode
+ * enabled -- empty bit vectors
+ *
+ * tried_fd_last -- nothing tried yet
+ * tried_count -- nothing tried yet
+ * results -- empty bit vectors
+ *
+ * pend_count -- no results to dispatch
+ * pend_mnum -- unset
+ * pend_fd -- unset
+ *
+ * signum -- no signal to be enabled
+ * sigmask -- unset
+ *
+ * So nothing else to do -- see also qps_selection_re_init(), below.
+ */
+
+ return qps ;
+} ;
+
+/* Re-initialise a selection.
+ */
+static void
+qps_selection_re_init(qps_selection qps)
+{
+ memset(qps, 0, sizeof(struct qps_selection)) ;
+} ;
+
+/* 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.
+ *
+ * Adding a file using the same fd as an existing file is a FATAL error.
+ *
+ * Adding a file which is already a member a selection is a FATAL error.
+ */
+void
+qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info)
+{
+ passert(qf->selection == NULL) ;
+
+ qf->selection = qps ;
+
+ qf->file_info = file_info ;
+ qf->fd = fd ;
+
+ qf->enabled_bits = 0 ;
+
+ qps_file_lookup_fd(qps, fd, qf) ; /* Add. */
+} ;
+
+/* 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.
+ *
+ * When the file is removed it is disabled in all modes.
+ */
+void
+qps_remove_file(qps_file qf)
+{
+ if (qf->selection != NULL)
+ qps_file_remove(qf->selection, qf) ;
+} ;
+
+/* Ream (another) file out of the selection.
+ *
+ * If selection is empty, release the qps_selection structure, if required.
+ *
+ * See: #define qps_selection_ream_free(qps)
+ * #define qps_selection_ream_keep(qps)
+ *
+ * Useful for emptying out and discarding a selection:
+ *
+ * while ((qf = qps_selection_ream_free(qps)))
+ * ... do what's required to release the qps_file
+ *
+ * The file is removed from the selection before being returned.
+ *
+ * Returns NULL when selection is empty (and has been released, if required).
+ *
+ * If the selection is not released, it may be reused without reinitialisation.
+ *
+ * NB: once reaming has started, the selection MUST NOT be used for anything,
+ * and the process MUST be run to completion.
+ */
+qps_file
+qps_selection_ream(qps_selection qps, int free_structure)
+{
+ qps_file qf ;
+
+ qf = vector_get_last_item(&qps->files) ;
+ if (qf != NULL)
+ qps_file_remove(qps, qf) ;
+ else
+ {
+ passert(qps->fd_count == 0) ;
+
+ if (free_structure)
+ XFREE(MTYPE_QPS_SELECTION, qps) ;
+ else
+ qps_selection_re_init(qps) ;
+ } ;
+
+ return qf ;
+} ;
+
+/* Set the signal mask for the selection.
+ *
+ * This supports the unmasking of a single signal for the duration of the
+ * pselect operation.
+ *
+ * It is assumed that the set of signals generally masked by a thread is
+ * essentially static. So this function is passed that set. (So the sigmask
+ * argument must have the signum signal masked.)
+ *
+ * If the set of signals masked by the thread changes, then this function
+ * should be called again.
+ *
+ * Setting a signum == 0 turns OFF the use of the sigmask.
+ */
+void
+qps_set_signal(qps_selection qps, int signum, sigset_t sigmask)
+{
+ qps->signum = signum ;
+
+ if (signum != 0)
+ {
+ assert(sigismember(&sigmask, signum)) ;
+ sigdelset(&sigmask, signum) ;
+ qps->sigmask = sigmask ;
+ } ;
+} ;
+
+/* Execute a pselect for the given selection -- subject to the given timeout
+ * *time*.
+ *
+ * The time-out time is an "absolute" time, as measured by qt_get_monotonic().
+ *
+ * A timeout time <= the current qt_get_monotonic() is treated as a zero
+ * timeout period, and will return immediately from the pselect.
+ *
+ * There is no support for an infinite timeout.
+ *
+ * Returns: -1 => EINTR occurred -- ie a signal has gone off
+ * 0 => hit timeout -- no files are ready
+ * > 0 => there are this many files ready in one or more modes
+ *
+ * All other errors are FATAL.
+ *
+ * The qps_dispatch_next() processes the returns from pselect().
+ */
+int
+qps_pselect(qps_selection qps, qtime_mono_t timeout)
+{
+ struct timespec ts ;
+ qps_mnum_t mnum ;
+ fd_set* p_fds[qps_mnum_count] ;
+ int n ;
+
+ /* TODO: put this under a debug skip */
+ 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 */
+ /* the enabled vectors, there are none from a previous run of pselect */
+ /* left hanging about. (pselect SHOULD ignore everything above the */
+ /* given count of fds -- but it does no harm to be tidy, and should */
+ /* not have to do this often.) */
+ if (qps->pend_count != 0)
+ qps_super_set_zero(qps->results, qps_mnum_count) ;
+
+ /* Prepare the argument/result bitmaps */
+ /* Capture pend_mnum and tried_count[] */
+
+ n = fd_byte_count[qps->fd_last] ; /* copy up to last sig. byte */
+
+ qps->pend_mnum = qps_mnum_count ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if ((qps->tried_count[mnum] = qps->enabled_count[mnum]) != 0)
+ {
+ memcpy(&(qps->results[mnum].bytes), &(qps->enabled[mnum].bytes), n) ;
+ p_fds[mnum] = &(qps->results[mnum].fdset) ;
+ if (mnum < qps->pend_mnum)
+ qps->pend_mnum = mnum ;
+ }
+ else
+ p_fds[mnum] = NULL ;
+
+ /* Capture tried_fd_last and set initial pend_fd. */
+ qps->tried_fd_last = qps->fd_last ;
+ qps->pend_fd = 0 ;
+
+ /* Convert timeout time to interval for pselect() */
+ timeout -= qt_get_monotonic() ;
+ if (timeout < 0)
+ timeout = 0 ;
+
+ /* Finally ready for the main event */
+ n = pselect(qps->fd_last + 1, p_fds[qps_read_mnum],
+ p_fds[qps_write_mnum],
+ p_fds[qps_error_mnum],
+ qtime2timespec(&ts, timeout),
+ (qps->signum != 0) ? &qps->sigmask : NULL) ;
+
+ /* If have something, set and return the pending count. */
+ if (n > 0)
+ {
+ assert(qps->pend_mnum < qps_mnum_count) ; /* expected something */
+
+ return qps->pend_count = n ; /* set and return pending count */
+ } ;
+
+ /* Flush the results vectors -- not apparently done if n <= 0) */
+ qps_super_set_zero(qps->results, qps_mnum_count) ;
+
+ qps->pend_count = 0 ; /* nothing pending */
+
+ /* Return appropriately, if we can */
+ if ((n == 0) || (errno == EINTR))
+ return n ;
+
+ zabort_errno("Failed in pselect") ;
+} ;
+
+/* 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.
+ *
+ * Processes one file per call of this function, by invoking the file's
+ * "action" routine.
+ *
+ * If a given file is ready in more than one mode, all modes will be processed,
+ * unless the action routine for one mode disables the file for other modes, or
+ * removes it from the selection.
+ *
+ * Returns the number of files left to process (after the one just processed).
+ */
+int
+qps_dispatch_next(qps_selection qps)
+{
+ int fd ;
+ qps_file qf ;
+ qps_mnum_t mnum ;
+
+ /* TODO: put this under a debug skip */
+ qps_selection_validate(qps) ;
+
+ if (qps->pend_count == 0)
+ return 0 ; /* quit immediately of nothing to do. */
+
+ fd = qps->pend_fd ;
+ mnum = qps->pend_mnum ;
+
+ dassert( (mnum >= 0) && (mnum < qps_mnum_count)
+ && (qps->tried_count[mnum] != 0)
+ && (qps->pend_count > 0) ) ;
+
+ while (1)
+ {
+ fd = qps_next_fd_pending(&(qps->results[mnum]), fd, qps->tried_fd_last) ;
+ if (fd >= 0)
+ break ; /* easy if have another fd in current mode. */
+
+ do /* step to next mode that was not empty */
+ {
+ qps->tried_count[mnum] = 0 ; /* tidy up as we go */
+ ++mnum ;
+ if (mnum >= qps_mnum_count)
+ zabort("Unexpectedly ran out of pending stuff") ;
+ } while (qps->tried_count[mnum] == 0) ;
+
+ qps->pend_mnum = mnum ; /* update mode */
+ fd = 0 ; /* back to the beginning */
+ } ;
+
+ qps->pend_count -= 1 ; /* one less pending */
+ qps->pend_fd = fd ; /* update scan */
+
+ qf = qps_file_lookup_fd(qps, fd, NULL) ;
+
+ dassert( ((qf->enabled_bits && qps_mbit(mnum)) != 0) &&
+ (qf->actions[mnum] != NULL) ) ;
+
+ qf->actions[mnum](qf, qf->file_info) ; /* dispatch the required action */
+
+ return qps->pend_count ;
+} ;
+
+/*==============================================================================
+ * qps_file structure handling
+ */
+
+/* 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.
+ *
+ * Once initialised, the file may be added to a selection.
+ *
+ * Returns the qps_file.
+ */
+qps_file
+qps_file_init_new(qps_file qf, qps_file template)
+{
+ if (qf == NULL)
+ qf = XCALLOC(MTYPE_QPS_FILE, sizeof(struct qps_file)) ;
+ else
+ memset(qf, 0, sizeof(struct qps_file)) ;
+
+ /* Zeroising has initialised:
+ *
+ * selection -- NULL -- ditto
+ *
+ * file_info -- NULL -- is set by qps_add_file()
+ * fd -- unset -- ditto
+ *
+ * enabled_bits -- nothing enabled
+ *
+ * actions[] -- all set to NULL
+ */
+
+ if (template != NULL)
+ memcpy(qf->actions, template->actions, sizeof(qf->actions)) ;
+
+ return qf ;
+} ;
+
+/* Free dynamically allocated qps_file structure.
+ *
+ * It is the caller's responsibility to have removed it from any selection it
+ * may have been in.
+ */
+void
+qps_file_free(qps_file qf)
+{
+ assert(qf->selection == NULL) ; /* Mustn't be a selection member ! */
+
+ XFREE(MTYPE_QPS_FILE, qf) ;
+} ;
+
+/* Enable (or re-enable) file for the given mode.
+ *
+ * If the action argument is not NULL, set the action for the mode.
+ *
+ * NB: It is a FATAL error to enable a mode with a NULL action.
+ *
+ * NB: It is a FATAL error to enable modes for a file which is not in a
+ * selection.
+ */
+void
+qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action)
+{
+ qps_mbit_t mbit = qps_mbit(mnum) ;
+ qps_selection qps = qf->selection ;
+
+ dassert(qps != NULL) ;
+ dassert((mnum >= 0) && (mnum <= qps_mnum_count)) ;
+
+ if (action != NULL)
+ qf->actions[mnum] = action ;
+ else
+ dassert(qf->actions[mnum] != NULL) ;
+
+ if (qf->enabled_bits & mbit)
+ dassert(FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset))) ;
+ else
+ {
+ dassert( ! FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset))) ;
+ FD_SET(qf->fd, &(qps->enabled[mnum].fdset)) ;
+ ++qps->enabled_count[mnum] ;
+ qf->enabled_bits |= mbit ;
+ } ;
+} ;
+
+/* Set action for given mode -- does not enable/disable.
+ *
+ * May unset an action by setting it NULL !
+ *
+ * See above for discussion of action functions.
+ *
+ * NB: it is a fatal error to unset an action for a mode which is enabled.
+ */
+void
+qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action)
+{
+ dassert((mnum >= 0) && (mnum <= qps_mnum_count)) ;
+
+ if (action == NULL)
+ passert((qf->enabled_bits & qps_mbit(mnum)) == 0) ;
+
+ qf->actions[mnum] = action ;
+} ;
+
+/* Disable file for one or more modes.
+ *
+ * If there are any pending pending results for the modes, those are discarded.
+ *
+ * Note that this is modestly "optimised" to deal with disabling a single mode.
+ * (Much of the time only the write mode will be being disabled !)
+ *
+ * NB: it is safe to disable modes which are not enabled -- even if the file
+ * is not currently a member of a selection. (If it is not a member of a
+ * collection no modes should be enabled !)
+ */
+
+static qps_mnum_t qps_first_mnum[qps_mbit(qps_mnum_count)] =
+ {
+ -1, /* 0 -> -1 -- no bit set */
+ 0, /* 1 -> 0 -- B0 is first bit */
+ 1, /* 2 -> 1 -- B1 is first bit */
+ 1, /* 3 -> 1 -- B1 is first bit */
+ 2, /* 4 -> 2 -- B2 is first bit */
+ 2, /* 5 -> 2 -- B2 is first bit */
+ 2, /* 6 -> 2 -- B2 is first bit */
+ 2 /* 7 -> 2 -- B2 is first bit */
+ } ;
+
+CONFIRM(qps_mbit(qps_mnum_count) == 8) ;
+
+void
+qps_disable_modes(qps_file qf, qps_mbit_t mbits)
+{
+ qps_mnum_t mnum ;
+
+ qps_selection qps = qf->selection ;
+
+ dassert((mbits >= 0) && (mbits <= qps_all_mbits)) ;
+
+ mbits &= qf->enabled_bits ; /* don't bother with any not enabled */
+ qf->enabled_bits ^= mbits ; /* unset what we're about to disable */
+
+ while (mbits != 0)
+ {
+ mnum = qps_first_mnum[mbits] ;
+
+ dassert(qps->enabled_count[mnum] > 0) ;
+ dassert(FD_ISSET(qf->fd, &(qps->enabled[mnum].fdset))) ;
+
+ FD_CLR(qf->fd, &(qps->enabled[mnum].fdset)) ;
+ --qps->enabled_count[mnum] ;
+
+ if ((qps->pend_count != 0) && (qps->tried_count[mnum] != 0)
+ && (FD_ISSET(qf->fd, &(qps->results[mnum].fdset))))
+ {
+ FD_CLR(qf->fd, &(qps->results[mnum].fdset)) ;
+ --qps->pend_count ;
+ } ;
+
+ mbits ^= qps_mbit(mnum) ;
+ } ;
+} ;
+
+/*==============================================================================
+ * Handling the files vector.
+ *
+ * For small numbers of fd's, the files vector is kept as a list, in fd order.
+ * Files are found by binary chop, and added/removed by insert/delete in the
+ * list.
+ *
+ * For large numbers of fd's, the files vector is kept as an array, indexed by
+ * fd.
+ */
+
+/* Comparison function for binary chop */
+static int
+qps_fd_cmp(const int** pp_fd, const qps_file* p_qf)
+{
+ if (**pp_fd < (*p_qf)->fd)
+ return -1 ;
+ if (**pp_fd > (*p_qf)->fd)
+ return +1 ;
+ return 0 ;
+}
+
+/* Lookup/Insert file by file-descriptor.
+ *
+ * Inserts if insert argument is not NULL.
+ *
+ * Returns the file we found (if any) or the file we just inserted.
+ *
+ * NB: FATAL error to insert file with same fd as an existing one.
+ */
+static qps_file
+qps_file_lookup_fd(qps_selection qps, int fd, qps_file insert)
+{
+ qps_file qf ;
+ vector_index i ;
+ int ret ;
+
+ dassert((fd >= 0) && (fd < FD_SETSIZE)) ;
+
+ /* Look-up */
+ /* */
+ /* Set i = index for entry in files vector */
+ /* Set ret = 0 <=> i is exact index. */
+ /* < 0 <=> i is just after where entry may be inserted */
+ /* > 0 <=> i is just before where entry may be inserted */
+ if (qps->fd_direct)
+ {
+ i = fd ; /* index of entry */
+ ret = 0 ; /* how to insert, if do */
+ }
+ else
+ i = vector_bsearch(&qps->files, (vector_bsearch_cmp*)qps_fd_cmp,
+ &fd, &ret) ;
+ if (ret == 0)
+ qf = vector_get_item(&qps->files, i) ; /* NULL if not there */
+ else
+ qf = NULL ; /* not there */
+
+ /* Insert now, if required and can: keep fd_count and fd_last up to date. */
+ if (insert != NULL)
+ {
+ if (qf != NULL)
+ zabort("File with given fd already exists in qps_selection") ;
+
+ /* If required, change up to a directly addressed files vector. */
+ if (!qps->fd_direct && (qps->fd_count > 9))
+ {
+ vector tmp ;
+
+ tmp = vector_move_here(NULL, &qps->files) ;
+
+ while ((qf = vector_pop_item(tmp)) != NULL)
+ vector_set_item(&qps->files, qf->fd, qf) ;
+
+ vector_free(tmp) ;
+
+ qps->fd_direct = 1 ;
+
+ i = fd ; /* index is now the fd */
+ ret = 0 ; /* and insert there */
+ } ;
+
+ /* Now can insert accordint to i & ret */
+ vector_insert_item_here(&qps->files, i, ret, insert) ;
+
+ ++qps->fd_count ;
+ if (fd > qps->fd_last)
+ qps->fd_last = fd ;
+
+ qf = insert ; /* will return what we just inserted. */
+ } ;
+
+ /* Sanity checking. */
+ dassert( (qf == NULL) || ((qps == qf->selection) && (fd == qf->fd)) ) ;
+
+ /* Return the file we found or inserted. */
+ return qf ;
+} ;
+
+/* 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 !).
+ */
+static void
+qps_file_remove(qps_selection qps, qps_file qf)
+{
+ qps_file qfd ;
+ int fd_last ;
+
+ passert((qf->fd >= 0) && (qf->fd <= qps->fd_last) && (qps == qf->selection)) ;
+
+ /* Look-up and remove. */
+ if (qps->fd_direct)
+ {
+ qfd = vector_unset_item(&qps->files, qf->fd) ; /* NULL if not there */
+ fd_last = vector_end(&qps->files) - 1 ;
+ }
+ else
+ {
+ qps_file qf_last ;
+ int ret ;
+ vector_index i = vector_bsearch(&qps->files,
+ (vector_bsearch_cmp*)qps_fd_cmp,
+ &qf->fd, &ret) ;
+ if (ret == 0)
+ qfd = vector_delete_item(&qps->files, i) ;
+ else
+ qfd = NULL ;
+
+ qf_last = vector_get_last_item(&qps->files) ;
+ if (qf_last != NULL)
+ fd_last = qf_last->fd ;
+ else
+ fd_last = -1 ;
+ } ;
+
+ passert(qfd == qf) ; /* must have been there and be the expected file */
+
+ /* Keep fd_count and fd_last up to date. */
+ dassert(qps->fd_count > 0) ;
+ --qps->fd_count ;
+
+ dassert( ((qps->fd_count != 0) && (fd_last >= 0)) ||
+ ((qps->fd_count == 0) && (fd_last < 0)) ) ;
+
+ qps->fd_last = (fd_last >= 0) ? fd_last : 0 ;
+
+ /* Also, remove the from all vectors. */
+ qps_disable_modes(qf, qps_all_mbits) ;
+
+ /* Is no longer in the selection. */
+ qf->selection = NULL ;
+} ;
+
+ /*==============================================================================
+ * fd_super_set support.
+ *
+ * For large sets of file descriptors something faster than testing for all
+ * possible bits is required. The fd_super_set assumes that the fd_set is a
+ * straightforward bit-vector, and overlays a 32-bit word array and a byte
+ * array over that.
+ *
+ * Cannot tell if the underlying bit vector is arranged in bytes, or some
+ * longer words. Cannot tell if words are held big or little endian. Cannot
+ * tell if lowest numbered fd will be highest or lowest in whatever unit it's
+ * held in.
+ *
+ * So... we have maps for fd -> our word index, and fd -> byte index.
+ *
+ * we have a map for fd -> mask for bit used in its byte.
+ *
+ * We require that fds will be numbered consistently in bytes. That is,
+ * every (fd mod 8) == n will appear in the same bit in a byte, for all fd (
+ * for n = 0..7). This allows the final map, which takes a byte value and
+ * returns the lowest numbered fd in the byte, mod 8.
+ *
+ * To copy all the bytes for all descriptors 0..fd, also construct
+ * fd_byte_count[] -- which copes with the fact that on a big-endian machine
+ * it is possible that descriptor fd - 8 may be in a higher numbered byte than
+ * fd ! Using this count assumes that the underlying system really does not
+ * look at bits beyond the given maximum fd.
+ */
+
+static short fd_word_map[FD_SETSIZE] ; /* maps fd to word index */
+static short fd_byte_map[FD_SETSIZE] ; /* maps fd to byte index */
+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 */
+
+# 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_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) )
+#else
+# define QPS_BYTE(fd) ( fd / 8 )
+#endif
+
+# if QPS_TEST_B_ORD == 07
+# define QPS_BIT(fd) (0x01 << (fd & 0x7))
+# else
+# define QPS_BIT(fd) (0x80 >> (fd & 0x7))
+# endif
+
+ static void
+ qFD_SET(int fd, fd_set* set)
+ {
+ *((uint8_t*)set + QPS_BYTE(fd)) |= QPS_BIT(fd) ;
+ } ;
+
+ static void
+ qFD_CLR(int fd, fd_set* set)
+ {
+ *((uint8_t*)set + QPS_BYTE(fd)) &= ~QPS_BIT(fd) ;
+ } ;
+
+ static int
+ qFD_ISSET(int fd, fd_set* set)
+ {
+ return (*((uint8_t*)set + QPS_BYTE(fd)) & QPS_BIT(fd)) != 0 ;
+ } ;
+
+ static void
+ qFD_ZERO(fd_set* set)
+ {
+ memset(set, 0, sizeof(fd_set)) ;
+ } ;
+
+#endif
+
+/* Scan for next fd in given fd set, and clear it.
+ *
+ * Starts at the given fd, will not consider anything above fd_last.
+ *
+ * Returns next fd, or -1 if none.
+ */
+static int
+qps_next_fd_pending(fd_super_set* pending, int fd, int fd_last)
+{
+ uint8_t b ;
+
+ while (pending->words[fd_word_map[fd]] == 0) /* step past zero words */
+ {
+ fd = (fd & ~ (FD_WORD_BITS - 1)) + FD_WORD_BITS ;
+ /* step to start of next word */
+ if (fd > fd_last)
+ return -1 ; /* quit if past last */
+ } ;
+
+ fd &= ~0x0007 ; /* step back to first in byte */
+ while ((b = pending->bytes[fd_byte_map[fd]]) == 0)
+ {
+ fd += 8 ;
+ if (fd > fd_last)
+ return -1 ;
+ } ;
+
+ fd += fd_first_map[b] ;
+
+ dassert(fd <= fd_last) ;
+ dassert((b & fd_bit_map[fd]) == fd_bit_map[fd]) ;
+
+ FD_CLR(fd, &pending->fdset) ;
+
+ dassert((b ^ fd_bit_map[fd]) == pending->bytes[fd_byte_map[fd]]) ;
+
+ return fd ;
+} ;
+
+/* 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 !
+ *
+ * It is a FATAL error if things don't work out.
+ */
+static void
+qps_make_super_set_map(void)
+{
+ fd_super_set test ;
+ int fd, i, iw, ib ;
+
+ /* (1) check that a zeroised fd_super_set is an empty one. */
+ qps_super_set_zero(&test, 1) ;
+
+ for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
+ if (qFD_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) ;
+ 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") ;
+
+ /* (3) check that setting one fd sets one bit, and construct the */
+ /* fd_word_map[], fd_byte_map[] and fd_bit_map[]. */
+ for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
+ {
+ fd_word_t w ;
+
+ qFD_SET(fd, &test.fdset) ;
+
+ w = 0 ;
+ for (iw = 0 ; iw < FD_SUPER_SET_WORD_SIZE ; ++iw)
+ {
+ if (test.words[iw] != 0)
+ {
+ if (w != 0)
+ zabort("FD_SET set a bit in more than one word") ;
+
+ w = test.words[iw] ;
+ if ((w == 0) || ((w & (w - 1)) != 0))
+ zabort("FD_SET set more than one bit in a word") ;
+
+ fd_word_map[fd] = iw ;
+
+ ib = iw * FD_WORD_BYTES ;
+ while (test.bytes[ib] == 0)
+ {
+ ++ib ;
+ if (ib >= ((iw + 1) * FD_WORD_BYTES))
+ zabort("FD_SET set something beyond the expected bytes") ;
+ } ;
+ fd_byte_map[fd] = ib ;
+ fd_bit_map[fd] = test.bytes[ib] ;
+ } ;
+ } ;
+
+ if (w == 0)
+ zabort("FD_SET did not set any bit in any word") ;
+
+ qFD_CLR(fd, &test.fdset) ;
+
+ for (iw = 0 ; iw < FD_SUPER_SET_WORD_SIZE ; ++iw)
+ if (test.words[iw] != 0)
+ zabort("FD_CLR did not leave the fd_super_set empty") ;
+ } ;
+
+ /* (4) check the fd_byte_map. */
+ /* make sure that have 8 contiguous fd to a byte. */
+ /* make sure that have 32 contiguous fd to a word. */
+
+ for (fd = 0 ; fd < FD_SETSIZE ; fd += 8)
+ {
+ int fds ;
+ ib = fd_byte_map[fd] ;
+ iw = fd_word_map[fd] ;
+
+ /* Must share the same byte as the next 7 fds */
+ for (fds = fd + 1 ; fds < (fd + 8) ; ++fds)
+ if (fd_byte_map[fds] != ib)
+ zabort("Broken fd_byte_map -- not 8 contiguous fd's in a byte") ;
+
+ /* Must not share the same byte as any other set of 8 fd's */
+ for (fds = 0 ; fds < FD_SETSIZE ; fds += 8)
+ if ((fd_byte_map[fds] == ib) && (fds != fd))
+ zabort("Broken fd_byte_map -- fd's not in expected bytes") ;
+
+ /* Must be one of the bytes in the current word's fd's */
+ if ( (ib < (iw * FD_WORD_BYTES)) || (ib >= ((iw + 1) * FD_WORD_BYTES)) )
+ zabort("Broken fd_byte_map -- fd's not in expected words") ;
+ } ;
+
+ /* (5) check the fd_bit_map */
+ /* make sure that all fd mod 8 map to the same byte value */
+
+ for (i = 0 ; i < 8 ; ++i)
+ {
+ uint8_t b = fd_bit_map[i] ;
+ for (fd = 8 + i ; fd < FD_SETSIZE ; fd += 8)
+ if (fd_bit_map[fd] != b)
+ zabort("Broken fd_bit_map -- inconsistent bit mapping") ;
+ } ;
+
+ /* (6) construct fd_first_map, to get lowest numbered fd (mod 8) from */
+ /* a given byte value. */
+
+ for (i = 0 ; i < 256 ; ++i)
+ fd_first_map[i] = -1 ;
+
+ for (fd = 0 ; fd < 8 ; ++fd)
+ {
+ uint8_t fdb = fd_bit_map[fd] ;
+ for (i = 1 ; i < 256 ; ++i)
+ if ((fd_first_map[i] == -1) && ((i & fdb) != 0))
+ fd_first_map[i] = fd ;
+ } ;
+
+ for (i = 1 ; i < 256 ; ++i)
+ if (fd_first_map[i] == -1)
+ zabort("Broken fd_first_map -- missing bits") ;
+
+ /* (7) construct fd_byte_count[] -- number of bytes required to */
+ /* include fds 0..fd. */
+
+ i = 0 ;
+ for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
+ {
+ int c = fd_byte_map[fd] + 1 ;
+
+ if (c < i)
+ c = i ; /* use largest so far. => big-endian */
+ else
+ i = c ; /* keep largest so far up to date */
+
+ fd_byte_count[fd] = c ;
+ } ;
+
+#if QPS_TESTING
+
+ /* Checking that the maps have been correctly deduced */
+
+ 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 ) ;
+
+ if (QPS_TEST_B_ORD == 07)
+ b = 0x01 << (fd % 8) ;
+ else
+ b = 0x80 >> (fd % 8) ;
+
+ if (QPS_TEST_BE)
+ c = (iw + 1) * QPS_TEST_WORD ;
+ else
+ c = (ib + 1) ;
+
+ if (fd_word_map[fd] != iw)
+ zabort("Broken fd_word_map") ;
+ if (fd_byte_map[fd] != ib)
+ zabort("Broken fd_byte_map") ;
+ if (fd_bit_map[fd] != b)
+ zabort("Broken fd_bit_map") ;
+ if (fd_byte_count[fd] != c)
+ zabort("Broken fd_byte_count") ;
+ } ;
+
+ for (i = 1 ; i < 256 ; ++i)
+ {
+ uint8_t b = i ;
+ fd = 0 ;
+ if (QPS_TEST_B_ORD == 07)
+ {
+ while ((b & 1) == 0)
+ {
+ b >>= 1 ;
+ ++fd ;
+ } ;
+ }
+ else
+ {
+ while ((b & 0x80) == 0)
+ {
+ b <<= 1 ;
+ ++fd ;
+ } ;
+ } ;
+
+ if (fd_first_map[i] != fd)
+ zabort("Broken fd_first_map") ;
+ } ;
+
+ zabort("OK fd mapping") ;
+#endif
+
+ /* Phew -- we're all set now */
+ qps_super_set_map_made = 1 ;
+} ;
+
+/* 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.
+ *
+ * NB: it is CONFIRMED elsewhere that the fd_set is no longer than the overlays.
+ */
+static void
+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
+ */
+static int
+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.
+ */
+static int
+qps_super_set_count(fd_super_set* p_set, int n)
+{
+ fd_word_t* p ;
+ int count = 0 ;
+
+ n *= FD_SUPER_SET_WORD_SIZE ;
+ confirm(sizeof(fd_super_set) == (FD_SUPER_SET_WORD_SIZE * FD_WORD_BYTES)) ;
+
+ p = (fd_word_t*)p_set ;
+ while (n--)
+ {
+ fd_word_t w = *p++ ;
+ while (w != 0)
+ {
+ ++count ;
+ w &= (w - 1) ;
+ } ;
+ } ;
+
+ return count ;
+} ;
+
+/*==============================================================================
+ * Selection state check -- for debug purposes.
+ *
+ * Runs a check across a given selection and verifies that:
+ *
+ * 1) for !fd_direct that the files are in fd order in the vector
+ * and are unique, and there are no NULL entries.
+ * 2) for fd_direct that the file fd and the index match
+ * and the last entry is not NULL
+ * 3) that all files point at the selection
+ * 4) that the enabled modes in each file are valid
+ * 5) the number of files in the selection matches fd_count.
+ * 6) the highest numbered fd matches fd_last
+ * 7) that the enabled counts in the selection are correct
+ * 8) that the enabled modes in each file match the enabled modes in the
+ * selection
+ * 9) that no extraneous fds are set in the enabled vectors
+ *
+ * If there are no pending fds:
+ *
+ * 10) if there are no pending fds, that the results vectors are empty.
+ *
+ * If there are pending fds:
+ *
+ * 11) that pend_mnum is valid and pend_fd <= tried_fd_last.
+ *
+ * 12) that the tried_count for modes 0..pend_mnum-1 is zero,
+ * and the tried_count for pend_mnum is not.
+ *
+ * 13) that the result vectors for modes where tried count == 0 are empty.
+ *
+ * 14) that the remaining result bits are a subset of the enabled bits.
+ *
+ * 15) that no bits beyond tried_fd_last are set in the result vectors.
+ *
+ * 16) that no bits before pend_fd are set in the pemd_mnum result vector.
+ *
+ * 17) that the number of bits remaining matches pend_count.
+ */
+static void
+qps_selection_validate(qps_selection qps)
+{
+ int fd_last ;
+ int enabled_count[qps_mnum_count] ;
+ fd_full_set enabled ;
+
+ qps_file qf ;
+ int fd, n, mnum, p_mnum ;
+ vector_index i ;
+
+ /* 1..4) Run down the selection vector and check. */
+ /* Collect new enabled_count and enabled bit vectors. */
+
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ enabled_count[mnum] = 0 ;
+ qps_super_set_zero(enabled, qps_mnum_count) ;
+
+ n = 0 ;
+ fd_last = -1 ;
+ for (VECTOR_ITEMS(&qps->files, qf, i))
+ {
+ if (qf != NULL)
+ {
+ ++n ; /* Number of files */
+
+ if (qps->fd_direct)
+ {
+ if (qf->fd != (int)i) /* index and fd must match */
+ zabort("File vector index and fd mismatch") ;
+ }
+ else
+ {
+ if (qf->fd <= fd_last) /* must be unique and in order */
+ zabort("File vector not in order") ;
+ } ;
+
+ fd_last = qf->fd ; /* keep track of last fd */
+
+ if (qf->selection != qps) /* file must refer to selection */
+ zabort("File does not refer to its selection") ;
+
+ if ((qf->enabled_bits < 0) || (qf->enabled_bits > qps_all_mbits))
+ zabort("File enabled bits are invalid") ;
+
+ /* Capture enabled state of all files. */
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if (qf->enabled_bits & qps_mbit(mnum))
+ {
+ ++enabled_count[mnum] ;
+ FD_SET(qf->fd, &enabled[mnum].fdset) ;
+ } ;
+ }
+ else
+ if (!qps->fd_direct)
+ zabort("Found NULL entry in !fd_direct files vector") ;
+ } ;
+
+ if ((n != 0) && (vector_get_last_item(&qps->files) == NULL))
+ zabort("Last entry in file vector is NULL") ;
+
+ /* 5) check that the number of files tallies. */
+ if (n != qps->fd_count)
+ zabort("Number of files in the selection does not tally") ;
+
+ /* 6) check the last fd */
+ if ( ((n == 0) && (qps->fd_last !=0)) || (fd_last != qps->fd_last) )
+ zabort("The last fd does not tally") ;
+
+ /* 7) check that the enabled counts tally. */
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if (enabled_count[mnum] != qps->enabled_count[mnum])
+ zabort("Enabled counts do not tally") ;
+
+ /* 8..9) Check that the enabled vectors are the same as the ones just */
+ /* created by scanning the files. */
+ if (qps_super_set_cmp(enabled, qps->enabled, qps_mnum_count) != 0)
+ zabort("Enabled bit vectors do not tally") ;
+
+ /* 10) if there are no pending fds, check result vectors empty. */
+ if (qps->pend_count == 0)
+ {
+ if (qps_super_set_count(qps->results, qps_mnum_count) != 0)
+ zabort("Nothing pending, but result vectors not empty") ;
+
+ return ;
+ } ;
+
+ /* This is to stop gcc whining about signed/unsigned comparisons. */
+ p_mnum = qps->pend_mnum ;
+
+ /* 11) that pend_mnum is valid and pend_fd <= tried_fd_last. */
+ if ( (p_mnum < 0) || (p_mnum > qps_mnum_count)
+ || (qps->pend_fd < 0)
+ || (qps->pend_fd > qps->tried_fd_last) )
+ zabort("Invalid pend_mnum or pend_fd") ;
+
+ /* 12) check tried_count[] */
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ {
+ if ((mnum < p_mnum) && (qps->tried_count[mnum] != 0))
+ zabort("Non-zero tried_count for mode < pend_mnum") ;
+ if ((mnum == p_mnum) && (qps->tried_count[qps->pend_mnum] <= 0))
+ zabort("Zero tried_count for pend_mnum") ;
+ if ((mnum > p_mnum) && (qps->tried_count[mnum] < 0))
+ zabort("Invalid tried_count for mode > pend_mnum") ;
+ } ;
+
+ /* 13) check result vectors for modes where tried count == 0 */
+ n = (qps_super_set_count(qps->results, qps_mnum_count) != 0) ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if ((qps->tried_count[mnum] == 0)
+ && (qps_super_set_count(&qps->results[mnum], 1) != 0))
+ zabort("Non-empty bit vector where tried count == 0") ;
+
+ /* 14) check remaining results are a subset of the enableds. */
+ /* 15) check no bit beyond tried_fd_last is set in the results. */
+ /* 16) check no bit before pend_fd is set in the pemd_mnum results. */
+ /* 17) check the number of bits remaining matches pend_count. */
+
+ n = 0 ;
+ for (mnum = 0 ; mnum < qps_mnum_count ; ++mnum)
+ if (qps->tried_count[mnum] != 0)
+ {
+ for (fd = 0 ; fd < FD_SETSIZE ; ++fd)
+ if (FD_ISSET(fd, &qps->results[mnum].fdset))
+ {
+ ++n ;
+ if (fd > qps->tried_fd_last)
+ zabort("Found pending fd beyond tried_fd_last") ;
+ if ( ! FD_ISSET(fd, &qps->results[mnum].fdset))
+ zabort("Found pending fd which is not enabled") ;
+ if ((mnum == p_mnum) && (fd < qps->pend_fd))
+ zabort("Found pending fd < current next pending") ;
+ } ;
+ } ;
+
+ if (n != qps->pend_count)
+ zabort("Non-empty bit vector where tried count == 0") ;
+} ;
diff --git a/lib/qpselect.h b/lib/qpselect.h
new file mode 100644
index 00000000..1e67d174
--- /dev/null
+++ b/lib/qpselect.h
@@ -0,0 +1,208 @@
+/* Quagga pselect support -- 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_QPSELECT_H
+#define _ZEBRA_QPSELECT_H
+
+#include <sys/select.h>
+#include <errno.h>
+
+#include "zassert.h"
+#include "qtime.h"
+#include "vector.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Quagga pselect -- qps_xxxx
+ *
+ * Here and in qpselect.c is a data structure for managing multiple file
+ * descriptors and running pselect to wait for I/O activity and to multiplex
+ * between the file descriptors.
+ */
+
+enum qps_mnum /* "mode" numbers: error/read/write */
+{
+ qps_mnum_first = 0,
+
+ qps_error_mnum = 0,
+ qps_read_mnum = 1,
+ qps_write_mnum = 2,
+
+ qps_mnum_count = 3
+} ;
+
+typedef enum qps_mnum qps_mnum_t ;
+
+#define qps_mbit(mnum) (1 << mnum)
+
+enum qps_mbits /* "mode" bits: error/read/write */
+{
+ qps_error_mbit = qps_mbit(qps_error_mnum),
+ qps_read_mbit = qps_mbit(qps_read_mnum),
+ qps_write_mbit = qps_mbit(qps_write_mnum),
+
+ qps_all_mbits = qps_mbit(qps_mnum_count) - 1
+} ;
+
+typedef enum qps_mbits qps_mbit_t ;
+
+/* Forward references */
+typedef struct qps_selection* qps_selection ;
+typedef struct qps_file* qps_file ;
+
+/*==============================================================================
+ * fd_super_set.
+ *
+ * To speed up scanning of large fd_set's this structure overlays a 32-bit
+ * word and a byte array over the (assumed) fd_set bit vector.
+ *
+ * There is no guarantee that FD_SETSIZE is a multiple of 32 (or of 8, for
+ * that matter) -- so some care must be taken.
+ */
+
+typedef uint32_t fd_word_t ;
+
+#define FD_WORD_BITS 32
+#define FD_WORD_BYTES (FD_WORD_BITS / 8)
+
+CONFIRM(FD_WORD_BITS == (FD_WORD_BYTES * 8)) ; /* for completeness */
+
+#define FD_SUPER_SET_WORD_SIZE ((FD_SETSIZE + FD_WORD_BITS - 1) / FD_WORD_BITS)
+#define FD_SUPER_SET_BYTE_SIZE (FD_SUPER_SET_WORD_SIZE * FD_WORD_BYTES)
+
+/* Make sure that the overlay is at least as big as the fd_set ! */
+CONFIRM(FD_SUPER_SET_BYTE_SIZE >= sizeof(fd_set)) ;
+
+typedef union /* see qps_make_super_set_map() */
+{
+ fd_word_t words[FD_SUPER_SET_WORD_SIZE] ;
+ uint8_t bytes[FD_SUPER_SET_BYTE_SIZE] ;
+ fd_set fdset ;
+} fd_super_set ;
+
+/* Make sure that the fd_super_set is an exact number of fd_word_t words */
+CONFIRM(sizeof(fd_super_set) == (FD_SUPER_SET_WORD_SIZE * FD_WORD_BYTES)) ;
+
+/*==============================================================================
+ * Action function.
+ *
+ * Each file has three action functions, to be called in qps_dispatch_next()
+ * when pselect() has reported error/read/write for the file.
+ *
+ * For further discussion, see: qps_file_init.
+ */
+
+typedef void qps_action(qps_file qf, void* file_info) ;
+
+/*==============================================================================
+ * Data Structures.
+ */
+
+typedef fd_super_set fd_full_set[qps_mnum_count] ;
+
+struct qps_selection
+{
+ int fd_count ; /* number of fds we are looking after */
+ int fd_direct ; /* direct lookup in vector or not */
+
+ struct vector files ; /* mapping fd to qps_file */
+
+ int fd_last ; /* highest numbered fd we are looking after */
+ int enabled_count[qps_mnum_count] ; /* no. enabled fds in each mode */
+ fd_full_set enabled ; /* bit vectors for pselect enabled stuff */
+
+ int tried_fd_last ; /* highest numbered fd on last pselect */
+ int tried_count[qps_mnum_count] ; /* enabled_count on last pselect */
+ fd_full_set results ; /* last set of results from pselect */
+
+ int pend_count ; /* results pending (if any) */
+ qps_mnum_t pend_mnum ; /* error/read/write mode pending (if any) */
+ int pend_fd ; /* fd pending (if any) */
+
+ int signum ; /* signal that sigmask is enabling -- 0 => none */
+ sigset_t sigmask ; /* sigmask to use for duration of pselect */
+} ;
+
+struct qps_file
+{
+ qps_selection selection ;
+
+ void* file_info ;
+ int fd ;
+
+ qps_mbit_t enabled_bits ;
+
+ qps_action* actions[qps_mnum_count] ;
+} ;
+
+/*==============================================================================
+ * qps_selection handling
+ */
+
+qps_selection
+qps_selection_init_new(qps_selection qps) ;
+
+void
+qps_add_file(qps_selection qps, qps_file qf, int fd, void* file_info) ;
+
+void
+qps_remove_file(qps_file qf) ;
+
+qps_file
+qps_selection_ream(qps_selection qps, int free_structure) ;
+
+/* Ream out selection and free the selection structure. */
+#define qps_selection_ream_free(qps) qps_selection_ream(qps, 1)
+/* Ream out selection but keep the selection structure. */
+#define qps_selection_ream_keep(qps) qps_selection_ream(qps, 0)
+
+void
+qps_set_signal(qps_selection qps, int signum, sigset_t sigmask) ;
+
+int
+qps_pselect(qps_selection qps, qtime_mono_t timeout) ;
+
+int
+qps_dispatch_next(qps_selection qps) ;
+
+/*==============================================================================
+ * qps_file structure handling
+ */
+
+qps_file
+qps_file_init_new(qps_file qf, qps_file template) ;
+
+void
+qps_file_free(qps_file qf) ;
+
+void
+qps_enable_mode(qps_file qf, qps_mnum_t mnum, qps_action* action) ;
+
+void
+qps_set_action(qps_file qf, qps_mnum_t mnum, qps_action* action) ;
+
+void
+qps_disable_modes(qps_file qf, qps_mbit_t mbits) ;
+
+#endif /* _ZEBRA_QPSELECT_H */
diff --git a/lib/qpthreads.c b/lib/qpthreads.c
new file mode 100644
index 00000000..e7a8da2f
--- /dev/null
+++ b/lib/qpthreads.c
@@ -0,0 +1,713 @@
+/* Quagga Pthreads support -- 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.
+ */
+
+/* This MUST come first... otherwise we don't get __USE_UNIX98, which is */
+/* essential if glibc is to allow pthread_mutexattr_settype() to be used. */
+#include "config.h"
+
+#include <signal.h>
+
+#include "qpthreads.h"
+#include "memory.h"
+
+/* If this is not set, will get errors later. */
+#ifndef __USE_UNIX98
+#error "_USE_UNIX98 not defined"
+#endif
+
+/*==============================================================================
+ * Quagga Pthread Interface -- qpt_xxxx
+ *
+ * Here (and in qpthreads.h) are captured all the pthreads features used in
+ * Quagga.
+ *
+ * This provides:
+ *
+ * * "wrappers" around functions which should not fail, but whose return
+ * code it is best to check... at least in a debug environment.
+ *
+ * * the possibility of a separate no pthreads build where pthread facilities
+ * are either dummied out or otherwise dealt with.
+ *
+ * * the ability to add any work-arounds which may be required if poorly
+ * conforming pthreads implementations are encountered
+ *
+ * Continued Working Without Pthreads
+ * ==================================
+ *
+ * A big Global Switch -- qpthreads_enabled -- is used to control whether the
+ * system is pthreaded or not.
+ *
+ * If this is never set, then the system runs without pthreads, and all the
+ * mutex and condition variable functions are NOPs. This allows, for example,
+ * mutex operations to be placed where they are needed for thread-safety,
+ * without affecting the code when running without pthreads.
+ *
+ * Before the first thread is created and before any mutexes or condition
+ * variables are initialised, the qpthreads_enabled MUST be set. And it MUST
+ * not be changed again !
+ *
+ * Pthread Requirements
+ * ====================
+ *
+ * This is assuming support for 1003.1-2004 -- XOPEN Issue 6, with [THR] and
+ * [XSI] options.
+ *
+ * The [XSI] is required for pthread_mutexattr_settype(), only.
+ *
+ * If qpt_thread_attr_init() uses:
+ *
+ * pthread_attr_getinheritsched()/_setinheritshed() [TPS]
+ * pthread_attr_getscope()/_setscope() [TPS]
+ * pthread_attr_getschedpolicy()/_setschedpolicy() [TPS]
+ * pthread_attr_getschedparam()/_setschedparam() [THR]
+ *
+ * but they are only required if explicit scheduling attributes are being set.
+ * (So, could be dropped where not supported.)
+ *
+ * Amongst the things which are NOT required:
+ *
+ * pthread_attr_getguardsize()/_setguardsize() [XSI]
+ * pthread_attr_getstack()/_setstack() [TSA TSS]
+ * pthread_attr_getstackaddr()/_setstackaddr() [TSA OB]
+ * pthread_attr_getstacksize()/_setstacksize() [TSA TSS]
+ *
+ * pthread_barrier_xxx() [BAR]
+ *
+ * pthread_condattr_getpshared()/_setpshared() [TSH]
+ *
+ * pthread_mutex_getprioceiling()/_setprioceiling() [TPP]
+ * pthread_mutex_timedlock() [TMO] pro tem
+ * pthread_mutexattr_getprioceiling()/_setprioceiling() [TPP]
+ * pthread_mutexattr_getprotocol()/_setprotocol() [TPP TPI]
+ * pthread_mutexattr_getpshared()/_setpshared() [TSH]
+ *
+ * pthread_rwlock_xxx() [THR] pro tem
+ * pthread_rwlockattr_init()/_destroy() [THR] pro tem
+ * pthread_rwlockattr_getpshared()/_setpshared() [TSH]
+ *
+ * pthread_spin_xxx() [SPI]
+ *
+ * [CS] (Clock Select) is assumed if HAVE_CLOCK_MONOTONIC.
+ *
+ * In 1003.1-2008, XOPEN issue 7, [THR] and pthread_mutexattr_settype() have
+ * been moved to Base.
+ *
+ * NB: it is essential that pthread_kill() delivers the signal to the target
+ * thread only -- ie, it must be POSIX compliant. That rules out the old
+ * (2.4) LinuxThreads. For Linux, 2.6 (or greater) is required, with
+ * NPTL (these days generally included in glibc).
+ *
+ * NB: for glibc to give all the required features, either _GNU_SOURCE or
+ * _XOPEN_SOURCE must be set *before* the first #include <features.h>.
+ * _XOPEN_SOURCE=600 is sufficient.
+ *
+ * Pthread Thread Attributes -- Scheduling
+ * =======================================
+ *
+ * Pthreads defines some useful looking real-time scheduling features.
+ *
+ * One would like to be able to give I/O intensive threads an advantage over
+ * CPU bound threads.
+ *
+ * Unfortunately, conformance allows a system to have its own scheduling
+ * system -- so long as the standard ones are implemented. Further, there is
+ * no way of telling what priority values are reasonable, even in the standard
+ * scheduling policies.
+ *
+ * The approach taken here is that by default a thread will be created with
+ * the system default attributes -- which may mean inheriting the creating
+ * thread's scheduling attributes.
+ *
+ * It is also possible to construct a set of attributes, using the most
+ * obviously useful properties. It is envisaged that this may be used when a
+ * configuration file is used to set locally sensible values. The attributes
+ * supported are:
+ *
+ * * attr_detached -- whether to start detached or not
+ * * attr_inherit_sched -- whether to inherit scheduling attributes
+ * * attr_sched_scope -- scheduling scope
+ * * attr_sched_policy -- scheduling policy
+ * * attr_sched_priority -- scheduling priority
+ *
+ * See qpt_thread_attr_init, below.
+ *
+ * Not supported here are:
+ *
+ * * attr_guardsize
+ * * attr_stack
+ * * attr_stacksize
+ *
+ * Pthread Mutex Attributes -- Error Checking
+ * ==========================================
+ *
+ * Mutexes are kept simple, only attr_type is used, and that by default.
+ *
+ * POSIX defines four types of mutex:
+ *
+ * _NORMAL no ownership check -- owner will deadlock if locks mutex !
+ * -- undefined what happens if unlock
+ * mutex not owned by self !
+ * no recursive locking
+ *
+ * _ERRORCHECK checks for ownership on lock and unlock
+ * no recursive locking
+ *
+ * _RECURSIVE checks for ownership on lock and unlock
+ * counts up locks and counts down unlocks
+ *
+ * This looks useful, but goes wrong with condition variables !
+ *
+ * _DEFAULT undefined whether checks owner or not, on lock and/or unlock.
+ * no recursive locking
+ *
+ * See qpthreads.h for discussion of Quagga's standard type (QPT_MUTEX_TYPE).
+ *
+ * Other attributes are left in their default state:
+ *
+ * * attr_prioceiling -- default undefined
+ * * attr_protocol -- default undefined
+ * * attr_pshared -- defaults to _PROCESS_PRIVATE
+ *
+ * For the time being it is assumed that these are too exotic.
+ *
+ * Pthread Condition Variable Attributes
+ * =====================================
+ *
+ * Condition variables have only two attributes:
+ *
+ * * attr_clock -- which clock to use
+ * * attr_pshared -- defaults to _PROCESS_PRIVATE
+ *
+ * The use a clock other than Quagga's standard (QPT_COND_CLOCK_ID) is possible,
+ * but not recommended. (See qpthreads.h for discussion of this.)
+ *
+ * Pthread Specific Signal Handling
+ * ================================
+ *
+ * In a threaded application, need to use pthread_sigmask (not sigproc_mask).
+ * (Can use pthread_sigmask in a single threaded application.)
+ *
+ * To direct a signal at a given thread need pthread_kill. *
+ */
+
+/*==============================================================================
+ * The Global Switch
+ *
+ * The state of the switch is: unset -- implicitly not enabled
+ * set_frozen -- implicitly not enabled & frozen
+ * set_disabled -- explicitly not enabled
+ * set_enabled -- explicitly set enabled
+ *
+ * "set_frozen" means that "qpthreads_freeze_enabled_state()" has been called,
+ * and the state was unset at the time. This means that some initialisation
+ * has been done on the basis of !qpthreads_enabled, and it is TOO LATE to
+ * enable qpthreads afterwards.
+ */
+
+enum qpthreads_enabled_state
+{
+ qpt_state_unset = 0,
+ qpt_state_set_frozen = 1,
+ qpt_state_set_disabled = 2,
+ qpt_state_set_enabled = 3,
+} ;
+
+static enum qpthreads_enabled_state qpthreads_enabled_state = qpt_state_unset ;
+
+int qpthreads_enabled_flag = 0 ;
+
+/* Function to set qpthreads_enabled, one way or the other.
+ *
+ * NB: can repeatedly set to the same state, but not change state once set.
+ */
+void
+qpt_set_qpthreads_enabled(int how)
+{
+
+ switch (qpthreads_enabled_state)
+ {
+ case qpt_state_unset:
+ break ;
+ case qpt_state_set_frozen:
+ if (how != 0)
+ zabort("Too late to enable qpthreads") ;
+ break ;
+ case qpt_state_set_disabled:
+ if (how != 0)
+ zabort("qpthreads_enabled is already set: cannot set enabled") ;
+ break ;
+ case qpt_state_set_enabled:
+ if (how == 0)
+ zabort("qpthreads_enabled is already set: cannot set disabled") ;
+ break ;
+ default:
+ break ;
+ }
+
+ qpthreads_enabled_flag = (how != 0) ;
+ qpthreads_enabled_state = (how != 0) ? qpt_state_set_enabled
+ : qpt_state_set_disabled ;
+} ;
+
+/* Get state of qpthreads_enabled, and freeze if not yet explictly set.
+ *
+ * Where some initialisation depends on the state of qpthreads_enabled(), this
+ * returns the state and freezes it if it is implicitly not enabled.
+ */
+extern int
+qpt_freeze_qpthreads_enabled(void)
+{
+ if (qpthreads_enabled_state == qpt_state_unset)
+ qpthreads_enabled_state = qpt_state_set_frozen ;
+
+ return qpthreads_enabled_flag ;
+} ;
+
+/*==============================================================================
+ * Thread creation and attributes.
+ *
+ * Threads may be created with a given set of attributes if required.
+ *
+ * qpt_thread_attr_init() will initialise a set of attributes including the
+ * current standard scheduling attributes. It is envisaged that configuration
+ * options may be used to specify these.
+ *
+ * qpt_thread_create() creates a thread using the given attributes. If those
+ * are NULL, then the system defaults are used.
+ */
+
+/* Initialise a set of attributes -- setting the scheduling options.
+ *
+ * Options:
+ *
+ * qpt_attr_joinable -- the default if nothing specified.
+ * qpt_attr_detached -- overrides qpt_attr_joinable.
+ *
+ * qpt_attr_sched_inherit -- all scheduling attributes are to be inherited.
+ * No explicit scheduling attributes may be set.
+ *
+ * qpt_attr_sched_scope -- set explicit, given, scope.
+ * qpt_attr_sched_policy -- set explicit, given, policy
+ * qpt_attr_sched_priority -- set explicit, given, priority
+ *
+ * If none of the _sched_ options are given, then the scheduling attributes are
+ * left to whatever default values the system chooses.
+ *
+ * If the _sched_inherit option is specified, none of the other _sched_ options
+ * may be specified.
+ *
+ * If any of the explicit scheduling options are given, they are set in this
+ * order. If only some of these options are given, then the caller is
+ * assuming that the system will choose sensible defaults.
+ *
+ * The scope, policy and priority arguments are use only if the corresponding
+ * option is specified.
+ *
+ * NB: FATAL error to attempt this is !qptthreads_enabled.
+ *
+ * Returns the address of the qpt_thread_attr_t structure.
+ */
+qpt_thread_attr_t*
+qpt_thread_attr_init(qpt_thread_attr_t* attr, enum qpt_attr_options opts,
+ int scope, int policy, int priority)
+{
+ int err ;
+
+ assert((opts & ~qpt_attr_known) == 0) ;
+ passert(qpthreads_enabled) ;
+
+ /* Initialise thread attributes structure (allocating if required.) */
+ if (attr == NULL)
+ attr = XMALLOC(MTYPE_QPT_THREAD_ATTR, sizeof(qpt_thread_attr_t)) ;
+
+ err = pthread_attr_init(attr) ;
+ if (err != 0)
+ zabort_err("pthread_attr_init failed", err) ;
+
+ /* If not qpt_attr_detached, then set joinable. */
+ err = pthread_attr_setdetachstate(attr,
+ (opts & qpt_attr_detached) ? PTHREAD_CREATE_DETACHED
+ : PTHREAD_CREATE_JOINABLE) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setdetachstate failed", err) ;
+
+ /* If setting anything to do with scheduling... */
+ if (opts & qpt_attr_sched_setting)
+ {
+ /* Either we inherit or we set explicit parameters. */
+
+ err = pthread_attr_setinheritsched(attr,
+ (opts & qpt_attr_sched_inherit) ? PTHREAD_INHERIT_SCHED
+ : PTHREAD_EXPLICIT_SCHED) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setinheritsched", err) ;
+
+ if (opts & qpt_attr_sched_inherit)
+ assert((opts & qpt_attr_sched_explicit) == 0) ;
+ else
+ {
+ if (opts & qpt_attr_sched_scope)
+ {
+ err = pthread_attr_setscope(attr, scope) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setscope failed", err) ;
+ } ;
+ if (opts & qpt_attr_sched_policy)
+ {
+ err = pthread_attr_setschedpolicy(attr, scope) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setschedpolicy failed", err) ;
+ } ;
+ if (opts & qpt_attr_sched_priority)
+ {
+ struct sched_param sparm ;
+ err = pthread_attr_getschedparam(attr, &sparm) ;
+ if (err != 0)
+ zabort_err("pthread_attr_getschedparam failed", err) ;
+ sparm.sched_priority = priority ;
+ err = pthread_attr_setschedparam(attr, &sparm) ;
+ if (err != 0)
+ zabort_err("pthread_attr_setschedparam failed", err) ;
+ } ;
+ } ;
+ } ;
+
+ /* Done -- return qpt_thread_attr_t* */
+ return attr ;
+} ;
+
+/* Create Thread with given attributes (if any).
+ *
+ * If no attributes are given (attr == NULL) the thread is created with system
+ * default attributes -- *except* that it is created joinable.
+ *
+ * NB: FATAL error to attempt this is !qptthreads_enabled.
+ *
+ * Returns the qpt_thread_t "thread id".
+ */
+qpt_thread_t
+qpt_thread_create(void* (*start)(void*), void* arg, qpt_thread_attr_t* attr)
+{
+ qpt_thread_attr_t thread_attr ;
+ qpt_thread_t thread_id ;
+ int default_attr ;
+ int err ;
+
+ passert(qpthreads_enabled) ;
+
+ default_attr = (attr == NULL) ;
+ if (default_attr)
+ attr = qpt_thread_attr_init(&thread_attr, qpt_attr_joinable, 0, 0, 0) ;
+
+ err = pthread_create(&thread_id, attr, start, arg) ;
+ if (err != 0)
+ zabort_err("pthread_create failed", err) ;
+
+ if (default_attr)
+ {
+ err = pthread_attr_destroy(attr) ; /* being tidy */
+ if (err != 0)
+ zabort_err("pthread_attr_destroy failed", err) ;
+ } ;
+
+ return thread_id ;
+} ;
+
+/*==============================================================================
+ * Mutex initialise and destroy.
+ */
+
+/* Initialise Mutex (allocating if required)
+ *
+ * Does nothing if !qpthreads_enabled -- but freezes the state (attempting to
+ * later enable qpthreads will be a FATAL error).
+ *
+ * Options:
+ *
+ * qpt_mutex_quagga -- see qpthreads.h for discussion of this.
+ * qpt_mutex_normal -- ie PTHREAD_MUTEX_NORMAL
+ * qpt_mutex_recursive -- ie PTHREAD_MUTEX_RECURSIVE
+ * qpt_mutex_errorcheck -- ie PTHREAD_MUTEX_ERRORCHECK
+ * qpt_mutex_default -- system default
+ *
+ * Of these _recursive is the most likely alternative to _quagga... BUT do
+ * remember that such mutexes DO NOT play well with condition variables.
+ *
+ * Returns the mutex -- or original mx if !qpthreads_enabled.
+ */
+qpt_mutex
+qpt_mutex_init_new(qpt_mutex mx, enum qpt_mutex_options opts)
+{
+ pthread_mutexattr_t mutex_attr ;
+ int type ;
+ int err ;
+
+ if (!qpthreads_enabled_freeze)
+ return mx ;
+
+ if (mx == NULL)
+ mx = XMALLOC(MTYPE_QPT_MUTEX, sizeof(qpt_mutex_t)) ;
+
+ /* Set up attributes so we can set the mutex type */
+ err = pthread_mutexattr_init(&mutex_attr);
+ if (err != 0)
+ zabort_err("pthread_mutexattr_init failed", err) ;
+
+ switch(opts)
+ {
+ case qpt_mutex_quagga:
+ type = QPT_MUTEX_TYPE ;
+ break ;
+ case qpt_mutex_normal:
+ type = PTHREAD_MUTEX_NORMAL ;
+ break ;
+ case qpt_mutex_recursive:
+ type = PTHREAD_MUTEX_RECURSIVE ;
+ break ;
+ case qpt_mutex_errorcheck:
+ type = PTHREAD_MUTEX_ERRORCHECK ;
+ break ;
+ case qpt_mutex_default:
+ type = PTHREAD_MUTEX_DEFAULT ;
+ break ;
+ default:
+ zabort("Invalid qpt_mutex option") ;
+ } ;
+
+ err = pthread_mutexattr_settype(&mutex_attr, type);
+ if (err != 0)
+ zabort_err("pthread_mutexattr_settype failed", err) ;
+
+ /* Now we're ready to initialize the mutex itself */
+ err = pthread_mutex_init(mx, &mutex_attr) ;
+ if (err != 0)
+ zabort_err("pthread_mutex_init failed", err) ;
+
+ /* Be tidy with the attributes */
+ err = pthread_mutexattr_destroy(&mutex_attr) ;
+ if (err != 0)
+ zabort_err("pthread_mutexattr_destroy failed", err) ;
+
+ /* Done: return the mutex */
+ return mx ;
+} ;
+
+/* Destroy given mutex, and (if required) free it.
+ * -- or do nothing if !qpthreads_enabled.
+ *
+ * Returns NULL if freed the mutex, otherwise the address of same.
+ *
+ * NB: if !qpthreads_enabled qpt_mutex_init_new() will not have allocated
+ * anything, so there can be nothing to release -- so does nothing, but
+ * returns the original mutex address (if any).
+ */
+qpt_mutex
+qpt_mutex_destroy(qpt_mutex mx, int free_mutex)
+{
+ int err ;
+
+ if (qpthreads_enabled)
+ {
+ err = pthread_mutex_destroy(mx) ;
+ if (err != 0)
+ zabort_err("pthread_mutex_destroy failed", err) ;
+
+ if (free_mutex)
+ XFREE(MTYPE_QPT_MUTEX, mx) ; /* sets mx == NULL */
+ } ;
+
+ return mx ;
+} ;
+
+/*==============================================================================
+ * Condition Variable initialise and destroy.
+ */
+
+/* Initialise Condition Variable (allocating if required).
+ *
+ * Does nothing if !qpthreads_enabled -- but freezes the state (attempting to
+ * later enable qpthreads will be a FATAL error).
+ *
+ * Options:
+ *
+ * qpt_cond_quagga -- use Quagga's default clock
+ * qpt_cond_realtime -- force CLOCK_REALTIME
+ * qpt_cond_monotonic -- force CLOCK_MONOTONIC (if available)
+ *
+ * NB: FATAL error to attempt this is !qptthreads_enabled.
+ *
+ * Returns the condition variable -- or original cv id !qpthreads_enabled.
+ */
+qpt_cond
+qpt_cond_init_new(qpt_cond cv, enum qpt_cond_options opts)
+{
+ pthread_condattr_t cond_attr ;
+ int err ;
+
+ if (!qpthreads_enabled_freeze)
+ return cv ;
+
+ if (cv == NULL)
+ cv = XMALLOC(MTYPE_QPT_COND, sizeof(qpt_cond_t)) ;
+
+ /* Set up attributes so we can set the type */
+ err = pthread_condattr_init(&cond_attr);
+ if (err != 0)
+ zabort_err("pthread_condattr_init failed", err) ;
+
+ switch(opts)
+ {
+ case qpt_cond_quagga:
+ break ;
+ default:
+ zabort("Invalid qpt_cond option") ;
+ } ;
+
+ err = pthread_condattr_setclock(&cond_attr, QPT_COND_CLOCK_ID);
+ if (err != 0)
+ zabort_err("pthread_condattr_setclock failed", err) ;
+
+ /* Now we're ready to initialize the condition variable itself */
+ err = pthread_cond_init(cv, &cond_attr) ;
+ if (err != 0)
+ zabort_err("pthread_cond_init failed", err) ;
+
+ /* Be tidy with the attributes */
+ err = pthread_condattr_destroy(&cond_attr) ;
+ if (err != 0)
+ zabort_err("pthread_condattr_destroy failed", err) ;
+
+ /* Done: return the condition variable */
+ return cv ;
+} ;
+
+/* Destroy given condition variable, and (if required) free it
+ * -- or do nothing if !qpthreads_enabled.
+ *
+ * NB: if !qpthreads_enabled qpt_cond_init_new() will not have allocated
+ * anything, so there can be nothing to release -- so does nothing, but
+ * returns the original condition variable address (if any).
+ *
+ * Returns NULL if freed the condition variable, otherwise the address of same.
+ */
+qpt_cond
+qpt_cond_destroy(qpt_cond cv, int free_cond)
+{
+ int err ;
+
+ if (qpthreads_enabled)
+ {
+ err = pthread_cond_destroy(cv) ;
+ if (err != 0)
+ zabort_err("pthread_cond_destroy failed", err) ;
+
+ if (free_cond)
+ XFREE(MTYPE_QPT_COND, cv) ; /* sets cv == NULL */
+ } ;
+
+ return cv ;
+} ;
+
+/* Wait for given condition variable or time-out
+ * -- or return immediate success if !qpthreads_enabled.
+ *
+ * Returns: wait succeeded (1 => success, 0 => timed-out).
+ *
+ * NB: timeout time is a qtime_mono_t (monotonic time).
+ *
+ * Has to check the return value, so zabort_errno if not EBUSY.
+ */
+
+int
+qpt_cond_timedwait(qpt_cond cv, qpt_mutex mx, qtime_mono_t timeout_time)
+{
+ struct timespec ts ;
+ int err ;
+
+ if (qpthreads_enabled)
+ {
+ if (QPT_COND_CLOCK_ID != CLOCK_MONOTONIC)
+ {
+ timeout_time = qt_clock_gettime(QPT_COND_CLOCK_ID)
+ + (timeout_time - qt_get_monotonic()) ;
+ } ;
+
+ err = pthread_cond_timedwait(cv, mx, qtime2timespec(&ts, timeout_time)) ;
+ if (err == 0)
+ return 1 ; /* got condition */
+ if (err == ETIMEDOUT)
+ return 0 ; /* got time-out */
+
+ zabort_err("pthread_cond_timedwait failed", err) ;
+ }
+ else
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * Signal Handling.
+ */
+
+/* Set thread signal mask -- requires qpthreads_enabled.
+ *
+ * Thin wrapper around pthread_sigmask.
+ *
+ * zaborts if gets any error.
+ *
+ * NB: it is a FATAL error to do this if !qpthreads_enabled.
+ *
+ * This is mostly because wish to avoid all pthreads_xxx calls when not
+ * using pthreads. There is no reason not to use this in a single threaded
+ * program.
+ */
+void
+qpt_thread_sigmask(int how, const sigset_t* set, sigset_t* oset)
+{
+ int err ;
+
+ passert(qpthreads_enabled) ;
+
+ if (oset != NULL)
+ sigemptyset(oset) ; /* to make absolutely sure */
+
+ err = pthread_sigmask(how, set, oset) ;
+ if (err != 0)
+ zabort_err("pthread_sigmask failed", err) ;
+} ;
+
+/* Send given thread the given signal -- requires qpthreads_enabled (!)
+ *
+ * Thin wrapper around pthread_kill.
+ *
+ * zaborts if gets any error.
+ */
+void
+qpt_thread_signal(qpt_thread_t thread, int signum)
+{
+ int err ;
+
+ passert(qpthreads_enabled) ;
+
+ err = pthread_kill(thread, signum) ;
+ if (err != 0)
+ zabort_err("pthread_kill failed", err) ;
+} ;
diff --git a/lib/qpthreads.h b/lib/qpthreads.h
new file mode 100644
index 00000000..49a7c1e3
--- /dev/null
+++ b/lib/qpthreads.h
@@ -0,0 +1,417 @@
+/* Quagga Pthreads support -- 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_QPTHREADS_H
+#define _ZEBRA_QPTHREADS_H
+
+#include <stdint.h>
+#include <time.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "zassert.h"
+#include "qtime.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+#ifndef private
+#define private extern
+#endif
+
+/*==============================================================================
+ * Quagga Pthread Interface -- qpt_xxxx
+ *
+ * Here are captured all the pthreads features used in Quagga.
+ *
+ * This provides:
+ *
+ * * "wrappers" around functions which should not fail, but whose return
+ * code it is best to check... at least in a debug environment.
+ *
+ * * the possibility of a separate no pthreads build where pthread facilities
+ * are either dummied out or otherwise dealt with.
+ *
+ * * the ability to add any work-arounds which may be required if poorly
+ * conforming pthreads implementations are encountered
+ */
+
+#if !defined(_POSIX_THREADS) || (_POSIX_THREADS <= 0)
+#error Require _POSIX_THREADS
+#endif
+
+/*==============================================================================
+ * Global Switch -- this allows the library to be run WITHOUT pthreads !
+ *
+ * Nearly every qpthreads function is a NOP if !qpthreads_enabled.
+ *
+ * Early in the morning a decision may be made to enable qpthreads -- that must
+ * be done before any threads are created (or will zabort) and before any
+ * mutexes and condition variables are initialised.
+ *
+ * Use: qpthreads_enabled -- to test for the enabled-ness
+ * qpthreads_enabled_freeze -- to test and freeze unset if not yet enabled
+ */
+
+#define qpthreads_enabled ((const int)qpthreads_enabled_flag)
+#define qpthreads_enabled_freeze qpt_freeze_qpthreads_enabled()
+
+/*==============================================================================
+ * Data types
+ */
+
+typedef pthread_t qpt_thread_t ;
+typedef pthread_mutex_t qpt_mutex_t ;
+typedef pthread_cond_t qpt_cond_t ;
+
+typedef pthread_attr_t qpt_thread_attr_t ;
+
+typedef qpt_mutex_t* qpt_mutex ;
+typedef qpt_cond_t* qpt_cond ;
+
+/*==============================================================================
+ * Thread Creation -- see qpthreads.c for further discussion.
+ *
+ * NB: it is a FATAL error to attempt these if !qpthreads_enabled.
+ */
+
+enum qpt_attr_options
+{
+ qpt_attr_joinable = 0, /* the default for Quagga */
+
+ qpt_attr_detached = 0x0001, /* otherwise will set joinable */
+
+ qpt_attr_sched_inherit = 0x0002, /* otherwise will use default */
+
+ qpt_attr_sched_scope = 0x0004, /* otherwise inherit/default */
+ qpt_attr_sched_policy = 0x0008, /* otherwise inherit/default */
+ qpt_attr_sched_priority = 0x0010, /* otherwise inherit/default */
+} ;
+
+#define qpt_attr_sched_explicit ( qpt_attr_sched_scope \
+ | qpt_attr_sched_policy \
+ | qpt_attr_sched_priority )
+
+#define qpt_attr_sched_setting ( qpt_attr_sched_inherit \
+ | qpt_attr_sched_explicit )
+
+#define qpt_attr_known ( qpt_attr_detached | qpt_attr_sched_setting )
+
+extern qpt_thread_attr_t* /* FATAL error if !qpthreads_enabled */
+qpt_thread_attr_init(qpt_thread_attr_t* attr, enum qpt_attr_options opts,
+ int scope, int policy, int priority) ;
+extern qpt_thread_t /* FATAL error if !qpthreads_enabled */
+qpt_thread_create(void* (*start)(void*), void* arg, qpt_thread_attr_t* attr) ;
+
+/*==============================================================================
+ * qpthreads_enabled support -- NOT FOR PUBLIC CONSUMPTION !
+ */
+private int qpthreads_enabled_flag ; /* DO NOT WRITE TO THIS PLEASE */
+
+private void
+qpt_set_qpthreads_enabled(int how) ; /* qpthreads_enabled := how */
+
+private int
+qpt_freeze_qpthreads_enabled(void) ; /* get and freeze qpthreads_enabled */
+
+/*==============================================================================
+ * Thread self knowledge -- returns 'NULL' if !qpthreads_enabled
+ */
+
+Inline qpt_thread_t qpt_thread_self(void)
+{
+ return qpthreads_enabled ? pthread_self() : (qpt_thread_t)NULL;
+} ;
+
+/*==============================================================================
+ * Mutex handling.
+ *
+ * Quagga's default mutex type is:
+ *
+ * * PTHREAD_MUTEX_ERRORCHECK unless NDEBUG && NDEBUG_QPTHREADS
+ * * QPT_MUTEX_TYPE_DEFAULT
+ *
+ * QPT_MUTEX_TYPE_DEFAULT may be set elsewhere. If it is not set then it is
+ * set here to be PTHREAD_MUTEX_NORMAL.
+ *
+ * NB: on the face of it PTHREAD_MUTEX_NORMAL should be the fastest. It is
+ * possible that PTHREAD_MUTEX_DEFAULT may have system specific semantics
+ * that make it faster than the standard _NORMAL. It is also possible that
+ * a given system may elect to provide a safer mutex than the _NORMAL by
+ * default.
+ *
+ * If _DEFAULT is faster than _NORMAL, then QPT_MUTEX_TYPE_DEFAULT may be
+ * used to override this choice.
+ *
+ * NB: if NOT qpthreads_enabled, all mutex actions are EMPTY. This allows
+ * code to be made thread-safe for when pthreads is running, but to work
+ * perfectly well without pthreads.
+ *
+ * NB: do not (currently) support pthread_mutex_timedlock().
+ */
+
+enum qpt_mutex_options
+{
+ qpt_mutex_quagga = 0x0000, /* Quagga's default */
+ qpt_mutex_normal = 0x0001,
+ qpt_mutex_recursive = 0x0002,
+ qpt_mutex_errorcheck = 0x0003,
+ qpt_mutex_default = 0x0004, /* system default */
+} ;
+
+#ifndef QPT_MUTEX_TYPE_DEFAULT
+# define QPT_MUTEX_TYPE_DEFAULT PTHREAD_MUTEX_NORMAL
+#endif
+
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+# define QPT_MUTEX_TYPE QPT_MUTEX_TYPE_DEFAULT
+#else
+# define QPT_MUTEX_TYPE PTHREAD_MUTEX_ERRORCHECK
+#endif
+
+extern qpt_mutex /* freezes qpthreads_enabled */
+qpt_mutex_init_new(qpt_mutex mx, enum qpt_mutex_options opts) ;
+
+#define qpt_mutex_init qpt_mutex_init_new
+
+extern qpt_mutex /* do nothing if !qpthreads_enabled */
+qpt_mutex_destroy(qpt_mutex mx, int free_mutex) ;
+
+#define qpt_mutex_destroy_keep(mx) qpt_mutex_destroy(mx, 0)
+#define qpt_mutex_destroy_free(mx) qpt_mutex_destroy(mx, 1)
+
+Inline void
+qpt_mutex_lock(qpt_mutex mx) ; /* do nothing if !qpthreads_enabled */
+
+Inline int
+qpt_mutex_trylock(qpt_mutex mx) ; /* always succeeds if !qpthreads_enabled */
+
+Inline void
+qpt_mutex_unlock(qpt_mutex mx) ; /* do nothing if !qpthreads_enabled */
+
+/*==============================================================================
+ * Condition Variable handling
+ *
+ * Quagga's clock for condition variables is QPT_COND_CLOCK_ID, which
+ * may be set elsewhere. If it is not set then it is set here to be:
+ *
+ * * CLOCK_MONOTONIC if available
+ * * CLOCK_REALTIME otherwise -- this is the standard default.
+ *
+ * QPT_COND_CLOCK_MONOTONIC is set if CLOCK_MONOTONIC is used (and must be set
+ * if QPT_COND_CLOCK_ID is set elsewhere to something that is monotonic).
+ *
+ * NB: the time-out time passed to qpt_cond_timedwait() is a qtime_mono_t
+ * time (so based on qtime's monotonic time, which is CLOCK_MONOTONIC if
+ * that is available.
+ *
+ * Otherwise, there is a conversion step from qtime_mono_t to whatever the
+ * timebase for the condition variable is.
+ *
+ * NB: static initialisation of condition variables is not supported, to avoid
+ * confusion between the standard default and Quagga's default.
+
+ * NB: if NOT qpthreads_enabled, all condition actions are EMPTY. This allows
+ * code to be made thread-safe for when pthreads is running, but to work
+ * perfectly well without pthreads.
+ */
+
+#ifndef QPT_COND_CLOCK_ID
+# ifdef HAVE_CLOCK_MONOTONIC
+# define QPT_COND_CLOCK_ID CLOCK_MONOTONIC
+# define QPT_COND_CLOCK_MONOTONIC 1
+# else
+# define QPT_COND_CLOCK_ID CLOCK_REALTIME
+# undef QPT_COND_CLOCK_MONOTONIC
+# endif
+#endif
+
+enum qpt_cond_options
+{
+ qpt_cond_quagga = 0x0000, /* Quagga's default */
+} ;
+
+extern qpt_cond /* freezes qpthreads_enabled */
+qpt_cond_init_new(qpt_cond cv, enum qpt_cond_options opts) ;
+
+extern qpt_cond /* do nothing if !qpthreads_enabled */
+qpt_cond_destroy(qpt_cond cv, int free_cond) ;
+
+#define qpt_cond_destroy_keep(cv) qpt_cond_destroy(cv, 0)
+#define qpt_cond_destroy_free(cv) qpt_cond_destroy(cv, 1)
+
+Inline void /* do nothing if !qpthreads_enabled */
+qpt_cond_wait(qpt_cond cv, qpt_mutex mx) ;
+
+extern int /* returns !qpthreads_enabled */
+qpt_cond_timedwait(qpt_cond cv, qpt_mutex mx, qtime_mono_t timeout_time) ;
+
+Inline void /* do nothing if !qpthreads_enabled */
+qpt_cond_signal(qpt_cond cv) ;
+
+Inline void /* do nothing if !qpthreads_enabled */
+qpt_cond_broadcast(qpt_cond cv) ;
+
+/*==============================================================================
+ * Mutex inline functions
+ */
+
+/* Lock given mutex -- or do nothing if !qpthreads_enabled.
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+
+Inline void
+qpt_mutex_lock(qpt_mutex mx)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_mutex_lock(mx) ;
+#else
+ int err = pthread_mutex_lock(mx) ;
+ if (err != 0)
+ zabort_err("pthread_mutex_lock failed", err) ;
+#endif
+ } ;
+} ;
+
+/* Try to lock given mutex -- every time a winner if !qpthreads_enabled.
+ *
+ * Returns: lock succeeded (1 => have locked, 0 => unable to lock).
+ *
+ * Has to check the return value, so zabort_errno if not EBUSY.
+ */
+
+Inline int
+qpt_mutex_trylock(qpt_mutex mx)
+{
+ if (qpthreads_enabled)
+ {
+ int err = pthread_mutex_trylock(mx) ;
+ if (err == 0)
+ return 1 ; /* success: it's locked. */
+ if (err == EBUSY)
+ return 0 ; /* unable to lock */
+
+ zabort_err("pthread_mutex_trylock failed", err) ;
+ }
+ else
+ return 1 ;
+} ;
+
+/* Unlock given mutex -- or do nothing if !qpthreads_enabled.
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+Inline void
+qpt_mutex_unlock(qpt_mutex mx)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_mutex_unlock(mx) ;
+#else
+ int err = pthread_mutex_unlock(mx) ;
+ if (err != 0)
+ zabort_err("pthread_mutex_unlock failed", err) ;
+#endif
+ } ;
+} ;
+
+/*==============================================================================
+ * Condition variable inline functions
+ */
+
+/* Wait for given condition variable -- do nothing if !qpthreads_enabled
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+Inline void
+qpt_cond_wait(qpt_cond cv, qpt_mutex mx)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_cond_wait(cv, mx) ;
+#else
+ int err = pthread_cond_wait(cv, mx) ;
+ if (err != 0)
+ zabort_err("pthread_cond_wait failed", err) ;
+#endif
+ } ;
+} ;
+
+/* Signal given condition -- do nothing if !qpthreads_enabled
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+Inline void
+qpt_cond_signal(qpt_cond cv)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_cond_signal(cv) ;
+#else
+ int err = pthread_cond_signal(cv) ;
+ if (err != 0)
+ zabort_err("pthread_cond_signal failed", err) ;
+#endif
+ } ;
+} ;
+
+/* Broadcast given condition -- do nothing if !qpthreads_enabled
+ *
+ * Unless both NCHECK_QPTHREADS and NDEBUG are defined, checks that the
+ * return value is valid -- zabort_errno if it isn't.
+ */
+Inline void
+qpt_cond_broadcast(qpt_cond cv)
+{
+ if (qpthreads_enabled)
+ {
+#if defined(NDEBUG) && defined(NDEBUG_QPTHREADS)
+ pthread_cond_broadcast(cv) ;
+#else
+ int err = pthread_cond_broadcast(cv) ;
+ if (err != 0)
+ zabort_err("pthread_cond_broadcast failed", err) ;
+#endif
+ } ;
+} ;
+
+/*==============================================================================
+ * Signal Handling.
+ */
+void /* FATAL error if !qpthreads_enabled */
+qpt_thread_sigmask(int how, const sigset_t* set, sigset_t* oset) ;
+
+void /* FATAL error if !qpthreads_enabled */
+qpt_thread_signal(qpt_thread_t thread, int signum) ;
+
+#endif /* _ZEBRA_QPTHREADS_H */
diff --git a/lib/qtime.c b/lib/qtime.c
new file mode 100644
index 00000000..3ec34a72
--- /dev/null
+++ b/lib/qtime.c
@@ -0,0 +1,194 @@
+/* Quagga realtime and monotonic clock handling -- functions
+ * 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 <sys/times.h>
+#include <errno.h>
+
+#include "zassert.h"
+#include "qtime.h"
+
+/*==============================================================================
+ * This is a collection of functions and (in qtime.h) macros and inline
+ * functions which support system time and a monotonic clock.
+ *
+ * TODO: introduce mutex for crafted monotonic time, and initialisation
+ * routine for that: which can preset the various variables... but
+ * unless is guaranteed to be called, must leave the on-the-fly
+ * initialisation... could also start a watchdog at that point.
+ */
+
+/*==============================================================================
+ * Replacement for CLOCK_MONOTONIC.
+ *
+ * With thanks to Joakim Tjernlund for reminding everyone of the return value
+ * from times() !
+ *
+ * times() is defined to return a value which is the time since some fixed time
+ * before the application started (or when the application started). This time
+ * is measured in units of sysconf(_SC_CLK_TCK) ticks per second.
+ *
+ * The only tricky bit is that the value returned (of type clock_t) is a
+ * signed integer, which can overflow. It is not defined exactly how it
+ * does this... This code assumes that the system will wrap around in some
+ * obvious way. The base of the time for this clock may be when the *system*
+ * started... so when it overflows may depend on how long the system has been
+ * up... which suggests that some sensible wrap around is likely (?).
+ *
+ * The qtime_t value is in nano-seconds.
+ *
+ * The result from times() is in units of sysconf(_SC_CLK_TCK) ticks per second.
+ *
+ * If clock_t is a signed 32-bit integer, which is kept +ve, then the clock
+ * overflows/wraps round in 2^31 ticks which is:
+ *
+ * at 100 ticks/sec: > 248 days
+ * at 1,000 ticks/sec: > 24 days
+ * at 10,000 ticks/sec: > 59 hours
+ *
+ * For safety, this asserts that sysconf(_SC_CLK_TCK) <= 1,000,000 for
+ * sizeof(clock_t) > 4, but <= 1,000 for sizeof(clock_t) == 4.
+ *
+ * (It appears that 60, 100, 250 and 1,000 ticks/sec. are popular options.)
+ *
+ * If sizeof(clock_t) > 4, it is assumed large enough never to wrap around.
+ *
+ * When clock_t is a 32-bit integer must be at least ready for wrap around.
+ * There are two cases:
+ *
+ * * +ve wrap around. new < old value, and new >= 0
+ *
+ * step = (INT32_MAX - old + 1) + new
+ *
+ * * -ve wrap around. new < old value, and new < 0 (and old > 0)
+ *
+ * step = (INT32_MAX - old + 1) - (INT32_MIN - new)
+ *
+ * In any event, a step > 24 hours is taken to means that something has gone
+ * very, very badly wrong.
+ *
+ * NB: it is assumed that qt_craft_monotonic will be called often enough to
+ * ensure that the check on the step size will not be triggered !
+ *
+ * NB: it is assumed that times() does not simply stick if it overflows.
+ *
+ * TODO: Add a watchdog to monitor the behaviour of this clock ?
+ */
+
+CONFIRM((sizeof(clock_t) >= 4) && (sizeof(clock_t) <= 8)) ;
+
+#ifdef GNU_LINUX
+#define TIMES_TAKES_NULL 1
+#else
+#undef TIMES_TAKES_NULL
+#endif
+
+static uint64_t monotonic = 0 ; /* monotonic clock in _SC_CLK_TCK's */
+static int64_t last_times_sample = 0 ; /* last value returned by times() */
+
+static uint64_t step_limit = 0 ; /* for sanity check */
+
+static int64_t times_clk_tcks = 0 ; /* sysconf(_SC_CLK_TCK) */
+static qtime_t times_scale_q = 0 ; /* 10**9 / times_clk_tcks */
+static qtime_t times_scale_r = 0 ; /* 10**9 % times_clk_tcks */
+
+qtime_mono_t
+qt_craft_monotonic(void) {
+ struct tms dummy ;
+ int64_t this_times_sample ;
+ uint64_t step ;
+
+ /* Set up times_scale_q & times_scale_q if not yet done. */
+ if (times_clk_tcks == 0) /* Is zero until it's initialized */
+ {
+ lldiv_t qr ;
+ confirm(sizeof(qtime_t) <= sizeof(long long int)) ;
+
+ times_clk_tcks = sysconf(_SC_CLK_TCK) ;
+ passert((times_clk_tcks > 0) &&
+ (times_clk_tcks <= (sizeof(clock_t) > 4) ? 1000000
+ : 1000)) ;
+
+ qr = lldiv(QTIME_SECOND, times_clk_tcks) ;
+ times_scale_q = qr.quot ;
+ times_scale_r = qr.rem ;
+
+ step_limit = (uint64_t)24 * 60 * 60 * times_clk_tcks ;
+ } ;
+
+ /* No errors are defined for times(), but a return of -1 is defined */
+ /* to indicate an error condition, with errno saying what it is ! */
+ /* */
+ /* The following deals carefully with this -- cannot afford for the */
+ /* clock either to jump or to get stuck ! */
+
+#ifdef TIMES_TAKES_NULL
+ this_times_sample = times(NULL) ; /* assume this saves effort ! */
+#else
+ this_times_sample = times(&dummy) ;
+#endif
+
+ if (this_times_sample == -1) /* deal with theoretical error */
+ {
+ errno = 0 ;
+ this_times_sample = times(&dummy) ;
+ if (errno != 0)
+ zabort_errno("times() failed") ;
+ } ;
+
+ /* Calculate the step and verify sensible. */
+ /* */
+ /* Watch out for huge jumps and/or time going backwards. */
+ /* For 32-bit clock_t, look out for wrap-around. */
+
+ if ((sizeof(clock_t) > 4) || (this_times_sample > last_times_sample))
+ /* time going backwards will appear as HUGE step forwards. */
+ step = (uint64_t)(this_times_sample - last_times_sample) ;
+ else
+ {
+ if (this_times_sample > 0)
+ /* both samples +ve => +ve wrap around. */
+ step = (uint64_t)( ((int64_t)INT32_MAX - last_times_sample + 1)
+ + this_times_sample ) ;
+ else
+ /* this sample -ve and last sample +ve => -ve wrap round */
+ /* this sample -ve and last sample -ve => time gone backwards */
+ /* (which appears as a HUGE step forwards). */
+ step = (uint64_t)( ((int64_t)INT32_MAX - last_times_sample + 1)
+ - ((int64_t)INT32_MIN - this_times_sample) ) ;
+ } ;
+
+ /* TODO: better error messaging for large clock jumps. */
+ if (step > step_limit)
+ zabort("Sudden large monotonic clock jump") ;
+
+ /* Advance the monotonic clock in sysconf(_SC_CLK_TCK) units. */
+ monotonic += step ;
+
+ /* Remember what we got, for next time. */
+ last_times_sample = this_times_sample ;
+
+ /* Scale to qtime_t units. */
+ if (times_scale_r == 0)
+ return monotonic * times_scale_q ;
+ else
+ return (monotonic * times_scale_q) +
+ ((monotonic * times_scale_r) / times_clk_tcks) ;
+} ;
diff --git a/lib/qtime.h b/lib/qtime.h
new file mode 100644
index 00000000..35e1a51b
--- /dev/null
+++ b/lib/qtime.h
@@ -0,0 +1,307 @@
+/* Quagga realtime and monotonic clock 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_QTIME_H
+#define _ZEBRA_QTIME_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "zassert.h"
+#include "config.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * qtime_t -- signed 64-bit integer.
+ *
+ * The various time functions work in terms of the structures:
+ *
+ * timespec -- tv_secs seconds
+ * tv_nsecs nano-seconds
+ *
+ * timeval -- tv_secs seconds
+ * tv_usecs micro-seconds
+ *
+ * Given a 64-bit integer it is much easier to do operations on a 64 bit
+ * (signed) nano-second value. That gives > 34 bits for the seconds count,
+ * and counts from zero to > 290 years.
+ */
+
+typedef int64_t qtime_t ;
+
+typedef qtime_t qtime_real_t ; /* qtime_t value, realtime time-base */
+typedef qtime_t qtime_mono_t ; /* qtime_t value, monotonic time-base */
+
+typedef qtime_t qtime_tod_t ; /* qtime_t value, timeofday time-base... */
+ /* ...just in case != CLOCK_REALTIME ! */
+
+/* A qtime_t second 123456789 -- nano-seconds */
+#define QTIME_SECOND 1000000000
+#define TIMESPEC_SECOND 1000000000
+#define TIMEVAL_SECOND 1000000
+
+/* Macro to convert time in seconds to a qtime_t */
+/* Note that the time to convert may be a float. */
+#define QTIME(s) ((qtime_t)((s) * (qtime_t)QTIME_SECOND))
+
+Inline qtime_t
+timespec2qtime(struct timespec* p_ts) ;
+
+Inline qtime_t
+timeval2qtime(struct timeval* p_tv) ;
+
+Inline struct timespec*
+qtime2timespec(struct timespec* p_ts, qtime_t qt) ;
+
+Inline struct timeval*
+qtime2timeval(struct timeval* p_tv, qtime_t qt) ;
+
+/*==============================================================================
+ * Clocks.
+ *
+ * Here is support for:
+ *
+ * * System Clock
+ *
+ * This can be read using either clock_gettime(CLOCK_REALTIME, &ts) or
+ * gettimeofday(&tv, NULL) -- which (are believed to) return the same clock,
+ * but in different units.
+ *
+ * * Monotonic Clock
+ *
+ * Using clock_gettime(CLOCK_MONOTONIC, &ts) if it is available, otherwise
+ * a manufactured equivalent using times() -- see qt_craft_monotonic().
+ */
+
+Inline qtime_real_t
+qt_get_realtime(void) ; /* clock_gettime(CLOCK_REALTIME, ...) */
+
+Inline qtime_mono_t
+qt_add_realtime(qtime_t interval) ; /* qt_get_realtime() + interval */
+
+Inline qtime_mono_t
+qt_get_monotonic(void) ; /* clock_gettime(CLOCK_MONOTONIC, ...) */
+ /* OR equivalent using times() */
+Inline qtime_mono_t
+qt_add_monotonic(qtime_t interval) ; /* qt_get_monotonic() + interval */
+
+Inline qtime_mono_t /* monotonic time from CLOCK_REALTIME */
+qt_realtime2monotonic(qtime_real_t realtime) ;
+Inline qtime_real_t /* CLOCK_REALTIME from monotonic time */
+qt_monotonic2realtime(qtime_mono_t monotonic) ;
+
+/* Function to manufacture a monotonic clock. */
+extern qtime_mono_t
+qt_craft_monotonic(void) ;
+
+/* These are provided just in case gettimeofday() != CLOCK_REALTIME */
+Inline qtime_tod_t
+qt_get_timeofday(void) ; /* gettimeofday(&tv, NULL) */
+
+Inline qtime_tod_t
+qt_add_timeofday(qtime_t interval) ; /* qt_get_timeofday() + interval */
+
+Inline qtime_mono_t /* monotonic time from timeofday */
+qt_timeofday2monotonic(qtime_tod_t timeofday) ;
+Inline qtime_tod_t /* timeofday from monotonic time */
+qt_monotonic2timeofday(qtime_mono_t monotonic) ;
+
+/*==============================================================================
+ * Inline conversion functions
+ */
+
+/* Convert timespec to qtime_t
+ *
+ * Returns qtime_t value.
+ */
+Inline qtime_t
+timespec2qtime(struct timespec* p_ts)
+{
+ return QTIME(p_ts->tv_sec) + p_ts->tv_nsec ;
+ confirm(QTIME_SECOND == TIMESPEC_SECOND) ;
+} ;
+
+/* Convert timeval to qtime_t
+ *
+ * Returns qtime_t value.
+ */
+Inline qtime_t
+timeval2qtime(struct timeval* p_tv)
+{
+ return QTIME(p_tv->tv_sec) + (p_tv->tv_usec * 1000) ;
+ confirm(QTIME_SECOND == TIMEVAL_SECOND * 1000) ;
+} ;
+
+/* Convert qtime_t to timespec
+ *
+ * Takes address of struct timespec and returns that address.
+ */
+Inline struct timespec*
+qtime2timespec(struct timespec* p_ts, qtime_t qt)
+{
+ lldiv_t imd = lldiv(qt, QTIME_SECOND) ;
+ confirm(sizeof(long long) >= sizeof(qtime_t)) ;
+
+ p_ts->tv_sec = imd.quot ;
+ p_ts->tv_nsec = imd.rem ;
+ confirm(TIMESPEC_SECOND == QTIME_SECOND) ;
+
+ return p_ts ;
+} ;
+
+/* Convert timespec to qtime_t
+ *
+ * Takes address of struct timespec and returns that address.
+ */
+Inline struct timeval*
+qtime2timeval(struct timeval* p_tv, qtime_t qt)
+{
+ lldiv_t imd = lldiv(qt, QTIME_SECOND) ;
+ confirm(sizeof(long long) >= sizeof(qtime_t)) ;
+
+ p_tv->tv_sec = imd.quot ;
+ p_tv->tv_usec = imd.rem / 1000 ;
+ confirm(TIMEVAL_SECOND * 1000 == QTIME_SECOND) ;
+
+ return p_tv ;
+} ;
+
+/*==============================================================================
+ * Inline Clock Functions.
+ */
+
+/* Read given clock & return a qtime_t value.
+ *
+ * While possibility of error is essentially theoretical, must treat it as a
+ * FATAL error -- cannot continue with broken time value !
+ */
+
+Inline qtime_t
+qt_clock_gettime(clockid_t clock_id)
+{
+ struct timespec ts ;
+
+ if (clock_gettime(clock_id, &ts) != 0)
+ zabort_errno("clock_gettime failed") ;
+
+ return timespec2qtime(&ts) ;
+} ;
+
+/* clock_gettime(CLOCK_REALTIME, ...) -- returning qtime_t value
+ *
+ * While possibility of error is essentially theoretical, must treat it as a
+ * FATAL error -- cannot continue with broken time value !
+ */
+Inline qtime_real_t
+qt_get_realtime(void)
+{
+ return qt_clock_gettime(CLOCK_REALTIME) ;
+} ;
+
+/* qt_get_realtime() + interval
+ */
+Inline qtime_real_t
+qt_add_realtime(qtime_t interval)
+{
+ return qt_get_realtime() + interval;
+} ;
+
+/* clock_gettime(CLOCK_MONOTONIC, ...) OR qt_craft_monotonic()
+ * -- returning qtime_t value
+ *
+ * While possibility of error is essentially theoretical, must treat it as a
+ * FATAL error -- cannot continue with broken time value !
+ */
+Inline qtime_mono_t
+qt_get_monotonic(void)
+{
+#ifdef HAVE_CLOCK_MONOTONIC
+ return qt_clock_gettime(CLOCK_MONOTONIC) ;
+#else
+ return qt_craft_monotonic() ;
+#endif
+} ;
+
+/* qt_get_monotonic() + interval
+ */
+Inline qtime_mono_t
+qt_add_monotonic(qtime_t interval)
+{
+ return qt_get_monotonic() + interval;
+} ;
+
+/* gettimeofday(&tv, NULL) -- returning qtime_t value
+ */
+Inline qtime_tod_t
+qt_get_timeofday(void)
+{
+ struct timeval tv ;
+ gettimeofday(&tv, NULL) ;
+ return timeval2qtime(&tv) ;
+}
+
+/* qt_get_timeofday() + interval
+ */
+Inline qtime_tod_t
+qt_add_timeofday(qtime_t interval)
+{
+ return qt_get_timeofday() + interval;
+} ;
+
+/*==============================================================================
+ * Conversion between realtime/timeofday and monotonic
+ */
+
+/* Convert a CLOCK_REALTIME time to our local monotonic time. */
+Inline qtime_mono_t
+qt_realtime2monotonic(qtime_real_t realtime)
+{
+ return qt_get_monotonic() + (realtime - qt_get_realtime()) ;
+} ;
+
+/* Convert a local monotonic time to CLOCK_REALTIME time. */
+Inline qtime_real_t
+qt_monotonic2realtime(qtime_mono_t monotonic)
+{
+ return qt_get_realtime() + (monotonic - qt_get_monotonic()) ;
+} ;
+
+/* Convert a gettimeofday() time to our local monotonic time. */
+Inline qtime_mono_t
+qt_timeofday2monotonic(qtime_tod_t timeofday)
+{
+ return qt_get_monotonic() + (timeofday - qt_get_timeofday()) ;
+} ;
+
+/* Convert a local monotonic time to gettimeofday() time. */
+Inline qtime_tod_t
+qt_monotonic2timeofday(qtime_mono_t monotonic)
+{
+ return qt_get_timeofday() + (monotonic - qt_get_monotonic()) ;
+} ;
+
+#endif /* _ZEBRA_QTIME_H */
diff --git a/lib/qtimers.c b/lib/qtimers.c
new file mode 100644
index 00000000..6b3db500
--- /dev/null
+++ b/lib/qtimers.c
@@ -0,0 +1,347 @@
+/* Quagga timers support -- functions
+ * 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 <stddef.h>
+#include <string.h>
+
+#include "zassert.h"
+#include "qtimers.h"
+#include "memory.h"
+#include "heap.h"
+
+/*==============================================================================
+ * Quagga Timers -- qtimer_xxxx
+ *
+ * Here and in qtimers.h is a data structure for managing multiple timers
+ * each with an action to be executed when the timer expires.
+ *
+ * The qtime_pile structure manages a "pile" of qtimer structures which are
+ * waiting for the right time to go off.
+ *
+ * NB: it is ASSUMED that a qtime_pile will be private to the thread in which
+ * it is created and used.
+ *
+ * There is NO mutex handling here.
+ *
+ * Timers are triggered by calling qtimer_dispatch_next(). This is given the
+ * current qtimer time (see below), and it dispatches the first timer whose
+ * time has come (or been passed). Dispatching a timer means calling its
+ * action function (see below). Each call of qtimer_dispatch_next() triggers
+ * at most one timer.
+ *
+ * Time Base
+ * ---------
+ *
+ * The time base for qtimers is the monotonic time provided in qtime.c/.h.
+ *
+ * Interval
+ * --------
+ *
+ * There is an optional interval associated with each timer.
+ *
+ * The timer may be set to "now + interval", and the interval is stored with
+ * the timer.
+ *
+ * The timer may be set to its current time + stored interval (to provide a
+ * "steady" clock).
+ *
+ * Action Functions
+ * ----------------
+ *
+ * There is a separate action function for each timer.
+ *
+ * When the action function is called it is passed the qtimer structure, the
+ * timer_info pointer from that structure and the time which triggered the
+ * timer (which may, or may not, be the current qtimer time).
+ *
+ * During an action function timers may be set/unset, actions changed, and so
+ * on... there are no restrictions EXCEPT that the qtimer structure may NOT be
+ * freed.
+ */
+
+static int
+qtimer_cmp(qtimer* a, qtimer* b) /* the heap discipline */
+{
+ if ((**a).time < (**b).time)
+ return -1 ;
+ if ((**a).time > (**b).time)
+ return +1 ;
+ return 0 ;
+} ;
+
+/*==============================================================================
+ * qtimer_pile handling
+ */
+
+/* Initialise a timer pile -- allocating it if required.
+ *
+ * Returns the qtimer_pile.
+ */
+qtimer_pile
+qtimer_pile_init_new(qtimer_pile qtp)
+{
+ if (qtp == NULL)
+ qtp = XCALLOC(MTYPE_QTIMER_PILE, sizeof(struct qtimer_pile)) ;
+ else
+ memset(qtp, 0, sizeof(struct qtimer_pile)) ;
+
+ /* Zeroising has initialised:
+ *
+ * timers -- invalid heap -- need to properly initialise
+ */
+
+ /* Eclipse flags offsetof(struct qtimer, backlink) as a syntax error :-( */
+ typedef struct qtimer qtimer_t ;
+
+ heap_init_new_backlinked(&qtp->timers, 0, (heap_cmp*)qtimer_cmp,
+ offsetof(qtimer_t, backlink)) ;
+ return qtp ;
+} ;
+
+/* Get the timer time for the first timer due to go off in the given pile.
+ *
+ * The caller must provide a maximum acceptable time. If the qtimer pile is
+ * empty, or the top entry times out after the maximum time, then the maximum
+ * is returned.
+ */
+qtime_mono_t
+qtimer_pile_top_time(qtimer_pile qtp, qtime_mono_t max_time)
+{
+ qtimer qtr = heap_top_item(&qtp->timers) ;
+
+ if ((qtr == NULL) || (qtr->time >= max_time))
+ return max_time ;
+ else
+ return qtr->time ;
+} ;
+
+/* Dispatch the next timer whose time is <= the given "upto" time.
+ *
+ * The upto time must be a qtimer time (!) -- see qtimer_time_now().
+ *
+ * The upto argument allows the caller to get a qtimer_time_now() value, and
+ * then process all timers upto that time.
+ *
+ * Returns true <=> dispatched a timer, and there may be more to do.
+ * false <=> nothing to do (and nothing done).
+ */
+int
+qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto)
+{
+ qtimer qtr ;
+
+ qtr = heap_top_item(&qtp->timers) ;
+ if ((qtr != NULL) && (qtr->time <= upto))
+ {
+ qtr->state = qtr_state_unset_pending ;
+
+ qtr->action(qtr, qtr->timer_info, upto) ;
+
+ if (qtr->state == qtr_state_unset_pending)
+ qtimer_unset(qtr) ;
+
+ return 1 ;
+ }
+ else
+ return 0 ;
+} ;
+
+/* Ream out (another) item from qtimer_pile.
+ *
+ * If pile is empty, release the qtimer_pile structure, if required.
+ *
+ * See: #define qtimer_pile_ream_free(qtp)
+ * #define qtimer_pile_ream_keep(qtp)
+ *
+ * Useful for emptying out and discarding a pile of timers:
+ *
+ * while ((p_qtr = qtimer_pile_ream_free(qtp)))
+ * ... do what's required to release the item p_qtr
+ *
+ * Returns NULL when timer pile is empty (and has been released, if required).
+ *
+ * If the timer pile is not released, it may be reused without reinitialisation.
+ *
+ * NB: once reaming has started, the timer pile MUST NOT be used for anything,
+ * and the process MUST be run to completion.
+ */
+qtimer
+qtimer_pile_ream(qtimer_pile qtp, int free_structure)
+{
+ qtimer qtr ;
+
+ qtr = heap_ream_keep(&qtp->timers) ; /* ream, keeping the heap structure */
+ if (qtr != NULL)
+ qtr->state = qtr_state_inactive ; /* has been removed from pile */
+ else
+ if (free_structure) /* pile is empty, may now free it */
+ XFREE(MTYPE_QTIMER_PILE, qtp) ;
+
+ return qtr ;
+} ;
+
+/*==============================================================================
+ * qtimer handling
+ */
+
+/* Initialise qtimer structure -- allocating one if required.
+ *
+ * Associates qtimer with the given pile of timers, and sets up the action and
+ * the timer_info.
+ *
+ * Once initialised, the timer may be set.
+ *
+ * Returns the qtimer.
+ */
+qtimer
+qtimer_init_new(qtimer qtr, qtimer_pile qtp,
+ qtimer_action* action, void* timer_info)
+{
+ if (qtr == NULL)
+ qtr = XCALLOC(MTYPE_QTIMER, sizeof(struct qtimer)) ;
+ else
+ memset(qtr, 0, sizeof(struct qtimer)) ;
+
+ /* Zeroising has initialised:
+ *
+ * pile -- NULL -- not in any pile (yet)
+ * backlink -- unset
+ *
+ * state -- not active
+ *
+ * time -- unset
+ * action -- NULL -- no action set (yet)
+ * timer_info -- NULL -- no timer info set (yet)
+ *
+ * interval -- unset
+ */
+
+ confirm(qtr_state_inactive == 0) ;
+
+ qtr->pile = qtp ;
+ qtr->action = action ;
+ qtr->timer_info = timer_info ;
+
+ return qtr ;
+} ;
+
+/* Free given timer.
+ *
+ * Unsets it first if it is active.
+ *
+ * The timer MAY NOT be currently the subject of qtimer_pile_dispatch_next().
+ */
+void
+qtimer_free(qtimer qtr)
+{
+ assert(qtr->state != qtr_state_unset_pending) ;
+
+ if (qtr->state != qtr_state_inactive)
+ qtimer_unset(qtr) ;
+
+ XFREE(MTYPE_QTIMER, qtr) ;
+} ;
+
+/* Set pile in which given timer belongs.
+ *
+ * Unsets the timer if active in another pile.
+ * (Does nothing if active in the "new" pile.)
+ */
+void
+qtimer_set_pile(qtimer qtr, qtimer_pile qtp)
+{
+ if (qtr_is_active(qtr) && (qtr->pile != qtp))
+ qtimer_unset(qtr) ;
+ qtr->pile = qtp ;
+}
+
+/* Set action for given timer.
+ */
+void
+qtimer_set_action(qtimer qtr, qtimer_action* action)
+{
+ qtr->action = action ;
+} ;
+
+/* Set timer_info for given timer.
+ */
+void
+qtimer_set_info(qtimer qtr, void* timer_info)
+{
+ qtr->timer_info = timer_info ;
+} ;
+
+/* Set given timer.
+ *
+ * Setting a -ve time => qtimer_unset.
+ *
+ * Sets any given action -- if the action given is NULL, retains previously set
+ * action.
+ *
+ * If the timer is already active, sets the new time & updates pile.
+ *
+ * Otherwise, sets the time and adds to pile -- making timer active.
+ *
+ * It is an error to set a timer which has a NULL action.
+ */
+void
+qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action)
+{
+ qtimer_pile qtp ;
+
+ if (when < 0)
+ return qtimer_unset(qtr) ;
+
+ qtp = qtr->pile ;
+ dassert(qtp != NULL) ;
+
+ qtr->time = when ;
+
+ if (qtr_is_active(qtr))
+ heap_update_item(&qtp->timers, qtr) ; /* update in heap */
+ else
+ heap_push_item(&qtp->timers, qtr) ; /* add to heap */
+
+ qtr->state = qtr_state_active ; /* overrides any unset pending */
+
+ if (action != NULL)
+ qtr->action = action ;
+ else
+ dassert(qtr->action != NULL) ;
+} ;
+
+/* Unset given timer
+ *
+ * If the timer is active, removes from pile and sets inactive.
+ */
+void
+qtimer_unset(qtimer qtr)
+{
+ if (qtr_is_active(qtr))
+ {
+ qtimer_pile qtp = qtr->pile ;
+ dassert(qtp != NULL) ;
+
+ heap_delete_item(&qtp->timers, qtr) ;
+
+ qtr->state = qtr_state_inactive ; /* overrides any unset pending */
+ } ;
+} ;
diff --git a/lib/qtimers.h b/lib/qtimers.h
new file mode 100644
index 00000000..d4305dc0
--- /dev/null
+++ b/lib/qtimers.h
@@ -0,0 +1,181 @@
+/* Quagga timers support -- 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_QTIMERS_H
+#define _ZEBRA_QTIMERS_H
+
+#include "zassert.h"
+#include "qtime.h"
+#include "heap.h"
+
+#ifndef Inline
+#define Inline static inline
+#endif
+
+/*==============================================================================
+ * Quagga Timers -- qtimer_xxxx
+ *
+ * Here and in qtimers.c is a data structure for managing multiple timers
+ * each with an action to be executed when the timer expires.
+ */
+
+/*==============================================================================
+ * Data Structures.
+ */
+
+typedef struct qtimer* qtimer ;
+typedef struct qtimer_pile* qtimer_pile ;
+
+typedef void (qtimer_action)(qtimer qtr, void* timer_info, qtime_mono_t when) ;
+
+enum qtimer_state {
+ qtr_state_inactive = 0,
+ qtr_state_active = 1, /* timer is active in its pile */
+ qtr_state_unset_pending = 3 /* timer is active, but unset is pending */
+} ;
+
+#define qtr_is_active(qtr) ((qtr)->state != qtr_state_inactive)
+
+typedef enum qtimer_state qtimer_state_t ;
+
+struct qtimer
+{
+ qtimer_pile pile ; /* pile currently allocated to */
+ heap_backlink_t backlink ;
+
+ qtimer_state_t state ;
+
+ qtime_mono_t time ; /* current time to trigger action */
+ qtimer_action* action ;
+ void* timer_info ;
+
+ qtime_t interval ; /* optional timer interval */
+} ;
+
+struct qtimer_pile
+{
+ struct heap timers ;
+} ;
+
+/*==============================================================================
+ * Functions
+ */
+
+qtimer_pile
+qtimer_pile_init_new(qtimer_pile qtp) ;
+
+int
+qtimer_pile_dispatch_next(qtimer_pile qtp, qtime_mono_t upto) ;
+
+qtime_mono_t
+qtimer_pile_top_time(qtimer_pile qtp, qtime_mono_t max_time) ;
+
+qtimer
+qtimer_pile_ream(qtimer_pile qtp, int free_structure) ;
+
+/* Ream out qtimer pile and free the qtimer structure. */
+#define qtimer_pile_ream_free(qtp) qtimer_pile_ream(qtp, 1)
+/* Ream out qtimer pile but keep the qtimer structure. */
+#define qtimer_pile_ream_keep(qtp) qtimer_pile_ream(qtp, 0)
+
+qtimer
+qtimer_init_new(qtimer qtr, qtimer_pile qtp,
+ qtimer_action* action, void* timer_info) ;
+void
+qtimer_set_pile(qtimer qtr, qtimer_pile qtp) ;
+
+void
+qtimer_set_action(qtimer qtr, qtimer_action* action) ;
+
+void
+qtimer_set_info(qtimer qtr, void* timer_info) ;
+
+void
+qtimer_free(qtimer qtr) ;
+
+void
+qtimer_set(qtimer qtr, qtime_mono_t when, qtimer_action* action) ;
+
+void
+qtimer_unset(qtimer qtr) ;
+
+Inline void
+qtimer_add(qtimer qtr, qtime_t interval, qtimer_action* action) ;
+
+Inline qtime_mono_t
+qtimer_get(qtimer qtr) ;
+
+Inline void
+qtimer_set_interval(qtimer qtr, qtime_t interval, qtimer_action* action) ;
+
+Inline void
+qtimer_add_interval(qtimer qtr, qtimer_action* action) ;
+
+Inline qtime_t
+qtimer_get_interval(qtimer qtr) ;
+
+/*==============================================================================
+ * Inline functions
+ */
+
+/* Set given timer to given time later than *its* current time.
+ */
+Inline void
+qtimer_add(qtimer qtr, qtime_t interval, qtimer_action* action)
+{
+ qtimer_set(qtr, qtimer_get(qtr) + interval, action);
+} ;
+
+/* Get the given timer's time.
+ */
+Inline qtime_mono_t
+qtimer_get(qtimer qtr)
+{
+ return qtr->time ;
+} ;
+
+/* Interval handling ---------------------------------------------------------*/
+
+/* Set the interval field
+ */
+
+Inline void
+qtimer_set_interval(qtimer qtr, qtime_t interval, qtimer_action* action)
+{
+ qtr->interval = interval ;
+ qtimer_set(qtr, qt_add_monotonic(interval), action) ;
+} ;
+
+Inline void
+qtimer_add_interval(qtimer qtr, qtimer_action* action)
+{
+ qtimer_add(qtr, qtr->interval, action) ;
+} ;
+
+/* Get the current value of the interval field
+ */
+Inline qtime_t
+qtimer_get_interval(qtimer qtr)
+{
+ return qtr->interval ;
+} ;
+
+#endif /* _ZEBRA_QTIMERS_H */
diff --git a/lib/routemap.c b/lib/routemap.c
index 4f4e6d62..2dfa5a46 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -207,7 +207,7 @@ vty_show_route_map_entry (struct vty *vty, struct route_map *map)
/* Print the name of the protocol */
if (zlog_default)
- vty_out (vty, "%s:%s", zlog_proto_names[zlog_default->protocol],
+ vty_out (vty, "%s:%s", zlog_get_proto_name(NULL),
VTY_NEWLINE);
for (index = map->head; index; index = index->next)
diff --git a/lib/thread.c b/lib/thread.c
index e89af541..9fa1fd9f 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -29,6 +29,7 @@
#include "hash.h"
#include "command.h"
#include "sigevent.h"
+#include "qpthreads.h"
/* Recent absolute time of day */
struct timeval recent_time;
@@ -40,6 +41,13 @@ static struct timeval relative_time_base;
static unsigned short timers_inited;
static struct hash *cpu_record = NULL;
+
+/* TODO: remove this */
+#define USE_MQUEUE
+#ifdef USE_MQUEUE
+#include "qpnexus.h"
+static sigset_t newmask;
+#endif
/* Struct timeval's tv_usec one second value. */
#define TIMER_SECOND_MICRO 1000000L
@@ -416,6 +424,11 @@ thread_master_debug (struct thread_master *m)
struct thread_master *
thread_master_create ()
{
+#ifdef USE_MQUEUE
+ sigfillset (&newmask);
+ sigdelset (&newmask, SIGMQUEUE);
+#endif
+
if (cpu_record == NULL)
cpu_record
= hash_create_size (1011, (unsigned int (*) (void *))cpu_record_hash_key,
@@ -921,7 +934,8 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
int num = 0;
/* Signals are highest priority */
- quagga_sigevent_process ();
+ if (!qpthreads_enabled)
+ quagga_sigevent_process ();
/* Normal event are the next highest priority. */
if ((thread = thread_trim_head (&m->event)) != NULL)
@@ -947,13 +961,27 @@ thread_fetch (struct thread_master *m, struct thread *fetch)
(!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0)))
timer_wait = timer_wait_bg;
+ /* TODO: remove this */
+#ifdef USE_MQUEUE
+ num = pselect (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait, &newmask);
+#else
num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait);
-
+#endif
+
/* Signals should get quick treatment */
if (num < 0)
{
if (errno == EINTR)
- continue; /* signal received - process it */
+#ifdef USE_MQUEUE
+ {
+ if (qpthreads_enabled)
+ return NULL;
+ else
+ continue; /* signal received - process it */
+ }
+#else
+ continue; /* signal received - process it */
+#endif
zlog_warn ("select() error: %s", safe_strerror (errno));
return NULL;
}
@@ -1126,3 +1154,4 @@ funcname_thread_execute (struct thread_master *m,
return NULL;
}
+#undef USE_MQUEUE
diff --git a/lib/vector.c b/lib/vector.c
index 8268c830..b1ec160d 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -234,6 +234,79 @@ vector_ream(vector v, int free_structure)
} ;
/*==============================================================================
+ * Unset item, condensing and trimming vector.
+ */
+
+/* Trim any NULL entries at the current (logical) end of the vector.
+ *
+ * Returns the (new) end of the vector.
+ */
+extern vector_index
+vector_trim(vector v)
+{
+ vector_index i = v->end ;
+ while ((i > 0) && (v->p_items[i - 1] == NULL))
+ --i ;
+ v->end = i ;
+ return i ;
+} ;
+
+/* Removes any NULL entries from the given vector.
+ *
+ * Returns the (new) end of the vector.
+ */
+extern vector_index vector_condense(vector v)
+{
+ vector_index i = 0 ;
+ vector_index j ;
+
+ /* Find first NULL, if any */
+ while ((i < v->end) && (v->p_items[i] != NULL))
+ ++i ;
+
+ /* Quit if no NULLs (or vector is empty) */
+ if (i == v->end)
+ return i ;
+
+ /* Shuffle any remaining non-NULL down */
+ for (j = i + 1 ; j < v->end ; ++j)
+ if (v->p_items[j] != NULL)
+ v->p_items[i++] = v->p_items[j] ;
+
+ v->end = i ;
+
+ return i ;
+} ;
+
+/* Unset item at given index (ie set it NULL).
+ *
+ * Return the old value of the item.
+ *
+ * If the item at the current (logical) end of the vector is NULL, move the
+ * end backwards until finds a non-NULL item, or the vector becomes empty.
+ */
+extern p_vector_item
+vector_unset_item(vector v, vector_index i)
+{
+ p_vector_item was ;
+
+ if (i < v->end)
+ {
+ was = v->p_items[i] ;
+ v->p_items[i] = NULL ;
+ }
+ else if (v->end == 0)
+ return NULL ; /* avoid test for last entry NULL if is empty */
+ else
+ was = NULL ;
+
+ if (v->p_items[v->end - 1] == NULL)
+ vector_trim(v) ;
+
+ return was ;
+} ;
+
+/*==============================================================================
* Inserting and deleting items.
*/
@@ -822,36 +895,6 @@ vector_lookup_ensure (vector v, vector_index i)
return v->p_items[i];
}
-/* Unset value at specified index slot. */
-void
-vector_unset (vector v, vector_index i)
-{
- vector_index j ;
- if (i >= v->end)
- return; /* Everything beyond or at end is implicitly NULL */
-
- v->p_items[i] = NULL;
-
- /* See if everything ahead of 'i' is also NULL. */
- j = i ;
- while (++j < v->end)
- if (v->p_items[j] != NULL)
- return ; /* Finished if anything ahead 'i' is not NULL */
-
- /* Everything from 'i' onwards is NULL.
- * Step backwards across any NULLs and then set the new end.
- */
-#if 0
- v->end--;
- while (i && v->p_items[--i] == NULL && v->end--)
- ; /* Is this ugly ? */
-#endif
- while ((i != 0) && (v->p_items[i - 1] == NULL))
- --i ;
-
- v->end = i ;
-}
-
/* Count the number of not empty slots. */
vector_index
vector_count (vector v)
diff --git a/lib/vector.h b/lib/vector.h
index 3a7e7ca5..08abdcf7 100644
--- a/lib/vector.h
+++ b/lib/vector.h
@@ -107,7 +107,7 @@ Inline void vector_ensure(vector v, vector_index i) ;
extern int vector_empty_slot (vector v);
extern int vector_set (vector v, void *val);
extern int vector_set_index (vector v, vector_index i, void *val);
-extern void vector_unset (vector v, vector_index i);
+#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);
@@ -138,6 +138,9 @@ Inline p_vector_item vector_get_item(vector v, vector_index i) ;
Inline p_vector_item vector_get_first_item(vector v) ;
Inline p_vector_item vector_get_last_item(vector v) ;
Inline void vector_set_item(vector v, vector_index i, p_vector_item p_v) ;
+extern p_vector_item vector_unset_item(vector v, vector_index i) ;
+extern vector_index vector_trim(vector v) ;
+extern vector_index vector_condense(vector v) ;
extern void vector_insert_item(vector v, vector_index i, p_vector_item p_v) ;
extern void vector_insert_item_here(vector v, vector_index i, int rider,
diff --git a/lib/vty.c b/lib/vty.c
index 680286c0..cc3a3951 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -38,6 +38,36 @@
#include "network.h"
#include <arpa/telnet.h>
+#include "qpthreads.h"
+#include "qpnexus.h"
+
+/* Needs to be qpthread safe */
+qpt_mutex_t* vty_mutex = NULL;
+#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. 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.
+ *
+ * As we are not using recursive mutexes so 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_.
+ *
+ * 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.
+ */
/* Vty events */
enum event
@@ -53,7 +83,24 @@ enum event
#endif /* VTYSH */
};
+/* 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;
@@ -71,10 +118,10 @@ static char *vty_accesslist_name = NULL;
static char *vty_ipv6_accesslist_name = NULL;
/* VTY server thread. */
-vector Vvty_serv_thread;
+static vector Vvty_serv_thread;
/* Current directory. */
-char *vty_cwd = NULL;
+static char *vty_cwd = NULL;
/* Configure lock. */
static int vty_config;
@@ -89,29 +136,61 @@ static u_char restricted_mode = 0;
/* Integrated configuration file path */
char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
+/* Master of the threads. */
+static struct thread_master *master = NULL;
+static qpn_nexus cli_nexus = NULL;
+static qpn_nexus bgp_nexus = NULL;
/* VTY standard output function. vty == NULL or VTY_SHELL => stdout */
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;
+}
+
+/* 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;
+}
+
+/* internal VTY standard output function. vty == NULL or VTY_SHELL => stdout */
+static int
+uty_vout(struct vty *vty, const char *format, va_list args)
+{
int len = 0;
int size = 1024;
char buf[1024];
char *p = NULL;
+ va_list ac;
+
+ ASSERTLOCKED
- if (vty_shell (vty))
+ if (uty_shell (vty))
{
- va_start (args, format);
vprintf (format, args);
- va_end (args);
}
else
{
/* Try to write to initial buffer. */
- va_start (args, format);
- len = vsnprintf (buf, sizeof buf, format, args);
- va_end (args);
+ va_copy(ac, args);
+ len = vsnprintf (buf, sizeof buf, format, ac);
+ va_end(ac);
/* Initial buffer is not enough. */
if (len < 0 || len >= size)
@@ -125,11 +204,11 @@ vty_out (struct vty *vty, const char *format, ...)
p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size);
if (! p)
- return -1;
+ return -1;
- va_start (args, format);
- len = vsnprintf (p, size, format, args);
- va_end (args);
+ va_copy(ac, args);
+ len = vsnprintf (p, size, format, ac);
+ va_end(ac);
if (len > -1 && len < size)
break;
@@ -180,9 +259,11 @@ vty_log_out (struct vty *vty, const char *level, const char *proto_str,
int len;
char buf[1024];
+ ASSERTLOCKED
+
if (!ctl->already_rendered)
{
- ctl->len = quagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
+ ctl->len = uquagga_timestamp(ctl->precision, ctl->buf, sizeof(ctl->buf));
ctl->already_rendered = 1;
}
if (ctl->len+1 >= sizeof(buf))
@@ -213,7 +294,7 @@ vty_log_out (struct vty *vty, const char *level, const char *proto_str,
return -1;
/* Fatal I/O error. */
vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("%s: write failed to vty client fd %d, closing: %s",
+ 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
@@ -248,6 +329,15 @@ vty_time_print (struct vty *vty, int cr)
void
vty_hello (struct vty *vty)
{
+ LOCK
+ uty_hello(vty);
+ UNLOCK
+}
+
+static void
+uty_hello (struct vty *vty)
+{
+ ASSERTLOCKED
if (host.motdfile)
{
FILE *f;
@@ -263,15 +353,15 @@ vty_hello (struct vty *vty)
for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1));
s--);
*s = '\0';
- vty_out (vty, "%s%s", buf, VTY_NEWLINE);
+ uty_out (vty, "%s%s", buf, VTY_NEWLINE);
}
fclose (f);
}
else
- vty_out (vty, "MOTD file not found%s", VTY_NEWLINE);
+ uty_out (vty, "MOTD file not found%s", VTY_NEWLINE);
}
else if (host.motd)
- vty_out (vty, host.motd);
+ uty_out (vty, "%s", host.motd);
}
/* Put out prompt and wait input from user. */
@@ -281,6 +371,8 @@ vty_prompt (struct vty *vty)
struct utsname names;
const char*hostname;
+ ASSERTLOCKED
+
if (vty->type == VTY_TERM)
{
hostname = host.name;
@@ -289,7 +381,7 @@ vty_prompt (struct vty *vty)
uname (&names);
hostname = names.nodename;
}
- vty_out (vty, cmd_prompt (vty->node), hostname);
+ uty_out (vty, cmd_prompt (vty->node), hostname);
}
}
@@ -298,7 +390,8 @@ static void
vty_will_echo (struct vty *vty)
{
unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
- vty_out (vty, "%s", cmd);
+ ASSERTLOCKED
+ uty_out (vty, "%s", cmd);
}
/* Make suppress Go-Ahead telnet option. */
@@ -306,7 +399,8 @@ static void
vty_will_suppress_go_ahead (struct vty *vty)
{
unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
- vty_out (vty, "%s", cmd);
+ ASSERTLOCKED
+ uty_out (vty, "%s", cmd);
}
/* Make don't use linemode over telnet. */
@@ -314,7 +408,8 @@ static void
vty_dont_linemode (struct vty *vty)
{
unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
- vty_out (vty, "%s", cmd);
+ ASSERTLOCKED
+ uty_out (vty, "%s", cmd);
}
/* Use window size. */
@@ -322,7 +417,8 @@ static void
vty_do_window_size (struct vty *vty)
{
unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
- vty_out (vty, "%s", cmd);
+ ASSERTLOCKED
+ uty_out (vty, "%s", cmd);
}
#if 0 /* Currently not used. */
@@ -331,21 +427,31 @@ static void
vty_dont_lflow_ahead (struct vty *vty)
{
unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
- vty_out (vty, "%s", cmd);
+ ASSERTLOCKED
+ uty_out (vty, "%s", cmd);
}
#endif /* 0 */
/* Allocate new vty struct. */
struct vty *
-vty_new ()
+vty_new (int fd, int type)
{
- struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty));
+ struct vty *vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
+
+ 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;
- new->obuf = buffer_new(0); /* Use default buffer size. */
- new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
- new->max = VTY_BUFSIZ;
+ 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);
+ }
- return new;
+ return vty;
}
/* Authentication of vty */
@@ -357,6 +463,8 @@ vty_auth (struct vty *vty, char *buf)
int fail;
char *crypt (const char *, const char *);
+ ASSERTLOCKED
+
switch (vty->node)
{
case AUTH_NODE:
@@ -400,14 +508,14 @@ vty_auth (struct vty *vty, char *buf)
{
if (vty->node == AUTH_NODE)
{
- vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
+ uty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE);
vty->status = VTY_CLOSE;
}
else
{
/* AUTH_ENABLE_NODE */
vty->fail = 0;
- vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
+ uty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE);
vty->node = restricted_mode ? RESTRICTED_NODE : VIEW_NODE;
}
}
@@ -422,6 +530,8 @@ vty_command (struct vty *vty, char *buf)
vector vline;
const char *protocolname;
+ ASSERTLOCKED
+
/* Split readline string up into the vector */
vline = cmd_make_strvec (buf);
@@ -437,20 +547,19 @@ vty_command (struct vty *vty, char *buf)
GETRUSAGE(&before);
#endif /* CONSUMED_TIME_CHECK */
- ret = cmd_execute_command (vline, vty, NULL, 0);
+ UNLOCK
+ ret = cmd_execute_command (vline, vty, NULL, bgp_nexus, 0);
+ LOCK
/* Get the name of the protocol if any */
- if (zlog_default)
- protocolname = zlog_proto_names[zlog_default->protocol];
- else
- protocolname = zlog_proto_names[ZLOG_NONE];
+ 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. */
- zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s",
+ uzlog(NULL, LOG_WARNING, "SLOW COMMAND: command took %lums (cpu time %lums): %s",
realtime/1000, cputime/1000, buf);
}
#endif /* CONSUMED_TIME_CHECK */
@@ -460,16 +569,16 @@ vty_command (struct vty *vty, char *buf)
{
case CMD_WARNING:
if (vty->type == VTY_FILE)
- vty_out (vty, "Warning...%s", VTY_NEWLINE);
+ uty_out (vty, "Warning...%s", VTY_NEWLINE);
break;
case CMD_ERR_AMBIGUOUS:
- vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
break;
case CMD_ERR_NO_MATCH:
- vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
+ uty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE);
break;
case CMD_ERR_INCOMPLETE:
- vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
+ uty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE);
break;
}
cmd_free_strvec (vline);
@@ -477,6 +586,24 @@ vty_command (struct vty *vty, char *buf)
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);
+ qpt_thread_signal(cli_nexus->thread_id, SIGMQUEUE);
+ }
+
+ UNLOCK
+}
+
static const char telnet_backward_char = 0x08;
static const char telnet_space_char = ' ';
@@ -484,6 +611,7 @@ static const char telnet_space_char = ' ';
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;
@@ -495,6 +623,7 @@ vty_write (struct vty *vty, const char *buf, size_t nbytes)
static void
vty_ensure (struct vty *vty, int length)
{
+ ASSERTLOCKED
if (vty->max <= length)
{
vty->max *= 2;
@@ -509,6 +638,8 @@ 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);
@@ -526,6 +657,7 @@ vty_self_insert (struct vty *vty, char c)
static void
vty_self_insert_overwrite (struct vty *vty, char c)
{
+ ASSERTLOCKED
vty_ensure (vty, vty->length + 1);
vty->buf[vty->cp++] = c;
@@ -542,7 +674,11 @@ vty_self_insert_overwrite (struct vty *vty, char c)
static void
vty_insert_word_overwrite (struct vty *vty, char *str)
{
+
int len = strlen (str);
+
+ ASSERTLOCKED
+
vty_write (vty, str, len);
strcpy (&vty->buf[vty->cp], str);
vty->cp += len;
@@ -553,6 +689,7 @@ vty_insert_word_overwrite (struct vty *vty, char *str)
static void
vty_forward_char (struct vty *vty)
{
+ ASSERTLOCKED
if (vty->cp < vty->length)
{
vty_write (vty, &vty->buf[vty->cp], 1);
@@ -564,6 +701,7 @@ vty_forward_char (struct vty *vty)
static void
vty_backward_char (struct vty *vty)
{
+ ASSERTLOCKED
if (vty->cp > 0)
{
vty->cp--;
@@ -575,6 +713,7 @@ vty_backward_char (struct vty *vty)
static void
vty_beginning_of_line (struct vty *vty)
{
+ ASSERTLOCKED
while (vty->cp)
vty_backward_char (vty);
}
@@ -583,6 +722,7 @@ vty_beginning_of_line (struct vty *vty)
static void
vty_end_of_line (struct vty *vty)
{
+ ASSERTLOCKED
while (vty->cp < vty->length)
vty_forward_char (vty);
}
@@ -597,6 +737,8 @@ vty_history_print (struct vty *vty)
{
int length;
+ ASSERTLOCKED
+
vty_kill_line_from_beginning (vty);
/* Get previous line from history buffer */
@@ -614,6 +756,8 @@ vty_next_line (struct vty *vty)
{
int try_index;
+ ASSERTLOCKED
+
if (vty->hp == vty->hindex)
return;
@@ -639,6 +783,8 @@ vty_previous_line (struct vty *vty)
{
int try_index;
+ ASSERTLOCKED
+
try_index = vty->hp;
if (try_index == 0)
try_index = VTY_MAXHIST - 1;
@@ -657,6 +803,7 @@ vty_previous_line (struct vty *vty)
static void
vty_redraw_line (struct vty *vty)
{
+ ASSERTLOCKED
vty_write (vty, vty->buf, vty->length);
vty->cp = vty->length;
}
@@ -665,6 +812,7 @@ vty_redraw_line (struct vty *vty)
static void
vty_forward_word (struct vty *vty)
{
+ ASSERTLOCKED
while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
vty_forward_char (vty);
@@ -676,6 +824,7 @@ vty_forward_word (struct vty *vty)
static void
vty_backward_pure_word (struct vty *vty)
{
+ ASSERTLOCKED
while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
vty_backward_char (vty);
}
@@ -684,6 +833,7 @@ vty_backward_pure_word (struct vty *vty)
static void
vty_backward_word (struct vty *vty)
{
+ ASSERTLOCKED
while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
vty_backward_char (vty);
@@ -696,7 +846,8 @@ vty_backward_word (struct vty *vty)
static void
vty_down_level (struct vty *vty)
{
- vty_out (vty, "%s", VTY_NEWLINE);
+ ASSERTLOCKED
+ uty_out (vty, "%s", VTY_NEWLINE);
(*config_exit_cmd.func)(NULL, vty, 0, NULL);
vty_prompt (vty);
vty->cp = 0;
@@ -706,7 +857,8 @@ vty_down_level (struct vty *vty)
static void
vty_end_config (struct vty *vty)
{
- vty_out (vty, "%s", VTY_NEWLINE);
+ ASSERTLOCKED
+ uty_out (vty, "%s", VTY_NEWLINE);
switch (vty->node)
{
@@ -734,7 +886,7 @@ vty_end_config (struct vty *vty)
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
case VTY_NODE:
- vty_config_unlock (vty);
+ uty_config_unlock (vty);
vty->node = ENABLE_NODE;
break;
default:
@@ -753,6 +905,8 @@ vty_delete_char (struct vty *vty)
int i;
int size;
+ ASSERTLOCKED
+
if (vty->length == 0)
{
vty_down_level (vty);
@@ -782,6 +936,7 @@ vty_delete_char (struct vty *vty)
static void
vty_delete_backward_char (struct vty *vty)
{
+ ASSERTLOCKED
if (vty->cp == 0)
return;
@@ -796,6 +951,8 @@ vty_kill_line (struct vty *vty)
int i;
int size;
+ ASSERTLOCKED
+
size = vty->length - vty->cp;
if (size == 0)
@@ -814,6 +971,7 @@ vty_kill_line (struct vty *vty)
static void
vty_kill_line_from_beginning (struct vty *vty)
{
+ ASSERTLOCKED
vty_beginning_of_line (vty);
vty_kill_line (vty);
}
@@ -822,6 +980,7 @@ vty_kill_line_from_beginning (struct vty *vty)
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] != ' ')
@@ -832,6 +991,7 @@ vty_forward_kill_word (struct vty *vty)
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] != ' ')
@@ -844,6 +1004,8 @@ 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)
@@ -875,11 +1037,14 @@ vty_transpose_chars (struct vty *vty)
static void
vty_complete_command (struct vty *vty)
{
+
int i;
int ret;
char **matched = NULL;
vector vline;
+ ASSERTLOCKED
+
if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
return;
@@ -891,15 +1056,15 @@ vty_complete_command (struct vty *vty)
if (isspace ((int) vty->buf[vty->length - 1]))
vector_set (vline, '\0');
- matched = cmd_complete_command (vline, vty, &ret);
+ matched = cmd_complete_command (vline, vty->node, &ret);
cmd_free_strvec (vline);
- vty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vty, "%s", VTY_NEWLINE);
switch (ret)
{
case CMD_ERR_AMBIGUOUS:
- vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
vty_prompt (vty);
vty_redraw_line (vty);
break;
@@ -929,11 +1094,11 @@ vty_complete_command (struct vty *vty)
for (i = 0; matched[i] != NULL; i++)
{
if (i != 0 && ((i % 6) == 0))
- vty_out (vty, "%s", VTY_NEWLINE);
- vty_out (vty, "%-10s ", matched[i]);
+ uty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vty, "%-10s ", matched[i]);
XFREE (MTYPE_TMP, matched[i]);
}
- vty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vty, "%s", VTY_NEWLINE);
vty_prompt (vty);
vty_redraw_line (vty);
@@ -957,11 +1122,13 @@ vty_describe_fold (struct vty *vty, int cmd_width,
const char *cmd, *p;
int pos;
+ ASSERTLOCKED
+
cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
if (desc_width <= 0)
{
- vty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE);
+ uty_out (vty, " %-*s %s%s", cmd_width, cmd, desc->str, VTY_NEWLINE);
return;
}
@@ -978,12 +1145,12 @@ vty_describe_fold (struct vty *vty, int cmd_width,
strncpy (buf, p, pos);
buf[pos] = '\0';
- vty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
+ uty_out (vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
cmd = "";
}
- vty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
+ uty_out (vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
XFREE (MTYPE_TMP, buf);
}
@@ -992,12 +1159,14 @@ vty_describe_fold (struct vty *vty, int cmd_width,
static void
vty_describe_command (struct vty *vty)
{
- int ret;
+ 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 '> ?'. */
@@ -1010,19 +1179,19 @@ vty_describe_command (struct vty *vty)
if (isspace ((int) vty->buf[vty->length - 1]))
vector_set (vline, '\0');
- describe = cmd_describe_command (vline, vty, &ret);
+ describe = cmd_describe_command (vline, vty->node, &ret);
- vty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vty, "%s", VTY_NEWLINE);
/* Ambiguous error. */
switch (ret)
{
case CMD_ERR_AMBIGUOUS:
- vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ uty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE);
goto out;
break;
case CMD_ERR_NO_MATCH:
- vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
+ uty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE);
goto out;
break;
}
@@ -1062,18 +1231,18 @@ vty_describe_command (struct vty *vty)
}
if (!desc->str)
- vty_out (vty, " %-s%s",
+ uty_out (vty, " %-s%s",
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
VTY_NEWLINE);
else if (desc_width >= strlen (desc->str))
- vty_out (vty, " %-*s %s%s", width,
+ 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
- vty_out (vty, " %-*s %s%s", width
+ uty_out (vty, " %-*s %s%s", width
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
desc->str ? desc->str : "", VTY_NEWLINE);
#endif /* 0 */
@@ -1082,11 +1251,11 @@ vty_describe_command (struct vty *vty)
if ((desc = desc_cr))
{
if (!desc->str)
- vty_out (vty, " %-s%s",
+ uty_out (vty, " %-s%s",
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
VTY_NEWLINE);
else if (desc_width >= strlen (desc->str))
- vty_out (vty, " %-*s %s%s", width,
+ uty_out (vty, " %-*s %s%s", width,
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
desc->str, VTY_NEWLINE);
else
@@ -1105,6 +1274,7 @@ out:
static void
vty_clear_buf (struct vty *vty)
{
+ ASSERTLOCKED
memset (vty->buf, 0, vty->max);
}
@@ -1112,9 +1282,10 @@ vty_clear_buf (struct vty *vty)
static void
vty_stop_input (struct vty *vty)
{
+ ASSERTLOCKED
vty->cp = vty->length = 0;
vty_clear_buf (vty);
- vty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vty, "%s", VTY_NEWLINE);
switch (vty->node)
{
@@ -1137,7 +1308,7 @@ vty_stop_input (struct vty *vty)
case KEYCHAIN_KEY_NODE:
case MASC_NODE:
case VTY_NODE:
- vty_config_unlock (vty);
+ uty_config_unlock (vty);
vty->node = ENABLE_NODE;
break;
default:
@@ -1156,6 +1327,8 @@ vty_hist_add (struct vty *vty)
{
int index;
+ ASSERTLOCKED
+
if (vty->length == 0)
return;
@@ -1191,46 +1364,48 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
#ifdef TELNET_OPTION_DEBUG
int i;
+ ASSERTLOCKED
+
for (i = 0; i < nbytes; i++)
{
switch (buf[i])
{
case IAC:
- vty_out (vty, "IAC ");
+ uty_out (vty, "IAC ");
break;
case WILL:
- vty_out (vty, "WILL ");
+ uty_out (vty, "WILL ");
break;
case WONT:
- vty_out (vty, "WONT ");
+ uty_out (vty, "WONT ");
break;
case DO:
- vty_out (vty, "DO ");
+ uty_out (vty, "DO ");
break;
case DONT:
- vty_out (vty, "DONT ");
+ uty_out (vty, "DONT ");
break;
case SB:
- vty_out (vty, "SB ");
+ uty_out (vty, "SB ");
break;
case SE:
- vty_out (vty, "SE ");
+ uty_out (vty, "SE ");
break;
case TELOPT_ECHO:
- vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
+ uty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
break;
case TELOPT_SGA:
- vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
+ uty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
break;
case TELOPT_NAWS:
- vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
+ uty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
break;
default:
- vty_out (vty, "%x ", buf[i]);
+ uty_out (vty, "%x ", buf[i]);
break;
}
}
- vty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vty, "%s", VTY_NEWLINE);
#endif /* TELNET_OPTION_DEBUG */
@@ -1255,11 +1430,11 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
{
case TELOPT_NAWS:
if (vty->sb_len != TELNET_NAWS_SB_LEN)
- zlog_warn("RFC 1073 violation detected: telnet NAWS option "
+ 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)
- zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, "
+ 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
@@ -1267,7 +1442,7 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
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
- vty_out(vty, "TELNET NAWS window size negotiation completed: "
+ uty_out(vty, "TELNET NAWS window size negotiation completed: "
"width %d, height %d%s",
vty->width, vty->height, VTY_NEWLINE);
#endif
@@ -1309,7 +1484,7 @@ vty_execute (struct vty *vty)
vty->cp = vty->length = 0;
vty_clear_buf (vty);
- if (vty->status != VTY_CLOSE )
+ if (vty->status != VTY_CLOSE && ret != CMD_QUEUED)
vty_prompt (vty);
return ret;
@@ -1324,6 +1499,7 @@ vty_execute (struct vty *vty)
static void
vty_escape_map (unsigned char c, struct vty *vty)
{
+ ASSERTLOCKED
switch (c)
{
case ('A'):
@@ -1350,22 +1526,51 @@ vty_escape_map (unsigned char c, struct vty *vty)
static void
vty_buffer_reset (struct vty *vty)
{
+ ASSERTLOCKED
buffer_reset (vty->obuf);
vty_prompt (vty);
vty_redraw_line (vty);
}
-/* Read data via vty socket. */
+/* 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 i;
- int nbytes;
- unsigned char buf[VTY_READ_BUFSIZ];
-
int vty_sock = THREAD_FD (thread);
struct vty *vty = THREAD_ARG (thread);
+ int result ;
+
+ LOCK
+
vty->t_read = NULL;
+ result = uty_read(vty, vty_sock);
+
+ UNLOCK
+ return result;
+}
+
+static int
+uty_read (struct vty *vty, int vty_sock)
+{
+ int i;
+ int nbytes;
+ unsigned char buf[VTY_READ_BUFSIZ];
/* Read raw data from socket */
if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0)
@@ -1378,7 +1583,7 @@ vty_read (struct thread *thread)
return 0;
}
vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("%s: read error on vty client fd %d, closing: %s",
+ uzlog(NULL, LOG_WARNING, "%s: read error on vty client fd %d, closing: %s",
__func__, vty->fd, safe_strerror(errno));
}
buffer_reset(vty->obuf);
@@ -1526,7 +1731,7 @@ vty_read (struct thread *thread)
break;
case '\n':
case '\r':
- vty_out (vty, "%s", VTY_NEWLINE);
+ uty_out (vty, "%s", VTY_NEWLINE);
vty_execute (vty);
break;
case '\t':
@@ -1556,32 +1761,66 @@ vty_read (struct thread *thread)
/* Check status. */
if (vty->status == VTY_CLOSE)
- vty_close (vty);
+ uty_close (vty);
else
{
vty_event (VTY_WRITE, vty_sock, vty);
vty_event (VTY_READ, vty_sock, vty);
}
+
return 0;
}
-/* Flush buffer to the vty. */
+/* 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->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 erase;
- buffer_status_t flushrc;
int vty_sock = THREAD_FD (thread);
struct vty *vty = THREAD_ARG (thread);
+ int result;
+ LOCK
vty->t_write = NULL;
- /* Tempolary disable read thread. */
+ /* 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));
@@ -1601,14 +1840,14 @@ vty_flush (struct thread *thread)
{
case BUFFER_ERROR:
vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("buffer_flush failed on vty client fd %d, closing",
+ uzlog(NULL, LOG_WARNING, "buffer_flush failed on vty client fd %d, closing",
vty->fd);
buffer_reset(vty->obuf);
- vty_close(vty);
- return 0;
+ uty_close(vty);
+ break;
case BUFFER_EMPTY:
if (vty->status == VTY_CLOSE)
- vty_close (vty);
+ uty_close (vty);
else
{
vty->status = VTY_NORMAL;
@@ -1633,10 +1872,10 @@ 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->fd = vty_sock;
- vty->type = VTY_TERM;
+ vty = vty_new (vty_sock, VTY_TERM);
vty->address = sockunion_su2str (su);
if (no_password_check)
{
@@ -1672,17 +1911,17 @@ vty_create (int vty_sock, union sockunion *su)
/* Vty is not available if password isn't set. */
if (host.password == NULL && host.password_encrypt == NULL)
{
- vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
+ uty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
vty->status = VTY_CLOSE;
- vty_close (vty);
+ uty_close (vty);
return NULL;
}
}
/* Say hello to the world. */
- vty_hello (vty);
+ uty_hello (vty);
if (! no_password_check)
- vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+ uty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
/* Setting up terminal. */
vty_will_echo (vty);
@@ -1701,22 +1940,45 @@ vty_create (int vty_sock, union sockunion *su)
return vty;
}
-/* Accept connection from the network. */
+/* 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;
- int accept_sock;
struct prefix *p = NULL;
struct access_list *acl = NULL;
char *bufp;
- accept_sock = THREAD_FD (thread);
-
/* We continue hearing vty socket. */
vty_event (VTY_SERV, accept_sock, NULL);
@@ -1726,7 +1988,7 @@ vty_accept (struct thread *thread)
vty_sock = sockunion_accept (accept_sock, &su);
if (vty_sock < 0)
{
- zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
+ uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s", safe_strerror (errno));
return -1;
}
set_nonblocking(vty_sock);
@@ -1740,7 +2002,7 @@ vty_accept (struct thread *thread)
(access_list_apply (acl, p) == FILTER_DENY))
{
char *buf;
- zlog (NULL, LOG_INFO, "Vty connection refused from %s",
+ uzlog (NULL, LOG_INFO, "Vty connection refused from %s",
(buf = sockunion_su2str (&su)));
free (buf);
close (vty_sock);
@@ -1749,7 +2011,6 @@ vty_accept (struct thread *thread)
vty_event (VTY_SERV, accept_sock, NULL);
prefix_free (p);
-
return 0;
}
}
@@ -1762,7 +2023,7 @@ vty_accept (struct thread *thread)
(access_list_apply (acl, p) == FILTER_DENY))
{
char *buf;
- zlog (NULL, LOG_INFO, "Vty connection refused from %s",
+ uzlog (NULL, LOG_INFO, "Vty connection refused from %s",
(buf = sockunion_su2str (&su)));
free (buf);
close (vty_sock);
@@ -1771,7 +2032,6 @@ vty_accept (struct thread *thread)
vty_event (VTY_SERV, accept_sock, NULL);
prefix_free (p);
-
return 0;
}
}
@@ -1783,10 +2043,10 @@ vty_accept (struct thread *thread)
ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY,
(char *) &on, sizeof (on));
if (ret < 0)
- zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
+ uzlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s",
safe_strerror (errno));
- zlog (NULL, LOG_INFO, "Vty connection from %s",
+ uzlog (NULL, LOG_INFO, "Vty connection from %s",
(bufp = sockunion_su2str (&su)));
if (bufp)
XFREE (MTYPE_TMP, bufp);
@@ -1807,6 +2067,8 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
int sock;
char port_str[BUFSIZ];
+ ASSERTLOCKED
+
memset (&req, 0, sizeof (struct addrinfo));
req.ai_flags = AI_PASSIVE;
req.ai_family = AF_UNSPEC;
@@ -1862,6 +2124,7 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port)
}
#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)
@@ -1871,6 +2134,8 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
int accept_sock;
void* naddr=NULL;
+ ASSERTLOCKED
+
memset (&su, 0, sizeof (union sockunion));
su.sa.sa_family = family;
if(addr)
@@ -1888,11 +2153,11 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
switch(inet_pton(family,addr,naddr))
{
case -1:
- zlog_err("bad address %s",addr);
+ uzlog(NULL, LOG_ERR, "bad address %s",addr);
naddr=NULL;
break;
case 0:
- zlog_err("error translating address %s: %s",addr,safe_strerror(errno));
+ uzlog(NULL, LOG_ERR, "error translating address %s: %s",addr,safe_strerror(errno));
naddr=NULL;
}
@@ -1909,7 +2174,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
ret = sockunion_bind (accept_sock, &su, port, naddr);
if (ret < 0)
{
- zlog_warn("can't bind socket");
+ uzlog(NULL, LOG_WARNING, "can't bind socket");
close (accept_sock); /* Avoid sd leak. */
return;
}
@@ -1926,6 +2191,7 @@ vty_serv_sock_family (const char* addr, unsigned short port, int family)
/* 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. */
@@ -1941,6 +2207,8 @@ vty_serv_un (const char *path)
mode_t old_mask;
struct zprivs_ids_t ids;
+ ASSERTLOCKED
+
/* First of all, unlink existing socket */
unlink (path);
@@ -1951,7 +2219,7 @@ vty_serv_un (const char *path)
sock = socket (AF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
{
- zlog_err("Cannot create unix stream socket: %s", safe_strerror(errno));
+ uzlog(NULL, LOG_ERR, "Cannot create unix stream socket: %s", safe_strerror(errno));
return;
}
@@ -1968,7 +2236,7 @@ vty_serv_un (const char *path)
ret = bind (sock, (struct sockaddr *) &serv, len);
if (ret < 0)
{
- zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno));
+ ulog(NULL, LOG_ERR, "Cannot bind path %s: %s", path, safe_strerror(errno));
close (sock); /* Avoid sd leak. */
return;
}
@@ -1976,7 +2244,7 @@ vty_serv_un (const char *path)
ret = listen (sock, 5);
if (ret < 0)
{
- zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno));
+ uzlog(NULL, LOG_ERR, "listen(fd %d) failed: %s", sock, safe_strerror(errno));
close (sock); /* Avoid sd leak. */
return;
}
@@ -1990,7 +2258,7 @@ vty_serv_un (const char *path)
/* set group of socket */
if ( chown (path, -1, ids.gid_vty) )
{
- zlog_err ("vty_serv_un: could chown socket, %s",
+ uzlog (NULL, LOG_ERR, "vty_serv_un: could chown socket, %s",
safe_strerror (errno) );
}
}
@@ -2000,16 +2268,36 @@ vty_serv_un (const char *path)
/* #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;
+ 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;
- accept_sock = THREAD_FD (thread);
+ ASSERTLOCKED
vty_event (VTYSH_SERV, accept_sock, NULL);
@@ -2021,13 +2309,13 @@ vtysh_accept (struct thread *thread)
if (sock < 0)
{
- zlog_warn ("can't accept vty socket : %s", safe_strerror (errno));
+ uzlog (NULL, LOG_WARNING, "can't accept vty socket : %s", safe_strerror (errno));
return -1;
}
if (set_nonblocking(sock) < 0)
{
- zlog_warn ("vtysh_accept: could not set vty socket %d to non-blocking,"
+ 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;
@@ -2050,6 +2338,8 @@ vtysh_accept (struct thread *thread)
static int
vtysh_flush(struct vty *vty)
{
+ ASSERTLOCKED
+
switch (buffer_flush_available(vty->obuf, vty->fd))
{
case BUFFER_PENDING:
@@ -2057,9 +2347,9 @@ vtysh_flush(struct vty *vty)
break;
case BUFFER_ERROR:
vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("%s: write error to fd %d, closing", __func__, vty->fd);
+ uzlog(NULL, LOG_WARNING, "%s: write error to fd %d, closing", __func__, vty->fd);
buffer_reset(vty->obuf);
- vty_close(vty);
+ uty_close(vty);
return -1;
break;
case BUFFER_EMPTY:
@@ -2068,21 +2358,48 @@ vtysh_flush(struct vty *vty)
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->t_read = NULL;
+ result = uty_read(vty, vty_soc);
+
+ UNLOCK
+ return result;
+}
+
+static int
+utysh_read (struct vty *vty, int sock)
+{
int ret;
- int sock;
int nbytes;
- struct vty *vty;
unsigned char buf[VTY_READ_BUFSIZ];
unsigned char *p;
u_char header[4] = {0, 0, 0, 0};
- sock = THREAD_FD (thread);
- vty = THREAD_ARG (thread);
- vty->t_read = NULL;
-
if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0)
{
if (nbytes < 0)
@@ -2093,11 +2410,11 @@ vtysh_read (struct thread *thread)
return 0;
}
vty->monitor = 0; /* disable monitoring to avoid infinite recursion */
- zlog_warn("%s: read failed on vtysh client fd %d, closing: %s",
+ uzlog(NULL, LOG_WARNING, "%s: read failed on vtysh client fd %d, closing: %s",
__func__, sock, safe_strerror(errno));
}
buffer_reset(vty->obuf);
- vty_close (vty);
+ uty_close (vty);
#ifdef VTYSH_DEBUG
printf ("close vtysh\n");
#endif /* VTYSH_DEBUG */
@@ -2139,13 +2456,32 @@ vtysh_read (struct thread *thread)
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;
}
@@ -2155,6 +2491,8 @@ vtysh_write (struct thread *thread)
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)
{
@@ -2174,6 +2512,8 @@ vty_serv_sock (const char *addr, unsigned short port, const char *path)
#ifdef VTYSH
vty_serv_un (path);
#endif /* VTYSH */
+
+ UNLOCK
}
/* Close vty interface. Warning: call this only from functions that
@@ -2183,8 +2523,18 @@ vty_serv_sock (const char *addr, unsigned short port, const char *path)
void
vty_close (struct vty *vty)
{
+ LOCK
+ uty_close(vty);
+ UNLOCK
+}
+
+static void
+uty_close (struct vty *vty)
+{
int i;
+ ASSERTLOCKED
+
/* Cancel threads.*/
if (vty->t_read)
thread_cancel (vty->t_read);
@@ -2192,6 +2542,17 @@ vty_close (struct vty *vty)
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)
+ {
+ qtimer_free(vty->qtr);
+ vty->qtr = NULL;
+ }
/* Flush buffer. */
buffer_flush_all (vty->obuf, vty->fd);
@@ -2217,29 +2578,48 @@ vty_close (struct vty *vty)
XFREE (MTYPE_VTY, vty->buf);
/* Check configure. */
- vty_config_unlock (vty);
+ uty_config_unlock (vty);
/* OK free vty. */
XFREE (MTYPE_VTY, vty);
}
-/* When time out occur output message then close connection. */
+/* 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)
{
- struct vty *vty;
-
- vty = THREAD_ARG (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);
- vty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
+ uty_out (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
/* Close connection. */
vty->status = VTY_CLOSE;
- vty_close (vty);
+ uty_close (vty);
return 0;
}
@@ -2251,14 +2631,14 @@ vty_read_file (FILE *confp)
int ret;
struct vty *vty;
- vty = vty_new ();
- vty->fd = 0; /* stdout */
- vty->type = VTY_TERM;
+ vty = vty_new (0, VTY_TERM); /* stdout */
vty->node = CONFIG_NODE;
/* Execute configuration file */
ret = config_from_file (vty, confp);
+ LOCK
+
if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) )
{
switch (ret)
@@ -2272,11 +2652,12 @@ vty_read_file (FILE *confp)
}
fprintf (stderr, "Error occured during reading below line.\n%s\n",
vty->buf);
- vty_close (vty);
+ uty_close (vty);
exit (1);
}
- vty_close (vty);
+ uty_close (vty);
+ UNLOCK
}
static FILE *
@@ -2409,7 +2790,7 @@ vty_read_config (char *config_file,
{
ret = stat (integrate_default, &conf_stat);
if (ret >= 0)
- return;
+ return;
}
#endif /* VTYSH */
@@ -2454,18 +2835,19 @@ vty_log (const char *level, const char *proto_str,
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)
- if (vty->monitor)
- {
- va_list ac;
- va_copy(ac, va);
- vty_log_out (vty, level, proto_str, format, ctl, ac);
- va_end(ac);
- }
+ 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);
+ }
}
/* Async-signal-safe version of vty_log for fixed strings. */
@@ -2497,17 +2879,32 @@ vty_log_fixed (const char *buf, size_t len)
int
vty_config_lock (struct vty *vty)
{
+ int result;
+ LOCK
if (vty_config == 0)
{
vty->config = 1;
vty_config = 1;
}
- return vty->config;
+ result = vty->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->config == 1)
{
vty->config = 0;
@@ -2516,14 +2913,23 @@ vty_config_unlock (struct vty *vty)
return vty->config;
}
-/* Master of the threads. */
-static struct thread_master *master;
-
static void
vty_event (enum 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 event event, int sock, struct vty *vty)
+ {
struct thread *vty_serv_thread;
+ ASSERTLOCKED
+
switch (event)
{
case VTY_SERV:
@@ -2556,7 +2962,7 @@ vty_event (enum event event, int sock, struct vty *vty)
case VTY_WRITE:
if (! vty->t_write)
vty->t_write = thread_add_write (master, vty_flush, vty, sock);
- break;
+ break;
case VTY_TIMEOUT_RESET:
if (vty->t_timeout)
{
@@ -2572,7 +2978,71 @@ vty_event (enum event event, int sock, struct vty *vty)
}
}
-DEFUN (config_who,
+/* 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->v_timeout)
+ {
+ qtimer_set(vty->qtr, qt_add_monotonic(QTIME(vty->v_timeout)), NULL) ;
+ }
+ else
+ {
+ qtimer_unset(vty->qtr);
+ }
+ break;
+ }
+}
+
+DEFUN_CALL (config_who,
config_who_cmd,
"who",
"Display who is on vty\n")
@@ -2580,22 +3050,26 @@ DEFUN (config_who,
unsigned int i;
struct vty *v;
+ LOCK
for (i = 0; i < vector_active (vtyvec); i++)
if ((v = vector_slot (vtyvec, i)) != NULL)
- vty_out (vty, "%svty[%d] connected from %s.%s",
+ uty_out (vty, "%svty[%d] connected from %s.%s",
v->config ? "*" : " ",
i, v->address, VTY_NEWLINE);
+ UNLOCK
return CMD_SUCCESS;
}
/* Move to vty configuration mode. */
-DEFUN (line_vty,
+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;
}
@@ -2605,6 +3079,8 @@ 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)
@@ -2619,11 +3095,11 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str)
vty->v_timeout = timeout;
vty_event (VTY_TIMEOUT_RESET, 0, vty);
-
+ UNLOCK
return CMD_SUCCESS;
}
-DEFUN (exec_timeout_min,
+DEFUN_CALL (exec_timeout_min,
exec_timeout_min_cmd,
"exec-timeout <0-35791>",
"Set timeout value\n"
@@ -2632,7 +3108,7 @@ DEFUN (exec_timeout_min,
return exec_timeout (vty, argv[0], NULL);
}
-DEFUN (exec_timeout_sec,
+DEFUN_CALL (exec_timeout_sec,
exec_timeout_sec_cmd,
"exec-timeout <0-35791> <0-2147483>",
"Set the EXEC timeout\n"
@@ -2642,7 +3118,7 @@ DEFUN (exec_timeout_sec,
return exec_timeout (vty, argv[0], argv[1]);
}
-DEFUN (no_exec_timeout,
+DEFUN_CALL (no_exec_timeout,
no_exec_timeout_cmd,
"no exec-timeout",
NO_STR
@@ -2652,61 +3128,71 @@ DEFUN (no_exec_timeout,
}
/* Set vty access class. */
-DEFUN (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 (no_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])))
{
- vty_out (vty, "Access-class is not currently applied to vty%s",
+ uty_out (vty, "Access-class is not currently applied to vty%s",
VTY_NEWLINE);
- return CMD_WARNING;
+ result = CMD_WARNING;
+ }
+ else
+ {
+ XFREE(MTYPE_VTY, vty_accesslist_name);
+ vty_accesslist_name = NULL;
}
- XFREE(MTYPE_VTY, vty_accesslist_name);
-
- vty_accesslist_name = NULL;
-
- return CMD_SUCCESS;
+ UNLOCK
+ return result;
}
#ifdef HAVE_IPV6
/* Set vty access class. */
-DEFUN (vty_ipv6_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 (no_vty_ipv6_access_class,
+DEFUN_CALL (no_vty_ipv6_access_class,
no_vty_ipv6_access_class_cmd,
"no ipv6 access-class [WORD]",
NO_STR
@@ -2714,112 +3200,135 @@ DEFUN (no_vty_ipv6_access_class,
"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])))
{
- vty_out (vty, "IPv6 access-class is not currently applied to vty%s",
+ uty_out (vty, "IPv6 access-class is not currently applied to vty%s",
VTY_NEWLINE);
- return CMD_WARNING;
+ result = CMD_WARNING;
}
+ else
+ {
+ XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
- XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
-
- vty_ipv6_accesslist_name = NULL;
+ vty_ipv6_accesslist_name = NULL;
+ }
+ UNLOCK
return CMD_SUCCESS;
}
#endif /* HAVE_IPV6 */
/* vty login. */
-DEFUN (vty_login,
+DEFUN_CALL (vty_login,
vty_login_cmd,
"login",
"Enable password checking\n")
{
+ LOCK
no_password_check = 0;
+ UNLOCK
return CMD_SUCCESS;
}
-DEFUN (no_vty_login,
+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 (vty_restricted_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 (vty_no_restricted_mode,
+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 (service_advanced_vty,
+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 (no_service_advanced_vty,
+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 (terminal_monitor,
+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->monitor = 1;
+ UNLOCK
return CMD_SUCCESS;
}
-DEFUN (terminal_no_monitor,
+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->monitor = 0;
+ UNLOCK
return CMD_SUCCESS;
}
-ALIAS (terminal_no_monitor,
+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 (show_history,
+DEFUN_CALL (show_history,
show_history_cmd,
"show history",
SHOW_STR
@@ -2827,6 +3336,8 @@ DEFUN (show_history,
{
int index;
+ LOCK
+
for (index = vty->hindex + 1; index != vty->hindex;)
{
if (index == VTY_MAXHIST)
@@ -2836,11 +3347,12 @@ DEFUN (show_history,
}
if (vty->hist[index] != NULL)
- vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
+ uty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE);
index++;
}
+ UNLOCK
return CMD_SUCCESS;
}
@@ -2892,25 +3404,49 @@ struct cmd_node vty_node =
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;
- vty_close (vty);
+ uty_close (vty);
}
- 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);
- }
+ 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;
@@ -2954,25 +3490,116 @@ vty_get_cwd ()
int
vty_shell (struct vty *vty)
{
+ LOCK
+ int result;
+ result = uty_shell (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)
{
- return vty->type == VTY_SHELL_SERV ? 1 : 0;
+ LOCK
+ int result;
+ result = ((vty->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->type;
+ 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
+ return result;
+}
+
+void
+vty_set_lines(struct vty *vty, int lines)
+{
+ LOCK
+ vty->lines = lines;
+ UNLOCK
+}
+
+/* qpthreads: Install vty's own commands like `who' command. */
+void
+vty_init_r (qpn_nexus cli_n, qpn_nexus bgp_n)
+{
+ cli_nexus = cli_n;
+ bgp_nexus = bgp_n;
+ vty_mutex = qpt_mutex_init(vty_mutex, qpt_mutex_quagga);
}
-/* Install vty's own commands like `who' command. */
+/* threads: Install vty's own commands like `who' command. */
void
vty_init (struct thread_master *master_thread)
{
+ LOCK
+
/* For further configuration read, preserve current directory. */
vty_save_cwd ();
@@ -3014,18 +3641,30 @@ 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)
{
- vty_reset ();
+ uty_reset ();
vector_free (vtyvec);
vector_free (Vvty_serv_thread);
}
+ UNLOCK
+
+ if (vty_mutex)
+ vty_mutex = qpt_mutex_destroy(vty_mutex, 1);
}
+
+#undef LOCK
+#undef UNLOCK
+#undef ASSERTLOCKED
diff --git a/lib/vty.h b/lib/vty.h
index 1e7f1261..3c56a0b3 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -23,6 +23,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "thread.h"
#include "log.h"
+#include "qpthreads.h"
+#include "qpselect.h"
+#include "qtimers.h"
+#include "qpnexus.h"
#define VTY_BUFSIZ 512
#define VTY_MAXHIST 20
@@ -30,7 +34,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
/* VTY struct. */
struct vty
{
- /* File descripter of this vty. */
+ /* File descriptor of this vty. */
int fd;
/* Is this vty connect to file or not */
@@ -39,7 +43,7 @@ struct vty
/* Node status of this vty */
int node;
- /* What address is this vty comming from. */
+ /* What address is this vty coming from. */
char *address;
/* Failure count */
@@ -112,11 +116,14 @@ struct vty
int config;
/* Read and write thread. */
+
+ qps_file qf;
struct thread *t_read;
struct thread *t_write;
/* Timeout seconds and thread. */
unsigned long v_timeout;
+ qtimer qtr;
struct thread *t_timeout;
};
@@ -205,13 +212,20 @@ do {
/* 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);
extern void vty_init (struct thread_master *);
extern void vty_init_vtysh (void);
extern void vty_terminate (void);
extern void vty_reset (void);
-extern struct vty *vty_new (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) ;
@@ -228,6 +242,14 @@ 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 *);
+extern int vty_get_status(struct vty *);
+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. */
diff --git a/lib/zassert.h b/lib/zassert.h
index 79126760..424688b9 100644
--- a/lib/zassert.h
+++ b/lib/zassert.h
@@ -8,6 +8,18 @@
extern void _zlog_assert_failed (const char *assertion, const char *file,
unsigned int line, const char *function)
__attribute__ ((noreturn));
+extern void _zlog_abort_mess (const char *mess, const char *file,
+ unsigned int line, const char *function)
+ __attribute__ ((noreturn));
+
+extern void _zlog_abort_errno (const char *mess, const char *file,
+ unsigned int line, const char *function)
+ __attribute__ ((noreturn));
+
+extern void _zlog_abort_err (const char *mess, int err, const char *file,
+ unsigned int line, const char *function)
+ __attribute__ ((noreturn));
+
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define __ASSERT_FUNCTION __func__
@@ -21,7 +33,45 @@ extern void _zlog_assert_failed (const char *assertion, const char *file,
(_zlog_assert_failed(#EX, __FILE__, __LINE__, \
__ASSERT_FUNCTION), 0)))
+/* Implicitly *permanent* assert() -- irrespective of NDEBUG */
#undef assert
#define assert(EX) zassert(EX)
+/* Explicitly permanent assert() */
+#define passert(EX) zassert(EX)
+
+/* NDEBUG time assert() */
+#ifndef NDEBUG
+#define dassert(EX) zassert(EX)
+#else
+#define dassert(EX)
+#endif
+
+/* TODO: implement _zlog_abort() to give required messages */
+
+/* Abort with message */
+#define zabort(MS) _zlog_assert_failed(MS, __FILE__, __LINE__, \
+ __ASSERT_FUNCTION)
+
+/* Abort with message and errno and strerror() thereof */
+#define zabort_errno(MS) _zlog_abort_errno(MS, __FILE__, __LINE__, \
+ __ASSERT_FUNCTION)
+
+/* Abort with message and given error and strerror() thereof */
+#define zabort_err(MS, ERR) _zlog_abort_err(MS, ERR, __FILE__, __LINE__, \
+ __ASSERT_FUNCTION)
+
+/*==============================================================================
+ * Compile time CONFIRM gizmo
+ *
+ * Two forms: CONFIRM(e) for use at top (file) level
+ * confirm(e) for use inside compound statements
+ */
+#ifndef CONFIRM
+
+ #define CONFIRM(e) extern void CONFIRMATION(char CONFIRM[(e) ? 1 : -1]) ;
+ #define confirm(e) { CONFIRM(e) }
+
+#endif
+
#endif /* _QUAGGA_ASSERT_H */
diff --git a/tests/test-privs.c b/tests/test-privs.c
index a888ea0f..568fe79a 100644
--- a/tests/test-privs.c
+++ b/tests/test-privs.c
@@ -125,26 +125,42 @@ main (int argc, char **argv)
#define PRIV_STATE() \
((test_privs.current_state() == ZPRIVS_RAISED) ? "Raised" : "Lowered")
- printf ("%s\n", PRIV_STATE());
+ printf ("Initial state: %s\n", PRIV_STATE());
+
+ test_privs.change(ZPRIVS_RAISE);
+ printf ("Change raise: state: %s\n", PRIV_STATE());
+
test_privs.change(ZPRIVS_RAISE);
+ printf ("Change raise: state: %s\n", PRIV_STATE());
- printf ("%s\n", PRIV_STATE());
test_privs.change(ZPRIVS_LOWER);
+ printf ("Change lower: state: %s\n", PRIV_STATE());
+
+ test_privs.change(ZPRIVS_LOWER);
+ printf ("Change lower: state: %s\n", PRIV_STATE());
- printf ("%s\n", PRIV_STATE());
+ printf ("Get ids %s\n", PRIV_STATE());
zprivs_get_ids (&ids);
/* terminate privileges */
zprivs_terminate(&test_privs);
/* but these should continue to work... */
- printf ("%s\n", PRIV_STATE());
+ printf ("Terminated state: %s\n", PRIV_STATE());
+
+ test_privs.change(ZPRIVS_RAISE);
+ printf ("Change raise: state: %s\n", PRIV_STATE());
+
test_privs.change(ZPRIVS_RAISE);
+ printf ("Change raise: state: %s\n", PRIV_STATE());
- printf ("%s\n", PRIV_STATE());
test_privs.change(ZPRIVS_LOWER);
+ printf ("Change lower: state: %s\n", PRIV_STATE());
+
+ test_privs.change(ZPRIVS_LOWER);
+ printf ("Change lower: state: %s\n", PRIV_STATE());
- printf ("%s\n", PRIV_STATE());
+ printf ("Get ids %s\n", PRIV_STATE());
zprivs_get_ids (&ids);
printf ("terminating\n");
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 3f189adb..c5e24546 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -290,7 +290,7 @@ vtysh_execute_func (const char *line, int pager)
if (vline == NULL)
return CMD_SUCCESS;
- saved_ret = ret = cmd_execute_command (vline, vty, &cmd, 1);
+ saved_ret = ret = cmd_execute_command (vline, vty, &cmd, NULL, 1);
saved_node = vty->node;
/* If command doesn't succeeded in current node, try to walk up in node tree.
@@ -300,7 +300,7 @@ vtysh_execute_func (const char *line, int pager)
&& vty->node > CONFIG_NODE)
{
vty->node = node_parent(vty->node);
- ret = cmd_execute_command (vline, vty, &cmd, 1);
+ ret = cmd_execute_command (vline, vty, &cmd, NULL, 1);
tried++;
}
@@ -398,7 +398,7 @@ vtysh_execute_func (const char *line, int pager)
return CMD_SUCCESS;
}
- ret = cmd_execute_command (vline, vty, &cmd, 1);
+ ret = cmd_execute_command (vline, vty, &cmd, , NULL, 1);
cmd_free_strvec (vline);
if (ret != CMD_SUCCESS_DAEMON)
break;
@@ -568,7 +568,7 @@ vtysh_rl_describe (void)
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- describe = cmd_describe_command (vline, vty, &ret);
+ describe = cmd_describe_command (vline, vty->node, &ret);
fprintf (stdout,"\n");
@@ -658,7 +658,7 @@ command_generator (const char *text, int state)
if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1]))
vector_set (vline, '\0');
- matched = cmd_complete_command (vline, vty, &complete_status);
+ matched = cmd_complete_command (vline, vty->node, &complete_status);
}
if (matched && matched[index])
@@ -2227,8 +2227,7 @@ void
vtysh_init_vty (void)
{
/* Make vty structure. */
- vty = vty_new ();
- vty->type = VTY_SHELL;
+ vty = vty_new (0, VTY_SHELL);
vty->node = VIEW_NODE;
/* Initialize commands. */
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index fb8a1269..bfcf8e64 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -353,9 +353,7 @@ vtysh_read_file (FILE *confp)
int ret;
struct vty *vty;
- vty = vty_new ();
- vty->fd = 0; /* stdout */
- vty->type = VTY_TERM;
+ vty = vty_new (0, VTY_TERM); /* stdout */
vty->node = CONFIG_NODE;
vtysh_execute_no_pager ("enable");