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