1 /*----------------------------------------------------------------------------*/
2 /* Xymon CGI tool to generate a report of the Xymon configuration             */
3 /*                                                                            */
4 /* Copyright (C) 2003-2011 Henrik Storner <henrik@storner.dk>                 */
5 /*                                                                            */
6 /* This program is released under the GNU General Public License (GPL),       */
7 /* version 2. See the file "COPYING" for details.                             */
8 /*                                                                            */
9 /*----------------------------------------------------------------------------*/
10 
11 static char rcsid[] = "$Id: confreport.c 8069 2019-07-23 15:29:06Z jccleaver $";
12 
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <limits.h>
16 #include <stdio.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include <time.h>
23 #include <dirent.h>
24 
25 #include "libxymon.h"
26 
27 typedef struct hostlist_t {
28 	char *hostname;
29 	int testcount;
30 	htnames_t *tests;
31 	htnames_t *disks, *svcs, *procs;
32 	struct hostlist_t *next;
33 } hostlist_t;
34 
35 typedef struct coltext_t {
36 	char *colname;
37 	char *coldescr;
38 	int used;
39 	struct coltext_t *next;
40 } coltext_t;
41 
42 hostlist_t *hosthead = NULL;
43 static char *pingcolumn = "conn";
44 static char *coldelim = ";";
45 static coltext_t *chead = NULL;
46 static int ccount = 0;
47 static int criticalonly = 0;
48 static int newcritconfig = 1;
49 
50 SBUF_DEFINE(pingplus);
51 
errormsg(char * msg)52 void errormsg(char *msg)
53 {
54         printf("Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
55         printf("<html><head><title>Invalid request</title></head>\n");
56         printf("<body>%s</body></html>\n", msg);
57         exit(1);
58 }
59 
host_compare(const void * v1,const void * v2)60 static int host_compare(const void *v1, const void *v2)
61 {
62 	hostlist_t **h1 = (hostlist_t **)v1;
63 	hostlist_t **h2 = (hostlist_t **)v2;
64 
65 	return strcmp((*h1)->hostname, (*h2)->hostname);
66 }
67 
test_compare(const void * v1,const void * v2)68 static int test_compare(const void *v1, const void *v2)
69 {
70 	htnames_t **t1 = (htnames_t **)v1;
71 	htnames_t **t2 = (htnames_t **)v2;
72 
73 	return strcmp((*t1)->name, (*t2)->name);
74 }
75 
76 
is_net_test(char * tname)77 static int is_net_test(char *tname)
78 {
79 	char *miscnet[] = { NULL,  "http", "dns", "dig", "rpc", "ntp", "ldap", "content", "sslcert", NULL };
80 	int i;
81 
82 	miscnet[0] = pingcolumn; /* Cannot be computed in advance */
83 	if (find_tcp_service(tname)) return 1;
84 	for (i=0; (miscnet[i]); i++) if (strcmp(tname, miscnet[i]) == 0) return 1;
85 
86 	return 0;
87 }
88 
89 
use_columndoc(char * column)90 void use_columndoc(char *column)
91 {
92 	coltext_t *cwalk;
93 
94 	for (cwalk = chead; (cwalk && strcasecmp(cwalk->colname, column)); cwalk = cwalk->next);
95 	if (cwalk) cwalk->used = 1;
96 }
97 
98 typedef struct tag_t {
99 	char *columnname;
100 	char *visualdata;	/* The URL or other end-user visible test spec. */
101 	char *expdata;
102 	int b1, b2, b3;		/* "badFOO" values, if any */
103 	struct tag_t *next;
104 } tag_t;
105 
print_disklist(char * hostname)106 static void print_disklist(char *hostname)
107 {
108 	/*
109 	 * We get the list of monitored disks/filesystems by looking at the
110 	 * set of disk RRD files for this host. That way we do not have to
111 	 * parse the disk status reports that come in many different flavours.
112 	 */
113 
114 	char dirname[PATH_MAX];
115 	char fn[PATH_MAX];
116 	DIR *d;
117 	struct dirent *de;
118 	char *p;
119 
120 	snprintf(dirname, sizeof(dirname)-1, "%s/%s", xgetenv("XYMONRRDS"), hostname);
121 	d = opendir(dirname);
122 	if (!d) return;
123 
124 	while ((de = readdir(d)) != NULL) {
125 		if (strncmp(de->d_name, "disk,", 5) == 0) {
126 			strncpy(fn, de->d_name + 4, sizeof(fn));
127 			p = strstr(fn, ".rrd"); if (!p) continue;
128 			*p = '\0';
129 			p = fn; while ((p = strchr(p, ',')) != NULL) *p = '/';
130 			fprintf(stdout, "%s<br>\n", fn);
131 		}
132 	}
133 
134 	closedir(d);
135 }
136 
criticalval(char * hname,char * tname,char * alerts)137 char *criticalval(char *hname, char *tname, char *alerts)
138 {
139 	STATIC_SBUF_DEFINE(result);
140 
141 	if (result) xfree(result);
142 
143 	if (newcritconfig) {
144 		SBUF_DEFINE(key);
145 		critconf_t *critrec;
146 
147 		SBUF_MALLOC(key, strlen(hname) + strlen(tname) + 2);
148 		snprintf(key, key_buflen, "%s|%s", hname, tname);
149 		critrec = get_critconfig(key, CRITCONF_FIRSTMATCH, NULL);
150 		if (!critrec) {
151 			result = strdup("No");
152 		}
153 		else {
154 			char *tspec;
155 
156 			tspec = (critrec->crittime ? timespec_text(critrec->crittime) : "24x7");
157 			SBUF_MALLOC(result, strlen(tspec) + 30);
158 			snprintf(result, result_buflen, "%s&nbsp;prio&nbsp;%d", tspec, critrec->priority);
159 		}
160 		xfree(key);
161 	}
162 	else {
163 		result = strdup((checkalert(alerts, tname) ? "Yes" : "No"));
164 	}
165 
166 	return result;
167 }
168 
169 
print_host(hostlist_t * host,htnames_t * testnames[],int testcount)170 static void print_host(hostlist_t *host, htnames_t *testnames[], int testcount)
171 {
172 	int testi, rowcount, netcount;
173 	void *hinfo = hostinfo(host->hostname);
174 	char *dispname = NULL, *clientalias = NULL, *comment = NULL, *description = NULL, *pagepathtitle = NULL;
175 	char *net = NULL, *alerts = NULL;
176 	char *crittime = NULL, *downtime = NULL, *reporttime = NULL;
177 	char *itm;
178 	tag_t *taghead = NULL;
179 	int contidx = 0, haveping = 0;
180 	char contcol[1024];
181 	activealerts_t *alert;
182 	strbuffer_t *buf = newstrbuffer(0);
183 
184 	fprintf(stdout, "<p style=\"page-break-before: always\">\n");
185 	fprintf(stdout, "<table width=\"100%%\" border=1 summary=\"%s configuration\">\n", host->hostname);
186 
187 	pagepathtitle = xmh_item(hinfo, XMH_PAGEPATHTITLE);
188 	if (!pagepathtitle || (strlen(pagepathtitle) == 0)) pagepathtitle = "Top page";
189 	dispname = xmh_item(hinfo, XMH_DISPLAYNAME);
190 	if (dispname && (strcmp(dispname, host->hostname) == 0)) dispname = NULL;
191 	clientalias = xmh_item(hinfo, XMH_CLIENTALIAS);
192 	if (clientalias && (strcmp(clientalias, host->hostname) == 0)) clientalias = NULL;
193 	comment = xmh_item(hinfo, XMH_COMMENT);
194 	description = xmh_item(hinfo, XMH_DESCRIPTION);
195 	net = xmh_item(hinfo, XMH_NET);
196 	alerts = xmh_item(hinfo, XMH_NK);
197 	crittime = xmh_item(hinfo, XMH_NKTIME); if (!crittime) crittime = "24x7"; else crittime = strdup(timespec_text(crittime));
198 	downtime = xmh_item(hinfo, XMH_DOWNTIME); if (downtime) downtime = strdup(timespec_text(downtime));
199 	reporttime = xmh_item(hinfo, XMH_REPORTTIME); if (!reporttime) reporttime = "24x7"; else reporttime = strdup(timespec_text(reporttime));
200 
201 	rowcount = 1;
202 	if (pagepathtitle) rowcount++;
203 	if (dispname || clientalias) rowcount++;
204 	if (comment) rowcount++;
205 	if (description) rowcount++;
206 	if (!newcritconfig && crittime) rowcount++;
207 	if (downtime) rowcount++;
208 	if (reporttime) rowcount++;
209 
210 	fprintf(stdout, "<tr>\n");
211 	fprintf(stdout, "<th rowspan=%d align=left width=\"25%%\" valign=top>Basics</th>\n", rowcount);
212 	fprintf(stdout, "<th align=center>%s (%s)</th>\n",
213 		(dispname ? dispname : host->hostname), xmh_item(hinfo, XMH_IP));
214 	fprintf(stdout, "</tr>\n");
215 
216 	if (dispname || clientalias) {
217 		fprintf(stdout, "<tr><td>Aliases:");
218 		if (dispname) fprintf(stdout, " %s", dispname);
219 		if (clientalias) fprintf(stdout, " %s", clientalias);
220 		fprintf(stdout, "</td></tr>\n");
221 	}
222 	if (pagepathtitle) fprintf(stdout, "<tr><td>Monitoring location: %s</td></tr>\n", pagepathtitle);
223 	if (comment) fprintf(stdout, "<tr><td>Comment: %s</td></tr>\n", comment);
224 	if (description) fprintf(stdout, "<tr><td>Description: %s</td></tr>\n", description);
225 	if (!newcritconfig && crittime) fprintf(stdout, "<tr><td>Critical monitoring period: %s</td></tr>\n", crittime);
226 	if (downtime) fprintf(stdout, "<tr><td>Planned downtime: %s</td></tr>\n", downtime);
227 	if (reporttime) fprintf(stdout, "<tr><td>SLA Reporting Period: %s</td></tr>\n", reporttime);
228 
229 
230 	/* Build a list of the network tests */
231 	itm = xmh_item_walk(hinfo);
232 	while (itm) {
233 		char *visdata = NULL, *colname = NULL, *expdata = NULL;
234 		weburl_t bu;
235 		int httpextra = 0;
236 
237 		/* Skip modifiers */
238 		itm += strspn(itm, "?!~");
239 
240 		if ( argnmatch(itm, "http")         ||
241 		     argnmatch(itm, "content=http") ||
242 		     argnmatch(itm, "cont;http")    ||
243 		     argnmatch(itm, "cont=")        ||
244 		     argnmatch(itm, "nocont;http")  ||
245 		     argnmatch(itm, "nocont=")      ||
246 		     argnmatch(itm, "post;http")    ||
247 		     argnmatch(itm, "post=")        ||
248 		     argnmatch(itm, "nopost;http")  ||
249 		     argnmatch(itm, "nopost=")      ||
250 		     argnmatch(itm, "type;http")    ||
251 		     argnmatch(itm, "type=")        ) {
252 			visdata = decode_url(itm, &bu);
253 			colname = bu.columnname;
254 			if (!colname) {
255 				if (bu.expdata) {
256 					httpextra = 1;
257 					if (contidx == 0) {
258 						colname = "content";
259 						contidx++;
260 					}
261 					else {
262 						snprintf(contcol, sizeof(contcol)-1, "content%d", contidx);
263 						colname = contcol;
264 						contidx++;
265 					}
266 				}
267 				else {
268 					colname = "http";
269 				}
270 			}
271 			expdata = bu.expdata;
272 		}
273 		else if (strncmp(itm, "rpc=", 4) == 0) {
274 			colname = "rpc";
275 			visdata = strdup(itm+4);
276 		}
277 		else if (strncmp(itm, "dns=", 4) == 0) {
278 			colname = "dns";
279 			visdata = strdup(itm+4);
280 		}
281 		else if (strncmp(itm, "dig=", 4) == 0) {
282 			colname = "dns";
283 			visdata = strdup(itm+4);
284 		}
285 		else if (strncmp(itm, pingplus, strlen(pingplus)) == 0) {
286 			haveping = 1;
287 			colname = pingcolumn;
288 			visdata = strdup(itm+strlen(pingplus));
289 		}
290 		else if (is_net_test(itm)) {
291 			colname = strdup(itm);
292 			visdata = strdup("");
293 		}
294 
295 		if (!visdata) visdata = strdup("");
296 		if (colname) {
297 			tag_t *newitem;
298 
299 addtolist:
300 			for (newitem = taghead; (newitem && strcmp(newitem->columnname, colname)); newitem = newitem->next);
301 
302 			if (!newitem) {
303 				newitem = (tag_t *)calloc(1, sizeof(tag_t));
304 				newitem->columnname = strdup(colname);
305 				newitem->visualdata = (visdata ? strdup(visdata) : NULL);
306 				newitem->expdata = (expdata ? strdup(expdata) : NULL);
307 				newitem->next = taghead;
308 				taghead = newitem;
309 			}
310 			else {
311 				/* Multiple tags for one column - must be http */
312 				newitem->visualdata = newitem->visualdata ?
313 					(char *)realloc(newitem->visualdata, strlen(newitem->visualdata) + strlen(visdata) + 5) :
314 					(char *)malloc(strlen(visdata) + 5);
315 				strcat(newitem->visualdata, "<br>");
316 				strcat(newitem->visualdata, visdata);
317 			}
318 
319 			if (httpextra) {
320 				httpextra = 0;
321 				colname = "http";
322 				expdata = NULL;
323 				goto addtolist;
324 			}
325 		}
326 
327 		itm = xmh_item_walk(NULL);
328 	}
329 
330 	if (!haveping && !xmh_item(hinfo, XMH_FLAG_NOCONN)) {
331 		for (testi = 0; (testi < testcount); testi++) {
332 			if (strcmp(testnames[testi]->name, pingcolumn) == 0) {
333 				tag_t *newitem = (tag_t *)calloc(1, sizeof(tag_t));
334 				newitem->columnname = strdup(pingcolumn);
335 				newitem->next = taghead;
336 				taghead = newitem;
337 			}
338 		}
339 	}
340 
341 	/* Add the "badFOO" settings */
342 	itm = xmh_item_walk(hinfo);
343 	while (itm) {
344 		if (strncmp(itm, "bad", 3) == 0) {
345 			char *tname, *p;
346 			int b1, b2, b3, n = -1;
347 			tag_t *tag = NULL;
348 
349 			tname = itm+3;
350 			p = strchr(tname, ':');
351 			if (p) {
352 				*p = '\0';
353 				n = sscanf(p+1, "%d:%d:%d", &b1, &b2, &b3);
354 				for (tag = taghead; (tag && strcmp(tag->columnname, tname)); tag = tag->next);
355 				*p = ':';
356 			}
357 
358 			if (tag && (n == 3)) {
359 				tag->b1 = b1; tag->b2 = b2; tag->b3 = b3;
360 			}
361 		}
362 
363 		itm = xmh_item_walk(NULL);
364 	}
365 
366 	if (taghead) {
367 		fprintf(stdout, "<tr>\n");
368 		fprintf(stdout, "<th align=left valign=top>Network tests");
369 		if (net) fprintf(stdout, "<br>(from %s)", net);
370 		fprintf(stdout, "</th>\n");
371 
372 		fprintf(stdout, "<td><table border=0 cellpadding=\"3\" cellspacing=\"5\" summary=\"%s network tests\">\n", host->hostname);
373 		fprintf(stdout, "<tr><th align=left valign=top>Service</th><th align=left valign=top>Critical</th><th align=left valign=top>C/Y/R limits</th><th align=left valign=top>Specifics</th></tr>\n");
374 	}
375 	for (testi = 0, netcount = 0; (testi < testcount); testi++) {
376 		tag_t *twalk;
377 
378 		for (twalk = taghead; (twalk && strcasecmp(twalk->columnname, testnames[testi]->name)); twalk = twalk->next);
379 		if (!twalk) continue;
380 
381 		use_columndoc(testnames[testi]->name);
382 		fprintf(stdout, "<tr>");
383 		fprintf(stdout, "<td valign=top>%s</td>", testnames[testi]->name);
384 		fprintf(stdout, "<td valign=top>%s</td>", criticalval(host->hostname, testnames[testi]->name, alerts));
385 
386 		fprintf(stdout, "<td valign=top>");
387 		if (twalk->b1 || twalk->b2 || twalk->b3) {
388 			fprintf(stdout, "%d/%d/%d", twalk->b1, twalk->b2, twalk->b3);
389 		}
390 		else {
391 			fprintf(stdout, "-/-/-");
392 		}
393 		fprintf(stdout, "</td>");
394 
395 		fprintf(stdout, "<td valign=top>");
396 		fprintf(stdout, "<i>%s</i>", (twalk->visualdata ? twalk->visualdata : "&nbsp;"));
397 		if (twalk->expdata) fprintf(stdout, " must return <i>'%s'</i>", twalk->expdata);
398 		fprintf(stdout, "</td>");
399 
400 		fprintf(stdout, "</tr>");
401 		netcount++;
402 	}
403 	if (taghead) {
404 		fprintf(stdout, "</table></td>\n");
405 		fprintf(stdout, "</tr>\n");
406 	}
407 
408 
409 	if (netcount != testcount) {
410 		fprintf(stdout, "<tr>\n");
411 		fprintf(stdout, "<th align=left valign=top>Local tests</th>\n");
412 		fprintf(stdout, "<td><table border=0 cellpadding=\"3\" cellspacing=\"5\" summary=\"%s local tests\">\n", host->hostname);
413 		fprintf(stdout, "<tr><th align=left valign=top>Service</th><th align=left valign=top>Critical</th><th align=left valign=top>C/Y/R limits</th><th align=left valign=top>Configuration <i>(NB: Thresholds on client may differ)</i></th></tr>\n");
414 	}
415 	for (testi = 0; (testi < testcount); testi++) {
416 		tag_t *twalk;
417 
418 		for (twalk = taghead; (twalk && strcasecmp(twalk->columnname, testnames[testi]->name)); twalk = twalk->next);
419 		if (twalk) continue;
420 
421 		use_columndoc(testnames[testi]->name);
422 		fprintf(stdout, "<tr>");
423 		fprintf(stdout, "<td valign=top>%s</td>", testnames[testi]->name);
424 		fprintf(stdout, "<td valign=top>%s</td>", criticalval(host->hostname, testnames[testi]->name, alerts));
425 		fprintf(stdout, "<td valign=top>-/-/-</td>");
426 
427 		/* Make up some default configuration data */
428 		fprintf(stdout, "<td valign=top>");
429 		if (strcmp(testnames[testi]->name, "cpu") == 0) {
430 			fprintf(stdout, "UNIX - Yellow: Load average > 1.5, Red: Load average > 3.0<br>");
431 			fprintf(stdout, "Windows - Yellow: CPU utilisation > 80%%, Red: CPU utilisation > 95%%");
432 		}
433 		else if (strcmp(testnames[testi]->name, "disk") == 0) {
434 			fprintf(stdout, "Default limits: Yellow 90%% full, Red 95%% full<br>\n");
435 			print_disklist(host->hostname);
436 		}
437 		else if (strcmp(testnames[testi]->name, "memory") == 0) {
438 			fprintf(stdout, "Yellow: swap/pagefile use > 80%%, Red: swap/pagefile use > 90%%");
439 		}
440 		else if (strcmp(testnames[testi]->name, "procs") == 0) {
441 			htnames_t *walk;
442 
443 			if (!host->procs) fprintf(stdout, "No processes monitored<br>\n");
444 
445 			for (walk = host->procs; (walk); walk = walk->next) {
446 				fprintf(stdout, "%s<br>\n", walk->name);
447 			}
448 		}
449 		else if (strcmp(testnames[testi]->name, "svcs") == 0) {
450 			htnames_t *walk;
451 
452 			if (!host->svcs) fprintf(stdout, "No services monitored<br>\n");
453 
454 			for (walk = host->svcs; (walk); walk = walk->next) {
455 				fprintf(stdout, "%s<br>\n", walk->name);
456 			}
457 		}
458 		else {
459 			fprintf(stdout, "&nbsp;");
460 		}
461 		fprintf(stdout, "</td>");
462 
463 		fprintf(stdout, "</tr>");
464 	}
465 	if (netcount != testcount) {
466 		fprintf(stdout, "</table></td>\n");
467 		fprintf(stdout, "</tr>\n");
468 	}
469 
470 	/* Do the alerts */
471 	alert = (activealerts_t *)calloc(1, sizeof(activealerts_t));
472 	alert->hostname = host->hostname;
473 	alert->location = xmh_item(hinfo, XMH_ALLPAGEPATHS);
474 	strncpy(alert->ip, "127.0.0.1", sizeof(alert->ip));
475 	alert->color = COL_RED;
476 	alert->pagemessage = "";
477 	alert->state = A_PAGING;
478 	alert->cookie = 12345;
479 	alert_printmode(2);
480 	for (testi = 0; (testi < testcount); testi++) {
481 		alert->testname = testnames[testi]->name;
482 		if (have_recipient(alert, NULL)) print_alert_recipients(alert, buf);
483 	}
484 	xfree(alert);
485 
486 	if (STRBUFLEN(buf) > 0) {
487 		fprintf(stdout, "<tr>\n");
488 		fprintf(stdout, "<th align=left valign=top>Alerts</th>\n");
489 		fprintf(stdout, "<td><table border=0 cellpadding=\"3\" cellspacing=\"5\" summary=\"%s alerts\">\n", host->hostname);
490 		fprintf(stdout, "<tr><th>Service</th><th>Recipient</th><th>1st Delay</th><th>Stop after</th><th>Repeat</th><th>Time of Day</th><th>Colors</th></tr>\n");
491 
492 		fprintf(stdout, "%s", STRBUF(buf));
493 
494 		fprintf(stdout, "</table></td>\n");
495 		fprintf(stdout, "</tr>\n");
496 	}
497 
498 	/* Finish off this host */
499 	fprintf(stdout, "</table>\n");
500 
501 	freestrbuffer(buf);
502 }
503 
504 
coltext_compare(const void * v1,const void * v2)505 static int coltext_compare(const void *v1, const void *v2)
506 {
507 	coltext_t **t1 = (coltext_t **)v1;
508 	coltext_t **t2 = (coltext_t **)v2;
509 
510 	return strcmp((*t1)->colname, (*t2)->colname);
511 }
512 
load_columndocs(void)513 void load_columndocs(void)
514 {
515 	char fn[PATH_MAX];
516 	FILE *fd;
517 	strbuffer_t *inbuf;
518 
519 	snprintf(fn, sizeof(fn)-1, "%s/etc/columndoc.csv", xgetenv("XYMONHOME"));
520 	fd = fopen(fn, "r"); if (!fd) return;
521 
522 	inbuf = newstrbuffer(0);
523 	initfgets(fd);
524 
525 	/* Skip the header line */
526 	if (!unlimfgets(inbuf, fd)) { fclose(fd); freestrbuffer(inbuf); return; }
527 
528 	while (unlimfgets(inbuf, fd)) {
529 		char *s1 = NULL, *s2 = NULL;
530 
531 		s1 = strtok(STRBUF(inbuf), coldelim);
532 		if (s1) s2 = strtok(NULL, coldelim);
533 
534 		if (s1 && s2) {
535 			coltext_t *newitem = (coltext_t *)calloc(1, sizeof(coltext_t));
536 			newitem->colname = strdup(s1);
537 			newitem->coldescr = strdup(s2);
538 			newitem->next = chead;
539 			chead = newitem;
540 			ccount++;
541 		}
542 	}
543 	fclose(fd);
544 	freestrbuffer(inbuf);
545 }
546 
547 
print_columndocs(void)548 void print_columndocs(void)
549 {
550 	coltext_t **clist;
551 	coltext_t *cwalk;
552 	int i;
553 
554 	clist = (coltext_t **)malloc(ccount * sizeof(coltext_t *));
555 	for (i=0, cwalk=chead; (cwalk); cwalk=cwalk->next,i++) clist[i] = cwalk;
556 	qsort(&clist[0], ccount, sizeof(coltext_t **), coltext_compare);
557 
558 	fprintf(stdout, "<p style=\"page-break-before: always\">\n");
559 	fprintf(stdout, "<table width=\"100%%\" border=1 summary=\"Column descriptions\">\n");
560 	fprintf(stdout, "<tr><th colspan=2>Xymon column descriptions</th></tr>\n");
561 	for (i=0; (i<ccount); i++) {
562 		if (clist[i]->used) {
563 			fprintf(stdout, "<tr><td align=left valign=top>%s</td><td>%s</td></tr>\n",
564 				clist[i]->colname, clist[i]->coldescr);
565 		}
566 	}
567 
568 	fprintf(stdout, "</table>\n");
569 }
570 
get_proclist(char * hostname,char * statusbuf)571 htnames_t *get_proclist(char *hostname, char *statusbuf)
572 {
573 	char *bol, *eol;
574 	SBUF_DEFINE(marker);
575 	htnames_t *head = NULL, *tail = NULL;
576 
577 	if (!statusbuf) return NULL;
578 
579 	SBUF_MALLOC(marker, strlen(hostname) + 3);
580 	snprintf(marker, marker_buflen, "\n%s|", hostname);
581 	if (strncmp(statusbuf, marker+1, strlen(marker)-1) == 0) {
582 		/* Found at start of buffer */
583 		bol = statusbuf;
584 	}
585 	else {
586 		bol = strstr(statusbuf, marker);
587 		if (bol) bol++;
588 	}
589 	xfree(marker);
590 
591 	if (!bol) return NULL;
592 
593 	bol += strlen(hostname) + 1;  /* Skip hostname and delimiter */
594 	marker = bol;
595 	eol = strchr(bol, '\n'); if (eol) *eol = '\0';
596 	marker = strstr(marker, "\\n&");
597 	while (marker) {
598 		char *p;
599 		htnames_t *newitem;
600 
601 		marker += strlen("\\n&");
602 		if      (strncmp(marker, "green", 5) == 0) marker += 5;
603 		else if (strncmp(marker, "yellow", 6) == 0) marker += 6;
604 		else if (strncmp(marker, "red", 3) == 0) marker += 3;
605 		else marker = NULL;
606 
607 		if (marker) {
608 			marker += strspn(marker, " \t");
609 
610 			p = strstr(marker, "\\n"); if (p) *p = '\0';
611 			newitem = (htnames_t *)malloc(sizeof(htnames_t));
612 			newitem->name = strdup(marker);
613 			newitem->next = NULL;
614 			if (!tail) {
615 				head = tail = newitem;
616 			}
617 			else {
618 				tail->next = newitem;
619 				tail = newitem;
620 			}
621 
622 			if (p) {
623 				*p = '\\';
624 			}
625 
626 			marker = strstr(marker, "\\n&");
627 		}
628 	}
629 	if (eol) *eol = '\n';
630 
631 	return head;
632 }
633 
main(int argc,char * argv[])634 int main(int argc, char *argv[])
635 {
636 	int argi, hosti, testi;
637 	char *pagepattern = NULL, *hostpattern = NULL;
638 	char *envarea = NULL, *cookie = NULL, *nexthost;
639 	SBUF_DEFINE(xymoncmd);
640 	SBUF_DEFINE(procscmd);
641 	SBUF_DEFINE(svcscmd);
642         int alertcolors, alertinterval;
643 	char configfn[PATH_MAX];
644 	char *respbuf = NULL, *procsbuf = NULL, *svcsbuf = NULL;
645 	hostlist_t *hwalk;
646 	htnames_t *twalk;
647 	hostlist_t **allhosts = NULL;
648 	htnames_t **alltests = NULL;
649 	int hostcount = 0, maxtests = 0;
650 	time_t now = getcurrenttime(NULL);
651 	sendreturn_t *sres;
652 	char *critconfigfn = NULL;
653 	int patternerror = 0;
654 
655 	for (argi=1; (argi < argc); argi++) {
656 		if (argnmatch(argv[argi], "--env=")) {
657 			char *p = strchr(argv[argi], '=');
658 			loadenv(p+1, envarea);
659 		}
660 		else if (argnmatch(argv[argi], "--area=")) {
661 			char *p = strchr(argv[argi], '=');
662 			envarea = strdup(p+1);
663 		}
664 		else if (strcmp(argv[argi], "--debug") == 0) {
665 			debug = 1;
666 		}
667 		else if (argnmatch(argv[argi], "--delimiter=")) {
668 			char *p = strchr(argv[argi], '=');
669 			coldelim = strdup(p+1);
670 		}
671 		else if (strcmp(argv[argi], "--critical") == 0) {
672 			criticalonly = 1;
673 		}
674 		else if (strcmp(argv[argi], "--old-critical-config") == 0) {
675 			newcritconfig = 0;
676 		}
677 		else if (argnmatch(argv[argi], "--critical-config=")) {
678 			char *p = strchr(argv[argi], '=');
679 			critconfigfn = strdup(p+1);
680 		}
681 	}
682 
683 	redirect_cgilog("confreport");
684 
685 	load_hostnames(xgetenv("HOSTSCFG"), NULL, get_fqdn());
686 	load_critconfig(critconfigfn);
687 
688 	SBUF_MALLOC(pingplus, 6); strncpy(pingplus, "conn=", pingplus_buflen);
689 
690 	/* Setup the filter we use for the report */
691 	cookie = get_cookie("pagepath"); if (cookie && *cookie) pagepattern = strdup(cookie);
692 	cookie = get_cookie("host");     if (cookie && *cookie) hostpattern = strdup(cookie);
693 
694 	/* Fetch the list of host+test statuses we currently know about */
695 	if (pagepattern) {
696 		pcre *dummy;
697 		SBUF_DEFINE(re);
698 
699 		SBUF_MALLOC(re, 8 + 2*strlen(pagepattern));
700 		snprintf(re, re_buflen, "^%s$|^%s/.+", pagepattern, pagepattern);
701 		dummy = compileregex(re);
702 		if (dummy) {
703 			freeregex(dummy);
704 
705 			SBUF_MALLOC(xymoncmd, 2*strlen(pagepattern) + 1024);
706 			SBUF_MALLOC(procscmd, 2*strlen(pagepattern) + 1024);
707 			SBUF_MALLOC(svcscmd, 2*strlen(pagepattern) + 1024);
708 
709 			snprintf(xymoncmd, xymoncmd_buflen, "xymondboard page=%s fields=hostname,testname", re);
710 			snprintf(procscmd, procscmd_buflen, "xymondboard page=%s test=procs fields=hostname,msg", re);
711 			snprintf(svcscmd,  svcscmd_buflen,  "xymondboard page=%s test=svcs fields=hostname,msg", re);
712 		}
713 		else
714 			patternerror = 1;
715 
716 		xfree(re);
717 	}
718 	else if (hostpattern) {
719 		pcre *dummy;
720 		SBUF_DEFINE(re);
721 
722 		SBUF_MALLOC(re,3 + strlen(hostpattern));
723 		snprintf(re, re_buflen, "^%s$", hostpattern);
724 		dummy = compileregex(re);
725 		if (dummy) {
726 			freeregex(dummy);
727 
728 			SBUF_MALLOC(xymoncmd, strlen(hostpattern) + 1024);
729 			SBUF_MALLOC(procscmd, strlen(hostpattern) + 1024);
730 			SBUF_MALLOC(svcscmd, strlen(hostpattern) + 1024);
731 
732 			snprintf(xymoncmd, xymoncmd_buflen, "xymondboard host=^%s$ fields=hostname,testname", hostpattern);
733 			snprintf(procscmd, procscmd_buflen, "xymondboard host=^%s$ test=procs fields=hostname,msg", hostpattern);
734 			snprintf(svcscmd,  svcscmd_buflen,  "xymondboard host=^%s$ test=svcs fields=hostname,msg", hostpattern);
735 		}
736 		else
737 			patternerror = 1;
738 
739 		xfree(re);
740 	}
741 	else {
742 		SBUF_MALLOC(xymoncmd, 1024);
743 		SBUF_MALLOC(procscmd, 1024);
744 		SBUF_MALLOC(svcscmd, 1024);
745 
746 		snprintf(xymoncmd, xymoncmd_buflen, "xymondboard fields=hostname,testname");
747 		snprintf(procscmd, procscmd_buflen, "xymondboard test=procs fields=hostname,msg");
748 		snprintf(svcscmd, svcscmd_buflen,   "xymondboard test=svcs fields=hostname,msg");
749 	}
750 
751 	if (patternerror) {
752 		errormsg("Invalid host/page filter\n");
753 		return 1;
754 	}
755 
756 	sres = newsendreturnbuf(1, NULL);
757 
758 	if (sendmessage(xymoncmd, NULL, XYMON_TIMEOUT, sres) != XYMONSEND_OK) {
759 		errormsg("Cannot contact the Xymon server\n");
760 		return 1;
761 	}
762 	respbuf = getsendreturnstr(sres, 1);
763 	if (sendmessage(procscmd, NULL, XYMON_TIMEOUT, sres) != XYMONSEND_OK) {
764 		errormsg("Cannot contact the Xymon server\n");
765 		return 1;
766 	}
767 	procsbuf = getsendreturnstr(sres, 1);
768 	if (sendmessage(svcscmd, NULL, XYMON_TIMEOUT, sres) != XYMONSEND_OK) {
769 		errormsg("Cannot contact the Xymon server\n");
770 		return 1;
771 	}
772 	svcsbuf = getsendreturnstr(sres, 1);
773 
774 	freesendreturnbuf(sres);
775 
776 	if (!respbuf) {
777 		errormsg("Unable to find host information\n");
778 		return 1;
779 	}
780 
781 	/* Parse it into a usable list */
782 	nexthost = respbuf;
783 	do {
784 		char *hname, *tname, *eoln;
785 		int wanted = 1;
786 
787 		eoln = strchr(nexthost, '\n'); if (eoln) *eoln = '\0';
788 		hname = nexthost;
789 		tname = strchr(nexthost, '|'); if (tname) { *tname = '\0'; tname++; }
790 
791 		if (criticalonly) {
792 			void *hinfo = hostinfo(hname);
793 			char *alerts = xmh_item(hinfo, XMH_NK);
794 
795 			if (newcritconfig) {
796 				if (strcmp(criticalval(hname, tname, alerts), "No") == 0 ) wanted = 0;
797 			} else {
798 				if (!alerts) wanted = 0;
799 			}
800 		}
801 
802 		if (wanted && hname && tname && strcmp(hname, "summary") && strcmp(tname, xgetenv("INFOCOLUMN")) && strcmp(tname, xgetenv("TRENDSCOLUMN"))) {
803 			htnames_t *newitem = (htnames_t *)malloc(sizeof(htnames_t));
804 
805 			for (hwalk = hosthead; (hwalk && strcmp(hwalk->hostname, hname)); hwalk = hwalk->next);
806 			if (!hwalk) {
807 				hwalk = (hostlist_t *)calloc(1, sizeof(hostlist_t));
808 				hwalk->hostname = strdup(hname);
809 				hwalk->procs = get_proclist(hname, procsbuf);
810 				hwalk->svcs  = get_proclist(hname, svcsbuf);
811 				hwalk->next = hosthead;
812 				hosthead = hwalk;
813 				hostcount++;
814 			}
815 
816 			newitem->name = strdup(tname);
817 			newitem->next = hwalk->tests;
818 			hwalk->tests = newitem;
819 			hwalk->testcount++;
820 		}
821 
822 		if (eoln) {
823 			nexthost = eoln+1;
824 			if (*nexthost == '\0') nexthost = NULL;
825 		}
826 	} while (nexthost);
827 
828 	if (hostcount > 0) {
829 		allhosts = (hostlist_t **) malloc(hostcount * sizeof(hostlist_t *));
830 		for (hwalk = hosthead, hosti=0; (hwalk); hwalk = hwalk->next, hosti++) {
831 			allhosts[hosti] = hwalk;
832 			if (hwalk->testcount > maxtests) maxtests = hwalk->testcount;
833 		}
834 		if (maxtests > 0) alltests = (htnames_t **) malloc(maxtests * sizeof(htnames_t *));
835 		qsort(&allhosts[0], hostcount, sizeof(hostlist_t **), host_compare);
836 	}
837 
838 	if ((hostcount == 0) || (maxtests == 0)) {
839 		printf("Content-Type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
840 		printf("<html><body><h1>No hosts or tests to report!</h1></body></html>\n");
841 		return 0;
842 	}
843 
844 	/* Get the static info */
845 	load_all_links();
846 	init_tcp_services();
847 	pingcolumn = xgetenv("PINGCOLUMN");
848 	SBUF_REALLOC(pingplus, strlen(pingcolumn) + 3);
849 	snprintf(pingplus, pingplus_buflen, "%s=", pingcolumn);
850 
851 	/* Load alert config */
852 	alertcolors = colorset(xgetenv("ALERTCOLORS"), ((1 << COL_GREEN) | (1 << COL_BLUE)));
853 	alertinterval = 60*atoi(xgetenv("ALERTREPEAT"));
854 	snprintf(configfn, sizeof(configfn)-1, "%s/etc/alerts.cfg", xgetenv("XYMONHOME"));
855 	load_alertconfig(configfn, alertcolors, alertinterval);
856 	load_columndocs();
857 
858 
859 	printf("Content-Type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
860 	sethostenv("", "", "", colorname(COL_BLUE), NULL);
861 	headfoot(stdout, "confreport", "", "header", COL_BLUE);
862 
863 	fprintf(stdout, "<table width=\"100%%\" border=0>\n");
864 	fprintf(stdout, "<tr><th align=center colspan=2><font size=\"+2\">Xymon configuration Report</font></th></tr>\n");
865 	fprintf(stdout, "<tr><th valign=top align=left>Date</th><td>%s</td></tr>\n", ctime(&now));
866 	fprintf(stdout, "<tr><th valign=top align=left>%d hosts included</th><td>\n", hostcount);
867 	for (hosti=0; (hosti < hostcount); hosti++) {
868 		fprintf(stdout, "%s ", allhosts[hosti]->hostname);
869 	}
870 	fprintf(stdout, "</td></tr>\n");
871 	if (criticalonly) {
872 		fprintf(stdout, "<tr><th valign=top align=left>Filter</th><td>Only data for the &quot;Critical Systems&quot; view reported</td></tr>\n");
873 	}
874 	fprintf(stdout, "</table>\n");
875 
876 	headfoot(stdout, "confreport", "", "front", COL_BLUE);
877 
878 	for (hosti=0; (hosti < hostcount); hosti++) {
879 		for (twalk = allhosts[hosti]->tests, testi = 0; (twalk); twalk = twalk->next, testi++) {
880 			alltests[testi] = twalk;
881 		}
882 		qsort(&alltests[0], allhosts[hosti]->testcount, sizeof(htnames_t **), test_compare);
883 
884 		print_host(allhosts[hosti], alltests, allhosts[hosti]->testcount);
885 	}
886 
887 	headfoot(stdout, "confreport", "", "back", COL_BLUE);
888 	print_columndocs();
889 
890 	headfoot(stdout, "confreport", "", "footer", COL_BLUE);
891 
892 	return 0;
893 }
894 
895