diff --git a/sensors/slothd/GNUmakefile.in b/sensors/slothd/GNUmakefile.in index c6228fcb709b5353260804e614f6301b9dbec064..79ae0c34d060e13d8191be0309b0da2b519e6cd5 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 830fc777b6dcf1478f2ca3ec0e8b50ae2f1f6cd6..de617a1bed1510e09a4f6e8f73c5534e7dae86df 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 53ec4b6a3e21246f073092a94348415dad6afeb1..c761023587a7ae2f21273a9439d82fc48b1003f8 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 ab649986905cc32811e8546686c289bd6a99e95c..845a1368b523b1f2c8def1181d3d8070e1abb589 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 4c8fb6588143840bd39e38bae7f4de87c3bca1a6..78da01bf9471754a50a044b194986f3e6200aa72 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 */