summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am6
-rw-r--r--lib/command.c112
-rw-r--r--lib/filter.c2
-rw-r--r--lib/heap.c517
-rw-r--r--lib/heap.h160
-rw-r--r--lib/log.c658
-rw-r--r--lib/log.h30
-rw-r--r--lib/memory.h6
-rw-r--r--lib/memtypes.c18
-rw-r--r--lib/mqueue.c615
-rw-r--r--lib/mqueue.h282
-rw-r--r--lib/plist.c2
-rw-r--r--lib/privs.c203
-rw-r--r--lib/privs.h4
-rw-r--r--lib/qpnexus.c120
-rw-r--r--lib/qpnexus.h86
-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.c336
-rw-r--r--lib/qtimers.h144
-rw-r--r--lib/routemap.c2
-rw-r--r--lib/vector.c62
-rw-r--r--lib/vector.h3
-rw-r--r--lib/vty.c866
-rw-r--r--lib/vty.h19
-rw-r--r--lib/zassert.h50
30 files changed, 7038 insertions, 425 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f655ac39..3184a9b6 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -12,7 +12,8 @@ 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
BUILT_SOURCES = memtypes.h route_types.h
@@ -27,7 +28,8 @@ 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
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..80b113e8 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -548,57 +548,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);
@@ -2569,8 +2569,7 @@ DEFUN (config_write_file,
}
/* Make vty for configuration file. */
- file_vty = vty_new ();
- file_vty->fd = fd;
+ file_vty = vty_new (fd);
file_vty->type = VTY_FILE;
/* Config file header print. */
@@ -3060,49 +3059,51 @@ DEFUN (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;
}
@@ -3113,7 +3114,7 @@ DEFUN (config_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;
}
@@ -3150,7 +3151,7 @@ DEFUN (config_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;
}
@@ -3238,7 +3239,7 @@ DEFUN (config_log_file,
"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,
@@ -3289,7 +3290,7 @@ DEFUN (config_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;
}
@@ -3321,8 +3322,8 @@ 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;
}
@@ -3358,7 +3359,7 @@ 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;
}
@@ -3370,7 +3371,7 @@ 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;
}
@@ -3387,10 +3388,7 @@ DEFUN_DEPRECATED (config_log_trap,
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;
}
@@ -3402,7 +3400,7 @@ 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;
}
@@ -3412,7 +3410,7 @@ DEFUN (config_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;
}
@@ -3423,7 +3421,7 @@ DEFUN (no_config_log_record_priority,
"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;
}
@@ -3435,6 +3433,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,7 +3442,9 @@ 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;
}
@@ -3454,7 +3456,7 @@ 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;
}
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..051c5164 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();
}
@@ -648,22 +768,33 @@ openzlog (const char *progname, zlog_proto_t protocol,
void
closezlog (struct zlog *zl)
{
+ LOCK
+
closelog();
if (zl->fp != NULL)
fclose (zl->fp);
XFREE (MTYPE_ZLOG, zl);
+
+ UNLOCK
}
/* Called from command.c. */
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 +802,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 +873,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;
+}
+
+int
+zlog_get_default_lvl (struct zlog *zl)
+{
+ int result = LOG_DEBUG;
+
+ LOCK
- if (zl->filename)
+ 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 +1360,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.h b/lib/memory.h
index 037efef2..fd9f1b97 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) \
diff --git a/lib/memtypes.c b/lib/memtypes.c
index 197fb88c..8146f21f 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -16,18 +16,30 @@ 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_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..7b93e4b0
--- /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) ;
+
+ last = mq->kick.signal.tail ;
+ if (last == NULL)
+ {
+ mq->kick.signal.head = mtsig ;
+ mtsig->prev = (void*)mq ;
+ }
+ else
+ {
+ 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..68757340 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,25 @@ 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(struct zebra_privs_t *zprivs)
+{
+ mx = qpt_mutex_init(mx, qpt_mutex_quagga);
+ zprivs_init(zprivs);
+}
+
+void
+zprivs_destroy_r(void)
+{
+ mx = qpt_mutex_destroy(mx, 1);
}
void
@@ -599,12 +695,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 +718,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 +735,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 +743,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 +758,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 +766,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 +775,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 +787,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 +795,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 +808,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 +819,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 +828,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..18d35d8d 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 (struct zebra_privs_t *zprivs);
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/qpnexus.c b/lib/qpnexus.c
new file mode 100644
index 00000000..01985d29
--- /dev/null
+++ b/lib/qpnexus.c
@@ -0,0 +1,120 @@
+/* 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 <string.h>
+
+#include "qpnexus.h"
+#include "memory.h"
+
+/* prototypes */
+static void* qpn_start(void* arg);
+
+/*==============================================================================
+ * Quagga Nexus Interface -- qpt_xxxx
+ *
+
+ */
+
+/* Initialise a nexus -- allocating it if required.
+ *
+ * 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)) ;
+
+ qpn->selection = qps_selection_init_new(qpn->selection);
+ qpn->pile = qtimer_pile_init_new(qpn->pile);
+
+ /* TODO mqueue initialisation */
+
+ return qpn;
+}
+
+void
+qpn_free(qpn_nexus qpn)
+{
+ /* timers and the pile */
+ qtimer qtr;
+ while ((qtr = qtimer_pile_ream(qpn->pile, 1)))
+ {
+ qtimer_free(qtr);
+ }
+
+ /* TODO: free qtn->selection */
+
+ /* TODO: free qtn->queue */
+
+ XFREE(MTYPE_QPN_NEXUS, qpn) ;
+}
+
+/* Create and execute the qpthread */
+void
+qpn_exec(qpn_nexus qpn)
+{
+ qpn->thread_id = qpt_thread_create(qpn_start, qpn, NULL) ;
+}
+
+static void*
+qpn_start(void* arg)
+{
+ qpn_nexus qpn = arg;
+ int actions;
+
+ while (!qpn->terminate)
+ {
+ qtime_mono_t now = qt_get_monotonic();
+
+ /* process timers */
+ 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) ;
+ }
+
+ /* TODO process message queue */
+ }
+
+ qpn_free(qpn);
+
+ return NULL;
+}
+
+
+
+
diff --git a/lib/qpnexus.h b/lib/qpnexus.h
new file mode 100644
index 00000000..ce546edd
--- /dev/null
+++ b/lib/qpnexus.h
@@ -0,0 +1,86 @@
+/* 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
+
+/*==============================================================================
+ * Data Structures.
+ */
+
+typedef struct qpn_nexus* qpn_nexus ;
+
+struct qpn_nexus
+{
+ /* set true to terminate the thread (eventually) */
+ int terminate;
+
+ /* thread ID */
+ qpt_thread_t thread_id;
+
+ /* pselect handler */
+ qps_selection selection;
+
+ /* timer pile */
+ qtimer_pile pile;
+
+ /* message queue */
+ mqueue_queue queue;
+
+};
+
+/*==============================================================================
+ * Functions
+ */
+
+extern qpn_nexus qpn_init_new(qpn_nexus qtn);
+extern void qpn_exec(qpn_nexus qtn);
+void qpn_free(qpn_nexus qpn);
+
+#endif /* _ZEBRA_QPNEXUS_H */
diff --git a/lib/qpselect.c b/lib/qpselect.c
new file mode 100644
index 00000000..cf1df985
--- /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_ream_keep(&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..d4aff6ec
--- /dev/null
+++ b/lib/qtimers.c
@@ -0,0 +1,336 @@
+/* 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.
+ *
+ * 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
+ *
+ * unset_pending -- NULL -- nothing pending
+ */
+
+ /* 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)
+ */
+
+ 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..3a774b38
--- /dev/null
+++ b/lib/qtimers.h
@@ -0,0 +1,144 @@
+/* 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 ;
+ heap_backlink_t backlink ;
+
+ qtimer_state_t state ;
+
+ qtime_mono_t time ;
+ qtimer_action* action ;
+ void* timer_info ;
+} ;
+
+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 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 ;
+} ;
+
+#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/vector.c b/lib/vector.c
index 8268c830..ad2af289 100644
--- a/lib/vector.c
+++ b/lib/vector.c
@@ -234,6 +234,38 @@ vector_ream(vector v, int free_structure)
} ;
/*==============================================================================
+ * Unset item.
+ */
+
+/* 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 vetor 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
+ was = NULL ;
+
+ /* Now move end back past any NULLs */
+ i = v->end ;
+ while ((i > 0) && (v->p_items[i-1] == NULL))
+ --i ;
+ v->end = i ;
+
+ return was ;
+} ;
+
+/*==============================================================================
* Inserting and deleting items.
*/
@@ -822,36 +854,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..2897cb51 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,7 @@ 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 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..45a5da0f 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,60 @@ 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 master_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;
- if (vty_shell (vty))
+ ASSERTLOCKED
+
+ 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 +203,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 +258,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 +293,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 +328,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 +352,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 +370,8 @@ vty_prompt (struct vty *vty)
struct utsname names;
const char*hostname;
+ ASSERTLOCKED
+
if (vty->type == VTY_TERM)
{
hostname = host.name;
@@ -289,7 +380,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 +389,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 +398,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 +407,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 +416,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 +426,30 @@ 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)
{
- struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty));
+ struct vty *vty = XCALLOC (MTYPE_VTY, sizeof (struct vty));
- new->obuf = buffer_new(0); /* Use default buffer size. */
- new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
- new->max = VTY_BUFSIZ;
+ vty->obuf = buffer_new(0); /* Use default buffer size. */
+ vty->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ);
+ vty->max = VTY_BUFSIZ;
+ vty->fd = fd;
- return new;
+ if (master_nexus)
+ {
+ vty->qf = qps_file_init_new(vty->qf, NULL);
+ qps_add_file(master_nexus->selection, vty->qf, vty->fd, vty);
+ vty->qtr = qtimer_init_new(vty->qtr, master_nexus->pile, vty_timeout_r, vty);
+ }
+
+ return vty;
}
/* Authentication of vty */
@@ -357,6 +461,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 +506,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 +528,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 +545,19 @@ vty_command (struct vty *vty, char *buf)
GETRUSAGE(&before);
#endif /* CONSUMED_TIME_CHECK */
+ UNLOCK
ret = cmd_execute_command (vline, vty, NULL, 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 +567,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);
@@ -484,6 +591,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 +603,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 +618,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 +637,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 +654,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 +669,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 +681,7 @@ vty_forward_char (struct vty *vty)
static void
vty_backward_char (struct vty *vty)
{
+ ASSERTLOCKED
if (vty->cp > 0)
{
vty->cp--;
@@ -575,6 +693,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 +702,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 +717,8 @@ vty_history_print (struct vty *vty)
{
int length;
+ ASSERTLOCKED
+
vty_kill_line_from_beginning (vty);
/* Get previous line from history buffer */
@@ -614,6 +736,8 @@ vty_next_line (struct vty *vty)
{
int try_index;
+ ASSERTLOCKED
+
if (vty->hp == vty->hindex)
return;
@@ -639,6 +763,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 +783,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 +792,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 +804,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 +813,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 +826,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 +837,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 +866,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 +885,8 @@ vty_delete_char (struct vty *vty)
int i;
int size;
+ ASSERTLOCKED
+
if (vty->length == 0)
{
vty_down_level (vty);
@@ -782,6 +916,7 @@ vty_delete_char (struct vty *vty)
static void
vty_delete_backward_char (struct vty *vty)
{
+ ASSERTLOCKED
if (vty->cp == 0)
return;
@@ -796,6 +931,8 @@ vty_kill_line (struct vty *vty)
int i;
int size;
+ ASSERTLOCKED
+
size = vty->length - vty->cp;
if (size == 0)
@@ -814,6 +951,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 +960,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 +971,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 +984,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 +1017,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;
@@ -895,11 +1040,11 @@ vty_complete_command (struct vty *vty)
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 +1074,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 +1102,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 +1125,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 +1139,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 '> ?'. */
@@ -1012,17 +1161,17 @@ vty_describe_command (struct vty *vty)
describe = cmd_describe_command (vline, vty, &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 +1211,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 +1231,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 +1254,7 @@ out:
static void
vty_clear_buf (struct vty *vty)
{
+ ASSERTLOCKED
memset (vty->buf, 0, vty->max);
}
@@ -1112,9 +1262,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 +1288,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 +1307,8 @@ vty_hist_add (struct vty *vty)
{
int index;
+ ASSERTLOCKED
+
if (vty->length == 0)
return;
@@ -1191,46 +1344,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 +1410,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 +1422,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
@@ -1324,6 +1479,7 @@ vty_execute (struct vty *vty)
static void
vty_escape_map (unsigned char c, struct vty *vty)
{
+ ASSERTLOCKED
switch (c)
{
case ('A'):
@@ -1350,22 +1506,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 +1563,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 +1711,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 +1741,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 +1820,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,9 +1852,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 = vty_new (vty_sock);
vty->type = VTY_TERM;
vty->address = sockunion_su2str (su);
if (no_password_check)
@@ -1672,17 +1892,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 +1921,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 +1969,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 +1983,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 +1992,6 @@ vty_accept (struct thread *thread)
vty_event (VTY_SERV, accept_sock, NULL);
prefix_free (p);
-
return 0;
}
}
@@ -1762,7 +2004,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 +2013,6 @@ vty_accept (struct thread *thread)
vty_event (VTY_SERV, accept_sock, NULL);
prefix_free (p);
-
return 0;
}
}
@@ -1783,10 +2024,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 +2048,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 +2105,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 +2115,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 +2134,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 +2155,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 +2172,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 +2188,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 +2200,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 +2217,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 +2225,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 +2239,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 +2249,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 +2290,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 +2319,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 +2328,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 +2339,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 +2391,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 +2437,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 +2472,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 +2493,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 +2504,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 +2523,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 +2559,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 +2612,15 @@ vty_read_file (FILE *confp)
int ret;
struct vty *vty;
- vty = vty_new ();
- vty->fd = 0; /* stdout */
+ vty = vty_new (0); /* stdout */
vty->type = VTY_TERM;
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 +2634,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 +2772,7 @@ vty_read_config (char *config_file,
{
ret = stat (integrate_default, &conf_stat);
if (ret >= 0)
- return;
+ return;
}
#endif /* VTYSH */
@@ -2454,18 +2817,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 +2861,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 +2895,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 (master_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 +2944,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,6 +2960,70 @@ vty_event (enum event event, int sock, struct vty *vty)
}
}
+/* 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(master_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 (config_who,
config_who_cmd,
"who",
@@ -2605,6 +3057,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,7 +3073,7 @@ 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;
}
@@ -2892,25 +3346,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 (master_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 +3432,60 @@ 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
}
-/* Install vty's own commands like `who' command. */
+/* qpthreads: Install vty's own commands like `who' command. */
+void
+vty_init_r (void)
+{
+ master_nexus = qpn_init_new(master_nexus);
+ vty_mutex = qpt_mutex_init(vty_mutex, qpt_mutex_quagga);
+ vty_init(NULL);
+}
+
+/* create and execute our thread */
+void
+vty_exec_r(void)
+{
+ if (master_nexus)
+ qpn_exec(master_nexus);
+}
+
+/* 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 +3527,33 @@ 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 (master_nexus)
+ master_nexus->terminate = 1;
+
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..37436040 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -23,6 +23,9 @@ 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"
#define VTY_BUFSIZ 512
#define VTY_MAXHIST 20
@@ -30,7 +33,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 +42,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 +115,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 +211,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 (void);
+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);
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) ;
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 */