1 /*----------------------------------------------------------------------------*/
2 /* Xymon monitor network test tool.                                           */
3 /*                                                                            */
4 /* Copyright (C) 2003-2011 Henrik Storner <henrik@hswn.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: xymonnet.c 8084 2019-08-30 23:01:18Z jccleaver $";
12 
13 #include <limits.h>
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <ctype.h>
21 #include <netdb.h>
22 #include <sys/wait.h>
23 #include <rpc/rpc.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 
27 #include "libxymon.h"
28 
29 #ifdef HAVE_RPCENT_H
30 #include <rpc/rpcent.h>
31 #endif
32 
33 #ifdef BROKEN_HPUX_NETDB
34 /*
35  * Some HP-UX include files fail to define RPC functions
36  * and structs that are purely standard. At the same time,
37  * their own docs claim that the DO define them. Go figure ...
38  */
39 struct rpcent {
40 	char    *r_name;        /* name of server for this rpc program */
41 	char    **r_aliases;    /* alias list */
42 	int     r_number;       /* rpc program number */
43 };
44 extern struct rpcent *getrpcbyname(char *);
45 #endif
46 
47 #include "libxymon.h"
48 #include "version.h"
49 
50 #include "xymonnet.h"
51 #include "dns.h"
52 #include "contest.h"
53 #include "httptest.h"
54 #include "httpresult.h"
55 #include "httpcookies.h"
56 #include "ldaptest.h"
57 
58 #define DEFAULT_PING_CHILD_COUNT 1
59 #define MSGBUFSIZE 4096
60 
61 char *reqenv[] = {
62 	"NONETPAGE",
63 	"HOSTSCFG",
64 	"XYMONTMP",
65 	"XYMONHOME",
66 	NULL
67 };
68 
69 void *	svctree;			/* All known services, has service_t records */
70 service_t	*pingtest = NULL;		/* Identifies the pingtest within svctree list */
71 int		pingcount = 0;
72 service_t	*dnstest = NULL;		/* Identifies the dnstest within svctree list */
73 service_t	*httptest = NULL;		/* Identifies the httptest within svctree list */
74 service_t	*ldaptest = NULL;		/* Identifies the ldaptest within svctree list */
75 service_t	*rpctest = NULL;		/* Identifies the rpctest within svctree list */
76 void *       testhosttree;			/* All tested hosts, has testedhost_t records */
77 SBUF_DEFINE(nonetpage);				/* The "NONETPAGE" env. variable */
78 int		dnsmethod = DNS_THEN_IP;	/* How to do DNS lookups */
79 int 		timeout=10;			/* The timeout (seconds) for all TCP-tests */
80 char		*contenttestname = "content";   /* Name of the content checks column */
81 char		*ssltestname = "sslcert";       /* Name of the SSL certificate checks column */
82 char		*failtext = "not OK";
83 int             sslwarndays = 30;		/* If cert expires in fewer days, SSL cert column = yellow */
84 int             sslalarmdays = 10;		/* If cert expires in fewer days, SSL cert column = red */
85 int             mincipherbits = 0;		/* If weakest cipher is weaker than this # of buts, SSL cert column = red */
86 int		validity = 30;
87 int		pingchildcount = DEFAULT_PING_CHILD_COUNT;	/* How many ping processes to start */
88 char		*location = "";			/* XYMONNETWORK value */
89 int		hostcount = 0;
90 int		testcount = 0;
91 int		notesthostcount = 0;
92 char		**selectedhosts;
93 int		selectedcount = 0;
94 int		testuntagged = 0;
95 time_t		frequenttestlimit = 1800;	/* Interval (seconds) when failing hosts are retried frequently */
96 int		checktcpresponse = 0;
97 int		dotraceroute = 0;
98 int		fqdn = 1;
99 int		dosendflags = 1;
100 SBUF_DEFINE(pingcmd);
101 char		pinglog[PATH_MAX];
102 char		pingerrlog[PATH_MAX];
103 pid_t		*pingpids;
104 int		respcheck_color = COL_YELLOW;
105 httpstatuscolor_t *httpstatusoverrides = NULL;
106 int		extcmdtimeout = 30;
107 int		bigfailure = 0;
108 char		*defaultsourceip = NULL;
109 int		loadhostsfromxymond = 0;
110 int		sslminkeysize = 0;
111 STATIC_SBUF_DEFINE(warnbuf);
112 
dump_hostlist(void)113 void dump_hostlist(void)
114 {
115 	xtreePos_t handle;
116 	testedhost_t *walk;
117 
118 	for (handle = xtreeFirst(testhosttree); (handle != xtreeEnd(testhosttree)); handle = xtreeNext(testhosttree, handle)) {
119 		walk = (testedhost_t *)xtreeData(testhosttree, handle);
120 		printf("Hostname: %s\n", textornull(walk->hostname));
121 		printf("\tIP           : %s\n", textornull(walk->ip));
122 		printf("\tHosttype     : %s\n", textornull(walk->hosttype));
123 
124 		printf("\tFlags        :");
125 		if (walk->testip) printf(" testip");
126 		if (walk->dialup) printf(" dialup");
127 		if (walk->nosslcert) printf(" nosslcert");
128 		if (walk->dodns) printf(" dodns");
129 		if (walk->dnserror) printf(" dnserror");
130 		if (walk->repeattest) printf(" repeattest");
131 		if (walk->noconn) printf(" noconn");
132 		if (walk->noping) printf(" noping");
133 		if (walk->dotrace) printf(" dotrace");
134 		printf("\n");
135 
136 		printf("\tbadconn      : %d:%d:%d\n", walk->badconn[0], walk->badconn[1], walk->badconn[2]);
137 		printf("\tdowncount    : %d started %s", walk->downcount, ctime(&walk->downstart));
138 		printf("\trouterdeps   : %s\n", textornull(walk->routerdeps));
139 		printf("\tdeprouterdown: %s\n", (walk->deprouterdown ? textornull(walk->deprouterdown->hostname) : ""));
140 		printf("\tldapauth     : '%s' '%s'\n", textornull(walk->ldapuser), textornull(walk->ldappasswd));
141 		printf("\tSSL alerts   : %d:%d\n", walk->sslwarndays, walk->sslalarmdays);
142 		printf("\n");
143 	}
144 }
dump_testitems(void)145 void dump_testitems(void)
146 {
147 	xtreePos_t handle;
148 	service_t *swalk;
149 	testitem_t *iwalk;
150 
151 	for (handle = xtreeFirst(svctree); handle != xtreeEnd(svctree); handle = xtreeNext(svctree, handle)) {
152 		swalk = (service_t *)xtreeData(svctree, handle);
153 
154 		printf("Service %s, port %d, toolid %d\n", swalk->testname, swalk->portnum, swalk->toolid);
155 
156 		for (iwalk = swalk->items; (iwalk); iwalk = iwalk->next) {
157 			printf("\tHost        : %s\n", textornull(iwalk->host->hostname));
158 			printf("\ttestspec    : %s\n", textornull(iwalk->testspec));
159 			printf("\tFlags       :");
160 			if (iwalk->dialup) printf(" dialup");
161 			if (iwalk->reverse) printf(" reverse");
162 			if (iwalk->silenttest) printf(" silenttest");
163 			if (iwalk->alwaystrue) printf(" alwaystrue");
164 			printf("\n");
165 			printf("\tOpen        : %d\n", iwalk->open);
166 			printf("\tBanner      : %s\n", textornull(STRBUF(iwalk->banner)));
167 			printf("\tcertinfo    : %s\n", textornull(iwalk->certinfo));
168 			printf("\tDuration    : %ld.%06ld\n", (long int)iwalk->duration.tv_sec, (long int)iwalk->duration.tv_nsec / 1000);
169 			printf("\tbadtest     : %d:%d:%d\n", iwalk->badtest[0], iwalk->badtest[1], iwalk->badtest[2]);
170 			printf("\tdowncount    : %d started %s", iwalk->downcount, ctime(&iwalk->downstart));
171 			printf("\n");
172 		}
173 
174 		printf("\n");
175 	}
176 }
177 
find_test(char * hostname,char * testname)178 testitem_t *find_test(char *hostname, char *testname)
179 {
180 	xtreePos_t handle;
181 	testedhost_t *h;
182 	service_t *s;
183 	testitem_t *t;
184 
185 	handle = xtreeFind(svctree, testname);
186 	if (handle == xtreeEnd(svctree)) return NULL;
187 	s = (service_t *)xtreeData(svctree, handle);
188 
189 	handle = xtreeFind(testhosttree, hostname);
190 	if (handle == xtreeEnd(testhosttree)) return NULL;
191 	h = (testedhost_t *)xtreeData(testhosttree, handle);
192 
193 	for (t=s->items; (t && (t->host != h)); t = t->next) ;
194 
195 	return t;
196 }
197 
198 
deptest_failed(testedhost_t * host,char * testname)199 char *deptest_failed(testedhost_t *host, char *testname)
200 {
201 	static char result[1024];
202 
203 	char *depcopy;
204 	char depitem[MAX_LINE_LEN];
205 	char *p, *q;
206 	char *dephostname, *deptestname, *nextdep;
207 	testitem_t *t;
208 
209 	if (host->deptests == NULL) return NULL;
210 
211 	depcopy = strdup(host->deptests);
212 	snprintf(depitem, sizeof(depitem), "(%s:", testname);
213 	p = strstr(depcopy, depitem);
214 	if (p == NULL) { xfree(depcopy); return NULL; }
215 
216 	result[0] = '\0';
217 	dephostname = p+strlen(depitem);
218 	q = strchr(dephostname, ')');
219 	if (q) *q = '\0';
220 
221 	/* dephostname now points to a list of "host1/test1,host2/test2" dependent tests. */
222 	while (dephostname) {
223 		p = strchr(dephostname, '/');
224 		if (p) {
225 			*p = '\0';
226 			deptestname = (p+1);
227 		}
228 		else deptestname = "";
229 
230 		p = strchr(deptestname, ',');
231 		if (p) {
232 			*p = '\0';
233 			nextdep = (p+1);
234 		}
235 		else nextdep = NULL;
236 
237 		t = find_test(dephostname, deptestname);
238 		if (t && !t->open) {
239 			if (strlen(result) == 0) {
240 				strncpy(result, "\nThis test depends on the following test(s) that failed:\n\n", sizeof(result));
241 			}
242 
243 			if ((strlen(result) + strlen(dephostname) + strlen(deptestname) + 2) < sizeof(result)) {
244 				strncat(result, dephostname, (sizeof(result) - strlen(result)));
245 				strncat(result, "/", (sizeof(result) - strlen(result)));
246 				strncat(result, deptestname, (sizeof(result) - strlen(result)));
247 				strncat(result, "\n", (sizeof(result) - strlen(result)));
248 			}
249 		}
250 
251 		dephostname = nextdep;
252 	}
253 
254 	xfree(depcopy);
255 	if (*result) strncat(result, "\n\n", (sizeof(result) - strlen(result)));
256 
257 	return (*result ? result : NULL);
258 }
259 
260 
add_service(char * name,int port,int namelen,int toolid)261 service_t *add_service(char *name, int port, int namelen, int toolid)
262 {
263 	xtreePos_t handle;
264 	service_t *svc;
265 
266 	/* Avoid duplicates */
267 	handle = xtreeFind(svctree, name);
268 	if (handle != xtreeEnd(svctree)) {
269 		svc = (service_t *)xtreeData(svctree, handle);
270 		return svc;
271 	}
272 
273 	svc = (service_t *) malloc(sizeof(service_t));
274 	svc->portnum = port;
275 	svc->testname = strdup(name);
276 	svc->toolid = toolid;
277 	svc->namelen = namelen;
278 	svc->items = NULL;
279 	xtreeAdd(svctree, svc->testname, svc);
280 
281 	return svc;
282 }
283 
getportnumber(char * svcname)284 int getportnumber(char *svcname)
285 {
286 	struct servent *svcinfo;
287 	int result = 0;
288 
289 	result = default_tcp_port(svcname);
290 	if (result == 0) {
291 		svcinfo = getservbyname(svcname, NULL);
292 		if (svcinfo) result = ntohs(svcinfo->s_port);
293 	}
294 
295 	return result;
296 }
297 
load_services(void)298 void load_services(void)
299 {
300 	char *netsvcs;
301 	char *p;
302 
303 	netsvcs = strdup(init_tcp_services());
304 
305 	p = strtok(netsvcs, " ");
306 	while (p) {
307 		add_service(p, getportnumber(p), 0, TOOL_CONTEST);
308 		p = strtok(NULL, " ");
309 	}
310 	xfree(netsvcs);
311 
312 	/* Save NONETPAGE env. var in ",test1,test2," format for easy and safe grepping */
313 	SBUF_MALLOC(nonetpage, strlen(xgetenv("NONETPAGE"))+3);
314 	snprintf(nonetpage, nonetpage_buflen, ",%s,", xgetenv("NONETPAGE"));
315 	for (p=nonetpage; (*p); p++) if (*p == ' ') *p = ',';
316 }
317 
318 
init_testedhost(char * hostname)319 testedhost_t *init_testedhost(char *hostname)
320 {
321 	testedhost_t *newhost;
322 
323 	hostcount++;
324 	newhost = (testedhost_t *) calloc(1, sizeof(testedhost_t));
325 	newhost->hostname = strdup(hostname);
326 	newhost->dotrace = dotraceroute;
327 	newhost->sslwarndays = sslwarndays;
328 	newhost->sslalarmdays = sslalarmdays;
329 	newhost->mincipherbits = mincipherbits;
330 
331 	return newhost;
332 }
333 
init_testitem(testedhost_t * host,service_t * service,char * srcip,char * testspec,int dialuptest,int reversetest,int alwaystruetest,int silenttest,int sendasdata)334 testitem_t *init_testitem(testedhost_t *host, service_t *service, char *srcip, char *testspec,
335                           int dialuptest, int reversetest, int alwaystruetest, int silenttest,
336 			  int sendasdata)
337 {
338 	testitem_t *newtest;
339 
340 	testcount++;
341 	newtest = (testitem_t *) calloc(1, sizeof(testitem_t));
342 	newtest->host = host;
343 	newtest->service = service;
344 	newtest->dialup = dialuptest;
345 	newtest->reverse = reversetest;
346 	newtest->alwaystrue = alwaystruetest;
347 	newtest->silenttest = silenttest;
348 	newtest->senddata = sendasdata;
349 	newtest->testspec = (testspec ? strdup(testspec) : NULL);
350 	if (srcip)
351 		newtest->srcip = strdup(srcip);
352 	else if (defaultsourceip)
353 		newtest->srcip = defaultsourceip;
354 	else
355 		newtest->srcip = NULL;
356 	newtest->privdata = NULL;
357 	newtest->open = 0;
358 	newtest->banner = newstrbuffer(0);
359 	newtest->certinfo = NULL;
360 	newtest->certissuer = NULL;
361 	newtest->certexpires = 0;
362 	newtest->certkeysz = 0;
363 	newtest->mincipherbits = 0;
364 	newtest->duration.tv_sec = newtest->duration.tv_nsec = -1;
365 	newtest->downcount = 0;
366 	newtest->badtest[0] = newtest->badtest[1] = newtest->badtest[2] = 0;
367 	newtest->internal = 0;
368 	newtest->next = NULL;
369 
370 	return newtest;
371 }
372 
373 
wanted_host(void * host,char * netstring)374 int wanted_host(void *host, char *netstring)
375 {
376 	char *netlocation = xmh_item(host, XMH_NET);
377 
378 	if (selectedcount == 0)
379 		return ((strlen(netstring) == 0) || 				   /* No XYMONNETWORK = do all */
380 			(netlocation && (strcmp(netlocation, netstring) == 0)) ||  /* XYMONNETWORK && matching NET: tag */
381 			(testuntagged && (netlocation == NULL)));		   /* No NET: tag for this host */
382 	else {
383 		/* User provided an explicit list of hosts to test */
384 		int i;
385 
386 		for (i=0; (i < selectedcount); i++) {
387 			if (strcmp(selectedhosts[i], xmh_item(host, XMH_HOSTNAME)) == 0) return 1;
388 		}
389 	}
390 
391 	return 0;
392 }
393 
394 
load_tests(void)395 void load_tests(void)
396 {
397 	char *p;
398 	SBUF_DEFINE(routestring);
399 	void *hwalk;
400 	testedhost_t *h;
401 	int badtagsused = 0;
402 
403 	if (loadhostsfromxymond) {
404 		if (load_hostnames("@", NULL, fqdn) != 0) {
405 			errprintf("Cannot load host configuration from xymond\n");
406 			return;
407 		}
408 	}
409 	else {
410 		if (load_hostnames(xgetenv("HOSTSCFG"), "netinclude", fqdn) != 0) {
411 			errprintf("Cannot load host configuration from %s\n", xgetenv("HOSTSCFG"));
412 			return;
413 		}
414 	}
415 
416 	if (first_host() == NULL) {
417 		errprintf("Empty configuration from %s\n", (loadhostsfromxymond ? "xymond" : xgetenv("HOSTSCFG")));
418 		return;
419 	}
420 
421 	/* Each network test tagged with NET:locationname */
422 	if (strlen(location) > 0) {
423 		SBUF_MALLOC(routestring, strlen(location)+strlen("route_:")+1);
424 		snprintf(routestring, routestring_buflen, "route_%s:", location);
425 	}
426 
427 	for (hwalk = first_host(); (hwalk); hwalk = next_host(hwalk, 0)) {
428 		int anytests = 0;
429 		int ping_dialuptest = 0, ping_reversetest = 0;
430 		char *testspec;
431 
432 		if (!wanted_host(hwalk, location)) continue;
433 
434 		h = init_testedhost(xmh_item(hwalk, XMH_HOSTNAME));
435 
436 		p = xmh_custom_item(hwalk, "badconn:");
437 		if (p) {
438 			sscanf(p+strlen("badconn:"), "%d:%d:%d", &h->badconn[0], &h->badconn[1], &h->badconn[2]);
439 			badtagsused = 1;
440 		}
441 
442 		p = xmh_custom_item(hwalk, "route:");
443 		if (p) h->routerdeps = p + strlen("route:");
444 		if (routestring) {
445 			p = xmh_custom_item(hwalk, routestring);
446 			if (p) h->routerdeps = p + strlen(routestring);
447 		}
448 
449 		if (xmh_item(hwalk, XMH_FLAG_NOCONN)) h->noconn = 1;
450 		if (xmh_item(hwalk, XMH_FLAG_NOPING)) h->noping = 1;
451 		if (xmh_item(hwalk, XMH_FLAG_TRACE)) h->dotrace = 1;
452 		if (xmh_item(hwalk, XMH_FLAG_NOTRACE)) h->dotrace = 0;
453 		if (xmh_item(hwalk, XMH_FLAG_TESTIP)) h->testip = 1;
454 		if (xmh_item(hwalk, XMH_FLAG_DIALUP)) h->dialup = 1;
455 		if (xmh_item(hwalk, XMH_FLAG_NOSSLCERT)) h->nosslcert = 1;
456 		if (xmh_item(hwalk, XMH_FLAG_LDAPFAILYELLOW)) h->ldapsearchfailyellow = 1;
457 		if (xmh_item(hwalk, XMH_FLAG_HIDEHTTP)) h->hidehttp = 1;
458 
459 		p = xmh_item(hwalk, XMH_SSLDAYS);
460 		if (p) sscanf(p, "%d:%d", &h->sslwarndays, &h->sslalarmdays);
461 
462 		p = xmh_item(hwalk, XMH_SSLMINBITS);
463 		if (p) h->mincipherbits = atoi(p);
464 
465 		p = xmh_item(hwalk, XMH_DEPENDS);
466 		if (p) h->deptests = p;
467 
468 		p = xmh_item(hwalk, XMH_LDAPLOGIN);
469 		if (p) {
470 			h->ldapuser = strdup(p);
471 			h->ldappasswd = (strchr(h->ldapuser, ':'));
472 			if (h->ldappasswd) {
473 				*h->ldappasswd = '\0';
474 				h->ldappasswd++;
475 			}
476 		}
477 
478 		p = xmh_item(hwalk, XMH_DESCRIPTION);
479 		if (p) {
480 			h->hosttype = strdup(p);
481 			p = strchr(h->hosttype, ':');
482 			if (p) *p = '\0';
483 		}
484 
485 		testspec = xmh_item_walk(hwalk);
486 		while (testspec) {
487 			service_t *s = NULL;
488 			int dialuptest = 0, reversetest = 0, silenttest = 0, sendasdata = 0;
489 			char *srcip = NULL;
490 			int alwaystruetest = (xmh_item(hwalk, XMH_FLAG_NOCLEAR) != NULL);
491 
492 			if (xmh_item_idx(testspec) == -1) {
493 
494 				/* Test prefixes:
495 				 * - '?' denotes dialup test, i.e. report failures as clear.
496 				 * - '|' denotes reverse test, i.e. service should be DOWN.
497 				 * - '~' denotes test that ignores ping result (normally,
498 				 *       TCP tests are reported CLEAR if ping check fails;
499 				 *       with this flag report their true status)
500 				 */
501 				if (*testspec == '?') { dialuptest=1;     testspec++; }
502 				if (*testspec == '!') { reversetest=1;    testspec++; }
503 				if (*testspec == '~') { alwaystruetest=1; testspec++; }
504 
505 				if (pingtest && argnmatch(testspec, pingtest->testname)) {
506 					char *p;
507 
508 					/*
509 					 * Ping/conn test. Save any modifier flags for later use.
510 					 */
511 					ping_dialuptest = dialuptest;
512 					ping_reversetest = reversetest;
513 					p = strchr(testspec, '=');
514 					if (p) {
515 						char *ips;
516 
517 						/* Extra ping tests - save them for later */
518 						h->extrapings = (extraping_t *)malloc(sizeof(extraping_t));
519 						h->extrapings->iplist = NULL;
520 						if (argnmatch(p, "=worst,")) {
521 							h->extrapings->matchtype = MULTIPING_WORST;
522 							ips = strdup(p+7);
523 						}
524 						else if (argnmatch(p, "=best,")) {
525 							h->extrapings->matchtype = MULTIPING_BEST;
526 							ips = strdup(p+6);
527 						}
528 						else {
529 							h->extrapings->matchtype = MULTIPING_BEST;
530 							ips = strdup(p+1);
531 						}
532 
533 						do {
534 							ipping_t *newping = (ipping_t *)malloc(sizeof(ipping_t));
535 
536 							newping->ip = ips;
537 							newping->open = 0;
538 							newping->banner = newstrbuffer(0);
539 							newping->next = h->extrapings->iplist;
540 							h->extrapings->iplist = newping;
541 							ips = strchr(ips, ',');
542 							if (ips) { *ips = '\0'; ips++; }
543 						} while (ips && (*ips));
544 					}
545 					s = NULL; /* Don't add the test now - ping is special (enabled by default) */
546 				}
547 				else if ((argnmatch(testspec, "ldap://")) || (argnmatch(testspec, "ldaps://"))) {
548 					/*
549 					 * LDAP test. This uses ':' a lot, so save it here.
550 					 */
551 #ifdef HAVE_LDAP
552 					s = ldaptest;
553 					add_url_to_dns_queue(testspec);
554 #else
555 					errprintf("Host %s: ldap test requested, but xymonnet was built with no ldap support\n", xmh_item(hwalk, XMH_HOSTNAME));
556 #endif
557 				}
558 				else if ((strcmp(testspec, "http") == 0) || (strcmp(testspec, "https") == 0)) {
559 					errprintf("Host %s: http/https tests requires a full URL\n", xmh_item(hwalk, XMH_HOSTNAME));
560 				}
561 				else if ( argnmatch(testspec, "http")         ||
562 					  argnmatch(testspec, "content=http") ||
563 					  argnmatch(testspec, "cont;http")    ||
564 					  argnmatch(testspec, "cont=")        ||
565 					  argnmatch(testspec, "nocont;http")  ||
566 					  argnmatch(testspec, "nocont=")      ||
567 					  argnmatch(testspec, "post;http")    ||
568 					  argnmatch(testspec, "post=")        ||
569 					  argnmatch(testspec, "nopost;http")  ||
570 					  argnmatch(testspec, "nopost=")      ||
571 					  argnmatch(testspec, "soap;http")    ||
572 					  argnmatch(testspec, "soap=")        ||
573 					  argnmatch(testspec, "nosoap;http")    ||
574 					  argnmatch(testspec, "nosoap=")        ||
575 					  argnmatch(testspec, "type;http")    ||
576 					  argnmatch(testspec, "type=")        )      {
577 
578 					/* HTTP test. */
579 					weburl_t url;
580 
581 					decode_url(testspec, &url);
582 					if (url.desturl->parseerror || (url.proxyurl && url.proxyurl->parseerror)) {
583 						s = NULL;
584 						errprintf("Host %s: Invalid URL for http test - ignored: %s\n",
585 							  xmh_item(hwalk, XMH_HOSTNAME), testspec);
586 					}
587 					else {
588 						s = httptest;
589 						if (!url.desturl->ip)
590 							add_url_to_dns_queue(testspec);
591 					}
592 				}
593 				else if (argnmatch(testspec, "apache") || argnmatch(testspec, "apache=")) {
594 					char *userfmt = "cont=apache;%s;.";
595 					char *deffmt = "cont=apache;http://%s/server-status?auto;.";
596 					STATIC_SBUF_DEFINE(statusurl);
597 					char *userurl;
598 
599 					if (statusurl != NULL) xfree(statusurl);
600 
601 					userurl = strchr(testspec, '=');
602 					if (userurl) {
603 						weburl_t url;
604 						userurl++;
605 
606 						decode_url(userurl, &url);
607 						if (url.desturl->parseerror || (url.proxyurl && url.proxyurl->parseerror)) {
608 							s = NULL;
609 							errprintf("Host %s: Invalid URL for apache test - ignored: %s\n", xmh_item(hwalk, XMH_HOSTNAME), testspec);
610 						}
611 						else {
612 							SBUF_MALLOC(statusurl, strlen(userurl) + strlen(userfmt) + 1);
613 							snprintf(statusurl, statusurl_buflen, userfmt, userurl);
614 							s = httptest;
615 						}
616 					}
617 					else {
618 						char *ip = xmh_item(hwalk, XMH_IP);
619 						SBUF_MALLOC(statusurl, strlen(deffmt) + strlen(ip) + 1);
620 						snprintf(statusurl, statusurl_buflen, deffmt, ip);
621 						s = httptest;
622 					}
623 
624 					if (s) {
625 						testspec = statusurl;
626 						add_url_to_dns_queue(testspec);
627 						sendasdata = 1;
628 					}
629 				}
630 				else if (argnmatch(testspec, "rpc")) {
631 					/*
632 					 * rpc check via rpcinfo
633 					 */
634 					s = rpctest;
635 				}
636 				else if (argnmatch(testspec, "dns=")) {
637 					s = dnstest;
638 				}
639 				else if (argnmatch(testspec, "dig=")) {
640 					s = dnstest;
641 				}
642 				else {
643 					/*
644 					 * Simple TCP connect test.
645 					 */
646 					char *option;
647 					xtreePos_t handle;
648 
649 					/* See if there's a source IP */
650 					srcip = strchr(testspec, '@');
651 					if (srcip) {
652 						*srcip = '\0';
653 						srcip++;
654 					}
655 
656 					/* Remove any trailing ":s", ":q", ":Q", ":portnumber" */
657 					option = strchr(testspec, ':');
658 					if (option) {
659 						*option = '\0';
660 						option++;
661 					}
662 
663 					/* Find the service */
664 					handle = xtreeFind(svctree, testspec);
665 					s = ((handle == xtreeEnd(svctree)) ? NULL : (service_t *)xtreeData(svctree, handle));
666 					if (option && s) {
667 						/*
668 						 * Check if it is a service with an explicit portnumber.
669 						 * If it is, then create a new service record named
670 						 * "SERVICE_PORT" so we can merge tests for this service+port
671 						 * combination for multiple hosts.
672 						 *
673 						 * According to Xymon docs, this type of services must be in
674 						 * XYMONNETSVCS - so it is known already.
675 						 */
676 						int specialport = 0;
677 						SBUF_DEFINE(specialname);
678 						char *opt2 = strrchr(option, ':');
679 
680 						if (opt2) {
681 							if (strcmp(opt2, ":s") == 0) {
682 								/* option = "portnumber:s" */
683 								silenttest = 1;
684 								*opt2 = '\0';
685 								specialport = atoi(option);
686 								*opt2 = ':';
687 							}
688 						}
689 						else if (strcmp(option, "s") == 0) {
690 							/* option = "s" */
691 							silenttest = 1;
692 							specialport = 0;
693 						}
694 						else {
695 							/* option = "portnumber" */
696 							specialport = atoi(option);
697 						}
698 
699 						if (specialport) {
700 							SBUF_MALLOC(specialname, strlen(s->testname)+10);
701 							snprintf(specialname, specialname_buflen,"%s_%d", s->testname, specialport);
702 							s = add_service(specialname, specialport, strlen(s->testname), TOOL_CONTEST);
703 							xfree(specialname);
704 						}
705 					}
706 
707 					if (s) h->dodns = 1;
708 					if (option) *(option-1) = ':';
709 				}
710 
711 				if (s) {
712 					testitem_t *newtest;
713 
714 					anytests = 1;
715 					newtest = init_testitem(h, s, srcip, testspec, dialuptest, reversetest, alwaystruetest, silenttest, sendasdata);
716 					newtest->next = s->items;
717 					s->items = newtest;
718 
719 					if (s == httptest) h->firsthttp = newtest;
720 					else if (s == ldaptest) {
721 						xtreePos_t handle;
722 						service_t *s2 = NULL;
723 						testitem_t *newtest2;
724 
725 						h->firstldap = newtest;
726 
727 						/*
728 						 * Add a plain tcp-connect test for the LDAP service.
729 						 * We don't want the LDAP library to run amok and do
730 						 * time-consuming connect retries if the service
731 						 * is down.
732 						 */
733 						handle = xtreeFind(svctree, "ldap");
734 						s2 = ((handle == xtreeEnd(svctree)) ? NULL : (service_t *)xtreeData(svctree, handle));
735 						if (s2) {
736 							newtest2 = init_testitem(h, s2, NULL, "ldap", 0, 0, 0, 0, 1);
737 							newtest2->internal = 1;
738 							newtest2->next = s2->items;
739 							s2->items = newtest2;
740 							newtest->privdata = newtest2;
741 						}
742 					}
743 				}
744 			}
745 
746 			testspec = xmh_item_walk(NULL);
747 		}
748 
749 		if (pingtest && !h->noconn) {
750 			/* Add the ping check */
751 			testitem_t *newtest;
752 
753 			anytests = 1;
754 			newtest = init_testitem(h, pingtest, NULL, NULL, ping_dialuptest, ping_reversetest, 1, 0, 0);
755 			newtest->next = pingtest->items;
756 			pingtest->items = newtest;
757 			h->dodns = 1;
758 		}
759 
760 
761 		/*
762 		 * Setup badXXX values.
763 		 *
764 		 * We need to do this last, because the testitem_t records do
765 		 * not exist until the test has been created.
766 		 *
767 		 * So after parsing the badFOO tag, we must find the testitem_t
768 		 * record created earlier for this test (it may not exist).
769 		 */
770 		testspec = xmh_item_walk(hwalk);
771 		while (testspec) {
772 			char *testname, *timespec, *badcounts;
773 			int badclear, badyellow, badred;
774 			int inscope;
775 			testitem_t *twalk;
776 			service_t *swalk;
777 
778 			if (strncmp(testspec, "bad", 3) != 0) {
779 				/* Not a bad* tag - skip it */
780 				testspec = xmh_item_walk(NULL);
781 				continue;
782 			}
783 
784 
785 			badtagsused = 1;
786 			badclear = badyellow = badred = 0;
787 			inscope = 1;
788 
789 			testname = testspec+strlen("bad");
790 			badcounts = strchr(testspec, ':');
791 			if (badcounts) {
792 				if (sscanf(badcounts, ":%d:%d:%d", &badclear, &badyellow, &badred) != 3) {
793 					errprintf("Host %s: Incorrect 'bad' counts: '%s'\n", xmh_item(hwalk, XMH_HOSTNAME), badcounts);
794 					badcounts = NULL;
795 				}
796 			}
797 			timespec = strchr(testspec, '-');
798 			if (timespec) inscope = periodcoversnow(timespec);
799 
800 			if (strlen(testname) && badcounts && inscope) {
801 				char *p;
802 				xtreePos_t handle;
803 				twalk = NULL;
804 
805 				p = strchr(testname, ':'); if (p) *p = '\0';
806 				handle = xtreeFind(svctree, testname);
807 				swalk = ((handle == xtreeEnd(svctree)) ? NULL : (service_t *)xtreeData(svctree, handle));
808 				if (p) *p = ':';
809 				if (swalk) {
810 					if (swalk == httptest) twalk = h->firsthttp;
811 					else if (swalk == ldaptest) twalk = h->firstldap;
812 					else for (twalk = swalk->items; (twalk && (twalk->host != h)); twalk = twalk->next) ;
813 				}
814 
815 				if (twalk) {
816 					twalk->badtest[0] = badclear;
817 					twalk->badtest[1] = badyellow;
818 					twalk->badtest[2] = badred;
819 				}
820 				else {
821 					dbgprintf("No test for badtest spec host=%s, test=%s\n",
822 						h->hostname, testname);
823 				}
824 			}
825 
826 			testspec = xmh_item_walk(NULL);
827 		}
828 
829 
830 		if (anytests) {
831 			xtreeStatus_t res;
832 
833 			/*
834 			 * Check for a duplicate host def. Causes all sorts of funny problems.
835 			 * However, don't drop the second definition - to do this, we will have
836 			 * to clean up the testitem lists as well, or we get crashes when
837 			 * tests belong to a non-existing host.
838 			 */
839 
840 			res = xtreeAdd(testhosttree, h->hostname, h);
841 			if (res == XTREE_STATUS_DUPLICATE_KEY) {
842 				errprintf("Host %s appears twice in hosts.cfg! This may cause strange results\n", h->hostname);
843 			}
844 
845 			strncpy(h->ip, xmh_item(hwalk, XMH_IP), sizeof(h->ip));
846 			if (!h->testip && (dnsmethod != IP_ONLY)) add_host_to_dns_queue(h->hostname);
847 		}
848 		else {
849 			/* No network tests for this host, so ignore it */
850 			dbgprintf("Did not find any network tests for host %s\n", h->hostname);
851 			xfree(h);
852 			notesthostcount++;
853 		}
854 
855 	}
856 
857 	if (badtagsused) {
858 		errprintf("WARNING: The 'bad<TESTNAME>' syntax has been deprecated, please convert to 'delayred' and/or 'delayyellow' tags\n");
859 	}
860 
861 	return;
862 }
863 
ip_to_test(testedhost_t * h)864 char *ip_to_test(testedhost_t *h)
865 {
866 	char *dnsresult;
867 	int nullip = (strcmp(h->ip, "0.0.0.0") == 0);
868 
869 	if (!nullip && (h->testip || (dnsmethod == IP_ONLY))) {
870 		/* Already have the IP setup */
871 	}
872 	else if (h->dodns) {
873 		dnsresult = dnsresolve(h->hostname);
874 
875 		if (dnsresult) {
876 			strncpy(h->ip, dnsresult, sizeof(h->ip));
877 		}
878 		else if ((dnsmethod == DNS_THEN_IP) && !nullip) {
879 			/* Already have the IP setup */
880 		}
881 		else {
882 			char msg[512];
883 			/* Cannot resolve hostname */
884 			h->dnserror = 1;
885 /* Make this a warning rather than an error
886 			errprintf("xymonnet: Cannot resolve IP for host %s\n", h->hostname);
887  */
888 			snprintf(msg, sizeof(msg), "xymonnet: Cannot resolve IP for host %s\n", h->hostname);
889 			if (warnbuf == NULL) {
890 				SBUF_MALLOC(warnbuf, 8192);
891 				*warnbuf = '\0';
892 			}
893 			else if ((strlen(warnbuf) + strlen(msg)) > warnbuf_buflen) {
894 				SBUF_REALLOC(warnbuf, warnbuf_buflen + 8192);
895 			}
896 			strncat(warnbuf, msg, (warnbuf_buflen - strlen(warnbuf)));
897 		}
898 	}
899 
900 	return h->ip;
901 }
902 
903 
load_ping_status(void)904 void load_ping_status(void)
905 {
906 	FILE *statusfd;
907 	char statusfn[PATH_MAX];
908 	char l[MAX_LINE_LEN];
909 	char host[MAX_LINE_LEN];
910 	int  downcount;
911 	time_t downstart;
912 	xtreePos_t handle;
913 	testedhost_t *h;
914 
915 	snprintf(statusfn, sizeof(statusfn), "%s/ping.%s.status", xgetenv("XYMONTMP"), location);
916 	statusfd = fopen(statusfn, "r");
917 	if (statusfd == NULL) return;
918 
919 	while (fgets(l, sizeof(l), statusfd)) {
920 		unsigned int uidownstart;
921 		if (sscanf(l, "%s %d %u", host, &downcount, &uidownstart) == 3) {
922 			downstart = uidownstart;
923 			handle = xtreeFind(testhosttree, host);
924 			if (handle != xtreeEnd(testhosttree)) {
925 				h = (testedhost_t *)xtreeData(testhosttree, handle);
926 				if (!h->noping && !h->noconn) {
927 					h->downcount = downcount;
928 					h->downstart = downstart;
929 				}
930 			}
931 		}
932 	}
933 
934 	fclose(statusfd);
935 }
936 
save_ping_status(void)937 void save_ping_status(void)
938 {
939 	FILE *statusfd;
940 	char statusfn[PATH_MAX];
941 	testitem_t *t;
942 	int didany = 0;
943 
944 	snprintf(statusfn, sizeof(statusfn), "%s/ping.%s.status", xgetenv("XYMONTMP"), location);
945 	statusfd = fopen(statusfn, "w");
946 	if (statusfd == NULL) return;
947 
948 	for (t=pingtest->items; (t); t = t->next) {
949 		if (t->host->downcount) {
950 			fprintf(statusfd, "%s %d %u\n", t->host->hostname, t->host->downcount, (unsigned int)t->host->downstart);
951 			didany = 1;
952 			t->host->repeattest = ((getcurrenttime(NULL) - t->host->downstart) < frequenttestlimit);
953 		}
954 	}
955 
956 	fclose(statusfd);
957 	if (!didany) unlink(statusfn);
958 }
959 
load_test_status(service_t * test)960 void load_test_status(service_t *test)
961 {
962 	FILE *statusfd;
963 	char statusfn[PATH_MAX];
964 	char l[MAX_LINE_LEN];
965 	char host[MAX_LINE_LEN];
966 	int  downcount;
967 	time_t downstart;
968 	xtreePos_t handle;
969 	testedhost_t *h;
970 	testitem_t *walk;
971 
972 	snprintf(statusfn, sizeof(statusfn), "%s/%s.%s.status", xgetenv("XYMONTMP"), test->testname, location);
973 	statusfd = fopen(statusfn, "r");
974 	if (statusfd == NULL) return;
975 
976 	while (fgets(l, sizeof(l), statusfd)) {
977 		unsigned int uidownstart;
978 		if (sscanf(l, "%s %d %u", host, &downcount, &uidownstart) == 3) {
979 			downstart = uidownstart;
980 			handle = xtreeFind(testhosttree, host);
981 			if (handle != xtreeEnd(testhosttree)) {
982 				h = (testedhost_t *)xtreeData(testhosttree, handle);
983 				if (test == httptest) walk = h->firsthttp;
984 				else if (test == ldaptest) walk = h->firstldap;
985 				else for (walk = test->items; (walk && (walk->host != h)); walk = walk->next) ;
986 
987 				if (walk) {
988 					walk->downcount = downcount;
989 					walk->downstart = downstart;
990 				}
991 			}
992 		}
993 	}
994 
995 	fclose(statusfd);
996 }
997 
save_test_status(service_t * test)998 void save_test_status(service_t *test)
999 {
1000 	FILE *statusfd;
1001 	char statusfn[PATH_MAX];
1002 	testitem_t *t;
1003 	int didany = 0;
1004 
1005 	snprintf(statusfn, sizeof(statusfn), "%s/%s.%s.status", xgetenv("XYMONTMP"), test->testname, location);
1006 	statusfd = fopen(statusfn, "w");
1007 	if (statusfd == NULL) return;
1008 
1009 	for (t=test->items; (t); t = t->next) {
1010 		if (t->downcount) {
1011 			fprintf(statusfd, "%s %d %u\n", t->host->hostname, t->downcount, (unsigned int)t->downstart);
1012 			didany = 1;
1013 			t->host->repeattest = ((getcurrenttime(NULL) - t->downstart) < frequenttestlimit);
1014 		}
1015 	}
1016 
1017 	fclose(statusfd);
1018 	if (!didany) unlink(statusfn);
1019 }
1020 
1021 
save_frequenttestlist(int argc,char * argv[])1022 void save_frequenttestlist(int argc, char *argv[])
1023 {
1024 	FILE *fd;
1025 	char fn[PATH_MAX];
1026 	xtreePos_t handle;
1027 	testedhost_t *h;
1028 	int didany = 0;
1029 	int i;
1030 
1031 	snprintf(fn, sizeof(fn), "%s/frequenttests.%s", xgetenv("XYMONTMP"), location);
1032 	fd = fopen(fn, "w");
1033 	if (fd == NULL) return;
1034 
1035 	for (i=1; (i<argc); i++) {
1036 		if (!argnmatch(argv[i], "--report")) fprintf(fd, "%s ", argv[i]);
1037 	}
1038 	for (handle = xtreeFirst(testhosttree); (handle != xtreeEnd(testhosttree)); handle = xtreeNext(testhosttree, handle)) {
1039 		h = (testedhost_t *)xtreeData(testhosttree, handle);
1040 		if (h->repeattest) {
1041 			fprintf(fd, "%s ", h->hostname);
1042 			didany = 1;
1043 		}
1044 	}
1045 
1046 	fclose(fd);
1047 	if (!didany) unlink(fn);
1048 }
1049 
1050 
run_nslookup_service(service_t * service)1051 void run_nslookup_service(service_t *service)
1052 {
1053 	testitem_t	*t;
1054 	char		*lookup;
1055 
1056 	for (t=service->items; (t); t = t->next) {
1057 		if (!t->host->dnserror) {
1058 			if (t->testspec && (lookup = strchr(t->testspec, '='))) {
1059 				lookup++;
1060 			}
1061 			else {
1062 				lookup = t->host->hostname;
1063 			}
1064 
1065 			t->open = (dns_test_server(ip_to_test(t->host), lookup, t->banner) == 0);
1066 		}
1067 	}
1068 }
1069 
run_ntp_service(service_t * service)1070 void run_ntp_service(service_t *service)
1071 {
1072 	testitem_t	*t;
1073 	char		cmd[PATH_MAX+1024];
1074 	char		*p;
1075 	char		cmdpath[PATH_MAX];
1076 	int		use_sntp = 0;
1077 
1078 	p = getenv("SNTP");	/* Plain "getenv" as we want to know if it's unset */
1079 	use_sntp = (p != NULL);
1080 
1081 	strncpy(cmdpath, (use_sntp ? xgetenv("SNTP") : xgetenv("NTPDATE")), sizeof(cmdpath));
1082 
1083 	for (t=service->items; (t); t = t->next) {
1084 		/* Do not run NTP test if host does not resolve in DNS or is down */
1085 		if (!t->host->dnserror && !t->host->pingerror) {
1086 			if (use_sntp) {
1087 				snprintf(cmd, sizeof(cmd), "%s %s -d %d %s 2>&1", cmdpath, xgetenv("SNTPOPTS"), extcmdtimeout-1, ip_to_test(t->host));
1088 			}
1089 			else {
1090 				snprintf(cmd, sizeof(cmd), "%s %s %s 2>&1", cmdpath, xgetenv("NTPDATEOPTS"), ip_to_test(t->host));
1091 			}
1092 
1093 			t->open = (run_command(cmd, "no server suitable for synchronization", t->banner, 1, extcmdtimeout) == 0);
1094 		}
1095 	}
1096 }
1097 
1098 
run_rpcinfo_service(service_t * service)1099 void run_rpcinfo_service(service_t *service)
1100 {
1101 	testitem_t	*t;
1102 	char		cmd[PATH_MAX+1024];
1103 	char		*p;
1104 	char		cmdpath[PATH_MAX];
1105 
1106 	p = xgetenv("RPCINFO");
1107 	strncpy(cmdpath, (p ? p : "rpcinfo"), sizeof(cmdpath));
1108 	for (t=service->items; (t); t = t->next) {
1109 		/* Do not run RPCINFO test if host does not resolve in DNS or is down */
1110 		if (!t->host->dnserror && (t->host->downcount == 0) && !t->host->pingerror) {
1111 			snprintf(cmd, sizeof(cmd), "%s -p %s 2>&1", cmdpath, ip_to_test(t->host));
1112 			t->open = (run_command(cmd, NULL, t->banner, 1, extcmdtimeout) == 0);
1113 		}
1114 	}
1115 }
1116 
1117 
start_ping_service(service_t * service)1118 int start_ping_service(service_t *service)
1119 {
1120 	testitem_t *t;
1121 	char *cmd;
1122 	char **cmdargs;
1123 	int pfd[2];
1124 	int i;
1125 	void *iptree;
1126 	xtreePos_t handle;
1127 
1128 	/* We build a tree of the IP's to test, so we only test each IP once */
1129 	iptree = xtreeNew(strcmp);
1130 	for (t=service->items; (t); t = t->next) {
1131 		char *rec;
1132 		char ip[IP_ADDR_STRLEN+1];
1133 
1134 		if (t->host->dnserror || t->host->noping) continue;
1135 
1136 		strncpy(ip, ip_to_test(t->host), sizeof(ip));
1137 		handle = xtreeFind(iptree, ip);
1138 		if (handle == xtreeEnd(iptree)) {
1139 			rec = strdup(ip);
1140 			xtreeAdd(iptree, rec, rec);
1141 		}
1142 
1143 		if (t->host->extrapings) {
1144 			ipping_t *walk;
1145 
1146 			for (walk = t->host->extrapings->iplist; (walk); walk = walk->next) {
1147 				handle = xtreeFind(iptree, walk->ip);
1148 				if (handle == xtreeEnd(iptree)) {
1149 					rec = strdup(walk->ip);
1150 					xtreeAdd(iptree, rec, rec);
1151 				}
1152 			}
1153 		}
1154 	}
1155 
1156 	/*
1157 	 * The idea here is to run ping in a separate process, in parallel
1158 	 * with some other time-consuming task (the TCP network tests).
1159 	 * We cannot use the simple "popen()/pclose()" interface, because
1160 	 *   a) ping doesn't start the tests until EOF is reached on stdin
1161 	 *   b) EOF on stdin happens with pclose(), but it will also wait
1162 	 *      for the process to finish.
1163 	 *
1164 	 * Therefore this slightly more complex solution, which in essence
1165 	 * forks a new process running "xymonping 2>&1 1>$XYMONTMP/ping.$$"
1166 	 * The output is then picked up by the finish_ping_service().
1167 	 */
1168 
1169 	pingcount = 0;
1170 	pingpids = calloc(pingchildcount, sizeof(pid_t));
1171 	SBUF_MALLOC(pingcmd, strlen(xgetenv("FPING")) + strlen(xgetenv("FPINGOPTS")) + 2);
1172 	snprintf(pingcmd, pingcmd_buflen, "%s %s", xgetenv("FPING"), xgetenv("FPINGOPTS"));
1173 
1174 	snprintf(pinglog, sizeof(pinglog), "%s/ping-stdout.%lu", xgetenv("XYMONTMP"), (unsigned long)getpid());
1175 	snprintf(pingerrlog, sizeof(pingerrlog), "%s/ping-stderr.%lu", xgetenv("XYMONTMP"), (unsigned long)getpid());
1176 
1177 	/* Setup command line and arguments */
1178 	cmdargs = setup_commandargs(pingcmd, &cmd);
1179 
1180 	for (i=0; (i < pingchildcount); i++) {
1181 		/* Get a pipe FD */
1182 		if (pipe(pfd) == -1) {
1183 			errprintf("Could not create pipe for xymonping\n");
1184 			return -1;
1185 		}
1186 
1187 		/* Now fork off the ping child-process */
1188 		pingpids[i] = fork();
1189 
1190 		if (pingpids[i] < 0) {
1191 			errprintf("Could not fork() the ping child\n");
1192 			return -1;
1193 		}
1194 		else if (pingpids[i] == 0) {
1195 			/*
1196 			 * child must have
1197 			 *  - stdin fed from the parent
1198 			 *  - stdout going to a file
1199 			 *  - stderr going to another file. This is important, as
1200 			 *    putting it together with stdout will wreak havoc when
1201 			 *    we start parsing the output later on. We could just
1202 			 *    dump it to /dev/null, but it might be useful to see
1203 			 *    what went wrong.
1204 			 */
1205 			int outfile, errfile;
1206 
1207 			snprintf(pinglog+strlen(pinglog), (sizeof(pinglog) - strlen(pinglog)), ".%02d", i);
1208 			snprintf(pingerrlog+strlen(pingerrlog), (sizeof(pingerrlog) - strlen(pingerrlog)), ".%02d", i);
1209 
1210 			outfile = open(pinglog, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
1211 			if (outfile == -1) errprintf("Cannot create file %s : %s\n", pinglog, strerror(errno));
1212 			errfile = open(pingerrlog, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR);
1213 			if (errfile == -1) errprintf("Cannot create file %s : %s\n", pingerrlog, strerror(errno));
1214 
1215 			if ((outfile == -1) || (errfile == -1)) {
1216 				/* Ouch - cannot create our output files. Abort. */
1217 				exit(98);
1218 			}
1219 
1220 			dup2(pfd[0], STDIN_FILENO);
1221 			dup2(outfile, STDOUT_FILENO);
1222 			dup2(errfile, STDERR_FILENO);
1223 			close(pfd[0]); close(pfd[1]); close(outfile); close(errfile);
1224 
1225 			execvp(cmd, cmdargs);
1226 
1227 			/* Should never go here ... just kill the child */
1228 			fprintf(stderr, "Command '%s' failed: %s\n", cmd, strerror(errno));
1229 			exit(99);
1230 		}
1231 		else {
1232 			/* parent */
1233 			char ip[IP_ADDR_STRLEN+1];	/* Must have room for the \n at the end also */
1234 			int hnum, feederror = 0;
1235 
1236 			close(pfd[0]);
1237 
1238 			/* Feed the IP's to test to the child */
1239 			for (handle = xtreeFirst(iptree), hnum = 0; ((feederror == 0) && (handle != xtreeEnd(iptree))); handle = xtreeNext(iptree, handle), hnum++) {
1240 				if ((hnum % pingchildcount) != i) continue;
1241 
1242 				snprintf(ip, sizeof(ip), "%s\n", xtreeKey(iptree, handle));
1243 				if (write(pfd[1], ip, strlen(ip)) != strlen(ip)) {
1244 					errprintf("Cannot feed IP to ping tool: %s\n", strerror(errno));
1245 					feederror = 1;
1246 					continue;
1247 				}
1248 				pingcount++;
1249 			}
1250 
1251 			close(pfd[1]);	/* This is when ping starts doing tests */
1252 		}
1253 	}
1254 
1255 	for (handle = xtreeFirst(iptree); handle != xtreeEnd(iptree); handle = xtreeNext(iptree, handle)) {
1256 		char *rec = xtreeKey(iptree, handle);
1257 		xfree(rec);
1258 	}
1259 	xtreeDestroy(iptree);
1260 
1261 	return 0;
1262 }
1263 
1264 
finish_ping_service(service_t * service)1265 int finish_ping_service(service_t *service)
1266 {
1267 	testitem_t	*t;
1268 	FILE		*logfd;
1269 	char 		*p;
1270 	char		l[MAX_LINE_LEN];
1271 	char		pingip[MAX_LINE_LEN];
1272 	int		ip1, ip2, ip3, ip4;
1273 	int		pingstatus, failed = 0, i;
1274 	char		fn[PATH_MAX];
1275 
1276 	/* Load status of previously failed tests */
1277 	load_ping_status();
1278 
1279 	/*
1280 	 * Wait for the ping child to finish.
1281 	 * If we're lucky, it will be done already since it has run
1282 	 * while we were doing tcp tests.
1283 	 */
1284 	for (i = 0; (i < pingchildcount); i++) {
1285 		waitpid(pingpids[i], &pingstatus, 0);
1286 		switch (WEXITSTATUS(pingstatus)) {
1287 			case 0: /* All hosts reachable */
1288 			case 1: /* Some hosts unreachable */
1289 			case 2: /* Some IP's not found (should not happen) */
1290 				break;
1291 
1292 			case 3: /* Bad command-line args, or not suid-root */
1293 				failed = 1;
1294 				errprintf("Execution of '%s' failed - program not suid root?\n", pingcmd);
1295 				break;
1296 
1297 			case 98:
1298 				failed = 1;
1299 				errprintf("xymonping child could not create outputfiles in %s\n", xgetenv("XYMONTMP"));
1300 				break;
1301 
1302 			case 99:
1303 				failed = 1;
1304 				errprintf("Could not run the command '%s' (exec failed)\n", pingcmd);
1305 				break;
1306 
1307 			default:
1308 				failed = 1;
1309 				errprintf("Execution of '%s' failed with error-code %d\n",
1310 						pingcmd, WEXITSTATUS(pingstatus));
1311 		}
1312 
1313 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
1314 		/* Ignore gcc warnings about truncating filenames when adding a number */
1315 		#pragma GCC diagnostic push
1316 		#pragma GCC diagnostic ignored "-Wformat-truncation"
1317 #endif  // __GNUC__
1318 
1319 		/* Open the new ping result file */
1320 		snprintf(fn, sizeof(fn), "%s.%02d", pinglog, i);
1321 		logfd = fopen(fn, "r");
1322 		if (logfd == NULL) {
1323 			failed = 1;
1324 			errprintf("Cannot open ping output file %s\n", fn);
1325 		}
1326 		if (!debug) unlink(fn);	/* We have an open filehandle, so it's ok to delete the file now */
1327 
1328 		/* Copy error messages to the Xymon logfile */
1329 		snprintf(fn, sizeof(fn), "%s.%02d", pingerrlog, i);
1330 		if (failed) {
1331 			FILE *errfd;
1332 			char buf[1024];
1333 
1334 			errfd = fopen(fn, "r");
1335 			if (errfd && fgets(buf, sizeof(buf), errfd)) {
1336 				errprintf("%s", buf);
1337 			}
1338 			if (errfd) fclose(errfd);
1339 		}
1340 		if (!debug) unlink(fn);
1341 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
1342 		#pragma GCC diagnostic pop
1343 #endif  // __GNUC__
1344 
1345 		if (failed) {
1346 			/* Flag all ping tests as "undecided" */
1347 			bigfailure = 1;
1348 			for (t=service->items; (t); t = t->next) t->open = -1;
1349 		}
1350 		else {
1351 			/* The test did run, and we have a result-file. Look at it. */
1352 			while (fgets(l, sizeof(l), logfd)) {
1353 				p = strchr(l, '\n'); if (p) *p = '\0';
1354 				if (sscanf(l, "%d.%d.%d.%d ", &ip1, &ip2, &ip3, &ip4) == 4) {
1355 
1356 					snprintf(pingip, sizeof(pingip), "%d.%d.%d.%d", ip1, ip2, ip3, ip4);
1357 
1358 					/*
1359 					 * Need to loop through all testitems - there may be multiple entries for
1360 					 * the same IP-address.
1361 					 */
1362 					for (t=service->items; (t); t = t->next) {
1363 						if (strcmp(t->host->ip, pingip) == 0) {
1364 							if (t->open) dbgprintf("More than one ping result for %s\n", pingip);
1365 							t->open = (strstr(l, "is alive") != NULL);
1366 							t->banner = dupstrbuffer(l);
1367 						}
1368 
1369 						if (t->host->extrapings) {
1370 							ipping_t *walk;
1371 							for (walk = t->host->extrapings->iplist; (walk); walk = walk->next) {
1372 								if (strcmp(walk->ip, pingip) == 0) {
1373 									if (t->open) dbgprintf("More than one ping result for %s\n", pingip);
1374 									walk->open = (strstr(l, "is alive") != NULL);
1375 									walk->banner = dupstrbuffer(l);
1376 								}
1377 							}
1378 						}
1379 					}
1380 				}
1381 			}
1382 		}
1383 
1384 		if (logfd) fclose(logfd);
1385 	}
1386 
1387 	/*
1388 	 * Handle the router dependency stuff. I.e. for all hosts
1389 	 * where the ping test failed, go through the list of router
1390 	 * dependencies and if one of the dependent hosts also has
1391 	 * a failed ping test, point the dependency there.
1392 	 */
1393 	for (t=service->items; (t); t = t->next) {
1394 		if (!t->open && t->host->routerdeps) {
1395 			testitem_t *router;
1396 
1397 			strncpy(l, t->host->routerdeps, sizeof(l));
1398 			p = strtok(l, ",");
1399 			while (p && (t->host->deprouterdown == NULL)) {
1400 				for (router=service->items;
1401 					(router && (strcmp(p, router->host->hostname) != 0));
1402 					router = router->next) ;
1403 
1404 				if (router && !router->open) t->host->deprouterdown = router->host;
1405 
1406 				p = strtok(NULL, ",");
1407 			}
1408 		}
1409 	}
1410 
1411 	return 0;
1412 }
1413 
1414 
decide_color(service_t * service,char * svcname,testitem_t * test,int failgoesclear,char * cause)1415 int decide_color(service_t *service, char *svcname, testitem_t *test, int failgoesclear, char *cause)
1416 {
1417 	int color = COL_GREEN;
1418 	int countasdown = 0;
1419 	char *deptest = NULL;
1420 
1421 	*cause = '\0';
1422 	if (service == pingtest) {
1423 		/*
1424 		 * "noconn" is handled elsewhere.
1425 		 * "noping" always sends back a status "clear".
1426 		 * If DNS error, return red and count as down.
1427 		 */
1428 		if (test->open == -1) {
1429 			/* Failed to run the ping utility. */
1430 			strcpy(cause, "Xymon system failure");
1431 			return COL_CLEAR;
1432 		}
1433 		else if (test->host->noping) {
1434 			/* Ping test disabled - go "clear". End of story. */
1435 			strcpy(cause, "Ping test disabled (noping)");
1436 			return COL_CLEAR;
1437 		}
1438 		else if (test->host->dnserror) {
1439 			strcpy(cause, "DNS lookup failure");
1440 			color = COL_RED; countasdown = 1;
1441 		}
1442 		else {
1443 			if (test->host->extrapings == NULL) {
1444 				/* Red if (open=0, reverse=0) or (open=1, reverse=1) */
1445 				if ((test->open + test->reverse) != 1) {
1446 					sprintf(cause, "Host %s respond to ping", (test->open ? "does" : "does not"));
1447 					color = COL_RED; countasdown = 1;
1448 				}
1449 			}
1450 			else {
1451 				/* Host with many pings */
1452 				int totalcount = 1;
1453 				int okcount = test->open;
1454 				ipping_t *walk;
1455 
1456 				for (walk = test->host->extrapings->iplist; (walk); walk = walk->next) {
1457 					if (walk->open) okcount++;
1458 					totalcount++;
1459 				}
1460 
1461 				switch (test->host->extrapings->matchtype) {
1462 				  case MULTIPING_BEST:
1463 					  if (okcount == 0) {
1464 						  color = COL_RED;
1465 						  countasdown = 1;
1466 						  sprintf(cause, "Host does not respond to ping on any of %d IP's",
1467 							  totalcount);
1468 					  }
1469 					  break;
1470 				  case MULTIPING_WORST:
1471 					  if (okcount < totalcount) {
1472 						  color = COL_RED;
1473 						  countasdown = 1;
1474 						  sprintf(cause, "Host responds to ping on %d of %d IP's",
1475 							  okcount, totalcount);
1476 					  }
1477 					  break;
1478 				}
1479 			}
1480 		}
1481 
1482 		/* Handle the "route" tag dependencies. */
1483 		if ((color == COL_RED) && test->host->deprouterdown) {
1484 			char *routertext;
1485 
1486 			routertext = test->host->deprouterdown->hosttype;
1487 			if (routertext == NULL) routertext = xgetenv("XYMONROUTERTEXT");
1488 			if (routertext == NULL) routertext = "router";
1489 
1490 			strcat(cause, "\nIntermediate ");
1491 			strcat(cause, routertext);
1492 			strcat(cause, " down ");
1493 			color = COL_YELLOW;
1494 		}
1495 
1496 		/* Set pingerror for later use by NTP and RPCINFO tests */
1497 		test->host->pingerror = (color == COL_RED ? 1 : 0);
1498 
1499 		/* Handle "badconn" */
1500 		if ((color == COL_RED) && (test->host->downcount < test->host->badconn[2])) {
1501 			if      (test->host->downcount >= test->host->badconn[1]) color = COL_YELLOW;
1502 			else if (test->host->downcount >= test->host->badconn[0]) color = COL_CLEAR;
1503 			else                                                      color = COL_GREEN;
1504 		}
1505 
1506 		/* Run traceroute , but not on dialup or reverse-test hosts */
1507 		if ((color == COL_RED) && test->host->dotrace && !test->host->dialup && !test->reverse && !test->dialup) {
1508 			char cmd[PATH_MAX];
1509 
1510 			if (getenv("TRACEROUTEOPTS")) {
1511 				/* post 4.3.21 */
1512 				snprintf(cmd, sizeof(cmd), "%s %s %s 2>&1", xgetenv("TRACEROUTE"), xgetenv("TRACEROUTEOPTS"), test->host->ip);
1513 			}
1514 			else {
1515 				snprintf(cmd, sizeof(cmd), "%s %s 2>&1", xgetenv("TRACEROUTE"), test->host->ip);
1516 			}
1517 			test->host->traceroute = newstrbuffer(0);
1518 			run_command(cmd, NULL, test->host->traceroute, 0, extcmdtimeout);
1519 		}
1520 	}
1521 	else {
1522 		/* TCP test */
1523 		if (test->host->dnserror) {
1524 			strcpy(cause, "DNS lookup failure");
1525 			color = COL_RED; countasdown = 1;
1526 		}
1527 		else {
1528 			if (test->reverse) {
1529 				/*
1530 				 * Reverse tests go RED when open.
1531 				 * If not open, they may go CLEAR if the ping test failed
1532 				 */
1533 
1534 				if (test->open) {
1535 					strcpy(cause, "Service responds when it should not");
1536 					color = COL_RED; countasdown = 1;
1537 				}
1538 				else if (failgoesclear && (test->host->downcount != 0) && !test->alwaystrue) {
1539 					strcpy(cause, "Host appears to be down");
1540 					color = COL_CLEAR; countasdown = 0;
1541 				}
1542 			}
1543 			else {
1544 				if (!test->open) {
1545 					if (failgoesclear && (test->host->downcount != 0) && !test->alwaystrue) {
1546 						strcpy(cause, "Host appears to be down");
1547 						color = COL_CLEAR; countasdown = 0;
1548 					}
1549 					else {
1550 						tcptest_t *tcptest = (tcptest_t *)test->privdata;
1551 
1552 						strcpy(cause, "Service unavailable");
1553 						if (tcptest) {
1554 							switch (tcptest->errcode) {
1555 							  case CONTEST_ETIMEOUT:
1556 								strcat(cause, " (connect timeout)");
1557 								break;
1558 							  case CONTEST_ENOCONN :
1559 								strcat(cause, " (");
1560 								strcat(cause, strerror(tcptest->connres));
1561 								strcat(cause, ")");
1562 								break;
1563 							  case CONTEST_EDNS    :
1564 								strcat(cause, " (DNS error)");
1565 								break;
1566 							  case CONTEST_EIO     :
1567 								strcat(cause, " (I/O error)");
1568 								break;
1569 							  case CONTEST_ESSL    :
1570 								strcat(cause, " (SSL error)");
1571 								break;
1572 							}
1573 						}
1574 						color = COL_RED; countasdown = 1;
1575 					}
1576 				}
1577 				else {
1578 					tcptest_t *tcptest = (tcptest_t *)test->privdata;
1579 
1580 					/* Check if we got the expected data */
1581 					if (checktcpresponse && (service->toolid == TOOL_CONTEST) && !tcp_got_expected((tcptest_t *)test->privdata)) {
1582 						strcpy(cause, "Unexpected service response");
1583 						color = respcheck_color; countasdown = 1;
1584 					}
1585 
1586 					/* Check that other transport issues didn't occur (like SSL) */
1587 					if (tcptest && (tcptest->errcode != CONTEST_ENOERROR)) {
1588 						switch (tcptest->errcode) {
1589 						  case CONTEST_ETIMEOUT    :
1590 							strcpy(cause, "Service listening but unavailable (connect timeout)");
1591 							color = COL_RED; countasdown = 1;
1592 							break;
1593 						  case CONTEST_ENOCONN    :
1594 							strcpy(cause, "Service listening but unavailable (");
1595 							strcat(cause, strerror(tcptest->connres));
1596 							strcat(cause, ")");
1597 							color = COL_RED; countasdown = 1;
1598 							break;
1599 						  case CONTEST_EDNS    :
1600 							strcpy(cause, "Service listening but unavailable (DNS error)");
1601 							color = COL_RED; countasdown = 1;
1602 							break;
1603 						  case CONTEST_EIO    :
1604 							strcpy(cause, "Service listening but unavailable (I/O error)");
1605 							color = COL_RED; countasdown = 1;
1606 							break;
1607 						  case CONTEST_ESSL    :
1608 							strcpy(cause, "Service listening but unavailable (SSL error)");
1609 							color = COL_RED; countasdown = 1;
1610 							break;
1611 						  default		:
1612 							/* Should not get here */
1613 							errprintf("-> TCPtest error %d seen on open connection for %s.%s\n", tcptest->errcode, test->host->hostname, test->service->testname);
1614 							color = COL_YELLOW;
1615 							break;
1616 						}
1617 						// color = COL_RED; countasdown = 1;
1618 					}
1619 
1620 				}
1621 			}
1622 		}
1623 
1624 		/* Handle test dependencies */
1625 		if ( failgoesclear && (color == COL_RED) && !test->alwaystrue && (deptest = deptest_failed(test->host, test->service->testname)) ) {
1626 			strcpy(cause, deptest);
1627 			color = COL_CLEAR;
1628 		}
1629 
1630 		/* Handle the "badtest" stuff for other tests */
1631 		if ((color == COL_RED) && (test->downcount < test->badtest[2])) {
1632 			if      (test->downcount >= test->badtest[1]) color = COL_YELLOW;
1633 			else if (test->downcount >= test->badtest[0]) color = COL_CLEAR;
1634 			else                                          color = COL_GREEN;
1635 		}
1636 	}
1637 
1638 
1639 	/* Dialup hosts and dialup tests report red as clear */
1640 	if ( ((color == COL_RED) || (color == COL_YELLOW)) && (test->host->dialup || test->dialup) && !test->reverse) {
1641 		strcat(cause, "\nDialup host or service");
1642 		color = COL_CLEAR; countasdown = 0;
1643 	}
1644 
1645 	/* If a NOPAGENET service, downgrade RED to YELLOW */
1646 	if (color == COL_RED) {
1647 		SBUF_DEFINE(nopagename);
1648 
1649 		/* Check if this service is a NOPAGENET service. */
1650 		SBUF_MALLOC(nopagename, strlen(svcname)+3);
1651 		snprintf(nopagename, nopagename_buflen, ",%s,", svcname);
1652 		if (strstr(nonetpage, nopagename) != NULL) color = COL_YELLOW;
1653 		xfree(nopagename);
1654 	}
1655 
1656 	if (service == pingtest) {
1657 		if (countasdown) {
1658 			test->host->downcount++;
1659 			if (test->host->downcount == 1) test->host->downstart = getcurrenttime(NULL);
1660 		}
1661 		else test->host->downcount = 0;
1662 	}
1663 	else {
1664 		if (countasdown) {
1665 			test->downcount++;
1666 			if (test->downcount == 1) test->downstart = getcurrenttime(NULL);
1667 		}
1668 		else test->downcount = 0;
1669 	}
1670 	return color;
1671 }
1672 
1673 
send_results(service_t * service,int failgoesclear)1674 void send_results(service_t *service, int failgoesclear)
1675 {
1676 	testitem_t	*t;
1677 	int		color;
1678 	char		msgline[4096];
1679 	char		msgtext[4096];
1680 	char		causetext[1024];
1681 	char		*svcname;
1682 
1683 	svcname = strdup(service->testname);
1684 	if (service->namelen) svcname[service->namelen] = '\0';
1685 
1686 	dbgprintf("Sending results for service %s\n", svcname);
1687 
1688 	for (t=service->items; (t); t = t->next) {
1689 		char flags[10];
1690 		int i;
1691 
1692 		if (t->internal) continue;
1693 
1694 		i = 0;
1695 		flags[i++] = (t->open ? 'O' : 'o');
1696 		flags[i++] = (t->reverse ? 'R' : 'r');
1697 		flags[i++] = ((t->dialup || t->host->dialup) ? 'D' : 'd');
1698 		flags[i++] = (t->alwaystrue ? 'A' : 'a');
1699 		flags[i++] = (t->silenttest ? 'S' : 's');
1700 		flags[i++] = (t->host->testip ? 'T' : 't');
1701 		flags[i++] = (t->host->dodns ? 'L' : 'l');
1702 		flags[i++] = (t->host->dnserror ? 'E' : 'e');
1703 		flags[i++] = '\0';
1704 
1705 		color = decide_color(service, svcname, t, failgoesclear, causetext);
1706 
1707 		init_status(color);
1708 		if (dosendflags)
1709 			snprintf(msgline, sizeof(msgline), "status+%d %s.%s %s <!-- [flags:%s] --> %s %s %s ",
1710 				validity, commafy(t->host->hostname), svcname, colorname(color),
1711 				flags, timestamp,
1712 				svcname, ( ((color == COL_RED) || (color == COL_YELLOW)) ? "NOT ok" : "ok"));
1713 		else
1714 			snprintf(msgline, sizeof(msgline), "status %s.%s %s %s %s %s ",
1715 				commafy(t->host->hostname), svcname, colorname(color),
1716 				timestamp,
1717 				svcname, ( ((color == COL_RED) || (color == COL_YELLOW)) ? "NOT ok" : "ok"));
1718 
1719 		if (t->host->dnserror) {
1720 			strncat(msgline, ": DNS lookup failed", (sizeof(msgline) - strlen(msgline)));
1721 			snprintf(msgtext, sizeof(msgtext), "\nUnable to resolve hostname %s\n\n", t->host->hostname);
1722 		}
1723 		else {
1724 			snprintf(msgtext, sizeof(msgtext), "\nService %s on %s is ", svcname, t->host->hostname);
1725 			switch (color) {
1726 			  case COL_GREEN:
1727 				  strncat(msgtext, "OK ", (sizeof(msgtext) - strlen(msgtext)));
1728 				  strncat(msgtext, (t->reverse ? "(down)" : "(up)"), (sizeof(msgtext) - strlen(msgtext)));
1729 				  strncat(msgtext, "\n", (sizeof(msgtext) - strlen(msgtext)));
1730 				  break;
1731 
1732 			  case COL_RED:
1733 			  case COL_YELLOW:
1734 				  if ((service == pingtest) && t->host->deprouterdown) {
1735 					char *routertext;
1736 
1737 					routertext = t->host->deprouterdown->hosttype;
1738 					if (routertext == NULL) routertext = xgetenv("XYMONROUTERTEXT");
1739 					if (routertext == NULL) routertext = "router";
1740 
1741 					strncat(msgline, ": Intermediate ", (sizeof(msgline) - strlen(msgline)));
1742 					strncat(msgline, routertext, (sizeof(msgline) - strlen(msgline)));
1743 					strncat(msgline, " down", (sizeof(msgline) - strlen(msgline)));
1744 
1745 					snprintf(msgtext+strlen(msgtext), (sizeof(msgtext) - strlen(msgtext)),
1746 						"%s.\nThe %s %s (IP:%s) is not reachable, causing this host to be unreachable.\n",
1747 						failtext, routertext,
1748 						((testedhost_t *)t->host->deprouterdown)->hostname,
1749 						((testedhost_t *)t->host->deprouterdown)->ip);
1750 				  }
1751 				  else {
1752 					snprintf(msgtext+strlen(msgtext), (sizeof(msgtext) - strlen(msgtext)), "%s : %s\n", failtext, causetext);
1753 				  }
1754 				  break;
1755 
1756 			  case COL_CLEAR:
1757 				  strncat(msgtext, "OK\n", (sizeof(msgtext) - strlen(msgtext)));
1758 				  if (service == pingtest) {
1759 					  if (t->host->deprouterdown) {
1760 						char *routertext;
1761 
1762 						routertext = t->host->deprouterdown->hosttype;
1763 						if (routertext == NULL) routertext = xgetenv("XYMONROUTERTEXT");
1764 						if (routertext == NULL) routertext = "router";
1765 
1766 						strncat(msgline, ": Intermediate ", (sizeof(msgline) - strlen(msgline)));
1767 						strncat(msgline, routertext, (sizeof(msgline) - strlen(msgline)));
1768 						strncat(msgline, " down", (sizeof(msgline) - strlen(msgline)));
1769 
1770 						strncat(msgtext, "\nThe ", (sizeof(msgtext) - strlen(msgtext)));
1771 						strncat(msgtext, routertext, (sizeof(msgtext) - strlen(msgtext))); strncat(msgtext, " ", (sizeof(msgtext) - strlen(msgtext)));
1772 						strncat(msgtext, ((testedhost_t *)t->host->deprouterdown)->hostname, (sizeof(msgtext) - strlen(msgtext)));
1773 						strncat(msgtext, " (IP:", (sizeof(msgtext) - strlen(msgtext)));
1774 						strncat(msgtext, ((testedhost_t *)t->host->deprouterdown)->ip, (sizeof(msgtext) - strlen(msgtext)));
1775 						strncat(msgtext, ") is not reachable, causing this host to be unreachable.\n", (sizeof(msgtext) - strlen(msgtext)));
1776 					  }
1777 					  else if (t->host->noping) {
1778 						  strncat(msgline, ": Disabled", (sizeof(msgline) - strlen(msgline)));
1779 						  strncat(msgtext, "Ping check disabled (noping)\n", (sizeof(msgtext) - strlen(msgtext)));
1780 					  }
1781 					  else if (t->host->dialup) {
1782 						  strncat(msgline, ": Disabled (dialup host)", (sizeof(msgline) - strlen(msgline)));
1783 						  strncat(msgtext, "Dialup host\n", (sizeof(msgtext) - strlen(msgtext)));
1784 					  }
1785 					  else if (t->open == -1) {
1786 						  strncat(msgline, ": System failure of the ping test", (sizeof(msgline) - strlen(msgline)));
1787 						  strncat(msgtext, "Xymon system error\n", (sizeof(msgtext) - strlen(msgtext)));
1788 					  }
1789 					  /* "clear" due to badconn: no extra text */
1790 				  }
1791 				  else {
1792 					  /* Non-ping test clear: Dialup test or failed ping */
1793 					  strncat(msgline, ": Ping failed, or dialup host/service", (sizeof(msgline) - strlen(msgline)));
1794 					  strncat(msgtext, "Dialup host/service, or test depends on another failed test\n", (sizeof(msgtext) - strlen(msgtext)));
1795 					  strncat(msgtext, causetext, (sizeof(msgtext) - strlen(msgtext)));
1796 				  }
1797 				  break;
1798 			}
1799 			strncat(msgtext, "\n", (sizeof(msgtext) - strlen(msgtext)));
1800 		}
1801 		strncat(msgline, "\n", (sizeof(msgline) - strlen(msgline)));
1802 		addtostatus(msgline);
1803 		addtostatus(msgtext);
1804 
1805 		if ((service == pingtest) && t->host->downcount) {
1806 			snprintf(msgtext, sizeof(msgtext), "\nSystem unreachable for %d poll periods (%u seconds)\n",
1807 				t->host->downcount, (unsigned int)(getcurrenttime(NULL) - t->host->downstart));
1808 			addtostatus(msgtext);
1809 		}
1810 
1811 		if (STRBUFLEN(t->banner)) {
1812 			if (service == pingtest) {
1813 				snprintf(msgtext, sizeof(msgtext), "\n&%s %s\n", colorname(t->open ? COL_GREEN : COL_RED), STRBUF(t->banner));
1814 				addtostatus(msgtext);
1815 				if (t->host->extrapings) {
1816 					ipping_t *walk;
1817 					for (walk = t->host->extrapings->iplist; (walk); walk = walk->next) {
1818 						if (STRBUFLEN(walk->banner)) {
1819 							snprintf(msgtext, sizeof(msgtext), "&%s %s\n",
1820 								colorname(walk->open ? COL_GREEN : COL_RED), STRBUF(walk->banner));
1821 							addtostatus(msgtext);
1822 						}
1823 					}
1824 				}
1825 			}
1826 			else {
1827 				addtostatus("\n"); addtostrstatus(t->banner); addtostatus("\n");
1828 			}
1829 		}
1830 
1831 		if ((service == pingtest) && t->host->traceroute && (STRBUFLEN(t->host->traceroute) > 0)) {
1832 			addtostatus("Traceroute results:\n");
1833 			addtostrstatus(t->host->traceroute);
1834 			addtostatus("\n");
1835 		}
1836 
1837 		if (t->duration.tv_sec != -1) {
1838 			snprintf(msgtext, sizeof(msgtext), "\nSeconds: %u.%.9ld\n",
1839 				(unsigned int)t->duration.tv_sec, t->duration.tv_nsec);
1840 			addtostatus(msgtext);
1841 		}
1842 		addtostatus("\n\n");
1843 		finish_status();
1844 	}
1845 }
1846 
1847 
send_rpcinfo_results(service_t * service,int failgoesclear)1848 void send_rpcinfo_results(service_t *service, int failgoesclear)
1849 {
1850 	testitem_t	*t;
1851 	int		color;
1852 	char		msgline[2048];
1853 	char		*msgbuf;
1854 	char		causetext[1024];
1855 
1856 	msgbuf = (char *)malloc(MSGBUFSIZE);
1857 
1858 	for (t=service->items; (t); t = t->next) {
1859 		char *wantedrpcsvcs = NULL;
1860 		char *p;
1861 
1862 		/* First see if the rpcinfo command succeeded */
1863 		*msgbuf = '\0';
1864 
1865 		color = decide_color(service, service->testname, t, failgoesclear, causetext);
1866 		p = strchr(t->testspec, '=');
1867 		if (p) wantedrpcsvcs = strdup(p+1);
1868 
1869 		if ((color == COL_GREEN) && STRBUFLEN(t->banner) && wantedrpcsvcs) {
1870 			char *rpcsvc, *aline;
1871 
1872 			rpcsvc = strtok(wantedrpcsvcs, ",");
1873 			while (rpcsvc) {
1874 				struct rpcent *rpcinfo;
1875 				int  svcfound = 0;
1876 				int  aprogram;
1877 				int  aversion;
1878 				char aprotocol[10];
1879 				int  aport;
1880 
1881 				rpcinfo = getrpcbyname(rpcsvc);
1882 				aline = STRBUF(t->banner);
1883 				while ((!svcfound) && rpcinfo && aline && (*aline != '\0')) {
1884 					p = strchr(aline, '\n');
1885 					if (p) *p = '\0';
1886 
1887 					if (sscanf(aline, "%d %d %s %d", &aprogram, &aversion, aprotocol, &aport) == 4) {
1888 						svcfound = (aprogram == rpcinfo->r_number);
1889 					}
1890 
1891 					aline = p;
1892 					if (p) {
1893 						*p = '\n';
1894 						aline++;
1895 					}
1896 				}
1897 
1898 				if (svcfound) {
1899 					snprintf(msgline, sizeof(msgline), "&%s Service %s (ID: %d) found on port %d\n",
1900 						colorname(COL_GREEN), rpcsvc, rpcinfo->r_number, aport);
1901 				}
1902 				else if (rpcinfo) {
1903 					color = COL_RED;
1904 					snprintf(msgline, sizeof(msgline), "&%s Service %s (ID: %d) NOT found\n",
1905 						colorname(COL_RED), rpcsvc, rpcinfo->r_number);
1906 				}
1907 				else {
1908 					color = COL_RED;
1909 					snprintf(msgline, sizeof(msgline), "&%s Unknown RPC service %s\n",
1910 						colorname(COL_RED), rpcsvc);
1911 				}
1912 				strncat(msgbuf, msgline, (MSGBUFSIZE - strlen(msgbuf)));
1913 
1914 				rpcsvc = strtok(NULL, ",");
1915 			}
1916 		}
1917 
1918 		if (wantedrpcsvcs) xfree(wantedrpcsvcs);
1919 
1920 		init_status(color);
1921 		snprintf(msgline, sizeof(msgline), "status+%d %s.%s %s %s %s %s, %s\n\n",
1922 			validity, commafy(t->host->hostname), service->testname, colorname(color), timestamp,
1923 			service->testname,
1924 			( ((color == COL_RED) || (color == COL_YELLOW)) ? "NOT ok" : "ok"),
1925 			causetext);
1926 		addtostatus(msgline);
1927 
1928 		/* The summary of wanted RPC services */
1929 		addtostatus(msgbuf);
1930 
1931 		/* rpcinfo output */
1932 		if (t->open) {
1933 			if (STRBUFLEN(t->banner)) {
1934 				addtostatus("\n\n");
1935 				addtostrstatus(t->banner);
1936 			}
1937 			else {
1938 				snprintf(msgline, sizeof(msgline), "\n\nNo output from rpcinfo -p %s\n", t->host->ip);
1939 				addtostatus(msgline);
1940 			}
1941 		}
1942 		else {
1943 			addtostatus("\n\nCould not connect to the portmapper service\n");
1944 			if (STRBUFLEN(t->banner)) addtostrstatus(t->banner);
1945 		}
1946 		finish_status();
1947 	}
1948 
1949 	xfree(msgbuf);
1950 }
1951 
1952 
send_sslcert_status(testedhost_t * host)1953 void send_sslcert_status(testedhost_t *host)
1954 {
1955 	int color = -1;
1956 	xtreePos_t handle;
1957 	service_t *s;
1958 	testitem_t *t;
1959 	char msgline[1024];
1960 	strbuffer_t *sslmsg;
1961 	time_t now = getcurrenttime(NULL);
1962 	char *certowner;
1963 
1964 	sslmsg = newstrbuffer(0);
1965 
1966 	for (handle = xtreeFirst(svctree); handle != xtreeEnd(svctree); handle = xtreeNext(svctree, handle)) {
1967 		s = (service_t *)xtreeData(svctree, handle);
1968 		certowner = s->testname;
1969 
1970 		for (t=s->items; (t); t=t->next) {
1971 			if ((t->host == host) && t->certinfo && (t->certexpires > 0)) {
1972 				int sslcolor = COL_GREEN;
1973 				int ciphercolor = COL_GREEN;
1974 				int keycolor = COL_GREEN;
1975 
1976 				if (s == httptest) certowner = ((http_data_t *)t->privdata)->url;
1977 				else if (s == ldaptest) certowner = t->testspec;
1978 
1979 				if (t->certexpires < (now+host->sslwarndays*86400)) sslcolor = COL_YELLOW;
1980 				if (t->certexpires < (now+host->sslalarmdays*86400)) sslcolor = COL_RED;
1981 				if (sslcolor > color) color = sslcolor;
1982 
1983 				if (host->mincipherbits && (t->mincipherbits < host->mincipherbits)) ciphercolor = COL_RED;
1984 				if (ciphercolor > color) color = ciphercolor;
1985 
1986 				if (sslminkeysize > 0) {
1987 					if ((t->certkeysz > 0) && (t->certkeysz < sslminkeysize)) keycolor = COL_YELLOW;
1988 					if (keycolor > color) color = keycolor;
1989 				}
1990 
1991 				if (t->certexpires > now) {
1992 					snprintf(msgline, sizeof(msgline), "\n&%s SSL certificate for %s expires in %u days\n\n",
1993 						colorname(sslcolor), certowner,
1994 						(unsigned int)((t->certexpires - now) / 86400));
1995 				}
1996 				else {
1997 					snprintf(msgline, sizeof(msgline), "\n&%s SSL certificate for %s expired %u days ago\n\n",
1998 						colorname(sslcolor), certowner,
1999 						(unsigned int)((now - t->certexpires) / 86400));
2000 				}
2001 				addtobuffer(sslmsg, msgline);
2002 
2003 				if (host->mincipherbits) {
2004 					snprintf(msgline, sizeof(msgline), "&%s Minimum available SSL encryption is %d bits (should be %d)\n",
2005 						colorname(ciphercolor), t->mincipherbits, host->mincipherbits);
2006 					addtobuffer(sslmsg, msgline);
2007 				}
2008 
2009 				if (keycolor != COL_GREEN) {
2010 					snprintf(msgline, sizeof(msgline), "&%s Certificate public key size is less than %d bits\n", colorname(keycolor), sslminkeysize);
2011 					addtobuffer(sslmsg, msgline);
2012 				}
2013 				addtobuffer(sslmsg, "\n");
2014 
2015 				addtobuffer(sslmsg, t->certinfo);
2016 			}
2017 		}
2018 	}
2019 
2020 	if (color != -1) {
2021 		/* Send off the sslcert status report */
2022 		init_status(color);
2023 		snprintf(msgline, sizeof(msgline), "status+%d %s.%s %s %s\n",
2024 			validity, commafy(host->hostname), ssltestname, colorname(color), timestamp);
2025 		addtostatus(msgline);
2026 		addtostrstatus(sslmsg);
2027 		addtostatus("\n\n");
2028 		finish_status();
2029 	}
2030 
2031 	freestrbuffer(sslmsg);
2032 }
2033 
main(int argc,char * argv[])2034 int main(int argc, char *argv[])
2035 {
2036 	xtreePos_t handle;
2037 	service_t *s;
2038 	testedhost_t *h;
2039 	testitem_t *t;
2040 	int argi;
2041 	int concurrency = 0;
2042 	char *pingcolumn = "";
2043 	char *egocolumn = NULL;
2044 	int failgoesclear = 0;		/* IPTEST_2_CLEAR_ON_FAILED_CONN */
2045 	int dumpdata = 0;
2046 	int runtimewarn;		/* 300 = default TASKSLEEP setting */
2047 	int servicedumponly = 0;
2048 	int pingrunning = 0;
2049 	int usebackfeedqueue = 0;
2050 
2051 	if (init_ldap_library() != 0) {
2052 		errprintf("Failed to initialize ldap library\n");
2053 		return 1;
2054 	}
2055 
2056 	if (xgetenv("CONNTEST") && (strcmp(xgetenv("CONNTEST"), "FALSE") == 0)) pingcolumn = NULL;
2057 	runtimewarn = (xgetenv("TASKSLEEP") ? atol(xgetenv("TASKSLEEP")) : 300);
2058 
2059 	for (argi=1; (argi < argc); argi++) {
2060 		if      (argnmatch(argv[argi], "--timeout=")) {
2061 			char *p = strchr(argv[argi], '=');
2062 			p++; timeout = atoi(p);
2063 		}
2064 		else if (argnmatch(argv[argi], "--conntimeout=")) {
2065 			int newtimeout;
2066 			char *p = strchr(argv[argi], '=');
2067 			p++; newtimeout = atoi(p);
2068 			if (newtimeout > timeout) timeout = newtimeout;
2069 			errprintf("Deprecated option '--conntimeout' should not be used\n");
2070 		}
2071 		else if (argnmatch(argv[argi], "--cmdtimeout=")) {
2072 			char *p = strchr(argv[argi], '=');
2073 			p++; extcmdtimeout = atoi(p);
2074 		}
2075 		else if (argnmatch(argv[argi], "--concurrency=")) {
2076 			char *p = strchr(argv[argi], '=');
2077 			p++; concurrency = atoi(p);
2078 		}
2079 		else if (argnmatch(argv[argi], "--dns-timeout=") || argnmatch(argv[argi], "--dns-max-all=")) {
2080 			char *p = strchr(argv[argi], '=');
2081 			p++; dnstimeout = atoi(p);
2082 		}
2083 		else if (argnmatch(argv[argi], "--dns=")) {
2084 			char *p = strchr(argv[argi], '=');
2085 			p++;
2086 			if (strcmp(p, "only") == 0)      dnsmethod = DNS_ONLY;
2087 			else if (strcmp(p, "ip") == 0)   dnsmethod = IP_ONLY;
2088 			else                             dnsmethod = DNS_THEN_IP;
2089 		}
2090 		else if (strcmp(argv[argi], "--no-ares") == 0) {
2091 			use_ares_lookup = 0;
2092 		}
2093 		else if (argnmatch(argv[argi], "--maxdnsqueue=")) {
2094 			char *p = strchr(argv[argi], '=');
2095 			max_dns_per_run = atoi(p+1);
2096 		}
2097 		else if (argnmatch(argv[argi], "--dnslog=")) {
2098 			char *fn = strchr(argv[argi], '=');
2099 			dnsfaillog = fopen(fn+1, "w");
2100 		}
2101 		else if (argnmatch(argv[argi], "--report=") || (strcmp(argv[argi], "--report") == 0)) {
2102 			char *p = strchr(argv[argi], '=');
2103 			if (p) {
2104 				egocolumn = strdup(p+1);
2105 			}
2106 			else egocolumn = "xymonnet";
2107 			timing = 1;
2108 		}
2109 		else if (strcmp(argv[argi], "--test-untagged") == 0) {
2110 			testuntagged = 1;
2111 		}
2112 		else if (argnmatch(argv[argi], "--frequenttestlimit=")) {
2113 			char *p = strchr(argv[argi], '=');
2114 			p++; frequenttestlimit = atoi(p);
2115 		}
2116 		else if (argnmatch(argv[argi], "--timelimit=")) {
2117 			char *p = strchr(argv[argi], '=');
2118 			p++; runtimewarn = atol(p);
2119 		}
2120 		else if (argnmatch(argv[argi], "--huge=")) {
2121 			char *p = strchr(argv[argi], '=');
2122 			p++; warnbytesread = atoi(p);
2123 		}
2124 		else if (strcmp(argv[argi], "--loadhostsfromxymond") == 0) {
2125 			loadhostsfromxymond = 1;
2126 		}
2127 
2128 		/* Options for TCP tests */
2129 		else if (strcmp(argv[argi], "--checkresponse") == 0) {
2130 			checktcpresponse = 1;
2131 		}
2132 		else if (argnmatch(argv[argi], "--checkresponse=")) {
2133 			char *p = strchr(argv[argi], '=');
2134 			checktcpresponse = 1;
2135 			respcheck_color = parse_color(p+1);
2136 			if (respcheck_color == -1) {
2137 				errprintf("Invalid colorname in '%s' - using yellow\n", argv[argi]);
2138 				respcheck_color = COL_YELLOW;
2139 			}
2140 		}
2141 		else if (strcmp(argv[argi], "--no-flags") == 0) {
2142 			dosendflags = 0;
2143 		}
2144 		else if (strcmp(argv[argi], "--shuffle") == 0) {
2145 			shuffletests = 1;
2146 		}
2147 		else if (argnmatch(argv[argi], "--source-ip=")) {
2148 			char *p = strchr(argv[argi], '=');
2149 			struct in_addr aa;
2150 			p++;
2151 			if (inet_aton(p, &aa))
2152 				defaultsourceip = strdup(p);
2153 			else
2154 				errprintf("Invalid source ip address '%s'\n", argv[argi]);
2155 		}
2156 
2157 		/* Options for PING tests */
2158 		else if (argnmatch(argv[argi], "--ping-tasks=")) {
2159 			/* Note: must check for this before checking "--ping" option */
2160 			char *p = strchr(argv[argi], '=');
2161 			pingchildcount = atoi(p+1);
2162 		}
2163 		else if (argnmatch(argv[argi], "--ping")) {
2164 			char *p = strchr(argv[argi], '=');
2165 			if (p) {
2166 				p++; pingcolumn = p;
2167 			}
2168 			else pingcolumn = "";
2169 		}
2170 		else if (strcmp(argv[argi], "--noping") == 0) {
2171 			pingcolumn = NULL;
2172 		}
2173 		else if (strcmp(argv[argi], "--trace") == 0) {
2174 			dotraceroute = 1;
2175 		}
2176 		else if (strcmp(argv[argi], "--notrace") == 0) {
2177 			dotraceroute = 0;
2178 		}
2179 
2180 		/* Options for HTTP tests */
2181 		else if (argnmatch(argv[argi], "--content=")) {
2182 			char *p = strchr(argv[argi], '=');
2183 			contenttestname = strdup(p+1);
2184 		}
2185 		else if (strcmp(argv[argi], "--bb-proxy-syntax") == 0) {
2186 			/* Obey the Big Brother format for http proxy listed as part of the URL */
2187 			obeybbproxysyntax = 1;
2188 		}
2189 
2190 		/* Options for SSL certificates */
2191 		else if (argnmatch(argv[argi], "--ssl=")) {
2192 			char *p = strchr(argv[argi], '=');
2193 			ssltestname = strdup(p+1);
2194 		}
2195 		else if (strcmp(argv[argi], "--no-ssl") == 0) {
2196 			ssltestname = NULL;
2197 		}
2198 		else if (argnmatch(argv[argi], "--sslwarn=")) {
2199 			char *p = strchr(argv[argi], '=');
2200 			p++; sslwarndays = atoi(p);
2201 		}
2202 		else if (argnmatch(argv[argi], "--sslalarm=")) {
2203 			char *p = strchr(argv[argi], '=');
2204 			p++; sslalarmdays = atoi(p);
2205 		}
2206 		else if (argnmatch(argv[argi], "--sslbits=")) {
2207 			char *p = strchr(argv[argi], '=');
2208 			p++; mincipherbits = atoi(p);
2209 		}
2210 		else if (argnmatch(argv[argi], "--validity=")) {
2211 			char *p = strchr(argv[argi], '=');
2212 			p++; validity = atoi(p);
2213 		}
2214 		else if (argnmatch(argv[argi], "--sslkeysize=")) {
2215 			char *p = strchr(argv[argi], '=');
2216 			p++; sslminkeysize = atoi(p);
2217 		}
2218 		else if (argnmatch(argv[argi], "--sni=")) {
2219 			char *p = strchr(argv[argi], '=');
2220 			p++; snienabled = ( (strcasecmp(p, "yes") == 0) || (strcasecmp(p, "on") == 0) || (strcasecmp(p, "enabled") == 0) || (strcasecmp(p, "true") == 0) || (strcasecmp(p, "1") == 0) );
2221 		}
2222 		else if (strcmp(argv[argi], "--no-cipherlist") == 0) {
2223 			sslincludecipherlist = 0;
2224 		}
2225 		else if (strcmp(argv[argi], "--showallciphers") == 0) {
2226 			sslshowallciphers = 1;
2227 		}
2228 
2229 		/* Debugging options */
2230 		else if (strcmp(argv[argi], "--debug") == 0) {
2231 			debug = 1;
2232 		}
2233 		else if (argnmatch(argv[argi], "--dump")) {
2234 			char *p = strchr(argv[argi], '=');
2235 
2236 			if (p) {
2237 				if (strcmp(p, "=before") == 0) dumpdata = 1;
2238 				else if (strcmp(p, "=after") == 0) dumpdata = 2;
2239 				else dumpdata = 3;
2240 			}
2241 			else dumpdata = 2;
2242 
2243 			debug = 1;
2244 		}
2245 		else if (strcmp(argv[argi], "--no-update") == 0) {
2246 			dontsendmessages = 1;
2247 		}
2248 		else if (strcmp(argv[argi], "--timing") == 0) {
2249 			timing = 1;
2250 		}
2251 
2252 		/* Informational options */
2253 		else if (strcmp(argv[argi], "--services") == 0) {
2254 			servicedumponly = 1;
2255 		}
2256 		else if (strcmp(argv[argi], "--version") == 0) {
2257 			printf("xymonnet version %s\n", VERSION);
2258 			if (ssl_library_version) printf("SSL library : %s\n", ssl_library_version);
2259 			if (ldap_library_version) printf("LDAP library: %s\n", ldap_library_version);
2260 			printf("\n");
2261 			return 0;
2262 		}
2263 		else if ((strcmp(argv[argi], "--help") == 0) || (strcmp(argv[argi], "-?") == 0)) {
2264 			printf("xymonnet version %s\n\n", VERSION);
2265 			printf("Usage: %s [options] [host1 host2 host3 ...]\n", argv[0]);
2266 			printf("General options:\n");
2267 			printf("    --timeout=N                 : Timeout (in seconds) for service tests\n");
2268 			printf("    --cmdtimeout=N              : Timeout for external commands for testing NTP, RPC and traceroute\n");
2269 			printf("    --concurrency=N             : Number of tests run in parallel\n");
2270 			printf("    --dns-timeout=N             : DNS lookups timeout and fail after N seconds [30]\n");
2271 			printf("    --dns=[only|ip|standard]    : How IP's are decided\n");
2272 			printf("    --no-ares                   : Use the system resolver library for hostname lookups\n");
2273 			printf("    --dnslog=FILENAME           : Log failed hostname lookups to file FILENAME\n");
2274 			printf("    --report[=COLUMNNAME]       : Send a status report about the running of xymonnet\n");
2275 			printf("    --test-untagged             : Include hosts without a NET: tag in the test\n");
2276 			printf("    --frequenttestlimit=N       : Seconds after detecting failures in which we poll frequently\n");
2277 			printf("    --timelimit=N               : Warns if the complete test run takes longer than N seconds [TASKSLEEP]\n");
2278 			printf("\nOptions for simple TCP service tests:\n");
2279 			printf("    --checkresponse             : Check response from known services\n");
2280 			printf("    --no-flags                  : Don't send extra xymonnet test flags\n");
2281 			printf("\nOptions for PING (connectivity) tests:\n");
2282 			printf("    --ping[=COLUMNNAME]         : Enable ping checking, default columname is \"conn\"\n");
2283 			printf("    --noping                    : Disable ping checking\n");
2284 			printf("    --trace                     : Run traceroute on all hosts where ping fails\n");
2285 			printf("    --notrace                   : Disable traceroute when ping fails (default)\n");
2286 			printf("    --ping-tasks=N              : Run N ping tasks in parallel (default N=1)\n");
2287 			printf("\nOptions for HTTP/HTTPS (Web) tests:\n");
2288 			printf("    --content=COLUMNNAME        : Define default columnname for CONTENT checks (content)\n");
2289 			printf("\nOptions for SSL certificate tests:\n");
2290 			printf("    --ssl=COLUMNNAME            : Define columnname for SSL certificate checks (sslcert)\n");
2291 			printf("    --no-ssl                    : Disable SSL certificate check\n");
2292 			printf("    --sslwarn=N                 : Go yellow if certificate expires in less than N days (default:30)\n");
2293 			printf("    --sslalarm=N                : Go red if certificate expires in less than N days (default:10)\n");
2294 			printf("    --no-cipherlist             : Do not display SSL cipher data in the SSL certificate check\n");
2295 			printf("    --showallciphers            : List all available ciphers supported by the local SSL library\n");
2296 			printf("\nDebugging options:\n");
2297 			printf("    --no-update                 : Send status messages to stdout instead of to Xymon\n");
2298 			printf("    --timing                    : Trace the amount of time spent on each series of tests\n");
2299 			printf("    --debug                     : Output debugging information\n");
2300 			printf("    --dump[=before|=after|=all] : Dump internal memory structures before/after tests run\n");
2301 			printf("    --maxdnsqueue=N             : Only queue N DNS lookups at a time\n");
2302 			printf("\nInformational options:\n");
2303 			printf("    --services                  : Dump list of known services and exit\n");
2304 			printf("    --version                   : Show program version and exit\n");
2305 			printf("    --help                      : Show help text and exit\n");
2306 
2307 			return 0;
2308 		}
2309 		else if (strncmp(argv[argi], "-", 1) == 0) {
2310 			errprintf("Unknown option %s - try --help\n", argv[argi]);
2311 		}
2312 		else {
2313 			/* Must be a hostname */
2314 			if (selectedcount == 0) selectedhosts = (char **) malloc(argc*sizeof(char *));
2315 			selectedhosts[selectedcount++] = strdup(argv[argi]);
2316 		}
2317 	}
2318 
2319 	svctree = xtreeNew(strcasecmp);
2320 	testhosttree = xtreeNew(strcasecmp);
2321 	cookietree = xtreeNew(strcmp);
2322 	init_timestamp();
2323 	envcheck(reqenv);
2324 	fqdn = get_fqdn();
2325 
2326 	/* Setup SEGV handler */
2327 	setup_signalhandler(egocolumn ? egocolumn : "xymonnet");
2328 
2329 	if (xgetenv("XYMONNETWORK") && (strlen(xgetenv("XYMONNETWORK")) > 0))
2330 		location = strdup(xgetenv("XYMONNETWORK"));
2331 	else if (xgetenv("BBLOCATION") && (strlen(xgetenv("BBLOCATION")) > 0))
2332 		location = strdup(xgetenv("BBLOCATION"));
2333 
2334 	if (pingcolumn && (strlen(pingcolumn) == 0)) pingcolumn = xgetenv("PINGCOLUMN");
2335 	if (pingcolumn && xgetenv("IPTEST_2_CLEAR_ON_FAILED_CONN")) {
2336 		failgoesclear = (strcmp(xgetenv("IPTEST_2_CLEAR_ON_FAILED_CONN"), "TRUE") == 0);
2337 	}
2338 	if (xgetenv("NETFAILTEXT")) failtext = strdup(xgetenv("NETFAILTEXT"));
2339 
2340 	if (debug) {
2341 		int i;
2342 		printf("Command: xymonnet");
2343 		for (i=1; (i<argc); i++) printf(" '%s'", argv[i]);
2344 		printf("\n");
2345 		printf("Environment XYMONNETWORK='%s'\n", textornull(location));
2346 		printf("Environment CONNTEST='%s'\n", textornull(xgetenv("CONNTEST")));
2347 		printf("Environment IPTEST_2_CLEAR_ON_FAILED_CONN='%s'\n", textornull(xgetenv("IPTEST_2_CLEAR_ON_FAILED_CONN")));
2348 		printf("\n");
2349 	}
2350 
2351 	add_timestamp("xymonnet startup");
2352 
2353 	load_services();
2354 	if (servicedumponly) {
2355 		dump_tcp_services();
2356 		return 0;
2357 	}
2358 
2359 	dnstest = add_service("dns", getportnumber("domain"), 0, TOOL_DNS);
2360 	add_service("ntp", getportnumber("ntp"),    0, TOOL_NTP);
2361 	rpctest  = add_service("rpc", getportnumber("sunrpc"), 0, TOOL_RPCINFO);
2362 	httptest = add_service("http", getportnumber("http"),  0, TOOL_HTTP);
2363 	ldaptest = add_service("ldapurl", getportnumber("ldap"), strlen("ldap"), TOOL_LDAP);
2364 	if (pingcolumn) pingtest = add_service(pingcolumn, 0, 0, TOOL_FPING);
2365 	add_timestamp("Service definitions loaded");
2366 
2367 	usebackfeedqueue = (sendmessage_init_local() > 0);
2368 
2369 	load_tests();
2370 	add_timestamp(use_ares_lookup ? "Tests loaded" : "Tests loaded, hostname lookups done");
2371 
2372 	flush_dnsqueue();
2373 	if (use_ares_lookup) add_timestamp("DNS lookups completed");
2374 
2375 	if (dumpdata & 1) { dump_hostlist(); dump_testitems(); }
2376 
2377 	/* Ping checks first */
2378 	if (pingtest && pingtest->items) pingrunning = (start_ping_service(pingtest) == 0);
2379 
2380 	/* Load current status files */
2381 	for (handle = xtreeFirst(svctree); handle != xtreeEnd(svctree); handle = xtreeNext(svctree, handle)) {
2382 		s = (service_t *)xtreeData(svctree, handle);
2383 		if (s != pingtest) load_test_status(s);
2384 	}
2385 
2386 	/* First run the TCP/IP and HTTP tests */
2387 	for (handle = xtreeFirst(svctree); handle != xtreeEnd(svctree); handle = xtreeNext(svctree, handle)) {
2388 		s = (service_t *)xtreeData(svctree, handle);
2389 		if ((s->items) && (s->toolid == TOOL_CONTEST)) {
2390 			char tname[128];
2391 
2392 			for (t = s->items; (t); t = t->next) {
2393 				if (!t->host->dnserror) {
2394 					strncpy(tname, s->testname, sizeof(tname));
2395 					if (s->namelen) tname[s->namelen] = '\0';
2396 					t->privdata = (void *)add_tcp_test(ip_to_test(t->host), s->portnum, tname, NULL,
2397 									   t->srcip,
2398 									   NULL, t->silenttest, NULL,
2399 									   NULL, NULL, NULL);
2400 				}
2401 			}
2402 		}
2403 	}
2404 	for (t = httptest->items; (t); t = t->next) add_http_test(t);
2405 	add_timestamp("Test engine setup completed");
2406 
2407 	do_tcp_tests(timeout, concurrency);
2408 	add_timestamp("TCP tests completed");
2409 
2410 	if (pingrunning) {
2411 		char msg[512];
2412 
2413 		finish_ping_service(pingtest);
2414 		snprintf(msg, sizeof(msg), "PING test completed (%d hosts)", pingcount);
2415 		add_timestamp(msg);
2416 
2417 		if (usebackfeedqueue)
2418 			combo_start_local();
2419 		else
2420 			combo_start();
2421 
2422 		send_results(pingtest, failgoesclear);
2423 		if (selectedhosts == 0) save_ping_status();
2424 		combo_end();
2425 		add_timestamp("PING test results sent");
2426 	}
2427 
2428 	if (debug) {
2429 		show_tcp_test_results();
2430 		show_http_test_results(httptest);
2431 	}
2432 
2433 	for (handle = xtreeFirst(svctree); handle != xtreeEnd(svctree); handle = xtreeNext(svctree, handle)) {
2434 		s = (service_t *)xtreeData(svctree, handle);
2435 		if ((s->items) && (s->toolid == TOOL_CONTEST)) {
2436 			for (t = s->items; (t); t = t->next) {
2437 				/*
2438 				 * If the test fails due to DNS error, t->privdata is NULL
2439 				 */
2440 				if (t->privdata) {
2441 					char *p;
2442 					int i;
2443 					tcptest_t *testresult = (tcptest_t *)t->privdata;
2444 
2445 					t->open = testresult->open;
2446 					t->banner = dupstrbuffer(testresult->banner);
2447 					t->certinfo = testresult->certinfo;
2448 					t->certissuer = testresult->certissuer;
2449 					t->certexpires = testresult->certexpires;
2450 					t->certkeysz = testresult->certkeysz;
2451 					t->mincipherbits = testresult->mincipherbits;
2452 					t->duration.tv_sec = testresult->duration.tv_sec;
2453 					t->duration.tv_nsec = testresult->duration.tv_nsec;
2454 
2455 					/* Binary data in banner ... */
2456 					for (i=0, p=STRBUF(t->banner); (i < STRBUFLEN(t->banner)); i++, p++) {
2457 						if (!isprint((int)*p) && !isspace((int)*p)) *p = '.';
2458 					}
2459 				}
2460 			}
2461 		}
2462 	}
2463 	for (t = httptest->items; (t); t = t->next) {
2464 		if (t->privdata) {
2465 			http_data_t *testresult = (http_data_t *)t->privdata;
2466 
2467 			t->certinfo = testresult->tcptest->certinfo;
2468 			t->certissuer = testresult->tcptest->certissuer;
2469 			t->certexpires = testresult->tcptest->certexpires;
2470 			t->certkeysz = testresult->tcptest->certkeysz;
2471 			t->mincipherbits = testresult->tcptest->mincipherbits;
2472 		}
2473 	}
2474 
2475 	add_timestamp("Test result collection completed");
2476 
2477 
2478 	/* Run the ldap tests */
2479 	for (t = ldaptest->items; (t); t = t->next) add_ldap_test(t);
2480 	add_timestamp("LDAP test engine setup completed");
2481 
2482 	run_ldap_tests(ldaptest, (ssltestname != NULL), timeout);
2483 	add_timestamp("LDAP tests executed");
2484 
2485 	if (debug) show_ldap_test_results(ldaptest);
2486 	for (t = ldaptest->items; (t); t = t->next) {
2487 		if (t->privdata) {
2488 			ldap_data_t *testresult = (ldap_data_t *)t->privdata;
2489 
2490 			t->certinfo = testresult->certinfo;
2491 			t->certissuer = testresult->certissuer;
2492 			t->mincipherbits = testresult->mincipherbits;
2493 			t->certexpires = testresult->certexpires;
2494 			t->certkeysz = testresult->certkeysz;
2495 		}
2496 	}
2497 	add_timestamp("LDAP tests result collection completed");
2498 
2499 
2500 	/* dns, ntp tests */
2501 	for (handle = xtreeFirst(svctree); handle != xtreeEnd(svctree); handle = xtreeNext(svctree, handle)) {
2502 		s = (service_t *)xtreeData(svctree, handle);
2503 		if (s->items) {
2504 			switch(s->toolid) {
2505 				case TOOL_DNS:
2506 					run_nslookup_service(s);
2507 					add_timestamp("DNS tests executed");
2508 					break;
2509 				case TOOL_NTP:
2510 					run_ntp_service(s);
2511 					add_timestamp("NTP tests executed");
2512 					break;
2513 				case TOOL_RPCINFO:
2514 					run_rpcinfo_service(s);
2515 					add_timestamp("RPC tests executed");
2516 					break;
2517 				default:
2518 					break;
2519 			}
2520 		}
2521 	}
2522 
2523 	if (usebackfeedqueue)
2524 		combo_start_local();
2525 	else
2526 		combo_start();
2527 
2528 	for (handle = xtreeFirst(svctree); handle != xtreeEnd(svctree); handle = xtreeNext(svctree, handle)) {
2529 		s = (service_t *)xtreeData(svctree, handle);
2530 		switch (s->toolid) {
2531 			case TOOL_CONTEST:
2532 			case TOOL_DNS:
2533 			case TOOL_NTP:
2534 				send_results(s, failgoesclear);
2535 				break;
2536 
2537 			case TOOL_FPING:
2538 			case TOOL_HTTP:
2539 			case TOOL_LDAP:
2540 				/* These handle result-transmission internally */
2541 				break;
2542 
2543 			case TOOL_RPCINFO:
2544 				send_rpcinfo_results(s, failgoesclear);
2545 				break;
2546 		}
2547 	}
2548 	for (handle = xtreeFirst(testhosttree); (handle != xtreeEnd(testhosttree)); handle = xtreeNext(testhosttree, handle)) {
2549 		h = (testedhost_t *)xtreeData(testhosttree, handle);
2550 		send_http_results(httptest, h, h->firsthttp, nonetpage, failgoesclear, usebackfeedqueue);
2551 		send_content_results(httptest, h, nonetpage, contenttestname, failgoesclear);
2552 		send_ldap_results(ldaptest, h, nonetpage, failgoesclear);
2553 		if (ssltestname && !h->nosslcert) send_sslcert_status(h);
2554 	}
2555 
2556 	combo_end();
2557 
2558 	add_timestamp("Test results transmitted");
2559 
2560 	/*
2561 	 * The list of hosts to test frequently because of a failure must
2562 	 * be saved - it is then picked up by the frequent-test ext script
2563 	 * that runs xymonnet again with the frequent-test hosts as
2564 	 * parameter.
2565 	 *
2566 	 * Should the retest itself update the frequent-test file ? It
2567 	 * would allow us to kick hosts from the frequent-test file sooner.
2568 	 * However, it is simpler (no races) if we just let the normal
2569 	 * test-engine be alone in updating the file.
2570 	 * At the worst, we'll re-test a host going up a couple of times
2571 	 * too much.
2572 	 *
2573 	 * So for now update the list only if we ran with no host-parameters.
2574 	 */
2575 	if (selectedcount == 0) {
2576 		/* Save current status files */
2577 		for (handle = xtreeFirst(svctree); handle != xtreeEnd(svctree); handle = xtreeNext(svctree, handle)) {
2578 			s = (service_t *)xtreeData(svctree, handle);
2579 			if (s != pingtest) save_test_status(s);
2580 		}
2581 		/* Save frequent-test list */
2582 		save_frequenttestlist(argc, argv);
2583 	}
2584 
2585 	/* Save session cookies - every time */
2586 	save_session_cookies();
2587 
2588 	shutdown_ldap_library();
2589 	add_timestamp("xymonnet completed");
2590 
2591 	if (dumpdata & 2) { dump_hostlist(); dump_testitems(); }
2592 
2593 	/* Tell about us */
2594 	if (egocolumn) {
2595 		char msgline[4096];
2596 		char *timestamps;
2597 		int color;
2598 
2599 		/* Go yellow if it runs for too long */
2600 		if ((runtimewarn > 0) && (total_runtime() > runtimewarn)) {
2601 			errprintf("WARNING: Runtime %ld longer than time limit (%ld)\n", total_runtime(), runtimewarn);
2602 		}
2603 		color = (errbuf ? COL_YELLOW : COL_GREEN);
2604 		if (bigfailure) color = COL_RED;
2605 
2606 		if (usebackfeedqueue) combo_start_local(); else combo_start();
2607 		init_status(color);
2608 		snprintf(msgline, sizeof(msgline), "status+%d %s.%s %s %s\n\n", validity, xgetenv("MACHINE"), egocolumn, colorname(color), timestamp);
2609 		addtostatus(msgline);
2610 
2611 		snprintf(msgline, sizeof(msgline), "xymonnet version %s\n", VERSION);
2612 		addtostatus(msgline);
2613 		if (ssl_library_version) {
2614 			snprintf(msgline, sizeof(msgline), "SSL library : %s\n", ssl_library_version);
2615 			addtostatus(msgline);
2616 		}
2617 		if (ldap_library_version) {
2618 			snprintf(msgline, sizeof(msgline), "LDAP library: %s\n", ldap_library_version);
2619 			addtostatus(msgline);
2620 		}
2621 
2622 		snprintf(msgline, sizeof(msgline), "\nStatistics:\n Hosts total           : %8d\n Hosts with no tests   : %8d\n Total test count      : %8d\n Status messages       : %8d\n Alert status msgs     : %8d\n Transmissions         : %8d\n",
2623 			hostcount, notesthostcount, testcount, xymonstatuscount, xymonnocombocount, xymonmsgcount);
2624 		addtostatus(msgline);
2625 		snprintf(msgline, sizeof(msgline), "\nDNS statistics:\n # hostnames resolved  : %8d\n # successful          : %8d\n # failed              : %8d\n # calls to dnsresolve : %8d\n",
2626 			dns_stats_total, dns_stats_success, dns_stats_failed, dns_stats_lookups);
2627 		addtostatus(msgline);
2628 		snprintf(msgline, sizeof(msgline), "\nTCP test statistics:\n # TCP tests total     : %8d\n # HTTP tests          : %8d\n # Simple TCP tests    : %8d\n # Connection attempts : %8d\n # bytes written       : %8ld\n # bytes read          : %8ld\n",
2629 			tcp_stats_total, tcp_stats_http, tcp_stats_plain, tcp_stats_connects,
2630 			tcp_stats_written, tcp_stats_read);
2631 		addtostatus(msgline);
2632 
2633 		if (errbuf) {
2634 			addtostatus("\n\nError output:\n");
2635 			addtostatus(prehtmlquoted(errbuf));
2636 		}
2637 
2638 		if (warnbuf) {
2639 			addtostatus("\n\nWarning output:\n");
2640 			addtostatus(prehtmlquoted(warnbuf));
2641 		}
2642 
2643 		show_timestamps(&timestamps);
2644 		addtostatus(timestamps);
2645 
2646 		finish_status();
2647 		combo_end();
2648 	}
2649 	else show_timestamps(NULL);
2650 
2651 	if (dnsfaillog) fclose(dnsfaillog);
2652 
2653 	if (usebackfeedqueue) sendmessage_finish_local();
2654 
2655 	return 0;
2656 }
2657 
2658