diff --git a/sensors/canaryd/GNUmakefile.in b/sensors/canaryd/GNUmakefile.in
index 9a983fe98c70debc5bb59d1c3b11cd8502ed774c..e979ebef902bc5a0a90f21d53475fb2b69f26062 100644
--- a/sensors/canaryd/GNUmakefile.in
+++ b/sensors/canaryd/GNUmakefile.in
@@ -32,8 +32,8 @@ all: $(CDPROGS) client
 
 include ${TESTBED_SRCDIR}/GNUmakerules
 
-canaryd: canaryd.o auxfuncs.o version.o
-	$(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ canaryd.o auxfuncs.o version.o $(LIBS) -ldevstat
+canaryd: canaryd.o childProcess.o networkInterface.o canarydEvents.o auxfuncs.o version.o
+	$(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ canaryd.o childProcess.o networkInterface.o canarydEvents.o auxfuncs.o version.o $(LIBS) -ldevstat
 
 alertlistener: alertlistener.o
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ alertlistener.o $(LIBS)
@@ -41,7 +41,7 @@ alertlistener: alertlistener.o
 $(TBLIB):
 	gmake -C $(OBJDIR)/lib
 
-version.c: canaryd.c canaryd.h auxfuncs.c auxfuncs.h alertlistener.c
+version.c: canaryd.c childProcess.h childProcess.c networkInterface.h networkInterface.c canarydEvents.h canarydEvents.c auxfuncs.c auxfuncs.h alertlistener.c
 	echo >$@ "char build_info[] = \"Built `date +%d-%b-%Y` by `id -nu`@`hostname | sed 's/\..*//'`:`pwd`\";"
 
 client: canaryd
diff --git a/sensors/canaryd/auxfuncs.c b/sensors/canaryd/auxfuncs.c
index 915c16e37f8f843921ed795ecc317e4d7eaeb0b0..10c74267fb36d07ddc930bb9d6c08ef33ec680c4 100644
--- a/sensors/canaryd/auxfuncs.c
+++ b/sensors/canaryd/auxfuncs.c
@@ -1,4 +1,6 @@
+
 #include <sys/param.h>
+#include <sys/wait.h>
 #include <sys/dkstat.h>
 #include <sys/mbuf.h>
 #include <sys/sysctl.h>
@@ -28,11 +30,62 @@ char **specified_devices;
 devstat_select_mode select_mode;
 struct statinfo cur, last;
 
+extern void lerror(const char* msgstr);
+
 static int getswapouts(void);
-static int cpu_state(int which);
 static long percentages(int cnt, int *out, register long *new,
                         register long *old, long *diffs);
 
+#define MAXLINELEN 256
+
+/* XXX change to combine last return value of procfunc with exec'ed process'
+   exit status & write macros for access.
+*/
+int procpipe(char *const prog[], int (procfunc)(char*,void*), void* data) {
+
+  int fdes[2], retcode, cpid, status;
+  char buf[MAXLINELEN];
+  FILE *in;
+
+  if ((retcode=pipe(fdes)) < 0) {
+    lerror("Couldn't alloc pipe");
+  }
+  
+  else {
+    switch ((cpid = fork())) {
+    case 0:
+      close(fdes[0]);
+      dup2(fdes[1], STDOUT_FILENO);
+      if (execvp(prog[0], prog) < 0) {
+        lerror("Couldn't exec program");
+        exit(1);
+      }
+      break;
+      
+    case -1:
+      lerror("Error forking child process");
+      close(fdes[0]);
+      close(fdes[1]);
+      retcode = -1;
+      break;
+      
+    default:
+      close(fdes[1]);
+      in = fdopen(fdes[0], "r");
+      while (!feof(in) && !ferror(in)) {
+        if (fgets(buf, sizeof(buf), in)) {
+          if ((retcode = procfunc(buf, data)) < 0)  break;
+        }
+      }
+      fclose(in);
+      wait(&status);
+      if (retcode > -1)  retcode = WEXITSTATUS(status);
+      break;
+    } /* switch ((cpid = fork())) */
+  }
+  return retcode;
+}
+
 int
 getnumcpus(void)
 {
@@ -75,12 +128,6 @@ getmempages(void)
 	return (int)(total / pagesize);
 }
 
-int
-getcpubusy(void)
-{
-	return (1000 - cpu_state(CP_IDLE)) / 10;
-}
-
 int *
 getmembusy(unsigned totalpages)
 {
@@ -279,25 +326,23 @@ getdiskbusy(void)
  * Use the constants in <sys/dkstat.h>
  * CP_USER=0, CP_NICE=1, CP_SYS=2, CP_INTR=3, CP_IDLE=4
  */
-static
-int cpu_state(int which) {
+int *getcpustates() {
 
    static long cp_time[CPUSTATES];
    static long cp_old[CPUSTATES];
    static long cp_diff[CPUSTATES];
    static int cpu_states[CPUSTATES];
-   static long tot;
    size_t len = sizeof(cp_time);
-
+   
    /* Copy the last cp_time into cp_old */
    memcpy(&cp_old, &cp_time, CPUSTATES*sizeof(long));
    /* puts kern.cp_time array into cp_time */
    if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) == -1 || !len)
 	return 0;
    /* Use percentages function lifted from top(1) to figure percentages */
-   tot = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
+   percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
 
-   return cpu_states[which];
+   return cpu_states;
 }
 
 /*
diff --git a/sensors/canaryd/auxfuncs.h b/sensors/canaryd/auxfuncs.h
index 01c8da7a74e79efef8e275368061ac1fabc04344..8d9b25be734bf23a32ff17a5f968d1ab50bad397 100644
--- a/sensors/canaryd/auxfuncs.h
+++ b/sensors/canaryd/auxfuncs.h
@@ -4,7 +4,10 @@ int getcpuspeed(void);
 int getmempages(void);
 char **getdrivedata(char**);
 
-int getcpubusy(void);
 int *getmembusy(unsigned pages);
 int *getnetmembusy(void);
 int getdiskbusy(void);
+
+int *getcpustates();
+
+int procpipe(char *const prog[], int (procfunc)(char*,void*), void* data);
diff --git a/sensors/canaryd/canaryd.c b/sensors/canaryd/canaryd.c
index 719af5afad226395351981c4fc0bc8b34bd10920..0cf1a5e0f374866a5ce22ddc016672b546d24bc5 100644
--- a/sensors/canaryd/canaryd.c
+++ b/sensors/canaryd/canaryd.c
@@ -1,1111 +1,735 @@
 /*
- * EMULAB-COPYRIGHT
- * Copyright (c) 2000-2004 University of Utah and the Flux Group.
+ * canaryd.c
+ *
+ * Copyright (c) 2004 The University of Utah and the Flux Group.
  * All rights reserved.
+ *
+ * This file is licensed under the terms of the GNU Public License.  
+ * See the file "license.terms" for restrictions on redistribution 
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  */
 
-#include "canaryd.h"
-#include "config.h"
-
-CANARYD_OPTS   *opts;
-CANARYD_PARAMS *parms;
-
-void lerror(const char* msgstr) {
-  if (msgstr) {
-    syslog(LOG_ERR, "%s: %m", msgstr);
-    fprintf(stderr, "canaryd: %s: %s\n", msgstr, strerror(errno));
-  }
-}
-
-void lwarn(const char* msgstr) {
-  if (msgstr) {
-    syslog(LOG_WARNING, "%s", msgstr);
-    fprintf(stderr, "canaryd: %s\n", msgstr);
-  }
-}
-
-void lnotice(const char *msgstr) {
-  if (msgstr) {
-    syslog(LOG_NOTICE, "%s", msgstr);
-    printf("canaryd: %s\n", msgstr);
-  }
-}
-
-void sigunkhandler(int signum) {
-  int status;
-  char message[50];
-
-  sprintf(message, "Unhandled signal: %d.  Exiting.", signum);
-  lerror(message);
-  unlink(PIDFILE);
-  while (wait(&status) != -1);
-  exit(signum);
-}
-
-void siginthandler(int signum) {
-  parms->dolast = 1;
-}
-
-void sigalrmhandler(int signum) {
-  lnotice("Run length has expired; exiting.");
-  parms->dolast = 1;
-}
-
-void usage(void) {  
-
-  printf("Usage:\tcanaryd -h\n"
-         "\tcanaryd [-o] [-d] [-i <interval>] [-p <port>] [-s <server>]\n"
-         "\t        [-l] [-t <runlen>] [-r <interval>]\n"
-         "\t -h\t\t This message.\n"
-         "\t -o\t\t Run once (collect a single report).\n"
-         "\t -d\t\t Debug mode; do not fork into background.\n"
-         "\t -i <interval>\t Regular run interval, in seconds.\n"
-         "\t -p <port>\t Send on port <port>\n"
-         "\t -s <server>\t Send data to <server>\n"
-         "\t -l\t\t Run in logging mode.  Will log to: " DEF_CDLOG "\n"
-         "\t -t <runlen>\t Run for <runlen> seconds.\n"
-         "\t -r <interval>\t Report overload at most every <interval> seconds \n");
-  exit(0);
-}
-
-
-int main(int argc, char **argv) {
-
-  int exitcode = -1;
-  u_int span;
-  time_t curtime;
-  int rnd_off = 0;
-  static CANARYD_OPTS mopts;
-  static CANARYD_PARAMS mparms;
-  static CANARYD_PACKET mpkt;
-  CANARYD_PACKET *pkt;
-
-  extern char build_info[];
-
-  /* pre-init */
-  bzero(&mopts, sizeof(CANARYD_OPTS));
-  bzero(&mparms, sizeof(CANARYD_PARAMS));
-  bzero(&mpkt, sizeof(CANARYD_PACKET));
-  opts = &mopts;
-  parms = &mparms;
-  pkt = &mpkt;
-
-  if (parse_args(argc, argv) < 0) {
-    fprintf(stderr, "Error processing arguments.\n");
-    return exitcode;
-  }
+/**
+ * @file canaryd.c
+ *
+ * Implementation file for the main bits of canaryd.
+ */
 
-  if (init_canaryd() < 0) {
-    lerror("Problem initializing, bailing out.");
-    do_exit(exitcode);
-  }
+#include <config.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/dkstat.h>
+#include <devstat.h>
+#include <sys/stat.h>
+#include <sys/rtprio.h>
+#include <dirent.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+#include <unistd.h>
+
+#include "auxfuncs.h"
+#include "canarydEvents.h"
+#include "childProcess.h"
+#include "networkInterface.h"
+
+/**
+ * The PATH to use when canaryd executes another program.
+ */
+#define CANARYD_PATH_ENV "/bin:/usr/bin:/sbin:/usr/sbin:" CLIENT_BINDIR
 
-  get_vmem_stats(pkt); /* XXX: doesn't belong here.. */
-  exitcode = 0;
-  lnotice("Canaryd started");
-  lnotice(build_info);
+/**
+ * The path to this program's PID file.
+ */
+#define PIDFILE "/var/run/canaryd.pid"
 
-  for (;;) {
-    curtime = time(0);
+/**
+ * The canaryd log file path.
+ */
+#define CANARYD_LOG "/var/emulab/logs/canaryd.log"
 
-    /* Collect current machine stats */
-    mparms.cnt++;
-    get_load(pkt);
-    get_packet_counts(pkt);
-    get_vnode_stats(pkt);
-    get_vmem_stats(pkt);
-    check_overload(pkt);
-    
-    if (pkt->overload && parms->numvnodes/2 > 1) {
-      // (curtime >= mparms.lastolrpt + mopts.ol_rep_interval)) {
-      send_ol_event(pkt);
-      mparms.lastolrpt = curtime;
-    }
+/**
+ * Version information.
+ *
+ * @see version.c
+ */
+extern char build_info[];
 
-    /* Poll the event system */
-    event_poll(mparms.ev_handle);
+enum {
+    CDB_LOOPING,
+    CDB_TRACE_IMMEDIATELY,
+    CDB_TRACING,
+    CDB_DEBUG,
+};
 
-    /*
-     * Time to send a packet?
-     * Yes, if:
-     * 1) We've been idle, and now we see activity (aggressive mode)
-     * 2) Its been over <reg_interval> seconds since the last report
-     */
-    if ((curtime >=  mparms.lastrpt + mopts.interval) ||
-        parms->dolast) {
-      if (send_pkt(pkt)) {
-        mparms.lastrpt = curtime;
-      }
-      if (parms->dolast) {
-        break;
-      }
-    }
+/**
+ * Flags for the canaryd_data.cd_Flags variable.
+ */
+enum {
+    CDF_LOOPING = (1L << CDB_LOOPING),	/**< Continue Looping waiting for
+					   events. */
+     /**
+      * Start tracing immediately, do not wait for START event.
+      */
+    CDF_TRACE_IMMEDIATELY = (1L << CDB_TRACE_IMMEDIATELY),
+    CDF_TRACING = (1L << CDB_TRACING),	/**< Generate trace data. */
+    CDF_DEBUG = (1L << CDB_DEBUG),	/**< Debugging mode. */
+};
 
-    if (mopts.once) {
-      break;
-    }
-    
-    /* 
-     * Figure out, based on run count, and activity, how long
-     * to sleep.
-     */
-    if (mopts.log) {
-      span = mopts.interval;
-    }
-    /* randomly offset the first packet to avoid packet implosion. */
-    else if (mparms.cnt == 1) {
-      rnd_off = (rand() / (float) RAND_MAX) * 
-        (OFFSET_FRACTION*mopts.interval);
-      rnd_off = (rnd_off > 0) ? rnd_off : 0;
-      span = mopts.interval - rnd_off;
-    }
-    else {
-      span = mopts.interval;
-    }
-    
-    if (opts->debug) {
-      printf("About to sleep for %u seconds.\n", span);
-      fflush(stdout);
-    }
-    
-    if (parms->dolast) {
-      continue;
+/*
+ * Global data for canaryd.
+ *
+ * cd_Flags - Holds the CDF_ flags.
+ * cd_IntervalTime - The time to wait between samples.
+ * cd_CurrentTime - The current time of day.
+ * cd_OutputFile - The output file for the trace.
+ */
+struct {
+    int cd_Flags;
+    struct itimerval cd_IntervalTime;
+    struct timeval cd_CurrentTime;
+    struct timeval cd_StopTime;
+    FILE *cd_OutputFile;
+} canaryd_data;
+
+/**
+ * Log an error.
+ *
+ * @param msgstr The message to log.
+ */
+void lerror(const char* msgstr)
+{
+    if( msgstr != NULL )
+    {
+	syslog(LOG_ERR, "%s: %m", msgstr);
+	fprintf(stderr, "canaryd: %s: %s\n", msgstr, strerror(errno));
     }
-    
-    sleep(span);
-  }
-
-  return exitcode; /* NOTREACHED */
 }
 
-int parse_args(int argc, char **argv) {
-
-  char ch;
-
-  /* setup defaults. */
-  opts->once = 0;
-  opts->interval = DEF_INTVL;
-  opts->debug = 0;
-  opts->port = CANARYD_DEF_PORT;
-  opts->servname = BOSSNODE;
-  opts->log = 0;
-  opts->run_len = 0;
-  opts->ol_rep_interval = DEF_OLREPINTVL;
-
-  while ((ch = getopt(argc, argv, "oi:g:dp:s:lt:r:h")) != -1) {
-    switch (ch) {
-
-    case 'o': /* run once */
-      opts->once = 1;
-      break;
-
-    case 'i':
-      if ((opts->interval = atol(optarg)) < MIN_INTVL) {
-        lwarn("Warning!  Tnterval set too low, defaulting.");
-        opts->interval = MIN_INTVL;
-      }
-      break;
-
-    case 'd':
-      lnotice("Debug mode requested; staying in foreground.");
-      opts->debug = 1;
-      break;
-
-    case 'p':
-      opts->port = (u_short)atoi(optarg);
-      break;
-
-    case 's':
-      if (optarg && *optarg) {
-        opts->servname = strdup(optarg);
-      }
-      else {
-        lwarn("Invalid server name, default used.");
-      }
-      break;
-
-    case 'l':
-      lnotice("Logging mode enabled");
-      opts->log = 1;
-      break;
-
-    case 't':
-      opts->run_len = strtoul(optarg, NULL, 0);
-      break;
-      
-
-    case 'r':
-      opts->ol_rep_interval = strtoul(optarg, NULL, 0);
-      break;
-
-    case 'h':
-    default:
-      usage();
-      return -1;
-      break;
+/**
+ * Log a warning.
+ *
+ * @param msgstr The message to log.
+ */
+void lwarn(const char* msgstr)
+{
+    if( msgstr != NULL )
+    {
+	syslog(LOG_WARNING, "%s", msgstr);
+	fprintf(stderr, "canaryd: %s\n", msgstr);
     }
-  }
-
-  return 0;
 }
 
-
-int init_canaryd(void) {
-
-  int pfd, i;
-  char pidbuf[10];
-  char servbuf[MAXHNAMELEN];
-  struct hostent *hent;
-  char *ciprog[] = {"control_interface", NULL};
-  char *stprog[] = {"tmcc", "status", NULL};
-  struct rtprio myprio = {RTP_PRIO_REALTIME, 0};
-  char *canaryd_evts = TBDB_EVENTTYPE_START ", " TBDB_EVENTTYPE_STOP ", "
-                      TBDB_EVENTTYPE_RESET ", " TBDB_EVENTTYPE_REPORT;
-  address_tuple_t tuple;
-  char *driveargv[] = {"ad0", NULL};
-
-  /* init internal vars */
-  parms->dolast = 0;  /* init send-last-report-before-exiting variable */
-  parms->cnt = 0;     /* daemon iter count */
-  parms->lastrpt = 0; /* the last time a report pkt was sent */
-  parms->startup = time(NULL); /* Make sure we don't report < invocation */
-  parms->pideid = "";
-  parms->ev_server = "boss.emulab.net"; /* XXX */
-  parms->ol_detect = 0;
-  parms->send_ev_report = 0;
-  parms->numvnodes = 0;
-
-  /* Event arg items */
-  for (i = 0; i < NUMRANGES; ++i) {
-    parms->olr[i].min = evargitems[i].defmin;
-    parms->olr[i].max = evargitems[i].defmax;
-  }
-
-  /* Setup signals */
-  signal(SIGTERM, siginthandler);
-  signal(SIGINT, siginthandler);
-  signal(SIGQUIT, siginthandler);
-  signal(SIGALRM, sigalrmhandler);
-  signal(SIGHUP, SIG_IGN);
-  signal(SIGPIPE, SIG_IGN);
-  signal(SIGBUS, sigunkhandler);
-  signal(SIGSEGV, sigunkhandler);
-  signal(SIGFPE, sigunkhandler);
-  signal(SIGILL, sigunkhandler);
-  signal(SIGSYS, sigunkhandler);
-
-  /* Set the priority to realtime */
-  if (rtprio(RTP_PRIO_REALTIME, getpid(), &myprio) < 0) {
-    lerror("Couldn't set RT priority!");
-  }
-
-  /* Setup logging facil. */
-  openlog("canaryd", LOG_NDELAY, LOG_TESTBED);
-
-  /* Setup path */
-  if (setenv("PATH", CANARYD_PATH_ENV, 1) < 0) {
-    lerror("Couldn't set path env");
-  }
-
-  /* Seed the random number generator */
-  srand(time(NULL));
-
-  /* Grab control net iface */
-  if (procpipe(ciprog, &grab_cifname, parms)) {
-    lwarn("Failed to get control net iface name");
-  }
-
-  /* Get the expt pid/eid. */
-  if (procpipe(stprog, &grab_pideid, parms)) {
-    lwarn("Failed to get pid/eid");
-  }
-
-  /* Open the logfile, if necessary */
-  if (opts->log) {
-    if ((parms->log = fopen(DEF_CDLOG, "w")) == NULL) {
-      lerror("Failed to open log file");
-      return -1;
-    }
-  }
-
-  /************************************************************
-   *                REGISTER WITH EVENT SYSTEM                *
-   ************************************************************/
-
-  /*
-   * Find the local monitor node 
-   */
-#ifdef COMMENT /* XXX: fixup to use expt-specific mon node's elvind */
-  if ((ssfile = fopen(SYNCSERVFILE, "r")) == NULL) {
-    lerror("Failed to open sync server ident file");
-    return -1;
-  }
-
-  if ((fgets(servbuf, sizeof(servbuf), ssfile) == NULL) ||
-      ! *servbuf) {
-    lerror("No sync server name found in file!");
-    fclose(ssfile);
-    return -1;
-  }
-
-  if (servbuf[strlen(servbuf)-1] == '\n') {
-    servbuf[strlen(servbuf)-1] = '\0';
-  }
-  parms->ev_server = strdup(servbuf);
-  fclose(ssfile);
-#endif
-
-  /*
-   * Convert server/port to elvin thing.
-   *
-   * XXX This elvin string stuff should be moved down a layer. 
-   */
-
-  if (parms->ev_server) {
-    snprintf(servbuf, sizeof(servbuf), "elvin://%s",
-             parms->ev_server);
-    /* free(parms->ev_server); */
-    parms->ev_server = strdup(servbuf);
-  }
-
-  /*
-   * Construct an address tuple for subscribing to events for
-   * this node.
-   */
-  tuple = address_tuple_alloc();
-  if (tuple == NULL) {
-    lerror("could not allocate an address tuple");
-    return -1;
-  }
-
-  printf("pid/eid: %s\n", parms->pideid);
-
-  /*
-   * Ask for canaryd agent events
-   */
-  tuple->host	   = ADDRESSTUPLE_ANY;
-  tuple->site      = ADDRESSTUPLE_ANY;
-  tuple->group     = ADDRESSTUPLE_ANY;
-  tuple->expt      = parms->pideid;
-  tuple->objtype   = TBDB_OBJECTTYPE_CANARYD;
-  tuple->objname   = ADDRESSTUPLE_ANY;
-  tuple->eventtype = canaryd_evts;
-  
-  /*
-   * Register with the event system. 
-   */
-  parms->ev_handle = event_register_withkeyfile(parms->ev_server, 0,
-                                                EVENTKEYFILE);
-  if (parms->ev_handle == NULL) {
-    lerror("could not register with event system");
-    return -1;
-  }
-  
-  /*
-   * Subscribe to the event we specified above.
-   */
-  if (! event_subscribe(parms->ev_handle, ev_callback, tuple, NULL)) {
-    lerror("could not subscribe to event");
-    return -1;
-  }
-
-  address_tuple_free(tuple); /* XXX: keep around for sending evts. */
-
-#ifdef __linux__
-  /* Open socket for SIOCGHWADDR ioctl (to get mac addresses) */
-  parms->ifd = socket(PF_INET, SOCK_DGRAM, 0);
-#endif
-
-  /* Setup "vmstat" stuff */
-  getdrivedata(driveargv);
-
-  /* prepare UDP connection to server */
-  if ((parms->sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-    lerror("Could not alloc socket");
-    return -1;
-  }
-  if (!(hent = gethostbyname(opts->servname))) {
-    lerror("Can't resolve server hostname"); /* XXX use herror */
-    return -1;
-  }
-  bzero(&parms->servaddr, sizeof(struct sockaddr_in));
-  parms->servaddr.sin_family = AF_INET;
-  parms->servaddr.sin_port = htons(opts->port);
-  bcopy(hent->h_addr_list[0], &parms->servaddr.sin_addr.s_addr, 
-        sizeof(struct in_addr));
-  if (connect(parms->sd, (struct sockaddr*)&parms->servaddr, 
-              sizeof(struct sockaddr_in)) < 0) {
-    lerror("Couldn't connect to server");
-    return -1;
-  }
-
-  /* Daemonize, unless in debug, or once-only mode. */
-  if (!opts->debug && !opts->once) {
-    if (daemon(0,0) < 0) {
-      lerror("Couldn't daemonize");
-      return -1;
-    }
-    /* Try to get lock.  If can't, then bail out. */
-    if ((pfd = open(PIDFILE, O_EXCL | O_CREAT | O_RDWR)) < 0) {
-      lerror("Can't create lock file.");
-      return -1;
+/**
+ * Log a notice.
+ *
+ * @param msgstr The message to log.
+ */
+void lnotice(const char *msgstr)
+{
+    if( msgstr != NULL )
+    {
+	syslog(LOG_NOTICE, "%s", msgstr);
+	printf("canaryd: %s\n", msgstr);
     }
-    fchmod(pfd, S_IRUSR | S_IRGRP | S_IROTH);
-    sprintf(pidbuf, "%d", getpid());
-    write(pfd, pidbuf, strlen(pidbuf));
-    close(pfd);
-  }
-
-  /* Setup run length alarm, if needed. */
-  if (opts->run_len) {
-    alarm(opts->run_len);
-  }
-
-  return 0;
 }
 
-
-void do_exit(int exval) {
-  int status;
-
-  unlink(PIDFILE);
-  if (event_unregister(parms->ev_handle) == 0) {
-    lnotice("could not unregister from event system");
-  }
-  while (wait(&status) != -1);
-  lnotice("exiting.");
-  exit(exval);
+/**
+ * Empty signal handler for SIGALRM.  The mechanics are done in the main loop,
+ * we just use SIGALRM to break the sigsuspend.
+ */
+void sigalrm(int signum)
+{
 }
 
-int grab_cifname(char *buf, void *data) {
-
-  int retval = -1;
-  char *tmpptr;
-  CANARYD_PARAMS *myparms = (CANARYD_PARAMS*) data;
-
-  if (buf && isalpha(buf[0])) {
-    tmpptr = myparms->cifname = strdup(buf);
-    while (isalnum(*tmpptr))  tmpptr++;
-    *tmpptr = '\0';
-    retval = 0;
-  }
-  else {
-    myparms->cifname = NULL;
-  }
-
-  return retval;
+/**
+ * Signal handler that will cause the program to exit from the main loop.
+ */
+void sigexit(int signum)
+{
+    canaryd_data.cd_Flags &= ~CDF_LOOPING;
 }
 
-int grab_pideid(char *buf, void *data) {
-  int retval = -1;
-  char *bp, *tmpbp;
-  CANARYD_PARAMS *myparms = (CANARYD_PARAMS*) data;
-
-  if (strstr(buf, "ALLOCATED=")) {
-    bp = tmpbp = buf+10;
-    while (!isspace(*tmpbp)) tmpbp++;
-    *tmpbp = '\0';
-    myparms->pideid = strdup(bp);
-    retval = 0;
-  }
-  else {
-    myparms->pideid = "";
-  }
-
-  return retval;
+/**
+ * Print the tool's usage message.
+ *
+ * @param file The file to send the usage to.
+ * @param prog_name The program's real name, as given on the command line.
+ */
+static void cdUsage(FILE *file, char *prog_name)
+{
+    fprintf(file,
+	    "Usage: %s [-hVdt]\n"
+	    "\n"
+	    "Physical node monitoring daemon.\n"
+	    "\n"
+	    "Options:\n"
+	    "  -h\t\tThis help message.\n"
+	    "  -V\t\tShow the version number.\n"
+	    "  -d\t\tDebug mode; do not fork into the background.\n"
+	    "  -t\t\tStart tracing immediately.\n",
+	    prog_name);
 }
 
-enum {
-  SITEIDX    = 0,
-  EXPTIDX    = 1,
-  GROUPIDX   = 2,
-  HOSTIDX    = 3,
-  OBJTYPEIDX = 4,
-  OBJNAMEIDX = 5,
-  EVTYPEIDX  = 6,
-  ARGSIDX  = 7
-};
-
-void ev_callback(event_handle_t handle, event_notification_t notification, 
-                 void *data) {
-
-  char		buf[8][128]; /* XXX: bad magic */
-  int		len = 128;
-  struct timeval	now;
-  static int ecount;
-
-  bzero(buf, sizeof(buf));
-
-  event_notification_get_site(handle, notification, buf[SITEIDX], len);
-  event_notification_get_expt(handle, notification, buf[EXPTIDX], len);
-  event_notification_get_group(handle, notification, buf[GROUPIDX], len);
-  event_notification_get_host(handle, notification, buf[HOSTIDX], len);
-  event_notification_get_objtype(handle, notification, buf[OBJTYPEIDX], len);
-  event_notification_get_objname(handle, notification, buf[OBJNAMEIDX], len);
-  event_notification_get_eventtype(handle, notification, buf[EVTYPEIDX], len);
-  event_notification_get_arguments(handle, notification, buf[ARGSIDX], len);
-  
-  if (opts->debug) {          
-    gettimeofday(&now, NULL);
-    fprintf(stderr,"Event %d: %lu.%03lu %s %s %s %s %s %s %s %s\n",
-            ++ecount, now.tv_sec, now.tv_usec / 1000,
-            buf[SITEIDX], buf[EXPTIDX], buf[GROUPIDX],
-            buf[HOSTIDX], buf[OBJTYPEIDX], buf[OBJNAMEIDX], 
-            buf[EVTYPEIDX], buf[ARGSIDX]);
-  }
-
-  if (!strcmp(buf[EVTYPEIDX], TBDB_EVENTTYPE_START)) {
-    if (! *buf[ARGSIDX]) {
-      lnotice("Arguments not provided in start event - ignoring.");
-    }
-    else {
-      parse_ev_args(buf[ARGSIDX]);
-    }
-  }
-  
-  else if (!strcmp(buf[EVTYPEIDX], TBDB_EVENTTYPE_START)) {
-    parms->ol_detect = 0;
-  }
-
-  else if (!strcmp(buf[EVTYPEIDX], TBDB_EVENTTYPE_RESET)) {
-    /* set_default_overload_params(); */
-  }
-
-  else if (!strcmp(buf[EVTYPEIDX], TBDB_EVENTTYPE_REPORT)) {
-    parms->send_ev_report = 1;
-  }
-
-  else {
-    lnotice("Unknown event type received.");
-  }
-
-  return;
+/**
+ * Process the command line options.
+ *
+ * @param argc The argc passed to main.
+ * @param argv The argv passed to main.
+ * @return Zero on success, one if there was an error and the usage message
+ * should be printed, or -1 if there was no error but the tool should exit
+ * immediately.
+ */
+static int cdProcessOptions(int argc, char *argv[])
+{
+    int ch, retval = 0;
+    char *prog_name;
+
+    prog_name = argv[0];
+    while( ((ch = getopt(argc, argv, "hVdt")) != -1) && (retval == 0) )
+    {
+	switch( ch )
+	{
+	case 'd':
+	    canaryd_data.cd_Flags |= CDF_DEBUG;
+	    break;
+	case 't':
+	    canaryd_data.cd_Flags |= CDF_TRACE_IMMEDIATELY;
+	    break;
+	case 'V':
+	    fprintf(stderr, "%s\n", build_info);
+	    retval = -1;
+	    break;
+	case 'h':
+	case '?':
+	default:
+	    retval = 1;
+	    break;
+	}
+    }
+    return( retval );
 }
 
-void send_ol_event(CANARYD_PACKET *pkt) {
-
-  int vnbufsiz, minibufsiz, i;
-  char minibuf[50];
-  char vnbuf[2000];
-  event_notification_t notification;
-  address_tuple_t tuple;
-  struct timeval now;
-
-  gettimeofday(&now,0);
-
-  tuple = address_tuple_alloc();
-  if (tuple == NULL) {
-    lerror("could not allocate an address tuple");
-    return;
-  }
-
-  /*
-   * Setup tuple to send profile alert
-   */
-  tuple->host	   = ADDRESSTUPLE_ANY;
-  tuple->site      = ADDRESSTUPLE_ANY;
-  tuple->group     = ADDRESSTUPLE_ANY;
-  tuple->expt      = parms->pideid;
-  tuple->objtype   = TBDB_OBJECTTYPE_CANARYD;
-  tuple->objname   = "canaryd";
-  tuple->eventtype = TBDB_EVENTTYPE_ALERT;
-
-  /* Generate the event */
-  notification = event_notification_alloc(parms->ev_handle, tuple);
+/**
+ * Write the PID file for this program.
+ *
+ * @param path The path to the file.
+ * @param pid The PID to store in the file.
+ */
+static int cdWritePIDFile(char *path, pid_t pid)
+{
+    int fd, retval = 1;
+
+    if( (fd = open(path, O_EXCL | O_CREAT | O_WRONLY)) < 0 )
+    {
+	retval = 0;
+    }
+    else
+    {
+	char scratch[32];
+	int rc;
 	
-  if (notification == NULL) {
-    lnotice("could not allocate notification");
-    return;
-  }
-
-  strcpy(vnbuf, "vnodes=");
-  vnbufsiz=7;
-
-  /* add vnode info, if necessary */
-  for (i = 0; pkt->vnodes[i].hname; ++i) {
-    sprintf(minibuf, "%s,", pkt->vnodes[i].hname);
-    minibufsiz = strlen(minibuf);
-    if (vnbufsiz + minibufsiz > sizeof(vnbuf)) {
-      lerror("vnbuf not big enough to hold all content!");
-      abort();
+	rc = snprintf(scratch, sizeof(scratch), "%d", pid);
+	write(fd, scratch, rc);
+	fchmod(fd, S_IRUSR | S_IRGRP | S_IROTH);
+	close(fd);
+	fd = -1;
     }
-    strcat(vnbuf, minibuf);
-    vnbufsiz += minibufsiz;
-  }
-  vnbuf[vnbufsiz-1] = '\0';
-
-  event_notification_set_arguments(parms->ev_handle, notification, vnbuf);
-  
-  if (event_schedule(parms->ev_handle, notification, &now) == 0) {
-    lnotice("could not send test event notification");
-  }
-
-  if (opts->debug) {
-    printf("** Alert event sent **\n");
-  }
-
-  event_notification_free(parms->ev_handle, notification);
-  address_tuple_free(tuple);
-  return;
-}
-
-  
-
-int SETRANGE(char *token, RANGE *range) {
-  char *tok = token;
-  char *ebp;
-
-  range->min = strtod(tok, &ebp);
-  if (ebp == tok) {
-    lnotice("bad data in numeric conversion");
-    goto bad;
-  }
-  if (*ebp != ',') {
-    lnotice("unexpected/missing separator in range");
-    goto bad;
-  }
-  tok = ebp+1;
-  range->max = strtod(tok, &ebp);
-  if (ebp == tok) {
-    lnotice("bad data in numeric conversion");
-    goto bad;
-  }
-
-  if (opts->debug) {
-    printf("range min/max: %.03f/%.03f\n", range->min, range->max);
-  }
-  return 1;
-
- bad:
-  range->min = 0;
-  range->max = 0;
-  return 0;
+    return( retval );
 }
 
-#define EVARGSEPS " \t"
-
-void parse_ev_args(char *args) {
-
-  int i;
-  char *tok;
-
-  tok = strtok(args, EVARGSEPS);
-  do {
-    if (tok && *tok) {
-      if (opts->debug) {
-        printf("ev arg token: %s\n", tok);
-      }
-      for(i = 0; i < NUMRANGES; ++i) {
-        if (strstr(tok, evargitems[i].key)) {
-          tok += strlen(evargitems[i].key)+1;
-          if (! SETRANGE(tok, &parms->olr[i])) goto bad;
-          break;
-        }
-      }
-      if (i == NUMRANGES) {
-        lnotice("Unknown token in args list");
-        goto bad;
-      }
-    }
-  } while ((tok = strtok(NULL, EVARGSEPS)) != NULL);
-
-  parms->ol_detect = 1;
-  return;
-
- bad:
-  lnotice("Problem encountered while parsing event args");
-  parms->ol_detect = 0;
-  return;
-}
-
-
-int send_pkt(CANARYD_PACKET *pkt) {
-
-  int i, numsent, retval = 1;
-  static char pktbuf[8192];
-  static char minibuf[128];
-  int pktbufsiz, minibufsiz;
-  struct timeval now;
-
-  /* Get current time. */
-  gettimeofday(&now, NULL);
-
-  /* flatten data into packet buffer */
-  sprintf(pktbuf, "vers=%s stamp=%lu,%lu lave=%.10f,%.10f,%.10f ovld=%u cpu=%u mem=%u,%u disk=%u netmem=%u,%u,%u ",
-          CDPROTOVERS,
-          now.tv_sec,
-          now.tv_usec,
-          pkt->loadavg[0],
-          pkt->loadavg[1],
-          pkt->loadavg[2],
-          pkt->overload,
-          pkt->olmet[PCTCPU],
-          pkt->olmet[PCTMEM],
-          pkt->olmet[PGOUT],
-          pkt->olmet[PCTDISK],
-          pkt->olmet[PCTNETMEM],
-          pkt->olmet[NETMEMWAITS],
-          pkt->olmet[NETMEMDROPS]);
-
-  pktbufsiz = strlen(pktbuf);
-  
-  /* get all the interfaces too */
-  for (i = 0; i < pkt->ifcnt; ++i) {
-    sprintf(minibuf, "iface=%s,%lu,%lu,%llu,%llu ",
-            pkt->ifaces[i].addr,
-            pkt->ifaces[i].ipkts,
-            pkt->ifaces[i].opkts,
-            pkt->ifaces[i].ibytes,
-            pkt->ifaces[i].obytes);
-    minibufsiz = strlen(minibuf);
-    if (pktbufsiz + minibufsiz + 1 > sizeof(pktbuf)) {
-      lerror("pktbuf not big enough to hold all content!");
-      abort();
-    }
-    strcat(pktbuf, minibuf);
-    pktbufsiz += minibufsiz;
-  }
-
-  /* add vnode info, if necessary */
-  for (i = 0; pkt->vnodes[i].hname; ++i) {
-    sprintf(minibuf, "vnode=%s,%.1f,%.1f ",
-            pkt->vnodes[i].hname,
-            pkt->vnodes[i].cpu,
-            pkt->vnodes[i].mem);
-    minibufsiz = strlen(minibuf);
-    if (pktbufsiz + minibufsiz + 1 > sizeof(pktbuf)) {
-      lerror("pktbuf not big enough to hold all content!");
-      abort();
-    }
-    strcat(pktbuf, minibuf);
-    pktbufsiz += minibufsiz;
-  }
-
-
-  if (opts->debug) {
-    printf("packet: %s\n", pktbuf);
-    printf("packet len: %d\n", pktbufsiz);
-  }
-  
-  /* send it */
-  else {
-    if (opts->log) {
-      fputs(pktbuf, parms->log);
-      fputs("\n", parms->log);
-      fflush(parms->log);
-      if (ferror(parms->log)) {
-        lwarn("Error on log file stream");
-      }
-    }
-    else {
-      if ((numsent = send(parms->sd, pktbuf, pktbufsiz+1, 0)) < 0) {
-        lerror("Problem sending canaryd packet");
-        retval = 0;
-      }
+/**
+ * Start tracing the system's performance.
+ *
+ * @param path The trace file path.
+ * @return True on success, false otherwise.
+ */
+static int cdStartTracing(char *path)
+{
+    int retval = 1;
     
-      else if (numsent < pktbufsiz+1) {
-        lwarn("Warning!  Canaryd packet truncated");
-        retval = 0;
-      }
-    }
-  }
-
-  return retval;
-}
-
-int process_ps(char *psln, void *data) {
-
-  VPROCENT *proctab = (VPROCENT*)data;
-  int i;
-  int pid;
-  float cpu, mem;
-  char *eptr;
-
-  /* skip label line */
-  if ((strtod(psln, &eptr) == 0) && (eptr == psln)) {
-    return 0;
-  }
-
-  sscanf(psln, "%u %f %f", &pid, &cpu, &mem);
-
-  for (i = 0; proctab[i].hname; ++i) {
-    if (proctab[i].pid == pid) {
-      break;
-    }
-  }
-
-  if (proctab[i].hname) {
-    proctab[i].cpu = cpu;
-    proctab[i].mem = mem;
-  }
-
-  return 0;
+    if( canaryd_data.cd_Flags & CDF_TRACING )
+    {
+	/* We're already tracing, nothing to do. */
+    }
+    else if( canaryd_data.cd_Flags & CDF_DEBUG )
+    {
+	/* Debug sends to stdout... */
+	canaryd_data.cd_OutputFile = stdout;
+	canaryd_data.cd_Flags |= CDF_TRACING;
+    }
+    else if( (canaryd_data.cd_OutputFile = fopen(path, "w")) == NULL )
+    {
+	lerror("Could not create trace file.");
+	retval = 0;
+    }
+    else
+    {
+	/* Good to go. */
+	canaryd_data.cd_Flags |= CDF_TRACING;
+    }
+    return( retval );
 }
-  
-void get_vnode_stats (CANARYD_PACKET *pkt) {
-
-  int i;
-  DIR *pfsdir;
-  struct dirent *procent;
-  FILE *procfile;
-  char sfilename[MAXPATHLEN];
-  char statbuf[MAXLINELEN];
-  static VPROCENT proctab[MAXPROCNUM];
-  unsigned int pid;
-  char hname[MAXHOSTNAMELEN];
-  char *endlptr;
-  char *psprog[] = {"ps", "ax", "-o", "pid,%cpu,%mem", NULL};
-
-  for (i = 0; pkt->vnodes[i].hname; ++i) {
-    pkt->vnodes[i].cpu = 0;
-    pkt->vnodes[i].mem = 0;
-  }
-
-  /* Iterate over the /proc entries, looking for jailed nodes.
-     Once found, we grab their usage stats and pack them up to
-     be sent back.
-  */
-  if ((pfsdir = opendir("/proc")) < 0) {
-    lerror("Problem opening /proc");
-    return;
-  }
-
-  i = 0;
-  while ((procent = readdir(pfsdir)) != NULL) {
-    if (!strcmp(procent->d_name, "curproc") ||
-        (procent->d_name[0] == '.')) {
-      continue;
-    }
-
-    snprintf(sfilename, sizeof(sfilename), "/%s/%s/%s", 
-             "proc", 
-             procent->d_name,
-             "status");
-
-    if ((procfile = fopen(sfilename, "r")) == NULL) {
-      lerror("problem opening process file entry");
-      continue;
-    }
-
-    fgets(statbuf, sizeof(statbuf), procfile);
-    fclose(procfile);
-    sscanf(statbuf, "%*s %u %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %s",
-           &pid, hname);
-    endlptr = strchr(hname, '\n');
-    if (endlptr) {
-      *endlptr = '\0';
-    }
-
-    if (!strcmp(hname, "-")) {
-      continue;
-    }
 
-    proctab[i].pid = pid;
-    proctab[i].hname = strdup(hname);
-    proctab[i].cpu = 0;
-    proctab[i].mem = 0;
-    i++;
-  }
-
-  closedir(pfsdir);
-  procpipe(psprog, process_ps, (void*)proctab);
-  
-  for (i = 0; proctab[i].hname; ++i) {
-    int j;
-
-    for (j = 0; pkt->vnodes[j].hname; ++j) {
-      if (!strcmp(pkt->vnodes[j].hname, proctab[i].hname)) {
-        break;
-      }
-    }
-
-    if (!pkt->vnodes[j].hname) {
-      pkt->vnodes[j].hname = strdup(proctab[i].hname);
-      parms->numvnodes++;
+/**
+ * Stop tracing the system's performance, if we haven't already.
+ */
+static void cdStopTracing(void)
+{
+    if( !(canaryd_data.cd_Flags & CDF_TRACING) )
+    {
+	/* We're already stopped, nothing to do. */
+    }
+    else
+    {
+	if( !(canaryd_data.cd_Flags & CDF_DEBUG) )
+	{
+	    fclose(canaryd_data.cd_OutputFile);
+	}
+	canaryd_data.cd_OutputFile = NULL;
+	canaryd_data.cd_Flags &= ~CDF_TRACING;
     }
-    pkt->vnodes[j].cpu += proctab[i].cpu;
-    pkt->vnodes[j].mem += proctab[i].mem;
-    free(proctab[i].hname);
-    proctab[i].hname = NULL;
-  }
-
-  return;
 }
 
-void get_vmem_stats(CANARYD_PACKET *pkt) {
-
-  int *pres;
-
-  pkt->olmet[PCTCPU] = getcpubusy();
-  pres = getmembusy(getmempages());
-  pkt->olmet[PGOUT] = pres[0];
-  pkt->olmet[PCTMEM] = pres[1];
-  pkt->olmet[PCTDISK] = getdiskbusy();
-  pres = getnetmembusy();
-  pkt->olmet[PCTNETMEM] = pres[0];
-  pkt->olmet[NETMEMWAITS] = pres[1];
-  pkt->olmet[NETMEMDROPS] = pres[2];
-
-  return;
+/**
+ * Scan the "/proc" file system and gather statistics for all of the processes.
+ *
+ * @return True on success, false otherwise.
+ */
+static int cdScanProcFS(void)
+{
+    int retval = 1;
+    DIR *pfs;
+
+    if( (pfs = opendir("/proc")) == NULL )
+    {
+	lerror("Could not open /proc");
+	retval = 0;
+    }
+    else
+    {
+	struct dirent *de;
+
+	/* Walk the directories. */
+	while( retval && ((de = readdir(pfs)) != NULL) )
+	{
+	    pid_t pid;
+
+	    /*
+	     * If the directory name is a number, assume its for a real
+	     * process.
+	     */
+	    if( sscanf(de->d_name, "%d", &pid) == 1 )
+	    {
+		struct cpChildProcess *cp;
+
+		/* Its a valid process, try to find/create it. */
+		if( (cp = cpFindChildProcess(pid)) == NULL )
+		{
+		    cp = cpCreateChildProcess(pid);
+		}
+		if( cp == NULL )
+		{
+		    lerror("out of memory");
+		    retval = 0;
+		}
+		else
+		{
+		    /*
+		     * Increment the generation so the cpChildProcess object
+		     * won't be garbage collected.
+		     */
+		    cp->cp_Generation += 1;
+		    if( cp->cp_VNode != NULL )
+		    {
+			/* Its a vnode, sample its usage. */
+			cpSampleUsage(cp);
+		    }
+		}
+	    }
+	}
+	closedir(pfs);
+
+	/* Do the garbage collection. */
+	cpCollectChildProcesses();
+    }
+    return( retval );
 }
 
-void check_overload(CANARYD_PACKET *pkt) {
-
-  int i;
-  
-  pkt->overload = 0;
-  for (i = 0; i < NUMRANGES; ++i) {
-    if (pkt->olmet[i] > (int)parms->olr[i].max) {
-      pkt->overload = 1;
+/**
+ * Print the statistics for the network interfaces.
+ *
+ * @param output_file The file to write the statistics to.
+ * @return True on success, false otherwise.
+ */
+static int cdPrintNetworkInterfaces(FILE *output_file)
+{
+    struct ifaddrs *ifa;
+    int retval = 1;
+    
+    if( getifaddrs(&ifa) < 0 )
+    {
+	lerror("getifaddrs failed");
+	retval = 0;
+    }
+    else
+    {
+	struct ifaddrs *curr;
+
+	/* Walk the list of interfaces. */
+	curr = ifa;
+	while( curr != NULL )
+	{
+	    struct if_data *id;
+	    
+	    if( (id = curr->ifa_data) != NULL ) // Link level only.
+	    {
+		char ifname[64];
+
+		/* Print out the stats. */
+		fprintf(output_file,
+			" iface=%s,%ld,%ld,%ld,%ld",
+			niFormatMACAddress(ifname, curr->ifa_addr),
+			id->ifi_ipackets,
+			id->ifi_ibytes,
+			id->ifi_opackets,
+			id->ifi_obytes);
+	    }
+	    curr = curr->ifa_next;
+	}
+	
+	freeifaddrs(ifa);
+	ifa = NULL;
     }
-  }
+    return( retval );
 }
 
-void get_load(CANARYD_PACKET *pkt) {
+int main(int argc, char *argv[])
+{
+    int rc, retval = EXIT_SUCCESS;
+    struct sigaction sa;
+    struct rtprio rtp;
+    sigset_t sigmask;
 
-  int retval;
+    /* Initialize our globals. */
+    canaryd_data.cd_Flags |= CDF_LOOPING;
 
-  if ((retval = getloadavg(pkt->loadavg, 3)) < 0 || retval < 3) {
-    lerror("unable to obtain load averages");
-    pkt->loadavg[0] = pkt->loadavg[1] = pkt->loadavg[2] = -1;
-  }
-  else if (opts->debug) {
-    printf("load averages: %f, %f, %f\n", pkt->loadavg[0], pkt->loadavg[1], 
-           pkt->loadavg[2]);
-  }
-  return;
-}
+    /* Run for forever. */
+    canaryd_data.cd_StopTime.tv_sec = LONG_MAX;
+    canaryd_data.cd_StopTime.tv_usec = LONG_MAX;
+    
+    canaryd_data.cd_IntervalTime.it_interval.tv_sec = 1;
+    canaryd_data.cd_IntervalTime.it_interval.tv_usec = 0;
+    canaryd_data.cd_IntervalTime.it_value.tv_sec = 1;
+    canaryd_data.cd_IntervalTime.it_value.tv_usec = 0;
+
+    /* Prime the sigaction for capturing SIGALRM. */
+    sigemptyset(&sigmask);
+    sigaddset(&sigmask, SIGALRM);
+    sigaddset(&sigmask, SIGINT);
+    sigaddset(&sigmask, SIGTERM);
+    
+    sa.sa_mask = sigmask;
+    sa.sa_flags = 0;
+#if defined(SA_RESTART)
+    sa.sa_flags |= SA_RESTART;
+#endif
 
+    sa.sa_handler = sigalrm;
+    sigaction(SIGALRM, &sa, NULL);
 
-void get_packet_counts(CANARYD_PACKET *pkt) {
+    sa.sa_handler = sigexit;
+    sigaction(SIGTERM, &sa, NULL);
+    sigaction(SIGINT, &sa, NULL);
 
-  int i;
-  char *niprog[] = {"netstat", NETSTAT_ARGS, NULL};
+    rtp.type = RTP_PRIO_REALTIME;
+    rtp.prio = 0;
 
-  pkt->ifcnt = 0;
-  if (procpipe(niprog, &get_counters, (void*)pkt)) {
-    lwarn("Netinfo exec failed.");
-    pkt->ifcnt = 0;
-  }
-  else if (opts->debug) {
-    for (i = 0; i < pkt->ifcnt; ++i) {
-      printf("IFACE: %s  ipkts: %lu  opkts: %lu ibytes: %llu obytes: %llu\n", 
-             pkt->ifaces[i].ifname, 
-             pkt->ifaces[i].ipkts,
-             pkt->ifaces[i].opkts,
-             pkt->ifaces[i].ibytes,
-             pkt->ifaces[i].obytes);
-    }
-  }
-  return;
+    getdrivedata((char *[]){ "ad0", NULL });
+    
+    openlog("canaryd", LOG_NDELAY, LOG_TESTBED);
+    
+    setenv("PATH", CANARYD_PATH_ENV, 1);
+    
+    switch( cdProcessOptions(argc, argv) )
+    {
+    case 1:
+	/* There was an error processing the arguments. */
+	cdUsage(stderr, argv[0]);
+	retval = EXIT_FAILURE;
+	break;
+    case -1:
+	/* No errors, but nothing else to do either. */
+	break;
+    case 0:
+	/* Optionally daemonize, */
+	if( !(canaryd_data.cd_Flags & CDF_DEBUG) && ((rc = daemon(0, 0)) < 0) )
+	{
+	    lerror("Could not daemonize.");
+	    retval = EXIT_FAILURE;
+	}
+	/* ... dump our PID file, */
+	else if( !cdWritePIDFile(PIDFILE, getpid()) )
+	{
+	    lerror("Could not write PID file.");
+	    retval = EXIT_FAILURE;
+	}
+	/* ... optionally start tracing immediately, */
+	else if( (canaryd_data.cd_Flags & CDF_TRACE_IMMEDIATELY) &&
+		 !cdStartTracing(CANARYD_LOG) )
+	{
+	    retval = EXIT_FAILURE;
+	}
+	/* ... bump our priority, */
+	else if( rtprio(RTP_SET, getpid(), &rtp) < 0 )
+	{
+	    lerror("Could not set real-time priority!");
+	    retval = EXIT_FAILURE;
+	}
+	/* ... initialize internals, */
+	else if( !cpInitChildProcessData() )
+	{
+	    lerror("Could not initialize internal state");
+	    retval = EXIT_FAILURE;
+	}
+	/* ... connect to the event system, */
+	else if( !ceInitCanarydEvents("elvin://boss.emulab.net") )
+	{
+	    lerror("Could not initialize events");
+	    retval = EXIT_FAILURE;
+	}
+	/* ... setup interval timer, and */
+	else if( setitimer(ITIMER_REAL,
+			   &canaryd_data.cd_IntervalTime,
+			   NULL) < 0 )
+	{
+	    lerror("Could not register itimer.");
+	    retval = EXIT_FAILURE;
+	}
+	/* ... start looping. */
+	else
+	{
+	    int pagesize, mempages;
+	    double physmem;
+	    sigset_t ss;
+
+	    pagesize = getpagesize();
+	    mempages = getmempages();
+	    physmem = pagesize * mempages;
+	    sigfillset(&ss);
+	    sigdelset(&ss, SIGALRM);
+	    sigdelset(&ss, SIGINT);
+	    sigdelset(&ss, SIGTERM);
+	    /* Main loop, waits for events and optionally prints stats. */
+	    while( canaryd_data.cd_Flags & CDF_LOOPING )
+	    {
+		gettimeofday(&canaryd_data.cd_CurrentTime, NULL);
+
+		/* Make sure we're not past the stop time and */
+		if( timercmp(&canaryd_data.cd_CurrentTime,
+			     &canaryd_data.cd_StopTime,
+			     >) )
+		{
+		    cdStopTracing();
+		}
+		/* ... try outputting trace data. */
+		else if( canaryd_data.cd_Flags & CDF_TRACING )
+		{
+		    int *cpu_pct, *mem_busy, *netmem_busy;
+		    struct cpVNode *vn;
+
+		    /* We're tracing now, update stats, and */
+		    cpu_pct = getcpustates();
+		    
+		    mem_busy = getmembusy(mempages);
+
+		    netmem_busy = getnetmembusy();
+		    
+		    cdScanProcFS();
+
+		    /* ... print them out. */
+		    fprintf(canaryd_data.cd_OutputFile,
+			    "vers=2 stamp=%ld,%ld cpu=%f intr=%f mem=%d,%d "
+			    "disk=%u netmem=%u,%u,%u",
+			    canaryd_data.cd_CurrentTime.tv_sec,
+			    canaryd_data.cd_CurrentTime.tv_usec,
+			    (1000.0 - cpu_pct[CP_IDLE]) / 10.0,
+			    cpu_pct[CP_INTR] / 10.0,
+			    mem_busy[1],
+			    mem_busy[0],
+			    getdiskbusy(),
+			    netmem_busy[0],
+			    netmem_busy[1],
+			    netmem_busy[2]);
+		    
+		    cdPrintNetworkInterfaces(canaryd_data.cd_OutputFile);
+
+		    /*
+		     * Dump the vnodes list and clear their counters for the
+		     * next round.
+		     */
+		    vn = child_process_data.cpd_VNodes;
+		    while( vn != NULL )
+		    {
+			double cpu_pct, mem_pct;
+			
+			cpu_pct = (vn->vn_Usage / 1000000.0) * 100.0;
+			mem_pct = ((double)vn->vn_MemoryUsage / physmem) *
+			    100.0;
+			fprintf(canaryd_data.cd_OutputFile,
+				" vnode=%s,%f,%f",
+				vn->vn_Name,
+				cpu_pct,
+				mem_pct);
+			vn->vn_Usage = 0;
+			vn->vn_MemoryUsage = 0;
+			vn = vn->vn_Next;
+		    }
+		    
+		    fprintf(canaryd_data.cd_OutputFile, "\n");
+		    fflush(canaryd_data.cd_OutputFile);
+		}
+
+		/* Check for events and */
+		event_poll(canaryd_events_data.ced_Handle);
+
+		/* ... wait for the next signal. */
+		if( canaryd_data.cd_Flags & CDF_LOOPING )
+		{
+		    sigsuspend(&ss);
+		    /*
+		     * XXX We should probably make sure it was a SIGALRM that
+		     * woke us up, but eh...
+		     */
+		}
+	    }
+	}
+
+	/* Clean up our mess. */
+	cdStopTracing();
+	if( !(canaryd_data.cd_Flags & CDF_DEBUG) )
+	{
+	    unlink(PIDFILE);
+	}
+	break;
+    }
+    return( retval );
 }
 
-int get_counters(char *buf, void *data) {
-
-  CANARYD_PACKET *pkt = (CANARYD_PACKET*)data;
-#ifdef __linux__
-  struct ifreq ifr;
-  bzero(&ifr, sizeof(struct ifreq));
-#endif
-
-  if (pkt->ifcnt < MAXNUMIFACES
-      && !strstr(buf, "lo")
-#ifdef __FreeBSD__
-      && !strstr(buf, "*")
-      && strstr(buf, "<Link"))
-#endif
-#ifdef __linux__
-      && strstr(buf, "eth"))
-#endif
-  {
+enum {
+  SITEIDX = 0,
+  EXPTIDX,
+  GROUPIDX,
+  HOSTIDX,
+  OBJTYPEIDX,
+  OBJNAMEIDX,
+  EVTYPEIDX,
+  ARGSIDX,
+
+  MAXIDX
+};
 
-    if (sscanf(buf, CNTFMTSTR,
-               pkt->ifaces[pkt->ifcnt].ifname,
-#ifdef __FreeBSD__
-               pkt->ifaces[pkt->ifcnt].addr,
-#endif
-               &pkt->ifaces[pkt->ifcnt].ipkts,
-               &pkt->ifaces[pkt->ifcnt].ibytes,               
-               &pkt->ifaces[pkt->ifcnt].opkts,
-               &pkt->ifaces[pkt->ifcnt].obytes)  != NUMSCAN) {
-      lwarn("scan of netstat line failed!");
-      return -1;
-    }
-#if 0
-    { 
-      struct in_addr iaddr;
-      if (inet_pton(AF_INET, pkt->ifaces[pkt->ifcnt].addr, &iaddr) != 1) {
-        if (opts->debug) {
-          printf("failed to convert addr to inet addr\n");
-        }
-        return 0;
-      }
-    }
-#endif
-#ifdef __linux__
-    strcpy(ifr.ifr_name, pkt->ifaces[pkt->ifcnt].ifname);
-    if (ioctl(parms->ifd, SIOCGIFHWADDR, &ifr) < 0) {
-      perror("error getting HWADDR");
-      return -1;
+void ceEventCallback(event_handle_t handle,
+		     event_notification_t en, 
+		     void *data)
+{
+    char buf[MAXIDX][128]; /* XXX: bad magic */
+    int len = 128;
+    struct timeval now;
+    static int ecount;
+    
+    bzero(buf, sizeof(buf));
+    
+    event_notification_get_site(handle, en, buf[SITEIDX], len);
+    event_notification_get_expt(handle, en, buf[EXPTIDX], len);
+    event_notification_get_group(handle, en, buf[GROUPIDX], len);
+    event_notification_get_host(handle, en, buf[HOSTIDX], len);
+    event_notification_get_objtype(handle, en, buf[OBJTYPEIDX], len);
+    event_notification_get_objname(handle, en, buf[OBJNAMEIDX], len);
+    event_notification_get_eventtype(handle, en, buf[EVTYPEIDX], len);
+    event_notification_get_arguments(handle, en, buf[ARGSIDX], len);
+    
+    if( canaryd_data.cd_Flags & CDF_DEBUG )
+    {
+	gettimeofday(&now, NULL);
+	fprintf(stderr,"Event %d: %lu.%03lu %s %s %s %s %s %s %s %s\n",
+		++ecount, now.tv_sec, now.tv_usec / 1000,
+		buf[SITEIDX], buf[EXPTIDX], buf[GROUPIDX],
+		buf[HOSTIDX], buf[OBJTYPEIDX], buf[OBJNAMEIDX], 
+		buf[EVTYPEIDX], buf[ARGSIDX]);
     }
-
-    strcpy(pkt->ifaces[pkt->ifcnt].addr, 
-           ether_ntoa((struct ether_addr*)&ifr.ifr_hwaddr.sa_data[0]));
-
-    if (opts->debug) {
-      printf("macaddr: %s\n", pkt->ifaces[pkt->ifcnt].addr);
+    
+    if( strcmp(buf[EVTYPEIDX], TBDB_EVENTTYPE_START) == 0 )
+    {
+	char *pair, *cursor = buf[ARGSIDX];
+
+	/* XXX Move this initialization elsewhere. */
+	canaryd_data.cd_StopTime.tv_sec = LONG_MAX;
+	canaryd_data.cd_StopTime.tv_usec = LONG_MAX;
+
+	/* Parse the arguments and */
+	while( (pair = strsep(&cursor, " \t")) != NULL )
+	{
+	    char *key_end;
+
+	    if( strlen(pair) == 0 )
+	    {
+		/* Empty argument, no reason to complain. */
+	    }
+	    else if( (key_end = strchr(pair, '=')) == NULL )
+	    {
+		/* Poorly formed argument, complain. */
+		lwarn("Bad START event argument");
+	    }
+	    else if( strncasecmp(pair, "duration", (key_end - pair)) == 0 )
+	    {
+		struct timeval duration;
+
+		duration.tv_usec = 0;
+		if( sscanf(key_end + 1, "%ld", &duration.tv_sec) == 0 )
+		{
+		    lwarn("Duration is not an integer.");
+		}
+		else
+		{
+		    timeradd(&canaryd_data.cd_CurrentTime,
+			     &duration,
+			     &canaryd_data.cd_StopTime);
+		}
+	    }
+	    else
+	    {
+		/* Unknown key... */
+		lwarn("Unknown key passed to START event");
+	    }
+	}
+
+	/* ... start tracing again. */
+	cdStartTracing(CANARYD_LOG); // XXX Let the user specify the log file.
+    }
+    else if( strcmp(buf[EVTYPEIDX], TBDB_EVENTTYPE_STOP) == 0 )
+    {
+	cdStopTracing();
+    }
+    else if( strcmp(buf[EVTYPEIDX], TBDB_EVENTTYPE_RESET) == 0 )
+    {
+	/* set_default_overload_params(); */
+    }
+    else if( strcmp(buf[EVTYPEIDX], TBDB_EVENTTYPE_REPORT) == 0 )
+    {
+    }
+    else
+    {
+	lnotice("Unknown event type received.");
     }
-#endif
-    pkt->ifcnt++;
-  }
-  return 0;
-}
-
-
-/* XXX change to combine last return value of procfunc with exec'ed process'
-   exit status & write macros for access.
-*/
-int procpipe(char *const prog[], int (procfunc)(char*,void*), void* data) {
-
-  int fdes[2], retcode, cpid, status;
-  char buf[MAXLINELEN];
-  FILE *in;
-
-  if ((retcode=pipe(fdes)) < 0) {
-    lerror("Couldn't alloc pipe");
-  }
-  
-  else {
-    switch ((cpid = fork())) {
-    case 0:
-      close(fdes[0]);
-      dup2(fdes[1], STDOUT_FILENO);
-      if (execvp(prog[0], prog) < 0) {
-        lerror("Couldn't exec program");
-        exit(1);
-      }
-      break;
-      
-    case -1:
-      lerror("Error forking child process");
-      close(fdes[0]);
-      close(fdes[1]);
-      retcode = -1;
-      break;
-      
-    default:
-      close(fdes[1]);
-      in = fdopen(fdes[0], "r");
-      while (!feof(in) && !ferror(in)) {
-        if (fgets(buf, sizeof(buf), in)) {
-          if ((retcode = procfunc(buf, data)) < 0)  break;
-        }
-      }
-      fclose(in);
-      wait(&status);
-      if (retcode > -1)  retcode = WEXITSTATUS(status);
-      break;
-    } /* switch ((cpid = fork())) */
-  }
-  return retcode;
 }
diff --git a/sensors/canaryd/canarydEvents.c b/sensors/canaryd/canarydEvents.c
new file mode 100644
index 0000000000000000000000000000000000000000..df810dad705b094ed81e866221d3ff836d394f46
--- /dev/null
+++ b/sensors/canaryd/canarydEvents.c
@@ -0,0 +1,108 @@
+/*
+ * canarydEvents.c
+ *
+ * Copyright (c) 2004 The University of Utah and the Flux Group.
+ * All rights reserved.
+ *
+ * This file is licensed under the terms of the GNU Public License.  
+ * See the file "license.terms" for restrictions on redistribution 
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+/**
+ * @file canarydEvents.c
+ *
+ * Implementation file for the event related stuff for canaryd.
+ */
+
+#include <ctype.h>
+
+#include "auxfuncs.h"
+
+#include "canarydEvents.h"
+
+struct ceCanarydEventsData canaryd_events_data;
+
+/**
+ * The list of events we care about: start, stop, reset, report.
+ */
+static const char *CANARYD_EVENTS = (TBDB_EVENTTYPE_START ", "
+				     TBDB_EVENTTYPE_STOP ", "
+				     TBDB_EVENTTYPE_RESET ", "
+				     TBDB_EVENTTYPE_REPORT);
+
+/**
+ * Grab a the PID/EID from a "tmcc status" invocation.
+ *
+ * <p>Expected input: ALLOCATED=pid/eid ...
+ *
+ * @param buf The output of tmcc.
+ * @param data ...
+ * @return Zero on success, -1 otherwise.
+ */
+static int ceGrabPidEid(char *buf, void *data)
+{
+    char *bp, *tmpbp, *value = "";
+    int retval = -1;
+
+    if( strstr(buf, "ALLOCATED=") )
+    {
+	bp = tmpbp = buf+10;
+	while (!isspace(*tmpbp)) tmpbp++;
+	*tmpbp = '\0';
+	value = strdup(bp);
+	retval = 0;
+    }
+    canaryd_events_data.ced_PidEid = value;
+    
+    return retval;
+}
+
+int ceInitCanarydEvents(const char *event_server)
+{
+    char *stprog[] = {"tmcc", "status", NULL};
+    address_tuple_t tuple = NULL;
+    int retval = 1;
+
+    /* Allocate the tuple that describes what we want from the event system, */
+    if( (tuple = address_tuple_alloc()) == NULL )
+    {
+	retval = 0;
+    }
+    /* ... determine the pid/eid for this node, and */
+    else if( procpipe(stprog, &ceGrabPidEid, NULL) )
+    {
+	retval = 0;
+    }
+    /* ... attempt the subscription. */
+    else
+    {
+	tuple->host	 = ADDRESSTUPLE_ANY;
+	tuple->site      = ADDRESSTUPLE_ANY;
+	tuple->group     = ADDRESSTUPLE_ANY;
+	tuple->expt      = (char *)canaryd_events_data.ced_PidEid;
+	tuple->objtype   = "SLOTHD"; // XXX This needs to be updated in the DB
+	tuple->objname   = ADDRESSTUPLE_ANY;
+	tuple->eventtype = (char *)CANARYD_EVENTS;
+	/* Get a handle to the event system and */
+	if( (canaryd_events_data.ced_Handle =
+	     event_register_withkeyfile((char *)event_server,
+					0,
+					EVENTKEYFILE)) == NULL )
+	{
+	    retval = 0;
+	}
+	/* ... subscribe to the canaryd events. */
+	else if( !event_subscribe(canaryd_events_data.ced_Handle,
+				  ceEventCallback,
+				  tuple,
+				  NULL) )
+	{
+	    retval = 0;
+	}
+    }
+
+    address_tuple_free(tuple);
+    
+    return( retval );
+}
diff --git a/sensors/canaryd/canarydEvents.h b/sensors/canaryd/canarydEvents.h
new file mode 100644
index 0000000000000000000000000000000000000000..dbc8794b1152437f57fcb01895c727580a953731
--- /dev/null
+++ b/sensors/canaryd/canarydEvents.h
@@ -0,0 +1,59 @@
+/*
+ * canarydEvents.h
+ *
+ * Copyright (c) 2004 The University of Utah and the Flux Group.
+ * All rights reserved.
+ *
+ * This file is licensed under the terms of the GNU Public License.  
+ * See the file "license.terms" for restrictions on redistribution 
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+/**
+ * @file canarydEvents.h
+ *
+ * Header file for the event related stuff for canaryd.
+ */
+
+#ifndef CANARYD_EVENTS_H
+#define CANARYD_EVENTS_H
+
+#include "event.h"
+#include "tbdefs.h"
+
+/**
+ * Initialize the canaryd connection to the Emulab event system.
+ *
+ * @param event_server An "elvin://" URL for the server.
+ * @return True if the initialization was successful, false otherwise.
+ */
+int ceInitCanarydEvents(const char *event_server);
+
+/**
+ * The path to the event secret key file.
+ */
+#define EVENTKEYFILE "/var/emulab/boot/eventkey"
+
+/*
+ * Global data for the canaryd-related event stuff.
+ *
+ * ced_PidEid - The pid/eid of the experiment.  For example, "tbres/ftest".
+ * ced_Handle - The handle to the event system.
+ */
+struct ceCanarydEventsData {
+    const char *ced_PidEid;
+    event_handle_t ced_Handle;
+} canaryd_events_data;
+
+/**
+ * Callback for individual events.
+ *
+ * @param handle The event handle the event was received on.
+ * @param notification The event notification itself.
+ * @param data ...
+ */
+extern void ceEventCallback(event_handle_t handle,
+			    event_notification_t notification, 
+			    void *data);
+
+#endif
diff --git a/sensors/canaryd/childProcess.c b/sensors/canaryd/childProcess.c
new file mode 100644
index 0000000000000000000000000000000000000000..959a4f15af1865881880d1838e8601679ec1d8de
--- /dev/null
+++ b/sensors/canaryd/childProcess.c
@@ -0,0 +1,310 @@
+/*
+ * childProcess.c
+ *
+ * Copyright (c) 2003, 2004 The University of Utah and the Flux Group.
+ * All rights reserved.
+ *
+ * This file is licensed under the terms of the GNU Public License.  
+ * See the file "license.terms" for restrictions on redistribution 
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+/**
+ * @file childProcess.c
+ *
+ * Implementation file for the child process accounting functions.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/time.h>
+#include <sys/param.h>
+
+#include "childProcess.h"
+
+/**
+ * The file name format for the process' memory map and statistics in '/proc'.
+ */
+#if defined(__FreeBSD__)
+static char *PROC_STAT_FORMAT = "/proc/%d/status";
+static char *PROC_MAP_FORMAT = "/proc/%d/map";
+#endif
+
+/**
+ * Global data for child processes.
+ */
+struct cpChildProcessData child_process_data;
+
+int cpInitChildProcessData(void)
+{
+    int lpc, retval = 1;
+
+    for( lpc = 0; lpc < CPD_TABLE_SIZE; lpc++ )
+    {
+	child_process_data.cpd_Table[lpc] = NULL;
+    }
+    child_process_data.cpd_Flags |= CPDF_INITIALIZED;
+    return( retval );
+}
+
+void cpKillChildProcessData(void)
+{
+    /* XXX implement me */
+}
+
+void cpCollectChildProcesses(void)
+{
+    int lpc;
+
+    /* Increment the generation number and */
+    child_process_data.cpd_CurrentGeneration += 1;
+
+    /* ... then collect any objects that do not match. */
+    for( lpc = 0; lpc < CPD_TABLE_SIZE; lpc++ )
+    {
+	struct cpChildProcess *cp, **prev, *next;
+	
+	prev = &child_process_data.cpd_Table[lpc];
+	cp = child_process_data.cpd_Table[lpc];
+	while( cp != NULL )
+	{
+	    next = cp->cp_Next;
+	    
+	    if( cp->cp_Generation != child_process_data.cpd_CurrentGeneration )
+	    {
+		*prev = cp->cp_Next;
+		cpDeleteChildProcess(cp);
+	    }
+	    else
+	    {
+		prev = &cp->cp_Next;
+	    }
+	    cp = next;
+	}
+    }
+}
+
+/**
+ * Find a cpVNode with the given name.
+ *
+ * @param name The name of the vnode.
+ * @return The matching cpVNode object or NULL.
+ */
+struct cpVNode *cpFindVNode(const char *name)
+{
+    struct cpVNode *vn, *retval = NULL;
+
+    vn = child_process_data.cpd_VNodes;
+    while( (vn != NULL) && (retval == NULL) )
+    {
+	if( strcmp(vn->vn_Name, name) == 0 )
+	{
+	    retval = vn;
+	}
+	vn = vn->vn_Next;
+    }
+    return( retval );
+}
+
+struct cpChildProcess *cpFindChildProcess(pid_t child_pid)
+{
+    struct cpChildProcess *curr, *retval = NULL;
+    int hash = child_pid % CPD_TABLE_SIZE;
+
+    curr = child_process_data.cpd_Table[hash];
+    while( (curr != NULL) && (retval == NULL) )
+    {
+	if( curr->cp_PID == child_pid )
+	{
+	    retval = curr;
+	}
+	curr = curr->cp_Next;
+    }
+    return( retval );
+}
+
+struct cpChildProcess *cpCreateChildProcess(pid_t child_pid)
+{
+    struct cpChildProcess *retval;
+
+    /* Allocate the structure and the /proc file name in one chunk. */
+    if( (retval = calloc(1,
+			 sizeof(struct cpChildProcess) +
+			 strlen(PROC_STAT_FORMAT) +
+			 16 + /* extra space for the PID */
+			 strlen(PROC_MAP_FORMAT) +
+			 16 +
+			 1)) != NULL )
+    {
+	int rc, hash;
+	FILE *file;
+
+	retval->cp_PID = child_pid;
+
+	/* Generate the "status" file name, */
+	rc = snprintf((char *)(retval + 1),
+		      strlen(PROC_STAT_FORMAT) + 16 + 1,
+		      PROC_STAT_FORMAT,
+		      child_pid);
+	retval->cp_StatusFileName = (const char *)(retval + 1);
+
+	/* ... the "map" file name, */
+	snprintf((char *)retval->cp_StatusFileName + rc + 1,
+		 strlen(PROC_MAP_FORMAT) + 16 + 1,
+		 PROC_MAP_FORMAT,
+		 child_pid);
+	retval->cp_MapFileName = (char *)retval->cp_StatusFileName + rc + 1;
+
+	/* ... and make the process part of the current generation. */
+	retval->cp_Generation = child_process_data.cpd_CurrentGeneration;
+
+	/* Check the status file to see if the process is in a vnode, then */
+	if( (file = fopen(retval->cp_StatusFileName, "r")) != NULL )
+	{
+	    char buffer[1024], vname[MAXHOSTNAMELEN];
+
+	    fgets(buffer, sizeof(buffer), file);
+	    sscanf(buffer,
+		   "%*s %*u %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s "
+		   "%s",
+		   vname);
+	    fclose(file);
+
+	    if( strcmp(vname, "-") == 0 )
+	    {
+		/* Not in a vnode. */
+	    }
+	    else if( (retval->cp_VNode = cpFindVNode(vname)) == NULL )
+	    {
+		struct cpVNode *vn;
+
+		/* In a new vnode. */
+		if( (vn = calloc(1,
+				 sizeof(struct cpVNode) +
+				 strlen(vname) + 1)) != NULL )
+		{
+		    strcpy((char *)(vn + 1), vname);
+		    vn->vn_Name = (const char *)(vn + 1);
+		    
+		    vn->vn_Next = child_process_data.cpd_VNodes;
+		    child_process_data.cpd_VNodes = vn;
+		}
+		retval->cp_VNode = vn;
+	    }
+	}
+
+	/* ... add the process to the hash table. */
+	hash = child_pid % CPD_TABLE_SIZE;
+	retval->cp_Next = child_process_data.cpd_Table[hash];
+	child_process_data.cpd_Table[hash] = retval;
+    }
+    return( retval );
+}
+
+void cpDeleteChildProcess(struct cpChildProcess *cp)
+{
+    if( cp != NULL )
+    {
+	free(cp);
+	cp = NULL;
+    }
+}
+
+#if defined(__FreeBSD__)
+unsigned long long cpSampleUsage(struct cpChildProcess *cp)
+{
+    static char unused_string[1024];
+    
+    int retval = 0;
+    FILE *file;
+
+    /* Get CPU statistics first, then */
+    if( (file = fopen(cp->cp_StatusFileName, "r")) != NULL )
+    {
+	struct timeval utime, stime, accum, usage;
+	char buffer[1024];
+	int unused_int;
+	
+	memset(&utime, 0, sizeof(struct timeval));
+	memset(&stime, 0, sizeof(struct timeval));
+	memset(&accum, 0, sizeof(struct timeval));
+	fgets(buffer, sizeof(buffer), file);
+	sscanf(buffer,
+	       "%s %d %d %d %d %d,%d %s %d,%d %ld,%ld %ld,%ld",
+	       unused_string,
+	       &unused_int,
+	       &unused_int,
+	       &unused_int,
+	       &unused_int,
+	       &unused_int,
+	       &unused_int,
+	       unused_string,
+	       &unused_int,
+	       &unused_int,
+	       &utime.tv_sec,
+	       &utime.tv_usec,
+	       &stime.tv_sec,
+	       &stime.tv_usec);
+	timeradd(&utime, &stime, &accum);
+	timersub(&accum, &cp->cp_LastUsage, &usage);
+	cp->cp_LastUsage = accum;
+	retval += (usage.tv_sec * 1000000) + usage.tv_usec;
+	cp->cp_VNode->vn_Usage += retval;
+	fclose(file);
+    }
+
+    /* ... do the memory stats. */
+    if( (file = fopen(cp->cp_MapFileName, "r")) != NULL )
+    {
+	unsigned long long total_memory = 0;
+	char buffer[16384];
+	int rc;
+
+	if( (rc = fread(buffer, 1, sizeof(buffer), file)) > 0 )
+	{
+	    char *line = buffer;
+	    
+	    do {
+		int resident, private_resident, ref_count, shadow_count, flags;
+		void *start, *end, *obj;
+		char *next_line;
+		char type[32];
+
+		next_line = strchr(line, '\n');
+		*next_line = '\0';
+		sscanf(line,
+		       "%p %p %d %d %p %*s %d %d %x %*s %*s %s",
+		       &start,
+		       &end,
+		       &resident,
+		       &private_resident,
+		       &obj,
+		       &ref_count,
+		       &shadow_count,
+		       &flags,
+		       type);
+
+		/* XXX Not sure which ones to count. */
+		if( ((strcmp(type, "default") == 0) && (ref_count <= 5)) ||
+		    ((strcmp(type, "vnode") == 0) && (ref_count <= 2)) )
+		{
+		    total_memory += ((char *)end) - ((char *)start);
+		}
+
+		line = next_line + 1;
+	    } while( line < &buffer[rc] );
+	}
+
+	cp->cp_MemoryUsage = total_memory;
+	cp->cp_VNode->vn_MemoryUsage += total_memory;
+	
+	fclose(file);
+    }
+    
+    return( retval );
+}
+#endif
diff --git a/sensors/canaryd/childProcess.h b/sensors/canaryd/childProcess.h
new file mode 100644
index 0000000000000000000000000000000000000000..d324e580c808c6981ba7ec36fa139823b6188e3f
--- /dev/null
+++ b/sensors/canaryd/childProcess.h
@@ -0,0 +1,168 @@
+/*
+ * childProcess.h
+ *
+ * Copyright (c) 2003, 2004 The University of Utah and the Flux Group.
+ * All rights reserved.
+ *
+ * This file is licensed under the terms of the GNU Public License.  
+ * See the file "license.terms" for restrictions on redistribution 
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+/**
+ * @file childProcess.h
+ *
+ * Header file for the child process accounting functions.
+ *
+ * NOTE: Most of this was taken from the CPU Broker and tweaked to suit the
+ * testbed's needs.
+ */
+
+#ifndef _child_process_h
+#define _child_process_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+/*
+ * A cpVNode records the resource usage of a particular vnode.
+ *
+ * vn_Next - Link to next object in the list.
+ * vn_Name - The name of the VNode (e.g. node.eid.pid.emulab.net)
+ * vn_Usage - The CPU usage since the last sample.
+ * vn_MemoryUsage - The Memory usage since the last sample.
+ */
+struct cpVNode {
+    struct cpVNode *vn_Next;
+    const char *vn_Name;
+    unsigned long long vn_Usage;
+    unsigned long long vn_MemoryUsage;
+};
+
+/*
+ * A cpChildProcess is used to track and record the resource usage of a child
+ * process.
+ *
+ * cp_Next - Link to next object in the list.
+ * cp_PID - The process ID of the child.
+ * cp_VNode - NULL or a reference to the VNode this process is in.
+ * cp_StatusFileName - The "status" file name in "/proc" for this process.
+ * cp_MapFileName - The "map" file name in "/proc" for this process.
+ * cp_Generation - Generation number, used to garbage collect processes that
+ *   do not exist any more.
+ * cp_MemoryUsage - The memory used by this process since the last sample.
+ * cp_LastUsage - The last recorded usage for this child.  Used to compute
+ *   the difference in usage between the current and last sample time.
+ */
+struct cpChildProcess {
+    struct cpChildProcess *cp_Next;
+    pid_t cp_PID;
+    struct cpVNode *cp_VNode;
+    const char *cp_StatusFileName;
+    const char *cp_MapFileName;
+    unsigned long cp_Generation;
+    unsigned long long cp_MemoryUsage;
+#if defined(linux) || defined(__FreeBSD__)
+    struct timeval cp_LastUsage;
+#else
+#error "Implement me"
+#endif
+};
+
+enum {
+    CPDB_INITIALIZED,
+};
+
+/*
+ * Flags for the cpChildProcessData structure.
+ *
+ * CPDF_INITIALIZED - The global data is initialized.
+ */
+enum {
+    CPDF_INITIALIZED = (1L << CPDB_INITIALIZED),
+};
+
+#define CPD_TABLE_SIZE 31
+
+/*
+ * Global data for tracking child processes.
+ *
+ * cpd_Flags - Holds the CPDF_ flags.
+ * cpd_CurrentGeneration - The current generation number, this will be compared
+ *   against each process' generation number to decide when to garbage collect
+ *   cpChildProcess objects.
+ * cpd_VNodes - The list of detected vnodes.
+ * cpd_Table - Hash table of processes, hashes are based on the PID.
+ */
+struct cpChildProcessData {
+    unsigned long cpd_Flags;
+    unsigned long cpd_CurrentGeneration;
+    struct cpVNode *cpd_VNodes;
+    struct cpChildProcess *cpd_Table[CPD_TABLE_SIZE];
+} child_process_data;
+
+/**
+ * Initialize the internal accounting data structures.
+ *
+ * @return True on success, false otherwise.
+ */
+int cpInitChildProcessData(void);
+
+/**
+ * Deinitialize the internal accounting data structures.
+ */
+void cpKillChildProcessData(void);
+
+/**
+ * Garbage collect cpChildProcess objects for processes that do not exist
+ * anymore.  Works by incrementing the generation number and then collecting
+ * any objects that do not have a matching generation number.
+ */
+void cpCollectChildProcesses(void);
+
+/**
+ * Find the cpChildProcess structure that corresponds to the given ID.
+ *
+ * @param child_pid The child process to track.
+ * @return The cpChildProcess object that corresponds to the given ID or NULL
+ * if one has not been created yet.
+ */
+struct cpChildProcess *cpFindChildProcess(pid_t child_pid);
+
+/**
+ * Create a cpChildProcess object for a child that does not have one yet.  Note
+ * that this will also create the appropriate cpVNode object if one does not
+ * already exist.
+ *
+ * @callgraph
+ *
+ * @param child_pid The child process to track.
+ * @return The newly created object or NULL if a problem was encountered
+ * during creation.
+ */
+struct cpChildProcess *cpCreateChildProcess(pid_t child_pid);
+
+/**
+ * Delete the given cpChildProcess object.
+ *
+ * @param cp NULL or a previously created cpChildProcess object.
+ */
+void cpDeleteChildProcess(struct cpChildProcess *cp);
+
+/**
+ * Sample the resource usage for a given child process.
+ *
+ * @param cp A valid cpChildProcess object.
+ * @param run_time The amount of time the parent process has been running.
+ * @return The CPU usage, in microseconds, for the given child process.
+ */
+unsigned long long cpSampleUsage(struct cpChildProcess *cp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sensors/canaryd/networkInterface.c b/sensors/canaryd/networkInterface.c
new file mode 100644
index 0000000000000000000000000000000000000000..cf5fa79d6fe739e9534dfbdaf6cc6a7f7832eed1
--- /dev/null
+++ b/sensors/canaryd/networkInterface.c
@@ -0,0 +1,65 @@
+/*
+ * networkInterface.c
+ *
+ * Copyright (c) 2004 The University of Utah and the Flux Group.
+ * All rights reserved.
+ *
+ * This file is licensed under the terms of the GNU Public License.  
+ * See the file "license.terms" for restrictions on redistribution 
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+/**
+ * @file networkInterface.c
+ *
+ * Implemenation of the network interface convenience functions.
+ *
+ * NOTE: Most of this was taken from the JanosVM.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+#if defined(__FreeBSD__)
+#include <net/if_dl.h>
+#include <net/if_arp.h>
+#include <net/if_types.h>
+#endif
+
+#include "networkInterface.h"
+
+char *niFormatMACAddress(char *dest, struct sockaddr *sa)
+{
+    char *retval = dest;
+    int lpc, hlen;
+    char *haddr;
+    
+#if defined(AF_LINK)
+    /* FreeBSD link layer address. */
+    struct sockaddr_dl *sdl;
+    
+    sdl = (struct sockaddr_dl *)sa;
+    hlen = sdl->sdl_alen;
+    haddr = sdl->sdl_data + sdl->sdl_nlen;
+#else
+    hlen = 0;
+#endif
+    for( lpc = 0; lpc < hlen; lpc++ )
+    {
+	sprintf(dest,
+		"%s%02x",
+		(lpc > 0) ? ":" : "",
+		haddr[lpc] & 0xff);
+	dest += strlen(dest);
+    }
+    return( retval );
+}
diff --git a/sensors/canaryd/networkInterface.h b/sensors/canaryd/networkInterface.h
new file mode 100644
index 0000000000000000000000000000000000000000..8f3b925e76604710fac345c1b0aaa72a7537dc27
--- /dev/null
+++ b/sensors/canaryd/networkInterface.h
@@ -0,0 +1,37 @@
+/*
+ * networkInterface.h
+ *
+ * Copyright (c) 2004 The University of Utah and the Flux Group.
+ * All rights reserved.
+ *
+ * This file is licensed under the terms of the GNU Public License.  
+ * See the file "license.terms" for restrictions on redistribution 
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+/**
+ * @file networkInterface.h
+ *
+ * Header file for network interface convenience functions.
+ *
+ * NOTE: Most of this was taken from the JanosVM.
+ */
+
+#ifndef NETWORK_INTERFACE_H
+#define NETWORK_INTERFACE_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+/**
+ * Format a link layer socket address into a string.
+ *
+ * <p>Example output: 00:02:b3:65:b6:46
+ *
+ * @param dest The destination for the formatted address string.
+ * @param sa The socket address to convert.
+ * @return dest
+ */
+char *niFormatMACAddress(char *dest, struct sockaddr *sa);
+
+#endif