Commit bf489797 authored by Timothy Stack's avatar Timothy Stack

More robot integration and some event system updates.

	* configure, configure.in: Detect rsync for loghole and add
	utils/loghole to the list of template files.

	* db/libdb.pm.in, db/xmlconvert.in: Add virt_node_startloc to the
	list of virtual tables.

	* event/lib/event.h, event/lib/event.c, event/lib/tbevent.py.tail:
	Add event_stop_main function to break out of the event_main()
	loop.  Add timeline to the address tuple.

	* event/sched/GNUmakefile.in, event/sched/error-record.h,
	event/sched/error-record.c, event/sched/event-sched.8,
	event/sched/event-sched.h, event/sched/event-sched.c,
	event/sched/group-agent.h, event/sched/group-agent.c,
	event/sched/listNode.h, event/sched/listNode.c,
	event/sched/local-agent.h, event/sched/local-agent.c,
	event/sched/node-agent.h, event/sched/node-agent.cc,
	event/sched/queue.c, event/sched/rpc.h, event/sched/rpc.cc,
	event/sched/simulator-agent.h, event/sched/simulator-agent.c,
	event/sched/timeline-agent.h, event/sched/timeline-agent.c:
	Updated event scheduler, not completely finished, but well enough
	along for the robots.

	* lib/libtb/GNUmakefile.in, lib/libtb/popenf.h,
	lib/libtb/popenf.c, lib/libtb/systemf.h, lib/libtb/systemf.c: Add
	some handy versions of system/popen that take format arguments.

	* lib/libtb/tbdefs.h, lib/libtb/tbdefs.c: Add some more event and
	object types.

	* tbsetup/assign_wrapper.in: Add the virt_node_startloc building
	to desires string for a node.

	* tbsetup/ptopgen.in: Add a node's location to the feature list.

	* tbsetup/tbreport.in: Display the timeline/sequence an event is a
	part of.

	* tbsetup/ns2ir/GNUmakefile.in: Add timeline, sequence, and
	topography files.

	* tbsetup/ns2ir/node.tcl: Add initial position for nodes and allow
	them to be attached to "topographys".

	* tbsetup/ns2ir/parse-ns.in: Make a hwtype_class array with a
	node_type's class.  Make an 'areas' array that holds the
	buildings where nodes are located.  Make an 'obstacles' table
	with any obstacles in the building.

	* tbsetup/ns2ir/parse.tcl.in: Move named-args function from
	tb_compat.tcl to here.  Add reltime-to-secs function that converts
	time given in a format like "10h2m1s" to a seconds value, used in
	"$ns at" so its easier to write time values.  Add "K", "Kb", and
	"Kbps" as possible units for bandwidth (only the lowercase
	versions were available before).

	* tbsetup/ns2ir/program.tcl: Add "dir" and "timeout" attributes,
	although they don't go anywhere at the moment.

	* tbsetup/ns2ir/sequence.tcl, tbsetup/ns2ir/timeline.tcl,
	tbsetup/ns2ir/topography.tcl: Initial versions.

	* tbsetup/ns2ir/sim.tcl.in: Add support for timelines and
	sequences.  Add 'node-config' method to change the default
	configuration for nodes produced by the Simulator object.  Send an
	initial MODIFY event to any trafgen objects so their configuration
	gets through, even when there are no start/stop events.  Move
	event parsing to the 'make_event' method.

	* utils/loghole.1, utils/loghole.in: Loghole utility, used for
	retrieving logs from experimental nodes and creating archives of
	the logs.

	* xmlrpc/emulabclient.py.in: Escape any strange characters in the
	output field.

	* xmlrpc/emulabserver.py.in: Add virt_node_startloc to the list of
	virtual_tables.  Add emulab.vision_config and
	emulab.obstacle_config methods for getting information pertaining
	to the robots.  Change the OSID listing to include more fields.
	Add a "physical" aspect to experiment.info to get information
	about the physical nodes.  Add parent field to the events in the
	array returned by eventlist.  Add sshdescription to get extra
	information needed to log into a vnode.  Add node.statewait so you
	can wait for nodes to come up.
parent 0d031e0b
......@@ -1839,6 +1839,45 @@ case "$INSTALL" in
;;
esac
# Extract the first word of "rsync", so it can be a program name with args.
set dummy rsync; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
echo "configure:1811: checking for $ac_word" >&5
if eval "test \"`echo '$''{'ac_cv_path_RSYNC'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
case "$RSYNC" in
/*)
ac_cv_path_RSYNC="$RSYNC" # Let the user override the test with a path.
;;
?:/*)
ac_cv_path_RSYNC="$RSYNC" # Let the user override the test with a dos path.
;;
*)
IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
ac_dummy="${PATH}"
for ac_dir in $ac_dummy; do
test -z "$ac_dir" && ac_dir=.
if test -f $ac_dir/$ac_word; then
ac_cv_path_RSYNC="$ac_dir/$ac_word"
break
fi
done
IFS="$ac_save_ifs"
test -z "$ac_cv_path_RSYNC" && ac_cv_path_RSYNC=""""
;;
esac
fi
RSYNC="$ac_cv_path_RSYNC"
if test -n "$RSYNC"; then
echo "$ac_t""$RSYNC" 1>&6
else
echo "$ac_t""no" 1>&6
fi
if test x"$RSYNC" = x"" || test ! -x "$RSYNC"; then
{ echo "configure: error: Rsync is required to use this software, see www.rsync.org." 1>&2; exit 1; }
fi
outfiles="$outfiles Makeconf GNUmakefile \
assign/GNUmakefile \
......@@ -1943,7 +1982,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
utils/nsgen/GNUmakefile utils/nsgen/webnsgen \
utils/link_config utils/import_commitlog utils/dhcpd_wrapper \
utils/opsreboot utils/deletenode utils/webdeletenode utils/spewleds \
utils/grabwebcams \
utils/grabwebcams utils/loghole \
www/GNUmakefile www/defs.php3 www/dbdefs.php3 \
www/swish.conf www/websearch www/garcia-telemetry/GNUmakefile \
vis/GNUmakefile vis/webvistopology vis/dbvistopology \
......@@ -2253,6 +2292,7 @@ s%@TESTMODE@%$TESTMODE%g
s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
s%@INSTALL_DATA@%$INSTALL_DATA%g
s%@RSYNC@%$RSYNC%g
s%@DISTCLEAN_FILES@%$DISTCLEAN_FILES%g
CEOF
......
......@@ -581,6 +581,10 @@ AC_PROG_INSTALL
;;
esac]
AC_PATH_PROG(RSYNC, rsync, "", [${PATH}])
if test x"$RSYNC" = x"" || test ! -x "$RSYNC"; then
AC_MSG_ERROR([Rsync is required to use this software, see www.rsync.org.])
fi
outfiles="$outfiles Makeconf GNUmakefile \
assign/GNUmakefile \
......@@ -685,7 +689,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
utils/nsgen/GNUmakefile utils/nsgen/webnsgen \
utils/link_config utils/import_commitlog utils/dhcpd_wrapper \
utils/opsreboot utils/deletenode utils/webdeletenode utils/spewleds \
utils/grabwebcams \
utils/grabwebcams utils/loghole \
www/GNUmakefile www/defs.php3 www/dbdefs.php3 \
www/swish.conf www/websearch www/garcia-telemetry/GNUmakefile \
vis/GNUmakefile vis/webvistopology vis/dbvistopology \
......
......@@ -3053,6 +3053,7 @@ sub TBGetSiteVar($;$)
"virt_vtypes",
"virt_programs",
"virt_node_desires",
"virt_node_startloc",
"virt_simnode_attributes",
# vis_nodes is locked during update in prerender, so we
# will get a consistent dataset when we backup.
......
......@@ -2,7 +2,7 @@
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -91,6 +91,10 @@ my %virtual_tables =
tag => "node_desires",
row => "node_desire",
attrs => [ "vname", "desire" ]},
"virt_node_startloc" => { rows => undef,
tag => "node_startlocs",
row => "node_startloc",
attrs => [ "vname", "building" ]},
"virt_routes" => { rows => undef,
tag => "routes",
row => "route",
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* Copyright (c) 2000-2005 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -360,8 +360,6 @@ int event_poll_blocking(event_handle_t handle, unsigned int timeout)
int
event_main(event_handle_t handle)
{
int loop = 1;
if (!handle) {
ERROR("invalid parameter\n");
return 0;
......@@ -372,9 +370,15 @@ event_main(event_handle_t handle)
return 0;
}
if (handle->do_loop) {
ERROR("loop is already running\n");
return 0;
}
TRACE("entering event loop...\n");
if (handle->mainloop(&loop, handle->status) == 0) {
handle->do_loop = 1;
if (handle->mainloop(&handle->do_loop, handle->status) == 0) {
ERROR("Elvin mainloop failed: ");
elvin_error_fprintf(stderr, handle->status);
return 0;
......@@ -384,6 +388,29 @@ event_main(event_handle_t handle)
}
int event_stop_main(event_handle_t handle)
{
if (!handle) {
ERROR("invalid parameter\n");
return 0;
}
if (!handle->mainloop) {
ERROR("multithreaded programs do not have a mainloop\n");
return 0;
}
if (!handle->do_loop) {
ERROR("mainloop is not running\n");
return 0;
}
handle->do_loop = 0;
return 1;
}
/*
* Send the event notification NOTIFICATION. NOTIFICATION is
* allocated by event_notification_alloc, and may optionally
......@@ -485,7 +512,6 @@ event_schedule(event_handle_t handle, event_notification_t notification,
return event_notify(handle, notification);
}
/*
* Allocate an event notification. The address TUPLE defines who
* should receive the notification. Returns a pointer to an event
......@@ -534,6 +560,7 @@ event_notification_alloc(event_handle_t handle, address_tuple_t tuple)
!EVPUT("OBJTYPE", objtype) ||
!EVPUT("OBJNAME", objname) ||
!EVPUT("EVENTTYPE", eventtype) ||
!EVPUT("TIMELINE", timeline) ||
! event_notification_put_int32(handle, notification, "SCHEDULER", 0)) {
ERROR("could not add attributes to notification %p\n", notification);
return NULL;
......@@ -554,7 +581,11 @@ int
event_notification_free(event_handle_t handle,
event_notification_t notification)
{
if (!handle || !notification || !notification->elvin_notification) {
if (!notification) {
return 1;
}
if (!handle || !notification->elvin_notification) {
ERROR("invalid parameter\n");
return 0;
}
......@@ -984,7 +1015,7 @@ event_notification_remove(event_handle_t handle,
if (elvin_notification_remove(notification->elvin_notification,
name, handle->status) == 0) {
ERROR("elvin_notification_remove failed: ");
ERROR("elvin_notification_remove of %s failed: ", name);
elvin_error_fprintf(stderr, handle->status);
return 0;
}
......@@ -1135,7 +1166,12 @@ event_subscribe(event_handle_t handle, event_notify_callback_t callback,
! addclause("EVENTTYPE", tuple->eventtype,
&expression[index], sizeof(expression) - index, &index))
return NULL;
if (tuple->timeline &&
! addclause("TIMELINE", tuple->timeline,
&expression[index], sizeof(expression) - index, &index))
return NULL;
index += snprintf(&expression[index], sizeof(expression) - index,
"%s SCHEDULER == %d ",
(index ? "&&" : ""),
......@@ -1212,6 +1248,7 @@ notify_callback(elvin_handle_t server,
/* If MAC does not match, throw it away */
if (handle->keydata &&
event_notification_check_hmac(handle, &notification)) {
ERROR("bad hmac\n");
return;
}
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2004 University of Utah and the Flux Group.
* Copyright (c) 2000-2005 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -15,18 +15,24 @@
#define __EVENT_H__
#include <stdio.h>
#include <stdarg.h>
#include <elvin/elvin.h>
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif /* MAXHOSTNAMELEN */
#ifdef __cplusplus
extern "C" {
#endif
/* Handle to the event server: */
struct event_handle {
elvin_handle_t server;
elvin_error_t status;
unsigned char *keydata;
int keylen;
int do_loop;
/* API function pointers: */
elvin_error_t (*init)(void);
int (*connect)(elvin_handle_t handle, elvin_error_t error);
......@@ -67,6 +73,7 @@ typedef struct _address_tuple {
char *objname; /* link0, cbr0, cbr1, etc ... */
char *eventtype; /* START, STOP, UP, DOWN, etc ... */
int scheduler; /* A dynamic event to schedule */
char *timeline; /* The timeline to schedule under */
} address_tuple, *address_tuple_t;
#define ADDRESSTUPLE_ANY NULL
#define ADDRESSTUPLE_ALL "*"
......@@ -96,6 +103,8 @@ int address_tuple_free(address_tuple_t);
event_notification_get_string(handle, note, "ARGS", buf, len)
#define event_notification_set_arguments(handle, note, buf) \
event_notification_put_string(handle, note, "ARGS", buf)
#define event_notification_get_timeline(handle, note, buf, len) \
event_notification_get_string(handle, note, "TIMELINE", buf, len)
/*
* For dynamic events.
......@@ -161,6 +170,7 @@ int event_unregister(event_handle_t handle);
int event_poll(event_handle_t handle);
int event_poll_blocking(event_handle_t handle, unsigned int timeout);
int event_main(event_handle_t handle);
int event_stop_main(event_handle_t handle);
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);
......@@ -240,4 +250,8 @@ int event_do(event_handle_t handle, ea_tag_t tag, ...);
void *xmalloc(int size);
void *xrealloc(void *p, int size);
#ifdef __cplusplus
}
#endif
#endif /* __EVENT_H__ */
......@@ -65,6 +65,9 @@ class NotificationWrapper:
self.notification,
args)
def getTimeline(self, args):
return event_notification_get_timeline(self.handle, self.notification)
def getSender(self):
return event_notification_get_sender(self.handle, self.notification)
......
......@@ -11,11 +11,12 @@ SUBDIR = event/sched
include $(OBJDIR)/Makeconf
all: event-sched event-sched_rrpc
all: event-sched_rrpc
include $(TESTBED_SRCDIR)/GNUmakerules
CFLAGS += -pthread -DBINDIR='"$(INSTALL_BINDIR)"'
CFLAGS += -g -pthread -DBINDIR='"$(INSTALL_BINDIR)"'
CFLAGS += -DSBINDIR='"$(INSTALL_SBINDIR)"'
#CFLAGS += -DDEBUG
CFLAGS += -O -Wall
CFLAGS += -I. -I${OBJDIR} -I$(SRCDIR)/../lib -I$(TESTBED_SRCDIR)/lib/libtb
......@@ -27,7 +28,8 @@ DBLIBS = -L/usr/local/lib/mysql -lmysqlclient -lz
LIBS += -levent_r -ltb -lcipher -lz
ULXRINC = -I/usr/local/include -I/usr/local/include/ulxmlrpcpp
CXXFLAGS += -pthread -O $(ULXRINC) -I$(OBJDIR)
CXXFLAGS += -pthread -O $(ULXRINC) -I$(OBJDIR) -I$(TESTBED_SRCDIR)/lib/libtb
CXXFLAGS += -I$(SRCDIR)/../lib
ULXRLIBS = -L/usr/local/lib -lulsshxmlrpcpp -lulxmlrpcpp -lexpat
#
......@@ -40,38 +42,50 @@ LIBS += -L/usr/local/lib -lvin4mt -lvin4c -lvin4 -lssl -lcrypto -lm
OBJS = event-sched.o
event-sched: event-sched.c queue.o event-sched.h ../lib/libevent.a
$(CC) $(CFLAGS) -DDBIFACE=1 $(LDFLAGS) -o $@ $< queue.o $(LIBS) $(DBLIBS)
event-sched_rpc: event-sched_rpc.o rpc.o queue.o event-sched.h \
../lib/libevent.a
$(CXX) $(CFLAGS) -static $(LDFLAGS) -o $@ event-sched_rpc.o \
rpc.o queue.o $(ULXRLIBS) $(LIBS)
event-sched_rrpc: event-sched_rpc.o rrpc.o queue.o event-sched.h \
../lib/libevent.a
$(CXX) $(CFLAGS) -static $(LDFLAGS) -o $@ event-sched_rpc.o \
rrpc.o queue.o $(ULXRLIBS) $(LIBS)
version.c: event-sched.c
echo >$@ "char build_info[] = \"Built on `date +%d-%b-%Y` by `id -nu`@`hostname | sed 's/\..*//'`:`pwd`\";"
OBJS = \
error-record.o \
event-sched_rpc.o \
group-agent.o \
listNode.o \
local-agent.o \
node-agent.o \
queue.o \
rrpc.o \
simulator-agent.o \
timeline-agent.o \
version.o
event-sched_rrpc: $(OBJS) event-sched.h ../lib/libevent.a
$(CXX) $(CFLAGS) -static $(LDFLAGS) -o $@ $(OBJS) $(ULXRLIBS) $(LIBS)
queue.o: event-sched.h
event-sched_rpc.o: event-sched.c event-sched.h rpc.h
listNode.o: listNode.h
error-record.o: error-record.h
local-agent.o: local-agent.h
group-agent.o: group-agent.h
event-sched_rpc.o: event-sched.c event-sched.h rpc.h listNode.h
$(CC) $(CFLAGS) -DRPC -c -o $@ $<
rpc.o: rpc.cc rpc.h
$(CXX) $(CXXFLAGS) -DSSHRPC $(ULXRINC) -c $<
rrpc.o: rpc.cc rpc.h
$(CXX) $(CXXFLAGS) -DSSLRPC $(ULXRINC) -c -o rrpc.o $<
$(CXX) -g $(CXXFLAGS) -DSSLRPC $(ULXRINC) -c -o rrpc.o $<
install: event-sched_rrpc
-mkdir -p $(INSTALL_DIR)/opsdir/sbin
$(INSTALL_PROGRAM) event-sched_rrpc $(INSTALL_DIR)/opsdir/sbin/event-sched
$(INSTALL_PROGRAM) $< $(INSTALL_DIR)/opsdir/sbin/event-sched
$(INSTALL) -m 0644 $(SRCDIR)/event-sched.8 \
$(DESTDIR)$(INSTALL_DIR)/opsdir/man/man8/event-sched.8
control-install: event-sched_rrpc
$(INSTALL_PROGRAM) event-sched_rrpc $(INSTALL_SBINDIR)/event-sched
$(INSTALL_PROGRAM) $< $(INSTALL_SBINDIR)/event-sched
# not a client thing
client:
client-install: client
clean:
/bin/rm -f *.o event-sched event-sched_rpc event-sched_rrpc
/bin/rm -f *.o event-sched event-sched_rpc event-sched_rrpc version.c
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2004 University of Utah and the Flux Group.
* All rights reserved.
*/
/**
* @file error-record.c
*
* Implementation of the error-record code.
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include "tbdefs.h"
#include "popenf.h"
#include "error-record.h"
/**
* Dump a digested version of the program-agent's "status" file to the given
* output. The status file is a 'key=value' formatted file that describes a
* particular invocation of a program agent. The file is written out by the
* agent after the program finishes, pulled back to ops by a 'loghole sync' and
* then gets included in the Simulator's report through this function.
*
* @param er An error record for a failed program-agent invocation.
* @param out The file to write any output to.
* @return Zero on success, -1 otherwise.
*
* @see send_report
*/
static int dump_agent_status(error_record_t er, FILE *out);
/**
* Utility function to pipe the tail of a file to the given output file. After
* the program-agent's "status" file is dumped to the output, the tail of the
* file gets included as well so the user can get an idea of what went wrong.
*
* @param path The path of the file whose tail should be written to the output
* file.
* @param out The file to write any output to.
* @return Zero on success, -1 otherwise.
*
* @see dump_agent_status
*/
static int tail_file(char *path, FILE *out);
error_record_t create_error_record(void)
{
error_record_t retval = NULL;
if ((retval = calloc(1, sizeof(struct _error_record))) == NULL) {
errno = ENOMEM;
}
else {
retval->er_agent = NULL;
retval->er_token = -1;
retval->er_error = 0;
}
return retval;
}
void delete_error_record(error_record_t er)
{
if (er != NULL) {
free(er);
er = NULL;
}
}
int error_record_invariant(error_record_t er)
{
assert(er != NULL);
assert(er->er_agent != NULL);
assert(agent_invariant(er->er_agent));
assert(er->er_token != -1);
return 1;
}
void delete_error_records(struct lnList *list)
{
error_record_t er;
assert(list != NULL);
lnCheck(list);
while ((er = (error_record_t)lnRemHead(list)) != NULL) {
delete_error_record(er);
er = NULL;
}
lnCheck(list);
assert(lnEmptyList(list));
}
static int dump_agent_status(error_record_t er, FILE *out)
{
/*
* The format of the path for the status file:
* logs/<node>/usr/testbed/logs/<agent>.<token>.status
*/
static char *file_format = "logs/%s/usr/testbed/logs/%s.%lu.status";
/*
* A map of status file 'keys' that people may be interested in and
* some human-readable prefix text.
*/
static struct {
char *key;
char *desc;
} status_map[] = {
{ "DIR", " Directory:\t" },
{ "COMMAND", " Command:\t" },
{ "START_TIME", " Started at:\t" },
{ "EXIT_CODE", " Exit code:\t" },
{ "TIMEOUT_FIRED", " Timeout Fired:\t" },
{ NULL, NULL }
};
char buffer[BUFSIZ];
int retval;
FILE *file;
assert(er != NULL);
assert(error_record_invariant(er));
assert(strcmp(er->er_agent->objtype, TBDB_OBJECTTYPE_PROGRAM) == 0);
assert(out != NULL);
snprintf(buffer,
sizeof(buffer),
file_format,
er->er_agent->vnode,
er->er_agent->name,
er->er_token);
/*
* We expect the file to have been brought over already, so just try
* to open out.
*/
if ((file = fopen(buffer, "r")) == NULL) {
fprintf(out, "warning: missing status file: '%s'\n", buffer);
retval = 0;
}
else {
/* Print out a short header, then */
fprintf(out,
"Program agent: '%s' located on node '%s'\n",
er->er_agent->name,
er->er_agent->vnode);
/* ... write out the data most people would care about. */
while (fgets(buffer, sizeof(buffer), file) != NULL) {
char *value;
if ((value = strchr(buffer, '=')) == NULL) {
warning("Bad line in status file: %s\n",
buffer);
}
else {
int lpc;
*value = '\0';
value += 1;
for (lpc = 0; status_map[lpc].key; lpc++) {
if (strcmp(buffer,
status_map[lpc].key) == 0) {
fprintf(out,
"%s%s",
status_map[lpc].desc,
value);
}
}
}
}
fclose(file);
file = NULL;
retval = 0;
}
return retval;
}
static int tail_file(char *path, FILE *out)
{
FILE *tail_in;
int retval;
assert(path != NULL);
assert(strlen(path) > 0);
assert(out != NULL);
if (access(path, R_OK) != 0) {
fprintf(out, "warning: missing log file: '%s'\n", path);
retval = 0; // Non-fatal error...
}
else if ((tail_in = popenf("tail %s", "r", path)) == NULL) {
fprintf(out, "error: cannot tail log '%s'\n", path);
retval = -1;
}
else {
char buffer[1024];
int rc;
/* Print a separator header, */
fprintf(out, ">> Tail of log '%s' <<\n", path);
/* ... the tail of the file, and */
while ((rc = fread(buffer,
1,
sizeof(buffer),
tail_in)) > 0) {
fwrite(buffer, 1, rc, out);
}
if (pclose(tail_in) == -1) {
warning("pclose for tail %s failed\n", path);
}
tail_in = NULL;
/* ... a separator footer. */
memset(buffer, '=', 79);
buffer[79] = '\0';
fprintf(out, "%s\n\n", buffer);
retval = 0;
}
return retval;
}
int dump_error_record(error_record_t er, FILE *out)
{
char path[PATH_MAX];
int lpc, retval = 0;
assert(er != NULL);
assert(error_record_invariant(er));
assert(out != NULL);
if (strcmp(er->er_agent->objtype, TBDB_OBJECTTYPE_PROGRAM) == 0) {
/**
* NULL-terminated array of log file name formats that should
* be sent back to the user.
*/
static char *filename_formats[] = {
"logs/%s/usr/testbed/logs/%s.%lu.out",
"logs/%s/usr/testbed/logs/%s.%lu.err",
NULL
};
/* First, write out any status information, then */
retval = dump_agent_status(er, out);
/* ... pass the logs through. */
for (lpc = 0; filename_formats[lpc] && (retval == 0); lpc++) {
snprintf(path,
sizeof(path),
filename_formats[lpc],
er->er_agent->vnode,
er->er_agent->name,
er->er_token);
retval = tail_file(path, out);
}
}
return retval;
}
int dump_error_records(struct lnList *list, FILE *out)
{
error_record_t er;
int retval = 0;
assert(list != NULL);
lnCheck(list);
assert(out != NULL);
for (er = (error_record_t)list->lh_Head;
(er->er_link.ln_Succ != NULL) && (retval == 0);
er = (error_record_t)er->er_link.ln_Succ) {
assert(error_record_invariant(er));
retval = dump_error_record(er, out);
}
return retval;
}
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2004 University of Utah and the Flux Group.
* All rights reserved.
*/
/**
* @file error-record.h
*
* Header file for error records, which are used to track errors caused by a
* particular event.
*/
#ifndef _error_record_h
#define _error_record_h
#include "event-sched.h"
#include "listNode.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Structure used to track errors generated by an event.
*/
struct _error_record {
struct lnNode er_link; /*< Linked list node. */
struct agent *er_agent; /*< The agent that encountered an error. */
long er_token; /*< The unique token for the event that caused
the error. Used to tie the event to the log
files. */
int er_error; /*< The error code returned in the COMPLETED
event. */
};
/**
* Pointer type for the _error_record structure.
*/
typedef struct _error_record *error_record_t;
/**
* Create an error record object and initialize it with the default values.