1 /*----------------------------------------------------------------------------*/
2 /* Xymon status-log viewer CGI.                                               */
3 /*                                                                            */
4 /* This CGI tool shows an HTML version of a status log.                       */
5 /*                                                                            */
6 /* Copyright (C) 2004-2011 Henrik Storner <henrik@storner.dk>                 */
7 /*                                                                            */
8 /* This program is released under the GNU General Public License (GPL),       */
9 /* version 2. See the file "COPYING" for details.                             */
10 /*                                                                            */
11 /*----------------------------------------------------------------------------*/
12 
13 static char rcsid[] = "$Id: svcstatus.c 8069 2019-07-23 15:29:06Z jccleaver $";
14 
15 #include <limits.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <ctype.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <libgen.h>
25 
26 #include "libxymon.h"
27 #include "version.h"
28 #include "svcstatus-info.h"
29 #include "svcstatus-trends.h"
30 
31 /* Command-line params */
32 static enum { SRC_XYMOND, SRC_HISTLOGS, SRC_CLIENTLOGS } source = SRC_XYMOND;
33 static int wantserviceid = 1;
34 SBUF_DEFINE(multigraphs);
35 static int locatorbased = 0;
36 static char *critconfigfn = NULL;
37 static char *accessfn = NULL;
38 
39 /* CGI params */
40 static char *hostname = NULL;
41 static char *service = NULL;
42 static char *tstamp = NULL;
43 static char *nkprio = NULL, *nkttgroup = NULL, *nkttextra = NULL;
44 static enum { FRM_STATUS, FRM_CLIENT } outform = FRM_STATUS;
45 STATIC_SBUF_DEFINE(clienturi);
46 static int backsecs = 0;
47 static time_t fromtime = 0, endtime = 0;
48 
49 static char errortxt[1000];
50 STATIC_SBUF_DEFINE(hostdatadir);
51 
52 
errormsg(int status,char * msg)53 static void errormsg(int status, char *msg)
54 {
55 	snprintf(errortxt, sizeof(errortxt),
56 		 "Status: %d\nRefresh: 30\nContent-type: %s\n\n<html><head><title>Invalid request</title></head>\n<body>%s</body></html>\n",
57 		 status, xgetenv("HTMLCONTENTTYPE"), msg);
58 
59 	errortxt[sizeof(errortxt)-1] = '\0';
60 }
61 
parse_query(void)62 static int parse_query(void)
63 {
64 	cgidata_t *cgidata = cgi_request();
65 	cgidata_t *cwalk;
66 
67 	cwalk = cgidata;
68 	while (cwalk) {
69 		if (strcasecmp(cwalk->name, "HOST") == 0) {
70 			hostname = strdup(basename(cwalk->value));
71 		}
72 		else if (strcasecmp(cwalk->name, "SERVICE") == 0) {
73 			service = strdup(basename(cwalk->value));
74 		}
75 		else if (strcasecmp(cwalk->name, "HOSTSVC") == 0) {
76 			/* For backwards compatibility */
77 			char *p = strrchr(cwalk->value, '.');
78 			if (p) {
79 				*p = '\0';
80 				hostname = strdup(basename(cwalk->value));
81 				service = strdup(p+1);
82 				for (p=strchr(hostname, ','); (p); p = strchr(p, ',')) *p = '.';
83 			}
84 		}
85 		else if (strcasecmp(cwalk->name, "TIMEBUF") == 0) {
86 			/* Only for the historical logs */
87 			tstamp = strdup(basename(cwalk->value));
88 		}
89 		else if (strcasecmp(cwalk->name, "CLIENT") == 0) {
90 			char *p;
91 
92 			hostname = strdup(cwalk->value);
93 			p = hostname; while ((p = strchr(p, ',')) != NULL) *p = '.';
94 			service = strdup("");
95 			outform = FRM_CLIENT;
96 		}
97 		else if (strcasecmp(cwalk->name, "SECTION") == 0) {
98 			service = strdup(cwalk->value);
99 		}
100 		else if (strcasecmp(cwalk->name, "NKPRIO") == 0) {
101 			nkprio = strdup(cwalk->value);
102 		}
103 		else if (strcasecmp(cwalk->name, "NKTTGROUP") == 0) {
104 			nkttgroup = strdup(cwalk->value);
105 		}
106 		else if (strcasecmp(cwalk->name, "NKTTEXTRA") == 0) {
107 			nkttextra = strdup(cwalk->value);
108 		}
109 		else if ((strcmp(cwalk->name, "backsecs") == 0)   && cwalk->value && strlen(cwalk->value)) {
110 			backsecs += atoi(cwalk->value);
111 		}
112 		else if ((strcmp(cwalk->name, "backmins") == 0)   && cwalk->value && strlen(cwalk->value)) {
113 			backsecs += 60*atoi(cwalk->value);
114 		}
115 		else if ((strcmp(cwalk->name, "backhours") == 0)   && cwalk->value && strlen(cwalk->value)) {
116 			backsecs += 60*60*atoi(cwalk->value);
117 		}
118 		else if ((strcmp(cwalk->name, "backdays") == 0)   && cwalk->value && strlen(cwalk->value)) {
119 			backsecs += 24*60*60*atoi(cwalk->value);
120 		}
121 		else if ((strcmp(cwalk->name, "FROMTIME") == 0)   && cwalk->value && strlen(cwalk->value)) {
122 			fromtime = eventreport_time(cwalk->value);
123 		}
124 		else if ((strcmp(cwalk->name, "TOTIME") == 0)   && cwalk->value && strlen(cwalk->value)) {
125 			endtime = eventreport_time(cwalk->value);
126 		}
127 
128 		cwalk = cwalk->next;
129 	}
130 
131 	if (backsecs == 0) {
132 		if (getenv("TRENDSECONDS")) backsecs = atoi(getenv("TRENDSECONDS"));
133 		else backsecs = 48*60*60;
134 	}
135 
136 	if (!hostname || !service || ((source == SRC_HISTLOGS) && !tstamp) ) {
137 		errormsg(403, "Invalid request");
138 		return 1;
139 	}
140 
141 	if (strcmp(service, xgetenv("CLIENTCOLUMN")) == 0) {
142 		/* Make this a client request */
143 		char *p = strdup(basename(hostname));
144 		xfree(hostname); hostname = p;	/* no need to convert to dots, since we'll already have them */
145 		xfree(service);			/* service does double-duty as the 'section' param */
146 		outform = FRM_CLIENT;
147 	}
148 
149 	if (outform == FRM_STATUS) {
150 		char *p, *req;
151 		char *hostquoted = htmlquoted(hostname);
152 
153 		req = getenv("SCRIPT_NAME");
154 		SBUF_MALLOC(clienturi, strlen(req) + 10 + strlen(hostquoted));
155 		strncpy(clienturi, req, clienturi_buflen);
156 		p = strchr(clienturi, '?'); if (p) *p = '\0'; else p = clienturi + strlen(clienturi);
157 		snprintf(p, (clienturi_buflen - (clienturi - p)), "?CLIENT=%s", hostquoted);
158 	}
159 
160 	return 0;
161 }
162 
loadhostdata(char * hostname,char ** ip,char ** displayname,char ** compacts,int full)163 int loadhostdata(char *hostname, char **ip, char **displayname, char **compacts, int full)
164 {
165 	void *hinfo = NULL;
166 	int loadres;
167 
168 	if (full) {
169 		loadres = load_hostnames(xgetenv("HOSTSCFG"), NULL, get_fqdn());
170 	}
171 	else {
172 		loadres = load_hostinfo(hostname);
173 	}
174 
175 	if ((loadres != 0) && (loadres != -2)) {
176 		errormsg(500, "Cannot load host configuration");
177 		return 1;
178 	}
179 
180 	if ((loadres == -2) || (hinfo = hostinfo(hostname)) == NULL) {
181 		errormsg(403, "No such host");
182 		return 1;
183 	}
184 
185 	*ip = xmh_item(hinfo, XMH_IP);
186 	*displayname = xmh_item(hinfo, XMH_DISPLAYNAME);
187 	if (!(*displayname)) *displayname = hostname;
188 	*compacts = xmh_item(hinfo, XMH_COMPACT);
189 
190 	return 0;
191 }
192 
do_request(void)193 int do_request(void)
194 {
195 	int color = 0, flapping = 0;
196 	char timesincechange[100];
197 	time_t logtime = 0, acktime = 0, disabletime = 0;
198 	SBUF_DEFINE(firstline);
199 	char *log = NULL, *sender = NULL, *clientid = NULL, *flags = NULL;	/* These are free'd */
200 	char *restofmsg = NULL, *ackmsg = NULL, *dismsg = NULL, *acklist=NULL, *modifiers = NULL;	/* These are just used */
201 	int ishtmlformatted = 0;
202 	int clientavail = 0;
203 	char *ip = NULL, *displayname = NULL, *compacts;
204 
205 	if (parse_query() != 0) return 1;
206 
207 	{
208 	  char *p = NULL;
209 	  if (!service || !strlen(service)) p = csp_header("svcstatus");
210 	  else {
211 		if (strcasecmp(service, xgetenv("INFOCOLUMN")) == 0) p = csp_header("svcstatus-info");
212 		else if (strcasecmp(service, xgetenv("TRENDSCOLUMN")) == 0) p = csp_header("svcstatus-trends");
213 		else {
214 			int d = atoi(xgetenv("XYMWEBREFRESH"));
215 			fprintf(stdout, "Refresh: %d\n", ((d > 0) ? d : 60) );
216 			p = csp_header("svcstatus");
217 		}
218 	  }
219 	  if (p) fprintf(stdout, "%s", p);
220 	}
221 	/* Load the host data (for access control) */
222 	if (accessfn) {
223 		load_hostinfo(hostname);
224 		load_web_access_config(accessfn);
225 		if (!web_access_allowed(getenv("REMOTE_USER"), hostname, service, WEB_ACCESS_VIEW)) {
226 			errormsg(403, "Not available (restricted).");
227 			return 1;
228 		}
229 	}
230 
231 	{
232 		char *s;
233 
234 		s = xgetenv("CLIENTLOGS");
235 		if (s) {
236 			SBUF_MALLOC(hostdatadir, strlen(s) + strlen(hostname) + 12);
237 			snprintf(hostdatadir, hostdatadir_buflen, "%s/%s", s, hostname);
238 		}
239 		else {
240 			s = xgetenv("XYMONVAR");
241 			SBUF_MALLOC(hostdatadir, strlen(s) + strlen(hostname) + 12);
242 			snprintf(hostdatadir, hostdatadir_buflen, "%s/hostdata/%s", s, hostname);
243 		}
244 	}
245 
246 	if (outform == FRM_CLIENT) {
247 		if (source == SRC_XYMOND) {
248 			SBUF_DEFINE(xymondreq);
249 			int xymondresult;
250 			sendreturn_t *sres = newsendreturnbuf(1, NULL);
251 
252 			SBUF_MALLOC(xymondreq, 1024 + strlen(hostname) + (service ? strlen(service) : 0));
253 			snprintf(xymondreq, xymondreq_buflen, "clientlog %s", hostname);
254 			if (service && *service) snprintf(xymondreq + strlen(xymondreq), (xymondreq_buflen - strlen(xymondreq)), " section=%s", service);
255 
256 			xymondresult = sendmessage(xymondreq, NULL, XYMON_TIMEOUT, sres);
257 			if (xymondresult != XYMONSEND_OK) {
258 				SBUF_DEFINE(errtxt);
259 
260 				SBUF_MALLOC(errtxt, 1024 + MAX_HTMLQUOTE_FACTOR*strlen(xymondreq));
261 				snprintf(errtxt, errtxt_buflen, "Status not available: Req=%s, result=%d\n", htmlquoted(xymondreq), xymondresult);
262 				errormsg(500, errtxt);
263 				return 1;
264 			}
265 			else {
266 				log = getsendreturnstr(sres, 1);
267 			}
268 			freesendreturnbuf(sres);
269 		}
270 		else if (source == SRC_HISTLOGS) {
271 			char logfn[PATH_MAX];
272 			FILE *fd;
273 
274 			snprintf(logfn, sizeof(logfn), "%s/%s", hostdatadir, tstamp);
275 			fd = fopen(logfn, "r");
276 			if (fd) {
277 				struct stat st;
278 				int n;
279 
280 				fstat(fileno(fd), &st);
281 				if (S_ISREG(st.st_mode)) {
282 					log = (char *)malloc(st.st_size + 1);
283 					n = fread(log, 1, st.st_size, fd);
284 					if (n >= 0) *(log+n) = '\0'; else *log = '\0';
285 				}
286 				fclose(fd);
287 			}
288 		}
289 
290 		restofmsg = (log ? log : strdup("<No data>\n"));
291 	}
292 	else if ((strcmp(service, xgetenv("TRENDSCOLUMN")) == 0) || (strcmp(service, xgetenv("INFOCOLUMN")) == 0)) {
293 		int fullload = (strcmp(service, xgetenv("INFOCOLUMN")) == 0);
294 
295 		if (loadhostdata(hostname, &ip, &displayname, &compacts, fullload) != 0) return 1;
296 
297 		ishtmlformatted = 1;
298 		sethostenv(displayname, ip, service, colorname(COL_GREEN), hostname);
299 		sethostenv_refresh(600);
300 		color = COL_GREEN;
301 		logtime = getcurrenttime(NULL);
302 		strncpy(timesincechange, "0 minutes", sizeof(timesincechange));
303 
304 		if (strcmp(service, xgetenv("TRENDSCOLUMN")) == 0) {
305 			if (locatorbased) {
306 				char *cgiurl, *qres;
307 
308 				qres = locator_query(hostname, ST_RRD, &cgiurl);
309 				if (!qres) {
310 					errprintf("Cannot find RRD files for host %s\n", hostname);
311 				}
312 				else {
313 					/* Redirect browser to the real server */
314 					fprintf(stdout, "Location: %s/svcstatus.sh?HOST=%s&SERVICE=%s\n\n",
315 						cgiurl, hostname, service);
316 					return 0;
317 				}
318 			}
319 			else {
320 				if (endtime == 0) endtime = getcurrenttime(NULL);
321 
322 				if (fromtime == 0) {
323 					fromtime = endtime - backsecs;
324 					sethostenv_backsecs(backsecs);
325 				}
326 				else {
327 					sethostenv_eventtime(fromtime, endtime);
328 				}
329 
330 				log = restofmsg = generate_trends(hostname, fromtime, endtime);
331 			}
332 		}
333 		else if (strcmp(service, xgetenv("INFOCOLUMN")) == 0) {
334 			log = restofmsg = generate_info(hostname, critconfigfn);
335 		}
336 	}
337 	else if (source == SRC_XYMOND) {
338 		SBUF_DEFINE(xymondreq);
339 		int xymondresult;
340 		char *items[25];
341 		int icount;
342 		time_t logage, clntstamp;
343 		char *sumline, *msg, *compitem, *complist;
344 		sendreturn_t *sres;
345 
346 		if (loadhostdata(hostname, &ip, &displayname, &compacts, 0) != 0) return 1;
347 
348 		complist = NULL;
349 		if (compacts && *compacts) {
350 			char *p;
351 
352 			compitem = strtok(compacts, ",");
353 			while (compitem && !complist) {
354 				p = strchr(compitem, '='); if (p) *p = '\0';
355 				if (strcmp(service, compitem) == 0) complist = p+1;
356 				compitem = strtok(NULL, ",");
357 			}
358 		}
359 
360 		/* We need not check that hostname is valid, has already been done with loadhostdata() */
361 		if (!complist) {
362 			pcre *dummy = NULL;
363 
364 			/* Check service as a pcre pattern. And no spaces in servicenames */
365 			if (strchr(service, ' ') == NULL) dummy = compileregex(service);
366 			if (dummy == NULL) {
367 				errormsg(500, "Invalid testname pattern");
368 				return 1;
369 			}
370 
371 			freeregex(dummy);
372 			SBUF_MALLOC(xymondreq, 1024 + strlen(hostname) + strlen(service));
373 			snprintf(xymondreq, xymondreq_buflen, "xymondlog host=%s test=%s fields=hostname,testname,color,flags,lastchange,logtime,validtime,acktime,disabletime,sender,cookie,ackmsg,dismsg,client,acklist,XMH_IP,XMH_DISPLAYNAME,clntstamp,flapinfo,modifiers", hostname, service);
374 		}
375 		else {
376 			pcre *dummy = NULL;
377 			SBUF_DEFINE(re);
378 
379 			SBUF_MALLOC(re, 5 + strlen(complist));
380 			snprintf(re, re_buflen, "^(%s)$", complist);
381 			dummy = compileregex(re);
382 			if (dummy == NULL) {
383 				errormsg(500, "Invalid testname pattern");
384 				return 1;
385 			}
386 
387 			freeregex(dummy);
388 			SBUF_MALLOC(xymondreq, 1024 + strlen(hostname) + strlen(re));
389 			snprintf(xymondreq, xymondreq_buflen, "xymondboard host=^%s$ test=%s fields=testname,color,lastchange", hostname, re);
390 		}
391 
392 		sres = newsendreturnbuf(1, NULL);
393 		xymondresult = sendmessage(xymondreq, NULL, XYMON_TIMEOUT, sres);
394 		if (xymondresult == XYMONSEND_OK) log = getsendreturnstr(sres, 1);
395 		freesendreturnbuf(sres);
396 		if ((xymondresult != XYMONSEND_OK) || (log == NULL) || (strlen(log) == 0)) {
397 			errormsg(404, "Status not available\n");
398 			return 1;
399 		}
400 
401 		if (!complist) {
402 			char *p;
403 
404 			sumline = log; p = strchr(log, '\n'); *p = '\0';
405 			msg = (p+1); p = strchr(msg, '\n');
406 			if (!p) {
407 				firstline = strdup(msg);
408 				restofmsg = NULL;
409 			}
410 			else {
411 				*p = '\0';
412 				firstline = strdup(msg);
413 				restofmsg = (p+1);
414 				*p = '\n';
415 			}
416 
417 			memset(items, 0, sizeof(items));
418 			p = gettok(sumline, "|"); icount = 0;
419 			while (p && (icount < 20)) {
420 				items[icount++] = p;
421 				p = gettok(NULL, "|");
422 			}
423 
424 			/*
425 			 * hostname,		[0]
426 			 * testname,		[1]
427 			 * color,		[2]
428 			 * flags,		[3]
429 			 * lastchange,		[4]
430 			 * logtime,		[5]
431 			 * validtime,		[6]
432 			 * acktime,		[7]
433 			 * disabletime,		[8]
434 			 * sender,		[9]
435 			 * cookie,		[10]
436 			 * ackmsg,		[11]
437 			 * dismsg,		[12]
438 			 * client,		[13]
439 			 * acklist		[14]
440 			 * XMH_IP		[15]
441 			 * XMH_DISPLAYNAME	[16]
442 			 * clienttstamp         [17]
443 			 * flapping		[18]
444 			 * modifiers		[19]
445 			 */
446 			color = parse_color(items[2]);
447 			flags = strdup(items[3]);
448 			logage = getcurrenttime(NULL) - atoi(items[4]);
449 			timesincechange[0] = '\0';
450 			p = timesincechange;
451 			{
452 				int days = (int) (logage / 86400);
453 				int hours = (int) ((logage % 86400) / 3600);
454 				int minutes = (int) ((logage % 3600) / 60);
455 
456 				if (days > 1) p += snprintf(p, (sizeof(timesincechange) - (p - timesincechange)), "%d days, ", days);
457 				else if (days == 1) p += snprintf(p, (sizeof(timesincechange) - (p - timesincechange)), "1 day, ");
458 
459 				if (hours == 1) p += snprintf(p, (sizeof(timesincechange) - (p - timesincechange)), "1 hour, ");
460 				else p += snprintf(p, (sizeof(timesincechange) - (p - timesincechange)), "%d hours, ", hours);
461 
462 				if (minutes == 1) p += snprintf(p, (sizeof(timesincechange) - (p - timesincechange)), "1 minute");
463 				else p += snprintf(p, (sizeof(timesincechange) - (p - timesincechange)), "%d minutes", minutes);
464 			}
465 			logtime = atoi(items[5]);
466 			if (items[7] && strlen(items[7])) acktime = atoi(items[7]);
467 			if (items[8] && strlen(items[8])) disabletime = atoi(items[8]);
468 			sender = strdup(items[9]);
469 
470 			if (items[11] && strlen(items[11])) ackmsg = items[11];
471 			if (ackmsg) nldecode(ackmsg);
472 
473 			if (items[12] && strlen(items[12])) dismsg = items[12];
474 			if (dismsg) nldecode(dismsg);
475 
476 			if (items[13]) clientavail = (*items[13] == 'Y');
477 
478 			acklist = ((items[14] && *items[14]) ? strdup(items[14]) : NULL);
479 
480 			ip = (items[15] ? items[15] : "");
481 			displayname = ((items[16]  && *items[16]) ? items[16] : hostname);
482 			clntstamp = ((items[17]  && *items[17]) ? atol(items[17]) : 0);
483 			flapping = (items[18] ? (*items[18] == '1') : 0);
484 			modifiers = (items[19] && *(items[19])) ? items[19] : NULL;
485 
486 			sethostenv(displayname, ip, service, colorname(COL_GREEN), hostname);
487 			sethostenv_refresh(60);
488 		}
489 		else {
490 			/* Compressed status display */
491 			strbuffer_t *cmsg;
492 			char *row, *p_row, *p_fld;
493 			SBUF_DEFINE(nonhistenv);
494 
495 			color = COL_GREEN;
496 
497 			cmsg = newstrbuffer(0);
498 			addtobuffer(cmsg, "<table width=\"80%\" summary=\"Compacted Status Info\">\n");
499 
500 			row = strtok_r(log, "\n", &p_row);
501 			while (row) {
502 				/* testname,color,lastchange */
503 				char *testname, *itmcolor, *chgs;
504 				time_t lastchange;
505 				int icolor;
506 
507 				testname = strtok_r(row, "|", &p_fld);
508 				itmcolor = strtok_r(NULL, "|", &p_fld);
509 				chgs = strtok_r(NULL, "|", &p_fld);
510 				lastchange = atoi(chgs);
511 
512 				icolor = parse_color(itmcolor);
513 				if (icolor > color) color = icolor;
514 
515 				addtobuffer(cmsg, "<tr><td align=left>&");
516 				addtobuffer(cmsg, itmcolor);
517 				addtobuffer(cmsg, "&nbsp;<a href=\"");
518 				addtobuffer(cmsg, hostsvcurl(hostname, testname, 1));
519 				addtobuffer(cmsg, "\">");
520 				addtobuffer(cmsg, htmlquoted(testname));
521 				addtobuffer(cmsg, "</a></td></tr>\n");
522 
523 				row = strtok_r(NULL, "\n", &p_row);
524 			}
525 
526 			addtobuffer(cmsg, "</table>\n");
527 			ishtmlformatted = 1;
528 
529 			sethostenv(displayname, ip, service, colorname(color), hostname);
530 			sethostenv_refresh(60);
531 			logtime = getcurrenttime(NULL);
532 			strncpy(timesincechange, "0 minutes", sizeof(timesincechange));
533 
534 			log = restofmsg = grabstrbuffer(cmsg);
535 
536 			SBUF_MALLOC(firstline, 1024);
537 			snprintf(firstline, firstline_buflen, "%s Compressed status display\n", colorname(color));
538 
539 			SBUF_MALLOC(nonhistenv, 10 + strlen(service));
540 			snprintf(nonhistenv, nonhistenv_buflen, "NONHISTS=%s", service);
541 			putenv(nonhistenv);
542 		}
543 	}
544 	else if (source == SRC_HISTLOGS) {
545 		char logfn[PATH_MAX];
546 		struct stat st;
547 		FILE *fd;
548 		/*
549 		 * Some clients (Unix disk reports) don't have a newline before the
550 		 * "Status unchanged in ..." text. Most do, but at least Solaris and
551 		 * AIX do not. So just look for the text, not the newline.
552 		 */
553 		char *statusunchangedtext = "Status unchanged in ";
554 		char *receivedfromtext = "Message received from ";
555 		char *clientidtext = "Client data ID ";
556 		char *p, *unchangedstr, *receivedfromstr, *clientidstr, *hostnamedash;
557 		int n;
558 
559 		if (!tstamp) { errormsg(500, "Invalid request"); return 1; }
560 
561 		if (loadhostdata(hostname, &ip, &displayname, &compacts, 0) != 0) return 1;
562 		hostnamedash = strdup(hostname);
563 		p = hostnamedash; while ((p = strchr(p, '.')) != NULL) *p = '_';
564 		p = hostnamedash; while ((p = strchr(p, ',')) != NULL) *p = '_';
565 		snprintf(logfn, sizeof(logfn), "%s/%s/%s/%s", xgetenv("XYMONHISTLOGS"), hostnamedash, service, tstamp);
566 		xfree(hostnamedash);
567 		p = tstamp; while ((p = strchr(p, '_')) != NULL) *p = ' ';
568 		sethostenv_histlog(tstamp);
569 
570 		if ((stat(logfn, &st) == -1) || (st.st_size < 10) || (!S_ISREG(st.st_mode))) {
571 			errormsg(404, "Historical status log not available\n");
572 			return 1;
573 		}
574 
575 		fd = fopen(logfn, "r");
576 		if (!fd) {
577 			errormsg(404, "Unable to access historical logfile\n");
578 			return 1;
579 		}
580 		log = (char *)malloc(st.st_size+1);
581 		n = fread(log, 1, st.st_size, fd);
582 		if (n >= 0) *(log+n) = '\0'; else *log = '\0';
583 		fclose(fd);
584 
585 		p = strchr(log, '\n');
586 		if (!p) {
587 			firstline = strdup(log);
588 			restofmsg = NULL;
589 		}
590 		else {
591 			*p = '\0';
592 			firstline = strdup(log);
593 			restofmsg = (p+1);
594 			*p = '\n';
595 		}
596 
597 
598 		color = parse_color(log);
599 
600 		p = strstr(log, "<!-- [flags:");
601 		if (p) {
602 			p += strlen("<!-- [flags:");
603 			n = strspn(p, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
604 			flags = (char *)malloc(n+1);
605 			strncpy(flags, p, n);
606 			*(flags + n) = '\0';
607 		}
608 
609 		timesincechange[0] = '\0';
610 
611 		p = clientidstr = strstr(restofmsg, clientidtext);
612 		if (p) {
613 			p += strlen(clientidtext);
614 			n = strspn(p, "0123456789");
615 			clientid = (char *)malloc(n+1);
616 			strncpy(clientid, p, n);
617 			*(clientid+n) = '\0';
618 		}
619 
620 		p = unchangedstr = strstr(restofmsg, statusunchangedtext);
621 		if (p) {
622 			p += strlen(statusunchangedtext);
623 			n = strcspn(p, "\n"); if (n >= sizeof(timesincechange)) n = sizeof(timesincechange);
624 			strncpy(timesincechange, p, n);
625 			timesincechange[n] = '\0';
626 		}
627 
628 		p = receivedfromstr = strstr(restofmsg, receivedfromtext);
629 		if (p) {
630 			p += strlen(receivedfromtext);
631 			n = strspn(p, "0123456789.");
632 			sender = (char *)malloc(n+1);
633 			strncpy(sender, p, n);
634 			*(sender+n) = '\0';
635 		}
636 
637 		/* Kill the "Status unchanged ..." and "Message received ..." lines */
638 		if (unchangedstr) *unchangedstr = '\0';
639 		if (receivedfromstr) *receivedfromstr = '\0';
640 	}
641 
642 	if (outform == FRM_CLIENT) {
643 		fprintf(stdout, "Content-type: text/plain\n\n");
644 		fprintf(stdout, "%s", restofmsg);
645 	}
646 	else {
647 		if (clientid && (source == SRC_HISTLOGS)) {
648 			if (locatorbased) {
649 				char *cgiurl, *qres;
650 
651 				qres = locator_query(hostname, ST_HOSTDATA, &cgiurl);
652 				if (!qres) {
653 					errprintf("Cannot find hostdata files for host %s\n", hostname);
654 				}
655 				else {
656 					SBUF_REALLOC(clienturi, 1024 + strlen(cgiurl) + MAX_HTMLQUOTE_FACTOR*strlen(htmlquoted(hostname)) + strlen(clientid));
657 					snprintf(clienturi, clienturi_buflen, "%s/svcstatus.sh?CLIENT=%s&amp;TIMEBUF=%s",
658 						cgiurl, htmlquoted(hostname), clientid);
659 				}
660 			}
661 			else {
662 				char logfn[PATH_MAX];
663 				struct stat st;
664 
665 				snprintf(logfn, sizeof(logfn), "%s/%s", hostdatadir, clientid);
666 				clientavail = (stat(logfn, &st) == 0);
667 
668 				if (clientavail) {
669 					int curlen = strlen(clienturi);
670 
671 					SBUF_REALLOC(clienturi, 1024 + curlen + strlen(clientid));
672 					snprintf(clienturi + curlen, (clienturi_buflen - curlen), "&amp;TIMEBUF=%s", clientid);
673 				}
674 			}
675 		}
676 
677 		fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
678 		generate_html_log(hostname,
679 			  displayname,
680 			  service,
681 			  ip,
682 		          color, flapping,
683 			  (sender ? sender : "Xymon"),
684 			  (flags ? flags : ""),
685 		          logtime, timesincechange,
686 		          (firstline ? firstline : ""),
687 			  (restofmsg ? restofmsg : ""),
688 			  modifiers,
689 			  acktime, ackmsg, acklist,
690 			  disabletime, dismsg,
691 		          (source == SRC_HISTLOGS),
692 			  wantserviceid,
693 			  ishtmlformatted,
694 			  locatorbased,
695 			  multigraphs, (clientavail ? clienturi : NULL),
696 			  nkprio, nkttgroup, nkttextra,
697 			  backsecs,
698 			  stdout);
699 	}
700 
701 	/* Cleanup CGI params */
702 	if (hostname) xfree(hostname);
703 	if (service) xfree(service);
704 	if (tstamp) xfree(tstamp);
705 
706 	/* Cleanup main vars */
707 	if (clientid) xfree(clientid);
708 	if (sender) xfree(sender);
709 	if (flags) xfree(flags);
710 	if (firstline) xfree(firstline);
711 	if (log) xfree(log);
712 
713 	return 0;
714 }
715 
716 
main(int argc,char * argv[])717 int main(int argc, char *argv[])
718 {
719 	int argi;
720 	char *envarea = NULL;
721 
722 	multigraphs = ",disk,inode,qtree,quotas,snapshot,TblSpace,if_load,";
723 
724 	for (argi = 1; (argi < argc); argi++) {
725 		if (strcmp(argv[argi], "--historical") == 0) {
726 			source = SRC_HISTLOGS;
727 		}
728 		else if (strncmp(argv[argi], "--history=", 10) == 0) {
729 			char *val = strchr(argv[argi], '=')+1;
730 
731 			if (strcmp(val, "none") == 0)
732 				histlocation = HIST_NONE;
733 			else if (strcmp(val, "top") == 0)
734 				histlocation = HIST_TOP;
735 			else if (strcmp(val, "bottom") == 0)
736 				histlocation = HIST_BOTTOM;
737 		}
738 		else if (argnmatch(argv[argi], "--env=")) {
739 			char *p = strchr(argv[argi], '=');
740 			loadenv(p+1, envarea);
741 		}
742 		else if (argnmatch(argv[argi], "--area=")) {
743 			char *p = strchr(argv[argi], '=');
744 			envarea = strdup(p+1);
745 		}
746 		else if (strcmp(argv[argi], "--no-svcid") == 0) {
747 			wantserviceid = 0;
748 		}
749 		else if (argnmatch(argv[argi], "--templates=")) {
750 			char *p = strchr(argv[argi], '=');
751 			sethostenv_template(p+1);
752 		}
753 		else if (argnmatch(argv[argi], "--multigraphs=")) {
754 			char *p = strchr(argv[argi], '=');
755 			SBUF_MALLOC(multigraphs, strlen(p+1) + 3);
756 			snprintf(multigraphs, multigraphs_buflen, ",%s,", p+1);
757 		}
758 		else if (strcmp(argv[argi], "--no-disable") == 0) {
759 			showenadis = 0;
760 		}
761 		else if (strcmp(argv[argi], "--no-jsvalidation") == 0) {
762 			usejsvalidation = 0;
763 		}
764 		else if (strcmp(argv[argi], "--old-critical-config") == 0) {
765 			newcritconfig = 0;
766 		}
767 		else if (strcmp(argv[argi], "--debug") == 0) {
768 			debug = 1;
769 		}
770 		else if (argnmatch(argv[argi], "--locator=")) {
771 			char *p = strchr(argv[argi], '=');
772 			locator_init(p+1);
773 			locatorbased = 1;
774 		}
775 		else if (argnmatch(argv[argi], "--critical-config=")) {
776 			char *p = strchr(argv[argi], '=');
777 			critconfigfn = strdup(p+1);
778 		}
779 		else if (argnmatch(argv[argi], "--access=")) {
780 			char *p = strchr(argv[argi], '=');
781 			accessfn = strdup(p+1);
782 		}
783 	}
784 
785 	redirect_cgilog("svcstatus");
786 
787 	*errortxt = '\0';
788 	hostname = service = tstamp = NULL;
789 	if (do_request() != 0) {
790 		fprintf(stdout, "%s", errortxt);
791 		return 1;
792 	}
793 
794 	return 0;
795 }
796 
797