From 62b5e3a88ead93f70f9652dced00346637d036c3 Mon Sep 17 00:00:00 2001
From: Timothy Stack <stack@flux.utah.edu>
Date: Tue, 27 Jul 2004 23:00:55 +0000
Subject: [PATCH] Updated canaryd, ended up starting fresh and pulling things
 in, rather than jamming more stuff into the old one.  Most of the code came
 from the previous version of canaryd, the cpu broker (process accounting),
 and the janosvm (network interface accounting).  Its missing some features of
 the old one, but those can be incorporated without too much trouble.

Changes:

  Designed to permanently run on the pnodes:  it waits for START events
    before it begins recording.  However, I haven't done the work
    necessary to have it always startup on the pnodes.

  No more exec'ing: process stuff is taken from "/proc", and network
    interface stats are pulled from getifaddrs(3).

  Fixed some minor bugs: A typo caused the real-time priority to not
    be set, use setitimer instead of sleep to get more accurate
    spacing between samples.
---
 sensors/canaryd/GNUmakefile.in     |    6 +-
 sensors/canaryd/auxfuncs.c         |   71 +-
 sensors/canaryd/auxfuncs.h         |    5 +-
 sensors/canaryd/canaryd.c          | 1736 +++++++++++-----------------
 sensors/canaryd/canarydEvents.c    |  108 ++
 sensors/canaryd/canarydEvents.h    |   59 +
 sensors/canaryd/childProcess.c     |  310 +++++
 sensors/canaryd/childProcess.h     |  168 +++
 sensors/canaryd/networkInterface.c |   65 ++
 sensors/canaryd/networkInterface.h |   37 +
 10 files changed, 1492 insertions(+), 1073 deletions(-)
 create mode 100644 sensors/canaryd/canarydEvents.c
 create mode 100644 sensors/canaryd/canarydEvents.h
 create mode 100644 sensors/canaryd/childProcess.c
 create mode 100644 sensors/canaryd/childProcess.h
 create mode 100644 sensors/canaryd/networkInterface.c
 create mode 100644 sensors/canaryd/networkInterface.h

diff --git a/sensors/canaryd/GNUmakefile.in b/sensors/canaryd/GNUmakefile.in
index 9a983fe98c..e979ebef90 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 915c16e37f..10c74267fb 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 01c8da7a74..8d9b25be73 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 719af5afad..0cf1a5e0f3 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 0000000000..df810dad70
--- /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 0000000000..dbc8794b11
--- /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 0000000000..959a4f15af
--- /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 0000000000..d324e580c8
--- /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 0000000000..cf5fa79d6f
--- /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 0000000000..8f3b925e76
--- /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
-- 
GitLab