From a337f7b972277a7ac4f5d08b4bf7f7fa9debca44 Mon Sep 17 00:00:00 2001 From: Kirk Webb <kwebb@flux.utah.edu> Date: Fri, 21 Mar 2003 23:46:43 +0000 Subject: [PATCH] Well, here it is: The new slothd and sdcollectd stuff. Several things have changed; here is an outline: - "Thresholds" added to slothd These are checked at each iteration against current idle data to determine whether or not to set bits in the "active" vector. - LAST_TTY_ACT (now) > LAST_TTY_ACT (previous) - LOAD > <provided value> - EXPER_PKT_COUNT(now) - EXPER_PKT_COUNT(prev) > <provided value> - CNTRL_PKT_COUNT(now) - CNTRL_PKT_COUNT(prev) > <provided value> - Values passed in via command line: do ./slothd -h for help - "Aggressive" mode added to slothd When a regular interval's worth of time has passed, and no activity has been noted, slothd goes into "aggressive" mode. In this mode, slothd changes its sleep interval, checking for activity much more frequently (still reporting every <reg_interval>) until activity is noted. This is based on the threshold logic above. When activity is seen, slothd sends a report, and goes back to "regular" reporting. - params passed via command line for regular, and aggressive intervals. - sdcollectd updates data in new "node_activity" table - Updates timestamps based on activity bits in received slothd packets - No new rows are added. - Option added to sdcollectd to insert into "old" node_idlestats and iface_counters tables - This is not the default (-o) - New sdcollectd still accepts old client reports - but does not update the node_activity table with them. - Misc. - Code cleanup - Scan "active terminals only" option & code removed from slothd - Always send report immediately on startup (no more option) - Randomize time of second report to mitigate synchronization - Send report on shutdown (receipt of INT, TERM, or QUIT) --- sensors/slothd/GNUmakefile.in | 29 +- sensors/slothd/sdcollectd.c | 174 ++++++++---- sensors/slothd/sdcollectd.h | 17 +- sensors/slothd/slothd.c | 512 ++++++++++++++++++++-------------- sensors/slothd/slothd.h | 62 ++-- 5 files changed, 487 insertions(+), 307 deletions(-) diff --git a/sensors/slothd/GNUmakefile.in b/sensors/slothd/GNUmakefile.in index c6228fcb70..79ae0c34d0 100644 --- a/sensors/slothd/GNUmakefile.in +++ b/sensors/slothd/GNUmakefile.in @@ -8,8 +8,10 @@ SRCDIR = @srcdir@ TESTBED_SRCDIR = @top_srcdir@ OBJDIR = ../.. SUBDIR = sensors/slothd +SLOTHD_DIR = unknownclient SBIN_SCRIPTS = sdisrunning sddeploy +SDPROGS = sdcollectd slothd include $(OBJDIR)/Makeconf @@ -21,17 +23,19 @@ SDLIBS+= -ltb -lmysqlclient LIBS= CP= cp -pf -PROGS= slothd sdcollectd +SYSTEM := $(shell uname -s) -all: $(PROGS) +all: $(SDPROGS) client include ${TESTBED_SRCDIR}/GNUmakerules -linux: islinux slothd - $(CP) slothd linux/slothd +ifeq ($(SYSTEM),Linux) +SLOTHD_DIR = linuxclient +endif -fbsd: isfbsd slothd - $(CP) slothd fbsd/slothd +ifeq ($(SYSTEM),FreeBSD) +SLOTHD_DIR = fbsdclient +endif slothd: slothd.o slothd.h version.o $(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ slothd.o version.o $(LIBS) @@ -42,14 +46,15 @@ sdcollectd: sdcollectd.o sdcollectd.h version.o version.c: slothd.c slothd.h sdcollectd.c sdcollectd.h echo >$@ "char build_info[] = \"Built `date +%d-%b-%Y` by `id -nu`@`hostname | sed 's/\..*//'`:`pwd`\";" -islinux: - @uname -s | grep inux > /dev/null +client: slothd + -mkdir $(SLOTHD_DIR) + $(CP) slothd $(SLOTHD_DIR)/slothd -isfbsd: - @uname -s | grep BSD > /dev/null +install: $(addprefix $(INSTALL_SBINDIR)/, $(SBIN_SCRIPTS) sdcollectd) -install: $(addprefix $(INSTALL_SBINDIR)/, $(SBIN_SCRIPTS)) +client-install: + $(INSTALL_PROGRAM) $(SLOTHD_DIR)/slothd $(DESTDIR)$(CLIENT_BINDIR) clean: - rm -f *.o $(PROGS) *~ core *.core version.c + rm -f *.o $(SDPROGS) *~ core *.core version.c diff --git a/sensors/slothd/sdcollectd.c b/sensors/slothd/sdcollectd.c index 830fc777b6..de617a1bed 100644 --- a/sensors/slothd/sdcollectd.c +++ b/sensors/slothd/sdcollectd.c @@ -29,11 +29,13 @@ void siginthandler(int signum) { void usage(void) { - printf("Usage:\tslothd -h\n"); - printf("\tslothd [-d] [-p <port>]\n"); - printf("\t -h\t This message.\n"); - printf("\t -d\t Debug mode; do not fork into background.\n"); - printf("\t -p <port>\t Send on port <port> (default is %d).\n", SDCOLLECTD_PORT); + printf("Usage:\tslothd -h\n" + "\tslothd [-d] [-p <port>]\n" + "\t -h\t This message.\n" + "\t -o\t DO populate old idlestats tables.\n" + "\t -d\t Debug mode; do not fork into background.\n" + "\t -p <port>\t Listen on port <port> (default is %d).\n", + SDCOLLECTD_PORT); } @@ -42,12 +44,18 @@ int parse_args(int argc, char **argv) { char ch; /* setup defaults. */ + opts->popold = 0; opts->debug = 0; opts->port = SDCOLLECTD_PORT; - while ((ch = getopt(argc, argv, "dp:h")) != -1) { + while ((ch = getopt(argc, argv, "odp:h")) != -1) { switch (ch) { + case 'o': + lnotice("Populating old node_idlestats, and iface_counters tables"); + opts->popold = 1; + break; + case 'd': lnotice("Debug mode requested; staying in foreground.\n"); opts->debug = 1; @@ -80,20 +88,20 @@ int main(int argc, char **argv) { extern char build_info[]; opts = &gopts; - + /* Foo on SIGPIPE & SIGHUP. */ signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); - + /* Cleanup on sigint/term */ signal(SIGTERM, siginthandler); signal(SIGINT, siginthandler); - + if (!parse_args(argc, argv)) { lnotice("Error processing arguments, exiting.\n"); exit(1); } - + /* clear, and initialize inet sockaddr */ bzero(&servaddr, sizeof(struct sockaddr_in)); servaddr.sin_family = AF_INET; @@ -140,8 +148,9 @@ int main(int argc, char **argv) { for ( ;; ) { bzero(&iddata, sizeof(IDLE_DATA)); /* cleanse out old values, if any */ if (CollectData(sd, &iddata)) { - ParseRecord(&iddata); - PutDBRecord(&iddata); + if (ParseRecord(&iddata)) { + PutDBRecord(&iddata); + } } } /* NOTREACHED */ @@ -149,6 +158,7 @@ int main(int argc, char **argv) { int CollectData(int sd, IDLE_DATA *iddata) { int numbytes, slen; + time_t curtime; /* struct hostent *hent; */ struct sockaddr_in cliaddr; @@ -157,20 +167,14 @@ int CollectData(int sd, IDLE_DATA *iddata) { if((numbytes = recvfrom(sd, iddata->buf, sizeof(iddata->buf), 0, (struct sockaddr*)&cliaddr, &slen))) { - /* Using libtb functionality now. - if (!(hent = gethostbyaddr((char*)&cliaddr.sin_addr, - sizeof(cliaddr.sin_addr), AF_INET))) { - lerror("Can't resolve host"); - return -1; - } - */ if (!mydb_iptonodeid(inet_ntoa(cliaddr.sin_addr), iddata->id)) { lerror("Couldn't obtain node id"); return 0; } if (opts->debug) { - printf("Received data from: %s\n", iddata->id); + curtime = time(NULL); + printf("Received data from: %s at %s\n", iddata->id, ctime(&curtime)); printf("buffer: %s\n", iddata->buf); } } @@ -246,50 +250,57 @@ char *tbmac(char *maddr, char **endptr) { } } -void ParseRecord(IDLE_DATA *iddata) { - int i; - char *itemptr, *ptr; +int ParseRecord(IDLE_DATA *iddata) { + + int tmpvers; + char *itemptr, *ptr, *tmpstr; iddata->ifcnt = 0; /* Parse out fields */ itemptr = strtok(iddata->buf, " \t"); do { - if (strstr(itemptr, "mis")) { + + if (strstr(itemptr, "vers")) { + if ((tmpvers = strtoul(itemptr+5, NULL, 10)) > SDPROTOVERS) { + lerror("Unsupported protocol version; rejecting."); + return 0; + } + iddata->version = tmpvers; + } + + else if (strstr(itemptr, "mis")) { iddata->mis = atol(itemptr+4); } + else if (strstr(itemptr, "lave")) { iddata->l1m = strtod(itemptr+5, &ptr); iddata->l5m = strtod(ptr+1, &ptr); iddata->l15m = strtod(ptr+1, NULL); } + else if (strstr(itemptr, "iface")) { - strcpy(iddata->ifaces[iddata->ifcnt].mac, tbmac(itemptr+6, &ptr)); + if (!(tmpstr = tbmac(itemptr+6, &ptr))) { + lerror("Malformed interface record encountered, skipping"); + continue; + } + strcpy(iddata->ifaces[iddata->ifcnt].mac, tmpstr); iddata->ifaces[iddata->ifcnt].ipkts = strtoul(ptr+1, &ptr, 10); iddata->ifaces[iddata->ifcnt].opkts = strtoul(ptr+1, NULL, 10); iddata->ifcnt++; } + + else if (strstr(itemptr, "abits")) { + iddata->actbits = strtoul(itemptr+6, NULL, 16); + } + else { lnotice("Unrecognized string in packet\n"); } } while ((itemptr = strtok(NULL, " \t")) && iddata->ifcnt < MAXNUMIFACES); - if (opts->debug) { - printf("iddata->mis: %lu\n", iddata->mis); - printf("iddata->l1m: %f\n", iddata->l1m); - printf("iddata->l5m: %f\n", iddata->l5m); - printf("iddata->l15m: %f\n", iddata->l15m); - for (i = 0; i < iddata->ifcnt; ++i) { - printf("iface%d: %s, ipkts %lu, opkts %lu\n", - i, - iddata->ifaces[i].mac, - iddata->ifaces[i].ipkts, - iddata->ifaces[i].opkts); - } - } - - return; + return 1; } @@ -297,26 +308,77 @@ void PutDBRecord(IDLE_DATA *iddata) { int i; time_t now = time(NULL); + char curstamp[100]; + char tmpstr[(NUMACTTYPES+1)*sizeof(curstamp)]; + char *actstr[] = ACTSTRARRAY; - if (!mydb_update("INSERT INTO node_idlestats VALUES ('%s', FROM_UNIXTIME(%lu), FROM_UNIXTIME(%lu), %f, %f, %f)", - iddata->id, - now, - iddata->mis, - iddata->l1m, - iddata->l5m, - iddata->l15m)) { - lerror("Error inserting data into node_idlestats table"); - } + sprintf(curstamp, "FROM_UNIXTIME(%lu)", now); - for (i = 0; i < iddata->ifcnt; ++i) { - if (!mydb_update("INSERT INTO iface_counters VALUES ('%s', FROM_UNIXTIME(%lu), '%s', %lu, %lu)", - iddata->id, + printf("now: %s\n", ctime(&now)); + + if (opts->debug) { + printf("\nReceived and parsed packet. Contents:\n" + "Version: %u\n" + "Node: %s\n" + "Last TTY: %s" + "Load averages (1, 5, 15): %f, %f, %f\n" + "Active bits: 0x%04x\n", + iddata->version, + iddata->id, + ctime(&iddata->mis), + iddata->l1m, + iddata->l5m, + iddata->l15m, + iddata->actbits); + for (i = 0; i < iddata->ifcnt; ++i) { + printf("Interface %s: ipkts: %lu opkts: %lu\n", + iddata->ifaces[i].mac, + iddata->ifaces[i].ipkts, + iddata->ifaces[i].opkts); + } + printf("\n\n"); + } + + if (opts->popold) { + if (!mydb_update("INSERT INTO node_idlestats VALUES ('%s', FROM_UNIXTIME(%lu), FROM_UNIXTIME(%lu), %f, %f, %f)", + iddata->id, now, - iddata->ifaces[i].mac, - iddata->ifaces[i].ipkts, - iddata->ifaces[i].opkts)) { - lerror("Error inserting data into iface_counters table"); + iddata->mis, + iddata->l1m, + iddata->l5m, + iddata->l15m)) { + lerror("Error inserting data into node_idlestats table"); + } + + for (i = 0; i < iddata->ifcnt; ++i) { + if (!mydb_update("INSERT INTO iface_counters VALUES ('%s', FROM_UNIXTIME(%lu), '%s', %lu, %lu)", + iddata->id, + now, + iddata->ifaces[i].mac, + iddata->ifaces[i].ipkts, + iddata->ifaces[i].opkts)) { + lerror("Error inserting data into iface_counters table"); + } } } + + if (iddata->version >= 2) { + sprintf(tmpstr, "last_report=%s", curstamp); + for (i = 0; i < NUMACTTYPES; ++i) { + if (iddata->actbits & (1<<i)) { + sprintf(tmpstr, "%s, %s=%s", + tmpstr, + actstr[i], + curstamp); + } + } + + if(!mydb_update("UPDATE node_activity SET %s WHERE node_id = '%s'", + tmpstr, + iddata->id)) { + lerror("Error updating stamps in node_activity table"); + } + } + return; } diff --git a/sensors/slothd/sdcollectd.h b/sensors/slothd/sdcollectd.h index 53ec4b6a3e..c761023587 100644 --- a/sensors/slothd/sdcollectd.h +++ b/sensors/slothd/sdcollectd.h @@ -26,13 +26,18 @@ #include <syslog.h> #include <tbdb.h> +#define SDPROTOVERS 2 #define SDCOLLECTD_PORT 8509 #define NODENAMESIZE 100 #define BUFSIZE 1500 #define MAXNUMIFACES 10 #define MACADDRLEN 12 +#define NUMACTTYPES 4 +#define ACTSTRARRAY {"last_tty_act", "last_cpu_act", "last_net_act", "last_ext_act"} + typedef struct { + u_char popold; u_char debug; u_short port; } SDCOLLECTD_OPTS; @@ -40,24 +45,26 @@ typedef struct { SDCOLLECTD_OPTS *opts; typedef struct { - long mis; + long mis; double l1m; double l5m; double l15m; u_char ifcnt; + u_int actbits; + u_int version; struct { char mac[MACADDRLEN+1]; long ipkts; long opkts; - } ifaces[MAXNUMIFACES]; - char id[NODENAMESIZE]; /* Host identifier - probably local part of hostname */ - char buf[BUFSIZE]; /* String containing monitor output values */ + } ifaces[MAXNUMIFACES]; + char id[NODENAMESIZE]; /* Host identifier - probably local part of hostname */ + char buf[BUFSIZE]; /* String containing monitor output values */ } IDLE_DATA; extern int errno; int CollectData(int, IDLE_DATA*); -void ParseRecord(IDLE_DATA*); +int ParseRecord(IDLE_DATA*); void PutDBRecord(IDLE_DATA*); char *tbmac(char*, char**); diff --git a/sensors/slothd/slothd.c b/sensors/slothd/slothd.c index ab64998690..845a1368b5 100644 --- a/sensors/slothd/slothd.c +++ b/sensors/slothd/slothd.c @@ -6,8 +6,8 @@ #include "slothd.h" -SLOTHD_PACKET *pkt; SLOTHD_OPTS *opts; +SLOTHD_PARAMS *parms; void lerror(const char* msgstr) { if (msgstr) { @@ -42,42 +42,53 @@ void sigunkhandler(int signum) { } void siginthandler(int signum) { - - int status; - - unlink(PIDFILE); - while (wait(&status) != -1); - lnotice("exiting."); - exit(0); + parms->dolast = 1; + signal(SIGTERM, siginthandler); + signal(SIGINT, siginthandler); + signal(SIGQUIT, siginthandler); } void usage(void) { - printf("Usage:\tslothd -h\n"); - printf("\tslothd [-a] [-d] [-i <interval>] [-p <port>] [-s <server>]\n"); - printf("\t -h\t This message.\n"); - printf("\t -a\t Only scan active terminal special files.\n"); - printf("\t -f\t Do not send idle data report immediately on startup.\n"); - printf("\t -d\t Debug mode; do not fork into background.\n"); - printf("\t -i <interval>\t Run interval, in seconds. (default is 10 min.)\n"); - printf("\t -p <port>\t Send on port <port> (default is %d).\n", SLOTHD_DEF_PORT); - printf("\t -s <server>\t Send data to <server> (default is %s).\n", SLOTHD_DEF_SERV); + printf("Usage:\tslothd -h\n" + "\tslothd [-o] [-a] [-d] [-i <interval>] [-p <port>] [-s <server>]\n" + "\t [-g <interval>] [-l <thresh>] [-c <thresh>]\n" + "\t -h\t\t This message.\n" + "\t -o\t\t Run once (collect a single report).\n" + "\t -a\t\t Only scan active terminal special files.\n" + "\t -d\t\t Debug mode; do not fork into background.\n" + "\t -i <interval>\t Regular run interval, in seconds.\n" + "\t -g <interval>\t Aggressive run interval, in seconds.\n" + "\t -l <thresh>\t load threshold (default 1 @ 1 minute).\n" + "\t -c <thresh>\t experimental network packet difference threshold (pps).\n" + "\t -n <thresh>\t control network packet difference threshold (pps)\n" + "\t -p <port>\t Send on port <port>\n" + "\t -s <server>\t Send data to <server>\n"); } int main(int argc, char **argv) { int exitcode = -1; + u_int myabits, span; + time_t curtime; static SLOTHD_OPTS mopts; + static SLOTHD_PARAMS mparms; static SLOTHD_PACKET mpkt; + static SLOTHD_PACKET mopkt; + SLOTHD_PACKET *pkt, *opkt, *tmppkt; extern char build_info[]; /* pre-init */ bzero(&mopts, sizeof(SLOTHD_OPTS)); + bzero(&mparms, sizeof(SLOTHD_PARAMS)); bzero(&mpkt, sizeof(SLOTHD_PACKET)); + bzero(&mopkt, sizeof(SLOTHD_PACKET)); opts = &mopts; + parms = &mparms; pkt = &mpkt; + opkt = &mopkt; if (parse_args(argc, argv) < 0) { fprintf(stderr, "Error processing arguments.\n"); @@ -91,19 +102,63 @@ int main(int argc, char **argv) { lnotice("Slothd started"); lnotice(build_info); for (;;) { - if (!opts->first) { - sleep(mopts.interval); + + /* Collect current machine stats */ + mparms.cnt++; + get_load(pkt); + get_min_tty_idle(pkt); + get_packet_counts(pkt); + myabits = get_active_bits(pkt,opkt); + + /* + * 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 ((!opkt->actbits && pkt->actbits) || + (time(&curtime) >= mparms.lastrpt + mopts.reg_interval) || + parms->dolast) { + if (send_pkt(pkt)) { + mparms.lastrpt = curtime; + } + if (parms->dolast) { + do_exit(); + } + tmppkt = pkt; + pkt = opkt; + opkt = tmppkt; } - get_load(); - get_min_tty_idle(); - get_packet_counts(); - send_pkt(); - if (opts->once) { + + if (mopts.once) { break; } - if (opts->first) { - sleep(mopts.interval); + + /* + * Figure out, based on run count, and activity, how long + * to sleep. + */ + if (mparms.cnt == 1) { + span = mopts.reg_interval - + (rand() / (float) RAND_MAX) * (OFFSET_FRACTION*mopts.reg_interval); + } + else if (myabits) { + span = mopts.reg_interval; + } + else { + span = mopts.agg_interval; + } + + if (opts->debug) { + printf("About to sleep for %u seconds.\n", span); + fflush(stdout); } + + if (parms->dolast) { + continue; + } + + sleep(span); } } } @@ -115,15 +170,17 @@ int parse_args(int argc, char **argv) { char ch; /* setup defaults. */ + opts->once = 0; + opts->reg_interval = DEF_RINTVL; + opts->agg_interval = DEF_AINTVL; opts->debug = 0; - opts->actterms = 0; - opts->interval = DEF_INTVL; opts->port = SLOTHD_DEF_PORT; opts->servname = SLOTHD_DEF_SERV; - opts->first = 1; - opts->once = 0; + opts->load_thresh = DEF_LTHRSH; + opts->pkt_thresh = DEF_CTHRSH; + opts->cif_thresh = DEF_CTHRSH; - while ((ch = getopt(argc, argv, "ai:dp:s:hfo")) != -1) { + while ((ch = getopt(argc, argv, "oi:g:dp:s:l:c:n:h")) != -1) { switch (ch) { case 'o': /* run once */ @@ -131,9 +188,16 @@ int parse_args(int argc, char **argv) { break; case 'i': - if ((opts->interval = atoi(optarg)) < MIN_INTVL) { - lwarn("Warning! Interval set too low, defaulting."); - opts->interval = MIN_INTVL; + if ((opts->reg_interval = atol(optarg)) < MIN_RINTVL) { + lwarn("Warning! Regular interval set too low, defaulting."); + opts->reg_interval = MIN_RINTVL; + } + break; + + case 'g': + if ((opts->agg_interval = atol(optarg)) < MIN_AINTVL) { + lwarn("Warning! Aggressive interval set too low, defaulting."); + opts->reg_interval = MIN_AINTVL; } break; @@ -142,15 +206,6 @@ int parse_args(int argc, char **argv) { opts->debug = 1; break; - case 'a': - lnotice("Scanning only active terminals."); - opts->actterms = 1; - break; - - case 'f': - opts->first = 0; - break; - case 'p': opts->port = (u_short)atoi(optarg); break; @@ -163,6 +218,27 @@ int parse_args(int argc, char **argv) { lwarn("Invalid server name, default used."); } break; + + case 'l': + if ((opts->load_thresh = atof(optarg)) < MIN_LTHRSH) { + lwarn("Warning! Load threshold set too low, defaulting."); + opts->load_thresh = DEF_LTHRSH; + } + break; + + case 'c': + if ((opts->pkt_thresh = atol(optarg)) < MIN_CTHRSH) { + lwarn("Warning! Experimental net packet diff threshold too low, defaulting."); + opts->pkt_thresh = DEF_CTHRSH; + } + break; + + case 'n': + if ((opts->cif_thresh = atol(optarg)) < MIN_CTHRSH) { + lwarn("Warning! Control net packet diff threshold too low, defaulting."); + opts->cif_thresh = DEF_CTHRSH; + } + break; case 'h': default: @@ -177,15 +253,20 @@ int parse_args(int argc, char **argv) { int init_slothd(void) { - DIR *devs; +DIR *devs; int pfd; char pidbuf[10]; char bufstr[MAXDEVLEN]; struct dirent *dptr; struct hostent *hent; + char *ciprog[] = {"control_interface", NULL}; /* init internal vars */ - opts->numttys = 0; + parms->dolast = 0; /* init send-last-report-before-exiting variable */ + parms->numttys = 0; /* will enum terms in this func */ + 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 */ /* Setup signals */ signal(SIGTERM, siginthandler); @@ -207,39 +288,47 @@ int init_slothd(void) { lerror("Couldn't set path env"); } - /* enum tty special files, if necessary. */ - if (!opts->actterms) { - if ((devs = opendir("/dev")) == 0) { - lerror("Can't open directory /dev for processing"); - return -1; - } - opts->ttys[opts->numttys] = strdup("/dev/console"); - opts->numttys++; + /* 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"); + } + +#ifdef __linux__ + /* Open socket for SIOCGHWADDR ioctl (to get mac addresses) */ + parms->ifd = socket(PF_INET, SOCK_DGRAM, 0); +#endif + + /* enum tty special files */ + if ((devs = opendir("/dev")) == 0) { + lerror("Can't open directory /dev for processing"); + return -1; + } + parms->ttys[parms->numttys] = strdup("/dev/console"); + parms->numttys++; #ifdef __linux__ - /* - Include the pts mux device to check for activity on - dynamically allocated linux pts devices: - (/dev/pts/<num>) - */ - opts->ttys[opts->numttys] = strdup("/dev/ptmx"); - opts->numttys++; - - /* Open socket for SIOCGHWADDR ioctl */ - pkt->ifd = socket(PF_INET, SOCK_DGRAM, 0); + /* + Include the pts mux device to check for activity on + dynamically allocated linux pts devices: + (/dev/pts/<num>) + */ + parms->ttys[parms->numttys] = strdup("/dev/ptmx"); + parms->numttys++; #endif - while (opts->numttys < MAXTTYS && (dptr = readdir(devs))) { - if (strstr(dptr->d_name, "tty") || strstr(dptr->d_name, "pty")) { - snprintf(bufstr, MAXDEVLEN, "/dev/%s", dptr->d_name); - opts->ttys[opts->numttys] = strdup(bufstr); - opts->numttys++; - bzero(bufstr, sizeof(bufstr)); - } + while (parms->numttys < MAXTTYS && (dptr = readdir(devs))) { + if (strstr(dptr->d_name, "tty") || strstr(dptr->d_name, "pty")) { + snprintf(bufstr, MAXDEVLEN, "/dev/%s", dptr->d_name); + parms->ttys[parms->numttys] = strdup(bufstr); + parms->numttys++; + bzero(bufstr, sizeof(bufstr)); } } closedir(devs); /* prepare UDP connection to server */ - if ((pkt->sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + if ((parms->sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { lerror("Could not alloc socket"); return -1; } @@ -247,12 +336,12 @@ int init_slothd(void) { lerror("Can't resolve server hostname"); /* XXX use herror */ return -1; } - bzero(&pkt->servaddr, sizeof(struct sockaddr_in)); - pkt->servaddr.sin_family = AF_INET; - pkt->servaddr.sin_port = htons(opts->port); - bcopy(hent->h_addr_list[0], &pkt->servaddr.sin_addr.s_addr, + 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(pkt->sd, (struct sockaddr*)&pkt->servaddr, + if (connect(parms->sd, (struct sockaddr*)&parms->servaddr, sizeof(struct sockaddr_in)) < 0) { lerror("Couldn't connect to server"); return -1; @@ -278,56 +367,98 @@ int init_slothd(void) { } -void get_min_tty_idle(void) { - - int i; - time_t mintime = 0; - struct stat sb; +void do_exit(void) { + int status; - if (opts->actterms) { - utmp_enum_terms(); - if (!opts->numttys) { - mintime = wtmp_get_last(); - } + unlink(PIDFILE); + while (wait(&status) != -1); + lnotice("exiting."); + exit(0); +} + +int grab_cifname(char *buf, void *data) { + + int retval = -1; + char *tmpptr; + SLOTHD_PARAMS *myparms = (SLOTHD_PARAMS*) data; + + if (buf && isalpha(buf[0])) { + tmpptr = myparms->cifname = strdup(buf); + while (isalnum(*tmpptr)) tmpptr++; + *tmpptr = '\0'; + retval = 0; + } + else { + myparms->cifname = NULL; } -#ifdef COMMENT - /* Linux uses dynamically allocated UNIX98 ptys */ + return retval; +} + + +int send_pkt(SLOTHD_PACKET *pkt) { + + int i, numsent, retval = 1; + static char pktbuf[1500]; + static char minibuf[50]; + + /* flatten data into packet buffer */ + sprintf(pktbuf, "vers=%s mis=%lu lave=%.10f,%.10f,%.10f abits=0x%x ", + SDPROTOVERS, + pkt->minidle, + pkt->loadavg[0], + pkt->loadavg[1], + pkt->loadavg[2], + pkt->actbits); + + /* get all the interfaces too */ + for (i = 0; i < pkt->ifcnt; ++i) { + sprintf(minibuf, "iface=%s,%lu,%lu ", + pkt->ifaces[i].addr, + pkt->ifaces[i].ipkts, + pkt->ifaces[i].opkts); + strcat(pktbuf, minibuf); + } + + if (opts->debug) { + printf("packet: %s\n", pktbuf); + } + + /* send it */ else { - DIR *ptsdir; - struct dirent *dptr; - char *ptspath = "/dev/pts", curpath[15]; - - if ((ptsdir = opendir(ptspath)) == NULL) { - lerror("Couldn't open /dev/pts for processing"); + if ((numsent = send(parms->sd, pktbuf, strlen(pktbuf)+1, 0)) < 0) { + lerror("Problem sending slothd packet"); + retval = 0; } - else { - while ((dptr = readdir(ptsdir))) { - sprintf(curpath, "%s/%s", ptspath, dptr->d_name); - if (stat(curpath, &sb) < 0) { - fprintf(stderr, "Can't stat %s: %s\n", - curpath, strerror(errno)); /* XXX change. */ - } - else if (sb.st_atime > mintime) { - mintime = sb.st_atime; - } - } - closedir(ptsdir); + + else if (numsent < strlen(pktbuf)+1) { + lwarn("Warning! Slothd packet truncated"); + retval = 0; } } -#endif /* __linux__ */ - for (i = 0; i < opts->numttys; ++i) { - if (stat(opts->ttys[i], &sb) < 0) { + return retval; +} + + +void get_min_tty_idle(SLOTHD_PACKET *pkt) { + + int i; + time_t mintime = 0; + struct stat sb; + + for (i = 0; i < parms->numttys; ++i) { + if (stat(parms->ttys[i], &sb) < 0) { fprintf(stderr, "Can't stat %s: %s\n", - opts->ttys[i], strerror(errno)); /* XXX change. */ + parms->ttys[i], strerror(errno)); /* XXX change. */ } else if (sb.st_atime > mintime) { mintime = sb.st_atime; } } - pkt->minidle = mintime; + /* Assign TTY activity, ensuring we don't go older than initial startup */ + pkt->minidle = (mintime > parms->startup) ? mintime : parms->startup; if (opts->debug) { printf("Minidle: %s", ctime(&mintime)); } @@ -335,90 +466,80 @@ void get_min_tty_idle(void) { } -void utmp_enum_terms(void) { - - int utfd, nbytes, i; - char bufstr[MAXDEVLEN]; - struct utmp u; - - for (i = 0; i < opts->numttys; ++i) { - free(opts->ttys[i]); - } +void get_load(SLOTHD_PACKET *pkt) { - opts->numttys = 0; + int retval; - if ((utfd = open(UTMP_PATH, O_RDONLY)) < 0) { - lerror("Couldn't open utmp file"); - return; + 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 { - while ((nbytes = read(utfd, &u, sizeof(struct utmp)))) { - if (nbytes < sizeof(struct utmp)) { - lwarn("problem reading utmp structure - truncated."); - } - else if (u.ut_name[0] != '\0' && u.ut_host[0] != '\0') { - snprintf(bufstr, MAXDEVLEN, "/dev/%s", u.ut_line); - opts->ttys[opts->numttys] = strdup(bufstr); - opts->numttys++; - } - } + else if (opts->debug) { + printf("load averages: %f, %f, %f\n", pkt->loadavg[0], pkt->loadavg[1], + pkt->loadavg[2]); } - - close(utfd); return; } -time_t wtmp_get_last(void) { - int nbytes; - time_t retval = 0; - struct utmp u; - FILE *wtf; +int get_active_bits(SLOTHD_PACKET *pkt, SLOTHD_PACKET *opkt) { - if ((wtf = fopen(WTMP_PATH, "r")) < 0) { - lerror("Couldn't open wtmp file"); - } + int i; - else if (fseek(wtf, -sizeof(struct utmp), SEEK_END) < 0) { - lerror("Can't seek to end of file"); - } + pkt->actbits = 0; + if (pkt->minidle > opkt->minidle) { + pkt->actbits |= TTYACT; + } else { - do { - nbytes = fread(&u, 1, sizeof(struct utmp), wtf); - if (nbytes < sizeof(struct utmp)) { - lwarn("problem reading utmp structure - truncated."); - } - else if (u.ut_name[0] != '\0' && - u.ut_host[0] != '\0' && - strlen(u.ut_line) > 1) { - retval = u.ut_time; - break; - } - } while (fseek(wtf, -2*sizeof(struct utmp), SEEK_CUR) == 0 && - !ferror(wtf)); + pkt->actbits &= ~TTYACT; } - fclose(wtf); - return retval; -} -void get_load(void) { + /* XXX: whats the best way to compare load averages to a threshold? */ + if (pkt->loadavg[0] > opts->load_thresh) { + pkt->actbits |= LOADACT; + } + else { + pkt->actbits &= ~LOADACT; + } - int retval; + /* + * Have the packet counters exceeded the threshold? Make sure we don't + * count the incoming packets on the control net interface. + */ + for (i = 0; i < pkt->ifcnt; ++i) { + if (strcmp(parms->cifname, pkt->ifaces[i].ifname) == 0) { + if ((pkt->ifaces[i].opkts - opkt->ifaces[i].opkts) >= + opts->cif_thresh) { + break; + } + } + else if (((pkt->ifaces[i].opkts - opkt->ifaces[i].opkts) >= + opts->pkt_thresh) || + ((pkt->ifaces[i].ipkts - opkt->ifaces[i].ipkts) >= + opts->pkt_thresh)) { + break; + } + } - 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; + if (i < pkt->ifcnt) { + pkt->actbits |= PKTACT; + if (opts->debug) { + printf ("Packet threshold exceeded on %s\n", pkt->ifaces[i].ifname); + } } - else if (opts->debug) { - printf("load averages: %f, %f, %f\n", pkt->loadavg[0], pkt->loadavg[1], - pkt->loadavg[2]); + else { + pkt->actbits &= ~PKTACT; + } + + if (opts->debug) { + printf("Active bits: 0x%04x\n", pkt->actbits); } - return; -} + return pkt->actbits; +} -void get_packet_counts(void) { +void get_packet_counts(SLOTHD_PACKET *pkt) { int i; char *niprog[] = {"netstat", "-ni", NULL}; @@ -469,7 +590,7 @@ int get_counters(char *buf, void *data) { } #ifdef __linux__ strcpy(ifr.ifr_name, pkt->ifaces[pkt->ifcnt].ifname); - if (ioctl(pkt->ifd, SIOCGIFHWADDR, &ifr) < 0) { + if (ioctl(parms->ifd, SIOCGIFHWADDR, &ifr) < 0) { perror("error getting HWADDR"); return -1; } @@ -534,42 +655,3 @@ int procpipe(char *const prog[], int (procfunc)(char*,void*), void* data) { } return retcode; } - - -void send_pkt(void) { - - int i, numsent; - static char pktbuf[1500]; - static char minibuf[50]; - - /* flatten data into packet buffer */ - sprintf(pktbuf, "mis=%lu lave=%.10f,%.10f,%.10f ", - pkt->minidle, - pkt->loadavg[0], - pkt->loadavg[1], - pkt->loadavg[2]); - - for (i = 0; i < pkt->ifcnt; ++i) { - sprintf(minibuf, "iface=%s,%lu,%lu ", - pkt->ifaces[i].addr, - pkt->ifaces[i].ipkts, - pkt->ifaces[i].opkts); - strcat(pktbuf, minibuf); - } - - if (opts->debug) { - printf("packet: %s\n", pktbuf); - } - - /* send it */ - else { - if ((numsent = send(pkt->sd, pktbuf, strlen(pktbuf)+1, 0)) < 0) { - lerror("Problem sending slothd packet"); - } - - else if (numsent < strlen(pktbuf)+1) { - lwarn("Warning! Slothd packet truncated"); - } - } - return; -} diff --git a/sensors/slothd/slothd.h b/sensors/slothd/slothd.h index 4c8fb65881..78da01bf94 100644 --- a/sensors/slothd/slothd.h +++ b/sensors/slothd/slothd.h @@ -20,6 +20,7 @@ #include <time.h> #include <fcntl.h> #include <stdio.h> +#include <ctype.h> #include <syslog.h> #include <string.h> #include <dirent.h> @@ -37,6 +38,8 @@ #include <sys/ioctl.h> #endif +#define SDPROTOVERS "2" + #define SLOTHD_PATH_ENV "/bin:/usr/bin:/sbin:/usr/sbin:" CLIENT_BINDIR #define UTMP_PATH "/var/run/utmp" #define WTMP_PATH "/var/log/wtmp" @@ -47,8 +50,15 @@ #define LINEBUFLEN 256 #define MAXTTYS 2000 #define MAXDEVLEN 50 -#define MIN_INTVL 1 -#define DEF_INTVL 3600 /* 1 hour */ +#define MIN_RINTVL 1 /* 1 minute */ +#define DEF_RINTVL 3600 /* 1 hour */ +#define MIN_AINTVL 1 /* 1 second */ +#define DEF_AINTVL 10 /* 10 seconds */ +#define MIN_LTHRSH 0.1 /* Load avg. threshold */ +#define DEF_LTHRSH 1 /* Fairly high - could get false pos/neg */ +#define MIN_CTHRSH 0 /* No packets */ +#define DEF_CTHRSH 1 /* At least 1 packet */ +#define OFFSET_FRACTION 0.5 #define SLOTHD_DEF_SERV "boss" #define SLOTHD_DEF_PORT 8509 /* XXX change */ @@ -63,15 +73,30 @@ #define NUMSCAN 3 #endif +#define TTYACT (1<<0) +#define LOADACT (1<<1) +#define PKTACT (1<<2) + typedef struct { - u_long minidle; - double loadavg[3]; - int ifcnt; int sd; #ifdef __linux__ int ifd; /* IOCTL file descriptor */ #endif + u_int cnt; + char *cifname; + u_char dolast; + time_t lastrpt; + time_t startup; struct sockaddr_in servaddr; + u_short numttys; + char *ttys[MAXTTYS]; +} SLOTHD_PARAMS; + +typedef struct { + int ifcnt; + u_long minidle; + double loadavg[3]; + u_short actbits; struct { u_long ipkts; u_long opkts; @@ -81,30 +106,29 @@ typedef struct { } SLOTHD_PACKET; typedef struct { - u_int interval; - u_short numttys; + time_t reg_interval; + time_t agg_interval; + double load_thresh; + u_long pkt_thresh; + u_long cif_thresh; u_char debug; - u_char actterms; - u_char first; u_char once; char *servname; u_short port; - char *ttys[MAXTTYS]; } SLOTHD_OPTS; int parse_args(int, char**); int init_slothd(void); +void do_exit(void); +int send_pkt(SLOTHD_PACKET*); +int procpipe(char *const prog[], int (procfunc)(char*,void*), void* data); -void get_min_tty_idle(void); -void utmp_enum_terms(void); -time_t wtmp_get_last(void); -void get_load(void); -void get_packet_counts(void); +void get_min_tty_idle(SLOTHD_PACKET*); +void get_load(SLOTHD_PACKET*); +void get_packet_counts(SLOTHD_PACKET*); +int get_active_bits(SLOTHD_PACKET*, SLOTHD_PACKET*); -int get_macaddrs(char*,void*); int get_counters(char*,void*); - -int procpipe(char *const prog[], int (procfunc)(char*,void*), void* data); -void send_pkt(void); +int grab_cifname(char*,void*); #endif /* #ifndef _SLOTHD_H */ -- GitLab