diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/bbgen.h ./bbdisplay/bbgen.h --- /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/bbgen.h 2006-08-09 22:09:54.000000000 +0200 +++ ./bbdisplay/bbgen.h 2006-10-03 13:52:28.000000000 +0200 @@ -51,9 +51,9 @@ dialup column -------> bbgen_col_t reportwarnlevel color name comment age next - banks oldage - banksize acked - next alert + next oldage + acked + alert onwap propagate reportinfo @@ -140,6 +140,9 @@ struct state_t *next; } state_t; +/* OSX has a built-in "host_t" type. */ +#define host_t bbgen_host_t + typedef struct host_t { char *hostname; char *displayname; @@ -166,8 +169,6 @@ struct bbgen_page_t *parent; double reportwarnlevel; char *reporttime; - int *banks; - int banksize; int nobb2; struct host_t *next; } host_t; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/loadbbhosts.c ./bbdisplay/loadbbhosts.c --- /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/loadbbhosts.c 2006-08-09 22:09:54.000000000 +0200 +++ ./bbdisplay/loadbbhosts.c 2007-02-09 11:01:19.389403275 +0100 @@ -109,7 +109,7 @@ } xfree(set); - return ((strlen(result) > 0) ? result : NULL); + return result; /* This may be an empty string */ } bbgen_page_t *init_page(char *name, char *title) @@ -174,8 +174,7 @@ int ip1, int ip2, int ip3, int ip4, int dialup, double warnpct, char *reporttime, char *alerts, int nktime, char *waps, - char *nopropyellowtests, char *nopropredtests, char *noproppurpletests, char *nopropacktests, - int modembanksize) + char *nopropyellowtests, char *nopropredtests, char *noproppurpletests, char *nopropacktests) { host_t *newhost = (host_t *) calloc(1, sizeof(host_t)); hostlist_t *oldlist; @@ -256,21 +255,6 @@ } newhost->parent = NULL; - newhost->banks = NULL; - newhost->banksize = modembanksize; - if (modembanksize) { - int i; - newhost->banks = (int *) calloc(modembanksize, sizeof(int)); - for (i=0; i<modembanksize; i++) newhost->banks[i] = -1; - - if (comment) { - newhost->comment = (char *) realloc(newhost->comment, strlen(newhost->comment) + 22); - sprintf(newhost->comment+strlen(newhost->comment), " - [%s]", newhost->ip); - } - else { - newhost->comment = newhost->ip; - } - } newhost->nobb2 = 0; newhost->next = NULL; @@ -440,7 +424,6 @@ host_t *curhost; char *curtitle; int ip1, ip2, ip3, ip4; - int modembanksize; char *p; int fqdn = get_fqdn(); @@ -482,8 +465,6 @@ dbgprintf("load_bbhosts: -- got line '%s'\n", STRBUF(inbuf)); - modembanksize = 0; - if (strncmp(STRBUF(inbuf), pagetag, strlen(pagetag)) == 0) { getnamelink(STRBUF(inbuf), &name, &link); if (curpage == NULL) { @@ -581,9 +562,7 @@ if (curtitle) { curgroup->pretitle = curtitle; curtitle = NULL; } curhost = NULL; } - else if ( (sscanf(STRBUF(inbuf), "%3d.%3d.%3d.%3d %s", &ip1, &ip2, &ip3, &ip4, hostname) == 5) || - (!reportstart && !snapshot && (sscanf(STRBUF(inbuf), "dialup %s %d.%d.%d.%d %d", hostname, &ip1, &ip2, &ip3, &ip4, &modembanksize) == 6) && (modembanksize > 0)) ) { - + else if (sscanf(STRBUF(inbuf), "%3d.%3d.%3d.%3d %s", &ip1, &ip2, &ip3, &ip4, hostname) == 5) { namelist_t *bbhost = NULL; int dialup, nobb2, nktime = 1; double warnpct = reportwarnlevel; @@ -685,8 +664,7 @@ ip1, ip2, ip3, ip4, dialup, warnpct, reporttime, alertlist, nktime, onwaplist, - nopropyellowlist, nopropredlist, noproppurplelist, nopropacklist, - modembanksize); + nopropyellowlist, nopropredlist, noproppurplelist, nopropacklist); if (curgroup != NULL) { curgroup->hosts = curhost; } @@ -710,8 +688,7 @@ warnpct, reporttime, alertlist, nktime, onwaplist, nopropyellowlist,nopropredlist, - noproppurplelist, nopropacklist, - modembanksize); + noproppurplelist, nopropacklist); } curhost->parent = (cursubparent ? cursubparent : (cursubpage ? cursubpage : curpage)); if (curtitle) { curhost->pretitle = curtitle; curtitle = NULL; } @@ -758,8 +735,7 @@ warnpct, reporttime, alertlist, nktime, onwaplist, nopropyellowlist,nopropredlist, - noproppurplelist, nopropacklist, - modembanksize); + noproppurplelist, nopropacklist); if (wantedgroup > 0) { group_t *gwalk; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/loadbbhosts.h ./bbdisplay/loadbbhosts.h --- /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/loadbbhosts.h 2006-08-09 22:09:54.000000000 +0200 +++ ./bbdisplay/loadbbhosts.h 2006-10-03 13:52:28.000000000 +0200 @@ -24,8 +24,7 @@ int dialup, double warnpct, char *reporttime, char *alerts, int nktime, char *waps, - char *nopropyellowtests, char *nopropredtests, char *noproppurpletests, char *nopropacktests, - int modembanksize); + char *nopropyellowtests, char *nopropredtests, char *noproppurpletests, char *nopropacktests); extern char *nopropyellowdefault; extern char *nopropreddefault; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/loaddata.c ./bbdisplay/loaddata.c --- /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/loaddata.c 2006-08-09 22:09:54.000000000 +0200 +++ ./bbdisplay/loaddata.c 2006-10-03 13:52:28.000000000 +0200 @@ -182,12 +182,6 @@ host = find_host(hostname); - /* If the host is a modem-bank host, dont mix in normal status messages */ - if (host && (host->banksize > 0)) { - errprintf("Modembank %s has additional status-logs - ignored\n", hostname); - return NULL; - } - newstate = (state_t *) calloc(1, sizeof(state_t)); newstate->entry = (entry_t *) calloc(1, sizeof(entry_t)); newstate->next = NULL; @@ -347,72 +341,6 @@ return newsum; } -void init_modembank_status(char *fn, logdata_t *log) -{ - char *msgcopy; - host_t *targethost; - time_t now = time(NULL); - - dbgprintf("init_modembank_status(%s)\n", textornull(fn)); - - if (log->validtime < now) return; - - targethost = find_host(fn+strlen("dialup.")); - if (targethost == NULL) { - dbgprintf("Modembank status from unknown host %s - ignored\n", fn+strlen("dialup.")); - return; - } - - msgcopy = strdup(log->msg); - if (strlen(msgcopy)) { - char *startip, *endip, *tag; - int idx = -1; - - startip = endip = NULL; - tag = strtok(msgcopy, " \n"); - while (tag) { - if (idx >= 0) { - /* Next result */ - if (idx < targethost->banksize) targethost->banks[idx] = parse_color(tag); - idx++; - } - else if (strcmp(tag, "DATA") == 0) { - if (startip && endip) idx = 0; - else errprintf("Invalid modembank status logfile %s (missing FROM and/or TO)\n", fn); - } - else if (strcmp(tag, "FROM") == 0) { - tag = strtok(NULL, " \n"); - - if (tag) { - startip = tag; - if (strcmp(startip, targethost->ip) != 0) { - errprintf("Modembank in bb-hosts begins with %s, but logfile begins with %s\n", - targethost->ip, startip); - } - } else errprintf("Invalid modembank status logfile %s (truncated)\n", fn); - } - else if (strcmp(tag, "TO") == 0) { - tag = strtok(NULL, " \n"); - - if (tag) { - if (startip) endip = tag; - else errprintf("Invalid modembank status logfile %s (no FROM)\n", fn); - } else errprintf("Invalid modembank status logfile %s (truncated)\n", fn); - } - - if (tag) tag = strtok(NULL, " \n"); - } - - if ((idx >= 0) && (idx != targethost->banksize)) { - errprintf("Modembank status log %s has more entries (%d) than expected (%d)\n", - fn, (idx-1), targethost->banksize); - } - } - - xfree(msgcopy); -} - - state_t *load_state(dispsummary_t **sumhead) { int hobbitdresult; @@ -506,9 +434,6 @@ } } } - else if (strncmp(fn, "dialup.", 7) == 0) { - init_modembank_status(fn, &log); - } else { if (acklist && *acklist) { /* diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/pagegen.c ./bbdisplay/pagegen.c --- /home/henrik/hobbit/release/hobbit-4.2.0/bbdisplay/pagegen.c 2006-08-09 22:09:54.000000000 +0200 +++ ./bbdisplay/pagegen.c 2006-10-03 13:52:28.000000000 +0200 @@ -322,8 +322,6 @@ int genstatic; int columncount; char *bbskin; - int maxbanksize = 0; - int anyplainhosts = 0; int rowcount = 0; if (head == NULL) @@ -338,27 +336,10 @@ else fprintf(output, "<A NAME=hosts-blk-%d> </A>\n\n", hostblkidx); hostblkidx++; - for (h = head; (h); h = h->next) { - if (h->banksize > maxbanksize) maxbanksize = h->banksize; - if (h->banksize == 0) anyplainhosts = 1; - } - - if (maxbanksize == 0) { - /* No modembanks - normal hostlist with columns and stuff */ - groupcols = gen_column_list(head, pagetype, onlycols, exceptcols); - for (columncount=0, gc=groupcols; (gc); gc = gc->next, columncount++) ; - } - else { - /* There are modembanks here! */ - if (anyplainhosts) { - errprintf("WARNING: Modembank displays should be in their own group or page.\nMixing normal hosts with modembanks yield strange output.\n"); - } - groupcols = NULL; - columncount = maxbanksize; - } - - if (groupcols || (maxbanksize > 0)) { + groupcols = gen_column_list(head, pagetype, onlycols, exceptcols); + for (columncount=0, gc=groupcols; (gc); gc = gc->next, columncount++) ; + if (groupcols) { int width; width = atoi(xgetenv("DOTWIDTH")); @@ -386,29 +367,11 @@ fprintf(output, "<TD VALIGN=MIDDLE ROWSPAN=2><CENTER><FONT %s>%s</FONT></CENTER></TD>\n", xgetenv("MKBBTITLE"), (strlen(grouptitle) ? grouptitle : " ")); - if ((groupcols == NULL) && (maxbanksize > 0)) { - int i,j; - - fprintf(output, "<TD><TABLE BORDER=0>\n"); - for (i=0; (i < maxbanksize); i+=16) { - fprintf(output, "<TR>\n"); - for (j=i; (((j-i) < 16) && (j < maxbanksize)); j++) { - fprintf(output, " <TD ALIGN=CENTER VALIGN=BOTTOM WIDTH=%d>", width); - fprintf(output, " <FONT %s><B>%d</B></FONT>", - xgetenv("MKBBCOLFONT"), j); - fprintf(output, " </TD>\n"); - } - fprintf(output, "</TR>\n"); - } - fprintf(output, "</TABLE></TD>\n"); - } - else { - for (gc=groupcols; (gc); gc = gc->next) { - fprintf(output, " <TD ALIGN=CENTER VALIGN=BOTTOM WIDTH=45>\n"); - fprintf(output, " <A HREF=\"%s\"><FONT %s><B>%s</B></FONT></A> </TD>\n", - columnlink(gc->column->name), - xgetenv("MKBBCOLFONT"), gc->column->name); - } + for (gc=groupcols; (gc); gc = gc->next) { + fprintf(output, " <TD ALIGN=CENTER VALIGN=BOTTOM WIDTH=45>\n"); + fprintf(output, " <A HREF=\"%s\"><FONT %s><B>%s</B></FONT></A> </TD>\n", + columnlink(gc->column->name), + xgetenv("MKBBCOLFONT"), gc->column->name); } fprintf(output, "</TR> \n<TR><TD COLSPAN=%d><HR WIDTH=\"100%%\"></TD></TR>\n\n", columncount); } @@ -421,44 +384,6 @@ hostnamehtml(h->hostname, ((pagetype != PAGE_BB) ? hostpage_link(h) : NULL)) ); /* Then the columns. */ - if ((groupcols == NULL) && (h->banksize > 0)) { - int i, j; - char alttag[30]; - unsigned int baseip, ip1, ip2, ip3, ip4; - - sscanf(h->ip, "%u.%u.%u.%u", &ip1, &ip2, &ip3, &ip4); - baseip = IPtou32(ip1, ip2, ip3, ip4); - - fprintf(output, "<TD ALIGN=CENTER><TABLE BORDER=0>"); - for (i=0; (i < h->banksize); i+=16) { - fprintf(output, "<TR>\n"); - for (j=i; (((j-i) < 16) && (j < h->banksize)); j++) { - fprintf(output, "<TD ALIGN=CENTER VALIGN=BOTTOM WIDTH=%d>", width); - - if (genstatic) { - /* - * Dont use htmlextension here - it's for the - * pages generated by bbd. - */ - fprintf(output, "<A HREF=\"%s/html/dialup.%s.html\">", - xgetenv("BBWEB"), h->hostname); - } - else { - fprintf(output, "<A HREF=\"%s\">", - hostsvcurl("dialup", commafy(h->hostname), 1)); - } - - sprintf(alttag, "%s:%s", u32toIP(baseip+j), colorname(h->banks[j])); - fprintf(output, "<IMG SRC=\"%s/%s\" ALT=\"%s\" TITLE=\"%s\" HEIGHT=\"%s\" WIDTH=\"%s\" BORDER=0></A>", - bbskin, dotgiffilename(h->banks[j], 0, 0), - alttag, alttag, xgetenv("DOTHEIGHT"), xgetenv("DOTWIDTH")); - - fprintf(output, "</TD>\n"); - } - fprintf(output, "</TR>\n"); - } - fprintf(output, "</TABLE></TD>\n"); - } for (gc = groupcols; (gc); gc = gc->next) { char *htmlalttag; @@ -657,7 +582,7 @@ if (newhost == NULL) { /* New summary "host" */ - newhost = init_host(s->row, 1, NULL, NULL, NULL, NULL, 0,0,0,0, 0, 0.0, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0); + newhost = init_host(s->row, 1, NULL, NULL, NULL, NULL, 0,0,0,0, 0, 0.0, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL); /* Insert into sorted host list */ if ((!sumhosts) || (strcmp(newhost->hostname, sumhosts->hostname) < 0)) { diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/bbnet/bbtest-net.c ./bbnet/bbtest-net.c --- /home/henrik/hobbit/release/hobbit-4.2.0/bbnet/bbtest-net.c 2006-08-09 22:09:55.000000000 +0200 +++ ./bbnet/bbtest-net.c 2006-10-03 13:52:28.000000000 +0200 @@ -64,27 +64,13 @@ NULL }; -/* toolid values */ -#define TOOL_CONTEST 0 -#define TOOL_NSLOOKUP 1 -#define TOOL_DIG 2 -#define TOOL_NTP 3 -#define TOOL_FPING 4 -#define TOOL_HTTP 5 -#define TOOL_MODEMBANK 6 -#define TOOL_LDAP 7 -#define TOOL_RPCINFO 8 - - RbtHandle svctree; /* All known services, has service_t records */ service_t *pingtest = NULL; /* Identifies the pingtest within svctree list */ int pingcount = 0; service_t *dnstest = NULL; /* Identifies the dnstest within svctree list */ -service_t *digtest = NULL; /* Identifies the digtest within svctree list */ service_t *httptest = NULL; /* Identifies the httptest within svctree list */ service_t *ldaptest = NULL; /* Identifies the ldaptest within svctree list */ service_t *rpctest = NULL; /* Identifies the rpctest within svctree list */ -service_t *modembanktest = NULL; /* Identifies the modembank test within svctree list */ RbtHandle testhosttree; /* All tested hosts, has testedhost_t records */ char *nonetpage = NULL; /* The "NONETPAGE" env. variable */ int dnsmethod = DNS_THEN_IP; /* How to do DNS lookups */ @@ -157,35 +143,21 @@ printf("Service %s, port %d, toolid %d\n", swalk->testname, swalk->portnum, swalk->toolid); for (iwalk = swalk->items; (iwalk); iwalk = iwalk->next) { - if (swalk == modembanktest) { - modembank_t *mentry; - int i; - - mentry = iwalk->privdata; - printf("\tModembank : %s\n", textornull(mentry->hostname)); - printf("\tStart-IP : %s\n", u32toIP(mentry->startip)); - printf("\tBanksize : %d\n", mentry->banksize); - printf("\tOpen :"); - for (i=0; i<mentry->banksize; i++) printf(" %d", mentry->responses[i]); - printf("\n"); - } - else { - printf("\tHost : %s\n", textornull(iwalk->host->hostname)); - printf("\ttestspec : %s\n", textornull(iwalk->testspec)); - printf("\tFlags :"); - if (iwalk->dialup) printf(" dialup"); - if (iwalk->reverse) printf(" reverse"); - if (iwalk->silenttest) printf(" silenttest"); - if (iwalk->alwaystrue) printf(" alwaystrue"); - printf("\n"); - printf("\tOpen : %d\n", iwalk->open); - printf("\tBanner : %s\n", textornull(STRBUF(iwalk->banner))); - printf("\tcertinfo : %s\n", textornull(iwalk->certinfo)); - printf("\tDuration : %ld.%06ld\n", (long int)iwalk->duration.tv_sec, (long int)iwalk->duration.tv_usec); - printf("\tbadtest : %d:%d:%d\n", iwalk->badtest[0], iwalk->badtest[1], iwalk->badtest[2]); - printf("\tdowncount : %d started %s", iwalk->downcount, ctime(&iwalk->downstart)); - printf("\n"); - } + printf("\tHost : %s\n", textornull(iwalk->host->hostname)); + printf("\ttestspec : %s\n", textornull(iwalk->testspec)); + printf("\tFlags :"); + if (iwalk->dialup) printf(" dialup"); + if (iwalk->reverse) printf(" reverse"); + if (iwalk->silenttest) printf(" silenttest"); + if (iwalk->alwaystrue) printf(" alwaystrue"); + printf("\n"); + printf("\tOpen : %d\n", iwalk->open); + printf("\tBanner : %s\n", textornull(STRBUF(iwalk->banner))); + printf("\tcertinfo : %s\n", textornull(iwalk->certinfo)); + printf("\tDuration : %ld.%06ld\n", (long int)iwalk->duration.tv_sec, (long int)iwalk->duration.tv_usec); + printf("\tbadtest : %d:%d:%d\n", iwalk->badtest[0], iwalk->badtest[1], iwalk->badtest[2]); + printf("\tdowncount : %d started %s", iwalk->downcount, ctime(&iwalk->downstart)); + printf("\n"); } printf("\n"); @@ -423,35 +395,6 @@ if (!wanted_host(hwalk, location)) continue; - if (argnmatch(hwalk->bbhostname, "@dialup.")) { - /* Modembank entry: "dialup displayname startIP count" */ - - char *realname; - testitem_t *newtest; - modembank_t *newentry; - int i, ip1, ip2, ip3, ip4, banksize; - - realname = hwalk->bbhostname + strlen("@dialup."); - banksize = atoi(bbh_item(hwalk, BBH_BANKSIZE)); - sscanf(bbh_item(hwalk, BBH_IP), "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4); - - newtest = init_testitem(NULL, modembanktest, NULL, 0, 0, 0, 0, 0); - newtest->next = modembanktest->items; - modembanktest->items = newtest; - - newtest->privdata = (void *)malloc(sizeof(modembank_t)); - newentry = (modembank_t *)newtest->privdata; - newentry->hostname = realname; - newentry->startip = IPtou32(ip1, ip2, ip3, ip4); - newentry->banksize = banksize; - newentry->responses = (int *) malloc(banksize * sizeof(int)); - for (i=0; i<banksize; i++) newentry->responses[i] = 0; - - /* No more to do for modembanks */ - continue; - } - - h = init_testedhost(hwalk->bbhostname); p = bbh_custom_item(hwalk, "badconn:"); @@ -645,7 +588,7 @@ s = dnstest; } else if (argnmatch(testspec, "dig=")) { - s = digtest; + s = dnstest; } else { /* @@ -1311,60 +1254,6 @@ return 0; } -void run_modembank_service(service_t *service) -{ - testitem_t *t; - char cmd[1024]; - char startip[IP_ADDR_STRLEN], endip[IP_ADDR_STRLEN]; - char *p; - char cmdpath[PATH_MAX]; - FILE *cmdpipe; - char l[MAX_LINE_LEN]; - int ip1, ip2, ip3, ip4; - - for (t=service->items; (t); t = t->next) { - modembank_t *req = (modembank_t *)t->privdata; - - p = xgetenv("FPING"); - strcpy(cmdpath, (p ? p : "hobbitping")); - strcpy(startip, u32toIP(req->startip)); - strcpy(endip, u32toIP(req->startip + req->banksize - 1)); - sprintf(cmd, "%s -g -Ae %s %s 2>/dev/null", cmdpath, startip, endip); - - dbgprintf("Running command: '%s'\n", cmd); - cmdpipe = popen(cmd, "r"); - if (cmdpipe == NULL) { - errprintf("Could not run the hobbitping command %s\n", cmd); - return; - } - - while (fgets(l, sizeof(l), cmdpipe)) { - dbgprintf("modembank response: %s", l); - - if (sscanf(l, "%d.%d.%d.%d ", &ip1, &ip2, &ip3, &ip4) == 4) { - unsigned int idx = IPtou32(ip1, ip2, ip3, ip4) - req->startip; - - if (idx >= req->banksize) { - errprintf("Unexpected response for IP not in bank - %d.%d.%d.%d", - ip1, ip2, ip3, ip4); - } - else { - req->responses[idx] = (strstr(l, "is alive") != NULL); - } - } - } - pclose(cmdpipe); - - if (debug) { - int i; - - dbgprintf("Results for modembank start=%s, length %d\n", u32toIP(req->startip), req->banksize); - for (i=0; (i<req->banksize); i++) - dbgprintf("\t%s is %d\n", u32toIP(req->startip+i), req->responses[i]); - } - } -} - int decide_color(service_t *service, char *svcname, testitem_t *test, int failgoesclear, char *cause) { @@ -1756,44 +1645,6 @@ } -void send_modembank_results(service_t *service) -{ - testitem_t *t; - char msgline[1024]; - int i, color, inuse; - char startip[IP_ADDR_STRLEN], endip[IP_ADDR_STRLEN]; - - for (t=service->items; (t); t = t->next) { - modembank_t *req = (modembank_t *)t->privdata; - - inuse = 0; - strcpy(startip, u32toIP(req->startip)); - strcpy(endip, u32toIP(req->startip + req->banksize - 1)); - - init_status(COL_GREEN); /* Modembanks are always green */ - sprintf(msgline, "status dialup.%s %s %s FROM %s TO %s DATA ", - req->hostname, colorname(COL_GREEN), timestamp, startip, endip); - addtostatus(msgline); - for (i=0; i<req->banksize; i++) { - if (req->responses[i]) { - color = COL_GREEN; - inuse++; - } - else { - color = COL_CLEAR; - } - - sprintf(msgline, "%s ", colorname(color)); - addtostatus(msgline); - } - - sprintf(msgline, "\n\nUsage: %d of %d (%d%%)\n", inuse, req->banksize, ((inuse * 100) / req->banksize)); - addtostatus(msgline); - finish_status(); - } -} - - void send_rpcinfo_results(service_t *service, int failgoesclear) { testitem_t *t; @@ -2234,14 +2085,12 @@ return 0; } - dnstest = add_service("dns", getportnumber("domain"), 0, TOOL_NSLOOKUP); - digtest = add_service("dig", getportnumber("domain"), 0, TOOL_DIG); + dnstest = add_service("dns", getportnumber("domain"), 0, TOOL_DNS); add_service("ntp", getportnumber("ntp"), 0, TOOL_NTP); rpctest = add_service("rpc", getportnumber("sunrpc"), 0, TOOL_RPCINFO); httptest = add_service("http", getportnumber("http"), 0, TOOL_HTTP); ldaptest = add_service("ldapurl", getportnumber("ldap"), strlen("ldap"), TOOL_LDAP); if (pingcolumn) pingtest = add_service(pingcolumn, 0, 0, TOOL_FPING); - modembanktest = add_service("dialup", 0, 0, TOOL_MODEMBANK); add_timestamp("Service definitions loaded"); load_tests(); @@ -2360,18 +2209,14 @@ add_timestamp("LDAP tests result collection completed"); - /* dns, dig, ntp tests */ + /* dns, ntp tests */ for (handle = rbtBegin(svctree); handle != rbtEnd(svctree); handle = rbtNext(svctree, handle)) { s = (service_t *)gettreeitem(svctree, handle); if (s->items) { switch(s->toolid) { - case TOOL_NSLOOKUP: + case TOOL_DNS: run_nslookup_service(s); - add_timestamp("NSLOOKUP tests executed"); - break; - case TOOL_DIG: - run_nslookup_service(s); - add_timestamp("DIG tests executed"); + add_timestamp("DNS tests executed"); break; case TOOL_NTP: run_ntp_service(s); @@ -2381,9 +2226,7 @@ run_rpcinfo_service(s); add_timestamp("RPC tests executed"); break; - case TOOL_MODEMBANK: - run_modembank_service(s); - add_timestamp("Modembank tests executed"); + default: break; } } @@ -2394,8 +2237,7 @@ s = (service_t *)gettreeitem(svctree, handle); switch (s->toolid) { case TOOL_CONTEST: - case TOOL_NSLOOKUP: - case TOOL_DIG: + case TOOL_DNS: case TOOL_NTP: send_results(s, failgoesclear); break; @@ -2406,10 +2248,6 @@ /* These handle result-transmission internally */ break; - case TOOL_MODEMBANK: - send_modembank_results(s); - break; - case TOOL_RPCINFO: send_rpcinfo_results(s, failgoesclear); break; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/bbnet/bbtest-net.h ./bbnet/bbtest-net.h --- /home/henrik/hobbit/release/hobbit-4.2.0/bbnet/bbtest-net.h 2006-08-09 22:09:55.000000000 +0200 +++ ./bbnet/bbtest-net.h 2006-10-03 13:52:28.000000000 +0200 @@ -19,6 +19,9 @@ #define MAX_CONTENT_DATA (1024*1024) /* 1 MB should be enough for most */ +/* toolids */ +enum toolid_t { TOOL_CONTEST, TOOL_DNS, TOOL_NTP, TOOL_FPING, TOOL_HTTP, TOOL_LDAP, TOOL_RPCINFO }; + /* * Structure of the bbtest-net in-memory records * @@ -62,12 +65,11 @@ char *testname; /* Name of the test = column name in Hobbit report */ int namelen; /* Length of name - "testname:port" has this as strlen(testname), others 0 */ int portnum; /* Port number this service runs on */ - int toolid; /* Which tool to use */ + enum toolid_t toolid; /* Which tool to use */ struct testitem_t *items; /* testitem_t linked list of tests for this service */ } service_t; -#define MULTIPING_BEST 0 -#define MULTIPING_WORST 1 +enum multiping_t { MULTIPING_BEST, MULTIPING_WORST }; typedef struct ipping_t { char *ip; int open; @@ -76,7 +78,7 @@ } ipping_t; typedef struct extraping_t { - int matchtype; + enum multiping_t matchtype; ipping_t *iplist; } extraping_t; @@ -150,13 +152,6 @@ struct testitem_t *next; } testitem_t; -typedef struct modembank_t { - char *hostname; - unsigned int startip; /* Saved as 32-bit binary */ - int banksize; - int *responses; -} modembank_t; - typedef struct dnstest_t { int testcount; int okcount; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/build/bb-commands.sh ./build/bb-commands.sh --- /home/henrik/hobbit/release/hobbit-4.2.0/build/bb-commands.sh 2006-08-09 22:09:57.000000000 +0200 +++ ./build/bb-commands.sh 2006-10-03 12:55:27.000000000 +0200 @@ -23,23 +23,13 @@ echo "# Hobbit does not use them, but they are provided here so if you use BB extension" echo "# scripts, then they will hopefully run without having to do a lot of tweaking." echo "" -for CMD in uptime awk cat cp cut date egrep expr find grep head id ln ls mv rm sed sort tail touch tr uniq who +for CMD in uptime awk cat cp cut date egrep expr find grep head id ln ls mv rm sed sort tail top touch tr uniq who do ENVNAME=`echo $CMD | tr "[a-z]" "[A-Z]"` PGM=`findbin $CMD | head -n 1` echo "${ENVNAME}=\"${PGM}\"" done -# TOP can either be "top", or on Solaris the "prstat" command. -PRSTAT=`findbin prstat | head -n 1` -if test "$PRSTAT" != "" -then - PGM="$PRSTAT -can 20 1 1" -else - PGM=`findbin top | head -n 1` -fi -echo "TOP=\"${PGM}\"" - # WC is special PGM=`findbin wc | head -n 1` echo "WC=\"${PGM} -l\"" diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/client/hobbitclient-aix.sh ./client/hobbitclient-aix.sh --- /home/henrik/hobbit/release/hobbit-4.2.0/client/hobbitclient-aix.sh 2006-08-09 22:09:58.000000000 +0200 +++ ./client/hobbitclient-aix.sh 2006-10-03 13:52:28.000000000 +0200 @@ -36,7 +36,7 @@ echo "[ifconfig]" ifconfig -a echo "[route]" -netstat -r +netstat -rn echo "[netstat]" netstat -s echo "[ports]" diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/client/hobbitclient-darwin.sh ./client/hobbitclient-darwin.sh --- /home/henrik/hobbit/release/hobbit-4.2.0/client/hobbitclient-darwin.sh 2006-08-09 22:09:58.000000000 +0200 +++ ./client/hobbitclient-darwin.sh 2006-10-03 13:52:28.000000000 +0200 @@ -33,7 +33,7 @@ echo "[ifconfig]" ifconfig -a echo "[route]" -netstat -r +netstat -rn echo "[netstat]" netstat -s echo "[ifstat]" diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/client/hobbitclient-sunos.sh ./client/hobbitclient-sunos.sh --- /home/henrik/hobbit/release/hobbit-4.2.0/client/hobbitclient-sunos.sh 2006-08-09 22:09:58.000000000 +0200 +++ ./client/hobbitclient-sunos.sh 2006-10-03 12:55:27.000000000 +0200 @@ -22,7 +22,7 @@ echo "[df]" # All of this because Solaris df cannot show multiple fs-types, or exclude certain fs types. -FSTYPES=`/bin/df -n -l|awk '{print $3}'|egrep -v "^proc|^fd|^mntfs|^ctfs|^devfs|^objfs|^nfs"|sort|uniq` +FSTYPES=`/bin/df -n -l|awk '{print $3}'|egrep -v "^proc|^fd|^mntfs|^ctfs|^devfs|^objfs|^nfs|^lofs"|sort|uniq` if test "$FSTYPES" = ""; then FSTYPES="ufs"; fi set $FSTYPES /bin/df -F $1 -k | grep -v " /var/run" @@ -54,14 +54,13 @@ echo "[ps]" ps -A -o pid,ppid,user,stime,s,pri,pcpu,time,pmem,rss,vsz,args -# $TOP must be set, the install utility should do that for us if it exists. -if test "$TOP" != "" +# If TOP is defined, then use it. If not, fall back to the Solaris prstat command. +echo "[top]" +if test "$TOP" != "" -a -x "$TOP" then - if test -x "$TOP" - then - echo "[top]" - $TOP -b 20 - fi + "$TOP" -b 20 +else + prstat -can 20 1 1 fi # vmstat and iostat (iostat -d provides a cpu utilisation with I/O wait number) diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/client/logfetch.c ./client/logfetch.c --- /home/henrik/hobbit/release/hobbit-4.2.0/client/logfetch.c 2006-08-09 22:09:58.000000000 +0200 +++ ./client/logfetch.c 2006-10-03 13:52:28.000000000 +0200 @@ -52,8 +52,10 @@ long lastpos[POSCOUNT]; long maxbytes; #endif - char *trigger; - char *ignore; + char **trigger; + int triggercount; + char **ignore; + int ignorecount; } logdef_t; typedef struct filedef_t { @@ -111,7 +113,7 @@ int openerr, i, status, triggerlinecount, done; char *linepos[2*LINES_AROUND_TRIGGER+1]; int lpidx; - regex_t ignexpr, trigexpr; + regex_t *ignexpr = NULL, *trigexpr = NULL; #ifdef _LARGEFILE_SOURCE off_t bufsz; #else @@ -188,13 +190,21 @@ } /* Compile the regex patterns */ - if (logdef->ignore) { - status = regcomp(&ignexpr, logdef->ignore, REG_EXTENDED|REG_ICASE|REG_NOSUB); - if (status != 0) logdef->ignore = NULL; - } - if (logdef->trigger) { - status = regcomp(&trigexpr, logdef->trigger, REG_EXTENDED|REG_ICASE|REG_NOSUB); - if (status != 0) logdef->trigger = NULL; + if (logdef->ignorecount) { + int i; + ignexpr = (regex_t *) malloc(logdef->ignorecount * sizeof(regex_t)); + for (i=0; (i < logdef->ignorecount); i++) { + status = regcomp(&ignexpr[i], logdef->ignore[i], REG_EXTENDED|REG_ICASE|REG_NOSUB); + if (status != 0) logdef->ignore[i] = NULL; + } + } + if (logdef->triggercount) { + int i; + trigexpr = (regex_t *) malloc(logdef->triggercount * sizeof(regex_t)); + for (i=0; (i < logdef->triggercount); i++) { + status = regcomp(&trigexpr[i], logdef->trigger[i], REG_EXTENDED|REG_ICASE|REG_NOSUB); + if (status != 0) logdef->trigger[i] = NULL; + } } triggerstartpos = triggerendpos = NULL; triggerlinecount = 0; @@ -222,17 +232,27 @@ } /* Check ignore pattern */ - if (logdef->ignore) { - status = regexec(&ignexpr, fillpos, 0, NULL, 0); - if (status == 0) continue; + if (logdef->ignorecount) { + int i, match = 0; + + for (i=0; ((i < logdef->ignorecount) && !match); i++) { + match = (regexec(&ignexpr[i], fillpos, 0, NULL, 0) == 0); + } + + if (match) continue; } linepos[lpidx] = fillpos; /* See if this is a trigger line */ - if (logdef->trigger) { - status = regexec(&trigexpr, fillpos, 0, NULL, 0); - if (status == 0) { + if (logdef->triggercount) { + int i, match = 0; + + for (i=0; ((i < logdef->ignorecount) && !match); i++) { + match = (regexec(&trigexpr[i], fillpos, 0, NULL, 0) == 0); + } + + if (match) { int sidx; sidx = lpidx - LINES_AROUND_TRIGGER; @@ -270,6 +290,7 @@ if (bytesread > logdef->maxbytes) { char *skiptxt = "<...SKIPPED...>\n"; + /* FIXME: Must make sure to only pass complete lines back to the server */ if (triggerstartpos) { /* Skip the beginning of the data up until the trigger was found */ startpos = triggerstartpos; @@ -322,8 +343,24 @@ cleanup: if (fd) fclose(fd); - if (logdef->ignore) regfree(&ignexpr); - if (logdef->trigger) regfree(&trigexpr); + + { + int i; + + if (logdef->ignorecount) { + for (i=0; (i < logdef->ignorecount); i++) { + if (logdef->ignore[i]) regfree(&ignexpr[i]); + } + xfree(ignexpr); + } + + if (logdef->triggercount) { + for (i=0; (i < logdef->triggercount); i++) { + if (logdef->trigger[i]) regfree(&trigexpr[i]); + } + xfree(trigexpr); + } + } return startpos; } @@ -707,12 +744,33 @@ checkdef_t *walk = currcfg; do { - walk->check.logcheck.ignore = strdup(p); + walk->check.logcheck.ignorecount++; + if (walk->check.logcheck.ignore == NULL) { + walk->check.logcheck.ignore = (char **)malloc(sizeof(char *)); + } + else { + walk->check.logcheck.ignore = + (char **)realloc(walk->check.logcheck.ignore, + walk->check.logcheck.ignorecount * sizeof(char **)); + } + + walk->check.logcheck.ignore[walk->check.logcheck.ignorecount-1] = strdup(p); + walk = walk->next; } while (walk && (walk != firstpipeitem->next)); } else { - currcfg->check.logcheck.ignore = strdup(p); + currcfg->check.logcheck.ignorecount++; + if (currcfg->check.logcheck.ignore == NULL) { + currcfg->check.logcheck.ignore = (char **)malloc(sizeof(char *)); + } + else { + currcfg->check.logcheck.ignore = + (char **)realloc(currcfg->check.logcheck.ignore, + currcfg->check.logcheck.ignorecount * sizeof(char **)); + } + + currcfg->check.logcheck.ignore[currcfg->check.logcheck.ignorecount-1] = strdup(p); } } else if (strncmp(bol, "trigger ", 8) == 0) { @@ -725,12 +783,33 @@ checkdef_t *walk = currcfg; do { - walk->check.logcheck.trigger = strdup(p); + walk->check.logcheck.triggercount++; + if (walk->check.logcheck.trigger == NULL) { + walk->check.logcheck.trigger = (char **)malloc(sizeof(char *)); + } + else { + walk->check.logcheck.trigger = + (char **)realloc(walk->check.logcheck.trigger, + walk->check.logcheck.triggercount * sizeof(char **)); + } + + walk->check.logcheck.trigger[walk->check.logcheck.triggercount-1] = strdup(p); + walk = walk->next; } while (walk && (walk != firstpipeitem->next)); } else { - currcfg->check.logcheck.trigger = strdup(p); + currcfg->check.logcheck.triggercount++; + if (currcfg->check.logcheck.trigger == NULL) { + currcfg->check.logcheck.trigger = (char **)malloc(sizeof(char *)); + } + else { + currcfg->check.logcheck.trigger = + (char **)realloc(currcfg->check.logcheck.trigger, + currcfg->check.logcheck.triggercount * sizeof(char **)); + } + + currcfg->check.logcheck.trigger[currcfg->check.logcheck.triggercount-1] = strdup(p); } } } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/client/msgcache.c ./client/msgcache.c --- /home/henrik/hobbit/release/hobbit-4.2.0/client/msgcache.c 2006-08-09 22:09:58.000000000 +0200 +++ ./client/msgcache.c 2006-10-03 12:55:27.000000000 +0200 @@ -440,7 +440,9 @@ freestrbuffer(zombie->msgbuf); xfree(zombie); } - if (!chead) ctail = NULL; + ctail = chead; + if (ctail) { while (ctail->next) ctail = ctail->next; } + /* Remove expired messages */ qwalk = qhead; qprev = NULL; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/client/runclient.sh ./client/runclient.sh --- /home/henrik/hobbit/release/hobbit-4.2.0/client/runclient.sh 2006-08-09 22:09:58.000000000 +0200 +++ ./client/runclient.sh 2006-10-03 12:55:27.000000000 +0200 @@ -76,7 +76,7 @@ if test -s $HOBBITCLIENTHOME/logs/clientlaunch.$MACHINEDOTS.pid; then echo "Hobbit client already running, re-starting it" - $0 stop + $0 --hostname="$MACHINEDOTS" stop rm -f $HOBBITCLIENTHOME/logs/clientlaunch.$MACHINEDOTS.pid fi @@ -99,12 +99,12 @@ "restart") if test -s $HOBBITCLIENTHOME/logs/clientlaunch.$MACHINEDOTS.pid; then - $0 stop + $0 --hostname="$MACHINEDOTS" stop else echo "Hobbit client not running, continuing to start it" fi - $0 start + $0 --hostname="$MACHINEDOTS" --os="$BBOSTYPE" start ;; "status") diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/common/bb-hosts.5 ./common/bb-hosts.5 --- /home/henrik/hobbit/release/hobbit-4.2.0/common/bb-hosts.5 2006-08-09 22:09:59.000000000 +0200 +++ ./common/bb-hosts.5 2006-10-03 12:55:27.000000000 +0200 @@ -494,7 +494,7 @@ tool; the values specified in the "ssldays" tag overrides the default. -.IP DOWNTIME=day:starttime:endtime:cause[,day:starttime:endtime:cause] +.IP DOWNTIME=[columns:]day:starttime:endtime:cause[,day:starttime:endtime:cause] This tag can be used to ignore failed checks during specific times of the day - e.g. if you run services that are only monitored e.g. Mon-Fri 8am-5pm, or you always @@ -506,6 +506,10 @@ will not be triggered and the downtime is not counted in the availability calculations generated by the Hobbit reports. +The "columns" setting is optional - it may be a comma-separated +list of status columns in which case the DOWNTIME setting only applies +to these columns. + The "cause" string (optional) is a text that will be displayed on the status web page to explain thy the system is down. diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/configure.server ./configure.server --- /home/henrik/hobbit/release/hobbit-4.2.0/configure.server 2006-08-09 22:10:23.000000000 +0200 +++ ./configure.server 2006-10-03 13:52:28.000000000 +0200 @@ -316,13 +316,16 @@ echo "top-level directory. I can set this up if you tell me" echo "what group-ID your webserver runs with. This is typically" echo "'nobody' or 'apache' or 'www-data'" -echo "If you dont know, just hit ENTER and we will handle it later." echo "" -echo "What group-ID does your webserver use ?" +echo "What group-ID does your webserver use [nobody] ?" if test -z "$HTTPDGID" then read HTTPDGID fi +if test -z "$HTTPDGID" +then + HTTPDGID="nobody" +fi echo ""; echo "" echo "Where to put the Hobbit logfiles [/var/log/hobbit] ? " diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/Makefile ./hobbitd/Makefile --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/Makefile 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/Makefile 2006-10-03 13:52:28.000000000 +0200 @@ -1,4 +1,4 @@ -PROGRAMS = hobbit.sh hobbitd hobbitd_channel hobbitd_filestore hobbitd_history hobbitd_alert hobbitd_rrd hobbitd_sample hobbitd_client hobbitd_hostdata hobbitd_capture hobbitfetch hobbit-mailack trimhistory bbcombotest hobbitreports.sh moverrd.sh +PROGRAMS = hobbit.sh hobbitd hobbitd_channel hobbitd_filestore hobbitd_history hobbitd_alert hobbitd_rrd hobbitd_sample hobbitd_client hobbitd_hostdata hobbitd_capture hobbitfetch hobbit-mailack trimhistory bbcombotest hobbitreports.sh moverrd.sh convertnk CLIENTPROGRAMS = ../client/hobbitd_client LIBOBJS = ../lib/libbbgen.a @@ -17,7 +17,7 @@ MAILACKOBJS = hobbit-mailack.o TRIMHISTOBJS = trimhistory.o FETCHOBJS = hobbitfetch.o - +CONVERTNKOBJS = convertnk.o IDTOOL := $(shell if test `uname -s` = "SunOS"; then echo /usr/xpg4/bin/id; else echo id; fi) @@ -91,6 +91,9 @@ hobbitfetch: $(FETCHOBJS) $(LIBOBJS) $(CC) $(CFLAGS) -o $@ $(RPATHOPT) $(FETCHOBJS) $(LIBOBJS) $(NETLIBS) +convertnk: $(CONVERTNKOBJS) $(LIBOBJS) + $(CC) $(CFLAGS) -o $@ $(RPATHOPT) $(CONVERTNKOBJS) $(LIBOBJS) $(NETLIBS) + hobbit.sh: hobbit.sh.DIST cat $< | sed -e 's!@BBHOME@!$(BBHOME)!g' | sed -e 's!@BBLOGDIR@!$(BBLOGDIR)!g' | sed -e 's!@BBUSER@!$(BBUSER)!g' | sed -e 's!@RUNTIMEDEFS@!$(RUNTIMEDEFS)!g' >$@ chmod 755 $@ diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/client_config.c ./hobbitd/client_config.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/client_config.c 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/client_config.c 2006-10-03 12:55:27.000000000 +0200 @@ -1249,6 +1249,7 @@ *abswarn = 0; *abspanic = 0; *ignored = 0; + *group = NULL; rule = getrule(hostname, pagename, classname, C_DISK); while (rule && !namematch(fsname, rule->rule.disk.fsexp->pattern, rule->rule.disk.fsexp->exp)) { diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/convertnk.c ./hobbitd/convertnk.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/convertnk.c 1970-01-01 01:00:00.000000000 +0100 +++ ./hobbitd/convertnk.c 2006-10-03 13:52:28.000000000 +0200 @@ -0,0 +1,54 @@ +/*----------------------------------------------------------------------------*/ +/* Hobbit utility to convert the deprecated NK tags to a hobbit-nkview.cfg */ +/* */ +/* Copyright (C) 2006 Henrik Storner <henrik@hswn.dk> */ +/* */ +/* This program is released under the GNU General Public License (GPL), */ +/* version 2. See the file "COPYING" for details. */ +/* */ +/*----------------------------------------------------------------------------*/ + +static char rcsid[] = "$Id: convertnk.c,v 1.1 2006/09/12 21:27:11 henrik Exp $"; + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#include "libbbgen.h" + +int main(int argc, char *argv[]) +{ + namelist_t *walk; + + load_hostnames(xgetenv("BBHOSTS"), NULL, get_fqdn()); + + for (walk = first_host(); (walk); walk=walk->next) { + char *nk, *nktime, *tok; + + nk = bbh_item(walk, BBH_NK); if (!nk) continue; + nktime = bbh_item(walk, BBH_NKTIME); + + nk = strdup(nk); + + tok = strtok(nk, ","); + while (tok) { + char *hostname = bbh_item(walk, BBH_HOSTNAME); + char *startstr = "", *endstr = "", *ttgroup = "", *ttextra = "", *updinfo = "Migrated"; + int priority = 2; + + fprintf(stdout, "%s|%s|%s|%s|%s|%d|%s|%s|%s\n", + hostname, tok, + startstr, endstr, + (nktime ? nktime : ""), + priority, ttgroup, ttextra, updinfo); + + tok = strtok(NULL, ","); + } + + xfree(nk); + } + + return 0; +} + diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/do_alert.c ./hobbitd/do_alert.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/do_alert.c 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/do_alert.c 2006-10-03 12:55:27.000000000 +0200 @@ -168,7 +168,7 @@ static char *message_text(activealerts_t *alert, recip_t *recip) { - strbuffer_t *buf = NULL; + static strbuffer_t *buf = NULL; char *eoln, *bom, *p; char info[4096]; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd.c ./hobbitd/hobbitd.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd.c 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/hobbitd.c 2007-02-09 11:28:42.039878822 +0100 @@ -124,7 +124,7 @@ typedef struct hobbitd_hostlist_t { char *hostname; char ip[IP_ADDR_STRLEN]; - enum { H_NORMAL, H_SUMMARY, H_DIALUP } hosttype; + enum { H_NORMAL, H_SUMMARY } hosttype; hobbitd_log_t *logs; hobbitd_log_t *pinglog; /* Points to entry in logs list, but we need it often */ time_t clientmsgtstamp; @@ -187,6 +187,7 @@ hobbitd_channel_t *enadischn = NULL; /* Receives "enable" and "disable" messages */ hobbitd_channel_t *clientchn = NULL; /* Receives "client" messages */ hobbitd_channel_t *clichgchn = NULL; /* Receives "clichg" messages */ +hobbitd_channel_t *userchn = NULL; /* Receives "usermsg" messages */ #define NO_COLOR (COL_COUNT) static char *colnames[COL_COUNT+1]; @@ -431,7 +432,6 @@ hitem->hostname = strdup(hostname); strcpy(hitem->ip, ip); if (strcmp(hostname, "summary") == 0) hitem->hosttype = H_SUMMARY; - else if (strcmp(hostname, "dialup") == 0) hitem->hosttype = H_DIALUP; else hitem->hosttype = H_NORMAL; rbtInsert(rbhosts, hitem->hostname, hitem); @@ -635,13 +635,14 @@ break; case C_NOTES: + case C_USER: n = snprintf(channel->channelbuf, (bufsz-5), "@@%s#%u|%d.%06d|%s|%s\n%s", channelmarker, channel->seq, (int) tstamp.tv_sec, (int) tstamp.tv_usec, sender, hostname, msg); if (n > (bufsz-5)) { - errprintf("Oversize notes msg from %s for %s:%s truncated (n=%d, limit=%d)\n", - sender, hostname, log->test->name, n, bufsz); + errprintf("Oversize notes/user msg from %s for %s truncated (n=%d, limit=%d)\n", + sender, hostname, n, bufsz); } *(channel->channelbuf + bufsz - 5) = '\0'; break; @@ -1292,6 +1293,13 @@ dbgprintf("<-handle_notes\n"); } +void handle_usermsg(char *msg, char *sender, char *hostname) +{ + dbgprintf("->handle_usermsg\n"); + posttochannel(userchn, channelnames[C_USER], msg, sender, hostname, NULL, NULL); + dbgprintf("<-handle_usermsg\n"); +} + void handle_enadis(int enabled, conn_t *msg, char *sender) { char *firstline = NULL, *hosttest = NULL, *durstr = NULL, *txtstart = NULL; @@ -2513,18 +2521,18 @@ handle_status(msg->buf, sender, h->hostname, t->name, NULL, log, color, NULL); } } - else if (strncmp(msg->buf, "notes", 5) == 0) { + else if ((strncmp(msg->buf, "notes", 5) == 0) || (strncmp(msg->buf, "usermsg", 7) == 0)) { char *hostname, *bhost, *ehost, *p; char savechar; - bhost = msg->buf + strlen("notes"); bhost += strspn(bhost, " \t"); + bhost = msg->buf + strcspn(msg->buf, " \t\r\n"); bhost += strspn(bhost, " \t"); ehost = bhost + strcspn(bhost, " \t\r\n"); savechar = *ehost; *ehost = '\0'; hostname = strdup(bhost); *ehost = savechar; p = hostname; while ((p = strchr(p, ',')) != NULL) *p = '.'; - if (*hostname == '\0') { errprintf("Invalid notes message from %s - blank hostname\n", sender); xfree(hostname); hostname = NULL; } + if (*hostname == '\0') { errprintf("Invalid notes/user message from %s - blank hostname\n", sender); xfree(hostname); hostname = NULL; } if (hostname) { char *hname, hostip[IP_ADDR_STRLEN]; @@ -2535,12 +2543,27 @@ if (hname == NULL) { log_ghost(hostname, sender, msg->buf); } - else if (!oksender(maintsenders, NULL, msg->addr.sin_addr, msg->buf)) { - /* Invalid sender */ - errprintf("Invalid notes message - sender %s not allowed for host %s\n", sender, hostname); - } else { - handle_notes(msg->buf, sender, hostname); + if (*msg->buf == 'n') { + /* "notes" message */ + if (!oksender(maintsenders, NULL, msg->addr.sin_addr, msg->buf)) { + /* Invalid sender */ + errprintf("Invalid notes message - sender %s not allowed for host %s\n", sender, hostname); + } + else { + handle_notes(msg->buf, sender, hostname); + } + } + else if (*msg->buf == 'u') { + /* "usermsg" message */ + if (!oksender(statussenders, NULL, msg->addr.sin_addr, msg->buf)) { + /* Invalid sender */ + errprintf("Invalid user message - sender %s not allowed for host %s\n", sender, hostname); + } + else { + handle_usermsg(msg->buf, sender, hostname); + } + } } xfree(hostname); @@ -2787,7 +2810,7 @@ continue; } - /* If there is a hostname filter, drop the "summary" and "dialup 'hosts' */ + /* If there is a hostname filter, drop the "summary" 'hosts' */ if (shost && (hwalk->hosttype != H_NORMAL)) continue; firstlog = hwalk->logs; @@ -2953,7 +2976,7 @@ if (!oksender(wwwsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; setup_filter(msg->buf, - "BBH_HOSTNAME,BBH_IP,BBH_BANKSIZE,BBH_RAW", + "BBH_HOSTNAME,BBH_IP,BBH_RAW", &spage, &shost, &snet, &stest, &scolor, &acklevel, &fields, &chspage, &chshost, &chsnet, &chstest); @@ -4159,6 +4182,8 @@ if (clientchn == NULL) { errprintf("Cannot setup client channel\n"); return 1; } clichgchn = setup_channel(C_CLICHG, CHAN_MASTER); if (clichgchn == NULL) { errprintf("Cannot setup clichg channel\n"); return 1; } + userchn = setup_channel(C_USER, CHAN_MASTER); + if (userchn == NULL) { errprintf("Cannot setup user channel\n"); return 1; } errprintf("Setting up logfiles\n"); setvbuf(stdout, NULL, _IONBF, 0); diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd.c~ ./hobbitd/hobbitd.c~ --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd.c~ 1970-01-01 01:00:00.000000000 +0100 +++ ./hobbitd/hobbitd.c~ 2007-02-09 11:27:54.415539157 +0100 @@ -0,0 +1,4580 @@ +/*----------------------------------------------------------------------------*/ +/* Hobbit message daemon. */ +/* */ +/* This is the master daemon, hobbitd. */ +/* */ +/* This is a daemon that implements the Big Brother network protocol, with */ +/* additional protocol items implemented for Hobbit. */ +/* */ +/* This daemon maintains the full state of the Hobbit system in memory, */ +/* eliminating the need for file-based storage of e.g. status logs. The web */ +/* frontend programs (bbgen, bbcombotest, bb-hostsvc.cgi etc) can retrieve */ +/* current statuslogs from this daemon to build the Hobbit webpages. However, */ +/* a "plugin" mechanism is also implemented to allow "worker modules" to */ +/* pickup various types of events that occur in the system. This allows */ +/* such modules to e.g. maintain the standard Hobbit file-based storage, or */ +/* implement history logging or RRD database updates. This plugin mechanism */ +/* uses System V IPC mechanisms for a high-performance/low-latency communi- */ +/* cation between hobbitd and the worker modules - under no circumstances */ +/* should the daemon be tasked with storing data to a low-bandwidth channel. */ +/* */ +/* Copyright (C) 2004-2006 Henrik Storner <henrik@hswn.dk> */ +/* */ +/* This program is released under the GNU General Public License (GPL), */ +/* version 2. See the file "COPYING" for details. */ +/* */ +/*----------------------------------------------------------------------------*/ + +static char rcsid[] = "$Id: hobbitd.c,v 1.253 2006/08/03 18:59:02 henrik Rel $"; + +#include <limits.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> /* Someday I'll move to GNU Autoconf for this ... */ +#endif +#include <errno.h> +#include <sys/resource.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <netdb.h> +#include <ctype.h> +#include <signal.h> +#include <time.h> + +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/shm.h> +#include <sys/wait.h> + +#include "libbbgen.h" + +#include "hobbitd_buffer.h" +#include "hobbitd_ipc.h" + +#define DISABLED_UNTIL_OK -1 + +/* + * The absolute maximum size we'll grow our buffers to accomodate an incoming message. + * This is really just an upper bound to squash the bad guys trying to data-flood us. + */ +#define MAX_HOBBIT_INBUFSZ (10*1024*1024) /* 10 MB */ + +/* The initial size of an input buffer. Make this large enough for most traffic. */ +#define HOBBIT_INBUF_INITIAL (128*1024) + +/* How much the input buffer grows per re-allocation */ +#define HOBBIT_INBUF_INCREMENT (32*1024) + +/* How long to keep an ack after the status has recovered */ +#define ACKCLEARDELAY 720 /* 12 minutes */ + +htnames_t *metanames = NULL; +typedef struct hobbitd_meta_t { + htnames_t *metaname; + char *value; + struct hobbitd_meta_t *next; +} hobbitd_meta_t; + +typedef struct ackinfo_t { + int level; + time_t received, validuntil, cleartime; + char *ackedby; + char *msg; + struct ackinfo_t *next; +} ackinfo_t; + +typedef struct testinfo_t { + char *name; + int clientsave; +} testinfo_t; + +/* This holds all information about a single status */ +typedef struct hobbitd_log_t { + struct hobbitd_hostlist_t *host; + testinfo_t *test; + char *origin; + int color, oldcolor, activealert, histsynced, downtimeactive; + char *testflags; + char *grouplist; /* For extended status reports (e.g. from hobbitd_client) */ + char sender[IP_ADDR_STRLEN]; + time_t lastchange; /* time when the currently logged status began */ + time_t logtime; /* time when last update was received */ + time_t validtime; /* time when status is no longer valid */ + time_t enabletime; /* time when test auto-enables after a disable */ + time_t acktime; /* time when test acknowledgement expires */ + unsigned char *message; + int msgsz; + unsigned char *dismsg, *ackmsg; + int cookie; + time_t cookieexpires; + struct hobbitd_meta_t *metas; + ackinfo_t *acklist; /* Holds list of acks */ + struct hobbitd_log_t *next; +} hobbitd_log_t; + +/* This is a list of the hosts we have seen reports for, and links to their status logs */ +typedef struct hobbitd_hostlist_t { + char *hostname; + char ip[IP_ADDR_STRLEN]; + enum { H_NORMAL, H_SUMMARY } hosttype; + hobbitd_log_t *logs; + hobbitd_log_t *pinglog; /* Points to entry in logs list, but we need it often */ + time_t clientmsgtstamp; + char *clientmsg; + int clientmsgposted; +} hobbitd_hostlist_t; + +typedef struct filecache_t { + char *fn; + long len; + unsigned char *fdata; +} filecache_t; + +RbtHandle rbhosts; /* The hosts we have reports from */ +RbtHandle rbtests; /* The tests (columns) we have seen */ +RbtHandle rborigins; /* The origins we have seen */ +RbtHandle rbcookies; /* The cookies we use */ +RbtHandle rbfilecache; + +sender_t *maintsenders = NULL; +sender_t *statussenders = NULL; +sender_t *adminsenders = NULL; +sender_t *wwwsenders = NULL; +sender_t *tracelist = NULL; +int traceall = 0; +int ignoretraced = 0; +int clientsavemem = 1; /* In memory */ +int clientsavedisk = 0; /* On disk via the CLICHG channel */ +int allow_downloads = 1; + +#define NOTALK 0 +#define RECEIVING 1 +#define RESPONDING 2 + +/* This struct describes an active connection with a Hobbit client */ +typedef struct conn_t { + int sock; /* Communications socket */ + struct sockaddr_in addr; /* Client source address */ + unsigned char *buf, *bufp; /* Message buffer and pointer */ + size_t buflen, bufsz; /* Active and maximum length of buffer */ + int doingwhat; /* Communications state (NOTALK, READING, RESPONDING) */ + time_t timeout; /* When the timeout for this connection happens */ + struct conn_t *next; +} conn_t; + +enum droprencmd_t { CMD_DROPHOST, CMD_DROPTEST, CMD_RENAMEHOST, CMD_RENAMETEST, CMD_DROPSTATE }; + +static volatile int running = 1; +static volatile int reloadconfig = 0; +static volatile time_t nextcheckpoint = 0; +static volatile int dologswitch = 0; +static volatile int gotalarm = 0; + +/* Our channels to worker modules */ +hobbitd_channel_t *statuschn = NULL; /* Receives full "status" messages */ +hobbitd_channel_t *stachgchn = NULL; /* Receives brief message about a status change */ +hobbitd_channel_t *pagechn = NULL; /* Receives alert messages (triggered from status changes) */ +hobbitd_channel_t *datachn = NULL; /* Receives raw "data" messages */ +hobbitd_channel_t *noteschn = NULL; /* Receives raw "notes" messages */ +hobbitd_channel_t *enadischn = NULL; /* Receives "enable" and "disable" messages */ +hobbitd_channel_t *clientchn = NULL; /* Receives "client" messages */ +hobbitd_channel_t *clichgchn = NULL; /* Receives "clichg" messages */ +hobbitd_channel_t *userchn = NULL; /* Receives "usermsg" messages */ + +#define NO_COLOR (COL_COUNT) +static char *colnames[COL_COUNT+1]; +int alertcolors, okcolors; +enum alertstate_t { A_OK, A_ALERT, A_UNDECIDED }; + +typedef struct ghostlist_t { + char *name; + char *sender; + time_t tstamp; +} ghostlist_t; +RbtHandle rbghosts; + +int ghosthandling = -1; + +char *checkpointfn = NULL; +FILE *dbgfd = NULL; +char *dbghost = NULL; +time_t boottime; +int hostcount = 0; +char *ackinfologfn = NULL; +FILE *ackinfologfd = NULL; + +typedef struct hobbitd_statistics_t { + char *cmd; + unsigned long count; +} hobbitd_statistics_t; + +hobbitd_statistics_t hobbitd_stats[] = { + { "status", 0 }, + { "combo", 0 }, + { "page", 0 }, + { "summary", 0 }, + { "data", 0 }, + { "client", 0 }, + { "notes", 0 }, + { "enable", 0 }, + { "disable", 0 }, + { "ack", 0 }, + { "config", 0 }, + { "query", 0 }, + { "hobbitdboard", 0 }, + { "hobbitdlog", 0 }, + { "drop", 0 }, + { "rename", 0 }, + { "dummy", 0 }, + { "ping", 0 }, + { "notify", 0 }, + { "schedule", 0 }, + { "download", 0 }, + { NULL, 0 } +}; + +enum boardfield_t { F_NONE, F_HOSTNAME, F_TESTNAME, F_COLOR, F_FLAGS, + F_LASTCHANGE, F_LOGTIME, F_VALIDTIME, F_ACKTIME, F_DISABLETIME, + F_SENDER, F_COOKIE, F_LINE1, + F_ACKMSG, F_DISMSG, F_MSG, F_CLIENT, F_CLIENTTSTAMP, + F_ACKLIST, + F_HOSTINFO, + F_LAST }; + +typedef struct boardfieldnames_t { + char *name; + enum boardfield_t id; +} boardfieldnames_t; +boardfieldnames_t boardfieldnames[] = { + { "hostname", F_HOSTNAME }, + { "testname", F_TESTNAME }, + { "color", F_COLOR }, + { "flags", F_FLAGS }, + { "lastchange", F_LASTCHANGE }, + { "logtime", F_LOGTIME }, + { "validtime", F_VALIDTIME }, + { "acktime", F_ACKTIME }, + { "disabletime", F_DISABLETIME }, + { "sender", F_SENDER }, + { "cookie", F_COOKIE }, + { "line1", F_LINE1 }, + { "ackmsg", F_ACKMSG }, + { "dismsg", F_DISMSG }, + { "msg", F_MSG }, + { "client", F_CLIENT }, + { "clntstamp", F_CLIENTTSTAMP }, + { "acklist", F_ACKLIST }, + { "BBH_", F_HOSTINFO }, + { NULL, F_LAST }, +}; +typedef struct boardfields_t { + enum boardfield_t field; + enum bbh_item_t bbhfield; +} boardfield_t; +#define BOARDFIELDS_MAX 50 +boardfield_t boardfields[BOARDFIELDS_MAX+1]; + +unsigned long msgs_total = 0; +unsigned long msgs_total_last = 0; +time_t last_stats_time = 0; + +/* List of scheduled (future) tasks */ +typedef struct scheduletask_t { + int id; + time_t executiontime; + char *command; + char *sender; + struct scheduletask_t *next; +} scheduletask_t; +scheduletask_t *schedulehead = NULL; +int nextschedid = 1; + +void update_statistics(char *cmd) +{ + int i; + + dbgprintf("-> update_statistics\n"); + + if (!cmd) { + dbgprintf("No command for update_statistics\n"); + return; + } + + msgs_total++; + + i = 0; + while (hobbitd_stats[i].cmd && strncmp(hobbitd_stats[i].cmd, cmd, strlen(hobbitd_stats[i].cmd))) { i++; } + hobbitd_stats[i].count++; + + dbgprintf("<- update_statistics\n"); +} + +char *generate_stats(void) +{ + static char *statsbuf = NULL; + static int statsbuflen = 0; + time_t now = time(NULL); + char *bufp; + int i, clients; + char bootuptxt[40]; + char uptimetxt[40]; + RbtHandle ghandle; + time_t uptime = (now - boottime); + + dbgprintf("-> generate_stats\n"); + + MEMDEFINE(bootuptxt); + MEMDEFINE(uptimetxt); + + if (statsbuf == NULL) { + statsbuflen = 8192; + statsbuf = (char *)malloc(statsbuflen); + } + bufp = statsbuf; + + strftime(bootuptxt, sizeof(bootuptxt), "%d-%b-%Y %T", localtime(&boottime)); + sprintf(uptimetxt, "%d days, %02d:%02d:%02d", + (int)(uptime / 86400), (int)(uptime % 86400)/3600, (int)(uptime % 3600)/60, (int)(uptime % 60)); + + bufp += sprintf(bufp, "status %s.hobbitd %s\nStatistics for Hobbit daemon\nUp since %s (%s)\n\n", + xgetenv("MACHINE"), colorname(errbuf ? COL_YELLOW : COL_GREEN), bootuptxt, uptimetxt); + bufp += sprintf(bufp, "Incoming messages : %10ld\n", msgs_total); + i = 0; + while (hobbitd_stats[i].cmd) { + bufp += sprintf(bufp, "- %-20s : %10ld\n", hobbitd_stats[i].cmd, hobbitd_stats[i].count); + i++; + } + bufp += sprintf(bufp, "- %-20s : %10ld\n", "Bogus/Timeouts ", hobbitd_stats[i].count); + + if ((now > last_stats_time) && (last_stats_time > 0)) { + bufp += sprintf(bufp, "Incoming messages/sec : %10ld (average last %d seconds)\n", + ((msgs_total - msgs_total_last) / (now - last_stats_time)), + (int)(now - last_stats_time)); + } + msgs_total_last = msgs_total; + + bufp += sprintf(bufp, "\n"); + clients = semctl(statuschn->semid, CLIENTCOUNT, GETVAL); + bufp += sprintf(bufp, "status channel messages: %10ld (%d readers)\n", statuschn->msgcount, clients); + clients = semctl(stachgchn->semid, CLIENTCOUNT, GETVAL); + bufp += sprintf(bufp, "stachg channel messages: %10ld (%d readers)\n", stachgchn->msgcount, clients); + clients = semctl(pagechn->semid, CLIENTCOUNT, GETVAL); + bufp += sprintf(bufp, "page channel messages: %10ld (%d readers)\n", pagechn->msgcount, clients); + clients = semctl(datachn->semid, CLIENTCOUNT, GETVAL); + bufp += sprintf(bufp, "data channel messages: %10ld (%d readers)\n", datachn->msgcount, clients); + clients = semctl(noteschn->semid, CLIENTCOUNT, GETVAL); + bufp += sprintf(bufp, "notes channel messages: %10ld (%d readers)\n", noteschn->msgcount, clients); + clients = semctl(enadischn->semid, CLIENTCOUNT, GETVAL); + bufp += sprintf(bufp, "enadis channel messages: %10ld (%d readers)\n", enadischn->msgcount, clients); + clients = semctl(clientchn->semid, CLIENTCOUNT, GETVAL); + bufp += sprintf(bufp, "client channel messages: %10ld (%d readers)\n", clientchn->msgcount, clients); + clients = semctl(clichgchn->semid, CLIENTCOUNT, GETVAL); + bufp += sprintf(bufp, "clichg channel messages: %10ld (%d readers)\n", clichgchn->msgcount, clients); + + ghandle = rbtBegin(rbghosts); + if (ghandle != rbtEnd(rbghosts)) bufp += sprintf(bufp, "\n\nGhost reports:\n"); + for (; (ghandle != rbtEnd(rbghosts)); ghandle = rbtNext(rbghosts, ghandle)) { + ghostlist_t *gwalk = (ghostlist_t *)gettreeitem(rbghosts, ghandle); + + /* Skip records older than 10 minutes */ + if (gwalk->tstamp < (now - 600)) continue; + + if ((statsbuflen - (bufp - statsbuf)) < 512) { + /* Less than 512 bytes left in buffer - expand it */ + statsbuflen += 4096; + statsbuf = (char *)realloc(statsbuf, statsbuflen); + bufp = statsbuf + strlen(statsbuf); + } + + bufp += sprintf(bufp, " %-15s reported host %s\n", gwalk->sender, gwalk->name); + } + + if (errbuf) { + if ((strlen(statsbuf) + strlen(errbuf) + 1024) > statsbuflen) { + statsbuflen = strlen(statsbuf) + strlen(errbuf) + 1024; + statsbuf = (char *)realloc(statsbuf, statsbuflen); + bufp = statsbuf + strlen(statsbuf); + } + + bufp += sprintf(bufp, "\n\nLatest errormessages:\n%s\n", errbuf); + } + + MEMUNDEFINE(bootuptxt); + MEMUNDEFINE(uptimetxt); + + dbgprintf("<- generate_stats\n"); + + return statsbuf; +} + + +enum alertstate_t decide_alertstate(int color) +{ + if ((okcolors & (1 << color)) != 0) return A_OK; + else if ((alertcolors & (1 << color)) != 0) return A_ALERT; + else return A_UNDECIDED; +} + + +hobbitd_hostlist_t *create_hostlist_t(char *hostname, char *ip) +{ + hobbitd_hostlist_t *hitem; + + hitem = (hobbitd_hostlist_t *) calloc(1, sizeof(hobbitd_hostlist_t)); + hitem->hostname = strdup(hostname); + strcpy(hitem->ip, ip); + if (strcmp(hostname, "summary") == 0) hitem->hosttype = H_SUMMARY; + else hitem->hosttype = H_NORMAL; + rbtInsert(rbhosts, hitem->hostname, hitem); + + return hitem; +} + +testinfo_t *create_testinfo(char *name) +{ + testinfo_t *newrec; + + newrec = (testinfo_t *)calloc(1, sizeof(testinfo_t)); + newrec->name = strdup(name); + newrec->clientsave = clientsavedisk; + rbtInsert(rbtests, newrec->name, newrec); + + return newrec; +} + +void posttochannel(hobbitd_channel_t *channel, char *channelmarker, + char *msg, char *sender, char *hostname, hobbitd_log_t *log, char *readymsg) +{ + struct sembuf s; + int clients; + int n; + struct timeval tstamp; + struct timezone tz; + int semerr = 0; + unsigned int bufsz = 1024*shbufsz(channel->channelid); + + dbgprintf("-> posttochannel\n"); + + /* First see how many users are on this channel */ + clients = semctl(channel->semid, CLIENTCOUNT, GETVAL); + if (clients == 0) { + dbgprintf("Dropping message - no readers\n"); + return; + } + + /* + * Wait for BOARDBUSY to go low. + * We need a loop here, because if we catch a signal + * while waiting on the semaphore, then we need to + * re-start the semaphore wait. Otherwise we may + * end up with semaphores that are out of sync + * (GOCLIENT goes up while a worker waits for it + * to go to 0). + */ + gotalarm = 0; alarm(5); + do { + s.sem_num = BOARDBUSY; s.sem_op = 0; s.sem_flg = 0; + n = semop(channel->semid, &s, 1); + if (n == -1) { + semerr = errno; + if (semerr != EINTR) errprintf("semop failed, %s\n", strerror(errno)); + } + } while ((n == -1) && (semerr == EINTR) && running && !gotalarm); + alarm(0); + if (!running) return; + + /* Check if the alarm fired */ + if (gotalarm) { + errprintf("BOARDBUSY locked at %d, GETNCNT is %d, GETPID is %d, %d clients\n", + semctl(channel->semid, BOARDBUSY, GETVAL), + semctl(channel->semid, BOARDBUSY, GETNCNT), + semctl(channel->semid, BOARDBUSY, GETPID), + semctl(channel->semid, CLIENTCOUNT, GETVAL)); + return; + } + + /* All clear, post the message */ + if (channel->seq == 999999) channel->seq = 0; + channel->seq++; + channel->msgcount++; + gettimeofday(&tstamp, &tz); + if (readymsg) { + n = snprintf(channel->channelbuf, (bufsz-5), + "@@%s#%u|%d.%06d|%s|%s", + channelmarker, channel->seq, (int) tstamp.tv_sec, (int) tstamp.tv_usec, sender, + readymsg); + if (n > (bufsz-5)) { + char *p, *overmsg = readymsg; + *(overmsg+100) = '\0'; + p = strchr(overmsg, '\n'); if (p) *p = '\0'; + errprintf("Oversize data/client msg from %s truncated (n=%d, limit %d)\nFirst line: %s\n", + sender, n, bufsz, overmsg); + } + *(channel->channelbuf + bufsz - 5) = '\0'; + } + else { + switch(channel->channelid) { + case C_STATUS: + n = snprintf(channel->channelbuf, (bufsz-5), + "@@%s#%u|%d.%06d|%s|%s|%s|%s|%d|%s|%s|%s|%d", + channelmarker, channel->seq, (int) tstamp.tv_sec, (int) tstamp.tv_usec, + sender, log->origin, hostname, log->test->name, + (int) log->validtime, colnames[log->color], (log->testflags ? log->testflags : ""), + colnames[log->oldcolor], (int) log->lastchange); + if (n < (bufsz-5)) { + n += snprintf(channel->channelbuf+n, (bufsz-n-5), "|%d|%s", + (int)log->acktime, nlencode(log->ackmsg)); + } + if (n < (bufsz-5)) { + n += snprintf(channel->channelbuf+n, (bufsz-n-5), "|%d|%s", + (int)log->enabletime, nlencode(log->dismsg)); + } + if (n < (bufsz-5)) { + n += snprintf(channel->channelbuf+n, (bufsz-n-5), "|%d", + (int)log->host->clientmsgtstamp); + } + if (n < (bufsz-5)) { + n += snprintf(channel->channelbuf+n, (bufsz-n-5), "\n%s", msg); + } + if (n > (bufsz-5)) { + errprintf("Oversize status msg from %s for %s:%s truncated (n=%d, limit=%d)\n", + sender, hostname, log->test->name, n, bufsz); + } + *(channel->channelbuf + bufsz - 5) = '\0'; + break; + + case C_STACHG: + n = snprintf(channel->channelbuf, (bufsz-5), + "@@%s#%u|%d.%06d|%s|%s|%s|%s|%d|%s|%s|%d", + channelmarker, channel->seq, (int) tstamp.tv_sec, (int) tstamp.tv_usec, + sender, log->origin, hostname, log->test->name, + (int) log->validtime, colnames[log->color], + colnames[log->oldcolor], (int) log->lastchange); + if (n < (bufsz-5)) { + n += snprintf(channel->channelbuf+n, (bufsz-n-5), "|%d|%s", + (int)log->enabletime, nlencode(log->dismsg)); + } + if (n < (bufsz-5)) { + n += snprintf(channel->channelbuf+n, (bufsz-n-5), "|%d", log->downtimeactive); + } + if (n < (bufsz-5)) { + n += snprintf(channel->channelbuf+n, (bufsz-n-5), "|%d", + (int) log->host->clientmsgtstamp); + } + if (n < (bufsz-5)) { + n += snprintf(channel->channelbuf+n, (bufsz-n-5), "\n%s", msg); + } + if (n > (bufsz-5)) { + errprintf("Oversize stachg msg from %s for %s:%s truncated (n=%d, limit=%d)\n", + sender, hostname, log->test->name, n, bufsz); + } + *(channel->channelbuf + bufsz - 5) = '\0'; + break; + + case C_CLICHG: + n = snprintf(channel->channelbuf, (bufsz-5), + "@@%s#%u|%d.%06d|%s|%s|%d\n%s", + channelmarker, channel->seq, (int) tstamp.tv_sec, (int) tstamp.tv_usec, + sender, hostname, (int) log->host->clientmsgtstamp, + log->host->clientmsg); + if (n > (bufsz-5)) { + errprintf("Oversize clichg msg from %s for %s truncated (n=%d, limit=%d)\n", + sender, hostname, n, bufsz); + } + *(channel->channelbuf + bufsz - 5) = '\0'; + log->host->clientmsgposted = 1; + break; + + case C_PAGE: + if (strcmp(channelmarker, "ack") == 0) { + n = snprintf(channel->channelbuf, (bufsz-5), + "@@%s#%u|%d.%06d|%s|%s|%s|%s|%d\n%s", + channelmarker, channel->seq, (int) tstamp.tv_sec, (int) tstamp.tv_usec, + sender, hostname, + log->test->name, log->host->ip, + (int) log->acktime, msg); + } + else { + namelist_t *hi = hostinfo(hostname); + char *pagepath, *classname, *osname; + + pagepath = (hi ? bbh_item(hi, BBH_PAGEPATH) : ""); + classname = (hi ? bbh_item(hi, BBH_CLASS) : ""); + osname = (hi ? bbh_item(hi, BBH_OS) : ""); + if (!classname) classname = ""; + if (!osname) osname = ""; + + n = snprintf(channel->channelbuf, (bufsz-5), + "@@%s#%u|%d.%06d|%s|%s|%s|%s|%d|%s|%s|%d|%s|%d|%s|%s|%s\n%s", + channelmarker, channel->seq, (int) tstamp.tv_sec, (int) tstamp.tv_usec, + sender, hostname, + log->test->name, log->host->ip, (int) log->validtime, + colnames[log->color], colnames[log->oldcolor], (int) log->lastchange, + pagepath, log->cookie, osname, classname, + (log->grouplist ? log->grouplist : ""), + msg); + } + if (n > (bufsz-5)) { + errprintf("Oversize page/ack/notify msg from %s for %s:%s truncated (n=%d, limit=%d)\n", + sender, hostname, (log->test->name ? log->test->name : "<none>"), n, bufsz); + } + *(channel->channelbuf + bufsz - 5) = '\0'; + break; + + case C_DATA: + case C_CLIENT: + /* Data channel messages are pre-formatted so we never go here */ + break; + + case C_NOTES: + n = snprintf(channel->channelbuf, (bufsz-5), + "@@%s#%u|%d.%06d|%s|%s\n%s", + channelmarker, channel->seq, (int) tstamp.tv_sec, (int) tstamp.tv_usec, + sender, hostname, msg); + if (n > (bufsz-5)) { + errprintf("Oversize notes msg from %s for %s:%s truncated (n=%d, limit=%d)\n", + sender, hostname, log->test->name, n, bufsz); + } + *(channel->channelbuf + bufsz - 5) = '\0'; + break; + + case C_ENADIS: + n = snprintf(channel->channelbuf, (bufsz-5), + "@@%s#%u|%d.%06d|%s|%s|%s|%d", + channelmarker, channel->seq, (int) tstamp.tv_sec, (int)tstamp.tv_usec, + sender, hostname, log->test->name, (int) log->enabletime); + if (n > (bufsz-5)) { + errprintf("Oversize enadis msg from %s for %s:%s truncated (n=%d, limit=%d)\n", + sender, hostname, log->test->name, n, bufsz); + } + *(channel->channelbuf + bufsz - 5) = '\0'; + break; + + case C_LAST: + break; + } + } + /* Terminate the message */ + strncat(channel->channelbuf, "\n@@\n", (bufsz-1)); + + /* Let the readers know it is there. */ + clients = semctl(channel->semid, CLIENTCOUNT, GETVAL); /* Get it again, maybe changed since last check */ + dbgprintf("Posting message %u to %d readers\n", channel->seq, clients); + /* Up BOARDBUSY */ + s.sem_num = BOARDBUSY; + s.sem_op = (clients - semctl(channel->semid, BOARDBUSY, GETVAL)); + if (s.sem_op <= 0) { + errprintf("How did this happen? clients=%d, s.sem_op=%d\n", clients, s.sem_op); + s.sem_op = clients; + } + s.sem_flg = 0; + n = semop(channel->semid, &s, 1); + + /* Make sure GOCLIENT is 0 */ + n = semctl(channel->semid, GOCLIENT, GETVAL); + if (n > 0) { + errprintf("Oops ... GOCLIENT is high (%d)\n", n); + } + + s.sem_num = GOCLIENT; s.sem_op = clients; s.sem_flg = 0; /* Up GOCLIENT */ + n = semop(channel->semid, &s, 1); + + dbgprintf("<- posttochannel\n"); + + return; +} + + +void log_ghost(char *hostname, char *sender, char *msg) +{ + RbtHandle ghandle; + ghostlist_t *gwalk; + + dbgprintf("-> log_ghost\n"); + + /* If debugging, log the full request */ + if (dbgfd) { + fprintf(dbgfd, "\n---- combo message from %s ----\n%s---- end message ----\n", sender, msg); + fflush(dbgfd); + } + + if ((hostname == NULL) || (sender == NULL)) return; + + ghandle = rbtFind(rbghosts, hostname); + if (ghandle == rbtEnd(rbghosts)) { + gwalk = (ghostlist_t *)malloc(sizeof(ghostlist_t)); + gwalk->name = strdup(hostname); + gwalk->sender = strdup(sender); + gwalk->tstamp = time(NULL); + rbtInsert(rbghosts, gwalk->name, gwalk); + } + else { + gwalk = (ghostlist_t *)gettreeitem(rbghosts, ghandle); + if (gwalk->sender) xfree(gwalk->sender); + gwalk->sender = strdup(sender); + gwalk->tstamp = time(NULL); + } + + dbgprintf("<- log_ghost\n"); +} + +hobbitd_log_t *find_log(char *hostname, char *testname, char *origin, hobbitd_hostlist_t **host) +{ + RbtIterator hosthandle, testhandle, originhandle; + hobbitd_hostlist_t *hwalk; + char *owalk = NULL; + testinfo_t *twalk; + hobbitd_log_t *lwalk; + + *host = NULL; + if ((hostname == NULL) || (testname == NULL)) return NULL; + + hosthandle = rbtFind(rbhosts, hostname); + if (hosthandle != rbtEnd(rbhosts)) *host = hwalk = gettreeitem(rbhosts, hosthandle); else return NULL; + + testhandle = rbtFind(rbtests, testname); + if (testhandle != rbtEnd(rbtests)) twalk = gettreeitem(rbtests, testhandle); else return NULL; + + if (origin) { + originhandle = rbtFind(rborigins, origin); + if (originhandle != rbtEnd(rborigins)) owalk = gettreeitem(rborigins, originhandle); + } + + for (lwalk = hwalk->logs; (lwalk && ((lwalk->test != twalk) || (lwalk->origin != owalk))); lwalk = lwalk->next); + return lwalk; +} + +void get_hts(char *msg, char *sender, char *origin, + hobbitd_hostlist_t **host, testinfo_t **test, char **grouplist, hobbitd_log_t **log, + int *color, char **downcause, int *alltests, int createhost, int createlog) +{ + /* + * This routine takes care of finding existing status log records, or + * (if they dont exist) creating new ones for an incoming status. + * + * "msg" contains an incoming message. First list is of the form "KEYWORD host,domain.test COLOR" + */ + + char *firstline, *p; + char *hosttest, *hostname, *testname, *colstr, *grp; + char hostip[IP_ADDR_STRLEN]; + RbtIterator hosthandle, testhandle, originhandle; + hobbitd_hostlist_t *hwalk = NULL; + testinfo_t *twalk = NULL; + char *owalk = NULL; + hobbitd_log_t *lwalk = NULL; + + dbgprintf("-> get_hts\n"); + + MEMDEFINE(hostip); + *hostip = '\0'; + + *host = NULL; + *test = NULL; + *log = NULL; + *color = -1; + if (grouplist) *grouplist = NULL; + if (downcause) *downcause = NULL; + if (alltests) *alltests = 0; + + hosttest = hostname = testname = colstr = grp = NULL; + p = strchr(msg, '\n'); + if (p == NULL) { + firstline = strdup(msg); + } + else { + *p = '\0'; + firstline = strdup(msg); + *p = '\n'; + } + + p = strtok(firstline, " \t"); /* Keyword ... */ + if (p) { + /* There might be a group-list */ + grp = strstr(p, "/group:"); + if (grp) grp += 7; + } + if (p) hosttest = strtok(NULL, " \t"); /* ... HOST.TEST combo ... */ + if (hosttest == NULL) goto done; + colstr = strtok(NULL, " \t"); /* ... and the color (if any) */ + if (colstr) { + *color = parse_color(colstr); + /* Dont create log-entries if we get a bad color spec. */ + if (*color == -1) createlog = 0; + } + else createlog = 0; + + if (strncmp(msg, "summary", 7) == 0) { + /* Summary messages are handled specially */ + hostname = hosttest; /* This will always be "summary" */ + testname = strchr(hosttest, '.'); + if (testname) { *testname = '\0'; testname++; } + } + else { + char *knownname; + + hostname = hosttest; + testname = strrchr(hosttest, '.'); + if (testname) { *testname = '\0'; testname++; } + p = hostname; while ((p = strchr(p, ',')) != NULL) *p = '.'; + + knownname = knownhost(hostname, hostip, ghosthandling); + if (knownname == NULL) { + log_ghost(hostname, sender, msg); + goto done; + } + hostname = knownname; + } + + hosthandle = rbtFind(rbhosts, hostname); + if (hosthandle == rbtEnd(rbhosts)) hwalk = NULL; + else hwalk = gettreeitem(rbhosts, hosthandle); + + if (createhost && (hosthandle == rbtEnd(rbhosts))) { + hwalk = create_hostlist_t(hostname, hostip); + hostcount++; + } + + if (testname && *testname) { + if (alltests && (*testname == '*')) { + *alltests = 1; + return; + } + + testhandle = rbtFind(rbtests, testname); + if (testhandle != rbtEnd(rbtests)) twalk = gettreeitem(rbtests, testhandle); + if (createlog && (twalk == NULL)) twalk = create_testinfo(testname); + } + else { + if (createlog) errprintf("Bogus message from %s: No testname '%s'\n", sender, msg); + } + + if (origin) { + originhandle = rbtFind(rborigins, origin); + if (originhandle != rbtEnd(rborigins)) owalk = gettreeitem(rborigins, originhandle); + if (createlog && (owalk == NULL)) { + owalk = strdup(origin); + rbtInsert(rborigins, owalk, owalk); + } + } + + if (hwalk && twalk && owalk) { + for (lwalk = hwalk->logs; (lwalk && ((lwalk->test != twalk) || (lwalk->origin != owalk))); lwalk = lwalk->next); + if (createlog && (lwalk == NULL)) { + lwalk = (hobbitd_log_t *)calloc(1, sizeof(hobbitd_log_t)); + lwalk->color = lwalk->oldcolor = NO_COLOR; + lwalk->host = hwalk; + lwalk->test = twalk; + lwalk->origin = owalk; + lwalk->cookie = -1; + lwalk->next = hwalk->logs; + hwalk->logs = lwalk; + if (strcmp(testname, xgetenv("PINGCOLUMN")) == 0) hwalk->pinglog = lwalk; + } + } + +done: + if (colstr) { + if ((*color == COL_RED) || (*color == COL_YELLOW)) { + char *cause; + + cause = check_downtime(hostname, testname); + if (lwalk) lwalk->downtimeactive = (cause != NULL); + if (cause) *color = COL_BLUE; + if (downcause) *downcause = cause; + } + else { + if (lwalk) lwalk->downtimeactive = 0; + } + } + + if (grouplist && grp) *grouplist = strdup(grp); + + xfree(firstline); + + *host = hwalk; + *test = twalk; + *log = lwalk; + + MEMUNDEFINE(hostip); + + dbgprintf("<- get_hts\n"); +} + + +hobbitd_log_t *find_cookie(int cookie) +{ + /* + * Find a cookie we have issued. + */ + hobbitd_log_t *result = NULL; + RbtIterator cookiehandle; + + dbgprintf("-> find_cookie\n"); + + cookiehandle = rbtFind(rbcookies, (void *)cookie); + if (cookiehandle != rbtEnd(rbcookies)) { + result = gettreeitem(rbcookies, cookiehandle); + if (result->cookieexpires <= time(NULL)) result = NULL; + } + + dbgprintf("<- find_cookie\n"); + + return result; +} + +void clear_cookie(hobbitd_log_t *log) +{ + RbtIterator cookiehandle; + + if (log->cookie <= 0) return; + + cookiehandle = rbtFind(rbcookies, (void *)log->cookie); + log->cookie = -1; log->cookieexpires = 0; + + if (cookiehandle == rbtEnd(rbcookies)) return; + + rbtErase(rbcookies, cookiehandle); +} + + +void handle_status(unsigned char *msg, char *sender, char *hostname, char *testname, char *grouplist, + hobbitd_log_t *log, int newcolor, char *downcause) +{ + int validity = 30; /* validity is counted in minutes */ + time_t now = time(NULL); + int msglen, issummary; + enum alertstate_t oldalertstatus, newalertstatus; + + dbgprintf("->handle_status\n"); + + if (msg == NULL) { + errprintf("handle_status got a NULL message for %s.%s, sender %s, color %s\n", + textornull(hostname), textornull(testname), textornull(sender), colorname(newcolor)); + return; + } + + msglen = strlen(msg); + if (msglen == 0) { + errprintf("Bogus status message for %s.%s contains no data: Sent from %s\n", + textornull(hostname), textornull(testname), textornull(sender)); + return; + } + if (msg_data(msg) == (char *)msg) { + errprintf("Bogus status message: msg_data finds no host.test. Sent from: '%s', data:'%s'\n", + sender, msg); + return; + } + + issummary = (log->host->hosttype == H_SUMMARY); + + if (strncmp(msg, "status+", 7) == 0) { + validity = durationvalue(msg+7); + } + + if (log->enabletime == DISABLED_UNTIL_OK) { + /* The test is disabled until we get an OK status */ + if ((newcolor != COL_BLUE) && (decide_alertstate(newcolor) == A_OK)) { + /* It's OK now - clear the disable status */ + log->enabletime = 0; + if (log->dismsg) { xfree(log->dismsg); log->dismsg = NULL; } + posttochannel(enadischn, channelnames[C_ENADIS], msg, sender, log->host->hostname, log, NULL); + } + else { + /* Still not OK - keep it BLUE */ + newcolor = COL_BLUE; + } + } + else if (log->enabletime > now) { + /* The test is currently disabled. */ + newcolor = COL_BLUE; + } + else if (log->enabletime) { + /* A disable has expired. Clear the timestamp and the message buffer */ + log->enabletime = 0; + if (log->dismsg) { xfree(log->dismsg); log->dismsg = NULL; } + posttochannel(enadischn, channelnames[C_ENADIS], msg, sender, log->host->hostname, log, NULL); + } + else { + /* If we got a downcause, and the status is not disabled, use downcause as the disable text */ + if (log->dismsg) { xfree(log->dismsg); log->dismsg = NULL; } + if (downcause && (newcolor == COL_BLUE)) log->dismsg = strdup(downcause); + } + + if (log->acktime) { + /* Handling of ack'ed tests */ + + if (decide_alertstate(newcolor) == A_OK) { + /* The test recovered. Clear the ack. */ + log->acktime = 0; + } + + if (log->acktime > now) { + /* Dont need to do anything about an acked test */ + } + else { + /* The acknowledge has expired. Clear the timestamp and the message buffer */ + log->acktime = 0; + if (log->ackmsg) { xfree(log->ackmsg); log->ackmsg = NULL; } + } + } + + log->logtime = now; + + /* + * Decide how long this status is valid. + * + * Normally we'll just set the valid time according + * to the validity of the status report. + * + * If the status is acknowledged, make it valid for the longest period + * of the acknowledgment and the normal validity (so an acknowledged status + * does not go purple because it is not being updated due to the host being down). + * + * Same tweak must be done for disabled tests. + */ + log->validtime = now + validity*60; + if (log->acktime && (log->acktime > log->validtime)) log->validtime = log->acktime; + if (log->enabletime) { + if (log->enabletime == DISABLED_UNTIL_OK) log->validtime = INT_MAX; + else if (log->enabletime > log->validtime) log->validtime = log->enabletime; + } + + strncpy(log->sender, sender, sizeof(log->sender)-1); + *(log->sender + sizeof(log->sender) - 1) = '\0'; + log->oldcolor = log->color; + log->color = newcolor; + oldalertstatus = decide_alertstate(log->oldcolor); + newalertstatus = decide_alertstate(newcolor); + if (log->grouplist) xfree(log->grouplist); + if (grouplist) log->grouplist = strdup(grouplist); + + if (log->acklist) { + ackinfo_t *awalk; + + if ((oldalertstatus != A_OK) && (newalertstatus == A_OK)) { + /* The status recovered. Set the "clearack" timer */ + time_t cleartime = getcurrenttime(NULL) + ACKCLEARDELAY; + for (awalk = log->acklist; (awalk); awalk = awalk->next) awalk->cleartime = cleartime; + } + else if ((oldalertstatus == A_OK) && (newalertstatus != A_OK)) { + /* The status went into a failure-mode. Any acks are revived */ + for (awalk = log->acklist; (awalk); awalk = awalk->next) awalk->cleartime = awalk->validuntil; + } + } + + if (msg != log->message) { /* They can be the same when called from handle_enadis() or check_purple_status() */ + char *p; + + /* + * Note here: + * - log->msgsz is the buffer size INCLUDING the final \0. + * - msglen is the message length WITHOUT the final \0. + */ + if ((log->message == NULL) || (log->msgsz == 0)) { + /* No buffer - get one */ + log->message = (unsigned char *)malloc(msglen+1); + memcpy(log->message, msg, msglen+1); + log->msgsz = msglen+1; + } + else if (log->msgsz > msglen) { + /* Message - including \0 - fits into the existing buffer. */ + memcpy(log->message, msg, msglen+1); + } + else { + /* Message does not fit into existing buffer. Grow it. */ + log->message = (unsigned char *)realloc(log->message, msglen+1); + memcpy(log->message, msg, msglen+1); + log->msgsz = msglen+1; + } + + /* Get at the test flags. They are immediately after the color */ + p = msg_data(msg); + p += strlen(colorname(newcolor)); + + if (strncmp(p, " <!-- [flags:", 13) == 0) { + char *flagstart = p+13; + char *flagend = strchr(flagstart, ']'); + + if (flagend) { + *flagend = '\0'; + if (log->testflags == NULL) + log->testflags = strdup(flagstart); + else if (strlen(log->testflags) >= strlen(flagstart)) + strcpy(log->testflags, flagstart); + else { + log->testflags = realloc(log->testflags, strlen(flagstart)); + strcpy(log->testflags, flagstart); + } + *flagend = ']'; + } + } + } + + /* If in an alert state, we may need to generate a cookie */ + if (newalertstatus == A_ALERT) { + if (log->cookieexpires < now) { + int newcookie; + + clear_cookie(log); + + /* Need to ensure that cookies are unique, hence the loop */ + log->cookie = -1; log->cookieexpires = 0; + do { + newcookie = (random() % 1000000); + } while (find_cookie(newcookie)); + + log->cookie = newcookie; + rbtInsert(rbcookies, (void *)newcookie, log); + + /* + * This is fundamentally flawed. The cookie should be generated by + * the alert module, because it may not be sent to the user for + * a long time, depending on the alert configuration. + * That's for 4.1 - for now, we'll just give it a long enough + * lifetime so that cookies will be valid. + */ + log->cookieexpires = now + 86400; /* Valid for 1 day */ + } + } + else { + /* Not alert state, so clear any cookies */ + if (log->cookie >= 0) clear_cookie(log); + } + + if (!issummary && (!log->histsynced || (log->oldcolor != newcolor))) { + /* + * Change of color goes to the status-change channel. + */ + dbgprintf("posting to stachg channel: host=%s, test=%s\n", hostname, testname); + posttochannel(stachgchn, channelnames[C_STACHG], msg, sender, hostname, log, NULL); + log->histsynced = 1; + + /* + * Dont update the log->lastchange timestamp while DOWNTIME is active. + * (It is only seen as active if the color has been forced BLUE). + */ + if (!log->downtimeactive && (log->oldcolor != newcolor)) { + if (log->host->clientmsg && !log->host->clientmsgposted && (newalertstatus == A_ALERT) && log->test->clientsave) { + posttochannel(clichgchn, channelnames[C_CLICHG], msg, sender, + hostname, log, NULL); + } + log->lastchange = time(NULL); + } + } + + if (!issummary) { + if (newalertstatus == A_ALERT) { + /* Status is critical, send alerts */ + dbgprintf("posting alert to page channel\n"); + + log->activealert = 1; + posttochannel(pagechn, channelnames[C_PAGE], msg, sender, hostname, log, NULL); + } + else if (log->activealert && (oldalertstatus != A_OK) && (newalertstatus == A_OK)) { + /* Status has recovered, send recovery notice */ + dbgprintf("posting recovery to page channel\n"); + + log->activealert = 0; + posttochannel(pagechn, channelnames[C_PAGE], msg, sender, hostname, log, NULL); + } + else if (log->activealert && (log->oldcolor != newcolor)) { + /* + * Status is in-between critical and recovered, but we do have an + * active alert for this status. So tell the pager module that the + * color has changed. + */ + dbgprintf("posting color change to page channel\n"); + posttochannel(pagechn, channelnames[C_PAGE], msg, sender, hostname, log, NULL); + } + } + + dbgprintf("posting to status channel\n"); + posttochannel(statuschn, channelnames[C_STATUS], msg, sender, hostname, log, NULL); + + dbgprintf("<-handle_status\n"); + return; +} + +void handle_meta(char *msg, hobbitd_log_t *log) +{ + /* + * msg has the format "meta HOST.TEST metaname\nmeta-value\n" + */ + char *metaname = NULL, *eoln, *line1 = NULL; + htnames_t *nwalk; + hobbitd_meta_t *mwalk; + + dbgprintf("-> handle_meta\n"); + + eoln = strchr(msg, '\n'); + if (eoln) { + char *tok; + + *eoln = '\0'; + line1 = strdup(msg); + *eoln = '\n'; + + tok = strtok(line1, " "); /* "meta" */ + if (tok) tok = strtok(NULL, " "); /* "host.test" */ + if (tok) tok = strtok(NULL, " "); /* metaname */ + if (tok) metaname = tok; + } + if (!metaname) { + if (line1) xfree(line1); + errprintf("Malformed 'meta' message: '%s'\n", msg); + return; + } + + for (nwalk = metanames; (nwalk && strcmp(nwalk->name, metaname)); nwalk = nwalk->next) ; + if (nwalk == NULL) { + nwalk = (htnames_t *)malloc(sizeof(htnames_t)); + nwalk->name = strdup(metaname); + nwalk->next = metanames; + metanames = nwalk; + } + + for (mwalk = log->metas; (mwalk && (mwalk->metaname != nwalk)); mwalk = mwalk->next); + if (mwalk == NULL) { + mwalk = (hobbitd_meta_t *)malloc(sizeof(hobbitd_meta_t)); + mwalk->metaname = nwalk; + mwalk->value = strdup(eoln+1); + mwalk->next = log->metas; + log->metas = mwalk; + } + else { + if (mwalk->value) xfree(mwalk->value); + mwalk->value = strdup(eoln+1); + } + + if (line1) xfree(line1); + + dbgprintf("<- handle_meta\n"); +} + +void handle_data(char *msg, char *sender, char *origin, char *hostname, char *testname) +{ + char *chnbuf; + int buflen = 0; + + dbgprintf("->handle_data\n"); + + if (origin) buflen += strlen(origin); else dbgprintf(" origin is NULL\n"); + if (hostname) buflen += strlen(hostname); else dbgprintf(" hostname is NULL\n"); + if (testname) buflen += strlen(testname); else dbgprintf(" testname is NULL\n"); + if (msg) buflen += strlen(msg); else dbgprintf(" msg is NULL\n"); + buflen += 4; + + chnbuf = (char *)malloc(buflen); + snprintf(chnbuf, buflen, "%s|%s|%s\n%s", + (origin ? origin : ""), + (hostname ? hostname : ""), + (testname ? testname : ""), + msg); + + posttochannel(datachn, channelnames[C_DATA], msg, sender, hostname, NULL, chnbuf); + xfree(chnbuf); + dbgprintf("<-handle_data\n"); +} + +void handle_notes(char *msg, char *sender, char *hostname) +{ + dbgprintf("->handle_notes\n"); + posttochannel(noteschn, channelnames[C_NOTES], msg, sender, hostname, NULL, NULL); + dbgprintf("<-handle_notes\n"); +} + +void handle_usermsg(char *msg, char *sender, char *hostname) +{ + dbgprintf("->handle_usermsg\n"); + posttochannel(userchn, channelnames[C_USER], msg, sender, hostname, NULL, NULL); + dbgprintf("<-handle_usermsg\n"); +} + +void handle_enadis(int enabled, conn_t *msg, char *sender) +{ + char *firstline = NULL, *hosttest = NULL, *durstr = NULL, *txtstart = NULL; + char *hname = NULL, *tname = NULL; + time_t expires = 0; + int alltests = 0; + RbtIterator hosthandle, testhandle; + hobbitd_hostlist_t *hwalk = NULL; + testinfo_t *twalk = NULL; + hobbitd_log_t *log; + char *p; + char hostip[IP_ADDR_STRLEN]; + + dbgprintf("->handle_enadis\n"); + + MEMDEFINE(hostip); + + p = strchr(msg->buf, '\n'); if (p) *p = '\0'; + firstline = strdup(msg->buf); + if (p) *p = '\n'; + + p = strtok(firstline, " \t"); + if (p) hosttest = strtok(NULL, " \t"); + if (hosttest) durstr = strtok(NULL, " \t"); + if (!hosttest) { + errprintf("Invalid enable/disable from %s - no host/test specified\n", sender); + goto done; + } + + if (!enabled) { + if (durstr) { + if (strcmp(durstr, "-1") == 0) { + expires = DISABLED_UNTIL_OK; + } + else { + expires = 60*durationvalue(durstr) + time(NULL); + } + + txtstart = msg->buf + (durstr + strlen(durstr) - firstline); + txtstart += strspn(txtstart, " \t\r\n"); + if (*txtstart == '\0') txtstart = "(No reason given)"; + } + else { + errprintf("Invalid disable from %s - no duration specified\n", sender); + goto done; + } + } + + p = hosttest + strlen(hosttest) - 1; + if (*p == '*') { + /* It ends with a '*' so assume this is for all tests */ + alltests = 1; + *p = '\0'; + p--; + if (*p == '.') *p = '\0'; + } + else { + /* No wildcard -> get the test name */ + p = strrchr(hosttest, '.'); + if (p == NULL) goto done; /* "enable foo" ... surely you must be joking. */ + *p = '\0'; + tname = (p+1); + } + p = hosttest; while ((p = strchr(p, ',')) != NULL) *p = '.'; + hname = knownhost(hosttest, hostip, ghosthandling); + if (hname == NULL) goto done; + + hosthandle = rbtFind(rbhosts, hname); + if (hosthandle == rbtEnd(rbhosts)) { + /* Unknown host */ + goto done; + } + else hwalk = gettreeitem(rbhosts, hosthandle); + + if (!oksender(maintsenders, hwalk->ip, msg->addr.sin_addr, msg->buf)) goto done; + + if (tname) { + testhandle = rbtFind(rbtests, tname); + if (testhandle == rbtEnd(rbtests)) { + /* Unknown test */ + goto done; + } + else twalk = gettreeitem(rbtests, testhandle); + } + + if (enabled) { + /* Enable is easy - just clear the enabletime */ + if (alltests) { + for (log = hwalk->logs; (log); log = log->next) { + log->enabletime = 0; + if (log->dismsg) { + xfree(log->dismsg); + log->dismsg = NULL; + } + posttochannel(enadischn, channelnames[C_ENADIS], msg->buf, sender, log->host->hostname, log, NULL); + } + } + else { + for (log = hwalk->logs; (log && (log->test != twalk)); log = log->next) ; + if (log) { + log->enabletime = 0; + if (log->dismsg) { + xfree(log->dismsg); + log->dismsg = NULL; + } + posttochannel(enadischn, channelnames[C_ENADIS], msg->buf, sender, log->host->hostname, log, NULL); + } + } + } + else { + /* disable code goes here */ + + if (alltests) { + for (log = hwalk->logs; (log); log = log->next) { + log->enabletime = expires; + log->validtime = (expires == DISABLED_UNTIL_OK) ? INT_MAX : log->validtime; + if (txtstart) { + if (log->dismsg) xfree(log->dismsg); + log->dismsg = strdup(txtstart); + } + posttochannel(enadischn, channelnames[C_ENADIS], msg->buf, sender, log->host->hostname, log, NULL); + /* Trigger an immediate status update */ + handle_status(log->message, sender, log->host->hostname, log->test->name, log->grouplist, log, COL_BLUE, NULL); + } + } + else { + for (log = hwalk->logs; (log && (log->test != twalk)); log = log->next) ; + if (log) { + log->enabletime = expires; + log->validtime = (expires == DISABLED_UNTIL_OK) ? INT_MAX : log->validtime; + if (txtstart) { + if (log->dismsg) xfree(log->dismsg); + log->dismsg = strdup(txtstart); + } + posttochannel(enadischn, channelnames[C_ENADIS], msg->buf, sender, log->host->hostname, log, NULL); + + /* Trigger an immediate status update */ + handle_status(log->message, sender, log->host->hostname, log->test->name, log->grouplist, log, COL_BLUE, NULL); + } + } + + } + +done: + MEMUNDEFINE(hostip); + xfree(firstline); + + dbgprintf("<-handle_enadis\n"); + + return; +} + + +void handle_ack(char *msg, char *sender, hobbitd_log_t *log, int duration) +{ + char *p; + + dbgprintf("->handle_ack\n"); + + log->acktime = time(NULL)+duration*60; + if (log->validtime < log->acktime) log->validtime = log->acktime; + + p = msg; + p += strspn(p, " \t"); /* Skip the space ... */ + p += strspn(p, "-0123456789"); /* and the cookie ... */ + p += strspn(p, " \t"); /* and the space ... */ + p += strspn(p, "0123456789hdwmy"); /* and the duration ... */ + p += strspn(p, " \t"); /* and the space ... */ + log->ackmsg = strdup(p); + + /* Tell the pagers */ + posttochannel(pagechn, "ack", log->ackmsg, sender, log->host->hostname, log, NULL); + + dbgprintf("<-handle_ack\n"); + return; +} + +void handle_ackinfo(char *msg, char *sender, hobbitd_log_t *log) +{ + int level = -1; + time_t validuntil = -1, itemval; + time_t received = getcurrenttime(NULL); + char *ackedby = NULL, *ackmsg = NULL; + char *tok, *item; + int itemno = 0; + + tok = msg; + while (tok) { + tok += strspn(tok, " \t\n"); + item = tok; itemno++; + tok = strchr(tok, '\n'); if (tok) { *tok = '\0'; tok++; } + + switch (itemno) { + case 1: break; /* First line has just the HOST.TEST */ + case 2: level = atoi(item); break; + case 3: itemval = atoi(item); + if (itemval == -1) itemval = 365*24*60*60; /* 1 year */ + validuntil = received + itemval; + break; + case 4: ackedby = strdup(item); break; + case 5: ackmsg = strdup(item); break; + } + } + + if ((level >= 0) && (validuntil > received) && ackedby && ackmsg) { + ackinfo_t *newack; + int isnew; + + dbgprintf("Got ackinfo: Level=%d,until=%d,ackby=%s,msg=%s\n", level, validuntil, ackedby, ackmsg); + + /* See if we already have this ack in the list */ + for (newack = log->acklist; (newack && ((level != newack->level) || strcmp(newack->ackedby, ackedby))); newack = newack->next); + + isnew = (newack == NULL); + dbgprintf("This ackinfo is %s\n", (isnew ? "new" : "old")); + if (isnew) { + dbgprintf("Creating new ackinfo record\n"); + newack = (ackinfo_t *)malloc(sizeof(ackinfo_t)); + } + else { + /* Drop the old data so we dont leak memory */ + dbgprintf("Dropping old ackinfo data: From %s, msg=%s\n", newack->ackedby, newack->msg); + if (newack->ackedby) xfree(newack->ackedby); + if (newack->msg) xfree(newack->msg); + } + + newack->level = level; + newack->received = received; + newack->validuntil = newack->cleartime = validuntil; + newack->ackedby = ackedby; + newack->msg = ackmsg; + + if (isnew) { + newack->next = log->acklist; + log->acklist = newack; + } + + if (ackinfologfd) { + char timestamp[25]; + + strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", localtime(&received)); + fprintf(ackinfologfd, "%s %s %s %s %d %d %d %d %s\n", + timestamp, log->host->hostname, log->test->name, + newack->ackedby, newack->level, + (int)log->lastchange, (int)newack->received, (int)newack->validuntil, + nlencode(newack->msg)); + fflush(ackinfologfd); + } + } + else { + if (ackedby) xfree(ackedby); + if (ackmsg) xfree(ackmsg); + } +} + +void handle_notify(char *msg, char *sender, char *hostname, char *testname) +{ + char *msgtext, *channelmsg; + namelist_t *hi; + + dbgprintf("-> handle_notify\n"); + + hi = hostinfo(hostname); + + msgtext = msg_data(msg); + channelmsg = (char *)malloc(1024 + strlen(msgtext)); + + /* Tell the pagers */ + sprintf(channelmsg, "%s|%s|%s\n%s", + hostname, (testname ? testname : ""), (hi ? hi->page->pagepath : ""), msgtext); + posttochannel(pagechn, "notify", msg, sender, hostname, NULL, channelmsg); + + xfree(channelmsg); + + dbgprintf("<- handle_notify\n"); + return; +} + +void handle_client(char *msg, char *sender, char *hostname, char *clientos, char *clientclass) +{ + char *chnbuf, *theclass; + int msglen, buflen = 0; + RbtIterator hosthandle; + + dbgprintf("->handle_client\n"); + + /* Default class is the OS */ + theclass = (clientclass ? clientclass : clientos); + buflen += strlen(hostname) + strlen(clientos) + strlen(theclass); + if (msg) { msglen = strlen(msg); buflen += msglen; } else { dbgprintf(" msg is NULL\n"); return; } + buflen += 5; + + if (clientsavemem) { + hosthandle = rbtFind(rbhosts, hostname); + if (hosthandle != rbtEnd(rbhosts)) { + hobbitd_hostlist_t *hwalk; + hwalk = gettreeitem(rbhosts, hosthandle); + + if (hwalk->clientmsg) { + if (strlen(hwalk->clientmsg) >= msglen) + strcpy(hwalk->clientmsg, msg); + else { + xfree(hwalk->clientmsg); + hwalk->clientmsg = strdup(msg); + } + } + else { + hwalk->clientmsg = strdup(msg); + } + hwalk->clientmsgtstamp = time(NULL); + hwalk->clientmsgposted = 0; + } + } + + chnbuf = (char *)malloc(buflen); + snprintf(chnbuf, buflen, "%s|%s|%s\n%s", hostname, clientos, theclass, msg); + posttochannel(clientchn, channelnames[C_CLIENT], msg, sender, hostname, NULL, chnbuf); + xfree(chnbuf); + dbgprintf("<-handle_client\n"); +} + + + +void flush_acklist(hobbitd_log_t *zombie, int flushall) +{ + ackinfo_t *awalk, *newhead = NULL, *newtail = NULL; + time_t now = getcurrenttime(NULL); + + awalk = zombie->acklist; + while (awalk) { + ackinfo_t *tmp = awalk; + awalk = awalk->next; + + if (flushall || (tmp->cleartime < now) || (tmp->validuntil < now)) { + if (tmp->ackedby) xfree(tmp->ackedby); + if (tmp->msg) xfree(tmp->msg); + xfree(tmp); + } + else { + /* We have a record we want to keep */ + if (newhead == NULL) { + newhead = newtail = tmp; + } + else { + newtail->next = tmp; + newtail = tmp; + } + } + } + + if (newtail) newtail->next = NULL; + zombie->acklist = newhead; +} + +char *acklist_string(hobbitd_log_t *log, int level) +{ + static strbuffer_t *res = NULL; + ackinfo_t *awalk; + char tmpstr[512]; + + if (log->acklist == NULL) return NULL; + + if (res) clearstrbuffer(res); else res = newstrbuffer(0); + + for (awalk = log->acklist; (awalk); awalk = awalk->next) { + if ((level != -1) && (awalk->level != level)) continue; + snprintf(tmpstr, sizeof(tmpstr), "%d:%d:%d:%s:%s\n", + (int)awalk->received, (int)awalk->validuntil, + awalk->level, awalk->ackedby, awalk->msg); + tmpstr[sizeof(tmpstr)-1] = '\0'; + addtobuffer(res, tmpstr); + } + + return STRBUF(res); +} + +void free_log_t(hobbitd_log_t *zombie) +{ + hobbitd_meta_t *mwalk, *mtmp; + + dbgprintf("-> free_log_t\n"); + mwalk = zombie->metas; + while (mwalk) { + mtmp = mwalk; + mwalk = mwalk->next; + + if (mtmp->value) xfree(mtmp->value); + xfree(mtmp); + } + + if (zombie->message) xfree(zombie->message); + if (zombie->dismsg) xfree(zombie->dismsg); + if (zombie->ackmsg) xfree(zombie->ackmsg); + if (zombie->grouplist) xfree(zombie->grouplist); + flush_acklist(zombie, 1); + xfree(zombie); + dbgprintf("<- free_log_t\n"); +} + +void handle_dropnrename(enum droprencmd_t cmd, char *sender, char *hostname, char *n1, char *n2) +{ + char hostip[IP_ADDR_STRLEN]; + RbtIterator hosthandle, testhandle; + hobbitd_hostlist_t *hwalk; + testinfo_t *twalk, *newt; + hobbitd_log_t *lwalk; + char *marker = NULL; + char *canonhostname; + + dbgprintf("-> handle_dropnrename\n"); + MEMDEFINE(hostip); + + { + /* + * We pass drop- and rename-messages to the workers, whether + * we know about this host or not. It could be that the drop command + * arrived after we had already re-loaded the bb-hosts file, and + * so the host is no longer known by us - but there is still some + * data stored about it that needs to be cleaned up. + */ + + char *msgbuf = (char *)malloc(20 + strlen(hostname) + (n1 ? strlen(n1) : 0) + (n2 ? strlen(n2) : 0)); + + *msgbuf = '\0'; + switch (cmd) { + case CMD_DROPTEST: + marker = "droptest"; + sprintf(msgbuf, "%s|%s", hostname, n1); + break; + case CMD_DROPHOST: + marker = "drophost"; + sprintf(msgbuf, "%s", hostname); + break; + case CMD_RENAMEHOST: + marker = "renamehost"; + sprintf(msgbuf, "%s|%s", hostname, n1); + break; + case CMD_RENAMETEST: + marker = "renametest"; + sprintf(msgbuf, "%s|%s|%s", hostname, n1, n2); + break; + case CMD_DROPSTATE: + marker = "dropstate"; + sprintf(msgbuf, "%s", hostname); + break; + } + + if (strlen(msgbuf)) { + /* Tell the workers */ + posttochannel(statuschn, marker, NULL, sender, NULL, NULL, msgbuf); + posttochannel(stachgchn, marker, NULL, sender, NULL, NULL, msgbuf); + posttochannel(pagechn, marker, NULL, sender, NULL, NULL, msgbuf); + posttochannel(datachn, marker, NULL, sender, NULL, NULL, msgbuf); + posttochannel(noteschn, marker, NULL, sender, NULL, NULL, msgbuf); + posttochannel(enadischn, marker, NULL, sender, NULL, NULL, msgbuf); + posttochannel(clientchn, marker, NULL, sender, NULL, NULL, msgbuf); + } + + xfree(msgbuf); + } + + + /* + * Now clean up our internal state info, if there is any. + * NB: knownhost() may return NULL, if the bb-hosts file was re-loaded before + * we got around to cleaning up a host. + */ + canonhostname = knownhost(hostname, hostip, ghosthandling); + if (canonhostname) hostname = canonhostname; + + hosthandle = rbtFind(rbhosts, hostname); + if (hosthandle == rbtEnd(rbhosts)) goto done; + else hwalk = gettreeitem(rbhosts, hosthandle); + + switch (cmd) { + case CMD_DROPTEST: + testhandle = rbtFind(rbtests, n1); + if (testhandle == rbtEnd(rbtests)) goto done; + twalk = gettreeitem(rbtests, testhandle); + + for (lwalk = hwalk->logs; (lwalk && (lwalk->test != twalk)); lwalk = lwalk->next) ; + if (lwalk == NULL) goto done; + if (lwalk == hwalk->pinglog) hwalk->pinglog = NULL; + if (lwalk == hwalk->logs) { + hwalk->logs = hwalk->logs->next; + } + else { + hobbitd_log_t *plog; + for (plog = hwalk->logs; (plog->next != lwalk); plog = plog->next) ; + plog->next = lwalk->next; + } + free_log_t(lwalk); + break; + + case CMD_DROPHOST: + case CMD_DROPSTATE: + /* Unlink the hostlist entry */ + rbtErase(rbhosts, hosthandle); + hostcount--; + + /* Loop through the host logs and free them */ + lwalk = hwalk->logs; + while (lwalk) { + hobbitd_log_t *tmp = lwalk; + lwalk = lwalk->next; + + free_log_t(tmp); + } + + /* Free the hostlist entry */ + xfree(hwalk->hostname); + if (hwalk->clientmsg) xfree(hwalk->clientmsg); + xfree(hwalk); + break; + + case CMD_RENAMEHOST: + rbtErase(rbhosts, hosthandle); + if (strlen(hwalk->hostname) <= strlen(n1)) { + strcpy(hwalk->hostname, n1); + } + else { + xfree(hwalk->hostname); + hwalk->hostname = strdup(n1); + } + rbtInsert(rbhosts, hwalk->hostname, hwalk); + break; + + case CMD_RENAMETEST: + testhandle = rbtFind(rbtests, n1); + if (testhandle == rbtEnd(rbtests)) goto done; + twalk = gettreeitem(rbtests, testhandle); + + for (lwalk = hwalk->logs; (lwalk && (lwalk->test != twalk)); lwalk = lwalk->next) ; + if (lwalk == NULL) goto done; + + if (lwalk == hwalk->pinglog) hwalk->pinglog = NULL; + + testhandle = rbtFind(rbtests, n2); + if (testhandle == rbtEnd(rbtests)) { + newt = create_testinfo(n2); + } + else { + newt = gettreeitem(rbtests, testhandle); + } + lwalk->test = newt; + break; + } + +done: + MEMUNDEFINE(hostip); + + dbgprintf("<- handle_dropnrename\n"); + + return; +} + + +unsigned char *get_filecache(char *fn) +{ + RbtIterator handle; + filecache_t *item; + unsigned char *result; + + handle = rbtFind(rbfilecache, fn); + if (handle == rbtEnd(rbfilecache)) return NULL; + + item = (filecache_t *)gettreeitem(rbfilecache, handle); + if (item->len < 0) return NULL; + + result = (unsigned char *)malloc(item->len); + memcpy(result, item->fdata, item->len); + + return result; +} + + +void add_filecache(char *fn, unsigned char *buf, off_t buflen) +{ + RbtIterator handle; + filecache_t *newitem; + + handle = rbtFind(rbfilecache, fn); + if (handle == rbtEnd(rbfilecache)) { + newitem = (filecache_t *)malloc(sizeof(filecache_t)); + newitem->fn = strdup(fn); + newitem->len = buflen; + newitem->fdata = (unsigned char *)malloc(buflen); + memcpy(newitem->fdata, buf, buflen); + rbtInsert(rbfilecache, newitem->fn, newitem); + } + else { + newitem = (filecache_t *)gettreeitem(rbfilecache, handle); + if (newitem->fdata) xfree(newitem->fdata); + newitem->len = buflen; + newitem->fdata = (unsigned char *)malloc(buflen); + memcpy(newitem->fdata, buf, buflen); + } +} + + +void flush_filecache(void) +{ + RbtIterator handle; + + for (handle = rbtBegin(rbfilecache); (handle != rbtEnd(rbfilecache)); handle = rbtNext(rbfilecache, handle)) { + filecache_t *item = (filecache_t *)gettreeitem(rbfilecache, handle); + if (item->fdata) xfree(item->fdata); + item->len = -1; + } +} + + +int get_config(char *fn, conn_t *msg) +{ + char fullfn[PATH_MAX]; + FILE *fd = NULL; + strbuffer_t *inbuf, *result; + + dbgprintf("-> get_config %s\n", fn); + sprintf(fullfn, "%s/etc/%s", xgetenv("BBHOME"), fn); + fd = stackfopen(fullfn, "r", NULL); + if (fd == NULL) { + errprintf("Config file %s not found\n", fn); + return -1; + } + + inbuf = newstrbuffer(0); + result = newstrbuffer(0); + while (stackfgets(inbuf, NULL) != NULL) addtostrbuffer(result, inbuf); + stackfclose(fd); + freestrbuffer(inbuf); + + msg->buflen = STRBUFLEN(result); + msg->buf = grabstrbuffer(result); + msg->bufp = msg->buf + msg->buflen; + + dbgprintf("<- get_config\n"); + + return 0; +} + +int get_binary(char *fn, conn_t *msg) +{ + char fullfn[PATH_MAX]; + int fd; + struct stat st; + unsigned char *result; + + dbgprintf("-> get_binary %s\n", fn); + sprintf(fullfn, "%s/download/%s", xgetenv("BBHOME"), fn); + + result = get_filecache(fullfn); + if (!result) { + fd = open(fullfn, O_RDONLY); + if (fd == -1) { + errprintf("Download file %s not found\n", fn); + return -1; + } + + if (fstat(fd, &st) == 0) { + ssize_t n; + + result = (unsigned char *)malloc(st.st_size); + n = read(fd, result, st.st_size); + if (n != st.st_size) { + errprintf("Error reading from %s : %s\n", fn, strerror(errno)); + xfree(result); result = NULL; + close(fd); + return -1; + } + } + + add_filecache(fullfn, result, st.st_size); + } + + msg->buflen = st.st_size; + msg->buf = result; + msg->bufp = msg->buf + msg->buflen; + + dbgprintf("<- get_binary\n"); + + return 0; +} + +char *timestr(time_t tstamp) +{ + static char result[30]; + char *p; + + MEMDEFINE(result); + + if (tstamp == 0) { + MEMUNDEFINE(result); + return "N/A"; + } + + strcpy(result, ctime(&tstamp)); + p = strchr(result, '\n'); if (p) *p = '\0'; + + MEMUNDEFINE(result); + return result; +} + +void setup_filter(char *buf, char *defaultfields, + pcre **spage, pcre **shost, pcre **snet, + pcre **stest, int *scolor, int *acklevel, char **fields, + char **chspage, char **chshost, char **chsnet, char **chstest) +{ + char *tok, *s; + int idx = 0; + + dbgprintf("-> setup_filter: %s\n", buf); + + *spage = *shost = *snet = *stest = NULL; + if (chspage) *chspage = NULL; + if (chshost) *chshost = NULL; + if (chsnet) *chsnet = NULL; + if (chstest) *chstest = NULL; + *fields = NULL; + *scolor = -1; + + tok = strtok(buf, " \t\r\n"); + if (tok) tok = strtok(NULL, " \t\r\n"); + while (tok) { + /* Get filter */ + if (strncmp(tok, "page=", 5) == 0) { + if (*(tok+5)) { + *spage = compileregex(tok+5); + if (chspage) *chspage = tok+5; + } + } + else if (strncmp(tok, "host=", 5) == 0) { + if (*(tok+5)) { + *shost = compileregex(tok+5); + if (chshost) *chshost = tok+5; + } + } + else if (strncmp(tok, "net=", 4) == 0) { + if (*(tok+4)) { + *snet = compileregex(tok+4); + if (chsnet) *chsnet = tok+4; + } + } + else if (strncmp(tok, "test=", 5) == 0) { + if (*(tok+5)) { + *stest = compileregex(tok+5); + if (chstest) *chstest = tok+5; + } + } + else if (strncmp(tok, "fields=", 7) == 0) *fields = tok+7; + else if (strncmp(tok, "color=", 6) == 0) *scolor = colorset(tok+6, 0); + else if (strncmp(tok, "acklevel=", 9) == 0) *acklevel = atoi(tok+9); + else { + /* Might be an old-style HOST.TEST request */ + char *hname, *tname, hostip[IP_ADDR_STRLEN]; + char *hnameexp, *tnameexp; + + MEMDEFINE(hostip); + + hname = tok; + tname = strrchr(tok, '.'); + if (tname) { *tname = '\0'; tname++; } + s = hname; while ((s = strchr(s, ',')) != NULL) *s = '.'; + hname = knownhost(hname, hostip, ghosthandling); + + if (hname && tname) { + hnameexp = (char *)malloc(strlen(hname)+3); + sprintf(hnameexp, "^%s$", hname); + *shost = compileregex(hnameexp); + xfree(hnameexp); + + tnameexp = (char *)malloc(strlen(tname)+3); + sprintf(tnameexp, "^%s$", tname); + *stest = compileregex(tnameexp); + xfree(tnameexp); + + if (chshost) *chshost = hname; + if (chstest) *chstest = tname; + } + } + + tok = strtok(NULL, " \t\r\n"); + } + + /* If no fields given, provide the default set. */ + if (*fields == NULL) *fields = defaultfields; + + s = strdup(*fields); + tok = strtok(s, ","); + while (tok) { + enum boardfield_t fieldid = F_LAST; + enum bbh_item_t bbhfieldid = BBH_LAST; + int validfield = 1; + + if (strncmp(tok, "BBH_", 4) == 0) { + fieldid = F_HOSTINFO; + bbhfieldid = bbh_key_idx(tok); + validfield = (bbhfieldid != BBH_LAST); + } + else { + int i; + for (i=0; (boardfieldnames[i].name && strcmp(tok, boardfieldnames[i].name)); i++) ; + if (boardfieldnames[i].name) { + fieldid = boardfieldnames[i].id; + bbhfieldid = BBH_LAST; + } + } + + if ((fieldid != F_LAST) && (idx < BOARDFIELDS_MAX) && validfield) { + boardfields[idx].field = fieldid; + boardfields[idx].bbhfield = bbhfieldid; + idx++; + } + + tok = strtok(NULL, ","); + } + boardfields[idx].field = F_NONE; + boardfields[idx].bbhfield = BBH_LAST; + + xfree(s); + + dbgprintf("<- setup_filter: %s\n", buf); +} + +int match_host_filter(namelist_t *hinfo, pcre *spage, pcre *shost, pcre *snet) +{ + char *match; + + match = bbh_item(hinfo, BBH_HOSTNAME); + if (shost && match && !matchregex(match, shost)) return 0; + match = bbh_item(hinfo, BBH_PAGEPATH); + if (spage && match && !matchregex(match, spage)) return 0; + match = bbh_item(hinfo, BBH_NET); + if (snet && match && !matchregex(match, snet)) return 0; + + return 1; +} + +int match_test_filter(hobbitd_log_t *log, pcre *stest, int scolor) +{ + /* Testname filter */ + if (stest && !matchregex(log->test->name, stest)) return 0; + + /* Color filter */ + if ((scolor != -1) && (((1 << log->color) & scolor) == 0)) return 0; + + return 1; +} + + + +void generate_outbuf(char **outbuf, char **outpos, int *outsz, + hobbitd_hostlist_t *hwalk, hobbitd_log_t *lwalk, int acklevel) +{ + int f_idx; + char *buf, *bufp; + int bufsz; + char *eoln; + namelist_t *hinfo = NULL; + char *acklist = NULL; + int needed, used; + enum boardfield_t f_type; + + buf = *outbuf; + bufp = *outpos; + bufsz = *outsz; + needed = 1024; + + /* First calculate how much buffer space is needed */ + for (f_idx = 0, f_type = boardfields[0].field; ((f_type != F_NONE) && (f_type != F_LAST)); f_type = boardfields[++f_idx].field) { + if ((lwalk == NULL) && (f_type != F_HOSTINFO)) continue; + + switch (f_type) { + case F_ACKMSG: if (lwalk->ackmsg) needed += 2*strlen(lwalk->ackmsg); break; + case F_DISMSG: if (lwalk->dismsg) needed += 2*strlen(lwalk->dismsg); break; + case F_MSG: needed += 2*strlen(lwalk->message); break; + + case F_ACKLIST: + flush_acklist(lwalk, 0); + acklist = acklist_string(lwalk, acklevel); + if (acklist) needed += 2*strlen(acklist); + break; + + case F_HOSTINFO: + if (!hinfo) hinfo = hostinfo(hwalk->hostname); + if (hinfo) { + char *infostr = bbh_item(hinfo, boardfields[f_idx].bbhfield); + if (infostr) needed += strlen(infostr); + } + break; + + default: break; + } + } + + /* Make sure the buffer is large enough */ + used = (bufp - buf); + if ((bufsz - used) < needed) { + bufsz += needed; + buf = (char *)realloc(buf, bufsz); + bufp = buf + used; + } + + /* Now generate the data */ + for (f_idx = 0, f_type = boardfields[0].field; ((f_type != F_NONE) && (f_type != F_LAST)); f_type = boardfields[++f_idx].field) { + if ((lwalk == NULL) && (f_type != F_HOSTINFO)) continue; + if (f_idx > 0) bufp += sprintf(bufp, "|"); + + switch (f_type) { + case F_NONE: break; + case F_HOSTNAME: bufp += sprintf(bufp, "%s", hwalk->hostname); break; + case F_TESTNAME: bufp += sprintf(bufp, "%s", lwalk->test->name); break; + case F_COLOR: bufp += sprintf(bufp, "%s", colnames[lwalk->color]); break; + case F_FLAGS: bufp += sprintf(bufp, "%s", (lwalk->testflags ? lwalk->testflags : "")); break; + case F_LASTCHANGE: bufp += sprintf(bufp, "%d", (int)lwalk->lastchange); break; + case F_LOGTIME: bufp += sprintf(bufp, "%d", (int)lwalk->logtime); break; + case F_VALIDTIME: bufp += sprintf(bufp, "%d", (int)lwalk->validtime); break; + case F_ACKTIME: bufp += sprintf(bufp, "%d", (int)lwalk->acktime); break; + case F_DISABLETIME: bufp += sprintf(bufp, "%d", (int)lwalk->enabletime); break; + case F_SENDER: bufp += sprintf(bufp, "%s", lwalk->sender); break; + case F_COOKIE: bufp += sprintf(bufp, "%d", lwalk->cookie); break; + + case F_LINE1: + eoln = strchr(lwalk->message, '\n'); if (eoln) *eoln = '\0'; + bufp += sprintf(bufp, "%s", msg_data(lwalk->message)); + if (eoln) *eoln = '\n'; + break; + + case F_ACKMSG: if (lwalk->ackmsg) bufp += sprintf(bufp, "%s", nlencode(lwalk->ackmsg)); break; + case F_DISMSG: if (lwalk->dismsg) bufp += sprintf(bufp, "%s", nlencode(lwalk->dismsg)); break; + case F_MSG: bufp += sprintf(bufp, "%s", nlencode(lwalk->message)); break; + case F_CLIENT: bufp += sprintf(bufp, "%s", (hwalk->clientmsg ? "Y" : "N")); break; + case F_CLIENTTSTAMP: bufp += sprintf(bufp, "%ld", (hwalk->clientmsg ? (long) hwalk->clientmsgtstamp : 0)); break; + case F_ACKLIST: if (acklist) bufp += sprintf(bufp, "%s", nlencode(acklist)); break; + + case F_HOSTINFO: + if (hinfo) { /* hinfo has been set above while scanning for the needed bufsize */ + char *infostr = bbh_item(hinfo, boardfields[f_idx].bbhfield); + if (infostr) bufp += sprintf(bufp, "%s", infostr); + } + break; + + case F_LAST: break; + } + } + bufp += sprintf(bufp, "\n"); + + *outbuf = buf; + *outpos = bufp; + *outsz = bufsz; +} + + +void generate_hostinfo_outbuf(char **outbuf, char **outpos, int *outsz, namelist_t *hinfo) +{ + int f_idx; + char *buf, *bufp; + int bufsz; + char *infostr = NULL; + + buf = *outbuf; + bufp = *outpos; + bufsz = *outsz; + + for (f_idx = 0; (boardfields[f_idx].field != F_NONE); f_idx++) { + int needed = 1024; + int used = (bufp - buf); + + switch (boardfields[f_idx].field) { + case F_HOSTINFO: + infostr = bbh_item(hinfo, boardfields[f_idx].bbhfield); + if (infostr) needed += strlen(infostr); + break; + + default: break; + } + + if ((bufsz - used) < needed) { + bufsz += 4096 + needed; + buf = (char *)realloc(buf, bufsz); + bufp = buf + used; + } + + if (f_idx > 0) bufp += sprintf(bufp, "|"); + + switch (boardfields[f_idx].field) { + case F_HOSTINFO: if (infostr) bufp += sprintf(bufp, "%s", infostr); break; + default: break; + } + } + + bufp += sprintf(bufp, "\n"); + + *outbuf = buf; + *outpos = bufp; + *outsz = bufsz; +} + + +void do_message(conn_t *msg, char *origin) +{ + static int nesting = 0; + hobbitd_hostlist_t *h; + testinfo_t *t; + hobbitd_log_t *log; + int color; + char *downcause; + char sender[IP_ADDR_STRLEN]; + char *grouplist; + time_t now; + char *msgfrom; + + nesting++; + if (debug) { + char *eoln = strchr(msg->buf, '\n'); + + if (eoln) *eoln = '\0'; + dbgprintf("-> do_message/%d (%d bytes): %s\n", nesting, msg->buflen, msg->buf); + if (eoln) *eoln = '\n'; + } + + MEMDEFINE(sender); + + /* Most likely, we will not send a response */ + msg->doingwhat = NOTALK; + strncpy(sender, inet_ntoa(msg->addr.sin_addr), sizeof(sender)); + now = time(NULL); + + if (traceall || tracelist) { + int found = 0; + + if (traceall) { + found = 1; + } + else { + int i = 0; + do { + if ((tracelist[i].ipval & tracelist[i].ipmask) == (ntohl(msg->addr.sin_addr.s_addr) & tracelist[i].ipmask)) { + found = 1; + } + i++; + } while (!found && (tracelist[i].ipval != 0)); + } + + if (found) { + char tracefn[PATH_MAX]; + struct timeval tv; + struct timezone tz; + FILE *fd; + + gettimeofday(&tv, &tz); + + sprintf(tracefn, "%s/%d_%06d_%s.trace", xgetenv("BBTMP"), + (int) tv.tv_sec, (int) tv.tv_usec, sender); + fd = fopen(tracefn, "w"); + if (fd) { + fwrite(msg->buf, msg->buflen, 1, fd); + fclose(fd); + } + + if (ignoretraced) goto done; + } + } + + /* Count statistics */ + update_statistics(msg->buf); + + if (strncmp(msg->buf, "combo\n", 6) == 0) { + char *currmsg, *nextmsg; + + currmsg = msg->buf+6; + do { + int validsender = 1; + + nextmsg = strstr(currmsg, "\n\nstatus"); + if (nextmsg) { *(nextmsg+1) = '\0'; nextmsg += 2; } + + /* Pick out the real sender of this message */ + msgfrom = strstr(currmsg, "\nStatus message received from "); + if (msgfrom) { + sscanf(msgfrom, "\nStatus message received from %16s\n", sender); + *msgfrom = '\0'; + } + + if (statussenders) { + get_hts(currmsg, sender, origin, &h, &t, &grouplist, &log, &color, &downcause, NULL, 0, 0); + if (!oksender(statussenders, (h ? h->ip : NULL), msg->addr.sin_addr, currmsg)) validsender = 0; + } + + if (validsender) { + get_hts(currmsg, sender, origin, &h, &t, &grouplist, &log, &color, &downcause, NULL, 1, 1); + if (h && dbgfd && dbghost && (strcasecmp(h->hostname, dbghost) == 0)) { + fprintf(dbgfd, "\n---- combo message from %s ----\n%s---- end message ----\n", sender, currmsg); + fflush(dbgfd); + } + + if (color == COL_PURPLE) { + errprintf("Ignored PURPLE status update from %s for %s.%s\n", + sender, (h ? h->hostname : "<unknown>"), (t ? t->name : "unknown")); + } + else { + /* Count individual status-messages also */ + update_statistics(currmsg); + + if (h && t && log && (color != -1)) { + handle_status(currmsg, sender, h->hostname, t->name, grouplist, log, color, downcause); + } + } + } + + currmsg = nextmsg; + } while (currmsg); + } + else if (strncmp(msg->buf, "meta", 4) == 0) { + char *currmsg, *nextmsg; + + currmsg = msg->buf; + do { + nextmsg = strstr(currmsg, "\n\nmeta"); + if (nextmsg) { *(nextmsg+1) = '\0'; nextmsg += 2; } + + get_hts(currmsg, sender, origin, &h, &t, NULL, &log, &color, NULL, NULL, 0, 0); + if (h && t && log && oksender(statussenders, (h ? h->ip : NULL), msg->addr.sin_addr, currmsg)) { + handle_meta(currmsg, log); + } + + currmsg = nextmsg; + } while (currmsg); + } + else if (strncmp(msg->buf, "status", 6) == 0) { + msgfrom = strstr(msg->buf, "\nStatus message received from "); + if (msgfrom) { + sscanf(msgfrom, "\nStatus message received from %16s\n", sender); + *msgfrom = '\0'; + } + + if (statussenders) { + get_hts(msg->buf, sender, origin, &h, &t, &grouplist, &log, &color, &downcause, NULL, 0, 0); + if (!oksender(statussenders, (h ? h->ip : NULL), msg->addr.sin_addr, msg->buf)) goto done; + } + + get_hts(msg->buf, sender, origin, &h, &t, &grouplist, &log, &color, &downcause, NULL, 1, 1); + if (h && dbgfd && dbghost && (strcasecmp(h->hostname, dbghost) == 0)) { + fprintf(dbgfd, "\n---- status message from %s ----\n%s---- end message ----\n", sender, msg->buf); + fflush(dbgfd); + } + + if (color == COL_PURPLE) { + errprintf("Ignored PURPLE status update from %s for %s.%s\n", + sender, (h ? h->hostname : "<unknown>"), (t ? t->name : "unknown")); + } + else { + if (h && t && log && (color != -1)) { + handle_status(msg->buf, sender, h->hostname, t->name, grouplist, log, color, downcause); + } + } + } + else if (strncmp(msg->buf, "data", 4) == 0) { + char *hostname = NULL, *testname = NULL; + char *bhost, *ehost, *btest; + char savechar; + + msgfrom = strstr(msg->buf, "\nStatus message received from "); + if (msgfrom) { + sscanf(msgfrom, "\nStatus message received from %16s\n", sender); + *msgfrom = '\0'; + } + + bhost = msg->buf + strlen("data"); bhost += strspn(bhost, " \t"); + ehost = bhost + strcspn(bhost, " \t\r\n"); + savechar = *ehost; *ehost = '\0'; + + btest = strrchr(bhost, '.'); + if (btest) { + char *p; + + *btest = '\0'; + hostname = strdup(bhost); + p = hostname; while ((p = strchr(p, ',')) != NULL) *p = '.'; + *btest = '.'; + testname = strdup(btest+1); + + if (*hostname == '\0') { errprintf("Invalid data message from %s - blank hostname\n", sender); xfree(hostname); hostname = NULL; } + if (*testname == '\0') { errprintf("Invalid data message from %s - blank testname\n", sender); xfree(testname); testname = NULL; } + } + else { + errprintf("Invalid data message - no testname in '%s'\n", bhost); + } + + *ehost = savechar; + + if (hostname && testname) { + char *hname, hostip[IP_ADDR_STRLEN]; + + MEMDEFINE(hostip); + + hname = knownhost(hostname, hostip, ghosthandling); + + if (hname == NULL) { + log_ghost(hostname, sender, msg->buf); + } + else if (!oksender(statussenders, hostip, msg->addr.sin_addr, msg->buf)) { + /* Invalid sender */ + errprintf("Invalid data message - sender %s not allowed for host %s\n", sender, hostname); + } + else { + handle_data(msg->buf, sender, origin, hname, testname); + } + + xfree(hostname); xfree(testname); + + MEMUNDEFINE(hostip); + } + } + else if (strncmp(msg->buf, "summary", 7) == 0) { + /* Summaries are always allowed. Or should we ? */ + get_hts(msg->buf, sender, origin, &h, &t, NULL, &log, &color, NULL, NULL, 1, 1); + if (h && t && log && (color != -1)) { + handle_status(msg->buf, sender, h->hostname, t->name, NULL, log, color, NULL); + } + } + else if ((strncmp(msg->buf, "notes", 5) == 0) || (strncmp(msg->buf, "usermsg", 7) == 0)) { + char *hostname, *bhost, *ehost, *p; + char savechar; + + bhost = msg->buf + strcspn(msg->buf, " \t\r\n"); bhost += strspn(bhost, " \t"); + ehost = bhost + strcspn(bhost, " \t\r\n"); + savechar = *ehost; *ehost = '\0'; + hostname = strdup(bhost); + *ehost = savechar; + + p = hostname; while ((p = strchr(p, ',')) != NULL) *p = '.'; + if (*hostname == '\0') { errprintf("Invalid notes/user message from %s - blank hostname\n", sender); xfree(hostname); hostname = NULL; } + + if (hostname) { + char *hname, hostip[IP_ADDR_STRLEN]; + + MEMDEFINE(hostip); + + hname = knownhost(hostname, hostip, ghosthandling); + if (hname == NULL) { + log_ghost(hostname, sender, msg->buf); + } + else { + if (*msg->buf == 'n') { + /* "notes" message */ + if (!oksender(maintsenders, NULL, msg->addr.sin_addr, msg->buf)) { + /* Invalid sender */ + errprintf("Invalid notes message - sender %s not allowed for host %s\n", sender, hostname); + } + else { + handle_notes(msg->buf, sender, hostname); + } + } + else if (*msg->buf == 'u') { + /* "usermsg" message */ + if (!oksender(statussenders, NULL, msg->addr.sin_addr, msg->buf)) { + /* Invalid sender */ + errprintf("Invalid user message - sender %s not allowed for host %s\n", sender, hostname); + } + else { + handle_usermsg(msg->buf, sender, hostname); + } + } + } + + xfree(hostname); + + MEMUNDEFINE(hostip); + } + } + else if (strncmp(msg->buf, "enable", 6) == 0) { + handle_enadis(1, msg, sender); + } + else if (strncmp(msg->buf, "disable", 7) == 0) { + handle_enadis(0, msg, sender); + } + else if (allow_downloads && (strncmp(msg->buf, "config", 6) == 0)) { + char *conffn, *p; + + if (!oksender(statussenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + p = msg->buf + 6; p += strspn(p, " \t"); + p = strtok(p, " \t\r\n"); + conffn = strdup(p); + xfree(msg->buf); + if (conffn && (strstr(conffn, "../") == NULL) && (get_config(conffn, msg) == 0) ) { + msg->doingwhat = RESPONDING; + msg->bufp = msg->buf; + } + } + else if (allow_downloads && (strncmp(msg->buf, "download", 8) == 0)) { + char *fn, *p; + + if (!oksender(statussenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + p = msg->buf + 8; p += strspn(p, " \t"); + p = strtok(p, " \t\r\n"); + fn = strdup(p); + xfree(msg->buf); + if (fn && (strstr(fn, "../") == NULL) && (get_binary(fn, msg) == 0) ) { + msg->doingwhat = RESPONDING; + msg->bufp = msg->buf; + } + } + else if (strncmp(msg->buf, "flush filecache", 15) == 0) { + flush_filecache(); + } + else if (strncmp(msg->buf, "query ", 6) == 0) { + get_hts(msg->buf, sender, origin, &h, &t, NULL, &log, &color, NULL, NULL, 0, 0); + if (!oksender(statussenders, (h ? h->ip : NULL), msg->addr.sin_addr, msg->buf)) goto done; + + if (log) { + xfree(msg->buf); + msg->doingwhat = RESPONDING; + if (log->message) { + unsigned char *bol, *eoln; + int msgcol; + char response[500]; + + bol = msg_data(log->message); + msgcol = parse_color(bol); + if (msgcol != -1) { + /* Skip the color - it may be different in real life */ + bol += strlen(colorname(msgcol)); + bol += strspn(bol, " \t"); + } + eoln = strchr(bol, '\n'); if (eoln) *eoln = '\0'; + snprintf(response, sizeof(response), "%s %s\n", colorname(log->color), bol); + response[sizeof(response)-1] = '\0'; + if (eoln) *eoln = '\n'; + + msg->buf = msg->bufp = strdup(response); + msg->buflen = strlen(msg->buf); + } + else { + msg->buf = msg->bufp = strdup(""); + msg->buflen = 0; + } + } + } + else if (strncmp(msg->buf, "hobbitdlog ", 11) == 0) { + /* + * Request for a single status log + * hobbitdlog HOST.TEST [fields=FIELDLIST] + * + */ + + pcre *spage = NULL, *shost = NULL, *snet = NULL, *stest = NULL; + char *chspage, *chshost, *chsnet, *chstest, *fields = NULL; + int scolor = -1, acklevel = -1; + + if (!oksender(wwwsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + setup_filter(msg->buf, + "hostname,testname,color,flags,lastchange,logtime,validtime,acktime,disabletime,sender,cookie,ackmsg,dismsg,client", + &spage, &shost, &snet, &stest, &scolor, &acklevel, &fields, + &chspage, &chshost, &chsnet, &chstest); + + log = find_log(chshost, chstest, "", &h); + if (log) { + char *buf, *bufp; + int bufsz; + + flush_acklist(log, 0); + if (log->message == NULL) { + errprintf("%s.%s has a NULL message\n", log->host->hostname, log->test->name); + log->message = strdup(""); + } + + bufsz = 1024 + strlen(log->message); + if (log->ackmsg) bufsz += 2*strlen(log->ackmsg); + if (log->dismsg) bufsz += 2*strlen(log->dismsg); + + xfree(msg->buf); + bufp = buf = (char *)malloc(bufsz); + generate_outbuf(&buf, &bufp, &bufsz, h, log, acklevel); + bufp += sprintf(bufp, "%s", msg_data(log->message)); + + msg->doingwhat = RESPONDING; + msg->bufp = msg->buf = buf; + msg->buflen = (bufp - buf); + } + + freeregex(spage); freeregex(shost); freeregex(snet); freeregex(stest); + } + else if (strncmp(msg->buf, "hobbitdxlog ", 12) == 0) { + /* + * Request for a single status log in XML format + * hobbitdxlog HOST.TEST + * + */ + if (!oksender(wwwsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + get_hts(msg->buf, sender, origin, &h, &t, NULL, &log, &color, NULL, NULL, 0, 0); + if (log) { + char *buf, *bufp; + int bufsz, buflen; + hobbitd_meta_t *mwalk; + + flush_acklist(log, 0); + if (log->message == NULL) { + errprintf("%s.%s has a NULL message\n", log->host->hostname, log->test->name); + log->message = strdup(""); + } + + bufsz = 4096 + strlen(log->message); + if (log->ackmsg) bufsz += strlen(log->ackmsg); + if (log->dismsg) bufsz += strlen(log->dismsg); + + xfree(msg->buf); + bufp = buf = (char *)malloc(bufsz); + buflen = 0; + + bufp += sprintf(bufp, "<?xml version='1.0' encoding='ISO-8859-1'?>\n"); + bufp += sprintf(bufp, "<ServerStatus>\n"); + bufp += sprintf(bufp, " <ServerName>%s</ServerName>\n", h->hostname); + bufp += sprintf(bufp, " <Type>%s</Type>\n", log->test->name); + bufp += sprintf(bufp, " <Status>%s</Status>\n", colnames[log->color]); + bufp += sprintf(bufp, " <TestFlags>%s</TestFlags>\n", (log->testflags ? log->testflags : "")); + bufp += sprintf(bufp, " <LastChange>%s</LastChange>\n", timestr(log->lastchange)); + bufp += sprintf(bufp, " <LogTime>%s</LogTime>\n", timestr(log->logtime)); + bufp += sprintf(bufp, " <ValidTime>%s</ValidTime>\n", timestr(log->validtime)); + bufp += sprintf(bufp, " <AckTime>%s</AckTime>\n", timestr(log->acktime)); + bufp += sprintf(bufp, " <DisableTime>%s</DisableTime>\n", timestr(log->enabletime)); + bufp += sprintf(bufp, " <Sender>%s</Sender>\n", log->sender); + + if (log->cookie > 0) + bufp += sprintf(bufp, " <Cookie>%d</Cookie>\n", log->cookie); + else + bufp += sprintf(bufp, " <Cookie>N/A</Cookie>\n"); + + if (log->ackmsg && (log->acktime > now)) + bufp += sprintf(bufp, " <AckMsg><![CDATA[%s]]></AckMsg>\n", log->ackmsg); + else + bufp += sprintf(bufp, " <AckMsg>N/A</AckMsg>\n"); + + if (log->dismsg && (log->enabletime > now)) + bufp += sprintf(bufp, " <DisMsg><![CDATA[%s]]></DisMsg>\n", log->dismsg); + else + bufp += sprintf(bufp, " <DisMsg>N/A</DisMsg>\n"); + + bufp += sprintf(bufp, " <Message><![CDATA[%s]]></Message>\n", msg_data(log->message)); + for (mwalk = log->metas; (mwalk); mwalk = mwalk->next) { + bufp += sprintf(bufp, "<%s>\n%s</%s>\n", + mwalk->metaname->name, mwalk->value, mwalk->metaname->name); + } + bufp += sprintf(bufp, "</ServerStatus>\n"); + + msg->doingwhat = RESPONDING; + msg->bufp = msg->buf = buf; + msg->buflen = (bufp - buf); + } + } + else if (strncmp(msg->buf, "hobbitdboard", 12) == 0) { + /* + * Request for a summmary of all known status logs + * + */ + RbtIterator hosthandle; + hobbitd_hostlist_t *hwalk; + hobbitd_log_t *lwalk, *firstlog; + hobbitd_log_t infologrec, rrdlogrec; + testinfo_t trendstest, infotest; + char *buf, *bufp; + int bufsz; + pcre *spage = NULL, *shost = NULL, *snet = NULL, *stest = NULL; + char *chspage = NULL, *chshost = NULL, *chsnet = NULL, *chstest = NULL; + char *fields = NULL; + int scolor = -1, acklevel = -1; + static unsigned int lastboardsize = 0; + + if (!oksender(wwwsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + setup_filter(msg->buf, + "hostname,testname,color,flags,lastchange,logtime,validtime,acktime,disabletime,sender,cookie,line1", + &spage, &shost, &snet, &stest, &scolor, &acklevel, &fields, + &chspage, &chshost, &chsnet, &chstest); + + if (lastboardsize <= 8192) { + /* A guesstimate - 8 tests per hosts, 1KB/test (only 1st line of msg) */ + bufsz = (hostcount+1)*8*1024; + } + else { + /* Add 10% to the last size we used */ + bufsz = lastboardsize + (lastboardsize / 10); + } + bufp = buf = (char *)malloc(bufsz); + + /* Setup fake log-records for the "info" and "trends" data. */ + memset(&infotest, 0, sizeof(infotest)); + infotest.name = xgetenv("INFOCOLUMN"); + memset(&infologrec, 0, sizeof(infologrec)); + infologrec.test = &infotest; + + memset(&trendstest, 0, sizeof(trendstest)); + trendstest.name = xgetenv("TRENDSCOLUMN"); + memset(&rrdlogrec, 0, sizeof(rrdlogrec)); + rrdlogrec.test = &trendstest; + + infologrec.color = rrdlogrec.color = COL_GREEN; + infologrec.message = rrdlogrec.message = ""; + + for (hosthandle = rbtBegin(rbhosts); (hosthandle != rbtEnd(rbhosts)); hosthandle = rbtNext(rbhosts, hosthandle)) { + hwalk = gettreeitem(rbhosts, hosthandle); + if (!hwalk) { + errprintf("host-tree has a record with no data\n"); + continue; + } + + /* If there is a hostname filter, drop the "summary" 'hosts' */ + if (shost && (hwalk->hosttype != H_NORMAL)) continue; + + firstlog = hwalk->logs; + + if (hwalk->hosttype == H_NORMAL) { + namelist_t *hinfo = hostinfo(hwalk->hostname); + + if (!hinfo) { + errprintf("Hostname '%s' in tree, but no host-info\n", hwalk->hostname); + continue; + } + + /* Host/pagename filter */ + if (!match_host_filter(hinfo, spage, shost, snet)) continue; + + /* Handle NOINFO and NOTRENDS here */ + if (!bbh_item(hinfo, BBH_FLAG_NOINFO)) { + infologrec.next = firstlog; + firstlog = &infologrec; + } + if (!bbh_item(hinfo, BBH_FLAG_NOTRENDS)) { + rrdlogrec.next = firstlog; + firstlog = &rrdlogrec; + } + } + + for (lwalk = firstlog; (lwalk); lwalk = lwalk->next) { + if (!match_test_filter(lwalk, stest, scolor)) continue; + + if (lwalk->message == NULL) { + errprintf("%s.%s has a NULL message\n", lwalk->host->hostname, lwalk->test->name); + lwalk->message = strdup(""); + } + + generate_outbuf(&buf, &bufp, &bufsz, hwalk, lwalk, acklevel); + } + } + *bufp = '\0'; + + xfree(msg->buf); + msg->doingwhat = RESPONDING; + msg->bufp = msg->buf = buf; + msg->buflen = (bufp - buf); + if (msg->buflen > lastboardsize) lastboardsize = msg->buflen; + + freeregex(spage); freeregex(shost); freeregex(snet); freeregex(stest); + } + else if (strncmp(msg->buf, "hobbitdxboard", 13) == 0) { + /* + * Request for a summmary of all known status logs in XML format + * + */ + RbtIterator hosthandle; + hobbitd_hostlist_t *hwalk; + hobbitd_log_t *lwalk; + char *buf, *bufp; + int bufsz; + pcre *spage = NULL, *shost = NULL, *snet = NULL, *stest = NULL; + char *chspage = NULL, *chshost = NULL, *chsnet = NULL, *chstest = NULL; + char *fields = NULL; + int scolor = -1, acklevel = -1; + static unsigned int lastboardsize = 0; + + if (!oksender(wwwsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + setup_filter(msg->buf, + "hostname,testname,color,flags,lastchange,logtime,validtime,acktime,disabletime,sender,cookie,line1", + &spage, &shost, &snet, &stest, &scolor, &acklevel, &fields, + &chspage, &chshost, &chsnet, &chstest); + + if (lastboardsize <= 8192) { + /* A guesstimate - 8 tests per hosts, 2KB/test (only 1st line of msg) */ + bufsz = (hostcount+1)*8*2048; + } + else { + /* Add 10% to the last size we used */ + bufsz = lastboardsize + (lastboardsize / 10); + } + bufp = buf = (char *)malloc(bufsz); + + bufp += sprintf(bufp, "<?xml version='1.0' encoding='ISO-8859-1'?>\n"); + bufp += sprintf(bufp, "<StatusBoard>\n"); + + for (hosthandle = rbtBegin(rbhosts); (hosthandle != rbtEnd(rbhosts)); hosthandle = rbtNext(rbhosts, hosthandle)) { + namelist_t *hinfo; + + hwalk = gettreeitem(rbhosts, hosthandle); + if (!hwalk) { + errprintf("host-tree has a record with no data\n"); + continue; + } + + hinfo = hostinfo(hwalk->hostname); + + /* Host/pagename filter */ + if (!match_host_filter(hinfo, spage, shost, snet)) continue; + + for (lwalk = hwalk->logs; (lwalk); lwalk = lwalk->next) { + char *eoln; + int buflen = (bufp - buf); + + if (!match_test_filter(lwalk, stest, scolor)) continue; + + if (lwalk->message == NULL) { + errprintf("%s.%s has a NULL message\n", lwalk->host->hostname, lwalk->test->name); + lwalk->message = strdup(""); + } + + eoln = strchr(lwalk->message, '\n'); + if (eoln) *eoln = '\0'; + if ((bufsz - buflen - strlen(lwalk->message)) < 4096) { + bufsz += (16384 + strlen(lwalk->message)); + buf = (char *)realloc(buf, bufsz); + bufp = buf + buflen; + } + + bufp += sprintf(bufp, " <ServerStatus>\n"); + bufp += sprintf(bufp, " <ServerName>%s</ServerName>\n", hwalk->hostname); + bufp += sprintf(bufp, " <Type>%s</Type>\n", lwalk->test->name); + bufp += sprintf(bufp, " <Status>%s</Status>\n", colnames[lwalk->color]); + bufp += sprintf(bufp, " <TestFlags>%s</TestFlags>\n", (lwalk->testflags ? lwalk->testflags : "")); + bufp += sprintf(bufp, " <LastChange>%s</LastChange>\n", timestr(lwalk->lastchange)); + bufp += sprintf(bufp, " <LogTime>%s</LogTime>\n", timestr(lwalk->logtime)); + bufp += sprintf(bufp, " <ValidTime>%s</ValidTime>\n", timestr(lwalk->validtime)); + bufp += sprintf(bufp, " <AckTime>%s</AckTime>\n", timestr(lwalk->acktime)); + bufp += sprintf(bufp, " <DisableTime>%s</DisableTime>\n", timestr(lwalk->enabletime)); + bufp += sprintf(bufp, " <Sender>%s</Sender>\n", lwalk->sender); + + if (lwalk->cookie > 0) + bufp += sprintf(bufp, " <Cookie>%d</Cookie>\n", lwalk->cookie); + else + bufp += sprintf(bufp, " <Cookie>N/A</Cookie>\n"); + + bufp += sprintf(bufp, " <MessageSummary><![CDATA[%s]]></MessageSummary>\n", lwalk->message); + bufp += sprintf(bufp, " </ServerStatus>\n"); + if (eoln) *eoln = '\n'; + } + } + bufp += sprintf(bufp, "</StatusBoard>\n"); + + xfree(msg->buf); + msg->doingwhat = RESPONDING; + msg->bufp = msg->buf = buf; + msg->buflen = (bufp - buf); + if (msg->buflen > lastboardsize) lastboardsize = msg->buflen; + + freeregex(spage); freeregex(shost); freeregex(snet); freeregex(stest); + } + else if (strncmp(msg->buf, "hostinfo", 8) == 0) { + /* + * Request for host configuration info + * + */ + namelist_t *hinfo; + char *buf, *bufp; + int bufsz; + pcre *spage = NULL, *shost = NULL, *stest = NULL, *snet = NULL; + char *chspage = NULL, *chshost = NULL, *chsnet = NULL, *chstest = NULL; + char *fields = NULL; + int scolor = -1, acklevel = -1; + static unsigned int lastboardsize = 0; + + if (!oksender(wwwsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + setup_filter(msg->buf, + "BBH_HOSTNAME,BBH_IP,BBH_RAW", + &spage, &shost, &snet, &stest, &scolor, &acklevel, &fields, + &chspage, &chshost, &chsnet, &chstest); + + if (lastboardsize == 0) { + /* A guesstimate - 500 bytes per host */ + bufsz = (hostcount+1)*500; + } + else { + /* Add 10% to the last size we used */ + bufsz = lastboardsize + (lastboardsize / 10); + } + bufp = buf = (char *)malloc(bufsz); + + for (hinfo = first_host(); (hinfo); hinfo = hinfo->next) { + if (!match_host_filter(hinfo, spage, shost, snet)) continue; + generate_hostinfo_outbuf(&buf, &bufp, &bufsz, hinfo); + } + + *bufp = '\0'; + + xfree(msg->buf); + msg->doingwhat = RESPONDING; + msg->bufp = msg->buf = buf; + msg->buflen = (bufp - buf); + if (msg->buflen > lastboardsize) lastboardsize = msg->buflen; + + freeregex(spage); freeregex(shost); freeregex(snet); freeregex(stest); + } + + else if ((strncmp(msg->buf, "hobbitdack", 10) == 0) || (strncmp(msg->buf, "ack ack_event", 13) == 0)) { + /* hobbitdack COOKIE DURATION TEXT */ + char *p; + int cookie, duration; + char durstr[100]; + hobbitd_log_t *lwalk; + + if (!oksender(maintsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + MEMDEFINE(durstr); + + /* + * For just a bit of compatibility with the old BB system, + * we will accept an "ack ack_event" message. This allows us + * to work with existing acknowledgement scripts. + */ + if (strncmp(msg->buf, "hobbitdack", 10) == 0) p = msg->buf + 10; + else if (strncmp(msg->buf, "ack ack_event", 13) == 0) p = msg->buf + 13; + else p = msg->buf; + + if (sscanf(p, "%d %99s", &cookie, durstr) == 2) { + log = find_cookie(abs(cookie)); + if (log) { + duration = durationvalue(durstr); + if (cookie > 0) + handle_ack(p, sender, log, duration); + else { + /* + * Negative cookies mean to ack all pending alerts for + * the host. So loop over the host logs and ack all that + * have a valid cookie (i.e. not -1) + */ + for (lwalk = log->host->logs; (lwalk); lwalk = lwalk->next) { + if (lwalk->cookie != -1) handle_ack(p, sender, lwalk, duration); + } + } + } + else { + errprintf("Cookie %d not found, dropping ack\n", cookie); + } + } + else { + errprintf("Bogus ack message from %s: '%s'\n", sender, msg->buf); + } + + MEMUNDEFINE(durstr); + } + else if (strncmp(msg->buf, "ackinfo ", 8) == 0) { + /* ackinfo HOST.TEST\nlevel\nvaliduntil\nackedby\nmsg */ + int ackall = 0; + + if (!oksender(maintsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + get_hts(msg->buf, sender, origin, &h, &t, NULL, &log, &color, NULL, &ackall, 0, 0); + if (log) { + handle_ackinfo(msg->buf, sender, log); + } + else if (ackall) { + hobbitd_log_t *lwalk; + + for (lwalk = h->logs; (lwalk); lwalk = lwalk->next) { + if (decide_alertstate(lwalk->color) != A_OK) { + handle_ackinfo(msg->buf, sender, lwalk); + } + } + } + } + else if (strncmp(msg->buf, "drop ", 5) == 0) { + char *hostname = NULL, *testname = NULL; + char *p; + + if (!oksender(adminsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + p = msg->buf + 4; p += strspn(p, " \t"); + hostname = strtok(p, " \t"); + if (hostname) testname = strtok(NULL, " \t"); + + if (hostname && !testname) { + handle_dropnrename(CMD_DROPHOST, sender, hostname, NULL, NULL); + } + else if (hostname && testname) { + handle_dropnrename(CMD_DROPTEST, sender, hostname, testname, NULL); + } + } + else if (strncmp(msg->buf, "rename ", 7) == 0) { + char *hostname = NULL, *n1 = NULL, *n2 = NULL; + char *p; + + if (!oksender(adminsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + p = msg->buf + 6; p += strspn(p, " \t"); + hostname = strtok(p, " \t"); + if (hostname) n1 = strtok(NULL, " \t"); + if (n1) n2 = strtok(NULL, " \t"); + + if (hostname && n1 && !n2) { + /* Host rename */ + handle_dropnrename(CMD_RENAMEHOST, sender, hostname, n1, NULL); + } + else if (hostname && n1 && n2) { + /* Test rename */ + handle_dropnrename(CMD_RENAMETEST, sender, hostname, n1, n2); + } + } + else if (strncmp(msg->buf, "dummy", 5) == 0) { + /* Do nothing */ + } + else if (strncmp(msg->buf, "ping", 4) == 0) { + /* Tell them we're here */ + char id[128]; + + sprintf(id, "hobbitd %s\n", VERSION); + msg->doingwhat = RESPONDING; + xfree(msg->buf); + msg->bufp = msg->buf = strdup(id); + msg->buflen = strlen(msg->buf); + } + else if (strncmp(msg->buf, "notify", 6) == 0) { + if (!oksender(maintsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + get_hts(msg->buf, sender, origin, &h, &t, NULL, &log, &color, NULL, NULL, 0, 0); + if (h && t) handle_notify(msg->buf, sender, h->hostname, t->name); + } + else if (strncmp(msg->buf, "schedule", 8) == 0) { + char *cmd; + + /* + * Schedule a later command. This is either + * "schedule" - no params: list the currently scheduled commands. + * "schedule TIME COMMAND": Add a COMMAND to run at TIME. + * "schedule cancel ID": Cancel the scheduled command with id ID. + */ + cmd = msg->buf + 8; cmd += strspn(cmd, " "); + + if (strlen(cmd) == 0) { + char *buf, *bufp; + int bufsz, buflen; + scheduletask_t *swalk; + + bufsz = 4096; + bufp = buf = (char *)malloc(bufsz); + *buf = '\0'; buflen = 0; + + for (swalk = schedulehead; (swalk); swalk = swalk->next) { + int needed = 128 + strlen(swalk->command); + + if ((bufsz - (bufp - buf)) < needed) { + int buflen = (bufp - buf); + bufsz += 4096 + needed; + buf = (char *)realloc(buf, bufsz); + bufp = buf + buflen; + } + + bufp += sprintf(bufp, "%d|%d|%s|%s\n", swalk->id, + (int)swalk->executiontime, swalk->sender, nlencode(swalk->command)); + } + + xfree(msg->buf); + msg->doingwhat = RESPONDING; + msg->bufp = msg->buf = buf; + msg->buflen = (bufp - buf); + } + else { + if (strncmp(cmd, "cancel", 6) != 0) { + scheduletask_t *newitem = (scheduletask_t *)malloc(sizeof(scheduletask_t)); + + newitem->id = nextschedid++; + newitem->executiontime = (time_t) atoi(cmd); + cmd += strspn(cmd, "0123456789"); + cmd += strspn(cmd, " "); + newitem->sender = strdup(sender); + newitem->command = strdup(cmd); + newitem->next = schedulehead; + schedulehead = newitem; + } + else { + scheduletask_t *swalk, *sprev; + int id; + + id = atoi(cmd + 6); + swalk = schedulehead; sprev = NULL; + while (swalk && (swalk->id != id)) { + sprev = swalk; + swalk = swalk->next; + } + + if (swalk) { + xfree(swalk->sender); + xfree(swalk->command); + if (sprev == NULL) { + schedulehead = swalk->next; + } + else { + sprev->next = swalk->next; + } + xfree(swalk); + } + } + } + } + else if (strncmp(msg->buf, "client ", 7) == 0) { + /* "client HOSTNAME.CLIENTOS CLIENTCLASS" */ + char *hostname = NULL, *clientos = NULL, *clientclass = NULL; + char *hname = NULL; + char *line1, *p; + char savech; + + msgfrom = strstr(msg->buf, "\n[proxy]\n"); + if (msgfrom) { + char *ipline = strstr(msgfrom, "\nClientIP:"); + if (ipline) { + sscanf(ipline, "\nClientIP:%16s\n", sender); + } + } + + p = msg->buf + strcspn(msg->buf, "\r\n"); + if ((*p == '\r') || (*p == '\n')) { + savech = *p; + *p = '\0'; + } + else { + p = NULL; + } + line1 = strdup(msg->buf); if (p) *p = savech; + + p = strtok(line1, " \t"); /* Skip the client keyword */ + if (p) hostname = strtok(NULL, " \t"); /* Actually, HOSTNAME.CLIENTOS */ + if (hostname) { + clientos = strrchr(hostname, '.'); + if (clientos) { *clientos = '\0'; clientos++; } + p = hostname; while ((p = strchr(p, ',')) != NULL) *p = '.'; + clientclass = strtok(NULL, " \t"); + } + + if (hostname && clientos) { + char hostip[IP_ADDR_STRLEN]; + + MEMDEFINE(hostip); + + hname = knownhost(hostname, hostip, ghosthandling); + + if (hname == NULL) { + log_ghost(hostname, sender, msg->buf); + } + else if (!oksender(statussenders, hostip, msg->addr.sin_addr, msg->buf)) { + /* Invalid sender */ + errprintf("Invalid client message - sender %s not allowed for host %s\n", sender, hostname); + hname = NULL; + } + else { + namelist_t *hinfo = hostinfo(hname); + + handle_client(msg->buf, sender, hname, clientos, clientclass); + + if (hinfo) { + if (clientos) bbh_set_item(hinfo, BBH_OS, clientos); + if (clientclass) { + /* + * If the client sends an explicit class, + * save it for later use unless there is an + * explicit override (BBH_CLASS is alread set). + */ + char *forcedclass = bbh_item(hinfo, BBH_CLASS); + + if (!forcedclass) + bbh_set_item(hinfo, BBH_CLASS, clientclass); + else + clientclass = forcedclass; + } + } + } + + MEMUNDEFINE(hostip); + } + + if (hname) { + char *cfg; + + cfg = get_clientconfig(hname, clientclass, clientos); + if (cfg) { + msg->doingwhat = RESPONDING; + xfree(msg->buf); + msg->bufp = msg->buf = strdup(cfg); + msg->buflen = strlen(msg->buf); + } + } + + xfree(line1); + } + else if (strncmp(msg->buf, "clientlog ", 10) == 0) { + char *hostname, *p; + RbtIterator hosthandle; + if (!oksender(wwwsenders, NULL, msg->addr.sin_addr, msg->buf)) goto done; + + p = msg->buf + strlen("clientlog"); p += strspn(p, "\t "); + hostname = p; p += strcspn(p, "\t "); if (*p) { *p = '\0'; p++; } + + hosthandle = rbtFind(rbhosts, hostname); + if (hosthandle != rbtEnd(rbhosts)) { + hobbitd_hostlist_t *hwalk; + hwalk = gettreeitem(rbhosts, hosthandle); + + if (hwalk->clientmsg) { + char *sections = NULL; + + if (strncmp(p, "section=", 8) == 0) sections = strdup(p+8); + + xfree(msg->buf); + msg->buf = NULL; + msg->doingwhat = RESPONDING; + + if (!sections) { + msg->bufp = msg->buf = strdup(hwalk->clientmsg); + msg->buflen = strlen(msg->buf); + } + else { + char *onesect; + strbuffer_t *resp; + + onesect = strtok(sections, ","); + resp = newstrbuffer(0); + while (onesect) { + char *sectmarker = (char *)malloc(strlen(onesect) + 4); + char *beginp, *endp; + + sprintf(sectmarker, "\n[%s]", onesect); + beginp = strstr(hwalk->clientmsg, sectmarker); + if (beginp) { + beginp += 1; /* Skip the newline */ + endp = strstr(beginp, "\n["); + if (endp) { endp++; *endp = '\0'; } + addtobuffer(resp, beginp); + if (endp) *endp = '['; + } + + xfree(sectmarker); + onesect = strtok(NULL, ","); + } + + msg->buflen = STRBUFLEN(resp); + msg->buf = grabstrbuffer(resp); + if (!msg->buf) msg->buf = strdup(""); + msg->bufp = msg->buf; + } + } + } + } + else if (strncmp(msg->buf, "ghostlist", 9) == 0) { + if (oksender(wwwsenders, NULL, msg->addr.sin_addr, msg->buf)) { + RbtHandle ghandle; + ghostlist_t *gwalk; + strbuffer_t *resp; + char msgline[1024]; + + resp = newstrbuffer(0); + + for (ghandle = rbtBegin(rbghosts); (ghandle != rbtEnd(rbghosts)); ghandle = rbtNext(rbghosts, ghandle)) { + gwalk = (ghostlist_t *)gettreeitem(rbghosts, ghandle); + snprintf(msgline, sizeof(msgline), "%s|%s|%ld\n", + gwalk->name, gwalk->sender, (long int)gwalk->tstamp); + addtobuffer(resp, msgline); + } + + msg->doingwhat = RESPONDING; + xfree(msg->buf); + msg->buflen = STRBUFLEN(resp); + msg->buf = grabstrbuffer(resp); + if (!msg->buf) msg->buf = strdup(""); + msg->bufp = msg->buf; + } + } + +done: + if (msg->doingwhat == RESPONDING) { + shutdown(msg->sock, SHUT_RD); + } + else { + shutdown(msg->sock, SHUT_RDWR); + close(msg->sock); + msg->sock = -1; + } + + MEMUNDEFINE(sender); + + dbgprintf("<- do_message/%d\n", nesting); + nesting--; +} + + +void save_checkpoint(void) +{ + char *tempfn; + FILE *fd; + RbtIterator hosthandle; + hobbitd_hostlist_t *hwalk; + hobbitd_log_t *lwalk; + time_t now = time(NULL); + scheduletask_t *swalk; + ackinfo_t *awalk; + int iores = 0; + + if (checkpointfn == NULL) return; + + dbgprintf("-> save_checkpoint\n"); + tempfn = malloc(strlen(checkpointfn) + 20); + sprintf(tempfn, "%s.%d", checkpointfn, (int)now); + fd = fopen(tempfn, "w"); + if (fd == NULL) { + errprintf("Cannot open checkpoint file %s : %s\n", tempfn, strerror(errno)); + xfree(tempfn); + return; + } + + for (hosthandle = rbtBegin(rbhosts); ((hosthandle != rbtEnd(rbhosts)) && (iores >= 0)); hosthandle = rbtNext(rbhosts, hosthandle)) { + char *msgstr; + + hwalk = gettreeitem(rbhosts, hosthandle); + + for (lwalk = hwalk->logs; (lwalk); lwalk = lwalk->next) { + if (lwalk->dismsg && (lwalk->enabletime < now) && (lwalk->enabletime != DISABLED_UNTIL_OK)) { + xfree(lwalk->dismsg); + lwalk->dismsg = NULL; + lwalk->enabletime = 0; + } + if (lwalk->ackmsg && (lwalk->acktime < now)) { + xfree(lwalk->ackmsg); + lwalk->ackmsg = NULL; + lwalk->acktime = 0; + } + flush_acklist(lwalk, 0); + iores = fprintf(fd, "@@HOBBITDCHK-V1|%s|%s|%s|%s|%s|%s|%s|%d|%d|%d|%d|%d|%d|%d|%s", + lwalk->origin, hwalk->hostname, lwalk->test->name, lwalk->sender, + colnames[lwalk->color], + (lwalk->testflags ? lwalk->testflags : ""), + colnames[lwalk->oldcolor], + (int)lwalk->logtime, (int) lwalk->lastchange, (int) lwalk->validtime, + (int) lwalk->enabletime, (int) lwalk->acktime, + lwalk->cookie, (int) lwalk->cookieexpires, + nlencode(lwalk->message)); + if (lwalk->dismsg) msgstr = nlencode(lwalk->dismsg); else msgstr = ""; + if (iores >= 0) iores = fprintf(fd, "|%s", msgstr); + if (lwalk->ackmsg) msgstr = nlencode(lwalk->ackmsg); else msgstr = ""; + if (iores >= 0) iores = fprintf(fd, "|%s", msgstr); + if (iores >= 0) iores = fprintf(fd, "\n"); + + for (awalk = lwalk->acklist; (awalk && (iores >= 0)); awalk = awalk->next) { + iores = fprintf(fd, "@@HOBBITDCHK-V1|.acklist.|%s|%s|%d|%d|%d|%d|%s|%s\n", + hwalk->hostname, lwalk->test->name, + (int)awalk->received, (int)awalk->validuntil, (int)awalk->cleartime, + awalk->level, awalk->ackedby, awalk->msg); + } + } + } + + for (swalk = schedulehead; (swalk && (iores >= 0)); swalk = swalk->next) { + iores = fprintf(fd, "@@HOBBITDCHK-V1|.task.|%d|%d|%s|%s\n", + swalk->id, (int)swalk->executiontime, swalk->sender, nlencode(swalk->command)); + } + + if (iores < 0) { + errprintf("I/O error while saving the checkpoint file: %s\n", strerror(errno)); + exit(1); + } + + iores = fclose(fd); + if (iores == EOF) { + errprintf("I/O error while closing the checkpoint file: %s\n", strerror(errno)); + exit(1); + } + + iores = rename(tempfn, checkpointfn); + if (iores == -1) { + errprintf("I/O error while renaming the checkpoint file: %s\n", strerror(errno)); + exit(1); + } + + xfree(tempfn); + dbgprintf("<- save_checkpoint\n"); +} + + +void load_checkpoint(char *fn) +{ + FILE *fd; + strbuffer_t *inbuf; + char *item; + int i, err; + char hostip[IP_ADDR_STRLEN]; + RbtIterator hosthandle, testhandle, originhandle; + hobbitd_hostlist_t *hitem = NULL; + testinfo_t *t = NULL; + char *origin = NULL; + hobbitd_log_t *ltail = NULL; + char *originname, *hostname, *testname, *sender, *testflags, *statusmsg, *disablemsg, *ackmsg; + time_t logtime, lastchange, validtime, enabletime, acktime, cookieexpires; + int color = COL_GREEN, oldcolor = COL_GREEN, cookie; + int count = 0; + + fd = fopen(fn, "r"); + if (fd == NULL) { + errprintf("Cannot access checkpoint file %s for restore\n", fn); + return; + } + + MEMDEFINE(hostip); + + inbuf = newstrbuffer(0); + initfgets(fd); + while (unlimfgets(inbuf, fd)) { + originname = hostname = testname = sender = testflags = statusmsg = disablemsg = ackmsg = NULL; + logtime = lastchange = validtime = enabletime = acktime = cookieexpires = 0; + cookie = -1; + err = 0; + + if (strncmp(STRBUF(inbuf), "@@HOBBITDCHK-V1|.task.|", 23) == 0) { + scheduletask_t *newtask = (scheduletask_t *)calloc(1, sizeof(scheduletask_t)); + + item = gettok(STRBUF(inbuf), "|\n"); i = 0; + while (item && !err) { + switch (i) { + case 0: break; + case 1: break; + case 2: newtask->id = atoi(item); break; + case 3: newtask->executiontime = (time_t) atoi(item); break; + case 4: newtask->sender = strdup(item); break; + case 5: nldecode(item); newtask->command = strdup(item); break; + default: break; + } + item = gettok(NULL, "|\n"); i++; + } + + if (newtask->id && (newtask->executiontime > time(NULL)) && newtask->sender && newtask->command) { + newtask->next = schedulehead; + schedulehead = newtask; + } + else { + if (newtask->sender) xfree(newtask->sender); + if (newtask->command) xfree(newtask->command); + xfree(newtask); + } + + continue; + } + + if (strncmp(STRBUF(inbuf), "@@HOBBITDCHK-V1|.acklist.|", 26) == 0) { + hobbitd_log_t *log = NULL; + ackinfo_t *newack = (ackinfo_t *)calloc(1, sizeof(ackinfo_t)); + + hitem = NULL; + + item = gettok(STRBUF(inbuf), "|\n"); i = 0; + while (item) { + + switch (i) { + case 0: break; + case 1: break; + case 2: + hosthandle = rbtFind(rbhosts, item); + hitem = gettreeitem(rbhosts, hosthandle); + break; + case 3: + testhandle = rbtFind(rbtests, item); + t = (testhandle == rbtEnd(rbtests)) ? NULL : gettreeitem(rbtests, testhandle); + break; + case 4: newack->received = atoi(item); break; + case 5: newack->validuntil = atoi(item); break; + case 6: newack->cleartime = atoi(item); break; + case 7: newack->level = atoi(item); break; + case 8: newack->ackedby = strdup(item); break; + case 9: newack->msg = strdup(item); break; + default: break; + } + item = gettok(NULL, "|\n"); i++; + } + + if (hitem && t) { + for (log = hitem->logs; (log && (log->test != t)); log = log->next) ; + } + + if (log && newack->msg) { + newack->next = log->acklist; + log->acklist = newack; + } + else { + if (newack->ackedby) xfree(newack->ackedby); + if (newack->msg) xfree(newack->msg); + xfree(newack); + } + + continue; + } + + if (strncmp(STRBUF(inbuf), "@@HOBBITDCHK-V1|.", 17) == 0) continue; + + item = gettok(STRBUF(inbuf), "|\n"); i = 0; + while (item && !err) { + switch (i) { + case 0: err = ((strcmp(item, "@@HOBBITDCHK-V1") != 0) && (strcmp(item, "@@BBGENDCHK-V1") != 0)); break; + case 1: originname = item; break; + case 2: if (strlen(item)) hostname = item; else err=1; break; + case 3: if (strlen(item)) testname = item; else err=1; break; + case 4: sender = item; break; + case 5: color = parse_color(item); if (color == -1) err = 1; break; + case 6: testflags = item; break; + case 7: oldcolor = parse_color(item); if (oldcolor == -1) oldcolor = NO_COLOR; break; + case 8: logtime = atoi(item); break; + case 9: lastchange = atoi(item); break; + case 10: validtime = atoi(item); break; + case 11: enabletime = atoi(item); break; + case 12: acktime = atoi(item); break; + case 13: cookie = atoi(item); break; + case 14: cookieexpires = atoi(item); break; + case 15: if (strlen(item)) statusmsg = item; else err=1; break; + case 16: disablemsg = item; break; + case 17: ackmsg = item; break; + default: err = 1; + } + + item = gettok(NULL, "|\n"); i++; + } + + if (i < 17) { + errprintf("Too few fields in record - found %d, expected 17\n", i); + err = 1; + } + + if (err) continue; + + /* Only load hosts we know; they may have been dropped while we were offline */ + hostname = knownhost(hostname, hostip, ghosthandling); + if (hostname == NULL) continue; + + /* Ignore the "info" and "trends" data, since we generate on the fly now. */ + if (strcmp(testname, xgetenv("INFOCOLUMN")) == 0) continue; + if (strcmp(testname, xgetenv("TRENDSCOLUMN")) == 0) continue; + + dbgprintf("Status: Host=%s, test=%s\n", hostname, testname); count++; + + hosthandle = rbtFind(rbhosts, hostname); + if (hosthandle == rbtEnd(rbhosts)) { + /* New host */ + hitem = create_hostlist_t(hostname, hostip); + hostcount++; + } + else { + hitem = gettreeitem(rbhosts, hosthandle); + } + + testhandle = rbtFind(rbtests, testname); + if (testhandle == rbtEnd(rbtests)) { + t = create_testinfo(testname); + } + else t = gettreeitem(rbtests, testhandle); + + originhandle = rbtFind(rborigins, originname); + if (originhandle == rbtEnd(rborigins)) { + origin = strdup(originname); + rbtInsert(rborigins, origin, origin); + } + else origin = gettreeitem(rborigins, originhandle); + + if (hitem->logs == NULL) { + ltail = hitem->logs = (hobbitd_log_t *) calloc(1, sizeof(hobbitd_log_t)); + } + else { + ltail->next = (hobbitd_log_t *)calloc(1, sizeof(hobbitd_log_t)); + ltail = ltail->next; + } + + if (strcmp(testname, xgetenv("PINGCOLUMN")) == 0) hitem->pinglog = ltail; + + /* Fixup validtime in case of ack'ed or disabled tests */ + if (validtime < acktime) validtime = acktime; + if (validtime < enabletime) validtime = enabletime; + + ltail->test = t; + ltail->host = hitem; + ltail->origin = origin; + ltail->color = color; + ltail->oldcolor = oldcolor; + ltail->activealert = (decide_alertstate(color) == A_ALERT); + ltail->histsynced = 0; + ltail->testflags = ( (testflags && strlen(testflags)) ? strdup(testflags) : NULL); + strcpy(ltail->sender, sender); + ltail->logtime = logtime; + ltail->lastchange = lastchange; + ltail->validtime = validtime; + ltail->enabletime = enabletime; + if (ltail->enabletime == DISABLED_UNTIL_OK) ltail->validtime = INT_MAX; + ltail->acktime = acktime; + nldecode(statusmsg); + ltail->message = strdup(statusmsg); + ltail->msgsz = strlen(statusmsg)+1; + if (disablemsg && strlen(disablemsg)) { + nldecode(disablemsg); + ltail->dismsg = strdup(disablemsg); + } + else + ltail->dismsg = NULL; + if (ackmsg && strlen(ackmsg)) { + nldecode(ackmsg); + ltail->ackmsg = strdup(ackmsg); + } + else + ltail->ackmsg = NULL; + ltail->cookie = cookie; + if (cookie > 0) rbtInsert(rbcookies, (void *)cookie, ltail); + ltail->cookieexpires = cookieexpires; + ltail->metas = NULL; + ltail->acklist = NULL; + ltail->next = NULL; + } + + fclose(fd); + freestrbuffer(inbuf); + dbgprintf("Loaded %d status logs\n", count); + + MEMDEFINE(hostip); +} + + +void check_purple_status(void) +{ + RbtIterator hosthandle; + hobbitd_hostlist_t *hwalk; + hobbitd_log_t *lwalk; + time_t now = time(NULL); + + dbgprintf("-> check_purple_status\n"); + for (hosthandle = rbtBegin(rbhosts); (hosthandle != rbtEnd(rbhosts)); hosthandle = rbtNext(rbhosts, hosthandle)) { + hwalk = gettreeitem(rbhosts, hosthandle); + + lwalk = hwalk->logs; + while (lwalk) { + if (lwalk->validtime < now) { + dbgprintf("Purple log from %s %s\n", hwalk->hostname, lwalk->test->name); + if (hwalk->hosttype == H_SUMMARY) { + /* + * A summary has gone stale. Drop it. + */ + hobbitd_log_t *tmp; + + if (lwalk == hwalk->logs) { + tmp = hwalk->logs; + hwalk->logs = lwalk->next; + lwalk = lwalk->next; + } + else { + for (tmp = hwalk->logs; (tmp->next != lwalk); tmp = tmp->next); + tmp->next = lwalk->next; + tmp = lwalk; + lwalk = lwalk->next; + } + free_log_t(tmp); + } + else { + int newcolor = COL_PURPLE; + + /* + * See if this is a host where the "conn" test shows it is down. + * If yes, then go CLEAR, instead of PURPLE. + */ + if (hwalk->pinglog) { + switch (hwalk->pinglog->color) { + case COL_RED: + case COL_YELLOW: + case COL_BLUE: + case COL_CLEAR: /* if the "route:" tag is involved */ + newcolor = COL_CLEAR; + break; + + default: + newcolor = COL_PURPLE; + break; + } + } + + /* Tests on dialup hosts go clear, not purple */ + if (newcolor == COL_PURPLE) { + namelist_t *hinfo = hostinfo(hwalk->hostname); + if (hinfo && bbh_item(hinfo, BBH_FLAG_DIALUP)) newcolor = COL_CLEAR; + } + + handle_status(lwalk->message, "hobbitd", + hwalk->hostname, lwalk->test->name, lwalk->grouplist, lwalk, newcolor, NULL); + lwalk = lwalk->next; + } + } + else { + lwalk = lwalk->next; + } + } + } + dbgprintf("<- check_purple_status\n"); +} + +void sig_handler(int signum) +{ + switch (signum) { + case SIGCHLD: + break; + + case SIGALRM: + gotalarm = 1; + break; + + case SIGTERM: + case SIGINT: + running = 0; + break; + + case SIGHUP: + reloadconfig = 1; + dologswitch = 1; + break; + + case SIGUSR1: + nextcheckpoint = 0; + break; + } +} + + +int main(int argc, char *argv[]) +{ + conn_t *connhead = NULL, *conntail=NULL; + char *listenip = "0.0.0.0"; + int listenport = 0; + char *bbhostsfn = NULL; + char *restartfn = NULL; + char *logfn = NULL; + int checkpointinterval = 900; + int do_purples = 1; + time_t nextpurpleupdate; + struct sockaddr_in laddr; + int lsocket, opt; + int listenq = 512; + int argi; + struct timeval tv; + struct timezone tz; + int daemonize = 0; + char *pidfile = NULL; + struct sigaction sa; + time_t conn_timeout = 30; + char *envarea = NULL; + + MEMDEFINE(colnames); + + boottime = time(NULL); + + /* Create our trees */ + rbhosts = rbtNew(name_compare); + rbtests = rbtNew(name_compare); + rborigins = rbtNew(name_compare); + rbcookies = rbtNew(int_compare); + rbfilecache = rbtNew(name_compare); + rbghosts = rbtNew(name_compare); + + /* For wildcard notify's */ + create_testinfo("*"); + + colnames[COL_GREEN] = "green"; + colnames[COL_YELLOW] = "yellow"; + colnames[COL_RED] = "red"; + colnames[COL_CLEAR] = "clear"; + colnames[COL_BLUE] = "blue"; + colnames[COL_PURPLE] = "purple"; + colnames[NO_COLOR] = "none"; + gettimeofday(&tv, &tz); + srandom(tv.tv_usec); + + /* Load alert config */ + alertcolors = colorset(xgetenv("ALERTCOLORS"), ((1 << COL_GREEN) | (1 << COL_BLUE))); + okcolors = colorset(xgetenv("OKCOLORS"), (1 << COL_RED)); + + for (argi=1; (argi < argc); argi++) { + if (argnmatch(argv[argi], "--debug")) { + debug = 1; + } + else if (argnmatch(argv[argi], "--listen=")) { + char *p = strchr(argv[argi], '=') + 1; + + listenip = strdup(p); + p = strchr(listenip, ':'); + if (p) { + *p = '\0'; + listenport = atoi(p+1); + } + } + else if (argnmatch(argv[argi], "--timeout=")) { + char *p = strchr(argv[argi], '=') + 1; + int newconn_timeout = atoi(p); + if ((newconn_timeout < 5) || (newconn_timeout > 60)) + errprintf("--timeout must be between 5 and 60\n"); + else + conn_timeout = newconn_timeout; + } + else if (argnmatch(argv[argi], "--bbhosts=")) { + char *p = strchr(argv[argi], '=') + 1; + bbhostsfn = strdup(p); + } + else if (argnmatch(argv[argi], "--checkpoint-file=")) { + char *p = strchr(argv[argi], '=') + 1; + checkpointfn = strdup(p); + } + else if (argnmatch(argv[argi], "--checkpoint-interval=")) { + char *p = strchr(argv[argi], '=') + 1; + checkpointinterval = atoi(p); + } + else if (argnmatch(argv[argi], "--restart=")) { + char *p = strchr(argv[argi], '=') + 1; + restartfn = strdup(p); + } + else if (argnmatch(argv[argi], "--ghosts=")) { + char *p = strchr(argv[argi], '=') + 1; + + if (strcmp(p, "allow") == 0) ghosthandling = 0; + else if (strcmp(p, "drop") == 0) ghosthandling = 1; + else if (strcmp(p, "log") == 0) ghosthandling = 2; + } + else if (argnmatch(argv[argi], "--no-purple")) { + do_purples = 0; + } + else if (argnmatch(argv[argi], "--daemon")) { + daemonize = 1; + } + else if (argnmatch(argv[argi], "--no-daemon")) { + daemonize = 0; + } + else if (argnmatch(argv[argi], "--pidfile=")) { + char *p = strchr(argv[argi], '='); + pidfile = strdup(p+1); + } + else if (argnmatch(argv[argi], "--log=")) { + char *p = strchr(argv[argi], '='); + logfn = strdup(p+1); + } + else if (argnmatch(argv[argi], "--ack-log=")) { + char *p = strchr(argv[argi], '='); + ackinfologfn = strdup(p+1); + } + else if (argnmatch(argv[argi], "--maint-senders=")) { + /* Who is allowed to send us "enable", "disable", "ack", "notes" messages */ + char *p = strchr(argv[argi], '='); + maintsenders = getsenderlist(p+1); + } + else if (argnmatch(argv[argi], "--status-senders=")) { + /* Who is allowed to send us "status", "combo", "summary", "data" messages */ + char *p = strchr(argv[argi], '='); + statussenders = getsenderlist(p+1); + } + else if (argnmatch(argv[argi], "--admin-senders=")) { + /* Who is allowed to send us "drop", "rename", "config", "query" messages */ + char *p = strchr(argv[argi], '='); + adminsenders = getsenderlist(p+1); + } + else if (argnmatch(argv[argi], "--www-senders=")) { + /* Who is allowed to send us "hobbitdboard", "hobbitdlog" messages */ + char *p = strchr(argv[argi], '='); + wwwsenders = getsenderlist(p+1); + } + else if (argnmatch(argv[argi], "--dbghost=")) { + char *p = strchr(argv[argi], '='); + + dbghost = strdup(p+1); + } + else if (argnmatch(argv[argi], "--env=")) { + char *p = strchr(argv[argi], '='); + loadenv(p+1, envarea); + } + else if (argnmatch(argv[argi], "--area=")) { + char *p = strchr(argv[argi], '='); + envarea = strdup(p+1); + } + else if (argnmatch(argv[argi], "--trace=")) { + char *p = strchr(argv[argi], '='); + tracelist = getsenderlist(p+1); + } + else if (strcmp(argv[argi], "--trace-all") == 0) { + traceall = 1; + } + else if (strcmp(argv[argi], "--ignore-traced") == 0) { + ignoretraced = 1; + } + else if (strcmp(argv[argi], "--no-clientlog") == 0) { + clientsavemem = 0; + clientsavedisk = 0; + } + else if (argnmatch(argv[argi], "--store-clientlogs")) { + int anypos = 0, anyneg = 0; + char *val = strchr(argv[argi], '='); + + if (!val) { + clientsavedisk = 1; + } + else { + char *tok; + + val = strdup(val+1); + tok = strtok(val, ","); + while (tok) { + testinfo_t *t; + + if (*tok == '!') { + anyneg = 1; + t = create_testinfo(tok+1); + t->clientsave = 0; + } + else { + anypos = 1; + t = create_testinfo(tok); + t->clientsave = 1; + } + + tok = strtok(NULL, ","); + } + xfree(val); + + /* + * If only positive testnames listed, let default + * be NOT to save, and vice versa. If mixed, + * warn about it. + */ + if (anypos && !anyneg) clientsavedisk = 0; + else if (anyneg && !anypos) clientsavedisk = 1; + else { + errprintf("Mixed list of testnames for --store-clientlogs option, will only save listed tests.\n"); + clientsavedisk = 0; + } + + } + + if (anypos || clientsavedisk) clientsavemem = 1; + } + else if (strcmp(argv[argi], "--no-download") == 0) { + allow_downloads = 0; + } + else if (argnmatch(argv[argi], "--help")) { + printf("Options:\n"); + printf("\t--listen=IP:PORT : The address the daemon listens on\n"); + printf("\t--bbhosts=FILENAME : The bb-hosts file\n"); + printf("\t--ghosts=allow|drop|log : How to handle unknown hosts\n"); + return 1; + } + else { + errprintf("Unknown option '%s' - ignored\n", argv[argi]); + } + } + + if (xgetenv("BBHOSTS") && (bbhostsfn == NULL)) { + bbhostsfn = strdup(xgetenv("BBHOSTS")); + } + + if (listenport == 0) { + if (xgetenv("BBPORT")) + listenport = atoi(xgetenv("BBPORT")); + else + listenport = 1984; + } + + if (ghosthandling == -1) { + if (xgetenv("BBGHOSTS")) ghosthandling = atoi(xgetenv("BBGHOSTS")); + else ghosthandling = 0; + } + + if (ghosthandling && (bbhostsfn == NULL)) { + errprintf("No bb-hosts file specified, required when using ghosthandling\n"); + exit(1); + } + + errprintf("Loading hostnames\n"); + load_hostnames(bbhostsfn, NULL, get_fqdn()); + load_clientconfig(); + + if (restartfn) { + errprintf("Loading saved state\n"); + load_checkpoint(restartfn); + } + + nextcheckpoint = time(NULL) + checkpointinterval; + nextpurpleupdate = time(NULL) + 600; /* Wait 10 minutes the first time */ + last_stats_time = time(NULL); /* delay sending of the first status report until we're fully running */ + + + /* Set up a socket to listen for new connections */ + errprintf("Setting up network listener on %s:%d\n", listenip, listenport); + memset(&laddr, 0, sizeof(laddr)); + inet_aton(listenip, (struct in_addr *) &laddr.sin_addr.s_addr); + laddr.sin_port = htons(listenport); + laddr.sin_family = AF_INET; + lsocket = socket(AF_INET, SOCK_STREAM, 0); + if (lsocket == -1) { + errprintf("Cannot create listen socket (%s)\n", strerror(errno)); + return 1; + } + opt = 1; + setsockopt(lsocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + fcntl(lsocket, F_SETFL, O_NONBLOCK); + if (bind(lsocket, (struct sockaddr *)&laddr, sizeof(laddr)) == -1) { + errprintf("Cannot bind to listen socket (%s)\n", strerror(errno)); + return 1; + } + if (listen(lsocket, listenq) == -1) { + errprintf("Cannot listen (%s)\n", strerror(errno)); + return 1; + } + + /* Go daemon */ + if (daemonize) { + pid_t childpid; + + /* Become a daemon */ + childpid = fork(); + if (childpid < 0) { + /* Fork failed */ + errprintf("Could not fork\n"); + exit(1); + } + else if (childpid > 0) { + /* Parent just exits */ + exit(0); + } + + /* Child (daemon) continues here */ + setsid(); + } + + if (pidfile == NULL) { + /* Setup a default pid-file */ + char fn[PATH_MAX]; + + sprintf(fn, "%s/hobbitd.pid", xgetenv("BBSERVERLOGS")); + pidfile = strdup(fn); + } + + /* Save PID */ + { + FILE *fd = fopen(pidfile, "w"); + if (fd) { + if (fprintf(fd, "%d\n", (int)getpid()) <= 0) { + errprintf("Error writing PID file %s: %s\n", pidfile, strerror(errno)); + } + fclose(fd); + } + else { + errprintf("Cannot open PID file %s: %s\n", pidfile, strerror(errno)); + } + } + + errprintf("Setting up signal handlers\n"); + setup_signalhandler("hobbitd"); + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_handler; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGALRM, &sa, NULL); + + errprintf("Setting up hobbitd channels\n"); + statuschn = setup_channel(C_STATUS, CHAN_MASTER); + if (statuschn == NULL) { errprintf("Cannot setup status channel\n"); return 1; } + stachgchn = setup_channel(C_STACHG, CHAN_MASTER); + if (stachgchn == NULL) { errprintf("Cannot setup stachg channel\n"); return 1; } + pagechn = setup_channel(C_PAGE, CHAN_MASTER); + if (pagechn == NULL) { errprintf("Cannot setup page channel\n"); return 1; } + datachn = setup_channel(C_DATA, CHAN_MASTER); + if (datachn == NULL) { errprintf("Cannot setup data channel\n"); return 1; } + noteschn = setup_channel(C_NOTES, CHAN_MASTER); + if (noteschn == NULL) { errprintf("Cannot setup notes channel\n"); return 1; } + enadischn = setup_channel(C_ENADIS, CHAN_MASTER); + if (enadischn == NULL) { errprintf("Cannot setup enadis channel\n"); return 1; } + clientchn = setup_channel(C_CLIENT, CHAN_MASTER); + if (clientchn == NULL) { errprintf("Cannot setup client channel\n"); return 1; } + clichgchn = setup_channel(C_CLICHG, CHAN_MASTER); + if (clichgchn == NULL) { errprintf("Cannot setup clichg channel\n"); return 1; } + userchn = setup_channel(C_USER, CHAN_MASTER); + if (userchn == NULL) { errprintf("Cannot setup user channel\n"); return 1; } + + errprintf("Setting up logfiles\n"); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + freopen("/dev/null", "r", stdin); + if (logfn) { + freopen(logfn, "a", stdout); + freopen(logfn, "a", stderr); + } + + if (ackinfologfn) { + ackinfologfd = fopen(ackinfologfn, "a"); + if (ackinfologfd == NULL) { + errprintf("Cannot open ack logfile %s: %s\n", ackinfologfn, strerror(errno)); + } + } + + if (dbghost) { + char fname[PATH_MAX]; + + sprintf(fname, "%s/hobbitd.dbg", xgetenv("BBTMP")); + dbgfd = fopen(fname, "a"); + if (dbgfd == NULL) errprintf("Cannot open debug file %s: %s\n", fname, strerror(errno)); + } + + errprintf("Setup complete\n"); + do { + /* + * The endless loop. + * + * First attend to the housekeeping chores: + * - send out our heartbeat signal; + * - pick up children to avoid zombies; + * - rotate logs, if we have been asked to; + * - re-load the bb-hosts configuration if needed; + * - check for stale status-logs that must go purple; + * - inject our own statistics message. + * - save the checkpoint file; + * + * Then do the network I/O. + */ + struct timeval seltmo; + fd_set fdread, fdwrite; + int maxfd, n; + conn_t *cwalk; + time_t now = time(NULL); + int childstat; + + /* Pickup any finished child processes to avoid zombies */ + while (wait3(&childstat, WNOHANG, NULL) > 0) ; + + if (logfn && dologswitch) { + freopen(logfn, "a", stdout); + freopen(logfn, "a", stderr); + if (ackinfologfd) freopen(ackinfologfn, "a", ackinfologfd); + dologswitch = 0; + posttochannel(statuschn, "logrotate", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(stachgchn, "logrotate", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(pagechn, "logrotate", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(datachn, "logrotate", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(noteschn, "logrotate", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(enadischn, "logrotate", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(clientchn, "logrotate", NULL, "hobbitd", NULL, NULL, ""); + } + + if (reloadconfig && bbhostsfn) { + RbtIterator hosthandle; + + reloadconfig = 0; + load_hostnames(bbhostsfn, NULL, get_fqdn()); + + /* Scan our list of hosts and weed out those we do not know about any more */ + hosthandle = rbtBegin(rbhosts); + while (hosthandle != rbtEnd(rbhosts)) { + hobbitd_hostlist_t *hwalk; + + hwalk = gettreeitem(rbhosts, hosthandle); + + if (hwalk->hosttype == H_SUMMARY) { + /* Leave the summaries as-is */ + hosthandle = rbtNext(rbhosts, hosthandle); + } + else if (hostinfo(hwalk->hostname) == NULL) { + /* Remove all state info about this host. This will NOT remove files. */ + handle_dropnrename(CMD_DROPSTATE, "hobbitd", hwalk->hostname, NULL, NULL); + + /* Must restart tree-walk after deleting node from the tree */ + hosthandle = rbtBegin(rbhosts); + } + else { + hosthandle = rbtNext(rbhosts, hosthandle); + } + } + + load_clientconfig(); + } + + if (do_purples && (now > nextpurpleupdate)) { + nextpurpleupdate = time(NULL) + 60; + check_purple_status(); + } + + if ((last_stats_time + 300) <= now) { + char *buf; + hobbitd_hostlist_t *h; + testinfo_t *t; + hobbitd_log_t *log; + int color; + + buf = generate_stats(); + get_hts(buf, "hobbitd", "", &h, &t, NULL, &log, &color, NULL, NULL, 1, 1); + if (!h || !t || !log) { + errprintf("hobbitd servername MACHINE='%s' not listed in bb-hosts, dropping hobbitd status\n", + xgetenv("MACHINE")); + } + else { + handle_status(buf, "hobbitd", h->hostname, t->name, NULL, log, color, NULL); + } + last_stats_time = now; + flush_errbuf(); + } + + if (now > nextcheckpoint) { + pid_t childpid; + + reloadconfig = 1; + nextcheckpoint = now + checkpointinterval; + childpid = fork(); + if (childpid == -1) { + errprintf("Could not fork checkpoint child:%s\n", strerror(errno)); + } + else if (childpid == 0) { + save_checkpoint(); + exit(0); + } + } + + /* + * Prepare for the network I/O. + * Find the largest open socket we have, from our active sockets, + * and setup the select() FD sets. + */ + FD_ZERO(&fdread); FD_ZERO(&fdwrite); + FD_SET(lsocket, &fdread); maxfd = lsocket; + + for (cwalk = connhead; (cwalk); cwalk = cwalk->next) { + switch (cwalk->doingwhat) { + case RECEIVING: + FD_SET(cwalk->sock, &fdread); + if (cwalk->sock > maxfd) maxfd = cwalk->sock; + break; + case RESPONDING: + FD_SET(cwalk->sock, &fdwrite); + if (cwalk->sock > maxfd) maxfd = cwalk->sock; + break; + } + } + + /* + * Do the select() with a static 2 second timeout. + * This is long enough that we will suspend activity for + * some time if there's nothing to do, but short enough for + * us to attend to the housekeeping stuff without undue delay. + */ + seltmo.tv_sec = 2; seltmo.tv_usec = 0; + n = select(maxfd+1, &fdread, &fdwrite, NULL, &seltmo); + if (n <= 0) { + if ((errno == EINTR) || (n == 0)) { + /* Interrupted or a timeout happened */ + continue; + } + else { + errprintf("Fatal error in select: %s\n", strerror(errno)); + break; + } + } + + /* + * Now do the actual data exchange over the net. + */ + for (cwalk = connhead; (cwalk); cwalk = cwalk->next) { + switch (cwalk->doingwhat) { + case RECEIVING: + if (FD_ISSET(cwalk->sock, &fdread)) { + n = read(cwalk->sock, cwalk->bufp, (cwalk->bufsz - cwalk->buflen - 1)); + if (n <= 0) { + /* End of input data on this connection */ + *(cwalk->bufp) = '\0'; + + /* FIXME - need to set origin here */ + do_message(cwalk, ""); + } + else { + /* Add data to the input buffer - within reason ... */ + cwalk->bufp += n; + cwalk->buflen += n; + *(cwalk->bufp) = '\0'; + if ((cwalk->bufsz - cwalk->buflen) < 2048) { + if (cwalk->bufsz < MAX_HOBBIT_INBUFSZ) { + cwalk->bufsz += HOBBIT_INBUF_INCREMENT; + cwalk->buf = (unsigned char *) realloc(cwalk->buf, cwalk->bufsz); + cwalk->bufp = cwalk->buf + cwalk->buflen; + } + else { + /* Someone is flooding us */ + errprintf("Data flooding from %s, closing connection\n", + inet_ntoa(cwalk->addr.sin_addr)); + shutdown(cwalk->sock, SHUT_RDWR); + close(cwalk->sock); + cwalk->sock = -1; + cwalk->doingwhat = NOTALK; + } + } + } + } + break; + + case RESPONDING: + if (FD_ISSET(cwalk->sock, &fdwrite)) { + n = write(cwalk->sock, cwalk->bufp, cwalk->buflen); + + if (n < 0) { + cwalk->buflen = 0; + } + else { + cwalk->bufp += n; + cwalk->buflen -= n; + } + + if (cwalk->buflen == 0) { + shutdown(cwalk->sock, SHUT_WR); + close(cwalk->sock); + cwalk->sock = -1; + cwalk->doingwhat = NOTALK; + } + } + break; + } + } + + /* Any scheduled tasks that need attending to? */ + { + scheduletask_t *swalk, *sprev; + + swalk = schedulehead; sprev = NULL; + while (swalk) { + if (swalk->executiontime <= now) { + scheduletask_t *runtask = swalk; + conn_t task; + + /* Unlink the entry */ + if (sprev == NULL) + schedulehead = swalk->next; + else + sprev->next = swalk->next; + swalk = swalk->next; + + memset(&task, 0, sizeof(task)); + inet_aton(runtask->sender, (struct in_addr *) &task.addr.sin_addr.s_addr); + task.buf = task.bufp = runtask->command; + task.buflen = strlen(runtask->command); task.bufsz = task.buflen+1; + do_message(&task, ""); + + errprintf("Ran scheduled task %d from %s: %s\n", + runtask->id, runtask->sender, runtask->command); + xfree(runtask->sender); xfree(runtask->command); xfree(runtask); + } + else { + sprev = swalk; + swalk = swalk->next; + } + } + } + + /* Clean up conn structs that are no longer used */ + { + conn_t *tmp, *khead; + + now = time(NULL); + khead = NULL; cwalk = connhead; + while (cwalk) { + /* Check for connections that timeout */ + if (now > cwalk->timeout) { + update_statistics(""); + cwalk->doingwhat = NOTALK; + if (cwalk->sock >= 0) { + shutdown(cwalk->sock, SHUT_RDWR); + close(cwalk->sock); + cwalk->sock = -1; + } + } + + /* Move dead connections to a purge-list */ + if ((cwalk == connhead) && (cwalk->doingwhat == NOTALK)) { + /* head of chain is dead */ + tmp = connhead; + connhead = connhead->next; + tmp->next = khead; + khead = tmp; + + cwalk = connhead; + } + else if (cwalk->next && (cwalk->next->doingwhat == NOTALK)) { + tmp = cwalk->next; + cwalk->next = tmp->next; + tmp->next = khead; + khead = tmp; + + /* cwalk is unchanged */ + } + else { + cwalk = cwalk->next; + } + } + if (connhead == NULL) { + conntail = NULL; + } + else { + conntail = connhead; + cwalk = connhead->next; + if (cwalk) { + while (cwalk->next) cwalk = cwalk->next; + conntail = cwalk; + } + } + + /* Purge the dead connections */ + while (khead) { + tmp = khead; + khead = khead->next; + + if (tmp->buf) xfree(tmp->buf); + xfree(tmp); + } + } + + /* Pick up new connections */ + if (FD_ISSET(lsocket, &fdread)) { + struct sockaddr_in addr; + int addrsz = sizeof(addr); + int sock = accept(lsocket, (struct sockaddr *)&addr, &addrsz); + + if (sock >= 0) { + if (connhead == NULL) { + connhead = conntail = (conn_t *)malloc(sizeof(conn_t)); + } + else { + conntail->next = (conn_t *)malloc(sizeof(conn_t)); + conntail = conntail->next; + } + + conntail->sock = sock; + memcpy(&conntail->addr, &addr, sizeof(conntail->addr)); + conntail->doingwhat = RECEIVING; + conntail->bufsz = HOBBIT_INBUF_INITIAL; + conntail->buf = (unsigned char *)malloc(conntail->bufsz); + conntail->bufp = conntail->buf; + conntail->buflen = 0; + conntail->timeout = now + conn_timeout; + conntail->next = NULL; + } + } + } while (running); + + /* Tell the workers we to shutdown also */ + running = 1; /* Kludge, but it's the only way to get posttochannel to do something. */ + posttochannel(statuschn, "shutdown", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(stachgchn, "shutdown", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(pagechn, "shutdown", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(datachn, "shutdown", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(noteschn, "shutdown", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(enadischn, "shutdown", NULL, "hobbitd", NULL, NULL, ""); + posttochannel(clientchn, "shutdown", NULL, "hobbitd", NULL, NULL, ""); + running = 0; + + /* Close the channels */ + close_channel(statuschn, CHAN_MASTER); + close_channel(stachgchn, CHAN_MASTER); + close_channel(pagechn, CHAN_MASTER); + close_channel(datachn, CHAN_MASTER); + close_channel(noteschn, CHAN_MASTER); + close_channel(enadischn, CHAN_MASTER); + close_channel(clientchn, CHAN_MASTER); + close_channel(clichgchn, CHAN_MASTER); + + save_checkpoint(); + unlink(pidfile); + + if (dbgfd) fclose(dbgfd); + + MEMUNDEFINE(colnames); + + return 0; +} + diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_alert.c ./hobbitd/hobbitd_alert.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_alert.c 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/hobbitd_alert.c 2006-10-03 12:55:27.000000000 +0200 @@ -124,6 +124,9 @@ curr = curr->next; if (tmp->state == A_DEAD) { + if (tmp->osname) xfree(tmp->osname); + if (tmp->classname) xfree(tmp->classname); + if (tmp->groups) xfree(tmp->groups); if (tmp->pagemessage) xfree(tmp->pagemessage); if (tmp->ackmessage) xfree(tmp->ackmessage); xfree(tmp); @@ -285,7 +288,8 @@ } if (i > 9) { - char *key, *valid = NULL; + char *valid = NULL; + activealerts_t *newalert = (activealerts_t *)calloc(1, sizeof(activealerts_t)); newalert->hostname = find_name(hostnames, item[0]); newalert->testname = find_name(testnames, item[1]); @@ -297,10 +301,13 @@ newalert->state = A_PAGING; if (statusbuf) { + char *key; + key = (char *)malloc(strlen(newalert->hostname) + strlen(newalert->testname) + 100); sprintf(key, "\n%s|%s|%s\n", newalert->hostname, newalert->testname, colorname(newalert->color)); valid = strstr(statusbuf, key); if (!valid && (strncmp(statusbuf, key+1, strlen(key+1)) == 0)) valid = statusbuf; + xfree(key); } if (!valid) { errprintf("Stale alert for %s:%s dropped\n", newalert->hostname, newalert->testname); @@ -648,10 +655,12 @@ strcpy(awalk->ip, metadata[5]); awalk->cookie = atoi(metadata[11]); + if (awalk->osname) xfree(awalk->osname); awalk->osname = (metadata[12] ? strdup(metadata[12]) : NULL); + if (awalk->classname) xfree(awalk->classname); awalk->classname = (metadata[13] ? strdup(metadata[13]) : NULL); + if (awalk->groups) xfree(awalk->groups); awalk->groups = (metadata[14] ? strdup(metadata[14]) : NULL); - if (awalk->pagemessage) xfree(awalk->pagemessage); awalk->pagemessage = strdup(restofmsg); } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_buffer.c ./hobbitd/hobbitd_buffer.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_buffer.c 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/hobbitd_buffer.c 2007-02-09 11:27:54.461530794 +0100 @@ -34,6 +34,7 @@ case C_STACHG: v = getenv("MAXMSG_STACHG"); defvalue = shbufsz(C_STATUS); break; case C_PAGE: v = getenv("MAXMSG_PAGE"); defvalue = shbufsz(C_STATUS); break; case C_ENADIS: v = getenv("MAXMSG_ENADIS"); defvalue = 32; break; + case C_USER: v = getenv("MAXMSG_USER"); defvalue = 128; break; default: break; } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_buffer.h ./hobbitd/hobbitd_buffer.h --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_buffer.h 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/hobbitd_buffer.h 2007-02-09 11:27:54.469529339 +0100 @@ -11,7 +11,7 @@ #ifndef __HOBBITD_BUFFER_H__ #define __HOBBITD_BUFFER_H__ -enum msgchannels_t { C_STATUS=1, C_STACHG, C_PAGE, C_DATA, C_NOTES, C_ENADIS, C_CLIENT, C_CLICHG, C_LAST }; +enum msgchannels_t { C_STATUS=1, C_STACHG, C_PAGE, C_DATA, C_NOTES, C_ENADIS, C_CLIENT, C_CLICHG, C_USER, C_LAST }; extern unsigned int shbufsz(enum msgchannels_t chnid); #endif diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_client.c ./hobbitd/hobbitd_client.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_client.c 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/hobbitd_client.c 2006-10-03 13:52:29.000000000 +0200 @@ -896,7 +896,9 @@ swalk->sname+5); } addtobuffer(yellowdata, msgline); + addtobuffer(yellowdata, "<pre>\n"); addtostrbuffer(yellowdata, logsummary); + addtobuffer(yellowdata, "</pre>\n"); break; case COL_RED: @@ -909,7 +911,9 @@ swalk->sname+5); } addtobuffer(reddata, msgline); + addtobuffer(yellowdata, "<pre>\n"); addtostrbuffer(reddata, logsummary); + addtobuffer(yellowdata, "</pre>\n"); break; } @@ -963,7 +967,9 @@ swalk->sname+5); } addtostatus(msgline); + addtobuffer(yellowdata, "<pre>\n"); addtostatus(swalk->sdata); + addtobuffer(yellowdata, "</pre>\n"); do { swalk=swalk->next; } while (swalk && strncmp(swalk->sname, "msgs:", 5)); } @@ -1490,11 +1496,12 @@ else if (strcmp(s, "disk") == 0) { unsigned long warnlevel, paniclevel; int abswarn, abspanic, ignored; + char *groups; printf("Filesystem: "); fflush(stdout); fgets(s, sizeof(s), stdin); clean_instr(s); cfid = get_disk_thresholds(hinfo, clientclass, s, &warnlevel, &paniclevel, - &abswarn, &abspanic, &ignored, NULL); + &abswarn, &abspanic, &ignored, &groups); if (ignored) printf("Ignored\n"); else @@ -1506,6 +1513,7 @@ int pchecks = clear_process_counts(hinfo, clientclass); char *pname, *pid; int pcount, pmin, pmax, pcolor, ptrack; + char *groups; FILE *fd; if (pchecks == 0) { @@ -1530,7 +1538,7 @@ } } while (*s); - while ((pname = check_process_count(&pcount, &pmin, &pmax, &pcolor, &pid, &ptrack, NULL)) != NULL) { + while ((pname = check_process_count(&pcount, &pmin, &pmax, &pcolor, &pid, &ptrack, &groups)) != NULL) { printf("Process %s color %s: Count=%d, min=%d, max=%d\n", pname, colorname(pcolor), pcount, pmin, pmax); } @@ -1576,6 +1584,7 @@ else if (strcmp(s, "port") == 0) { char *localstr, *remotestr, *statestr, *p, *pname, *pid; int pcount, pmin, pmax, pcolor, pchecks, ptrack; + char *groups; int localcol = 4, remotecol = 5, statecol = 6, portcolor = COL_GREEN; pchecks = clear_port_counts(hinfo, clientclass); @@ -1619,7 +1628,7 @@ } while (*s); /* Check the number found for each monitored port */ - while ((pname = check_port_count(&pcount, &pmin, &pmax, &pcolor, &pid, &ptrack, NULL)) != NULL) { + while ((pname = check_port_count(&pcount, &pmin, &pmax, &pcolor, &pid, &ptrack, &groups)) != NULL) { char limtxt[1024]; if (pmax == -1) { diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_ipc.c ./hobbitd/hobbitd_ipc.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitd_ipc.c 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/hobbitd_ipc.c 2007-02-09 11:27:54.477527885 +0100 @@ -49,6 +49,7 @@ "enadis", "client", "clichg", + "user", NULL }; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitfetch.c ./hobbitd/hobbitfetch.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/hobbitfetch.c 2006-08-09 22:10:05.000000000 +0200 +++ ./hobbitd/hobbitfetch.c 2006-10-03 12:55:27.000000000 +0200 @@ -30,6 +30,7 @@ #include <stdlib.h> #include <string.h> #include <stdio.h> +#include <arpa/nameser.h> #include <netdb.h> #include <ctype.h> #include <signal.h> @@ -619,14 +620,36 @@ ip = strdup(bbh_item(hostwalk, BBH_IP)); } else { + /* There is an explicit IP setting in the pulldata tag */ char *p; ip++; /* Skip the '=' */ ip = strdup(ip); p = strchr(ip, ':'); if (p) { *p = '\0'; port = atoi(p+1); } + + if (*ip == '\0') { + /* No IP given, just a port number */ + xfree(ip); + ip = strdup(bbh_item(hostwalk, BBH_IP)); + } + } + + if (strcmp(ip, "0.0.0.0") == 0) { + struct hostent *hent; + + xfree(ip); ip = NULL; + hent = gethostbyname(clientwalk->hostname); + if (hent) { + struct in_addr addr; + + memcpy(&addr, *(hent->h_addr_list), sizeof(addr)); + ip = strdup(inet_ntoa(addr)); + } } + if (!ip) continue; + /* * Build the "pullclient" request, which includes the latest * clientdata config we got from the server. Keep the clientdata diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/rrd/do_ncv.c ./hobbitd/rrd/do_ncv.c --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/rrd/do_ncv.c 2006-08-09 22:10:06.000000000 +0200 +++ ./hobbitd/rrd/do_ncv.c 2006-10-03 12:55:27.000000000 +0200 @@ -11,7 +11,7 @@ /* */ /*----------------------------------------------------------------------------*/ -static char ncv_rcsid[] = "$Id: do_ncv.c,v 1.10 2006/06/09 22:23:49 henrik Rel $"; +static char ncv_rcsid[] = "$Id: do_ncv.c,v 1.10 2006/06/09 22:23:49 henrik Rel henrik $"; int do_ncv_rrd(char *hostname, char *testname, char *msg, time_t tstamp) { @@ -44,11 +44,22 @@ l += strspn(l, " \t\n"); if (*l) { + /* See if this line contains a '=' or ':' sign */ name = l; - l += strcspn(l, ":="); - if( *l ) { *l = '\0'; l++; } - else break; + l += strcspn(l, ":=\n"); + + if (*l) { + if (( *l == '=') || (*l == ':')) { + *l = '\0'; l++; + } + else { + /* No marker, so skip this line */ + name = NULL; + } + } + else break; /* We've hit the end of the message */ } + if (name) { val = l + strspn(l, " \t"); l = val + strspn(val, "0123456789."); diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/confreport_front ./hobbitd/webfiles/confreport_front --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/confreport_front 2006-08-09 22:10:10.000000000 +0200 +++ ./hobbitd/webfiles/confreport_front 2006-10-03 12:55:27.000000000 +0200 @@ -7,7 +7,6 @@ <tr><th align=left valign=top width="25%">Aliases</th><td>Names for this host other than the primary name, e.g. a hostname used by a client installed on the server</td></tr> <tr><th align=left valign=top width="25%">Monitoring location</th><td>The location of this host on the Hobbit webpages</td></tr> <tr><th align=left valign=top width="25%">Comment<br>Description</th><td>Explanatory text about the host</td></tr> - <tr><th align=left valign=top width="25%">NK monitoring period</th><td>Time of day/week when the NK-enabled alerts will appear on the NK monitor</td></tr> <tr><th align=left valign=top width="25%">Planned downtime</th><td>Time of day/week when the host monitoring is disabled</td></tr> <tr><th align=left valign=top width="25%">SLA Reporting Period</th><td>Time of day/week where the status impacts the SLA availability calculation</td></tr> </table> @@ -17,7 +16,7 @@ <td> <table width="100%"> <tr><th align=left valign=top width="25%">Service</th><td>Corresponds to the column-name on the Hobbit webpage</td></tr> - <tr><th align=left valign=top width="25%">NK</th><td>Whether this test appears on the NK view</td></tr> + <tr><th align=left valign=top width="25%">Critical</th><td>Whether this test appears on the Critical Systems view</td></tr> <tr><th align=left valign=top width="25%">C/Y/R limits</th><td>If set, this is the number of failures that must happen before the status changes to Clear/Yellow/Red</td></tr> <tr><th align=left valign=top width="25%">Specifics</th><td>Details about how this status is monitored</td></tr> </table> @@ -27,7 +26,7 @@ <td> <table width="100%"> <tr><th align=left valign=top width="25%">Service</th><td>Corresponds to the column-name on the Hobbit webpage</td></tr> - <tr><th align=left valign=top width="25%">NK</th><td>Whether this test appears on the NK view</td></tr> + <tr><th align=left valign=top width="25%">Critical</th><td>Whether this test appears on the Critical view</td></tr> <tr><th align=left valign=top width="25%">C/Y/R limits</th><td>If set, this is the number of failures that must happen before the status changes to Clear/Yellow/Red</td></tr> <tr><th align=left valign=top width="25%">Configuration</th><td>Details about how this status is monitored. NOTE: The exact thresholds for each test are configured on the client, and may differ from that listed here.</td></tr> </table> diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/hobbitnk_footer ./hobbitd/webfiles/hobbitnk_footer --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/hobbitnk_footer 2006-08-09 22:10:10.000000000 +0200 +++ ./hobbitd/webfiles/hobbitnk_footer 2006-10-03 12:55:27.000000000 +0200 @@ -17,6 +17,7 @@ </TD> <TD ALIGN=CENTER> <SELECT NAME="MINCOLOR"> + <OPTION VALUE="purple" &SELECT_MINCOLOR_PURPLE>Red+Yellow+Purple <OPTION VALUE="yellow" &SELECT_MINCOLOR_YELLOW>Red+Yellow <OPTION VALUE="red" &SELECT_MINCOLOR_RED>Red only </SELECT> diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/info_header ./hobbitd/webfiles/info_header --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/info_header 2006-08-09 22:10:10.000000000 +0200 +++ ./hobbitd/webfiles/info_header 2006-10-03 13:52:29.000000000 +0200 @@ -20,6 +20,15 @@ else field.checked = val; } + +function mark4Disable(services) { + for(i=0; i<document.disableform.disabletest.length; i++) { + if (services.indexOf(',' + document.disableform.disabletest.options[i].value + ',') > -1) { + document.disableform.disabletest.options[i].selected = !document.disableform.disabletest.options[i].selected; + } + } +} + </script> <script language="JavaScript1.2" type="text/javascript"> diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/notify_footer ./hobbitd/webfiles/notify_footer --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/notify_footer 1970-01-01 01:00:00.000000000 +0100 +++ ./hobbitd/webfiles/notify_footer 2006-08-09 22:10:10.000000000 +0200 @@ -0,0 +1,24 @@ +<BR><BR> + +<TABLE SUMMARY="Bottomline" WIDTH="100%"> +<TR> + <TD> <HR WIDTH="100%"> </TD> +</TR> +<TR> + <TD ALIGN=RIGHT><FONT FACE="Arial, Helvetica" SIZE="-2" COLOR="silver"><B><A HREF="http://hobbitmon.sourceforge.net/" style="text-decoration: none">Hobbit Monitor &HOBBITDREL</A></B></FONT></TD> +</TR> +</TABLE> + + +<!-- menu script itself. you should not modify this file --> +<script type="text/javascript" language="JavaScript" src="&BBMENUSKIN/menu.js"></script> +<!-- items structure. menu hierarchy and links are stored there --> +<script type="text/javascript" language="JavaScript" src="&BBMENUSKIN/menu_items.js"></script> +<!-- files with geometry and styles structures --> +<script type="text/javascript" language="JavaScript" src="&BBMENUSKIN/menu_tpl.js"></script> +<script type="text/javascript" language="JavaScript"> + new menu (MENU_ITEMS, MENU_POS); +</script> + +</BODY> +</HTML> diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/notify_form ./hobbitd/webfiles/notify_form --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/notify_form 1970-01-01 01:00:00.000000000 +0100 +++ ./hobbitd/webfiles/notify_form 2007-02-09 11:06:47.325879886 +0100 @@ -0,0 +1,102 @@ +<CENTER> +<FORM METHOD="GET" ACTION="&SCRIPT_NAME"> + <TABLE BORDER=0 SUMMARY="Notification time period"> + <TR> + <TD><TABLE BORDER=0> + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Starting </B></FONT></TD> + <TD><INPUT TYPE=TEXT NAME="MAXTIME" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B> minutes ago </B>( Default 1440 )</FONT></TD> + </TR> + <TR> + <TD COLSPAN=3 ALIGN=CENTER><FONT FACE="Arial, Helvetica" COLOR="silver"><B> - OR - </B></FONT></TD> + </TR> + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>From: </B></FONT></TD> + <TD><INPUT TYPE=TEXT NAME="FROMTIME" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver">(ccyy/mm/dd@hh:mm:ss)</FONT></TD> + </TR> + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>To: </B></FONT></TD> + <TD><INPUT TYPE=TEXT NAME="TOTIME" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver">(ccyy/mm/dd@hh:mm:ss)</FONT></TD> + </TR> + </TABLE></TD> + </TR> + </TABLE> + + <BR><BR> + + <TABLE BORDER=0 SUMMARY="Notification criteria"> + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Max # of notifications</B></FONT></TD> + <TD WIDTH=10> </TD> + <TD><INPUT TYPE=TEXT NAME="MAXCOUNT" SIZE=40 VALUE=100></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"> ( Default 100 )</FONT></TD> + </TR> + + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Hosts to match</B></FONT></TD> + <TD WIDTH=10> </TD> + <TD><INPUT TYPE=TEXT NAME="HOSTMATCH" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"> ( ex: ^host.*$ )</FONT></TD> + </TR> + + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Hosts to skip</B></FONT></TD> + <TD WIDTH=10> </TD> + <TD><INPUT TYPE=TEXT NAME="EXHOSTMATCH" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"> ( ex: ^host.*$ )</FONT></TD> + </TR> + + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Pages to match</B></FONT></TD> + <TD WIDTH=10> </TD> + <TD><INPUT TYPE=TEXT NAME="PAGEMATCH" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"> ( ex: ^webservers/.*$ )</FONT></TD> + </TR> + + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Pages to skip</B></FONT></TD> + <TD WIDTH=10> </TD> + <TD><INPUT TYPE=TEXT NAME="EXPAGEMATCH" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"> ( ex: ^webservers/.*$ )</FONT></TD> + </TR> + + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Tests to match</B></FONT></TD> + <TD WIDTH=10> </TD> + <TD><INPUT TYPE=TEXT NAME="TESTMATCH" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"> ( ex: cpu|vmstat )</FONT></TD> + </TR> + + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Tests to skip</B></FONT></TD> + <TD WIDTH=10> </TD> + <TD><INPUT TYPE=TEXT NAME="EXTESTMATCH" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"> ( ex: cpu|vmstat )</FONT></TD> + </TR> + + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Recipients to match</B></FONT></TD> + <TD WIDTH=10> </TD> + <TD><INPUT TYPE=TEXT NAME="RCPTMATCH" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"> ( ex: admin@test.com )</FONT></TD> + </TR> + + <TR> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"><B>Recipients to skip</B></FONT></TD> + <TD WIDTH=10> </TD> + <TD><INPUT TYPE=TEXT NAME="EXRCPTMATCH" SIZE=40></TD> + <TD><FONT FACE="Arial, Helvetica" COLOR="silver"> ( ex: admin@test.com )</FONT></TD> + </TR> + + <TR> + <TD COLSPAN=3 ALIGN=CENTER> + <BR><BR> + <INPUT TYPE="SUBMIT" NAME="Send" VALUE="View log" ALT="View log"> + </TD> + </TR> + </TABLE> +</FORM> +</CENTER> diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/notify_header ./hobbitd/webfiles/notify_header --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/notify_header 1970-01-01 01:00:00.000000000 +0100 +++ ./hobbitd/webfiles/notify_header 2007-02-09 11:06:51.215173324 +0100 @@ -0,0 +1,35 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<HTML> +<HEAD> +<META HTTP-EQUIV="EXPIRES" CONTENT="Sat, 01 Jan 2001 00:00:00 GMT"> +<TITLE>&BBBACKGROUND : Hobbit - Notification Log @ &BBDATE</TITLE> + +<!-- Styles for the menu bar --> +<link rel="stylesheet" type="text/css" href="&BBMENUSKIN/menu.css"> + +<!-- The favicon image --> +<link rel="shortcut icon" href="&BBSKIN/favicon-&BBBACKGROUND.ico"> + +</HEAD> + +<BODY BGCOLOR="&BBBACKGROUND" BACKGROUND="&BBSKIN/bkg-&BBBACKGROUND.gif" TEXT="#D8D8BF" LINK="#00FFAA" VLINK="#FFFF44"> + +<TABLE SUMMARY="Topline" WIDTH="100%"> +<TR><TD HEIGHT=16> </TD></TR> <!-- For the menu bar --> +<TR> + <TD VALIGN=MIDDLE ALIGN=LEFT WIDTH="30%"> + <FONT FACE="Arial, Helvetica" SIZE="+1" COLOR="silver"><B>&HOBBITLOGO</B></FONT> + </TD> + <TD VALIGN=MIDDLE ALIGN=CENTER WIDTH="40%"> + <CENTER><FONT FACE="Arial, Helvetica" SIZE="+1" COLOR="silver"><B>Notification Log</B></FONT></CENTER> + </TD> + <TD VALIGN=MIDDLE ALIGN=RIGHT WIDTH="30%"> + <FONT FACE="Arial, Helvetica" SIZE="+1" COLOR="silver"><B>&BBDATE</B></FONT> + </TD> +</TR> +<TR> + <TD COLSPAN=3> <HR WIDTH="100%"> </TD> +</TR> +</TABLE> +<BR> + diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/zoom.js ./hobbitd/webfiles/zoom.js --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/webfiles/zoom.js 2006-08-09 22:10:10.000000000 +0200 +++ ./hobbitd/webfiles/zoom.js 2006-10-03 12:55:27.000000000 +0200 @@ -601,6 +601,7 @@ var idxCount = gUrlObj.getUrlParameterValue("count"); var graphWidth = gUrlObj.getUrlParameterValue("graph_width"); var graphHeight = gUrlObj.getUrlParameterValue("graph_height"); + var bgColor = gUrlObj.getUrlParameterValue("color"); if (firstIdx != "") { idxStr = "&first=" + firstIdx; @@ -609,7 +610,7 @@ countStr = "&count=" + idxCount; } - open(urlBase + "&host=" + host + "&service=" + service + "&disp=" + dispName + idxStr + countStr + "&graph_start=" + newGraphStart + "&graph_end=" + newGraphEnd + "&graph_height=" + graphHeight + "&graph_width=" + graphWidth, "_self"); + open(urlBase + "&host=" + host + "&service=" + service + "&disp=" + dispName + idxStr + countStr + "&graph_start=" + newGraphStart + "&graph_end=" + newGraphEnd + "&graph_height=" + graphHeight + "&graph_width=" + graphWidth + "&color=" + bgColor, "_self"); } if ((gMouseObj.leftButtonPressed()) && (gMouseObj.dragging)) { @@ -655,6 +656,7 @@ var idxCount = gUrlObj.getUrlParameterValue("count"); var graphWidth = gUrlObj.getUrlParameterValue("graph_width"); var graphHeight = gUrlObj.getUrlParameterValue("graph_height"); + var bgColor = gUrlObj.getUrlParameterValue("color"); if (firstIdx != "") { idxStr = "&first=" + firstIdx; @@ -663,7 +665,7 @@ countStr = "&count=" + idxCount; } - open(urlBase + "&host=" + host + "&service=" + service + "&disp=" + dispName + idxStr + countStr + "&graph_start=" + newGraphStart + "&graph_end=" + newGraphEnd + "&graph_height=" + graphHeight + "&graph_width=" + graphWidth, "_self"); + open(urlBase + "&host=" + host + "&service=" + service + "&disp=" + dispName + idxStr + countStr + "&graph_start=" + newGraphStart + "&graph_end=" + newGraphEnd + "&graph_height=" + graphHeight + "&graph_width=" + graphWidth + "&color=" + bgColor, "_self"); } } } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/wwwfiles/menu/menu_items.js.DIST ./hobbitd/wwwfiles/menu/menu_items.js.DIST --- /home/henrik/hobbit/release/hobbit-4.2.0/hobbitd/wwwfiles/menu/menu_items.js.DIST 2006-08-09 22:10:08.000000000 +0200 +++ ./hobbitd/wwwfiles/menu/menu_items.js.DIST 2007-02-09 11:06:18.670085600 +0100 @@ -9,8 +9,10 @@ ['Availability Report', '@BBCGIURL@/bb-rep.sh'], ['Snapshot Report', '@BBCGIURL@/bb-snapshot.sh'], ['Config Report', '@BBCGIURL@/hobbit-confreport.sh'], + ['Config Report (Critical)', '@BBCGIURL@/hobbit-confreport-critical.sh'], ['Metrics Report', '@BBCGIURL@/hobbit-hostgraphs.sh'], ['Ghost Clients', '@BBCGIURL@/hobbit-ghosts.sh'], + ['Notification Report', '@BBCGIURL@/hobbit-notifylog.sh'], ], ['Administration', null, null, ['Find host', '@BBCGIURL@/bb-findhost.sh'], diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/include/libbbgen.h ./include/libbbgen.h --- /home/henrik/hobbit/release/hobbit-4.2.0/include/libbbgen.h 2006-08-09 22:10:13.000000000 +0200 +++ ./include/libbbgen.h 2007-02-09 11:06:18.657087961 +0100 @@ -42,6 +42,7 @@ #include "../lib/eventlog.h" #include "../lib/headfoot.h" #include "../lib/htmllog.h" +#include "../lib/notifylog.h" #include "../lib/reportlog.h" #include "../lib/availability.h" diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/Makefile ./lib/Makefile --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/Makefile 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/Makefile 2007-02-09 11:07:56.711274061 +0100 @@ -1,9 +1,9 @@ # bbgen library Makefile # -BBGENLIBOBJS = osdefs.o acklog.o availability.o calc.o cgi.o cgiurls.o clientlocal.o color.o digest.o encoding.o environ.o errormsg.o eventlog.o files.o headfoot.o hobbitrrd.o htmllog.o ipaccess.o loadalerts.o loadhosts.o loadnkconf.o links.o matching.o md5.o memory.o misc.o netservices.o rbtr.o reportlog.o rmd160c.o sendmsg.o sha1.o sig.o stackio.o strfunc.o suid.o timefunc.o timing.o url.o +BBGENLIBOBJS = osdefs.o acklog.o availability.o calc.o cgi.o cgiurls.o clientlocal.o color.o digest.o encoding.o environ.o errormsg.o eventlog.o files.o headfoot.o hobbitrrd.o htmllog.o ipaccess.o loadalerts.o loadhosts.o loadnkconf.o links.o matching.o md5.o memory.o misc.o netservices.o notifylog.o rbtr.o reportlog.o rmd160c.o sendmsg.o sha1.o sig.o stackio.o strfunc.o suid.o timefunc.o timing.o url.o -CLIENTLIBOBJS = osdefs.o cgiurls.o color.o digest.o encoding.o environ-client.o errormsg.o ipaccess.o loadhosts.o md5.o memory.o misc.o rbtr.o rmd160c.o sendmsg.o sha1.o sig.o stackio.o strfunc.o suid.o timefunc.o +CLIENTLIBOBJS = osdefs.o cgiurls.o color-client.o digest.o encoding.o environ-client.o errormsg.o ipaccess.o loadhosts.o md5.o memory.o misc.o rbtr.o rmd160c.o sendmsg.o sha1.o sig.o stackio.o strfunc.o suid.o timefunc-client.o ifeq ($(LOCALCLIENT),yes) CLIENTLIBOBJS += matching.o endif @@ -32,6 +32,9 @@ eventlog.o: eventlog.c $(CC) $(CFLAGS) $(PCREINCDIR) -c -o $@ $< +notifylog.o: notifylog.c + $(CC) $(CFLAGS) $(PCREINCDIR) -c -o $@ $< + headfoot.o: headfoot.c $(CC) $(CFLAGS) $(PCREINCDIR) -c -o $@ $< @@ -53,6 +56,12 @@ environ-client.o: environ.c $(CC) $(CFLAGS) -DBBTOPDIR=\"$(BBTOPDIR)\" -DBBLOGDIR=\"$(BBLOGDIR)\" -DBBHOSTNAME=\"$(BBHOSTNAME)\" -DBBHOSTIP=\"$(BBHOSTIP)\" -DBBHOSTOS=\"$(BBHOSTOS)\" -DBUILD_HOME=\"$(BBTOPDIR)/client\" -c -o $@ environ.c +color-client.o: color.c + $(CC) $(CFLAGS) -DCLIENTONLY -c -o $@ $< + +timefunc-client.o: timefunc.c + $(CC) $(CFLAGS) -DCLIENTONLY -c -o $@ $< + loadhosts: loadhosts.c libbbgen.a $(CC) $(CFLAGS) -DSTANDALONE -o $@ loadhosts.c ./libbbgen.a diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/cgi.c ./lib/cgi.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/cgi.c 2006-08-09 22:10:15.000000000 +0200 +++ ./lib/cgi.c 2006-10-03 12:55:27.000000000 +0200 @@ -139,7 +139,7 @@ token = strtok(NULL, "&"); } } - else if ((cgi_method == CGI_POST) || (conttype && (strcasecmp(conttype, "multipart/form-data") == 0))) { + else if ((cgi_method == CGI_POST) && (conttype && (strcasecmp(conttype, "multipart/form-data") == 0))) { char *bol, *eoln, *delim; char eolnchar = '\n'; char *currelembegin = NULL, *currelemend = NULL; @@ -252,3 +252,32 @@ return head; } +char *get_cookie(char *cookiename) +{ + static char *ckdata = NULL; + char *tok, *p; + int n; + + /* If no cookie, just return NULL */ + p = getenv("HTTP_COOKIE"); + if (!p) return NULL; + + if (ckdata) xfree(ckdata); + n = strlen(cookiename); + + /* Split the cookie variable into elements, separated by ";" and possible space. */ + ckdata = strdup(p); + tok = strtok(ckdata, "; "); + while (tok) { + if ((strncmp(cookiename, tok, n) == 0) && (*(tok+n) == '=')) { + /* Got it */ + return (tok+n+1); + } + + tok = strtok(NULL, "; "); + } + + xfree(ckdata); ckdata = NULL; + return NULL; +} + diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/cgi.h ./lib/cgi.h --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/cgi.h 2006-08-09 22:10:15.000000000 +0200 +++ ./lib/cgi.h 2006-10-03 12:55:27.000000000 +0200 @@ -23,6 +23,7 @@ extern char *cgi_error(void); extern cgidata_t *cgi_request(void); +extern char *get_cookie(char *cookiename); #endif diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/color.c ./lib/color.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/color.c 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/color.c 2006-10-03 12:55:27.000000000 +0200 @@ -111,6 +111,7 @@ return filename; } +#ifndef CLIENTONLY int colorset(char *colspec, int excludeset) { char *cspeccopy = strdup(colspec); @@ -131,4 +132,5 @@ ac = (ac & ~excludeset); return ac; } +#endif diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/headfoot.c ./lib/headfoot.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/headfoot.c 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/headfoot.c 2006-10-03 13:52:28.000000000 +0200 @@ -316,7 +316,7 @@ walk = statusboard; while (walk) { eoln = strchr(walk, '\n'); if (eoln) *eoln = '\0'; - if (strlen(walk) && (strncmp(walk, "summary|", 8) != 0) && (strncmp(walk, "dialup|", 7) != 0)) { + if (strlen(walk) && (strncmp(walk, "summary|", 8) != 0)) { char *buf, *hname = NULL, *tname = NULL; treerec_t *newrec; @@ -1233,8 +1233,6 @@ inbuf[st.st_size] = '\0'; close(formfile); - printf("Content-Type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); - headfoot(output, headertemplate, "", "header", color); if (pretext) fprintf(output, "%s", pretext); output_parsed(output, inbuf, color, seltime); diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/hobbitrrd.c ./lib/hobbitrrd.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/hobbitrrd.c 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/hobbitrrd.c 2006-10-03 12:55:27.000000000 +0200 @@ -194,7 +194,7 @@ } -static char *hobbit_graph_text(char *hostname, char *dispname, char *service, +static char *hobbit_graph_text(char *hostname, char *dispname, char *service, int bgcolor, hobbitgraph_t *graphdef, int itemcount, hg_stale_rrds_t nostale, const char *fmt) { static char *rrdurl = NULL; @@ -273,6 +273,7 @@ strcat(svcurl, urlencode(dispname ? dispname : hostname)); if (nostale == HG_WITHOUT_STALE_RRDS) strcat(svcurl, "&nostale"); + if (bgcolor != -1) sprintf(svcurl+strlen(svcurl), "&color=%s", colorname(bgcolor)); sprintf(rrdparturl, fmt, rrdservicename, svcurl, svcurl, rrdservicename, svcurl, xgetenv("BBSKIN")); if ((strlen(rrdparturl) + strlen(rrdurl) + 1) >= rrdurlsize) { @@ -294,12 +295,12 @@ return rrdurl; } -char *hobbit_graph_data(char *hostname, char *dispname, char *service, +char *hobbit_graph_data(char *hostname, char *dispname, char *service, int bgcolor, hobbitgraph_t *graphdef, int itemcount, hg_stale_rrds_t nostale, hg_link_t wantmeta) { return hobbit_graph_text(hostname, dispname, - service, graphdef, + service, bgcolor, graphdef, itemcount, nostale, ((wantmeta == HG_META_LINK) ? metafmt : hobbitlinkfmt)); } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/hobbitrrd.h ./lib/hobbitrrd.h --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/hobbitrrd.h 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/hobbitrrd.h 2006-10-03 12:55:27.000000000 +0200 @@ -37,7 +37,8 @@ extern hobbitrrd_t *find_hobbit_rrd(char *service, char *flags); extern hobbitgraph_t *find_hobbit_graph(char *rrdname); -extern char *hobbit_graph_data(char *hostname, char *dispname, char *service, hobbitgraph_t *graphdef, int itemcount, +extern char *hobbit_graph_data(char *hostname, char *dispname, char *service, int bgcolor, + hobbitgraph_t *graphdef, int itemcount, hg_stale_rrds_t nostale, hg_link_t wantmeta); #endif diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/htmllog.c ./lib/htmllog.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/htmllog.c 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/htmllog.c 2006-10-03 12:55:27.000000000 +0200 @@ -358,7 +358,7 @@ xfree(multikey); fprintf(output, "<!-- linecount=%d -->\n", linecount); - fprintf(output, "%s\n", hobbit_graph_data(hostname, displayname, service, graph, linecount, HG_WITHOUT_STALE_RRDS, HG_PLAIN_LINK)); + fprintf(output, "%s\n", hobbit_graph_data(hostname, displayname, service, color, graph, linecount, HG_WITHOUT_STALE_RRDS, HG_PLAIN_LINK)); } if (histlocation == HIST_BOTTOM) { diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/loadhosts.c ./lib/loadhosts.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/loadhosts.c 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/loadhosts.c 2006-10-03 13:52:28.000000000 +0200 @@ -134,7 +134,6 @@ bbh_item_name[BBH_IP] = "BBH_IP"; bbh_item_name[BBH_CLIENTALIAS] = "BBH_CLIENTALIAS"; - bbh_item_name[BBH_BANKSIZE] = "BBH_BANKSIZE"; bbh_item_name[BBH_HOSTNAME] = "BBH_HOSTNAME"; bbh_item_name[BBH_PAGENAME] = "BBH_PAGENAME"; bbh_item_name[BBH_PAGEPATH] = "BBH_PAGEPATH"; @@ -300,9 +299,8 @@ /* If default method, just say yes */ if (ghosthandling == 0) return result; - /* Allow all summaries and modembanks */ + /* Allow all summaries */ if (strcmp(hostname, "summary") == 0) return result; - if (strcmp(hostname, "dialup") == 0) return result; return (walk ? result : NULL); } @@ -403,11 +401,6 @@ else return bbh_find_item(host, item); break; - case BBH_BANKSIZE: - if (host->banksize == 0) return NULL; - sprintf(inttxt, "%d", host->banksize); - return inttxt; - case BBH_HOSTNAME: return host->bbhostname; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/loadhosts.h ./lib/loadhosts.h --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/loadhosts.h 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/loadhosts.h 2006-10-03 13:52:28.000000000 +0200 @@ -53,7 +53,6 @@ BBH_LDAPLOGIN, BBH_IP, BBH_HOSTNAME, - BBH_BANKSIZE, BBH_DOCURL, BBH_NOPROP, BBH_PAGEINDEX, @@ -80,7 +79,6 @@ char *bbhostname; /* Name for item 2 of bb-hosts */ char *logname; /* Name of the host directory in BBHISTLOGS (underscores replaces dots). */ int preference; /* For host with multiple entries, mark if we have the preferred one */ - int banksize; /* For modem-bank entries only */ pagelist_t *page; /* Host location in the page/subpage/subparent tree */ void *data; /* Misc. data supplied by the user of this library function */ struct namelist_t *defaulthost; /* Points to the latest ".default." host */ diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/loadhosts_file.c ./lib/loadhosts_file.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/loadhosts_file.c 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/loadhosts_file.c 2006-10-03 13:52:28.000000000 +0200 @@ -49,7 +49,7 @@ { static void *bbhfiles = NULL; FILE *bbhosts; - int ip1, ip2, ip3, ip4, banksize, groupid, pageidx; + int ip1, ip2, ip3, ip4, groupid, pageidx; char hostname[4096]; strbuffer_t *inbuf; pagelist_t *curtoppage, *curpage, *pgtail; @@ -285,30 +285,6 @@ MEMUNDEFINE(clientname); MEMUNDEFINE(downtime); } - else if (sscanf(STRBUF(inbuf), "dialup %s %d.%d.%d.%d %d", hostname, &ip1, &ip2, &ip3, &ip4, &banksize) == 6) { - char groupidstr[10]; - namelist_t *newitem = calloc(1, sizeof(namelist_t)); - - sprintf(newitem->ip, "%d.%d.%d.%d", ip1, ip2, ip3, ip4); - sprintf(groupidstr, "%d", groupid); - newitem->bbhostname = (char *)malloc(strlen("@dialup.") + strlen(hostname) + 1); - sprintf(newitem->bbhostname, "@dialup.%s", hostname); - newitem->clientname = newitem->bbhostname; - newitem->page = curpage; - newitem->elems = (char **)malloc(sizeof(char *)); - newitem->elems[0] = NULL; - newitem->banksize = banksize; - newitem->groupid = strdup(groupidstr); - newitem->pageindex = pageidx++; - newitem->next = NULL; - - if (namehead == NULL) - namehead = nametail = newitem; - else { - nametail->next = newitem; - nametail = newitem; - } - } } stackfclose(bbhosts); freestrbuffer(inbuf); diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/loadhosts_net.c ./lib/loadhosts_net.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/loadhosts_net.c 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/loadhosts_net.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,182 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Hobbit monitor library. */ -/* */ -/* This is a library module for Hobbit, responsible for loading the host */ -/* configuration from hobbitd. */ -/* */ -/* Copyright (C) 2006 Henrik Storner <henrik@hswn.dk> */ -/* */ -/* This program is released under the GNU General Public License (GPL), */ -/* version 2. See the file "COPYING" for details. */ -/* */ -/*----------------------------------------------------------------------------*/ - -static char rcsid_file[] = "$Id: loadhosts_net.c,v 1.1 2006/03/30 19:59:50 henrik Rel $"; - -namelist_t *load_hostnames_net(void) -{ - char *configbuf = NULL, *inbuf = NULL; - namelist_t *nametail = NULL; - char bbmsg[200]; - char *cnfptr; - - configloaded = 1; - initialize_hostlist(); - - strcpy(bbmsg, "hostinfo fields=BBH_HOSTNAME,BBH_PAGEPATH,BBH_GROUPID,BBH_BANKSIZE,BBH_IP,BBH_RAW"); - if (xgetenv("BBLOCATION")) sprintf(bbmsg+strlen(bbmsg), " net=%s", xgetenv("BBLOCATION")); - - if (sendmessage(bbmsg, NULL, NULL, &configbuf, 1, BBTALK_TIMEOUT) != BB_OK) return NULL; - inbuf = strtok_r(bbmsg, "\n", &cnfptr); - while (inbuf) { - char *hostname, *pagepath, *groupid, *banksize, *ip; - - namelist_t *newitem = calloc(1, sizeof(namelist_t)); - - hostname = pagepath = groupid = banksize = ip = NULL; - hostname = gettok(inbuf, "|"); - if (hostname) pagepath = gettok(NULL, "|"); - if (pagepath) groupid = gettok(NULL, "|"); - if (groupid) banksize = gettok(NULL, "|"); - if (banksize) ip = gettok(NULL, "|"); - - newitem->bbhostname = strdup(hostname); - newitem->clientname = newitem->bbhostname; - newitem->page = setup_page(pagepath); - newitem->groupid = strdup(groupid); - newitem->banksize = atoi(banksize); - strcpy(newitem->ip, ip); - - if (strncmp(hostname, "@dialup", 7) == 0) { - /* No other elements for this type of entry */ - newitem->elems = (char **)malloc(sizeof(char *)); - newitem->elems[0] = NULL; - newitem->next = NULL; - } - - if (namehead == NULL) - namehead = nametail = newitem; - else { - nametail->next = newitem; - nametail = newitem; - } - - newitem->bbhostname = strdup(hostname); - if (ip1 || ip2 || ip3 || ip4) newitem->preference = 1; else newitem->preference = 0; - newitem->clientname = NULL; - newitem->logname = strdup(newitem->bbhostname); - { char *p = newitem->logname; while ((p = strchr(p, '.')) != NULL) { *p = '_'; } } - newitem->downtime = NULL; - newitem->page = curpage; - newitem->data = NULL; - newitem->defaulthost = defaulthost; - - clientname[0] = downtime[0] = '\0'; - startoftags = strchr(inbuf, '#'); - if (startoftags == NULL) startoftags = ""; else startoftags++; - startoftags += strspn(startoftags, " \t\r\n"); - newitem->allelems = strdup(startoftags); - elemsize = 5; - newitem->elems = (char **)malloc((elemsize+1)*sizeof(char *)); - - tag = newitem->allelems; elemidx = 0; - while (tag && *tag) { - if (elemidx == elemsize) { - elemsize += 5; - newitem->elems = (char **)realloc(newitem->elems, (elemsize+1)*sizeof(char *)); - } - newitem->elems[elemidx] = tag; - - /* Skip until we hit a whitespace or a quote */ - tag += strcspn(tag, " \t\r\n\""); - if (*tag == '"') { - delim = tag; - - /* Hit a quote - skip until the next matching quote */ - tag = strchr(tag+1, '"'); - if (tag != NULL) { - /* Found end-quote, NULL the item here and move on */ - *tag = '\0'; tag++; - } - - /* Now move quoted data one byte down (including the NUL) to kill quotechar */ - memmove(delim, delim+1, strlen(delim)); - } - else if (*tag) { - /* Normal end of item, NULL it and move on */ - *tag = '\0'; tag++; - } - else { - /* End of line - no more to do. */ - tag = NULL; - } - - /* - * If we find a "noconn", drop preference value to 0. - * If we find a "prefer", up reference value to 2. - */ - if ((newitem->preference == 1) && (strcmp(newitem->elems[elemidx], "noconn") == 0)) - newitem->preference = 0; - else if (strcmp(newitem->elems[elemidx], "prefer") == 0) - newitem->preference = 2; - - /* Skip whitespace until start of next tag */ - if (tag) tag += strspn(tag, " \t\r\n"); - elemidx++; - } - - newitem->elems[elemidx] = NULL; - - /* See if this host is defined before */ - for (iwalk = namehead, iprev = NULL; (iwalk && strcmp(iwalk->bbhostname, newitem->bbhostname)); iprev = iwalk, iwalk = iwalk->next) ; - if (strcasecmp(newitem->bbhostname, ".default.") == 0) { - /* The pseudo DEFAULT host */ - newitem->next = NULL; - defaulthost = newitem; - } - else if (iwalk == NULL) { - /* New item, so add to end of list */ - newitem->next = NULL; - if (namehead == NULL) - namehead = nametail = newitem; - else { - nametail->next = newitem; - nametail = newitem; - } - } - else if (newitem->preference <= iwalk->preference) { - /* Add after the existing (more preferred) entry */ - newitem->next = iwalk->next; - iwalk->next = newitem; - } - else { - /* New item has higher preference, so add before the iwalk item (i.e. after iprev) */ - if (iprev == NULL) { - newitem->next = namehead; - namehead = newitem; - } - else { - newitem->next = iprev->next; - iprev->next = newitem; - } - } - - newitem->clientname = bbh_find_item(newitem, BBH_CLIENTALIAS); - if (newitem->clientname == NULL) newitem->clientname = newitem->bbhostname; - newitem->downtime = bbh_find_item(newitem, BBH_DOWNTIME); - - MEMUNDEFINE(clientname); - MEMUNDEFINE(downtime); - } - - - inbuf = strtok_r(NULL, "\n", &cnfptr); - } - xfree(configbuf); - - build_hosttree(); - return namehead; -} - - - diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/notifylog.c ./lib/notifylog.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/notifylog.c 1970-01-01 01:00:00.000000000 +0100 +++ ./lib/notifylog.c 2007-02-09 11:06:31.266797268 +0100 @@ -0,0 +1,360 @@ +/*----------------------------------------------------------------------------*/ +/* Hobbit monitor library. */ +/* */ +/* This displays the "notification" log. */ +/* */ +/* Copyright (C) 2002-2006 Henrik Storner <henrik@storner.dk> */ +/* Host/test/color/start/end filtering code by Eric Schwimmer 2005 */ +/* */ +/* This program is released under the GNU General Public License (GPL), */ +/* version 2. See the file "COPYING" for details. */ +/* */ +/*----------------------------------------------------------------------------*/ + +static char rcsid[] = "$Id: notifylog.c,v 1.2 2007/02/07 21:51:31 henrik Exp $"; + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <time.h> + +#include <pcre.h> + +#include "libbbgen.h" + +typedef struct notification_t { + struct namelist_t *host; + struct htnames_t *service; + time_t eventtime; + char *recipient; + struct notification_t *next; +} notification_t; + + +static time_t convert_time(char *timestamp) +{ + time_t event = 0; + unsigned int year,month,day,hour,min,sec,count; + struct tm timeinfo; + + count = sscanf(timestamp, "%u/%u/%u@%u:%u:%u", + &year, &month, &day, &hour, &min, &sec); + if(count != 6) { + return -1; + } + if(year < 1970) { + return 0; + } + else { + memset(&timeinfo, 0, sizeof(timeinfo)); + timeinfo.tm_year = year - 1900; + timeinfo.tm_mon = month - 1; + timeinfo.tm_mday = day; + timeinfo.tm_hour = hour; + timeinfo.tm_min = min; + timeinfo.tm_sec = sec; + timeinfo.tm_isdst = -1; + event = mktime(&timeinfo); + } + + return event; +} + +static htnames_t *namehead = NULL; +static htnames_t *getname(char *name, int createit) +{ + htnames_t *walk; + + for (walk = namehead; (walk && strcmp(walk->name, name)); walk = walk->next) ; + if (walk || (!createit)) return walk; + + walk = (htnames_t *)malloc(sizeof(htnames_t)); + walk->name = strdup(name); + walk->next = namehead; + namehead = walk; + + return walk; +} + +void do_notifylog(FILE *output, + int maxcount, int maxminutes, char *fromtime, char *totime, + char *pageregex, char *expageregex, + char *hostregex, char *exhostregex, + char *testregex, char *extestregex, + char *rcptregex, char *exrcptregex) +{ + FILE *notifylog; + char notifylogfilename[PATH_MAX]; + time_t firstevent = 0; + time_t lastevent = time(NULL); + notification_t *head, *walk; + struct stat st; + char l[MAX_LINE_LEN]; + char title[200]; + + /* For the PCRE matching */ + const char *errmsg = NULL; + int errofs = 0; + pcre *pageregexp = NULL; + pcre *expageregexp = NULL; + pcre *hostregexp = NULL; + pcre *exhostregexp = NULL; + pcre *testregexp = NULL; + pcre *extestregexp = NULL; + pcre *rcptregexp = NULL; + pcre *exrcptregexp = NULL; + + if (maxminutes && (fromtime || totime)) { + fprintf(output, "<B>Only one time interval type is allowed!</B>"); + return; + } + + if (fromtime) { + firstevent = convert_time(fromtime); + if(firstevent < 0) { + fprintf(output,"<B>Invalid 'from' time: %s</B>", fromtime); + return; + } + } + else if (maxminutes) { + firstevent = time(NULL) - maxminutes*60; + } + else { + firstevent = time(NULL) - 86400; + } + + if (totime) { + lastevent = convert_time(totime); + if (lastevent < 0) { + fprintf(output,"<B>Invalid 'to' time: %s</B>", totime); + return; + } + if (lastevent < firstevent) { + fprintf(output,"<B>'to' time must be after 'from' time.</B>"); + return; + } + } + + if (!maxcount) maxcount = 100; + + if (pageregex && *pageregex) pageregexp = pcre_compile(pageregex, PCRE_CASELESS, &errmsg, &errofs, NULL); + if (expageregex && *expageregex) expageregexp = pcre_compile(expageregex, PCRE_CASELESS, &errmsg, &errofs, NULL); + if (hostregex && *hostregex) hostregexp = pcre_compile(hostregex, PCRE_CASELESS, &errmsg, &errofs, NULL); + if (exhostregex && *exhostregex) exhostregexp = pcre_compile(exhostregex, PCRE_CASELESS, &errmsg, &errofs, NULL); + if (testregex && *testregex) testregexp = pcre_compile(testregex, PCRE_CASELESS, &errmsg, &errofs, NULL); + if (extestregex && *extestregex) extestregexp = pcre_compile(extestregex, PCRE_CASELESS, &errmsg, &errofs, NULL); + if (rcptregex && *rcptregex) rcptregexp = pcre_compile(rcptregex, PCRE_CASELESS, &errmsg, &errofs, NULL); + if (exrcptregex && *exrcptregex) exrcptregexp = pcre_compile(exrcptregex, PCRE_CASELESS, &errmsg, &errofs, NULL); + + sprintf(notifylogfilename, "%s/notifications.log", xgetenv("BBSERVERLOGS")); + notifylog = fopen(notifylogfilename, "r"); + + if (notifylog && (stat(notifylogfilename, &st) == 0)) { + time_t curtime; + int done = 0; + + /* Find a spot in the notification log file close to where the firstevent time is */ + fseeko(notifylog, 0, SEEK_END); + do { + /* Go back maxcount*80 bytes - one entry is ~80 bytes */ + if (ftello(notifylog) > maxcount*80) { + unsigned int uicurtime; + fseeko(notifylog, -maxcount*80, SEEK_CUR); + fgets(l, sizeof(l), notifylog); /* Skip to start of line */ + fgets(l, sizeof(l), notifylog); + /* Sun Jan 7 10:29:08 2007 myhost.disk (130.225.226.90) foo@test.com 1168162147 100 */ + sscanf(l, "%*s %*s %*u %*u:%*u:%*u %*u %*s %*s %*s %u %*d", &uicurtime); + curtime = uicurtime; + done = (curtime < firstevent); + } + else { + rewind(notifylog); + done = 1; + } + } while (!done); + } + + head = NULL; + + while (notifylog && (fgets(l, sizeof(l), notifylog))) { + + time_t eventtime; + char hostsvc[MAX_LINE_LEN]; + char recipient[MAX_LINE_LEN]; + char *hostname, *svcname, *p; + int itemsfound, pagematch, hostmatch, testmatch, rcptmatch; + notification_t *newrec; + struct namelist_t *eventhost; + struct htnames_t *eventcolumn; + int ovector[30]; + + itemsfound = sscanf(l, "%*s %*s %*u %*u:%*u:%*u %*u %s %*s %s %u %*d", hostsvc, recipient, &eventtime); + if (itemsfound != 3) continue; + if (eventtime < firstevent) continue; + if (eventtime > lastevent) break; + + hostname = hostsvc; svcname = strrchr(hostsvc, '.'); if (svcname) { *svcname = '\0'; svcname++; } else svcname = ""; + eventhost = hostinfo(hostname); + if (!eventhost) continue; /* Dont report hosts that no longer exist */ + eventcolumn = getname(svcname, 1); + + p = strchr(recipient, '['); if (p) *p = '\0'; + + if (pageregexp) { + char *pagename = bbh_item(eventhost, BBH_PAGEPATH); + pagematch = (pcre_exec(pageregexp, NULL, pagename, strlen(pagename), 0, 0, + ovector, (sizeof(ovector)/sizeof(int))) >= 0); + } + else + pagematch = 1; + if (!pagematch) continue; + + if (expageregexp) { + char *pagename = bbh_item(eventhost, BBH_PAGEPATH); + pagematch = (pcre_exec(expageregexp, NULL, pagename, strlen(pagename), 0, 0, + ovector, (sizeof(ovector)/sizeof(int))) >= 0); + } + else + pagematch = 0; + if (pagematch) continue; + + if (hostregexp) + hostmatch = (pcre_exec(hostregexp, NULL, hostname, strlen(hostname), 0, 0, + ovector, (sizeof(ovector)/sizeof(int))) >= 0); + else + hostmatch = 1; + if (!hostmatch) continue; + + if (exhostregexp) + hostmatch = (pcre_exec(exhostregexp, NULL, hostname, strlen(hostname), 0, 0, + ovector, (sizeof(ovector)/sizeof(int))) >= 0); + else + hostmatch = 0; + if (hostmatch) continue; + + if (testregexp) + testmatch = (pcre_exec(testregexp, NULL, svcname, strlen(svcname), 0, 0, + ovector, (sizeof(ovector)/sizeof(int))) >= 0); + else + testmatch = 1; + if (!testmatch) continue; + + if (extestregexp) + testmatch = (pcre_exec(extestregexp, NULL, svcname, strlen(svcname), 0, 0, + ovector, (sizeof(ovector)/sizeof(int))) >= 0); + else + testmatch = 0; + if (testmatch) continue; + + if (rcptregexp) + rcptmatch = (pcre_exec(rcptregexp, NULL, recipient, strlen(recipient), 0, 0, + ovector, (sizeof(ovector)/sizeof(int))) >= 0); + else + rcptmatch = 1; + if (!rcptmatch) continue; + + if (exrcptregexp) + rcptmatch = (pcre_exec(exrcptregexp, NULL, recipient, strlen(recipient), 0, 0, + ovector, (sizeof(ovector)/sizeof(int))) >= 0); + else + rcptmatch = 0; + if (rcptmatch) continue; + + newrec = (notification_t *) malloc(sizeof(notification_t)); + newrec->host = eventhost; + newrec->service = eventcolumn; + newrec->eventtime = eventtime; + newrec->recipient = strdup(recipient); + newrec->next = head; + head = newrec; + } + + if (head) { + char *bgcolors[2] = { "#000000", "#000066" }; + int bgcolor = 0; + int count; + struct notification_t *lasttoshow = head; + + count=0; + walk=head; + do { + count++; + lasttoshow = walk; + walk = walk->next; + } while (walk && (count<maxcount)); + + if (maxminutes) { + sprintf(title, "%d notifications in the past %u minutes", + count, (unsigned int)((time(NULL) - lasttoshow->eventtime) / 60)); + } + else { + sprintf(title, "%d notifications sent.", count); + } + + fprintf(output, "<BR><BR>\n"); + fprintf(output, "<TABLE SUMMARY=\"Notification log\" BORDER=0>\n"); + fprintf(output, "<TR BGCOLOR=\"#333333\">\n"); + fprintf(output, "<TD ALIGN=CENTER COLSPAN=4><FONT SIZE=-1 COLOR=\"#33ebf4\">%s</FONT></TD></TR>\n", title); + fprintf(output, "<TR BGCOLOR=\"#333333\"><TH>Time</TH><TH>Host</TH><TH>Service</TH><TH>Recipient</TH></TR>\n"); + + for (walk=head; (walk != lasttoshow->next); walk=walk->next) { + char *hostname = bbh_item(walk->host, BBH_HOSTNAME); + + fprintf(output, "<TR BGCOLOR=%s>\n", bgcolors[bgcolor]); + bgcolor = ((bgcolor + 1) % 2); + + fprintf(output, "<TD ALIGN=LEFT>%s</TD>\n", ctime(&walk->eventtime)); + + fprintf(output, "<TD ALIGN=LEFT>%s</TD>\n", hostname); + fprintf(output, "<TD ALIGN=LEFT>%s</TD>\n", walk->service->name); + fprintf(output, "<TD ALIGN=LEFT>%s</TD>\n", walk->recipient); + } + + fprintf(output, "</TABLE>\n"); + + /* Clean up */ + walk = head; + do { + struct notification_t *tmp = walk; + + walk = walk->next; + xfree(tmp->recipient); + xfree(tmp); + } while (walk); + } + else { + /* No notifications during the past maxminutes */ + if (notifylog) + sprintf(title, "No notifications sent in the last %d minutes", maxminutes); + else + strcpy(title, "No notifications logged"); + + fprintf(output, "<CENTER><BR>\n"); + fprintf(output, "<TABLE SUMMARY=\"%s\" BORDER=0>\n", title); + fprintf(output, "<TR BGCOLOR=\"#333333\">\n"); + fprintf(output, "<TD ALIGN=CENTER COLSPAN=6><FONT SIZE=-1 COLOR=\"#33ebf4\">%s</FONT></TD>\n", title); + fprintf(output, "</TR>\n"); + fprintf(output, "</TABLE>\n"); + fprintf(output, "</CENTER>\n"); + } + + if (notifylog) fclose(notifylog); + + if (pageregexp) pcre_free(pageregexp); + if (expageregexp) pcre_free(expageregexp); + if (hostregexp) pcre_free(hostregexp); + if (exhostregexp) pcre_free(exhostregexp); + if (testregexp) pcre_free(testregexp); + if (extestregexp) pcre_free(extestregexp); + if (rcptregexp) pcre_free(rcptregexp); + if (exrcptregexp) pcre_free(exrcptregexp); +} + diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/notifylog.h ./lib/notifylog.h --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/notifylog.h 1970-01-01 01:00:00.000000000 +0100 +++ ./lib/notifylog.h 2007-02-09 11:06:31.269796723 +0100 @@ -0,0 +1,21 @@ +/*----------------------------------------------------------------------------*/ +/* Hobbit monitor library. */ +/* */ +/* Copyright (C) 2002-2006 Henrik Storner <henrik@storner.dk> */ +/* */ +/* This program is released under the GNU General Public License (GPL), */ +/* version 2. See the file "COPYING" for details. */ +/* */ +/*----------------------------------------------------------------------------*/ + +#ifndef __NOTIFYLOG_H_ +#define __NOTIFYLOG_H_ + +extern void do_notifylog(FILE *output, int maxcount, int maxminutes, char *fromtime, char *totime, + char *pagematch, char *expagematch, + char *hostmatch, char *exhostmatch, + char *testmatch, char *extestmatch, + char *rcptmatch, char *exrcptmatch); + +#endif + diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/lib/timefunc.c ./lib/timefunc.c --- /home/henrik/hobbit/release/hobbit-4.2.0/lib/timefunc.c 2006-08-09 22:10:16.000000000 +0200 +++ ./lib/timefunc.c 2006-10-03 13:52:29.000000000 +0200 @@ -245,6 +245,7 @@ return found; } +#ifndef CLIENTONLY char *check_downtime(char *hostname, char *testname) { namelist_t *hinfo = hostinfo(hostname); @@ -300,6 +301,7 @@ return NULL; } +#endif int periodcoversnow(char *tag) { @@ -415,20 +417,30 @@ */ int result = 0; - char *p; - char modifier; + char *startofval; - p = dur + strspn(dur, "0123456789"); - modifier = *p; - *p = '\0'; - result = atoi(dur); - *p = modifier; + startofval = dur; + + while (startofval && (isdigit((int)*startofval))) { + char *p; + char modifier; + int oneval = 0; + + p = startofval + strspn(startofval, "0123456789"); + modifier = *p; + *p = '\0'; + oneval = atoi(startofval); + *p = modifier; + + switch (modifier) { + case 'm': break; /* minutes */ + case 'h': oneval *= 60; break; /* hours */ + case 'd': oneval *= 1440; break; /* days */ + case 'w': oneval *= 10080; break; /* weeks */ + } - switch (modifier) { - case 'm': break; /* minutes */ - case 'h': result *= 60; break; /* hours */ - case 'd': result *= 1440; break; /* days */ - case 'w': result *= 10080; break; /* weeks */ + result += oneval; + startofval = ((*p) ? p+1 : NULL); } return result; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/Makefile ./web/Makefile --- /home/henrik/hobbit/release/hobbit-4.2.0/web/Makefile 2006-08-09 22:10:13.000000000 +0200 +++ ./web/Makefile 2007-02-09 11:06:18.666086326 +0100 @@ -1,5 +1,5 @@ -PROGRAMS = bb-hist.cgi bb-eventlog.cgi bb-rep.cgi bb-replog.cgi bb-snapshot.cgi bb-findhost.cgi bb-csvinfo.cgi bb-ack.cgi bb-webpage bb-datepage.cgi hobbitgraph.cgi hobbitsvc.cgi hobbit-enadis.cgi hobbit-confreport.cgi hobbit-nkview.cgi hobbit-nkedit.cgi hobbit-ackinfo.cgi hobbit-statusreport.cgi boilerplate.cgi hobbit-hostgraphs.cgi hobbit-ghosts.cgi -CGISCRIPTS = bb-hist.sh bb-eventlog.sh bb-rep.sh bb-replog.sh bb-snapshot.sh bb-findhost.sh bb-csvinfo.sh hobbitcolumn.sh bb-datepage.sh hobbitgraph.sh bb-hostsvc.sh bb-histlog.sh hobbit-confreport.sh hobbit-nkview.sh hobbit-certreport.sh hobbit-nongreen.sh hobbit-hostgraphs.sh hobbit-ghosts.sh +PROGRAMS = bb-hist.cgi bb-eventlog.cgi bb-rep.cgi bb-replog.cgi bb-snapshot.cgi bb-findhost.cgi bb-csvinfo.cgi bb-ack.cgi bb-webpage bb-datepage.cgi hobbitgraph.cgi hobbitsvc.cgi hobbit-enadis.cgi hobbit-confreport.cgi hobbit-nkview.cgi hobbit-nkedit.cgi hobbit-ackinfo.cgi hobbit-statusreport.cgi boilerplate.cgi hobbit-hostgraphs.cgi hobbit-ghosts.cgi hobbit-notifylog.cgi +CGISCRIPTS = bb-hist.sh bb-eventlog.sh bb-rep.sh bb-replog.sh bb-snapshot.sh bb-findhost.sh bb-csvinfo.sh hobbitcolumn.sh bb-datepage.sh hobbitgraph.sh bb-hostsvc.sh bb-histlog.sh hobbit-confreport.sh hobbit-confreport-critical.sh hobbit-nkview.sh hobbit-certreport.sh hobbit-nongreen.sh hobbit-hostgraphs.sh hobbit-ghosts.sh hobbit-notifylog.sh SECCGISCRIPTS = bb-ack.sh hobbit-enadis.sh hobbit-nkedit.sh hobbit-ackinfo.sh LIBOBJS = ../lib/libbbgen.a @@ -25,6 +25,7 @@ STATUSREPOBJS = hobbit-statusreport.o MAILACKOBJS = hobbit-mailack.o GHOSTOBJS = hobbit-ghosts.o +NOTIFYOBJS = hobbit-notifylog.o HOSTGRAPHSOBJS = hobbit-hostgraphs.o BOILERPLATEOBJS = boilerplate.o @@ -105,6 +106,9 @@ hobbit-ghosts.cgi: $(GHOSTOBJS) $(LIBOBJS) $(CC) $(CFLAGS) -o $@ $(RPATHOPT) $(GHOSTOBJS) $(LIBOBJS) $(PCRELIBS) $(NETLIBS) +hobbit-notifylog.cgi: $(NOTIFYOBJS) $(LIBOBJS) + $(CC) $(CFLAGS) -o $@ $(RPATHOPT) $(NOTIFYOBJS) $(LIBOBJS) $(PCRELIBS) $(NETLIBS) + bb-ack.sh: bb-ack.sh.DIST cat $< | sed -e 's!@BBHOME@!$(BBHOME)!g' | sed -e 's!@RUNTIMEDEFS@!$(RUNTIMEDEFS)!g' >$@ @@ -178,6 +182,10 @@ cat $< | sed -e 's!@BBHOME@!$(BBHOME)!g' | sed -e 's!@RUNTIMEDEFS@!$(RUNTIMEDEFS)!g' >$@ chmod 755 $@ +hobbit-confreport-critical.sh: hobbit-confreport-critical.sh.DIST + cat $< | sed -e 's!@BBHOME@!$(BBHOME)!g' | sed -e 's!@RUNTIMEDEFS@!$(RUNTIMEDEFS)!g' >$@ + chmod 755 $@ + hobbit-certreport.sh: hobbit-certreport.sh.DIST cat $< | sed -e 's!@BBHOME@!$(BBHOME)!g' | sed -e 's!@RUNTIMEDEFS@!$(RUNTIMEDEFS)!g' >$@ chmod 755 $@ @@ -194,6 +202,10 @@ cat $< | sed -e 's!@BBHOME@!$(BBHOME)!g' | sed -e 's!@RUNTIMEDEFS@!$(RUNTIMEDEFS)!g' >$@ chmod 755 $@ +hobbit-notifylog.sh: hobbit-notifylog.sh.DIST + cat $< | sed -e 's!@BBHOME@!$(BBHOME)!g' | sed -e 's!@RUNTIMEDEFS@!$(RUNTIMEDEFS)!g' >$@ + chmod 755 $@ + %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-ack.c ./web/bb-ack.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-ack.c 2006-08-09 22:10:12.000000000 +0200 +++ ./web/bb-ack.c 2006-10-03 13:52:29.000000000 +0200 @@ -133,7 +133,7 @@ } if (acknum) awalk->acknum = atoi(acknum); - if (validity) awalk->validity = atoi(validity); + if (validity) awalk->validity = durationvalue(validity); if (ackmsg) awalk->ackmsg = strdup(ackmsg); if (hostname) awalk->hostname = strdup(hostname); if (testname) awalk->testname = strdup(testname); @@ -161,7 +161,7 @@ fprintf(output, " <td>%s</td>\n", (hname ? hname : " ")); fprintf(output, " <td>%s</td>\n", (tname ? tname : " ")); - fprintf(output, " <TD><INPUT TYPE=TEXT NAME=\"DELAY_%s\" SIZE=4 MAXLENGTH=4></TD>\n", numstr); + fprintf(output, " <TD><INPUT TYPE=TEXT NAME=\"DELAY_%s\" SIZE=8 MAXLENGTH=20></TD>\n", numstr); fprintf(output, " <TD><INPUT TYPE=TEXT NAME=\"MESSAGE_%s\" SIZE=60 MAXLENGTH=80></TD>\n", numstr); fprintf(output, " <TD>\n"); @@ -224,42 +224,31 @@ NULL, NULL); } else { - char cmd[1024]; + char *cmd; char *respbuf = NULL; - char *cookie = NULL, *p; + char *hostname, *pagename; int gotfilter = 0; headfoot(stdout, "acknowledge", "", "header", COL_RED); + cmd = (char *)malloc(1024); strcpy(cmd, "hobbitdboard color=red,yellow fields=hostname,testname,cookie"); - p = getenv("HTTP_COOKIE"); - if (p) cookie = strdup(p); - - if (obeycookies && cookie && ((p = strstr(cookie, "host=")) != NULL)) { - char *hostname; - - hostname = p + strlen("host="); - p = strchr(hostname, ';'); if (p) *p = '\0'; + if (obeycookies && !gotfilter && ((hostname = get_cookie("host")) != NULL)) { if (*hostname) { - sprintf(cmd + strlen(cmd), " host=%s", hostname); + cmd = (char *)realloc(cmd, 1024 + strlen(hostname)); + sprintf(cmd + strlen(cmd), " host=^%s$", hostname); gotfilter = 1; } - if (p) *p = ';'; } - if (obeycookies && cookie && !gotfilter && ((p = strstr(cookie, "pagepath=")) != NULL)) { - char *pagename; - - pagename = p + strlen("pagepath="); - p = strchr(pagename, ';'); if (p) *p = '\0'; + if (obeycookies && !gotfilter && ((pagename = get_cookie("pagepath")) != NULL)) { if (*pagename) { - sprintf(cmd + strlen(cmd), " page=^%s$", pagename); + cmd = (char *)realloc(cmd, 1024 + 2*strlen(pagename)); + sprintf(cmd + strlen(cmd), " page=^%s$|^%s/.+", pagename, pagename); gotfilter = 1; } - if (p) *p = ';'; } - xfree(cookie); if (sendmessage(cmd, NULL, NULL, &respbuf, 1, BBTALK_TIMEOUT) == BB_OK) { char *bol, *eoln; @@ -327,7 +316,7 @@ if (reqtype == ACK_MANY) { if (!awalk->ackmsg) awalk->ackmsg = ackmsgall; - if (!awalk->validity && validityall) awalk->validity = atoi(validityall); + if (!awalk->validity && validityall) awalk->validity = durationvalue(validityall); } count++; diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-datepage.c ./web/bb-datepage.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-datepage.c 2006-08-09 22:10:12.000000000 +0200 +++ ./web/bb-datepage.c 2006-10-03 12:55:27.000000000 +0200 @@ -108,30 +108,21 @@ parse_query(); if (cgi_method == CGI_POST) { - char *cookie, *pagepath, *p; - char *endurl; + char *pagepath, *cookie, *endurl; - cookie = getenv("HTTP_COOKIE"); - if (cookie == NULL) { - errormsg("Cookies must be enabled\n"); - return 1; + cookie = get_cookie("pagepath"); + + if (cookie && *cookie) { + pagepath = strdup(cookie); } + else { + cookie = get_cookie("host"); - p = strstr(cookie, "pagepath="); if (p) p+= strlen("pagepath="); - if ((p == NULL) || (strlen(p) == 0) || (*p == ';')) { - p = strstr(cookie, "host="); if (p) p += strlen("host="); - if ((p == NULL) || (strlen(p) == 0) || (*p == ';')) { - pagepath = ""; - } - else { - char *hname; + if (cookie && *cookie) { namelist_t *hinfo; - hname = strdup(p); - p = strchr(hname, ';'); if (p) *p = '\0'; - load_hostnames(xgetenv("BBHOSTS"), NULL, get_fqdn()); - hinfo = hostinfo(hname); + hinfo = hostinfo(cookie); if (hinfo) { pagepath = bbh_item(hinfo, BBH_PAGEPATH); } @@ -139,10 +130,9 @@ pagepath = ""; } } - } - else { - pagepath = strdup(p); - p = strchr(pagepath, ';'); if (p) *p = '\0'; + else { + pagepath = ""; + } } endurl = (char *)malloc(strlen(urlprefix) + strlen(pagepath) + 1024); @@ -201,6 +191,7 @@ } sethostenv("", "", "", colorname(bgcolor), NULL); + fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); showform(stdout, hffile, formfn, COL_BLUE, seltime, NULL, NULL); } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-eventlog.c ./web/bb-eventlog.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-eventlog.c 2006-08-09 22:10:12.000000000 +0200 +++ ./web/bb-eventlog.c 2006-10-03 12:55:27.000000000 +0200 @@ -114,6 +114,8 @@ redirect_cgilog("bb-eventlog"); + fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); + cgidata = cgi_request(); if (cgidata == NULL) { /* Present the query form */ @@ -126,8 +128,6 @@ load_hostnames(xgetenv("BBHOSTS"), NULL, get_fqdn()); /* Now generate the webpage */ - printf("Content-Type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); - headfoot(stdout, "event", "", "header", COL_GREEN); fprintf(stdout, "<center>\n"); do_eventlog(stdout, maxcount, maxminutes, fromtime, totime, diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-findhost.c ./web/bb-findhost.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-findhost.c 2006-08-09 22:10:12.000000000 +0200 +++ ./web/bb-findhost.c 2006-10-03 12:55:27.000000000 +0200 @@ -152,6 +152,7 @@ if (cgidata == NULL) { /* Present the query form */ sethostenv("", "", "", colorname(COL_BLUE), NULL); + printf("Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); showform(stdout, "findhost", "findhost_form", COL_BLUE, getcurrenttime(NULL), NULL, NULL); return 0; } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-rep.c ./web/bb-rep.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-rep.c 2006-08-09 22:10:12.000000000 +0200 +++ ./web/bb-rep.c 2006-10-03 12:55:27.000000000 +0200 @@ -230,6 +230,7 @@ if (cgidata == NULL) { /* Present the query form */ sethostenv("", "", "", colorname(COL_BLUE), NULL); + printf("Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); showform(stdout, "report", "report_form", COL_BLUE, getcurrenttime(NULL)-86400, NULL, NULL); return 0; } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-snapshot.c ./web/bb-snapshot.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/bb-snapshot.c 2006-08-09 22:10:12.000000000 +0200 +++ ./web/bb-snapshot.c 2006-10-03 12:55:27.000000000 +0200 @@ -173,6 +173,7 @@ if (cgidata == NULL) { /* Present the query form */ sethostenv("", "", "", colorname(COL_BLUE), NULL); + printf("Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); showform(stdout, "snapshot", "snapshot_form", COL_BLUE, getcurrenttime(NULL), NULL, NULL); return 0; } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-confreport-critical.sh.DIST ./web/hobbit-confreport-critical.sh.DIST --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-confreport-critical.sh.DIST 1970-01-01 01:00:00.000000000 +0100 +++ ./web/hobbit-confreport-critical.sh.DIST 2006-10-03 12:55:27.000000000 +0200 @@ -0,0 +1,10 @@ +#!/bin/sh + +# This is the Hobbit CGI script interface to hobbit-confreport.cgi +# It shows only the statuses on the Critical systems view +# +# Install this script in your webservers' cgi-bin directory + +. @BBHOME@/etc/hobbitcgi.cfg +@RUNTIMEDEFS@ exec @BBHOME@/bin/hobbit-confreport.cgi $CGI_HOBBITCONFREPORT_OPTS --critical + diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-confreport.c ./web/hobbit-confreport.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-confreport.c 2006-08-09 22:10:12.000000000 +0200 +++ ./web/hobbit-confreport.c 2006-10-03 12:56:57.000000000 +0200 @@ -8,7 +8,7 @@ /* */ /*----------------------------------------------------------------------------*/ -static char rcsid[] = "$Id: hobbit-confreport.c,v 1.15 2006/05/31 16:12:38 henrik Rel $"; +static char rcsid[] = "$Id: hobbit-confreport.c,v 1.18 2006/08/19 06:25:27 henrik Exp $"; #include <sys/types.h> #include <sys/stat.h> @@ -45,6 +45,8 @@ static char *coldelim = ";"; static coltext_t *chead = NULL; static int ccount = 0; +static int nkonly = 0; +static int newnkconfig = 1; void errormsg(char *msg) { @@ -131,6 +133,39 @@ closedir(d); } +char *nkval(char *hname, char *tname, char *nkalerts) +{ + static char *result = NULL; + + if (result) xfree(result); + + if (newnkconfig) { + char *key; + nkconf_t *nkrec; + + key = (char *)malloc(strlen(hname) + strlen(tname) + 2); + sprintf(key, "%s|%s", hname, tname); + nkrec = get_nkconfig(key, NKCONF_FIRSTMATCH, NULL); + if (!nkrec) { + result = strdup("No"); + } + else { + char *tspec; + + tspec = (nkrec->nktime ? timespec_text(nkrec->nktime) : "24x7"); + result = (char *)malloc(strlen(tspec) + 30); + sprintf(result, "%s prio %d", tspec, nkrec->priority); + } + xfree(key); + } + else { + result = strdup((checkalert(nkalerts, tname) ? "Yes" : "No")); + } + + return result; +} + + static void print_host(hostlist_t *host, htnames_t *testnames[], int testcount) { int testi, rowcount, netcount; @@ -157,6 +192,7 @@ comment = bbh_item(hinfo, BBH_COMMENT); description = bbh_item(hinfo, BBH_DESCRIPTION); net = bbh_item(hinfo, BBH_NET); + nkalerts = bbh_item(hinfo, BBH_NK); nktime = bbh_item(hinfo, BBH_NKTIME); if (!nktime) nktime = "24x7"; else nktime = strdup(timespec_text(nktime)); downtime = bbh_item(hinfo, BBH_DOWNTIME); if (downtime) downtime = strdup(timespec_text(downtime)); reporttime = bbh_item(hinfo, BBH_REPORTTIME); if (!reporttime) reporttime = "24x7"; else reporttime = strdup(timespec_text(reporttime)); @@ -166,7 +202,7 @@ if (dispname || clientalias) rowcount++; if (comment) rowcount++; if (description) rowcount++; - if (nktime) rowcount++; + if (!newnkconfig && nktime) rowcount++; if (downtime) rowcount++; if (reporttime) rowcount++; @@ -185,13 +221,11 @@ if (pagepathtitle) fprintf(stdout, "<tr><td>Monitoring location: %s</td></tr>\n", pagepathtitle); if (comment) fprintf(stdout, "<tr><td>Comment: %s</td></tr>\n", comment); if (description) fprintf(stdout, "<tr><td>Description: %s</td></tr>\n", description); - if (nktime) fprintf(stdout, "<tr><td>NK monitoring period: %s</td></tr>\n", nktime); + if (!newnkconfig && nktime) fprintf(stdout, "<tr><td>NK monitoring period: %s</td></tr>\n", nktime); if (downtime) fprintf(stdout, "<tr><td>Planned downtime: %s</td></tr>\n", downtime); if (reporttime) fprintf(stdout, "<tr><td>SLA Reporting Period: %s</td></tr>\n", reporttime); - nkalerts = bbh_item(hinfo, BBH_NK); - /* Build a list of the network tests */ itm = bbh_item_walk(hinfo); while (itm) { @@ -344,7 +378,7 @@ use_columndoc(testnames[testi]->name); fprintf(stdout, "<tr>"); fprintf(stdout, "<td valign=top>%s</td>", testnames[testi]->name); - fprintf(stdout, "<td valign=top>%s</td>", (checkalert(nkalerts, testnames[testi]->name) ? "Yes" : "No")); + fprintf(stdout, "<td valign=top>%s</td>", nkval(host->hostname, testnames[testi]->name, nkalerts)); fprintf(stdout, "<td valign=top>"); if (twalk->b1 || twalk->b2 || twalk->b3) { @@ -384,7 +418,7 @@ use_columndoc(testnames[testi]->name); fprintf(stdout, "<tr>"); fprintf(stdout, "<td valign=top>%s</td>", testnames[testi]->name); - fprintf(stdout, "<td valign=top>%s</td>", (checkalert(nkalerts, testnames[testi]->name) ? "Yes" : "No")); + fprintf(stdout, "<td valign=top>%s</td>", nkval(host->hostname, testnames[testi]->name, nkalerts)); fprintf(stdout, "<td valign=top>-/-/-</td>"); /* Make up some default configuration data */ @@ -599,8 +633,8 @@ { int argi, hosti, testi; char *pagepattern = NULL, *hostpattern = NULL; - char *envarea = NULL, *cookie = NULL, *p, *nexthost; - char hobbitcmd[1024], procscmd[1024], svcscmd[1024]; + char *envarea = NULL, *cookie = NULL, *nexthost; + char *hobbitcmd, *procscmd, *svcscmd; int alertcolors, alertinterval; char configfn[PATH_MAX]; char *respbuf = NULL, *procsbuf = NULL, *svcsbuf = NULL; @@ -627,38 +661,50 @@ char *p = strchr(argv[argi], '='); coldelim = strdup(p+1); } + else if (strcmp(argv[argi], "--critical") == 0) { + nkonly = 1; + } + else if (strcmp(argv[argi], "--old-nk-config") == 0) { + newnkconfig = 0; + } } redirect_cgilog("hobbit-confreport"); - /* Setup the filter we use for the report */ - cookie = getenv("HTTP_COOKIE"); - if (cookie && ((p = strstr(cookie, "pagepath=")) != NULL)) { - p += strlen("pagepath="); - pagepattern = strdup(p); - p = strchr(pagepattern, ';'); if (p) *p = '\0'; - if (strlen(pagepattern) == 0) { xfree(pagepattern); pagepattern = NULL; } - } + load_hostnames(xgetenv("BBHOSTS"), NULL, get_fqdn()); + load_nkconfig(NULL); - if (cookie && (!pagepattern) && ((p = strstr(cookie, "host=")) != NULL)) { - p += strlen("host="); - hostpattern = strdup(p); - p = strchr(hostpattern, ';'); if (p) *p = '\0'; - if (strlen(hostpattern) == 0) { xfree(hostpattern); hostpattern = NULL; } - } + /* Setup the filter we use for the report */ + cookie = get_cookie("pagepath"); if (cookie && *cookie) pagepattern = strdup(cookie); + cookie = get_cookie("host"); if (cookie && *cookie) hostpattern = strdup(cookie); /* Fetch the list of host+test statuses we currently know about */ if (pagepattern) { - sprintf(hobbitcmd, "hobbitdboard page=%s fields=hostname,testname", pagepattern); - sprintf(procscmd, "hobbitdboard page=%s test=procs fields=hostname,msg", pagepattern); - sprintf(svcscmd, "hobbitdboard page=%s test=svcs fields=hostname,msg", pagepattern); + hobbitcmd = (char *)malloc(2*strlen(pagepattern) + 1024); + procscmd = (char *)malloc(2*strlen(pagepattern) + 1024); + svcscmd = (char *)malloc(2*strlen(pagepattern) + 1024); + + sprintf(hobbitcmd, "hobbitdboard page=^%s$|^%s/.+ fields=hostname,testname", + pagepattern, pagepattern); + sprintf(procscmd, "hobbitdboard page=^%s$|^%s/.+ test=procs fields=hostname,msg", + pagepattern, pagepattern); + sprintf(svcscmd, "hobbitdboard page=^%s$|^%s/.+ test=svcs fields=hostname,msg", + pagepattern, pagepattern); } else if (hostpattern) { - sprintf(hobbitcmd, "hobbitdboard host=%s fields=hostname,testname", hostpattern); - sprintf(procscmd, "hobbitdboard host=%s test=procs fields=hostname,msg", hostpattern); - sprintf(svcscmd, "hobbitdboard host=%s test=svcs fields=hostname,msg", hostpattern); + hobbitcmd = (char *)malloc(strlen(hostpattern) + 1024); + procscmd = (char *)malloc(strlen(hostpattern) + 1024); + svcscmd = (char *)malloc(strlen(hostpattern) + 1024); + + sprintf(hobbitcmd, "hobbitdboard host=^%s$ fields=hostname,testname", hostpattern); + sprintf(procscmd, "hobbitdboard host=^%s$ test=procs fields=hostname,msg", hostpattern); + sprintf(svcscmd, "hobbitdboard host=^%s$ test=svcs fields=hostname,msg", hostpattern); } else { + hobbitcmd = (char *)malloc(1024); + procscmd = (char *)malloc(1024); + svcscmd = (char *)malloc(1024); + sprintf(hobbitcmd, "hobbitdboard fields=hostname,testname"); sprintf(procscmd, "hobbitdboard test=procs fields=hostname,msg"); sprintf(svcscmd, "hobbitdboard test=svcs fields=hostname,msg"); @@ -686,12 +732,20 @@ nexthost = respbuf; do { char *hname, *tname, *eoln; + int wanted = 1; eoln = strchr(nexthost, '\n'); if (eoln) *eoln = '\0'; hname = nexthost; tname = strchr(nexthost, '|'); if (tname) { *tname = '\0'; tname++; } - if (hname && tname && strcmp(hname, "summary") && strcmp(tname, xgetenv("INFOCOLUMN")) && strcmp(tname, xgetenv("TRENDSCOLUMN"))) { + if (nkonly) { + namelist_t *hinfo = hostinfo(hname); + char *nkalerts = bbh_item(hinfo, BBH_NK); + + if (!nkalerts || (strcmp(nkval(hname, tname, nkalerts), "No") == 0)) wanted = 0; + } + + if (wanted && hname && tname && strcmp(hname, "summary") && strcmp(tname, xgetenv("INFOCOLUMN")) && strcmp(tname, xgetenv("TRENDSCOLUMN"))) { htnames_t *newitem = (htnames_t *)malloc(sizeof(htnames_t)); for (hwalk = hosthead; (hwalk && strcmp(hwalk->hostname, hname)); hwalk = hwalk->next); @@ -726,7 +780,6 @@ qsort(&allhosts[0], hostcount, sizeof(hostlist_t **), host_compare); /* Get the static info */ - load_hostnames(xgetenv("BBHOSTS"), NULL, get_fqdn()); load_all_links(); init_tcp_services(); pingcolumn = xgetenv("PINGCOLUMN"); @@ -753,6 +806,9 @@ fprintf(stdout, "%s ", allhosts[hosti]->hostname); } fprintf(stdout, "</td></tr>\n"); + if (nkonly) { + fprintf(stdout, "<tr><th valign=top align=left>Filter</th><td>Only data for the "Critical Systems" view reported</td></tr>\n"); + } fprintf(stdout, "</table>\n"); headfoot(stdout, "confreport", "", "front", COL_BLUE); diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-confreport.cgi.1 ./web/hobbit-confreport.cgi.1 --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-confreport.cgi.1 2006-08-09 22:10:12.000000000 +0200 +++ ./web/hobbit-confreport.cgi.1 2006-10-03 12:55:27.000000000 +0200 @@ -23,6 +23,14 @@ the Hobbit system. .SH OPTIONS +.IP "--critical" +Report only on the statuses that are configured to show up on the +\fBCritical Systems\fR view. + +.IP "--old-nk-config" +Use the deprecated \fBNK\fR tag in bb-hosts to determine if tests +appear on the Critical Systems view. + .IP "--env=FILENAME" Loads the environment defined in FILENAME before executing the CGI script. diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-enadis.c ./web/hobbit-enadis.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-enadis.c 2006-08-09 22:10:12.000000000 +0200 +++ ./web/hobbit-enadis.c 2006-10-03 12:55:27.000000000 +0200 @@ -289,21 +289,15 @@ if (cgi_method == CGI_GET) { /* - * It's a GET , so the initial request. + * It's a GET, so the initial request. * If we have a pagepath cookie, use that as the initial * host-name filter. */ - char *cookie, *p; + char *pagepath; action = ACT_FILTER; - - cookie = getenv("HTTP_COOKIE"); - if (obeycookies && cookie && ((p = strstr(cookie, "pagepath=")) != NULL)) { - p += strlen("pagepath="); - pagepattern = strdup(p); - p = strchr(pagepattern, ';'); if (p) *p = '\0'; - if (strlen(pagepattern) == 0) { xfree(pagepattern); pagepattern = 0; } - } + pagepath = get_cookie("pagepath"); + if (obeycookies && pagepath && *pagepath) pagepattern = strdup(pagepath); } if (action == ACT_FILTER) { @@ -312,6 +306,7 @@ load_hostnames(xgetenv("BBHOSTS"), NULL, get_fqdn()); sethostenv("", "", "", colorname(COL_BLUE), NULL); sethostenv_filter(hostpattern, pagepattern, ippattern); + printf("Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); showform(stdout, "maint", "maint_form", COL_BLUE, getcurrenttime(NULL), NULL, NULL); return 0; } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-hostgraphs.c ./web/hobbit-hostgraphs.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-hostgraphs.c 2006-08-09 22:10:13.000000000 +0200 +++ ./web/hobbit-hostgraphs.c 2006-10-03 12:55:27.000000000 +0200 @@ -209,24 +209,13 @@ fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); if (action == A_SELECT) { - char *cookie, *p; + char *cookie; - cookie = getenv("HTTP_COOKIE"); - if (cookie && !pagepattern && ((p = strstr(cookie, "pagepath=")) != NULL)) { - /* Match ONLY the exact pagename by using start/end of line markers */ - - p += strlen("pagepath="); - pagepattern = (char *)malloc(strlen(p) + 3); - sprintf(pagepattern, "^%s", p); - p = strchr(pagepattern, ';'); if (p) *p = '\0'; - - if (strlen(pagepattern) == 0) { - xfree(pagepattern); - pagepattern = NULL; - } - else { - strcat(pagepattern, "$"); - } + cookie = get_cookie("pagepath"); + if (!pagepattern && cookie && *cookie) { + /* Match the exact pagename and sub-pages */ + pagepattern = (char *)malloc(10 + 2*strlen(cookie)); + sprintf(pagepattern, "^%s$|^%s/.+", cookie, cookie); } if (hostpattern || pagepattern || ippattern) diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-nkedit.c ./web/hobbit-nkedit.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-nkedit.c 2006-08-09 22:10:13.000000000 +0200 +++ ./web/hobbit-nkedit.c 2006-10-03 12:55:27.000000000 +0200 @@ -238,6 +238,7 @@ if (isaclone && isclonewarning) sprintf(warnmsg, "<SCRIPT LANGUAGE=\"Javascript\" type=\"text/javascript\"> alert('%s'); </SCRIPT>\n", isclonewarning); if (hasclones && hascloneswarning) sprintf(warnmsg, "<SCRIPT LANGUAGE=\"Javascript\" type=\"text/javascript\"> alert('%s'); </SCRIPT>\n", hascloneswarning); + printf("Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); showform(stdout, "nkedit", "nkedit_form", COL_BLUE, getcurrenttime(NULL), warnmsg, NULL); } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-nkview.c ./web/hobbit-nkview.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-nkview.c 2006-08-09 22:10:13.000000000 +0200 +++ ./web/hobbit-nkview.c 2006-10-03 12:55:27.000000000 +0200 @@ -46,8 +46,10 @@ char *bol, *eol; time_t now; char msg[1024]; + int i; - sprintf(msg, "hobbitdboard color=red,yellow acklevel=%d fields=hostname,testname,color,lastchange,logtime,validtime,acklist", nkacklevel); + sprintf(msg, "hobbitdboard acklevel=%d fields=hostname,testname,color,lastchange,logtime,validtime,acklist color=%s", nkacklevel,colorname(mincolor)); + for (i=mincolor+1; (i < COL_COUNT); i++) sprintf(msg+strlen(msg), ",%s", colorname(i)); hobbitdresult = sendmessage(msg, NULL, NULL, &board, 1, BBTALK_TIMEOUT); if (hobbitdresult != BB_OK) { diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-notifylog.c ./web/hobbit-notifylog.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-notifylog.c 1970-01-01 01:00:00.000000000 +0100 +++ ./web/hobbit-notifylog.c 2007-02-09 11:07:18.206269768 +0100 @@ -0,0 +1,138 @@ +/*----------------------------------------------------------------------------*/ +/* Hobbit notification log viewer */ +/* */ +/* Copyright (C) 2007 Henrik Storner <henrik@storner.dk> */ +/* */ +/* This program is released under the GNU General Public License (GPL), */ +/* version 2. See the file "COPYING" for details. */ +/* */ +/*----------------------------------------------------------------------------*/ + +static char rcsid[] = "$Id: hobbit-notifylog.c,v 1.2 2007/02/07 21:49:47 henrik Exp $"; + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <time.h> + +#include "libbbgen.h" + +int maxcount = 100; /* Default: Include last 100 events */ +int maxminutes = 1440; /* Default: for the past 24 hours */ +char *totime = NULL; +char *fromtime = NULL; +char *hostregex = NULL; +char *exhostregex = NULL; +char *testregex = NULL; +char *extestregex = NULL; +char *pageregex = NULL; +char *expageregex = NULL; +char *rcptregex = NULL; +char *exrcptregex = NULL; +cgidata_t *cgidata = NULL; + +static void parse_query(void) +{ + cgidata_t *cwalk; + + cwalk = cgidata; + while (cwalk) { + /* + * cwalk->name points to the name of the setting. + * cwalk->value points to the value (may be an empty string). + */ + + if (strcasecmp(cwalk->name, "MAXCOUNT") == 0) { + maxcount = atoi(cwalk->value); + } + else if (strcasecmp(cwalk->name, "MAXTIME") == 0) { + maxminutes = atoi(cwalk->value); + } + else if (strcasecmp(cwalk->name, "FROMTIME") == 0) { + if (*(cwalk->value)) fromtime = strdup(cwalk->value); + } + else if (strcasecmp(cwalk->name, "TOTIME") == 0) { + if (*(cwalk->value)) totime = strdup(cwalk->value); + } + else if (strcasecmp(cwalk->name, "HOSTMATCH") == 0) { + if (*(cwalk->value)) hostregex = strdup(cwalk->value); + } + else if (strcasecmp(cwalk->name, "EXHOSTMATCH") == 0) { + if (*(cwalk->value)) exhostregex = strdup(cwalk->value); + } + else if (strcasecmp(cwalk->name, "TESTMATCH") == 0) { + if (*(cwalk->value)) testregex = strdup(cwalk->value); + } + else if (strcasecmp(cwalk->name, "EXTESTMATCH") == 0) { + if (*(cwalk->value)) extestregex = strdup(cwalk->value); + } + else if (strcasecmp(cwalk->name, "PAGEMATCH") == 0) { + if (*(cwalk->value)) pageregex = strdup(cwalk->value); + } + else if (strcasecmp(cwalk->name, "EXPAGEMATCH") == 0) { + if (*(cwalk->value)) expageregex = strdup(cwalk->value); + } + else if (strcasecmp(cwalk->name, "RCPTMATCH") == 0) { + if (*(cwalk->value)) rcptregex = strdup(cwalk->value); + } + else if (strcasecmp(cwalk->name, "EXRCPTMATCH") == 0) { + if (*(cwalk->value)) exrcptregex = strdup(cwalk->value); + } + + cwalk = cwalk->next; + } +} + +int main(int argc, char *argv[]) +{ + int argi; + char *envarea = NULL; + + for (argi=1; (argi < argc); argi++) { + if (argnmatch(argv[argi], "--env=")) { + char *p = strchr(argv[argi], '='); + loadenv(p+1, envarea); + } + else if (argnmatch(argv[argi], "--area=")) { + char *p = strchr(argv[argi], '='); + envarea = strdup(p+1); + } + } + + redirect_cgilog("hobbit-notifylog"); + + fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); + + cgidata = cgi_request(); + if (cgidata == NULL) { + /* Present the query form */ + sethostenv("", "", "", colorname(COL_BLUE), NULL); + showform(stdout, "notify", "notify_form", COL_BLUE, getcurrenttime(NULL), NULL, NULL); + return 0; + } + + parse_query(); + load_hostnames(xgetenv("BBHOSTS"), NULL, get_fqdn()); + + /* Now generate the webpage */ + headfoot(stdout, "notify", "", "header", COL_GREEN); + fprintf(stdout, "<center>\n"); + do_notifylog(stdout, maxcount, maxminutes, fromtime, totime, + pageregex, expageregex, + hostregex, exhostregex, + testregex, extestregex, + rcptregex, exrcptregex); + fprintf(stdout, "</center>\n"); + headfoot(stdout, "notify", "", "footer", COL_GREEN); + + return 0; +} + diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-notifylog.sh.DIST ./web/hobbit-notifylog.sh.DIST --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-notifylog.sh.DIST 1970-01-01 01:00:00.000000000 +0100 +++ ./web/hobbit-notifylog.sh.DIST 2007-02-09 11:07:23.802253102 +0100 @@ -0,0 +1,7 @@ +#!/bin/sh + +# This is the Hobbit wrapper for the notifylog CGI + +. @BBHOME@/etc/hobbitcgi.cfg +@RUNTIMEDEFS@ exec @BBHOME@/bin/hobbit-notifylog.cgi $CGI_NOTIFYLOG_OPTS + diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-statusreport.c ./web/hobbit-statusreport.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbit-statusreport.c 2006-08-09 22:10:13.000000000 +0200 +++ ./web/hobbit-statusreport.c 2006-10-03 12:55:27.000000000 +0200 @@ -26,7 +26,7 @@ { char *envarea = NULL; char *server = NULL; - char *cookie, *p, *pagefilter = ""; + char *cookie, *pagefilter = ""; char *filter = NULL; char *heading = NULL; int showcolors = 1; @@ -108,13 +108,10 @@ if (!allhosts) { /* Setup the filter we use for the report */ - cookie = getenv("HTTP_COOKIE"); - if (cookie && ((p = strstr(cookie, "pagepath=")) != NULL)) { - p += strlen("pagepath="); - pagefilter = malloc(strlen(p) + 6); - sprintf(pagefilter, "page=%s", p); - p = strchr(pagefilter, ';'); if (p) *p = '\0'; - if (strlen(pagefilter) == 0) { xfree(pagefilter); pagefilter = ""; } + cookie = get_cookie("pagepath"); + if (cookie && *cookie) { + pagefilter = malloc(10 + 2*strlen(cookie)); + sprintf(pagefilter, "page=^%s$|^%s/.+", cookie, cookie); } } diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbitgraph.c ./web/hobbitgraph.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbitgraph.c 2006-08-09 22:10:13.000000000 +0200 +++ ./web/hobbitgraph.c 2006-10-03 12:55:27.000000000 +0200 @@ -63,6 +63,7 @@ int graphwidth = 0; int graphheight = 0; int ignorestalerrds = 0; +int bgcolor = COL_GREEN; int coloridx = 0; char *colorlist[] = { @@ -216,6 +217,10 @@ else if (strcmp(cwalk->name, "nostale") == 0) { ignorestalerrds = 1; } + else if (strcmp(cwalk->name, "color") == 0) { + int color = parse_color(cwalk->value); + if (color != -1) bgcolor = color; + } cwalk = cwalk->next; } @@ -440,8 +445,8 @@ case ACT_MENU: fprintf(output, " <td align=\"left\"><img src=\"%s&action=view&graph=%s\" alt=\"%s graph\"></td>\n", uri, grtype, grtype); - fprintf(output, " <td align=\"left\" valign=\"top\"> <a href=\"%s&graph=%s&action=selzoom\"> <img src=\"%s/zoom.gif\" border=0 alt=\"Zoom graph\" style='padding: 3px'> </a> </td>\n", - uri, grtype, getenv("BBSKIN")); + fprintf(output, " <td align=\"left\" valign=\"top\"> <a href=\"%s&graph=%s&action=selzoom&color=%s\"> <img src=\"%s/zoom.gif\" border=0 alt=\"Zoom graph\" style='padding: 3px'> </a> </td>\n", + uri, grtype, colorname(bgcolor), getenv("BBSKIN")); break; case ACT_SELZOOM: @@ -538,8 +543,8 @@ else p += sprintf(p, "?host=%s", hostname); - p += sprintf(p, "&service=%s&graph_height=%d&graph_width=%d", - service, graphheight, graphwidth); + p += sprintf(p, "&service=%s&graph_height=%d&graph_width=%d&color=%s", + service, graphheight, graphwidth, colorname(bgcolor)); if (displayname != hostname) p += sprintf(p, "&disp=%s", displayname); if (firstidx != -1) p += sprintf(p, "&first=%d", firstidx+1); if (idxcount != -1) p += sprintf(p, "&count=%d", idxcount); @@ -548,8 +553,8 @@ switch (action) { case ACT_MENU: fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); - sethostenv(displayname, "", service, colorname(COL_GREEN), hostname); - headfoot(stdout, "graphs", "", "header", COL_GREEN); + sethostenv(displayname, "", service, colorname(bgcolor), hostname); + headfoot(stdout, "graphs", "", "header", bgcolor); fprintf(stdout, "<table align=\"center\" summary=\"Graphs\">\n"); @@ -560,13 +565,13 @@ fprintf(stdout, "</table>\n"); - headfoot(stdout, "graphs", "", "footer", COL_GREEN); + headfoot(stdout, "graphs", "", "footer", bgcolor); return 0; case ACT_SELZOOM: fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); - sethostenv(displayname, "", service, colorname(COL_GREEN), hostname); - headfoot(stdout, "graphs", "", "header", COL_GREEN); + sethostenv(displayname, "", service, colorname(bgcolor), hostname); + headfoot(stdout, "graphs", "", "header", bgcolor); fprintf(stdout, " <div id='zoomBox' style='position:absolute; overflow:none; left:0px; top:0px; width:0px; height:0px; visibility:visible; background:red; filter:alpha(opacity=50); -moz-opacity:0.5; -khtml-opacity:0.5'></div>\n"); @@ -608,13 +613,13 @@ } - headfoot(stdout, "graphs", "", "footer", COL_GREEN); + headfoot(stdout, "graphs", "", "footer", bgcolor); return 0; case ACT_SHOWZOOM: fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE")); - sethostenv(displayname, "", service, colorname(COL_GREEN), hostname); - headfoot(stdout, "graphs", "", "header", COL_GREEN); + sethostenv(displayname, "", service, colorname(bgcolor), hostname); + headfoot(stdout, "graphs", "", "header", bgcolor); fprintf(stdout, "<table align=\"center\" summary=\"Graphs\">\n"); @@ -622,7 +627,7 @@ fprintf(stdout, "</table>\n"); - headfoot(stdout, "graphs", "", "footer", COL_GREEN); + headfoot(stdout, "graphs", "", "footer", bgcolor); return 0; case ACT_VIEW: diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbitsvc-info.c ./web/hobbitsvc-info.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbitsvc-info.c 2006-08-09 22:10:13.000000000 +0200 +++ ./web/hobbitsvc-info.c 2006-10-03 13:52:29.000000000 +0200 @@ -40,7 +40,7 @@ char *name; int color; char *dismsg; - time_t distime; + time_t distime, lastchange; struct hinf_t *next; } hinf_t; hinf_t *tnames = NULL; @@ -73,7 +73,7 @@ int testsz; int haveuname = 0; - sprintf(hobbitcmd, "hobbitdboard fields=testname,color,disabletime,dismsg,client host=%s", hostname); + sprintf(hobbitcmd, "hobbitdboard fields=testname,color,disabletime,dismsg,client,lastchange host=^%s$", hostname); if (sendmessage(hobbitcmd, NULL, NULL, &statuslist, 1, BBTALK_TIMEOUT) != BB_OK) { return 1; } @@ -94,7 +94,8 @@ if (tok) { tnames[testcount].color = parse_color(tok); tok = gettok(NULL, "|"); } if (tok) { tnames[testcount].distime = atol(tok); tok = gettok(NULL, "|"); } if (tok) { tnames[testcount].dismsg = strdup(tok); tok = gettok(NULL, "|"); } - if (tok) { haveuname |= (*tok == 'Y'); } + if (tok) { haveuname |= (*tok == 'Y'); tok = gettok(NULL, "|"); } + if (tok) { tnames[testcount].lastchange = atol(tok); } tnames[testcount].next = NULL; testcount++; if (testcount == testsz) { @@ -113,7 +114,7 @@ /* Sort them so the display looks prettier */ qsort(&tnames[0], testcount, sizeof(hinf_t), test_name_compare); - xfree(statuslist); statuslist = NULL; + if (statuslist) xfree(statuslist); statuslist = NULL; sprintf(hobbitcmd, "schedule"); @@ -241,6 +242,108 @@ } +static void generate_hobbit_statuslist(char *hostname, strbuffer_t *buf) +{ + char msgline[4096]; + char datestr[100]; + int i, btncount; + char *bbdatefmt; + strbuffer_t *servRed, *servYellow, *servPurple, *servBlue; + time_t logage; + + bbdatefmt = xgetenv("BBDATEFORMAT"); + + servRed = newstrbuffer(0); + servYellow = newstrbuffer(0); + servPurple = newstrbuffer(0); + servBlue = newstrbuffer(0); + + addtobuffer(buf, "<tr><th align=left valign=top>Status summary</th><td align=left>\n"); + addtobuffer(buf, "<form name=\"colorsel\" action=\"nosubmit\" method=\"GET\">\n"); + addtobuffer(buf, "<table summary=\"Status summary\" border=1>\n"); + addtobuffer(buf, "<tr><th>Service</th><th>Since</th><th>Duration</th></tr>\n"); + + for (i = 0; i < testcount; i++) { + strftime(datestr, sizeof(datestr), bbdatefmt, localtime(&tnames[i].lastchange)); + logage = time(NULL) - tnames[i].lastchange; + + addtobuffer(buf, "<tr>"); + + sprintf(msgline, "<td><img src=\"%s/%s\" height=\"%s\" width=\"%s\" border=0 alt=\"%s status\"> %s</td>", + xgetenv("BBSKIN"), dotgiffilename(tnames[i].color, 0, 1), + xgetenv("DOTHEIGHT"), xgetenv("DOTWIDTH"), + colorname(tnames[i].color), tnames[i].name); + addtobuffer(buf, msgline); + + sprintf(msgline, "<td>%s</td>", datestr); + addtobuffer(buf, msgline); + + sprintf(msgline, "<td align=right>%d days, %02d hours, %02d minutes</td>", + (int)(logage / 86400),(int) ((logage % 86400) / 3600),(int) ((logage % 3600) / 60)); + addtobuffer(buf, msgline); + + addtobuffer(buf, "</tr>\n"); + + sprintf(msgline, ",%s", tnames[i].name); + switch (tnames[i].color) { + case COL_BLUE : addtobuffer(servBlue, msgline); break; + case COL_RED : addtobuffer(servRed, msgline); break; + case COL_YELLOW : addtobuffer(servYellow, msgline); break; + case COL_PURPLE : addtobuffer(servPurple, msgline); break; + } + } + + btncount = 0; + if (STRBUFLEN(servRed) > 0) btncount++; + if (STRBUFLEN(servYellow) > 0) btncount++; + if (STRBUFLEN(servPurple) > 0) btncount++; + if (STRBUFLEN(servBlue) > 0) btncount++; + if (btncount > 0) { + addtobuffer(buf, "<tr><td colspan=3>\n"); + + addtobuffer(buf, "<table width=\"100%\">\n"); + sprintf(msgline, "<tr><th colspan=%d><center><i>Toggle tests to disable</i></center></th></tr>\n", btncount); + addtobuffer(buf, msgline); + + addtobuffer(buf, "<tr>\n"); + if (STRBUFLEN(servRed) > 0) { + addtobuffer(buf, "<td align=center><input type=button value=\"Toggle red\" onClick=\"mark4Disable('"); + addtostrbuffer(buf, servRed); + addtobuffer(buf, ",');\"></td>\n"); + } + if (STRBUFLEN(servYellow) > 0) { + addtobuffer(buf, "<td align=center><input type=button value=\"Toggle yellow\" onClick=\"mark4Disable('"); + addtostrbuffer(buf, servYellow); + addtobuffer(buf, ",');\"></td>\n"); + } + if (STRBUFLEN(servPurple) > 0) { + addtobuffer(buf, "<td align=center><input type=button value=\"Toggle purple\" onClick=\"mark4Disable('"); + addtostrbuffer(buf, servPurple); + addtobuffer(buf, ",');\"></td>\n"); + } + if (STRBUFLEN(servBlue) > 0) { + addtobuffer(buf, "<td align=center><input type=button value=\"Toggle blue\" onClick=\"mark4Disable('"); + addtostrbuffer(buf, servBlue); + addtobuffer(buf, ",');\"></td>\n"); + } + + addtobuffer(buf, "</tr>\n"); + addtobuffer(buf, "</table>\n"); + + addtobuffer(buf,"</td></tr>\n"); + + } + + addtobuffer(buf,"</table></form>\n"); + addtobuffer(buf, "</td></tr>\n"); + addtobuffer(buf, "<tr><td colspan=2> </td></tr>\n"); + + freestrbuffer(servRed); + freestrbuffer(servYellow); + freestrbuffer(servPurple); + freestrbuffer(servBlue); +} + static void generate_hobbit_disable(char *hostname, strbuffer_t *buf) { int i; @@ -256,7 +359,7 @@ beginyear = nowtm->tm_year + 1900; endyear = nowtm->tm_year + 1900 + 5; - sprintf(l, "<form method=\"post\" action=\"%s/hobbit-enadis.sh\">\n", xgetenv("SECURECGIBINURL")); + sprintf(l, "<form name=\"disableform\" method=\"post\" action=\"%s/hobbit-enadis.sh\">\n", xgetenv("SECURECGIBINURL")); addtobuffer(buf, l); sprintf(l, "<table summary=\"%s disable\" border=1>\n", hostname); addtobuffer(buf, l); @@ -266,7 +369,18 @@ addtobuffer(buf, "<td rowspan=2><select multiple size=\"15\" name=\"disabletest\">\n"); addtobuffer(buf, "<option value=\"*\">ALL</option>\n"); for (i=0; (i < testcount); i++) { - sprintf(l, "<option value=\"%s\">%s</option>\n", tnames[i].name, tnames[i].name); + char *colstyle; + switch (tnames[i].color) { + case COL_RED: colstyle = "color: red"; break; + case COL_YELLOW: colstyle = "color: #FFDE0F"; break; + case COL_GREEN: colstyle = "color: green"; break; + case COL_BLUE: colstyle = "color: blue;"; break; + case COL_PURPLE: colstyle = "color: fuchsia;"; break; + default: colstyle = "color: black;"; break; + } + + sprintf(l, "<option value=\"%s\" style=\"%s\">%s</option>\n", + tnames[i].name, colstyle, tnames[i].name); addtobuffer(buf, l); } addtobuffer(buf, "</select></td>\n"); @@ -544,7 +658,7 @@ if (strcmp(val, "0.0.0.0") == 0) { struct in_addr addr; struct hostent *hent; - static char hostip[IP_ADDR_STRLEN]; + static char hostip[IP_ADDR_STRLEN + 20]; hent = gethostbyname(hostname); if (hent) { @@ -809,6 +923,7 @@ if (gotstatus && showenadis) { int i, anydisabled = 0; + generate_hobbit_statuslist(hostname, infobuf); addtobuffer(infobuf, "<tr><th align=left valign=top>Disable tests</th><td align=left>\n"); generate_hobbit_disable(hostname, infobuf); addtobuffer(infobuf, "</td></tr>\n"); diff -urN /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbitsvc-trends.c ./web/hobbitsvc-trends.c --- /home/henrik/hobbit/release/hobbit-4.2.0/web/hobbitsvc-trends.c 2006-08-09 22:10:13.000000000 +0200 +++ ./web/hobbitsvc-trends.c 2006-10-03 12:55:27.000000000 +0200 @@ -125,7 +125,7 @@ /* If no rrdgraphs definition, include all with default links */ if (hostrrdgraphs == NULL) { dbgprintf("rrdlink_text: Standard URL (no rrdgraphs)\n"); - return hobbit_graph_data(host->bbhostname, hostdisplayname, NULL, rrd->gdef, rrd->count, + return hobbit_graph_data(host->bbhostname, hostdisplayname, NULL, -1, rrd->gdef, rrd->count, HG_WITH_STALE_RRDS, wantmeta); } @@ -141,7 +141,7 @@ dbgprintf("rrdlink_text: Default URL included\n"); /* Yes, return default link for this RRD */ - return hobbit_graph_data(host->bbhostname, hostdisplayname, NULL, rrd->gdef, rrd->count, + return hobbit_graph_data(host->bbhostname, hostdisplayname, NULL, -1, rrd->gdef, rrd->count, HG_WITH_STALE_RRDS, wantmeta); } else { @@ -193,7 +193,7 @@ myrrd->gdef->maxgraphs = 0; myrrd->count = rrd->count; myrrd->next = NULL; - partlink = hobbit_graph_data(host->bbhostname, hostdisplayname, NULL, myrrd->gdef, myrrd->count, + partlink = hobbit_graph_data(host->bbhostname, hostdisplayname, NULL, -1, myrrd->gdef, myrrd->count, HG_WITH_STALE_RRDS, wantmeta); if ((strlen(rrdlink) + strlen(partlink) + 1) >= rrdlinksize) { rrdlinksize += strlen(partlink) + 4096; @@ -215,7 +215,7 @@ } else { /* It is included with the default graph */ - return hobbit_graph_data(host->bbhostname, hostdisplayname, NULL, rrd->gdef, rrd->count, + return hobbit_graph_data(host->bbhostname, hostdisplayname, NULL, -1, rrd->gdef, rrd->count, HG_WITH_STALE_RRDS, wantmeta); }