Commit 8305021f authored by Leigh Stoller's avatar Leigh Stoller

Some whacking of the event system. I have implemented the addressing

scheme that we discussed in email. Notifications and subscriptions now
take an "address_tuple" argument (I know, crappy name) that is a
structure that looks like this:

	char		*site;		/* Which Emulab site. God only */
	char		*expt;		/* Project and experiment IDs */
	char		*group;		/* User defined group of nodes */
	char		*host;		/* A specific host */
	char		*objtype;	/* LINK, TRAFGEN, etc ... */
        char		*objname;	/* link0, cbr0, cbr1, etc ... */
        char		*eventtype;	/* START, STOP, UP, DOWN, etc ... */

These can be a specific value, ADDRESSTUPLE_ANY if you are a
subscriber, or ADDRESSTUPLE_ALL if you are a producer. The reason for
the distinction is that you can optimize the match expression with the
extra bit of information, and the above structure can make for a
fairly lengthy match expression, which takes more time of course.
You should use address_tuple_alloc() and address_tuple_free() rather
than allocating them yourself. Note that host above is actually the
ipaddr of control interface. This turns out to be more convenient
since free nodes do not have virtual names.

Also added a new tbgen directly. This directory includes 3 programs in
the making:

tbmevd: Is the Testbed Master Event Daemon, to be run on boss and will
handle TBCONTROL events (reboot, reload, etc). It is just a shell of a
program right now, that takes the events but does not do anything
useful with them. Have not defined what the events are, and what DB
state will be modified.

tbmevc: Is the Testbed Master Event Client (akin to tmcc). It
generates TBCONTROL events which the tbmevd will pick up and do
something useful with. This program is intended to be wrapped by a
perl script that will ask the tmcd for the name of the boss (running
the event daemon).

sample-client: This is a little client to demonstrate how to connect
to the event system and use the address tuple to subscribe to events,
and then how to get information out of notifications.

Note that I have not created a proper build environment yet, so new
programs should probably go in the event dir for now, and link using
the same approach as in tbgen/GNUmakefile.in.
parent 11e0ea5f
......@@ -1032,10 +1032,11 @@ fi
if test "$enable_events" = "yes"; then
elvinfiles="event/GNUmakefile event/lib/GNUmakefile \
event/sched/GNUmakefile event/test/GNUmakefile";
eventfiles="event/GNUmakefile event/lib/GNUmakefile \
event/sched/GNUmakefile event/test/GNUmakefile \
event/tbgen/GNUmakefile event/tbgen/tbmevd.restart";
else
elvinfiles="";
eventfiles="";
fi
#
......@@ -1072,7 +1073,7 @@ fi
# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
# ./install, which can be erroneously created by make from ./install.sh.
echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
echo "configure:1076: checking for a BSD compatible install" >&5
echo "configure:1077: checking for a BSD compatible install" >&5
if test -z "$INSTALL"; then
if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
......@@ -1181,7 +1182,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
vis/GNUmakefile vis/vistopology vis/webvistopology vis/top2gif \
rc.d/GNUmakefile rc.d/2.mysql-server.sh rc.d/3.testbed.sh \
rc.d/cvsupd.sh \
$elvinfiles \
$eventfiles \
apache/GNUmakefile apache/apache.conf "
#
......
......@@ -177,10 +177,11 @@ AC_ARG_ENABLE(events,
--disable-events Disable events (requires Elvin libraries)])
if test "$enable_events" = "yes"; then
elvinfiles="event/GNUmakefile event/lib/GNUmakefile \
event/sched/GNUmakefile event/test/GNUmakefile";
eventfiles="event/GNUmakefile event/lib/GNUmakefile \
event/sched/GNUmakefile event/test/GNUmakefile \
event/tbgen/GNUmakefile event/tbgen/tbmevd.restart";
else
elvinfiles="";
eventfiles="";
fi
#
......@@ -260,7 +261,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
vis/GNUmakefile vis/vistopology vis/webvistopology vis/top2gif \
rc.d/GNUmakefile rc.d/2.mysql-server.sh rc.d/3.testbed.sh \
rc.d/cvsupd.sh \
$elvinfiles \
$eventfiles \
apache/GNUmakefile apache/apache.conf "
#
......
......@@ -8,7 +8,7 @@ SUBDIR = event
include $(OBJDIR)/Makeconf
SUBDIRS = lib sched test
SUBDIRS = lib sched tbgen
all: $(SUBDIRS)
......@@ -20,19 +20,23 @@ lib:
sched:
@$(MAKE) -C sched all
tbgen:
@$(MAKE) -C tbgen all
test:
@$(MAKE) -C test all
install:
@$(MAKE) -C lib install
@$(MAKE) -C sched install
@$(MAKE) -C test install
@$(MAKE) -C tbgen install
clean: subdir-clean
subdir-clean:
@$(MAKE) -C lib clean
@$(MAKE) -C sched clean
@$(MAKE) -C tbgen clean
@$(MAKE) -C test clean
distclean: subdir-distclean
......
......@@ -7,14 +7,14 @@
* @COPYRIGHT@
*/
static char rcsid[] = "$Id: event.c,v 1.11 2002-02-19 17:12:52 stoller Exp $";
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "event.h"
#undef TRACE
#define TRACE(fmt,...)
static char hostname[MAXHOSTNAMELEN];
......@@ -259,8 +259,6 @@ int
event_schedule(event_handle_t handle, event_notification_t notification,
struct timeval *time)
{
event_type_t type;
if (!handle || !notification || !time) {
ERROR("invalid parameter\n");
return 0;
......@@ -269,38 +267,12 @@ event_schedule(event_handle_t handle, event_notification_t notification,
TRACE("scheduling event notification %p to be sent at time (%ld, %ld)\n",
notification, time->tv_sec, time->tv_usec);
/* Change the event type to EVENT_SCHEDULE, saving the old event
type in the notification structure so the event scheduler can
change it back when the event is scheduled: */
if (event_notification_get_int32(handle, notification, "type",
(int *) &type)
== 0)
{
ERROR("could not get old type attribute from notification %p\n",
notification);
return 0;
}
if (event_notification_remove(handle, notification, "type") == 0) {
ERROR("could not remove old type attribute from notification %p\n",
notification);
return 0;
}
if (event_notification_put_int32(handle, notification, "type",
EVENT_SCHEDULE)
== 0)
{
ERROR("could not add new type attribute to notification %p\n",
notification);
return 0;
}
if (event_notification_put_int32(handle, notification, "old_type",
type)
== 0)
{
ERROR("could not add old type attribute to notification %p\n",
/*
* Add an attribute that signifies its a scheduler operation.
*/
if (! event_notification_remove(handle, notification, "SCHEDULER") ||
! event_notification_put_int32(handle, notification, "SCHEDULER", 1)) {
ERROR("could not add scheduler attribute to notification %p\n",
notification);
return 0;
}
......@@ -332,25 +304,22 @@ event_schedule(event_handle_t handle, event_notification_t notification,
/*
* Allocate an event notification. The HOST parameter specifies
* the hostname of the node that should receive the notification,
* or EVENT_HOST_ANY if the notification should go to all hosts.
* The TYPE parameter specifies the event type. Returns
* a pointer to an event notification structure if the operation
* is successful, 0 otherwise.
* Allocate an event notification. The address TUPLE defines who
* should receive the notification. Returns a pointer to an event
* notification structure if the operation is successful, 0 otherwise.
*/
event_notification_t
event_notification_alloc(event_handle_t handle, char *host, event_type_t type)
event_notification_alloc(event_handle_t handle, address_tuple_t tuple)
{
elvin_notification_t notification;
if (!handle || !host || !type) {
if (!handle || !tuple) {
ERROR("invalid paramater\n");
return NULL;
}
TRACE("allocating notification (host=\"%s\", type=%d)\n", host, type);
TRACE("allocating notification (tuple=%p)\n", tuple);
notification = elvin_notification_alloc(handle->status);
if (notification == NULL) {
......@@ -360,22 +329,23 @@ event_notification_alloc(event_handle_t handle, char *host, event_type_t type)
}
TRACE("allocated notification %p\n", notification);
/* Add hostname to notification: */
if (event_notification_put_string(handle, notification, "host", host)
== 0)
{
ERROR("could not add host attribute to notification %p\n",
notification);
return NULL;
}
/* Add type to notification: */
if (event_notification_put_int32(handle, notification, "type", type)
== 0)
{
ERROR("could not add type attribute to notification %p\n",
notification);
#define EVPUT(name, field) \
({ \
char *foo = (tuple->field ? tuple->field : ADDRESSTUPLE_ALL); \
\
event_notification_put_string(handle, notification, name, foo); \
})
/* Add the target address stuff to the notification */
if (!EVPUT("SITE", site) ||
!EVPUT("EXPT", expt) ||
!EVPUT("GROUP", group) ||
!EVPUT("HOST", host) ||
!EVPUT("OBJTYPE", objtype) ||
!EVPUT("OBJNAME", objname) ||
!EVPUT("EVENTTYPE", eventtype) ||
! event_notification_put_int32(handle, notification, "SCHEDULER", 0)) {
ERROR("could not add attributes to notification %p\n", notification);
return NULL;
}
......@@ -818,38 +788,77 @@ static void notify_callback(elvin_handle_t server,
*
* void callback(event_handle_t handle,
* event_notification_t notification,
* char *host,
* event_type_t type,
* void *data);
*
* where HANDLE is the handle to the event server, NOTIFICATION is the
* event notification, HOST and TYPE are the respective attributes of
* the event notification, and DATA is the arbitrary pointer passed to
* event notification, and DATA is the arbitrary pointer passed to
* event_subscribe. Returns a pointer to an event
* subscription structure if the operation is successful, 0 otherwise.
*/
event_subscription_t
event_subscribe(event_handle_t handle, event_notify_callback_t callback,
event_type_t type, void *data)
address_tuple_t tuple, void *data)
{
elvin_subscription_t subscription;
struct notify_callback_arg *arg;
char expression[EXPRESSION_LENGTH];
int index = 0;
/* XXX: The declaration of expression has to go last, or the
local variables on the stack after it get smashed. Check
Elvin for buffer overruns. */
if (!handle || !callback || !type) {
if (!handle || !callback || !tuple) {
ERROR("invalid parameter\n");
return NULL;
}
snprintf(expression, EXPRESSION_LENGTH,
"(host == \"*\" || host == \"%s\") && type == %d",
hostname,
type);
if (tuple->site) {
index += snprintf(&expression[index], sizeof(expression) - index,
"SITE == \"%s\" ",
tuple->site);
}
if (tuple->expt) {
index += snprintf(&expression[index], sizeof(expression) - index,
"%s EXPT == \"%s\" ",
(index ? "&&" : ""),
tuple->expt);
}
if (tuple->group) {
index += snprintf(&expression[index], sizeof(expression) - index,
"%s GROUP == \"%s\" ",
(index ? "&&" : ""),
tuple->group);
}
if (tuple->host) {
index += snprintf(&expression[index], sizeof(expression) - index,
"%s HOST == \"%s\" ",
(index ? "&&" : ""),
tuple->host);
}
if (tuple->objtype) {
index += snprintf(&expression[index], sizeof(expression) - index,
"%s OBJTYPE == \"%s\" ",
(index ? "&&" : ""),
tuple->objtype);
}
if (tuple->objname) {
index += snprintf(&expression[index], sizeof(expression) - index,
"%s OBJNAME == \"%s\" ",
(index ? "&&" : ""),
tuple->objname);
}
if (tuple->eventtype) {
index += snprintf(&expression[index], sizeof(expression) - index,
"%s EVENTTYPE == \"%s\" ",
(index ? "&&" : ""),
tuple->eventtype);
}
index += snprintf(&expression[index], sizeof(expression) - index,
"%s SCHEDULER == %d ",
(index ? "&&" : ""),
tuple->scheduler);
TRACE("subscribing to event %s\n", expression);
......@@ -910,8 +919,6 @@ notify_callback(elvin_handle_t server,
struct notify_callback_arg *arg = (struct notify_callback_arg *) rock;
event_notify_callback_t callback;
void *data;
char host[MAXHOSTNAMELEN];
event_type_t type;
TRACE("received event notification\n");
......@@ -921,23 +928,25 @@ notify_callback(elvin_handle_t server,
handle.server = server;
handle.status = status;
if (event_notification_get_string(&handle, notification, "host",
host, MAXHOSTNAMELEN)
== 0)
{
ERROR("could not get host attribute from notification %p\n",
notification);
return;
}
callback(&handle, notification, data);
}
if (event_notification_get_int32(&handle, notification, "type",
(int *) &type)
== 0)
{
ERROR("could not get type attribute from notification %p\n",
notification);
return;
}
/*
* address tuple alloc and free.
*/
address_tuple_t
address_tuple_alloc(void)
{
address_tuple_t tuple = xmalloc(sizeof(address_tuple));
bzero(tuple, sizeof(address_tuple));
return tuple;
}
callback(&handle, notification, host, type, data);
int
address_tuple_free(address_tuple_t tuple)
{
free(tuple);
return 1;
}
......@@ -5,7 +5,7 @@
*
* @COPYRIGHT@
*
* $Id: event.h,v 1.6 2002-02-19 15:45:24 imurdock Exp $
* $Id: event.h,v 1.7 2002-02-21 17:49:38 stoller Exp $
*/
#ifndef __EVENT_H__
......@@ -44,6 +44,43 @@ typedef elvin_notification_t event_notification_t;
/* Event subscription: */
typedef elvin_subscription_t event_subscription_t;
/*
* A tuple defines the target of the event, or if you are a subscriber,
* what events you want to subscribe to.
*/
typedef struct {
char *site; /* Which Emulab site. God only */
char *expt; /* Project and experiment IDs */
char *group; /* User defined group of nodes */
char *host; /* A specific host */
char *objtype; /* LINK, TRAFGEN, etc ... */
char *objname; /* link0, cbr0, cbr1, etc ... */
char *eventtype; /* START, STOP, UP, DOWN, etc ... */
int scheduler; /* A dynamic event to schedule */
} address_tuple, *address_tuple_t;
#define ADDRESSTUPLE_ANY NULL
#define ADDRESSTUPLE_ALL "*"
#define OBJECTTYPE_TESTBED "TBCONTROL"
address_tuple_t address_tuple_alloc(void);
int address_tuple_free(address_tuple_t);
#define event_notification_get_site(handle, note, buf, len) \
event_notification_get_string(handle, note, "SITE", buf, len)
#define event_notification_get_expt(handle, note, buf, len) \
event_notification_get_string(handle, note, "EXPT", buf, len)
#define event_notification_get_group(handle, note, buf, len) \
event_notification_get_string(handle, note, "GROUP", buf, len)
#define event_notification_get_host(handle, note, buf, len) \
event_notification_get_string(handle, note, "HOST", buf, len)
#define event_notification_get_objtype(handle, note, buf, len) \
event_notification_get_string(handle, note, "OBJTYPE", buf, len)
#define event_notification_get_objname(handle, note, buf, len) \
event_notification_get_string(handle, note, "OBJNAME", buf, len)
#define event_notification_get_eventtype(handle, note, buf, len) \
event_notification_get_string(handle, note, "EVENTTYPE", buf, len)
/* The "any host" string: */
#define EVENT_HOST_ANY "*"
......@@ -60,11 +97,10 @@ typedef enum {
and called whenever the specified event is triggered.
HANDLE is the handle to the event server, NOTIFICATION is the event
notification itself, and DATA is an arbitrary value passed to
event_subscribe (argument 4). HOST and TYPE are attributes from the
event notification. */
event_subscribe (argument 4).
*/
typedef void (*event_notify_callback_t)(event_handle_t handle,
event_notification_t notification,
char *host, event_type_t type,
void *data);
/*
......@@ -74,8 +110,10 @@ typedef void (*event_notify_callback_t)(event_handle_t handle,
#define ERROR(fmt,...) fprintf(stderr, __FUNCTION__ ": " fmt, ## __VA_ARGS__)
#ifdef DEBUG
#define TRACE(fmt,...) printf(__FUNCTION__ ": " fmt, ## __VA_ARGS__)
#define DBG(fmt,...) printf(fmt, ## __VA_ARGS__)
#else
#define TRACE(fmt,...)
#define DBG(fmt,...)
#endif /* DEBUG */
/*
......@@ -90,7 +128,7 @@ int event_notify(event_handle_t handle, event_notification_t notification);
int event_schedule(event_handle_t handle, event_notification_t notification,
struct timeval *time);
event_notification_t event_notification_alloc(event_handle_t handle,
char *host, event_type_t type);
address_tuple_t tuple);
int event_notification_free(event_handle_t handle,
event_notification_t notification);
int event_notification_get_double(event_handle_t handle,
......@@ -127,7 +165,7 @@ int event_notification_remove(event_handle_t handle,
event_notification_t notification, char *name);
event_subscription_t event_subscribe(event_handle_t handle,
event_notify_callback_t callback,
event_type_t type, void *data);
address_tuple_t tuple, void *data);
/* util.c */
void *xmalloc(int size);
......
......@@ -9,19 +9,19 @@ all: event-sched
include $(TESTBED_SRCDIR)/GNUmakerules
CFLAGS += -O -g -static -I. -I$(SRCDIR)/../lib -Wall -pthread -DDEBUG
CFLAGS += -O -g -I. -I${OBJDIR} -I$(SRCDIR)/../lib -Wall -pthread -DDEBUG
CFLAGS += `elvin-config --cflags vin4mt`
LDFLAGS += -pthread -L../lib
LIBS += -levent -lcipher
LDFLAGS += -pthread -L../lib
LIBS += -levent -lcipher -L/usr/local/lib/mysql -lmysqlclient
LIBS += `elvin-config --libs vin4mt`
OBJS = event-sched.o queue.o
OBJS = event-sched.o queue.o libdb.o
event-sched: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
$(OBJS): event-sched.h
$(OBJS): event-sched.h ../lib/libevent.a
clean:
/bin/rm -f *.o event-sched
......@@ -16,32 +16,79 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <math.h>
#include <ctype.h>
#include "event-sched.h"
#include "libdb.h"
static void enqueue(event_handle_t handle, event_notification_t notification,
char *host, event_type_t type, void *data);
static void enqueue(event_handle_t handle,
event_notification_t notification, void *data);
static void dequeue(event_handle_t handle);
static char *progname;
static char *pid, *eid;
static int get_static_events(event_handle_t handle);
void
usage()
{
fprintf(stderr, "Usage: %s [-s server] [-p port] <pid> <eid>\n",
progname);
exit(-1);
}
int
main(int argc, char **argv)
{
address_tuple_t tuple;
event_handle_t handle;
char *server = NULL;
char *port = NULL;
char buf[BUFSIZ];
int c;
/* Initialize event queue semaphores: */
sched_event_init();
while ((c = getopt(argc, argv, "s:")) != -1) {
while ((c = getopt(argc, argv, "s:p:")) != -1) {
switch (c) {
case 's':
server = optarg;
break;
case 'p':
port = optarg;
break;
default:
fprintf(stderr, "Usage: %s [-s SERVER]\n", argv[0]);
return 1;
}
}
argc -= optind;
argv += optind;
if (argc != 2)
usage();
pid = argv[0];
eid = argv[1];
/*
* Set up DB state.
*/
if (!dbinit())
return 1;
/*
* Convert server/port to elvin thing.
*
* XXX This elvin string stuff should be moved down a layer.
*/
if (server) {
snprintf(buf, sizeof(buf), "elvin://%s%s%s",
server,
(port ? ":" : ""),
(port ? port : ""));
server = buf;
}
/* Register with the event system: */
handle = event_register(server, 1);
......@@ -50,9 +97,26 @@ main(int argc, char **argv)
return 1;
}
/* Subscribe to the EVENT_SCHEDULE event, and enqueue events as
they arrive: */
if (event_subscribe(handle, enqueue, EVENT_SCHEDULE, NULL) == NULL) {
/*
* Read the static events list and schedule.
*/
if (!get_static_events(handle)) {
ERROR("could not get static event list\n");
return 1;
}
/*
* Construct an address tuple for event subscription. We set the scheduler
* flag to indicate we want to capture those notifications.
*/
tuple = address_tuple_alloc();
if (tuple == NULL) {
ERROR("could not allocate an address tuple\n");
return 1;
}
tuple->scheduler = 1;
if (event_subscribe(handle, enqueue, tuple, NULL) == NULL) {
ERROR("could not subscribe to EVENT_SCHEDULE event\n");
return 1;
}
......@@ -71,11 +135,9 @@ main(int argc, char **argv)
/* Enqueue event notifications as they arrive. */
static void
enqueue(event_handle_t handle, event_notification_t notification, char *host,
event_type_t type, void *data)
enqueue(event_handle_t handle, event_notification_t notification, void *data)
{
sched_event_t event;
event_type_t old_type;
/* Clone the event notification, since we want the notification to
live beyond the callback function: */
......@@ -87,28 +149,11 @@ enqueue(event_handle_t handle, event_notification_t notification, char *host,
return;
}
/* Restore the original event type: */
if (event_notification_get_int32(handle, event.notification, "old_type",
(int *) &old_type)
== 0)
{
ERROR("could not restore type attribute of notification %p\n",
event.notification);
return;
}
if (event_notification_remove(handle, event.notification, "type") == 0) {
ERROR("could not restore type attribute of notification %p\n",
event.notification);
return;
}
if (event_notification_put_int32(handle, event.notification, "type",
old_type)
== 0)
{
ERROR("could not restore type attribute of notification %p\n",
/* Clear the scheduler flag */
if (! event_notification_remove(handle, event.notification, "SCHEDULER") ||
! event_notification_put_int32(handle,
event.notification, "SCHEDULER", 0)) {
ERROR("could not clear scheduler attribute of notification %p\n",
event.notification);
return;
}
......@@ -163,17 +208,23 @@ dequeue(event_handle_t handle)
{
sched_event_t next_event;
struct timeval next_event_wait, now;
int foo;
while (sched_event_dequeue(&next_event, 1) != 0) {
/* Determine how long to wait before firing the next event. */
again:
next_event_wait = sched_time_until_event_fires(next_event);
/* If the event's firing time is in the future, then use
select to wait until the event should fire. */
if (next_event_wait.tv_sec >= 0 && next_event_wait.tv_usec > 0) {
if (select(0, NULL, NULL, NULL, &next_event_wait) != 0) {
ERROR("select did not timeout\n");
return;
if ((foo = select(0, NULL, NULL, NULL, &next_event_wait)) != 0) {
/*
* I'll assume that this fails cause of a pthread
* related signal issue.
*/
ERROR("select did not timeout %d %d\n", foo, errno);
goto again;
}
}
......@@ -198,3 +249,106 @@ dequeue(event_handle_t handle)
event_notification_free(handle, next_event.notification);
}
}
/*
* Get the static event list from the DB and schedule according to
* the relative time stamps.
*/
static int
get_static_events(event_handle_t handle)
{
MYSQL_RES *res;
MYSQL_ROW row;
int nrows;
struct timeval now, time;
address_tuple_t tuple;
char pideid[BUFSIZ];
res = mydb_query("select ex.time,ex.vnode,ex.vname,ex.arguments,"
" ot.type,et.type,i.IP from %s_%s_events as ex "
"left join event_eventtypes as et on "
" ex.eventtype=et.idx "
"left join event_objecttypes as ot on "
" ex.objecttype=ot.idx "
"left join reserved as r on "
" ex.vnode=r.vname and r.pid='%s' and r.eid='%s' "
"left join nodes as n on r.node_id=n.node_id "
"left join node_types as nt on nt.type=n.type "
"left join interfaces as i on "
" i.node_id=r.node_id and i.iface=nt.control_iface",
7, pid, eid, pid, eid);
#define EXTIME row[0]
#define EXVNODE row[1]
#define EXVNAME row[2]
#define EXARGS row[3]
#define OBJTYPE row[4]
#define EVTTYPE row[5]
#define IPADDR row[6]
if (!res) {
ERROR("getting static event list for %s/%s", pid, eid);
return 0;
}
if ((nrows = (int)mysql_num_rows(res)) == 0) {
mysql_free_result(res);
return 1;
}
/*
* Construct an address tuple for the notifications. We can reuse
* the same one over and over since the data is copied out.
*/
tuple = address_tuple_alloc();
if (tuple == NULL) {
ERROR("could not allocate an address tuple\n");
return 1;
}
sprintf(pideid, "%s/%s", pid, eid);
gettimeofday(&now, NULL);
while (nrows) {
sched_event_t event;
double firetime;
row = mysql_fetch_row(res);
firetime = atof(EXTIME);
DBG("EV: %8s %10s %10s %10s %10s %10s %10s\n",
row[0], row[1], row[2],
row[3] ? row[3] : "",
row[4], row[5],
row[6] ? row[6] : "");
tuple->expt = pideid;
tuple->host = IPADDR;
tuple->objname = EXVNAME;