Exemple #1
0
int main(int argc, char *argv[])
{
	int argi; 
	int showeval = 1;
	int cleanexpr = 0;

	setup_signalhandler(argv[0]);

	for (argi = 1; (argi < argc); argi++) {
		if ((strcmp(argv[argi], "--help") == 0)) {
			printf("%s version %s\n\n", argv[0], VERSION);
			printf("Usage:\n%s [--quiet] [--clean] [--debug] [--no-update]\n", argv[0]);
			exit(0);
		}
		else if ((strcmp(argv[argi], "--version") == 0)) {
			printf("%s version %s\n", argv[0], VERSION);
			exit(0);
		}
		else if ((strcmp(argv[argi], "--debug") == 0)) {
			debug = 1;
		}
		else if ((strcmp(argv[argi], "--no-update") == 0)) {
			dontsendmessages = 1;
		}
		else if ((strcmp(argv[argi], "--quiet") == 0)) {
			showeval = 0;
		}
		else if ((strcmp(argv[argi], "--clean") == 0)) {
			cleanexpr = 1;
		}
		else if ((strncmp(argv[argi], "--error-colors=", 15) == 0)) {
			char *tok;
			int newerrorcolors = 0;

			tok = strtok(strchr(argv[argi], '=')+1, ",");
			while (tok) {
				int col = parse_color(tok);

				if ((col >= 0) && (col <= COL_RED)) newerrorcolors |= (1 << parse_color(tok));
				tok = strtok(NULL, ",");
			}

			if (newerrorcolors) errorcolors = newerrorcolors;
		}
	}

	return update_combotests(showeval, cleanexpr);
}
Exemple #2
0
int main(int argc, char *argv[])
{
	int daemonize = 1;
	int listenq = 10;
	char *pidfile = "msgcache.pid";
	int lsocket;
	struct sockaddr_in laddr;
	struct sigaction sa;
	int opt;

	/* Dont save the output from errprintf() */
	save_errbuf = 0;

	memset(&laddr, 0, sizeof(laddr));
	inet_aton("0.0.0.0", (struct in_addr *) &laddr.sin_addr.s_addr);
	laddr.sin_port = htons(1984);
	laddr.sin_family = AF_INET;

	for (opt=1; (opt < argc); opt++) {
		if (argnmatch(argv[opt], "--listen=")) {
			char *locaddr, *p;
			int locport;

			locaddr = strchr(argv[opt], '=')+1;
			p = strchr(locaddr, ':');
			if (p) { locport = atoi(p+1); *p = '\0'; } else locport = 1984;

			memset(&laddr, 0, sizeof(laddr));
			laddr.sin_port = htons(locport);
			laddr.sin_family = AF_INET;
			if (inet_aton(locaddr, (struct in_addr *) &laddr.sin_addr.s_addr) == 0) {
				errprintf("Invalid listen address %s\n", locaddr);
				return 1;
			}
		}
		else if (argnmatch(argv[opt], "--server=")) {
			/* Who is allowed to fetch cached messages */
			char *p = strchr(argv[opt], '=');
			serverlist = getsenderlist(p+1);
		}
		else if (argnmatch(argv[opt], "--max-age=")) {
			char *p = strchr(argv[opt], '=');
			maxage = atoi(p+1);
		}
		else if (argnmatch(argv[opt], "--lqueue=")) {
			char *p = strchr(argv[opt], '=');
			listenq = atoi(p+1);
		}
		else if (strcmp(argv[opt], "--daemon") == 0) {
			daemonize = 1;
		}
		else if (strcmp(argv[opt], "--no-daemon") == 0) {
			daemonize = 0;
		}
		else if (argnmatch(argv[opt], "--pidfile=")) {
			char *p = strchr(argv[opt], '=');
			pidfile = strdup(p+1);
		}
		else if (argnmatch(argv[opt], "--logfile=")) {
			char *p = strchr(argv[opt], '=');
			logfile = strdup(p+1);
		}
		else if (strcmp(argv[opt], "--debug") == 0) {
			debug = 1;
		}
		else if (strcmp(argv[opt], "--version") == 0) {
			printf("xymonproxy version %s\n", VERSION);
			return 0;
		}
	}

	/* Set up a socket to listen for new connections */
	lsocket = socket(AF_INET, SOCK_STREAM, 0);
	if (lsocket == -1) {
		errprintf("Cannot create listen socket (%s)\n", strerror(errno));
		return 1;
	}
	opt = 1;
	setsockopt(lsocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	fcntl(lsocket, F_SETFL, O_NONBLOCK);
	if (bind(lsocket, (struct sockaddr *)&laddr, sizeof(laddr)) == -1) {
		errprintf("Cannot bind to listen socket (%s)\n", strerror(errno));
		return 1;
	}

	if (listen(lsocket, listenq) == -1) {
		errprintf("Cannot listen (%s)\n", strerror(errno));
		return 1;
	}

	/* Redirect logging to the logfile, if requested */
	if (logfile) {
		reopen_file(logfile, "a", stdout);
		reopen_file(logfile, "a", stderr);
	}

	errprintf("Xymon msgcache version %s starting\n", VERSION);
	errprintf("Listening on %s:%d\n", inet_ntoa(laddr.sin_addr), ntohs(laddr.sin_port));

	if (daemonize) {
		pid_t childpid;

		reopen_file("/dev/null", "r", stdin);

		/* Become a daemon */
		childpid = fork();
		if (childpid < 0) {
			/* Fork failed */
			errprintf("Could not fork\n");
			exit(1);
		}
		else if (childpid > 0) {
			/* Parent - save PID and exit */
			FILE *fd = fopen(pidfile, "w");
			if (fd) {
				fprintf(fd, "%d\n", (int)childpid);
				fclose(fd);
			}
			exit(0);
		}
		/* Child (daemon) continues here */
		setsid();
	}

	setup_signalhandler("msgcache");
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sigmisc_handler;
	sigaction(SIGHUP, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);

	do {
		fd_set fdread, fdwrite;
		int maxfd;
		int n;
		conn_t *cwalk, *cprev;
		msgqueue_t *qwalk, *qprev;
		time_t mintstamp;

		/* Remove any finished connections */
		cwalk = chead; cprev = NULL;
		while (cwalk) {
			conn_t *zombie;

			if (cwalk->action != C_DONE) {
				cprev = cwalk;
				cwalk = cwalk->next;
				continue;
			}

			/* Close the socket */
			close(cwalk->sockfd);

			zombie = cwalk;
			if (cprev == NULL) {
				chead = zombie->next;
				cwalk = chead;
				cprev = NULL;
			}
			else {
				cprev->next = zombie->next;
				cwalk = zombie->next;
			}

			freestrbuffer(zombie->msgbuf);
			xfree(zombie);
		}
		ctail = chead;
		if (ctail) { while (ctail->next) ctail = ctail->next; }


		/* Remove expired messages */
		qwalk = qhead; qprev = NULL;
		mintstamp = getcurrenttime(NULL) - maxage;
		while (qwalk) {
			msgqueue_t *zombie;

			if (qwalk->tstamp > mintstamp) {
				/* Hasn't expired yet */
				qprev = qwalk;
				qwalk = qwalk->next;
				continue;
			}

			zombie = qwalk;
			if (qprev == NULL) {
				qhead = zombie->next;
				qwalk = qhead;
				qprev = NULL;
			}
			else {
				qprev->next = zombie->next;
				qwalk = zombie->next;
			}

			freestrbuffer(zombie->msgbuf);
			xfree(zombie);
		}
		qtail = qhead;
		if (qtail) { while (qtail->next) qtail = qtail->next; }


		/* Now we're ready to handle some data */
		FD_ZERO(&fdread);
		FD_ZERO(&fdwrite);

		/* Add the listen socket */
		FD_SET(lsocket, &fdread); 
		maxfd = lsocket;

		for (cwalk = chead; (cwalk); cwalk = cwalk->next) {
			switch (cwalk->action) {
			  case C_READING:
				FD_SET(cwalk->sockfd, &fdread); 
				if (cwalk->sockfd > maxfd) maxfd = cwalk->sockfd; 
				break;

			  case C_WRITING:
				FD_SET(cwalk->sockfd, &fdwrite);
				if (cwalk->sockfd > maxfd) maxfd = cwalk->sockfd; 
				break;

			  case C_DONE:
				break;
			}
		}

		n = select(maxfd+1, &fdread, &fdwrite, NULL, NULL);

		if (n < 0) {
			if (errno == EINTR) continue;
			errprintf("select failed: %s\n", strerror(errno));
			return 0;
		}

		if (n == 0) continue; /* Timeout */

		for (cwalk = chead; (cwalk); cwalk = cwalk->next) {
			switch (cwalk->action) {
			  case C_READING:
				if (FD_ISSET(cwalk->sockfd, &fdread)) grabdata(cwalk);
				break;

			  case C_WRITING:
				if (FD_ISSET(cwalk->sockfd, &fdwrite)) senddata(cwalk);
				break;

			  case C_DONE:
				break;
			}
		}

		if (FD_ISSET(lsocket, &fdread)) {
			/* New incoming connection */
			conn_t *newconn;
			int caddrsize;

			dbgprintf("New connection\n");
			newconn = calloc(1, sizeof(conn_t));

			caddrsize = sizeof(newconn->caddr);
			newconn->sockfd = accept(lsocket, (struct sockaddr *)&newconn->caddr, &caddrsize);
			if (newconn->sockfd == -1) {
				/* accept() failure. Yes, it does happen! */
				dbgprintf("accept failure, ignoring connection (%s)\n", strerror(errno));
				xfree(newconn);
				newconn = NULL;
			}
			else {
				fcntl(newconn->sockfd, F_SETFL, O_NONBLOCK);
				newconn->action = C_READING;
				newconn->msgbuf = newstrbuffer(0);
				newconn->tstamp = getcurrenttime(NULL);
			}

			if (newconn) {
				if (ctail) { ctail->next = newconn; ctail = newconn; }
				else chead = ctail = newconn;
			}
		}

	} while (keeprunning);

	if (pidfile) unlink(pidfile);
	return 0;
}
int main(int argc, char *argv[])
{
	int daemonize = 0;
	int lsocket;
	struct sockaddr_in laddr;
	struct sigaction sa;
	int argi, opt;

	libxymon_init(argv[0]);

	/* Dont save the output from errprintf() */
	save_errbuf = 0;

	memset(&laddr, 0, sizeof(laddr));
	laddr.sin_addr.s_addr = htonl(INADDR_ANY);
	laddr.sin_port = htons(1984);
	laddr.sin_family = AF_INET;

	for (argi=1; (argi < argc); argi++) {
		if (argnmatch(argv[argi], "--listen=")) {
			char *locaddr, *p;
			int locport;

			locaddr = strchr(argv[argi], '=')+1;
			p = strchr(locaddr, ':');
			if (p) { locport = atoi(p+1); *p = '\0'; } else locport = 1984;

			memset(&laddr, 0, sizeof(laddr));
			laddr.sin_port = htons(locport);
			laddr.sin_family = AF_INET;
			if (inet_aton(locaddr, (struct in_addr *) &laddr.sin_addr.s_addr) == 0) {
				errprintf("Invalid listen address %s\n", locaddr);
				return 1;
			}
		}
		else if (strcmp(argv[argi], "--daemon") == 0) {
			daemonize = 1;
		}
		else if (strcmp(argv[argi], "--no-daemon") == 0) {
			daemonize = 0;
		}
		else if (standardoption(argv[argi])) {
			if (showhelp) return 0;
		}
	}

	/* Set up a socket to listen for new connections */
	lsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (lsocket == -1) {
		errprintf("Cannot create listen socket (%s)\n", strerror(errno));
		return 1;
	}

	opt = 1;
	setsockopt(lsocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	if (bind(lsocket, (struct sockaddr *)&laddr, sizeof(laddr)) == -1) {
		errprintf("Cannot bind to listener address (%s)\n", strerror(errno));
		return 1;
	}

	/* Make it non-blocking */
	fcntl(lsocket, F_SETFL, O_NONBLOCK);

	/* Redirect logging to the logfn, if requested */
	if (logfn) {
		reopen_file(logfn, "a", stdout);
		reopen_file(logfn, "a", stderr);
	}

	errprintf("Xymon locator version %s starting\n", VERSION);
	errprintf("Listening on %s:%d\n", inet_ntoa(laddr.sin_addr), ntohs(laddr.sin_port));

	if (daemonize) {
		pid_t childpid;

		reopen_file("/dev/null", "r", stdin);

		/* Become a daemon */
		childpid = fork();
		if (childpid < 0) {
			/* Fork failed */
			errprintf("Could not fork\n");
			exit(1);
		}
		else if (childpid > 0) {
			/* Parent - exit */
			exit(0);
		}
		/* Child (daemon) continues here */
		setsid();
	}

	setup_signalhandler("xymond_locator");
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sigmisc_handler;
	sigaction(SIGHUP, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);

	tree_init();
	load_state();

	do {
		ssize_t n;
		struct sockaddr_in remaddr;
		socklen_t remaddrsz;
		char buf[32768];
		fd_set fdread;

		/* Wait for a message */
		FD_ZERO(&fdread);
		FD_SET(lsocket, &fdread);
		n = select(lsocket+1, &fdread, NULL, NULL, NULL);

		if (n == -1) {
			/* Select error */
			errprintf("select error, aborting: %s\n", strerror(errno));
			keeprunning = 0;
			continue;
		}

		/* We know there is some data */
		remaddrsz = sizeof(remaddr);
		n = recvfrom(lsocket, buf, sizeof(buf), 0, (struct sockaddr *)&remaddr, &remaddrsz);
		if (n == -1) {
			/* We may get EAGAIN if there is not a full message yet */
			if (errno != EAGAIN) {
				errprintf("Recv error: %s\n", strerror(errno));
			}
			continue;
		}
		else if (n == 0) {
			continue;
		}

		buf[n] = '\0';
		dbgprintf("Got message from %s:%d : '%s'\n", 
				inet_ntoa(remaddr.sin_addr), ntohs(remaddr.sin_port), buf);

		handle_request(buf);

		n = sendto(lsocket, buf, strlen(buf)+1, 0, (struct sockaddr *)&remaddr, remaddrsz);
		if (n == -1) {
			if (errno == EAGAIN) {
				errprintf("Out-queue full to %s, dropping response\n", inet_ntoa(remaddr.sin_addr));
			}
			else {
				errprintf("Send failure %s while sending to %s\n", 
					  strerror(errno), inet_ntoa(remaddr.sin_addr));
			}
		}

	} while (keeprunning);

	save_state();

	return 0;
}
Exemple #4
0
int main(int argc, char *argv[])
{
	char *msg;
	int argi;
	struct sigaction sa;
	char *exthandler = NULL;
	char *extids = NULL;
	char *processor = NULL;
	struct sockaddr_un ctlsockaddr;
	int ctlsocket;

	/* Handle program options. */
	for (argi = 1; (argi < argc); argi++) {
		if (strcmp(argv[argi], "--debug") == 0) {
			debug = 1;
		}
		else if (argnmatch(argv[argi], "--rrddir=")) {
			char *p = strchr(argv[argi], '=');
			rrddir = strdup(p+1);
		}
		else if (argnmatch(argv[argi], "--extra-script=")) {
			char *p = strchr(argv[argi], '=');
			exthandler = strdup(p+1);
		}
		else if (argnmatch(argv[argi], "--extra-tests=")) {
			char *p = strchr(argv[argi], '=');
			extids = strdup(p+1);
		}
		else if (argnmatch(argv[argi], "--processor=")) {
			char *p = strchr(argv[argi], '=');
			processor = strdup(p+1);
		}
		else if (strcmp(argv[argi], "--no-cache") == 0) {
			use_rrd_cache = 0;
		}
		else if (net_worker_option(argv[argi])) {
			/* Handled in the subroutine */
		}
	}

	save_errbuf = 0;

	if ((rrddir == NULL) && xgetenv("XYMONRRDS")) {
		rrddir = strdup(xgetenv("XYMONRRDS"));
	}

	if (exthandler && extids) setup_exthandler(exthandler, extids);

	/* Do the network stuff if needed */
	net_worker_run(ST_RRD, LOC_STICKY, update_locator_hostdata);

	setup_signalhandler("xymond_rrd");
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sig_handler;
	sigaction(SIGHUP, &sa, NULL);
	sigaction(SIGCHLD, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGINT, &sa, NULL);
	signal(SIGPIPE, SIG_DFL);

	/* Setup the control socket that receives cache-flush commands */
	memset(&ctlsockaddr, 0, sizeof(ctlsockaddr));
	sprintf(ctlsockaddr.sun_path, "%s/rrdctl.%d", xgetenv("XYMONTMP"), getpid());
	unlink(ctlsockaddr.sun_path);     /* In case it was accidentally left behind */
	ctlsockaddr.sun_family = AF_UNIX;
	ctlsocket = socket(AF_UNIX, SOCK_DGRAM, 0);
	if (ctlsocket == -1) {
		errprintf("Cannot create cache-control socket (%s)\n", strerror(errno));
		return 1;
	}
	fcntl(ctlsocket, F_SETFL, O_NONBLOCK);
	if (bind(ctlsocket, (struct sockaddr *)&ctlsockaddr, sizeof(ctlsockaddr)) == -1) {
		errprintf("Cannot bind to cache-control socket (%s)\n", strerror(errno));
		return 1;
	}
	/* Linux obeys filesystem permissions on the socket file, so make it world-accessible */
	if (chmod(ctlsockaddr.sun_path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) {
		errprintf("Setting permissions on cache-control socket failed: %s\n", strerror(errno));
	}

	/* Load the RRD definitions */
	load_rrddefs();

	/* If we are passing data to an external processor, create the pipe to it */
	setup_extprocessor(processor);

	running = 1;
	while (running) {
		char *eoln, *restofmsg = NULL;
		char *metadata[MAX_META+1];
		int metacount;
		char *p;
		char *hostname = NULL, *testname = NULL, *sender = NULL, *classname = NULL, *pagepaths = NULL;
		xymonrrd_t *ldef = NULL;
		time_t tstamp;
		int childstat;
                ssize_t n;
		char ctlbuf[PATH_MAX];
		int gotcachectlmessage;
		time_t now;

		/* See if we have any cache-control messages pending */
		do {
			n = recv(ctlsocket, ctlbuf, sizeof(ctlbuf), 0);
			gotcachectlmessage = (n > 0);
			if (gotcachectlmessage) {
				/* We have a control message */
				char *bol, *eol;

				ctlbuf[n] = '\0';
				bol = ctlbuf;
				do {
					eol = strchr(bol, '\n'); if (eol) *eol = '\0';
					rrdcacheflushhost(bol);
					if (eol) { bol = eol+1; } else bol = NULL;
				} while (bol && *bol);
			}
		} while (gotcachectlmessage);

		/* Get next message */
		msg = get_xymond_message(C_LAST, argv[0], &seq, NULL);
		if (msg == NULL) {
			running = 0;
			continue;
		}

		now = gettimer();
		if (reloadtime < now) {
			/* Reload configuration files */
			load_hostnames(xgetenv("HOSTSCFG"), NULL, get_fqdn());
			load_client_config(NULL);
			reloadtime = now + 600;
		}

		/* Split the message in the first line (with meta-data), and the rest */
 		eoln = strchr(msg, '\n');
		if (eoln) {
			*eoln = '\0';
			restofmsg = eoln+1;
		}

		/* Parse the meta-data */
		metacount = 0; 
		memset(&metadata, 0, sizeof(metadata));
		p = gettok(msg, "|");
		while (p && (metacount < MAX_META)) {
			metadata[metacount++] = p;
			p = gettok(NULL, "|");
		}
		metadata[metacount] = NULL;

		if ((metacount >= 14) && (strncmp(metadata[0], "@@status", 8) == 0)) {
			/*
			 * @@status|timestamp|sender|origin|hostname|testname|expiretime|color|testflags|\
			 * prevcolor|changetime|ackexpiretime|ackmessage|disableexpiretime|disablemessage|\
			 * clienttstamp|flapping|classname|pagepaths
			 */
			int color = parse_color(metadata[7]);

			switch (color) {
			  case COL_GREEN:
			  case COL_YELLOW:
			  case COL_RED:
			  case COL_BLUE: /* Blue is OK, because it only arrives here when an update is sent */
				tstamp = atoi(metadata[1]);
				sender = metadata[2];
				hostname = metadata[4]; 
				testname = metadata[5];
				classname = (metadata[17] ? metadata[17] : "");
				pagepaths = (metadata[18] ? metadata[18] : "");
				ldef = find_xymon_rrd(testname, metadata[8]);
				update_rrd(hostname, testname, restofmsg, tstamp, sender, ldef, classname, pagepaths);
				break;

			  default:
				/* Ignore reports with purple, blue or clear - they have no data we want. */
				break;
			}
		}
		else if ((metacount > 5) && (strncmp(metadata[0], "@@data", 6) == 0)) {
			/* @@data|timestamp|sender|origin|hostname|testname|classname|pagepaths */
			tstamp = atoi(metadata[1]);
			sender = metadata[2];
			hostname = metadata[4]; 
			testname = metadata[5];
			classname = (metadata[6] ? metadata[6] : "");
			pagepaths = (metadata[7] ? metadata[7] : "");
			ldef = find_xymon_rrd(testname, "");
			update_rrd(hostname, testname, restofmsg, tstamp, sender, ldef, classname, pagepaths);
		}
		else if (strncmp(metadata[0], "@@shutdown", 10) == 0) {
			running = 0;
			continue;
		}
		else if (strncmp(metadata[0], "@@idle", 6) == 0) {
			/* Ignored */
			continue;
		}
		else if (strncmp(metadata[0], "@@logrotate", 11) == 0) {
			char *fn = xgetenv("XYMONCHANNEL_LOGFILENAME");
			if (fn && strlen(fn)) {
				freopen(fn, "a", stdout);
				freopen(fn, "a", stderr);
			}
			continue;
		}
		else if (strncmp(metadata[0], "@@reload", 8) == 0) {
			reloadtime = 0;
		}
		else if ((metacount > 3) && (strncmp(metadata[0], "@@drophost", 10) == 0)) {
			char hostdir[PATH_MAX];
			hostname = metadata[3];

			MEMDEFINE(hostdir);

			sprintf(hostdir, "%s/%s", rrddir, hostname);
			dropdirectory(hostdir, 1);

			MEMUNDEFINE(hostdir);
		}
		else if ((metacount > 4) && (strncmp(metadata[0], "@@droptest", 10) == 0)) {
			/*
			 * Not implemented. Mappings of testnames -> rrd files is
			 * too complex, so on the rare occasion that a single test
			 * is deleted, they will have to delete the rrd files themselves.
			 */
		}
		else if ((metacount > 4) && (strncmp(metadata[0], "@@renamehost", 12) == 0)) {
			char oldhostdir[PATH_MAX];
			char newhostdir[PATH_MAX];
			char *newhostname;

			MEMDEFINE(oldhostdir);
			MEMDEFINE(newhostdir);

			hostname = metadata[3];
			newhostname = metadata[4];
			sprintf(oldhostdir, "%s/%s", rrddir, hostname);
			sprintf(newhostdir, "%s/%s", rrddir, newhostname);
			rename(oldhostdir, newhostdir);

			if (net_worker_locatorbased()) locator_rename_host(hostname, newhostname, ST_RRD);

			MEMUNDEFINE(newhostdir);
			MEMUNDEFINE(oldhostdir);
		}
		else if ((metacount > 5) && (strncmp(metadata[0], "@@renametest", 12) == 0)) {
			/* Not implemented. See "droptest". */
		}

		/* 
		 * We fork a subprocess when processing drophost requests.
		 * Pickup any finished child processes to avoid zombies
		 */
		while (wait3(&childstat, WNOHANG, NULL) > 0) ;
	}

	/* Flush all cached updates to disk */
	errprintf("Shutting down, flushing cached updates to disk\n");
	rrdcacheflushall();
	errprintf("Cache flush completed\n");

	/* Close the external processor */
	shutdown_extprocessor();

	/* Close the control socket */
	close(ctlsocket);
	unlink(ctlsockaddr.sun_path);

	return 0;
}
Exemple #5
0
int main(int argc, char *argv[])
{
	char		*pagedir;
	bbgen_page_t 	*p;
	dispsummary_t	*s;
	int		i;
	char		*pageset = NULL;
	char		*nssidebarfilename = NULL;
	char		*egocolumn = NULL;
	char		*csvfile = NULL;
	char		csvdelim = ',';
	int		embedded = 0;
	int		hobbitddump = 0;
	char		*envarea = NULL;
	int		do_normal_pages = 1;

	/* Setup standard header+footer (might be modified by option pageset) */
	select_headers_and_footers("bb");

	bb_color = bb2_color = bbnk_color = -1;
	pagedir = NULL;
	init_timestamp();
	fqdn = get_fqdn();

	/* Setup values from env. vars that may be overridden via commandline options */
	if (xgetenv("MKBB2COLREPEAT")) {
		int i = atoi(xgetenv("MKBB2COLREPEAT"));

		if (i > 0) maxrowsbeforeheading = i;
	}

	for (i = 1; (i < argc); i++) {
		if ( (strcmp(argv[i], "--hobbitd") == 0)       ||
		     (argnmatch(argv[i], "--purplelifetime=")) ||
		     (strcmp(argv[i], "--nopurple") == 0)      ) {
			/* Deprecated */
		}
		else if (argnmatch(argv[i], "--env=")) {
			char *lp = strchr(argv[i], '=');
			loadenv(lp+1, envarea);
		}
		else if (argnmatch(argv[i], "--area=")) {
			char *lp = strchr(argv[i], '=');
			envarea = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--hobbitddump")) {
			hobbitddump = 1;
		}

		else if (argnmatch(argv[i], "--ignorecolumns=")) {
			char *lp = strchr(argv[i], '=');
			ignorecolumns = (char *) malloc(strlen(lp)+2);
			sprintf(ignorecolumns, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--nk-reds-only")) {
			nkonlyreds = 1;
		}
		else if (argnmatch(argv[i], "--bb2-ignorecolumns=")) {
			char *lp = strchr(argv[i], '=');
			bb2ignorecolumns = (char *) malloc(strlen(lp)+2);
			sprintf(bb2ignorecolumns, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--bb2-colors=")) {
			char *lp = strchr(argv[i], '=') + 1;
			bb2colors = colorset(lp, (1 << COL_GREEN));
		}
		else if (argnmatch(argv[i], "--bb2-ignorepurples")) {
			bb2colors = (bb2colors & ~(1 << COL_PURPLE));
		}
		else if (argnmatch(argv[i], "--bb2-ignoredialups")) {
			bb2nodialups = 1;
		}
		else if (argnmatch(argv[i], "--includecolumns=")) {
			char *lp = strchr(argv[i], '=');
			includecolumns = (char *) malloc(strlen(lp)+2);
			sprintf(includecolumns, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--eventignore=")) {
			char *lp = strchr(argv[i], '=');
			eventignorecolumns = (char *) malloc(strlen(lp)+2);
			sprintf(eventignorecolumns, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--doccgi=")) {
			char *lp = strchr(argv[i], '=');
			char *url = (char *)malloc(strlen(xgetenv("CGIBINURL"))+strlen(lp+1)+2);
			sprintf(url, "%s/%s", xgetenv("CGIBINURL"), lp+1);
			setdocurl(url);
			xfree(url);
		}
		else if (argnmatch(argv[i], "--docurl=")) {
			char *lp = strchr(argv[i], '=');
			setdocurl(lp+1);
		}
		else if (argnmatch(argv[i], "--no-doc-window")) {
			/* This is a no-op now */
		}
		else if (argnmatch(argv[i], "--doc-window")) {
			setdocurl("TARGET=\"_blank\"");
		}
		else if (argnmatch(argv[i], "--htmlextension=")) {
			char *lp = strchr(argv[i], '=');
			htmlextension = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--htaccess")) {
			char *lp = strchr(argv[i], '=');
			if (lp) htaccess = strdup(lp+1);
			else htaccess = ".htaccess";
		}
		else if ((strcmp(argv[i], "--wml") == 0) || argnmatch(argv[i], "--wml=")) {
			char *lp = strchr(argv[i], '=');

			if (lp) {
				wapcolumns = (char *) malloc(strlen(lp)+2);
				sprintf(wapcolumns, ",%s,", (lp+1));
			}
			enable_wmlgen = 1;
		}
		else if (argnmatch(argv[i], "--nstab=")) {
			char *lp = strchr(argv[i], '=');

			if (strlen(lp+1) > 0) {
				nssidebarfilename = strdup(lp+1);
			}
			else errprintf("--nstab requires a filename\n");
		}
		else if (argnmatch(argv[i], "--nslimit=")) {
			char *lp = strchr(argv[i], '=');
			nssidebarcolorlimit = parse_color(lp+1);
		}
		else if (argnmatch(argv[i], "--rssversion=")) {
			char *lp = strchr(argv[i], '=');

			rssversion = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--rsslimit=")) {
			char *lp = strchr(argv[i], '=');
			rsscolorlimit = parse_color(lp+1);
		}
		else if (argnmatch(argv[i], "--rss")) {
			wantrss = 1;
		}
		else if (argnmatch(argv[i], "--rssextension=")) {
			char *lp = strchr(argv[i], '=');
			rssextension = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--reportopts=")) {
			char style[MAX_LINE_LEN];
			unsigned int rstart, rend;

			int count = sscanf(argv[i], "--reportopts=%u:%u:%d:%s", 
					   &rstart, &rend, &dynamicreport, style);
			reportstart = rstart; reportend = rend;

			if (count < 2) {
				errprintf("Invalid --reportopts option: Must have start- and end-times\n");
				return 1;
			}

			if (count < 3) dynamicreport = 1;
			if (count == 4) {
				if (strcmp(style, stylenames[STYLE_CRIT]) == 0) reportstyle = STYLE_CRIT;
				else if (strcmp(style, stylenames[STYLE_NONGR]) == 0) reportstyle = STYLE_NONGR;
				else reportstyle = STYLE_OTHER;
			}

			if (reportstart < 788918400) reportstart = 788918400;
			if (reportend > time(NULL)) reportend = time(NULL);

			if (xgetenv("BBREPWARN")) reportwarnlevel = atof(xgetenv("BBREPWARN"));
			if (xgetenv("BBREPGREEN")) reportgreenlevel = atof(xgetenv("BBREPGREEN"));

			if ((reportwarnlevel < 0.0) || (reportwarnlevel > 100.0)) reportwarnlevel = 97.0;
			if ((reportgreenlevel < 0.0) || (reportgreenlevel > 100.0)) reportgreenlevel = 99.995;

			select_headers_and_footers("bbrep");
			sethostenv_report(reportstart, reportend, reportwarnlevel, reportgreenlevel);
		}
		else if (argnmatch(argv[i], "--csv="))  {
			char *lp = strchr(argv[i], '=');
			csvfile = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--csvdelim="))  {
			char *lp = strchr(argv[i], '=');
			csvdelim = *(lp+1);
		}
		else if (argnmatch(argv[i], "--snapshot=")) {
			char *lp = strchr(argv[i], '=');

			snapshot = atol(lp+1);
			select_headers_and_footers("bbsnap");
			sethostenv_snapshot(snapshot);
		}

		else if (strcmp(argv[i], "--pages-first") == 0) {
			hostsbeforepages = 0;
		}
		else if (strcmp(argv[i], "--pages-last") == 0) {
			hostsbeforepages = 1;
		}
		else if (argnmatch(argv[i], "--subpagecolumns=")) {
			char *lp = strchr(argv[i], '=');

			subpagecolumns = atoi(lp+1);
			if (subpagecolumns < 1) subpagecolumns=1;
		}
		else if (argnmatch(argv[i], "--maxrows=")) {
			char *lp = strchr(argv[i], '=');

			maxrowsbeforeheading = atoi(lp+1);
			if (maxrowsbeforeheading < 0) maxrowsbeforeheading=0;
		}
		else if (strcmp(argv[i], "--recentgifs") == 0) {
			use_recentgifs = 1;
		}
		else if (argnmatch(argv[i], "--recentgifs=")) {
			char *lp = strchr(argv[i], '=');

			use_recentgifs = 1;
			recentgif_limit = 60*durationvalue(lp+1);
		}
		else if (strcmp(argv[i], "--sort-group-only-items") == 0) {
			sort_grouponly_items = 1;
		}
		else if (argnmatch(argv[i], "--page-title=")) {
			char *lp = strchr(argv[i], '=');

			defaultpagetitle = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--dialupskin=")) {
			char *lp = strchr(argv[i], '=');

			dialupskin = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--reverseskin=")) {
			char *lp = strchr(argv[i], '=');

			reverseskin = strdup(lp+1);
		}
		else if (strcmp(argv[i], "--pagetitle-links") == 0) {
			pagetitlelinks = 1;
		}
		else if (strcmp(argv[i], "--pagetext-headings") == 0) {
			pagetextheadings = 1;
		}
		else if (strcmp(argv[i], "--underline-headings") == 0) {
			underlineheadings = 1;
		}
		else if (strcmp(argv[i], "--no-underline-headings") == 0) {
			underlineheadings = 0;
		}
		else if (strcmp(argv[i], "--no-eventlog") == 0) {
			bb2eventlog = 0;
		}
		else if (argnmatch(argv[i], "--max-eventcount=")) {
			char *lp = strchr(argv[i], '=');

			bb2eventlogmaxcount = atoi(lp+1);
		}
		else if (argnmatch(argv[i], "--max-eventtime=")) {
			char *lp = strchr(argv[i], '=');

			bb2eventlogmaxtime = atoi(lp+1);
		}
		else if (argnmatch(argv[i], "--max-ackcount=")) {
			char *lp = strchr(argv[i], '=');

			bb2acklogmaxcount = atoi(lp+1);
		}
		else if (argnmatch(argv[i], "--max-acktime=")) {
			char *lp = strchr(argv[i], '=');

			bb2acklogmaxtime = atoi(lp+1);
		}
		else if (strcmp(argv[i], "--no-acklog") == 0) {
			bb2acklog = 0;
		}
		else if (strcmp(argv[i], "--no-pages") == 0) {
			do_normal_pages = 0;
		}

		else if (argnmatch(argv[i], "--noprop=")) {
			char *lp = strchr(argv[i], '=');
			nopropyellowdefault = (char *) malloc(strlen(lp)+2);
			sprintf(nopropyellowdefault, ",%s,", (lp+1));
			errprintf("--noprop is deprecated - use --nopropyellow instead\n");
		}
		else if (argnmatch(argv[i], "--nopropyellow=")) {
			char *lp = strchr(argv[i], '=');
			nopropyellowdefault = (char *) malloc(strlen(lp)+2);
			sprintf(nopropyellowdefault, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--nopropred=")) {
			char *lp = strchr(argv[i], '=');
			nopropreddefault = (char *) malloc(strlen(lp)+2);
			sprintf(nopropreddefault, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--noproppurple=")) {
			char *lp = strchr(argv[i], '=');
			noproppurpledefault = (char *) malloc(strlen(lp)+2);
			sprintf(noproppurpledefault, ",%s,", (lp+1));
		}
		else if (argnmatch(argv[i], "--nopropack=")) {
			char *lp = strchr(argv[i], '=');
			nopropackdefault = (char *) malloc(strlen(lp)+2);
			sprintf(nopropackdefault, ",%s,", (lp+1));
		}

		else if (strcmp(argv[i], "--bbpageONLY") == 0) {
			/* Deprecated */
			errprintf("--bbpageONLY is deprecated - use --pageset=NAME to generate pagesets\n");
		}
		else if (strcmp(argv[i], "--embedded") == 0) {
			embedded = 1;
		}
		else if (argnmatch(argv[i], "--pageset=")) {
			char *lp = strchr(argv[i], '=');
			pageset = strdup(lp+1);
		}
		else if (argnmatch(argv[i], "--template=")) {
			char *lp = strchr(argv[i], '=');
			lp++;
			select_headers_and_footers(lp);
		}

		else if (argnmatch(argv[i], "--purplelog=")) {
			char *lp = strchr(argv[i], '=');
			if (*(lp+1) == '/') purplelogfn = strdup(lp+1);
			else {
				purplelogfn = (char *) malloc(strlen(xgetenv("BBHOME"))+1+strlen(lp+1)+1);
				sprintf(purplelogfn, "%s/%s", xgetenv("BBHOME"), (lp+1));
			}
		}
		else if (argnmatch(argv[i], "--report=") || (strcmp(argv[i], "--report") == 0)) {
			char *lp = strchr(argv[i], '=');
			if (lp) {
				egocolumn = strdup(lp+1);
			}
			else egocolumn = "bbgen";
			timing = 1;
		}
		else if (argnmatch(argv[i], "--nklog=") || (strcmp(argv[i], "--nklog") == 0)) {
			char *lp = strchr(argv[i], '=');
			if (lp) {
				lognkstatus = strdup(lp+1);
			}
			else lognkstatus = "nk";
		}
		else if (strcmp(argv[i], "--timing") == 0) {
			timing = 1;
		}
		else if (strcmp(argv[i], "--debug") == 0) {
			debug = 1;
		}
		else if (strcmp(argv[i], "--no-update") == 0) {
			dontsendmessages = 1;
		}
		else if (strcmp(argv[i], "--version") == 0) {
			printf("bbgen version %s\n", VERSION);
			printf("\n");
			exit(0);
		}

		else if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-?") == 0)) {
			printf("bbgen for Hobbit version %s\n\n", VERSION);
			printf("Usage: %s [options] [WebpageDirectory]\n", argv[0]);
			printf("Options:\n");
			printf("    --ignorecolumns=test[,test] : Completely ignore these columns\n");
			printf("    --nk-reds-only              : Only show red statuses on the NK page\n");
			printf("    --bb2-ignorecolumns=test[,test]: Ignore these columns for the BB2 page\n");
			printf("    --bb2-ignorepurples         : Ignore all-purple hosts on BB2 page\n");
			printf("    --includecolumns=test[,test]: Always include these columns on BB2 page\n");
		        printf("    --max-eventcount=N          : Max number of events to include in eventlog\n");
		        printf("    --max-eventtime=N           : Show events that occurred within the last N minutes\n");
			printf("    --eventignore=test[,test]   : Columns to ignore in bb2 event-log display\n");
			printf("    --no-eventlog               : Do not generate the bb2 eventlog display\n");
			printf("    --no-acklog                 : Do not generate the bb2 ack-log display\n");
			printf("    --no-pages                  : Generate only the bb2 and bbnk pages\n");
			printf("    --docurl=documentation-URL  : Hostnames link to a general (dynamic) web page for docs\n");
			printf("    --no-doc-window             : Open doc-links in same window\n");
			printf("    --htmlextension=.EXT        : Sets filename extension for generated file (default: .html\n");
			printf("    --report[=COLUMNNAME]       : Send a status report about the running of bbgen\n");
			printf("    --reportopts=ST:END:DYN:STL : Run in Hobbit Reporting mode\n");
			printf("    --csv=FILENAME              : For Hobbit Reporting, output CSV file\n");
			printf("    --csvdelim=CHARACTER        : Delimiter in CSV file output (default: comma)\n");
			printf("    --snapshot=TIME             : Snapshot mode\n");
			printf("\nPage layout options:\n");
			printf("    --pages-first               : Put page- and subpage-links before hosts (default)\n");
			printf("    --pages-last                : Put page- and subpage-links after hosts\n");
			printf("    --subpagecolumns=N          : Number of columns for links to pages and subpages\n");
			printf("    --maxrows=N                 : Repeat column headings for every N hosts shown\n");
			printf("    --recentgifs                : Use xxx-recent.gif icons for newly changed tests\n");
			printf("    --sort-group-only-items     : Display group-only items in alphabetical order\n");
			printf("    --page-title=TITLE          : Set a default page title for all pages\n");
			printf("    --dialupskin=URL            : Use a different icon skin for dialup tests\n");
			printf("    --reverseskin=URL           : Use a different icon skin for reverse tests\n");
			printf("    --pagetitle-links           : Make page- and subpage-titles act as links\n");
			printf("    --pagetext-headings         : Use page texts as headings\n");
			printf("    --no-underline-headings     : Do not underline the page headings\n");
			printf("\nStatus propagation control options:\n");
			printf("    --noprop=test[,test]        : Disable upwards status propagation when YELLOW\n");
			printf("    --nopropred=test[,test]     : Disable upwards status propagation when RED or YELLOW\n");
			printf("    --noproppurple=test[,test]  : Disable upwards status propagation when PURPLE\n");
			printf("\nAlternate pageset generation support:\n");
			printf("    --pageset=SETNAME           : Generate non-standard pageset with tag SETNAME\n");
			printf("    --template=TEMPLATE         : template for header and footer files\n");
			printf("\nAlternate output formats:\n");
			printf("    --wml[=test1,test2,...]     : Generate a small (bb2-style) WML page\n");
			printf("    --nstab=FILENAME            : Generate a Netscape Sidebar feed\n");
			printf("    --nslimit=COLOR             : Minimum color to include on Netscape sidebar\n");
			printf("    --rss                       : Generate a RSS/RDF feed of alerts\n");
			printf("    --rssextension=.EXT         : Sets filename extension for RSS files (default: .rss\n");
			printf("    --rssversion={0.91|0.92|1.0|2.0} : Specify RSS/RDF version (default: 0.91)\n");
			printf("    --rsslimit=COLOR            : Minimum color to include on RSS feed\n");
			printf("\nDebugging/troubleshooting options:\n");
			printf("    --timing                    : Collect timing information\n");
			printf("    --debug                     : Debugging information\n");
			printf("    --version                   : Show version information\n");
			printf("    --purplelog=FILENAME        : Create a log of purple hosts and tests\n");
			exit(0);
		}
		else if (argnmatch(argv[i], "-")) {
			errprintf("Unknown option : %s\n", argv[i]);
		}

		else {
			/* Last argument is pagedir */
			pagedir = strdup(argv[i]);
		}
	}

	/* In case they changed the name of our column ... */
	if (egocolumn) setup_signalhandler(egocolumn);

	if (debug) {
		int i;
		printf("Command: bbgen");
		for (i=1; (i<argc); i++) printf(" '%s'", argv[i]);
		printf("\n");
		printf("Environment BBHOSTS='%s'\n", textornull(xgetenv("BBHOSTS")));
		printf("\n");
	}

	add_timestamp("Startup");

	/* Check that all needed environment vars are defined */
	envcheck(reqenv);

	/* Catch a SEGV fault */
	setup_signalhandler("bbgen");

	/* Set umask to 0022 so that the generated HTML pages have world-read access */
	umask(0022);

	if (pagedir == NULL) {
		if (xgetenv("BBWWW")) {
			pagedir = strdup(xgetenv("BBWWW"));
		}
		else {
			pagedir = (char *) malloc(strlen(xgetenv("BBHOME"))+5);
			sprintf(pagedir, "%s/www", xgetenv("BBHOME"));
		}
	}

	if (xgetenv("BBHTACCESS")) bbhtaccess = strdup(xgetenv("BBHTACCESS"));
	if (xgetenv("BBPAGEHTACCESS")) bbpagehtaccess = strdup(xgetenv("BBPAGEHTACCESS"));
	if (xgetenv("BBSUBPAGEHTACCESS")) bbsubpagehtaccess = strdup(xgetenv("BBSUBPAGEHTACCESS"));

	/*
	 * When doing alternate pagesets, disable some stuff:
	 * No WML or RSS pages.
	 */
	if (pageset || embedded || snapshot) enable_wmlgen = wantrss = 0;
	if (embedded) {
		egocolumn = htaccess = NULL;

		/*
		 * Need to have default SIGPIPE handling when doing embedded stuff.
		 * We are probably run from a CGI script or something similar.
		 */
		signal(SIGPIPE, SIG_DFL);
	}

	/* Load all data from the various files */
	load_all_links();
	add_timestamp("Load links done");
	pagehead = load_bbhosts(pageset);
	add_timestamp("Load bbhosts done");

	if (!embedded) {
		/* Remove old acknowledgements */
		delete_old_acks();
		add_timestamp("ACK removal done");
	}

	statehead = load_state(&dispsums);
	if (embedded || snapshot) dispsums = NULL;
	add_timestamp("Load STATE done");

	if (hobbitddump) {
		dump_hobbitdchk();
		return 0;
	}

	/* Calculate colors of hosts and pages */
	calc_hostcolors(bb2ignorecolumns);
	calc_pagecolors(pagehead);

	/* Topmost page (background color for bb.html) */
	for (p=pagehead; (p); p = p->next) {
		if (p->color > pagehead->color) pagehead->color = p->color;
	}
	bb_color = pagehead->color;

	if (xgetenv("SUMMARY_SET_BKG") && (strcmp(xgetenv("SUMMARY_SET_BKG"), "TRUE") == 0)) {
		/*
		 * Displayed summaries affect the BB page only, 
		 * but should not go into the color we report to
		 * others.
		 */
		for (s=dispsums; (s); s = s->next) {
			if (s->color > pagehead->color) pagehead->color = s->color;
		}
	}
	add_timestamp("Color calculation done");

	if (debug) dumpall(pagehead);

	/* Generate pages */
	if (chdir(pagedir) != 0) {
		errprintf("Cannot change to webpage directory %s\n", pagedir);
		exit(1);
	}

	if (embedded) {
		/* Just generate that one page */
		do_one_page(pagehead, NULL, 1);
		return 0;
	}

	/* The main page - bb.html and pages/subpages thereunder */
	add_timestamp("Hobbit pagegen start");
	if (reportstart && csvfile) {
		csv_availability(csvfile, csvdelim);
	}
	else if (do_normal_pages) {
		do_page_with_subs(pagehead, dispsums);
	}
	add_timestamp("Hobbit pagegen done");

	if (reportstart) {
		/* Reports end here */
		return 0;
	}

	/* The full summary page - bb2.html */
	bb2_color = do_bb2_page(nssidebarfilename, PAGE_BB2);
	add_timestamp("BB2 generation done");

	/* Reduced summary (alerts) page - bbnk.html */
	bbnk_color = do_bb2_page(NULL, PAGE_NK);
	add_timestamp("BBNK generation done");

	if (snapshot) {
		/* Snapshots end here */
		return 0;
	}

	/* Send summary notices - only once, so not on pagesets */
	if (pageset == NULL) {
		send_summaries(sumhead);
		add_timestamp("Summary transmission done");
	}

	/* Generate WML cards */
	if (enable_wmlgen) {
		do_wml_cards(pagedir);
		add_timestamp("WML generation done");
	}

	/* Need to do this before sending in our report */
	add_timestamp("Run completed");

	/* Tell about us */
	if (egocolumn) {
		char msgline[4096];
		char *timestamps;
		long bbsleep = (xgetenv("BBSLEEP") ? atol(xgetenv("BBSLEEP")) : 300);
		int color;

		/* Go yellow if it runs for too long */
		if (total_runtime() > bbsleep) {
			errprintf("WARNING: Runtime %ld longer than BBSLEEP (%ld)\n", total_runtime(), bbsleep);
		}
		color = (errbuf ? COL_YELLOW : COL_GREEN);

		combo_start();
		init_status(color);
		sprintf(msgline, "status %s.%s %s %s\n\n", xgetenv("MACHINE"), egocolumn, colorname(color), timestamp);
		addtostatus(msgline);

		sprintf(msgline, "bbgen for Hobbit version %s\n", VERSION);
		addtostatus(msgline);

		sprintf(msgline, "\nStatistics:\n Hosts               : %5d\n Status messages     : %5d\n Purple messages     : %5d\n Pages               : %5d\n", 
			hostcount, statuscount, purplecount, pagecount);
		addtostatus(msgline);

		if (errbuf) {
			addtostatus("\n\nError output:\n");
			addtostatus(errbuf);
		}

		show_timestamps(&timestamps);
		addtostatus(timestamps);

		finish_status();
		combo_end();
	}
	else show_timestamps(NULL);

	return 0;
}
int main(int argc, char *argv[])
{
	char *msg;
	int seq;
	int argi;
	int alertcolors, alertinterval;
	char *configfn = NULL;
	char *checkfn = NULL;
	int checkpointinterval = 900;
	char acklogfn[PATH_MAX];
	FILE *acklogfd = NULL;
	char notiflogfn[PATH_MAX];
	FILE *notiflogfd = NULL;
	char *tracefn = NULL;
	struct sigaction sa;
	int configchanged;
	time_t lastxmit = 0;

	MEMDEFINE(acklogfn);
	MEMDEFINE(notiflogfn);

	libxymon_init(argv[0]);

	/* Dont save the error buffer */
	save_errbuf = 0;

	/* Load alert config */
	alertcolors = colorset(xgetenv("ALERTCOLORS"), ((1 << COL_GREEN) | (1 << COL_BLUE)));
	alertinterval = 60*atoi(xgetenv("ALERTREPEAT"));

	/* Create our loookup-trees */
	hostnames = xtreeNew(strcasecmp);
	testnames = xtreeNew(strcasecmp);
	locations = xtreeNew(strcasecmp);

	for (argi=1; (argi < argc); argi++) {
		if (argnmatch(argv[argi], "--config=")) {
			configfn = strdup(strchr(argv[argi], '=')+1);
		}
		else if (argnmatch(argv[argi], "--checkpoint-file=")) {
			checkfn = strdup(strchr(argv[argi], '=')+1);
		}
		else if (argnmatch(argv[argi], "--checkpoint-interval=")) {
			char *p = strchr(argv[argi], '=') + 1;
			checkpointinterval = atoi(p);
		}
		else if (argnmatch(argv[argi], "--dump-config")) {
			load_alertconfig(configfn, alertcolors, alertinterval);
			dump_alertconfig(1);
			return 0;
		}
		else if (argnmatch(argv[argi], "--cfid")) {
			include_configid = 1;
		}
		else if (argnmatch(argv[argi], "--test")) {
			char *testhost = NULL, *testservice = NULL, *testpage = NULL, 
			     *testcolor = "red", *testgroups = NULL;
			void *hinfo;
			int testdur = 0;
			FILE *logfd = NULL;
			activealerts_t *awalk = NULL;
			int paramno = 0;

			argi++; if (argi < argc) testhost = argv[argi];
			argi++; if (argi < argc) testservice = argv[argi];
			argi++; 
			while (argi < argc) {
				if (strncasecmp(argv[argi], "--duration=", 11) == 0) {
					testdur = durationvalue(strchr(argv[argi], '=')+1);
				}
				else if (strncasecmp(argv[argi], "--color=", 8) == 0) {
					testcolor = strchr(argv[argi], '=')+1;
				}
				else if (strncasecmp(argv[argi], "--group=", 8) == 0) {
					testgroups = strchr(argv[argi], '=')+1;
				}
				else if (strncasecmp(argv[argi], "--time=", 7) == 0) {
					fakestarttime = (time_t)atoi(strchr(argv[argi], '=')+1);
				}
				else {
					paramno++;
					if (paramno == 1) testdur = atoi(argv[argi]);
					else if (paramno == 2) testcolor = argv[argi];
					else if (paramno == 3) fakestarttime = (time_t) atoi(argv[argi]);
				}

				argi++;
			}

			if ((testhost == NULL) || (testservice == NULL)) {
				printf("Usage: xymond_alert --test HOST SERVICE [options]\n");
				printf("Possible options:\n\t[--duration=MINUTES]\n\t[--color=COLOR]\n\t[--group=GROUPNAME]\n\t[--time=TIMESPEC]\n");

				return 1;
			}

			load_hostnames(xgetenv("HOSTSCFG"), NULL, get_fqdn());
			hinfo = hostinfo(testhost);
			if (hinfo) {
				testpage = strdup(xmh_item(hinfo, XMH_ALLPAGEPATHS));
			}
			else {
				errprintf("Host not found in hosts.cfg - assuming it is on the top page\n");
				testpage = "";
			}

			awalk = (activealerts_t *)calloc(1, sizeof(activealerts_t));
			awalk->hostname = find_name(hostnames, testhost);
			awalk->testname = find_name(testnames, testservice);
			awalk->location = find_name(locations, testpage);
			awalk->ip = strdup("127.0.0.1");
			awalk->color = awalk->maxcolor = parse_color(testcolor);
			awalk->pagemessage = "Test of the alert configuration";
			awalk->eventstart = getcurrenttime(NULL) - testdur*60;
			awalk->groups = (testgroups ? strdup(testgroups) : NULL);
			awalk->state = A_PAGING;
			awalk->cookie = 12345;
			awalk->next = NULL;

			logfd = fopen("/dev/null", "w");
			starttrace(NULL);
			testonly = 1;

			load_alertconfig(configfn, alertcolors, alertinterval);
			load_holidays(0);
			send_alert(awalk, logfd);
			return 0;
		}
		else if (argnmatch(argv[argi], "--trace=")) {
			tracefn = strdup(strchr(argv[argi], '=')+1);
			starttrace(tracefn);
		}
		else if (net_worker_option(argv[argi])) {
			/* Handled in the subroutine */
		}
		else if (standardoption(argv[argi])) {
			if (showhelp) return 0;
		}
		else {
			errprintf("Unknown option '%s'\n", argv[argi]);
		}
	}

	/* Do the network stuff if needed */
	net_worker_run(ST_ALERT, LOC_SINGLESERVER, NULL);

	if (checkfn) {
		load_checkpoint(checkfn);
		nextcheckpoint = gettimer() + checkpointinterval;
		dbgprintf("Next checkpoint at %d, interval %d\n", (int) nextcheckpoint, checkpointinterval);
	}

	setup_signalhandler("xymond_alert");
	/* Need to handle these ourselves, so we can shutdown and save state-info */
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sig_handler;
	sigaction(SIGPIPE, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGINT,  &sa, NULL);
	sigaction(SIGCHLD, &sa, NULL);
	sigaction(SIGUSR1, &sa, NULL);
	sigaction(SIGHUP,  &sa, NULL);

	if (xgetenv("XYMONSERVERLOGS")) {
		sprintf(acklogfn, "%s/acknowledge.log", xgetenv("XYMONSERVERLOGS"));
		acklogfd = fopen(acklogfn, "a");
		sprintf(notiflogfn, "%s/notifications.log", xgetenv("XYMONSERVERLOGS"));
		notiflogfd = fopen(notiflogfn, "a");
	}

	/*
	 * The general idea here is that this loop handles receiving of alert-
	 * and ack-messages from the master daemon, and maintains a list of 
	 * host+test combinations that may have alerts going out.
	 *
	 * This module does not deal with any specific alert-configuration, 
	 * it just picks up the alert messages, maintains the list of 
	 * known tests that are in some sort of critical condition, and
	 * periodically pushes alerts to the do_alert.c module for handling.
	 *
	 * The only modification of alerts that happen here is the handling
	 * of when the next alert is due. It calls into the next_alert() 
	 * routine to learn when an alert should be repeated, and also 
	 * deals with Acknowledgments that stop alerts from going out for
	 * a period of time.
	 */
	while (running) {
		char *eoln, *restofmsg;
		char *metadata[20];
		char *p;
		int metacount;
		char *hostname = NULL, *testname = NULL;
		struct timespec timeout;
		time_t now, nowtimer;
		int anytogo;
		activealerts_t *awalk;
		int childstat;

		nowtimer = gettimer();
		if (checkfn && (nowtimer > nextcheckpoint)) {
			dbgprintf("Saving checkpoint\n");
			nextcheckpoint = nowtimer + checkpointinterval;
			save_checkpoint(checkfn);

			if (acklogfd) acklogfd = freopen(acklogfn, "a", acklogfd);
			if (notiflogfd) notiflogfd = freopen(notiflogfn, "a", notiflogfd);
		}

		timeout.tv_sec = 60; timeout.tv_nsec = 0;
		msg = get_xymond_message(C_PAGE, "xymond_alert", &seq, &timeout);
		if (msg == NULL) {
			running = 0;
			continue;
		}

		/* See what time it is - must happen AFTER the timeout */
		now = getcurrenttime(NULL);

		/* Split the message in the first line (with meta-data), and the rest */
 		eoln = strchr(msg, '\n');
		if (eoln) {
			*eoln = '\0';
			restofmsg = eoln+1;
		}
		else {
			restofmsg = "";
		}

		/* 
		 * Now parse the meta-data into elements.
		 * We use our own "gettok()" routine which works
		 * like strtok(), but can handle empty elements.
		 */
		metacount = 0; 
		memset(&metadata, 0, sizeof(metadata));
		p = gettok(msg, "|");
		while (p && (metacount < 19)) {
			metadata[metacount] = p;
			metacount++;
			p = gettok(NULL, "|");
		}
		metadata[metacount] = NULL;

		if (metacount > 3) hostname = metadata[3];
		if (metacount > 4) testname = metadata[4];

		if ((metacount > 10) && (strncmp(metadata[0], "@@page", 6) == 0)) {
			/* @@page|timestamp|sender|hostname|testname|hostip|expiretime|color|prevcolor|changetime|location|cookie|osname|classname|grouplist|modifiers */

			int newcolor, newalertstatus, oldalertstatus;

			dbgprintf("Got page message from %s:%s\n", hostname, testname);
			traceprintf("@@page %s:%s:%s=%s\n", hostname, testname, metadata[10], metadata[7]);

			awalk = find_active(hostname, testname);
			if (awalk == NULL) {
				char *hwalk = find_name(hostnames, hostname);
				char *twalk = find_name(testnames, testname);
				char *pwalk = find_name(locations, metadata[10]);

				awalk = (activealerts_t *)calloc(1, sizeof(activealerts_t));
				awalk->hostname = hwalk;
				awalk->testname = twalk;
				awalk->location = pwalk;
				awalk->cookie = -1;
				awalk->state = A_DEAD;
				/*
				 * Use changetime here, if we restart the alert module then
				 * this gets the duration values more right than using "now".
				 * Also, define this only when a new alert arrives - we should
				 * NOT clear this when a status goes yellow->red, or if it
				 * flaps between yellow and red.
				 */
				awalk->eventstart = atoi(metadata[9]);
				add_active(awalk->hostname, awalk);
				traceprintf("New record\n");
			}

			newcolor = parse_color(metadata[7]);
			oldalertstatus = ((alertcolors & (1 << awalk->color)) != 0);
			newalertstatus = ((alertcolors & (1 << newcolor)) != 0);

			traceprintf("state %d->%d\n", oldalertstatus, newalertstatus);

			if (newalertstatus) {
				/* It's in an alert state. */
				awalk->color = newcolor;
				awalk->state = A_PAGING;

				if (newcolor > awalk->maxcolor) {
					if (awalk->maxcolor != 0) {
						/*
						 * Severity has increased (yellow -> red).
						 * Clear the repeat-interval, and set maxcolor to
						 * the new color. If it drops to yellow again,
						 * maxcolor stays at red, so a test that flaps
						 * between yellow and red will only alert on red
						 * the first time, and then follow the repeat
						 * interval.
						 */
						dbgprintf("Severity increased, cleared repeat interval: %s/%s %s->%s\n",
							awalk->hostname, awalk->testname,
							colorname(awalk->maxcolor), colorname(newcolor));
						clear_interval(awalk);
					}

					awalk->maxcolor = newcolor;
				}
			}
			else {
				/* 
				 * Send one "recovered" message out now, then go to A_DEAD.
				 * Dont update the color here - we want recoveries to go out 
				 * only if the alert color triggered an alert
				 */
				awalk->state = (newcolor == COL_BLUE) ? A_DISABLED : A_RECOVERED;
			}

			if (oldalertstatus != newalertstatus) {
				dbgprintf("Alert status changed from %d to %d\n", oldalertstatus, newalertstatus);
				clear_interval(awalk);
			}

			if (awalk->ip) xfree(awalk->ip);
			awalk->ip = strdup(metadata[5]);
			awalk->cookie = atoi(metadata[11]);
			if (awalk->osname) xfree(awalk->osname);
			awalk->osname    = (metadata[12] ? strdup(metadata[12]) : NULL);
			if (awalk->classname) xfree(awalk->classname);
			awalk->classname = (metadata[13] ? strdup(metadata[13]) : NULL);
			if (awalk->groups) xfree(awalk->groups);
			awalk->groups    = (metadata[14] ? strdup(metadata[14]) : NULL);
			if (awalk->pagemessage) xfree(awalk->pagemessage);
			if (metadata[15]) {
				/* Modifiers are more interesting than the message itself */
				awalk->pagemessage = (char *)malloc(strlen(awalk->hostname) + strlen(awalk->testname) + strlen(colorname(awalk->color)) + strlen(metadata[15]) + strlen(restofmsg) + 10);
				sprintf(awalk->pagemessage, "%s:%s %s\n%s\n%s",
					awalk->hostname, awalk->testname, colorname(awalk->color), metadata[15], restofmsg);
			}
			else {
				awalk->pagemessage = strdup(restofmsg);
			}
		}
		else if ((metacount > 5) && (strncmp(metadata[0], "@@ack", 5) == 0)) {
 			/* @@ack|timestamp|sender|hostname|testname|hostip|expiretime */

			/*
			 * An ack is handled simply by setting the next
			 * alert-time to when the ack expires.
			 */
			time_t nextalert = atoi(metadata[6]);

			dbgprintf("Got ack message from %s:%s\n", hostname, testname);
			traceprintf("@@ack: %s:%s now=%d, ackeduntil %d\n",
				     hostname, testname, (int)now, (int)nextalert);

			awalk = find_active(hostname, testname);

			if (acklogfd) {
				int cookie = (awalk ? awalk->cookie : -1);
				int color  = (awalk ? awalk->color : 0);

				fprintf(acklogfd, "%d\t%d\t%d\t%d\t%s\t%s.%s\t%s\t%s\n",
					(int)now, cookie, 
					(int)((nextalert - now) / 60), cookie,
					"np_filename_not_used", 
					hostname, testname, 
					colorname(color),
					nlencode(restofmsg));
				fflush(acklogfd);
			}

			if (awalk && (awalk->state == A_PAGING)) {
				traceprintf("Record updated\n");
				awalk->state = A_ACKED;
				awalk->nextalerttime = nextalert;
				if (awalk->ackmessage) xfree(awalk->ackmessage);
				awalk->ackmessage = strdup(restofmsg);
			}
			else {
				traceprintf("No record\n");
			}
		}
		else if ((metacount > 4) && (strncmp(metadata[0], "@@notify", 5) == 0)) {
			/* @@notify|timestamp|sender|hostname|testname|pagepath */

			char *hwalk = find_name(hostnames, hostname);
			char *twalk = find_name(testnames, testname);
			char *pwalk = find_name(locations, (metadata[5] ? metadata[5] : ""));

			awalk = (activealerts_t *)calloc(1, sizeof(activealerts_t));
			awalk->hostname = hwalk;
			awalk->testname = twalk;
			awalk->location = pwalk;
			awalk->cookie = -1;
			awalk->pagemessage = strdup(restofmsg);
			awalk->eventstart = getcurrenttime(NULL);
			awalk->state = A_NOTIFY;
			add_active(awalk->hostname, awalk);
		}
		else if ((metacount > 3) && 
			 ((strncmp(metadata[0], "@@drophost", 10) == 0) || (strncmp(metadata[0], "@@dropstate", 11) == 0))) {
			/* @@drophost|timestamp|sender|hostname */
			/* @@dropstate|timestamp|sender|hostname */
			xtreePos_t handle;

			handle = xtreeFind(hostnames, hostname);
			if (handle != xtreeEnd(hostnames)) {
				alertanchor_t *anchor = (alertanchor_t *)xtreeData(hostnames, handle);
				for (awalk = anchor->head; (awalk); awalk = awalk->next) awalk->state = A_DEAD;
			}
		}
		else if ((metacount > 4) && (strncmp(metadata[0], "@@droptest", 10) == 0)) {
			/* @@droptest|timestamp|sender|hostname|testname */

			awalk = find_active(hostname, testname);
			if (awalk) awalk->state = A_DEAD;
		}
		else if ((metacount > 4) && (strncmp(metadata[0], "@@renamehost", 12) == 0)) {
			/* @@renamehost|timestamp|sender|hostname|newhostname */

			/* 
			 * We handle rename's simply by dropping the alert. If there is still an
			 * active alert for the host, it will have to be dealt with when the next
			 * status update arrives.
			 */
			xtreePos_t handle;

			handle = xtreeFind(hostnames, hostname);
			if (handle != xtreeEnd(hostnames)) {
				alertanchor_t *anchor = (alertanchor_t *)xtreeData(hostnames, handle);
				for (awalk = anchor->head; (awalk); awalk = awalk->next) awalk->state = A_DEAD;
			}
		}
		else if ((metacount > 5) && (strncmp(metadata[0], "@@renametest", 12) == 0)) {
			/* @@renametest|timestamp|sender|hostname|oldtestname|newtestname */

			/* 
			 * We handle rename's simply by dropping the alert. If there is still an
			 * active alert for the host, it will have to be dealt with when the next
			 * status update arrives.
			 */
			awalk = find_active(hostname, testname);
			if (awalk) awalk->state = A_DEAD;
		}
		else if (strncmp(metadata[0], "@@shutdown", 10) == 0) {
			running = 0;
			errprintf("Got a shutdown message\n");
			continue;
		}
		else if (strncmp(metadata[0], "@@logrotate", 11) == 0) {
			char *fn = xgetenv("XYMONCHANNEL_LOGFILENAME");
			if (fn && strlen(fn)) {
				reopen_file(fn, "a", stdout);
				reopen_file(fn, "a", stderr);

				if (tracefn) {
					stoptrace();
					starttrace(tracefn);
				}
			}
			continue;
		}
		else if (strncmp(metadata[0], "@@reload", 8) == 0) {
			/* Nothing ... right now */
		}
		else if (strncmp(metadata[0], "@@idle", 6) == 0) {
			/* Timeout */
		}

		/*
		 * When a burst of alerts happen, we get lots of alert messages
		 * coming in quickly. So lets handle them in bunches and only 
		 * do the full alert handling once every 10 secs - that lets us
		 * combine a bunch of alerts into one transmission process.
		 */
		if (nowtimer < (lastxmit+10)) continue;
		lastxmit = nowtimer;

		/* 
		 * Loop through the activealerts list and see if anything is pending.
		 * This is an optimization, we could just as well just fork off the
		 * notification child and let it handle all of it. But there is no
		 * reason to fork a child process unless it is going to do something.
		 */
		configchanged = load_alertconfig(configfn, alertcolors, alertinterval);
		configchanged += load_holidays(0);
		anytogo = 0;
		for (awalk = alistBegin(); (awalk); awalk = alistNext()) {
			int anymatch = 0;

			switch (awalk->state) {
			  case A_NORECIP:
				if (!configchanged) break;

				/* The configuration has changed - switch NORECIP -> PAGING */
				awalk->state = A_PAGING;
				clear_interval(awalk);
				/* Fall through */

			  case A_PAGING:
				if (have_recipient(awalk, &anymatch)) {
					if (awalk->nextalerttime <= now) anytogo++;
				}
				else {
					if (!anymatch) {
						awalk->state = A_NORECIP;
						cleanup_alert(awalk);
					}
				}
				break;

			  case A_ACKED:
				if (awalk->nextalerttime <= now) {
					/* An ack has expired, so drop the ack message and switch to A_PAGING */
					anytogo++;
					if (awalk->ackmessage) xfree(awalk->ackmessage);
					awalk->state = A_PAGING;
				}
				break;

			  case A_RECOVERED:
			  case A_DISABLED:
			  case A_NOTIFY:
				anytogo++;
				break;

			  case A_DEAD:
				break;
			}
		}
		dbgprintf("%d alerts to go\n", anytogo);

		if (anytogo) {
			pid_t childpid;

			childpid = fork();
			if (childpid == 0) {
				/* The child */
				start_alerts();
				for (awalk = alistBegin(); (awalk); awalk = alistNext()) {
					switch (awalk->state) {
					  case A_PAGING:
						if (awalk->nextalerttime <= now) {
							send_alert(awalk, notiflogfd);
						}
						break;

					  case A_ACKED:
						/* Cannot be A_ACKED unless the ack is still valid, so no alert. */
						break;

					  case A_RECOVERED:
					  case A_DISABLED:
					  case A_NOTIFY:
						send_alert(awalk, notiflogfd);
						break;

					  case A_NORECIP:
					  case A_DEAD:
						break;
					}
				}
				finish_alerts();

				/* Child does not continue */
				exit(0);
			}
			else if (childpid < 0) {
				errprintf("Fork failed, cannot send alerts: %s\n", strerror(errno));
			}
		}

		/* Update the state flag and the next-alert timestamp */
		for (awalk = alistBegin(); (awalk); awalk = alistNext()) {
			switch (awalk->state) {
			  case A_PAGING:
				if (awalk->nextalerttime <= now) awalk->nextalerttime = next_alert(awalk);
				break;

			  case A_NORECIP:
				break;

			  case A_ACKED:
				/* Still cannot get here except if ack is still valid */
				break;

			  case A_RECOVERED:
			  case A_DISABLED:
			  case A_NOTIFY:
				awalk->state = A_DEAD;
				/* Fall through */

			  case A_DEAD:
				cleanup_alert(awalk); 
				break;
			}
		}

		clean_all_active();

		/* Pickup any finished child processes to avoid zombies */
		while (wait3(&childstat, WNOHANG, NULL) > 0) ;
	}

	if (checkfn) save_checkpoint(checkfn);
	if (acklogfd) fclose(acklogfd);
	if (notiflogfd) fclose(notiflogfd);
	stoptrace();

	MEMUNDEFINE(notiflogfn);
	MEMUNDEFINE(acklogfn);

	if (termsig >= 0) {
		errprintf("Terminated by signal %d\n", termsig);
	}

	return 0;
}
Exemple #7
0
static int net_worker_listener(char *ipport)
{
	/*
	 * Setup a listener socket on IP+port. When a connection arrives,
	 * pick it up, fork() and let the rest of the input go via the
	 * network socket.
	 */

	char *listenip, *p;
	int listenport = 0;
	int lsocket = -1;
	struct sockaddr_in laddr;
	struct sigaction sa;
	int opt;

	listenip = ipport;
	p = strchr(listenip, ':');
	if (p) {
		*p = '\0';
		listenport = atoi(p+1);
	}

	if (listenport == 0) {
		errprintf("Must include PORT number in --listen=IP:PORT option\n");
		return -1;
	}

        /* Set up a socket to listen for new connections */
	errprintf("Setting up network listener on %s:%d\n", listenip, listenport);
	memset(&laddr, 0, sizeof(laddr));
	if ((strlen(listenip) == 0) || (strcmp(listenip, "0.0.0.0") == 0)) {
		listenip = "0.0.0.0";
		laddr.sin_addr.s_addr = INADDR_ANY;
	}
	else if (inet_aton(listenip, (struct in_addr *) &laddr.sin_addr.s_addr) == 0) {
		/* Not an IP */
		errprintf("Listener IP must be an IP-address, not hostname\n");
		return -1;
	}
	laddr.sin_port = htons(listenport);
	laddr.sin_family = AF_INET;
	lsocket = socket(AF_INET, SOCK_STREAM, 0);
	if (lsocket == -1) {
		errprintf("Cannot create listen socket (%s)\n", strerror(errno));
		return -1;
	}

	opt = 1;
	setsockopt(lsocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	if (bind(lsocket, (struct sockaddr *)&laddr, sizeof(laddr)) == -1) {
		errprintf("Cannot bind to listen socket (%s)\n", strerror(errno));
		return -1;
	}
	if (listen(lsocket, 5) == -1) {
		errprintf("Cannot listen (%s)\n", strerror(errno));
		return -1;
	}

	/* Make listener socket non-blocking, so we can send keep-alive's while waiting for connections */
	fcntl(lsocket, F_SETFL, O_NONBLOCK);

	/* Catch some signals */
	setup_signalhandler("xymond_listener");
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = netinp_sighandler;
	sigaction(SIGCHLD, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGINT, &sa, NULL);

	while (running) {
		struct sockaddr_in netaddr;
		int addrsz, sock;
		int childstat;
		pid_t childpid;
		fd_set fdread;
		struct timeval tmo;
		int n;

		FD_ZERO(&fdread);
		FD_SET(lsocket, &fdread);
		tmo.tv_sec = 60; tmo.tv_usec = 0;
		n = select(lsocket+1, &fdread, NULL, NULL, &tmo);
		if (n == -1) {
			if (errno != EINTR) {
				errprintf("select() failed while waiting for connection : %s\n", strerror(errno));
				running = 0;
			}
		}
		else if (n == 0) {
			/* Timeout */
			net_worker_heartbeat();
		}
		else {
			/* We have a connection ready */
			addrsz = sizeof(netaddr);
			sock = accept(lsocket, (struct sockaddr *)&netaddr, &addrsz);

			if (sock >= 0) {
				/* Got a new connection */

				childpid = fork();
				if (childpid == 0) {
					/* Child takes input from the new socket, and starts working */
					close(lsocket);	/* Close the listener socket */
					inputfd = sock;
					return 0;
				}
				else if (childpid > 0) {
					/* Parent closes the new socket (child has it) */
					close(sock);
					continue;
				}
				else {
					errprintf("Error forking worker for new connection: %s\n", strerror(errno));
					running = 0;
					continue;
				}
			}
			else {
				/* Error while waiting for accept() to complete */
				if (errno != EINTR) {
					errprintf("accept() failed: %s\n", strerror(errno));
					running = 0;
				}
			}
		}

		/* Pickup failed children */
		while ((childpid = wait3(&childstat, WNOHANG, NULL)) > 0);
	}

	/* Close the listener socket */
	close(lsocket);

	/* Kill any children that are still around */
	kill(0, SIGTERM);

	return 1;
}
Exemple #8
0
int main(int argc, char *argv[])
{
	char *msg;
	int running;
	int argi, seq;
	int recentperiod = 3600;
	int maxrecentcount = 5;
	int logdirfull = 0;
	int minlogspace = 5;
	struct sigaction sa;

	/* Handle program options. */
	for (argi = 1; (argi < argc); argi++) {
                if (argnmatch(argv[argi], "--logdir=")) {
			clientlogdir = strchr(argv[argi], '=')+1;
		}
		else if (argnmatch(argv[argi], "--recent-period=")) {
			char *p = strchr(argv[argi], '=');
			recentperiod = 60*atoi(p+1);
		}
		else if (argnmatch(argv[argi], "--recent-count=")) {
			char *p = strchr(argv[argi], '=');
			maxrecentcount = atoi(p+1);
		}
		else if (argnmatch(argv[argi], "--minimum-free=")) {
			minlogspace = atoi(strchr(argv[argi], '=')+1);
		}
		else if (strcmp(argv[argi], "--debug") == 0) {
			/*
			 * A global "debug" variable is available. If
			 * it is set, then "dbgprintf()" outputs debug messages.
			 */
			debug = 1;
		}
		else if (net_worker_option(argv[argi])) {
			/* Handled in the subroutine */
		}
	}

	if (clientlogdir == NULL) clientlogdir = xgetenv("CLIENTLOGS");
	if (clientlogdir == NULL) {
		clientlogdir = (char *)malloc(strlen(xgetenv("XYMONVAR")) + 10);
		sprintf(clientlogdir, "%s/hostdata", xgetenv("XYMONVAR"));
	}

	save_errbuf = 0;

	/* Do the network stuff if needed */
	net_worker_run(ST_HOSTDATA, LOC_STICKY, update_locator_hostdata);

	setup_signalhandler("xymond_hostdata");
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sig_handler;
	signal(SIGCHLD, SIG_IGN);
	sigaction(SIGHUP, &sa, NULL);
	signal(SIGPIPE, SIG_DFL);

	savetimes = xtreeNew(strcasecmp);

	running = 1;
	while (running) {
		char *eoln, *restofmsg, *p;
		char *metadata[MAX_META+1];
		int metacount;

		msg = get_xymond_message(C_CLICHG, "xymond_hostdata", &seq, NULL);
		if (msg == NULL) {
			/*
			 * get_xymond_message will return NULL if xymond_channel closes
			 * the input pipe. We should shutdown when that happens.
			 */
			running = 0;
			continue;
		}

		if (nextfscheck < gettimer()) {
			logdirfull = (chkfreespace(clientlogdir, minlogspace, minlogspace) != 0);
			if (logdirfull) errprintf("Hostdata directory %s has less than %d%% free space - disabling save of data for 5 minutes\n", clientlogdir, minlogspace);
			nextfscheck = gettimer() + 300;
		}

		/* Split the message in the first line (with meta-data), and the rest */
 		eoln = strchr(msg, '\n');
		if (eoln) {
			*eoln = '\0';
			restofmsg = eoln+1;
		}
		else {
			restofmsg = "";
		}

		metacount = 0; 
		memset(&metadata, 0, sizeof(metadata));
		p = gettok(msg, "|");
		while (p && (metacount < MAX_META)) {
			metadata[metacount++] = p;
			p = gettok(NULL, "|");
		}
		metadata[metacount] = NULL;

		if (strncmp(metadata[0], "@@clichg", 8) == 0) {
			xtreePos_t handle;
			savetimes_t *itm;
			int i, recentcount;
			time_t now = gettimer();
			char hostdir[PATH_MAX];
			char fn[PATH_MAX];
			FILE *fd;

			/* metadata[3] is the hostname */
			handle = xtreeFind(savetimes, metadata[3]);
			if (handle != xtreeEnd(savetimes)) {
				itm = (savetimes_t *)xtreeData(savetimes, handle);
			}
			else {
				itm = (savetimes_t *)calloc(1, sizeof(savetimes_t));
				itm->hostname = strdup(metadata[3]);
				xtreeAdd(savetimes, itm->hostname, itm);
			}

			/* See how many times we've saved the hostdata recently (within the past 'recentperiod' seconds) */
			for (i=0, recentcount=0; ((i < 12) && (itm->tstamp[i] > (now - recentperiod))); i++) recentcount++;
			/* If it's been saved less than 'maxrecentcount' times, then save it. Otherwise just drop it */
			if (!logdirfull && (recentcount < maxrecentcount)) {
				int written, closestatus, ok = 1;

				for (i = 10; (i > 0); i--) itm->tstamp[i+1] = itm->tstamp[i];
				itm->tstamp[0] = now;

				sprintf(hostdir, "%s/%s", clientlogdir, metadata[3]);
				mkdir(hostdir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
				sprintf(fn, "%s/%s", hostdir, metadata[4]);
				fd = fopen(fn, "w");
				if (fd == NULL) {
					errprintf("Cannot create file %s: %s\n", fn, strerror(errno));
					continue;
				}
				written = fwrite(restofmsg, 1, strlen(restofmsg), fd);
				if (written != strlen(restofmsg)) {
					errprintf("Cannot write hostdata file %s: %s\n", fn, strerror(errno));
					closestatus = fclose(fd);	/* Ignore any close errors */
					ok = 0;
				}
				else {
					closestatus = fclose(fd);
					if (closestatus != 0) {
						errprintf("Cannot write hostdata file %s: %s\n", fn, strerror(errno));
						ok = 0;
					}
				}

				if (!ok) remove(fn);
			}
		}

		/*
		 * A "shutdown" message is sent when the master daemon
		 * terminates. The child workers should shutdown also.
		 */
		else if (strncmp(metadata[0], "@@shutdown", 10) == 0) {
			running = 0;
			continue;
		}
		else if (strncmp(metadata[0], "@@idle", 6) == 0) {
			/* Ignored */
			continue;
		}

		/*
		 * A "logrotate" message is sent when the Xymon logs are
		 * rotated. The child workers must re-open their logfiles,
		 * typically stdin and stderr - the filename is always
		 * provided in the XYMONCHANNEL_LOGFILENAME environment.
		 */
		else if (strncmp(metadata[0], "@@logrotate", 11) == 0) {
			char *fn = xgetenv("XYMONCHANNEL_LOGFILENAME");
			if (fn && strlen(fn)) {
				reopen_file(fn, "a", stdout);
				reopen_file(fn, "a", stderr);
			}
			continue;
		}

		else if ((metacount > 3) && (strncmp(metadata[0], "@@drophost", 10) == 0)) {
			/* @@drophost|timestamp|sender|hostname */
			char hostdir[PATH_MAX];
			snprintf(hostdir, sizeof(hostdir), "%s/%s", clientlogdir, basename(metadata[3]));
			dropdirectory(hostdir, 1);
		}

		else if ((metacount > 4) && (strncmp(metadata[0], "@@renamehost", 12) == 0)) {
			/* @@renamehost|timestamp|sender|hostname|newhostname */
			char oldhostdir[PATH_MAX], newhostdir[PATH_MAX];
			snprintf(oldhostdir, sizeof(oldhostdir), "%s/%s", clientlogdir, basename(metadata[3]));
			snprintf(newhostdir, sizeof(newhostdir), "%s/%s", clientlogdir, basename(metadata[4]));
			rename(oldhostdir, newhostdir);

			if (net_worker_locatorbased()) locator_rename_host(metadata[3], metadata[4], ST_HOSTDATA);
		}
		else if (strncmp(metadata[0], "@@reload", 8) == 0) {
			/* Do nothing */
		}
	}

	return 0;
}
Exemple #9
0
int main(int argc, char *argv[])
{
	int argi;
	struct sigaction sa;
	namelist_t *hostwalk;
	time_t nexttimeout;

	for (argi=1; (argi < argc); argi++) {
		if (argnmatch(argv[argi], "--server=")) {
			char *p = strchr(argv[argi], '=');
			serverip = strdup(p+1);
		}
		else if (argnmatch(argv[argi], "--interval=")) {
			char *p = strchr(argv[argi], '=');
			pollinterval = atoi(p+1);
		}
		else if (argnmatch(argv[argi], "--log-interval=")) {
			char *p = strchr(argv[argi], '=');
			errorloginterval = atoi(p+1);
		}
		else if (argnmatch(argv[argi], "--id=")) {
			char *p = strchr(argv[argi], '=');
			serverid = atoi(p+1);
		}
		else if (strcmp(argv[argi], "--debug") == 0) {
			debug = 1;
		}
	}

	setup_signalhandler("hobbitfetch");
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sigmisc_handler;
	sigaction(SIGHUP, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGUSR1, &sa, NULL);	/* SIGUSR1 triggers logging of active requests */

	clients = rbtNew(name_compare);
	nexttimeout = time(NULL) + 60;

	{
		/* Seed the random number generator */
		struct timeval tv;
		struct timezone tz;

		gettimeofday(&tv, &tz);
		srandom(tv.tv_usec);
	}

	do {
		RbtIterator handle;
		conn_t *connwalk, *cprev;
		fd_set fdread, fdwrite;
		int n, maxfd;
		struct timeval tmo;
		time_t now;
		
		now = time(NULL);
		if (now > reloadtime) {
			/* Time to reload the bb-hosts file */
			reloadtime = now + 600;

			load_hostnames(xgetenv("BBHOSTS"), NULL, get_fqdn());
			for (hostwalk = first_host(); (hostwalk); hostwalk = hostwalk->next) {
				char *hname;
				clients_t *newclient;

				if (!bbh_item(hostwalk, BBH_FLAG_PULLDATA)) continue;

				hname = bbh_item(hostwalk, BBH_HOSTNAME);
				handle = rbtFind(clients, hname);
				if (handle == rbtEnd(clients)) {
					newclient = (clients_t *)calloc(1, sizeof(clients_t));
					newclient->hostname = strdup(hname);
					rbtInsert(clients, newclient->hostname, newclient);
					whentoqueue = now;
				}
			}
		}

		now = time(NULL);
		if (now > nexttimeout) {
			/* Check for connections that have timed out */
			nexttimeout = now + 60;

			for (connwalk = chead; (connwalk); connwalk = connwalk->next) {
				if ((connwalk->tstamp + 60) < now) {
					if (debug || (connwalk->client->nexterrortxt < now)) {
						errprintf("Timeout while talking to %s (req %lu): Aborting session\n",
							  addrstring(&connwalk->caddr), connwalk->seq);
						connwalk->client->nexterrortxt = now + errorloginterval;
					}
					flag_cleanup(connwalk);
				}
			}
		}

		if (needcleanup) {
			/* Remove any finished requests */
			needcleanup = 0;
			connwalk = chead; cprev = NULL;
			dbgprintf("Doing cleanup\n");

			while (connwalk) {
				conn_t *zombie;

				if ((connwalk->action == C_READING) || (connwalk->action == C_WRITING)) {
					/* Active connection - skip to the next conn_t record */
					cprev = connwalk;
					connwalk = connwalk->next;
					continue;
				}

				if (connwalk->action == C_CLEANUP) {
					if (connwalk->ctype == C_CLIENT) {
						/* 
						 * Finished getting data from a client, 
						 * flag idle and set next poll time.
						 */
						connwalk->client->busy = 0;
						set_polltime(connwalk->client);
					}
					else if (connwalk->ctype == C_SERVER) {
						/* Nothing needed for server cleanups */
					}
				}

				/* Unlink the request from the list of active connections */
				zombie = connwalk;
				if (cprev == NULL) {
					chead = zombie->next;
					connwalk = chead;
					cprev = NULL;
				}
				else {
					cprev->next = zombie->next;
					connwalk = zombie->next;
				}

				/* Purge the zombie */
				dbgprintf("Request completed: req %lu, peer %s, action was %d, type was %d\n", 
					zombie->seq, addrstring(&zombie->caddr), 
					zombie->action, zombie->ctype);
				close(zombie->sockfd);
				freestrbuffer(zombie->msgbuf);
				xfree(zombie);
			}

			/* Set the tail pointer correctly */
			ctail = chead;
			if (ctail) { while (ctail->next) ctail = ctail->next; }
		}

		if (dumpsessions) {
			/* Set by SIGUSR1 - dump the list of active requests */
			dumpsessions = 0;
			for (connwalk = chead; (connwalk); connwalk = connwalk->next) {
				char *ctypestr, *actionstr;
				char timestr[30];

				switch (connwalk->ctype) {
				  case C_CLIENT: ctypestr = "client"; break;
				  case C_SERVER: ctypestr = "server"; break;
				}

				switch (connwalk->action) {
				  case C_READING: actionstr = "reading"; break;
				  case C_WRITING: actionstr = "writing"; break;
				  case C_CLEANUP: actionstr = "cleanup"; break;
				}

				strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S",
					 localtime(&connwalk->tstamp));

				errprintf("Request %lu: state %s/%s, peer %s, started %s (%lu secs ago)\n",
					  connwalk->seq, ctypestr, actionstr, addrstring(&connwalk->caddr),
					  timestr, (now - connwalk->tstamp));
			}
		}

		now = time(NULL);
		if (now >= whentoqueue) {
			/* Scan host-tree for clients we need to contact */
			for (handle = rbtBegin(clients); (handle != rbtEnd(clients)); handle = rbtNext(clients, handle)) {
				clients_t *clientwalk;
				char msgline[100];
				strbuffer_t *request;
				char *pullstr, *ip;
				int port;

				clientwalk = (clients_t *)gettreeitem(clients, handle);
				if (clientwalk->busy) continue;
				if (clientwalk->nextpoll > now) continue;

				/* Deleted hosts stay in our tree - but should disappear from the known hosts */
				hostwalk = hostinfo(clientwalk->hostname); if (!hostwalk) continue;
				pullstr = bbh_item(hostwalk, BBH_FLAG_PULLDATA); if (!pullstr) continue;

				ip = strchr(pullstr, '=');
				port = atoi(xgetenv("BBPORT"));

				if (!ip) {
					ip = strdup(bbh_item(hostwalk, BBH_IP));
				}
				else {
					/* There is an explicit IP setting in the pulldata tag */
					char *p;

					ip++; /* Skip the '=' */
					ip = strdup(ip);
					p = strchr(ip, ':');
					if (p) { *p = '\0'; port = atoi(p+1); }

					if (*ip == '\0') {
						/* No IP given, just a port number */
						xfree(ip);
						ip = strdup(bbh_item(hostwalk, BBH_IP));
					}
				}

				if (strcmp(ip, "0.0.0.0") == 0) {
					struct hostent *hent;

					xfree(ip); ip = NULL;
					hent = gethostbyname(clientwalk->hostname);
					if (hent) {
						struct in_addr addr;

						memcpy(&addr, *(hent->h_addr_list), sizeof(addr));
						ip = strdup(inet_ntoa(addr));
					}
				}

				if (!ip) continue;

				/* 
				 * Build the "pullclient" request, which includes the latest
				 * clientdata config we got from the server. Keep the clientdata
				 * here - we send "pullclient" requests more often that we actually
				 * contact the server, but we should provide the config data always.
				 */
				request = newstrbuffer(0);
				sprintf(msgline, "pullclient %d\n", serverid);
				addtobuffer(request, msgline);
				if (clientwalk->clientdata) addtobuffer(request, clientwalk->clientdata);

				/* Put the request on the connection queue */
				addrequest(C_CLIENT, ip, port, request, clientwalk);
				clientwalk->busy = 1;

				xfree(ip);
			}
		}

		/* Handle request queue */
		FD_ZERO(&fdread);
		FD_ZERO(&fdwrite);
		maxfd = -1;
		for (connwalk = chead; (connwalk); connwalk = connwalk->next) {
			switch (connwalk->action) {
			  case C_READING: 
				FD_SET(connwalk->sockfd, &fdread);
				if (connwalk->sockfd > maxfd) maxfd = connwalk->sockfd;
				break;

			  case C_WRITING: 
				FD_SET(connwalk->sockfd, &fdwrite);
				if (connwalk->sockfd > maxfd) maxfd = connwalk->sockfd;
				break;

			  case C_CLEANUP:
				break;
			}
		}

		/* Do select with a 1 second timeout */
		tmo.tv_sec = 1;
		tmo.tv_usec = 0;
		n = select(maxfd+1, &fdread, &fdwrite, NULL, &tmo);

		if (n == -1) {
			if (errno == EINTR) continue;	/* Interrupted, e.g. a SIGHUP */

			/* This is a "cannot-happen" failure. Bail out */
			errprintf("select failure: %s\n", strerror(errno));
			return 0;
		}

		if (n == 0) continue;	/* Timeout */

		for (connwalk = chead; (connwalk); connwalk = connwalk->next) {
			switch (connwalk->action) {
			  case C_READING: 
				if (FD_ISSET(connwalk->sockfd, &fdread)) grabdata(connwalk);
				break;

			  case C_WRITING: 
				if (FD_ISSET(connwalk->sockfd, &fdwrite)) senddata(connwalk);
				break;

			  case C_CLEANUP:
				break;
			}
		}

	} while (running);

	return 0;
}
Exemple #10
0
int main(int argc, char *argv[])
{
	tasklist_t *twalk, *dwalk;
	grouplist_t *gwalk;
	int argi;
	int daemonize = 1;
	int verbose = 0;
	char *config = "/etc/tasks.cfg";
	char *logfn = NULL;
	char *pidfn = NULL;
	pid_t cpid;
	int status;
	struct sigaction sa;
	char *envarea = NULL;

	for (argi=1; (argi < argc); argi++) {
		if (strcmp(argv[argi], "--debug") == 0) {
			debug = 1;
		}
		else if (strcmp(argv[argi], "--no-daemon") == 0) {
			daemonize = 0;
		}
		else if (strcmp(argv[argi], "--verbose") == 0) {
			verbose = 1;
		}
		else if (argnmatch(argv[argi], "--config=")) {
			char *p = strchr(argv[argi], '=');
			config = strdup(expand_env(p+1));
		}
		else if (argnmatch(argv[argi], "--log=")) {
			char *p = strchr(argv[argi], '=');
			logfn = strdup(expand_env(p+1));
		}
		else if (argnmatch(argv[argi], "--area=")) {
			char *p = strchr(argv[argi], '=');
			envarea = strdup(p+1);
		}
		else if (argnmatch(argv[argi], "--env=")) {
			char *p = strchr(argv[argi], '=');
			loadenv(p+1, envarea);
		}
		else if (argnmatch(argv[argi], "--pidfile=")) {
			char *p = strchr(argv[argi], '=');
			pidfn = strdup(expand_env(p+1));
		}
		else if (strcmp(argv[argi], "--dump") == 0) {
			/* Dump configuration */
			forcereload=1;
			load_config(config);
			forcereload=0;
			for (gwalk = grouphead; (gwalk); gwalk = gwalk->next) {
				if (gwalk->maxuse > 1) printf("GROUP %s %d\n", gwalk->groupname, gwalk->maxuse);
			}
			printf("\n");
			for (twalk = taskhead; (twalk); twalk = twalk->next) {
				printf("[%s]\n", twalk->key);
				printf("\tCMD %s\n", twalk->cmd);
				if (twalk->disabled)     printf("\tDISABLED\n");
				if (twalk->group)        printf("\tGROUP %s\n", twalk->group->groupname);
				if (twalk->depends)      printf("\tNEEDS %s\n", twalk->depends->key);
				if (twalk->interval > 0) printf("\tINTERVAL %d\n", twalk->interval);
				if (twalk->cronstr)      printf("\tCRONDATE %s\n", twalk->cronstr);
				if (twalk->maxruntime)   printf("\tMAXTIME %d\n", twalk->maxruntime);
				if (twalk->logfile)      printf("\tLOGFILE %s\n", twalk->logfile);
				if (twalk->envfile)      printf("\tENVFILE %s\n", twalk->envfile);
				if (twalk->envarea)      printf("\tENVAREA %s\n", twalk->envarea);
				if (twalk->onhostptn)    printf("\tONHOST %s\n", twalk->onhostptn);
				printf("\n");
			}
			fflush(stdout);
			return 0;
		}
		else {
			fprintf(stderr,"%s: Unsupported argument: %s\n",argv[0],argv[argi]);
			fflush(stderr);
			return 1;
		}
	}

	/* Go daemon */
	if (daemonize) {
		pid_t childpid;

		/* Become a daemon */
		childpid = fork();
		if (childpid < 0) {
			/* Fork failed */
			errprintf("Could not fork child\n");
			exit(1);
		}
		else if (childpid > 0) {
			/* Parent exits */
			if (pidfn) {
				FILE *pidfd = fopen(pidfn, "w");

				if (pidfd) {
					fprintf(pidfd, "%d\n", (int)childpid);
					fclose(pidfd);
				}
			}

			exit(0);
		}
		/* Child (daemon) continues here */
		setsid();
	}

	/* If using a logfile, switch stdout and stderr to go there */
	if (logfn) {
		/* Should we close stdin here ? No ... */
		reopen_file("/dev/null", "r", stdin);
		reopen_file(logfn, "a", stdout);
		reopen_file(logfn, "a", stderr);
	}

	save_errbuf = 0;
	setup_signalhandler("xymonlaunch");
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sig_handler;
	sigaction(SIGHUP, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGCHLD, &sa, NULL);

	errprintf("xymonlaunch starting\n");
	while (running) {
		time_t now = gettimer();

		if (now >= nextcfgload) {
			load_config(config);
			nextcfgload = (now + 30);
		}

		if (logfn && dologswitch) {
			reopen_file(logfn, "a", stdout);
			reopen_file(logfn, "a", stderr);
			dologswitch = 0;
		}

		/* Pick up children that have terminated */
		while ((cpid = wait3(&status, WNOHANG, NULL)) > 0) {
			for (twalk = taskhead; (twalk && (twalk->pid != cpid)); twalk = twalk->next);
			if (twalk) {
				twalk->pid = 0;
				twalk->beingkilled = 0;
				if (WIFEXITED(status)) {
					twalk->exitcode = WEXITSTATUS(status);
					if (twalk->exitcode) {
						errprintf("Task %s terminated, status %d\n", twalk->key, twalk->exitcode);
						twalk->failcount++;
					}
					else {
						twalk->failcount = 0;
					}
				}
				else if (WIFSIGNALED(status)) {
					twalk->exitcode = -WTERMSIG(status);
					twalk->failcount++;
					errprintf("Task %s terminated by signal %d\n", twalk->key, abs(twalk->exitcode));
				}

				if (twalk->group) twalk->group->currentuse--;

				/* Tasks that depend on this task should be killed ... */
				for (dwalk = taskhead; (dwalk); dwalk = dwalk->next) {
					if ((dwalk->depends == twalk) && (dwalk->pid > 0)) {
						dwalk->beingkilled = 1;
						kill(dwalk->pid, SIGTERM);
					}
				}
			}
		}

		/* See what new tasks need to get going */
		dbgprintf("\n");
		dbgprintf("Starting tasklist scan\n");
		crongettime();
		for (twalk = taskhead; (twalk); twalk = twalk->next) {
			if ( (twalk->pid == 0) && !twalk->disabled && 
			       ( ((twalk->interval >= 0) && (now >= (twalk->laststart + twalk->interval))) || /* xymon interval condition */
			         (twalk->crondate && ((twalk->laststart + 55) < now) && cronmatch(twalk->crondate)) /* cron date */
			       ) 
			   ) {
				if (twalk->depends && ((twalk->depends->pid == 0) || (twalk->depends->laststart > (now - 5)))) {
					dbgprintf("Postponing start of %s due to %s not yet running\n",
						twalk->key, twalk->depends->key);
					continue;
				}

				if (twalk->group && (twalk->group->currentuse >= twalk->group->maxuse)) {
					dbgprintf("Postponing start of %s due to group %s being busy\n",
						twalk->key, twalk->group->groupname);
					continue;
				}

				if ((twalk->failcount > MAX_FAILS) && ((twalk->laststart + 600) < now)) {
					dbgprintf("Releasing %s from failure hold\n", twalk->key);
					twalk->failcount = 0;
				}

				if (twalk->failcount > MAX_FAILS) {
					dbgprintf("Postponing start of %s due to multiple failures\n", twalk->key);
					continue;
				}

				if (twalk->laststart > (now - 5)) {
					dbgprintf("Postponing start of %s, will not try more than once in 5 seconds\n", twalk->key);
					continue;
				}

				dbgprintf("About to start task %s\n", twalk->key);

				twalk->laststart = now;
				twalk->pid = fork();
				if (twalk->pid == 0) {
					/* Exec the task */
					char *cmd;
					char **cmdargs = NULL;
					static char tasksleepenv[20],bbsleepenv[20];

					/* Setup environment */
					if (twalk->envfile) {
						dbgprintf("%s -> Loading environment from %s area %s\n", 
							twalk->key, expand_env(twalk->envfile), 
							(twalk->envarea ? twalk->envarea : ""));
						loadenv(expand_env(twalk->envfile), twalk->envarea);
					}

					/* Setup TASKSLEEP to match the interval */
					sprintf(tasksleepenv, "TASKSLEEP=%d", twalk->interval);
					sprintf(bbsleepenv, "BBSLEEP=%d", twalk->interval);	/* For compatibility */
					putenv(tasksleepenv); putenv(bbsleepenv);

					/* Setup command line and arguments */
					cmdargs = setup_commandargs(twalk->cmd, &cmd);

					/* Point stdout/stderr to a logfile, if requested */
					if (twalk->logfile) {
						char *logfn = expand_env(twalk->logfile);

						dbgprintf("%s -> Assigning stdout/stderr to log '%s'\n", twalk->key, logfn);

						reopen_file(logfn, "a", stdout);
						reopen_file(logfn, "a", stderr);
					}

					/* Go! */
					dbgprintf("%s -> Running '%s', XYMONHOME=%s\n", twalk->key, cmd, xgetenv("XYMONHOME"));
					execvp(cmd, cmdargs);

					/* Should never go here */
					errprintf("Could not start task %s using command '%s': %s\n", 
						   twalk->key, cmd, strerror(errno));
					exit(0);
				}
				else if (twalk->pid == -1) {
					/* Fork failed */
					errprintf("Fork failed!\n");
					twalk->pid = 0;
				}
				else {
					if (twalk->group) twalk->group->currentuse++;
					if (verbose) errprintf("Task %s started with PID %d\n", twalk->key, (int)twalk->pid);
				}
			}
			else if (twalk->pid > 0) {
				dbgprintf("Task %s active with PID %d\n", twalk->key, (int)twalk->pid);
				if (twalk->maxruntime && ((now - twalk->laststart) > twalk->maxruntime)) {
					errprintf("Killing hung task %s (PID %d) after %d seconds\n",
						  twalk->key, (int)twalk->pid,
						  (now - twalk->laststart));
					kill(twalk->pid, (twalk->beingkilled ? SIGKILL : SIGTERM));
					twalk->beingkilled = 1; /* Next time it's a real kill */
				}
			}
		}

		sleep(5);
	}

	/* Shutdown running tasks */
	for (twalk = taskhead; (twalk); twalk = twalk->next) {
		if (twalk->pid) kill(twalk->pid, SIGTERM);
	}

	if (pidfn) unlink(pidfn);

	return 0;
}
Exemple #11
0
int main(int argc, char *argv[])
{
	time_t starttime = time(NULL);
	char *histdir = NULL;
	char *histlogdir = NULL;
	char *msg;
	int argi, seq;
	int save_allevents = 1;
	int save_hostevents = 1;
	int save_statusevents = 1;
	int save_histlogs = 1;
	FILE *alleventsfd = NULL;
	int running = 1;
	struct sigaction sa;
	char newcol2[3];
	char oldcol2[3];

	MEMDEFINE(newcol2);
	MEMDEFINE(oldcol2);

	/* Dont save the error buffer */
	save_errbuf = 0;

	if (xgetenv("BBALLHISTLOG")) save_allevents = (strcmp(xgetenv("BBALLHISTLOG"), "TRUE") == 0);
	if (xgetenv("BBHOSTHISTLOG")) save_hostevents = (strcmp(xgetenv("BBHOSTHISTLOG"), "TRUE") == 0);
	if (xgetenv("SAVESTATUSLOG")) save_histlogs = (strcmp(xgetenv("SAVESTATUSLOG"), "TRUE") == 0);

	for (argi = 1; (argi < argc); argi++) {
		if (argnmatch(argv[argi], "--histdir=")) {
			histdir = strchr(argv[argi], '=')+1;
		}
		else if (argnmatch(argv[argi], "--histlogdir=")) {
			histlogdir = strchr(argv[argi], '=')+1;
		}
		else if (argnmatch(argv[argi], "--debug")) {
			debug = 1;
		}
	}

	if (xgetenv("BBHIST") && (histdir == NULL)) {
		histdir = strdup(xgetenv("BBHIST"));
	}
	if (histdir == NULL) {
		errprintf("No history directory given, aborting\n");
		return 1;
	}

	if (save_histlogs && (histlogdir == NULL) && xgetenv("BBHISTLOGS")) {
		histlogdir = strdup(xgetenv("BBHISTLOGS"));
	}
	if (save_histlogs && (histlogdir == NULL)) {
		errprintf("No history-log directory given, aborting\n");
		return 1;
	}

	if (save_allevents) {
		char alleventsfn[PATH_MAX];

		MEMDEFINE(alleventsfn);

		sprintf(alleventsfn, "%s/allevents", histdir);
		alleventsfd = fopen(alleventsfn, "a");
		if (alleventsfd == NULL) {
			errprintf("Cannot open the all-events file '%s'\n", alleventsfn);
		}
		setvbuf(alleventsfd, (char *)NULL, _IOLBF, 0);

		MEMUNDEFINE(alleventsfn);
	}

	/* For picking up lost children */
	setup_signalhandler("hobbitd_history");
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sig_handler;
	sigaction(SIGCHLD, &sa, NULL);
	signal(SIGPIPE, SIG_DFL);

	while (running) {
		char *metadata[20] = { NULL, };
		int metacount;
		char *p;
		char *statusdata = "";
		char *hostname, *hostnamecommas, *testname, *dismsg;
		time_t tstamp, lastchg, disabletime, clienttstamp;
		int tstamp_i, lastchg_i;
		int newcolor, oldcolor;
		int downtimeactive;
		struct tm tstamptm;
		int trend;
		int childstat;

		/* Pickup any finished child processes to avoid zombies */
		while (wait3(&childstat, WNOHANG, NULL) > 0) ;

		msg = get_hobbitd_message(C_STACHG, "hobbitd_history", &seq, NULL);
		if (msg == NULL) {
			running = 0;
			continue;
		}

		p = strchr(msg, '\n'); 
		if (p) {
			*p = '\0'; 
			statusdata = msg_data(p+1);
		}
		metacount = 0;
		memset(&metadata, 0, sizeof(metadata));
		p = gettok(msg, "|");
		while (p && (metacount < 20)) {
			metadata[metacount++] = p;
			p = gettok(NULL, "|");
		}

		if ((metacount > 9) && (strncmp(metadata[0], "@@stachg", 8) == 0)) {
			/* @@stachg#seq|timestamp|sender|origin|hostname|testname|expiretime|color|prevcolor|changetime|disabletime|disablemsg|downtimeactive|clienttstamp */
			sscanf(metadata[1], "%d.%*d", &tstamp_i); tstamp = tstamp_i;
			hostname = metadata[4];
			testname = metadata[5];
			newcolor = parse_color(metadata[7]);
			oldcolor = parse_color(metadata[8]);
			lastchg  = atoi(metadata[9]);
			disabletime = atoi(metadata[10]);
			dismsg   = metadata[11];
			downtimeactive = (atoi(metadata[12]) > 0);
			clienttstamp = atoi(metadata[13]);

			if (newcolor == -1) {
				errprintf("Bad message: newcolor is unknown '%s'\n", metadata[7]);
				continue;
			}

			p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ',';

			if (save_statusevents) {
				char statuslogfn[PATH_MAX];
				int logexists;
				FILE *statuslogfd;
				char oldcol[100];
				char timestamp[40];
				struct stat st;

				MEMDEFINE(statuslogfn);
				MEMDEFINE(oldcol);
				MEMDEFINE(timestamp);

				sprintf(statuslogfn, "%s/%s.%s", histdir, hostnamecommas, testname);
				stat(statuslogfn, &st);
				statuslogfd = fopen(statuslogfn, "r+");
				logexists = (statuslogfd != NULL);
				*oldcol = '\0';

				if (logexists) {
					/*
					 * There is a fair chance hobbitd has not been
					 * running all the time while this system was monitored.
					 * So get the time of the latest status change from the file,
					 * instead of relying on the "lastchange" value we get
					 * from hobbitd. This is also needed when migrating from 
					 * standard bbd to hobbitd.
					 */
					off_t pos = -1;
					char l[1024];
					int gotit;

					MEMDEFINE(l);

					fseeko(statuslogfd, 0, SEEK_END);
					if (ftello(statuslogfd) > 512) {
						/* Go back 512 from EOF, and skip to start of a line */
						fseeko(statuslogfd, -512, SEEK_END);
						gotit = (fgets(l, sizeof(l)-1, statuslogfd) == NULL);
					}
					else {
						/* Read from beginning of file */
						fseeko(statuslogfd, 0, SEEK_SET);
						gotit = 0;
					}


					while (!gotit) {
						off_t tmppos = ftello(statuslogfd);
						time_t dur;
						int dur_i;

						if (fgets(l, sizeof(l)-1, statuslogfd)) {
							/* Sun Oct 10 06:49:42 2004 red   1097383782 602 */

							if ((strlen(l) > 24) && 
							    (sscanf(l+24, " %s %d %d", oldcol, &lastchg_i, &dur_i) == 2) &&
							    (parse_color(oldcol) != -1)) {
								/* 
								 * Record the start location of the line
								 */
								pos = tmppos;
								lastchg = lastchg_i;
								dur = dur_i;
							}
						}
						else {
							gotit = 1;
						}
					}

					if (pos == -1) {
						/* 
						 * Couldnt find anything in the log.
						 * Take lastchg from the timestamp of the logfile,
						 * and just append the data.
						 */
						lastchg = st.st_mtime;
						fseeko(statuslogfd, 0, SEEK_END);
					}
					else {
						/*
						 * lastchg was updated above.
						 * Seek to where the last line starts.
						 */
						fseeko(statuslogfd, pos, SEEK_SET);
					}

					MEMUNDEFINE(l);
				}
				else {
					/*
					 * Logfile does not exist.
					 */
					lastchg = tstamp;
					statuslogfd = fopen(statuslogfn, "a");
					if (statuslogfd == NULL) {
						errprintf("Cannot open status historyfile '%s' : %s\n", 
							statuslogfn, strerror(errno));
					}
				}

				if (strcmp(oldcol, colorname(newcolor)) == 0) {
					/* We wont update history unless the color did change. */
					if ((time(NULL) - starttime) > 300) {
						errprintf("Will not update %s - color unchanged (%s)\n", 
							  statuslogfn, oldcol);
					}

					if (hostnamecommas) xfree(hostnamecommas);
					if (statuslogfd) fclose(statuslogfd);

					MEMUNDEFINE(statuslogfn);
					MEMUNDEFINE(oldcol);
					MEMUNDEFINE(timestamp);

					continue;
				}

				if (statuslogfd) {
					if (logexists) {
						struct tm oldtm;

						/* Re-print the old record, now with the final duration */
						memcpy(&oldtm, localtime(&lastchg), sizeof(oldtm));
						strftime(timestamp, sizeof(timestamp), "%a %b %e %H:%M:%S %Y", &oldtm);
						fprintf(statuslogfd, "%s %s %d %d\n", 
							timestamp, oldcol, (int)lastchg, (int)(tstamp - lastchg));
					}

					/* And the new record. */
					memcpy(&tstamptm, localtime(&tstamp), sizeof(tstamptm));
					strftime(timestamp, sizeof(timestamp), "%a %b %e %H:%M:%S %Y", &tstamptm);
					fprintf(statuslogfd, "%s %s %d", timestamp, colorname(newcolor), (int)tstamp);

					fclose(statuslogfd);
				}

				MEMUNDEFINE(statuslogfn);
				MEMUNDEFINE(oldcol);
				MEMUNDEFINE(timestamp);
			}

			if (save_histlogs) {
				char *hostdash;
				char fname[PATH_MAX];
				FILE *histlogfd;

				MEMDEFINE(fname);

				p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_';
				sprintf(fname, "%s/%s", histlogdir, hostdash);
				mkdir(fname, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
				p = fname + sprintf(fname, "%s/%s/%s", histlogdir, hostdash, testname);
				mkdir(fname, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
				p += sprintf(p, "/%s", histlogtime(tstamp));

				histlogfd = fopen(fname, "w");
				if (histlogfd) {
					/*
					 * When a host gets disabled or goes purple, the status
					 * message data is not changed - so it will include a
					 * wrong color as the first word of the message.
					 * Therefore we need to fixup this so it matches the
					 * newcolor value.
					 */
					int txtcolor = parse_color(statusdata);
					char *origstatus = statusdata;

					if (txtcolor != -1) {
						fprintf(histlogfd, "%s", colorname(newcolor));
						statusdata += strlen(colorname(txtcolor));
					}

					if (dismsg && *dismsg) nldecode(dismsg);
					if (disabletime > 0) {
						fprintf(histlogfd, " Disabled until %s\n%s\n\n", 
							ctime(&disabletime), (dismsg ? dismsg : ""));
						fprintf(histlogfd, "Status message when disabled follows:\n\n");
						statusdata = origstatus;
					}
					else if (dismsg && *dismsg) {
						fprintf(histlogfd, " Planned downtime: %s\n\n", dismsg);
						fprintf(histlogfd, "Original status message follows:\n\n");
						statusdata = origstatus;
					}

					fwrite(statusdata, strlen(statusdata), 1, histlogfd);
					fprintf(histlogfd, "Status unchanged in 0.00 minutes\n");
					fprintf(histlogfd, "Message received from %s\n", metadata[2]);
					if (clienttstamp) fprintf(histlogfd, "Client data ID %d\n", (int) clienttstamp);
					fclose(histlogfd);
				}
				else {
					errprintf("Cannot create histlog file '%s' : %s\n", fname, strerror(errno));
				}
				xfree(hostdash);

				MEMUNDEFINE(fname);
			}

			strncpy(oldcol2, ((oldcolor >= 0) ? colorname(oldcolor) : "-"), 2);
			strncpy(newcol2, colorname(newcolor), 2);
			newcol2[2] = oldcol2[2] = '\0';

			if (oldcolor == -1)           trend = -1;	/* we dont know how bad it was */
			else if (newcolor > oldcolor) trend = 2;	/* It's getting worse */
			else if (newcolor < oldcolor) trend = 1;	/* It's getting better */
			else                          trend = 0;	/* Shouldn't happen ... */

			if (save_hostevents) {
				char hostlogfn[PATH_MAX];
				FILE *hostlogfd;

				MEMDEFINE(hostlogfn);

				sprintf(hostlogfn, "%s/%s", histdir, hostname);
				hostlogfd = fopen(hostlogfn, "a");
				if (hostlogfd) {
					fprintf(hostlogfd, "%s %d %d %d %s %s %d\n",
						testname, (int)tstamp, (int)lastchg, (int)(tstamp - lastchg),
						newcol2, oldcol2, trend);
					fclose(hostlogfd);
				}
				else {
					errprintf("Cannot open host logfile '%s' : %s\n", hostlogfn, strerror(errno));
				}

				MEMUNDEFINE(hostlogfn);
			}

			if (save_allevents) {
				fprintf(alleventsfd, "%s %s %d %d %d %s %s %d\n",
					hostname, testname, (int)tstamp, (int)lastchg, (int)(tstamp - lastchg),
					newcol2, oldcol2, trend);
				fflush(alleventsfd);
			}

			xfree(hostnamecommas);
		}
		else if ((metacount > 3) && ((strncmp(metadata[0], "@@drophost", 10) == 0))) {
			/* @@drophost|timestamp|sender|hostname */

			hostname = metadata[3];

			if (save_histlogs) {
				char *hostdash;
				char testdir[PATH_MAX];

				MEMDEFINE(testdir);

				/* Remove all directories below the host-specific histlog dir */
				p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_';
				sprintf(testdir, "%s/%s", histlogdir, hostdash);
				dropdirectory(testdir, 1);
				xfree(hostdash);

				MEMUNDEFINE(testdir);
			}

			if (save_hostevents) {
				char hostlogfn[PATH_MAX];
				struct stat st;

				MEMDEFINE(hostlogfn);

				sprintf(hostlogfn, "%s/%s", histdir, hostname);
				if ((stat(hostlogfn, &st) == 0) && S_ISREG(st.st_mode)) {
					unlink(hostlogfn);
				}

				MEMUNDEFINE(hostlogfn);
			}

			if (save_statusevents) {
				DIR *dirfd;
				struct dirent *de;
				char *hostlead;
				char statuslogfn[PATH_MAX];
				struct stat st;

				MEMDEFINE(statuslogfn);

				/* Remove bbvar/hist/host,name.* */
				p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ',';
				hostlead = malloc(strlen(hostname) + 2);
				strcpy(hostlead, hostnamecommas); strcat(hostlead, ".");

				dirfd = opendir(histdir);
				if (dirfd) {
					while ((de = readdir(dirfd)) != NULL) {
						if (strncmp(de->d_name, hostlead, strlen(hostlead)) == 0) {
							sprintf(statuslogfn, "%s/%s", histdir, de->d_name);
							if ((stat(statuslogfn, &st) == 0) && S_ISREG(st.st_mode)) {
								unlink(statuslogfn);
							}
						}
					}
					closedir(dirfd);
				}

				xfree(hostlead);
				xfree(hostnamecommas);

				MEMUNDEFINE(statuslogfn);
			}
		}
		else if ((metacount > 4) && ((strncmp(metadata[0], "@@droptest", 10) == 0))) {
			/* @@droptest|timestamp|sender|hostname|testname */

			hostname = metadata[3];
			testname = metadata[4];

			if (save_histlogs) {
				char *hostdash;
				char testdir[PATH_MAX];

				MEMDEFINE(testdir);

				p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_';
				sprintf(testdir, "%s/%s/%s", histlogdir, hostdash, testname);
				dropdirectory(testdir, 1);
				xfree(hostdash);

				MEMUNDEFINE(testdir);
			}

			if (save_statusevents) {
				char *hostnamecommas;
				char statuslogfn[PATH_MAX];
				struct stat st;

				MEMDEFINE(statuslogfn);

				p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ',';
				sprintf(statuslogfn, "%s/%s.%s", histdir, hostnamecommas, testname);
				if ((stat(statuslogfn, &st) == 0) && S_ISREG(st.st_mode)) unlink(statuslogfn);
				xfree(hostnamecommas);

				MEMUNDEFINE(statuslogfn);
			}
		}
		else if ((metacount > 4) && ((strncmp(metadata[0], "@@renamehost", 12) == 0))) {
			/* @@renamehost|timestamp|sender|hostname|newhostname */
			char *newhostname;

			hostname = metadata[3];
			newhostname = metadata[4];

			if (save_histlogs) {
				char *hostdash;
				char *newhostdash;
				char olddir[PATH_MAX];
				char newdir[PATH_MAX];

				MEMDEFINE(olddir); MEMDEFINE(newdir);

				p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_';
				p = newhostdash = strdup(newhostname); while ((p = strchr(p, '.')) != NULL) *p = '_';
				sprintf(olddir, "%s/%s", histlogdir, hostdash);
				sprintf(newdir, "%s/%s", histlogdir, newhostdash);
				rename(olddir, newdir);
				xfree(newhostdash);
				xfree(hostdash);

				MEMUNDEFINE(newdir); MEMUNDEFINE(olddir);
			}

			if (save_hostevents) {
				char hostlogfn[PATH_MAX];
				char newhostlogfn[PATH_MAX];

				MEMDEFINE(hostlogfn); MEMDEFINE(newhostlogfn);

				sprintf(hostlogfn, "%s/%s", histdir, hostname);
				sprintf(newhostlogfn, "%s/%s", histdir, newhostname);
				rename(hostlogfn, newhostlogfn);

				MEMUNDEFINE(hostlogfn); MEMUNDEFINE(newhostlogfn);
			}

			if (save_statusevents) {
				DIR *dirfd;
				struct dirent *de;
				char *hostlead;
				char *newhostnamecommas;
				char statuslogfn[PATH_MAX];
				char newlogfn[PATH_MAX];

				MEMDEFINE(statuslogfn); MEMDEFINE(newlogfn);

				p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ',';
				hostlead = malloc(strlen(hostname) + 2);
				strcpy(hostlead, hostnamecommas); strcat(hostlead, ".");

				p = newhostnamecommas = strdup(newhostname); while ((p = strchr(p, '.')) != NULL) *p = ',';


				dirfd = opendir(histdir);
				if (dirfd) {
					while ((de = readdir(dirfd)) != NULL) {
						if (strncmp(de->d_name, hostlead, strlen(hostlead)) == 0) {
							char *testname = strchr(de->d_name, '.');
							sprintf(statuslogfn, "%s/%s", histdir, de->d_name);
							sprintf(newlogfn, "%s/%s%s", histdir, newhostnamecommas, testname);
							rename(statuslogfn, newlogfn);
						}
					}
					closedir(dirfd);
				}

				xfree(newhostnamecommas);
				xfree(hostlead);
				xfree(hostnamecommas);

				MEMUNDEFINE(statuslogfn); MEMUNDEFINE(newlogfn);
			}
		}
		else if ((metacount > 5) && (strncmp(metadata[0], "@@renametest", 12) == 0)) {
			/* @@renametest|timestamp|sender|hostname|oldtestname|newtestname */
			char *newtestname;

			hostname = metadata[3];
			testname = metadata[4];
			newtestname = metadata[5];

			if (save_histlogs) {
				char *hostdash;
				char olddir[PATH_MAX];
				char newdir[PATH_MAX];

				MEMDEFINE(olddir); MEMDEFINE(newdir);

				p = hostdash = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = '_';
				sprintf(olddir, "%s/%s/%s", histlogdir, hostdash, testname);
				sprintf(newdir, "%s/%s/%s", histlogdir, hostdash, newtestname);
				rename(olddir, newdir);
				xfree(hostdash);

				MEMUNDEFINE(newdir); MEMUNDEFINE(olddir);
			}

			if (save_statusevents) {
				char *hostnamecommas;
				char statuslogfn[PATH_MAX];
				char newstatuslogfn[PATH_MAX];

				MEMDEFINE(statuslogfn); MEMDEFINE(newstatuslogfn);

				p = hostnamecommas = strdup(hostname); while ((p = strchr(p, '.')) != NULL) *p = ',';
				sprintf(statuslogfn, "%s/%s.%s", histdir, hostnamecommas, testname);
				sprintf(newstatuslogfn, "%s/%s.%s", histdir, hostnamecommas, newtestname);
				rename(statuslogfn, newstatuslogfn);
				xfree(hostnamecommas);

				MEMUNDEFINE(newstatuslogfn); MEMUNDEFINE(statuslogfn);
			}
		}
		else if (strncmp(metadata[0], "@@shutdown", 10) == 0) {
			running = 0;
		}
		else if (strncmp(metadata[0], "@@logrotate", 11) == 0) {
			char *fn = xgetenv("HOBBITCHANNEL_LOGFILENAME");
			if (fn && strlen(fn)) {
				freopen(fn, "a", stdout);
				freopen(fn, "a", stderr);
			}
			continue;
		}
	}

	MEMUNDEFINE(newcol2);
	MEMUNDEFINE(oldcol2);

	fclose(alleventsfd);
	return 0;
}
Exemple #12
0
int main(int argc, char *argv[])
{
	int daemonize = 0;
	char *pidfile = NULL;
	char *envarea = NULL;
	int cnid = -1;
	pcre *msgfilter = NULL;
	pcre *stdfilter = NULL;

	int argi;
	struct sigaction sa;
	RbtIterator handle;


	/* Dont save the error buffer */
	save_errbuf = 0;

	/* Create the peer container */
	peers = rbtNew(name_compare);

	for (argi=1; (argi < argc); argi++) {
		if (argnmatch(argv[argi], "--debug")) {
			debug = 1;
		}
		else if (argnmatch(argv[argi], "--channel=")) {
			char *cn = strchr(argv[argi], '=') + 1;

			for (cnid = C_STATUS; (channelnames[cnid] && strcmp(channelnames[cnid], cn)); cnid++) ;
			if (channelnames[cnid] == NULL) cnid = -1;
		}
		else if (argnmatch(argv[argi], "--daemon")) {
			daemonize = 1;
		}
		else if (argnmatch(argv[argi], "--no-daemon")) {
			daemonize = 0;
		}
		else if (argnmatch(argv[argi], "--pidfile=")) {
			char *p = strchr(argv[argi], '=');
			pidfile = strdup(p+1);
		}
		else if (argnmatch(argv[argi], "--log=")) {
			char *p = strchr(argv[argi], '=');
			logfn = strdup(p+1);
		}
		else if (argnmatch(argv[argi], "--env=")) {
			char *p = strchr(argv[argi], '=');
			loadenv(p+1, envarea);
		}
		else if (argnmatch(argv[argi], "--area=")) {
			char *p = strchr(argv[argi], '=');
			envarea = strdup(p+1);
		}
		else if (argnmatch(argv[argi], "--locator=")) {
			char *p = strchr(argv[argi], '=');
			locator_init(p+1);
			locatorbased = 1;
		}
		else if (argnmatch(argv[argi], "--service=")) {
			char *p = strchr(argv[argi], '=');
			locatorservice = get_servicetype(p+1);
		}
		else if (argnmatch(argv[argi], "--filter=")) {
			char *p = strchr(argv[argi], '=');
			msgfilter = compileregex(p+1);
			if (!msgfilter) {
				errprintf("Invalid filter (bad expression): %s\n", p+1);
			}
			else {
				stdfilter = compileregex("^@@(logrotate|shutdown|drophost|droptest|renamehost|renametest)");
			}
		}
		else {
			char *childcmd;
			char **childargs;
			int i = 0;

			childcmd = argv[argi];
			childargs = (char **) calloc((1 + argc - argi), sizeof(char *));
			while (argi < argc) { childargs[i++] = argv[argi++]; }
			addlocalpeer(childcmd, childargs);
		}
	}

	/* Sanity checks */
	if (cnid == -1) {
		errprintf("No channel/unknown channel specified\n");
		return 1;
	}
	if (locatorbased && (locatorservice == ST_MAX)) {
		errprintf("Must specify --service when using locator\n");
		return 1;
	}
	if (!locatorbased && (rbtBegin(peers) == rbtEnd(peers))) {
		errprintf("Must specify command for local worker\n");
		return 1;
	}

	/* Do cache responses to avoid doing too many lookups */
	if (locatorbased) locator_prepcache(locatorservice, 0);

	/* Go daemon */
	if (daemonize) {
		/* Become a daemon */
		pid_t daemonpid = fork();
		if (daemonpid < 0) {
			/* Fork failed */
			errprintf("Could not fork child\n");
			exit(1);
		}
		else if (daemonpid > 0) {
			/* Parent creates PID file and exits */
			FILE *fd = NULL;
			if (pidfile) fd = fopen(pidfile, "w");
			if (fd) {
				fprintf(fd, "%d\n", (int)daemonpid);
				fclose(fd);
			}
			exit(0);
		}
		/* Child (daemon) continues here */
		setsid();
	}

	/* Catch signals */
	setup_signalhandler("xymond_channel");
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = sig_handler;
	sigaction(SIGINT, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGCHLD, &sa, NULL);
	signal(SIGALRM, SIG_IGN);

	/* Switch stdout/stderr to the logfile, if one was specified */
	freopen("/dev/null", "r", stdin);	/* xymond_channel's stdin is not used */
	if (logfn) {
		freopen(logfn, "a", stdout);
		freopen(logfn, "a", stderr);
	}

	/* Attach to the channel */
	channel = setup_channel(cnid, CHAN_CLIENT);
	if (channel == NULL) {
		errprintf("Channel not available\n");
		running = 0;
	}

	while (running) {
		/* 
		 * Wait for GOCLIENT to go up.
		 *
		 * Note that we use IPC_NOWAIT if there are messages in the
		 * queue, because then we just want to pick up a message if
		 * there is one, and if not we want to continue pushing the
		 * queued data to the worker.
		 */
		struct sembuf s;
		int n;

		s.sem_num = GOCLIENT; s.sem_op  = -1; s.sem_flg = ((pendingcount > 0) ? IPC_NOWAIT : 0);
		n = semop(channel->semid, &s, 1);

		if (n == 0) {
			/*
			 * GOCLIENT went high, and so we got alerted about a new
			 * message arriving. Copy the message to our own buffer queue.
			 */
			char *inbuf = NULL;

			if (!msgfilter || matchregex(channel->channelbuf, msgfilter) || matchregex(channel->channelbuf, stdfilter)) {
				inbuf = strdup(channel->channelbuf);
			}

			/* 
			 * Now we have safely stored the new message in our buffer.
			 * Wait until any other clients on the same channel have picked up 
			 * this message (GOCLIENT reaches 0).
			 *
			 * We wrap this into an alarm handler, because it can occasionally
			 * fail, causing the whole system to lock up. We dont want that....
			 * We'll set the alarm to trigger after 1 second. Experience shows
			 * that we'll either succeed in a few milliseconds, or fail completely
			 * and wait the full alarm-timer duration.
			 */
			gotalarm = 0; signal(SIGALRM, sig_handler); alarm(2); 
			do {
				s.sem_num = GOCLIENT; s.sem_op  = 0; s.sem_flg = 0;
				n = semop(channel->semid, &s, 1);
			} while ((n == -1) && (errno == EAGAIN) && running && (!gotalarm));
			signal(SIGALRM, SIG_IGN);

			if (gotalarm) {
				errprintf("Gave up waiting for GOCLIENT to go low.\n");
			}

			/* 
			 * Let master know we got it by downing BOARDBUSY.
			 * This should not block, since BOARDBUSY is upped
			 * by the master just before he ups GOCLIENT.
			 */
			do {
				s.sem_num = BOARDBUSY; s.sem_op  = -1; s.sem_flg = IPC_NOWAIT;
				n = semop(channel->semid, &s, 1);
			} while ((n == -1) && (errno == EINTR));
			if (n == -1) {
				errprintf("Tried to down BOARDBUSY: %s\n", strerror(errno));
			}

			if (inbuf) {
				/*
				 * See if they want us to rotate logs. We pass this on to
				 * the worker module as well, but must handle our own logfile.
				 */
				if (strncmp(inbuf, "@@logrotate", 11) == 0) {
					freopen(logfn, "a", stdout);
					freopen(logfn, "a", stderr);
				}

				/*
				 * Put the new message on our outbound queue.
				 */
				if (addmessage(inbuf) != 0) {
					/* Failed to queue message, free the buffer */
					xfree(inbuf);
				}
			}
		}
		else {
			if (errno != EAGAIN) {
				dbgprintf("Semaphore wait aborted: %s\n", strerror(errno));
				continue;
			}
		}

		/* 
		 * We've picked up messages from the master. Now we 
		 * must push them to the worker process. Since there 
		 * is no way to hang off both a semaphore and select(),
		 * we'll just push as much data as possible into the 
		 * pipe. If we get to a point where we would block,
		 * then wait a teeny bit of time and restart the 
		 * whole loop with checking for new messages from the
		 * master etc.
		 *
		 * In theory, this could become an almost busy-wait loop.
		 * In practice, however, the queue will be empty most
		 * of the time because we'll just shove the data to the
		 * worker child.
		 */
		for (handle = rbtBegin(peers); (handle != rbtEnd(peers)); handle = rbtNext(peers, handle)) {
			int canwrite = 1, hasfailed = 0;
			xymon_peer_t *pwalk;
			time_t msgtimeout = gettimer() - MSGTIMEOUT;
			int flushcount = 0;

			pwalk = (xymon_peer_t *) gettreeitem(peers, handle);
			if (pwalk->msghead == NULL) continue; /* Ignore peers with nothing queued */

			switch (pwalk->peerstatus) {
			  case P_UP:
				canwrite = 1;
				break;

			  case P_DOWN:
				openconnection(pwalk);
				canwrite = (pwalk->peerstatus == P_UP);
				break;

			  case P_FAILED:
				canwrite = 0;
				break;
			}

			/* See if we have stale messages queued */
			while (pwalk->msghead && (pwalk->msghead->tstamp < msgtimeout)) {
				flushmessage(pwalk);
				flushcount++;
			}

			if (flushcount) {
				errprintf("Flushed %d stale messages for %s:%d\n",
					  flushcount,
				  	  inet_ntoa(pwalk->peeraddr.sin_addr), 
					  ntohs(pwalk->peeraddr.sin_port));
			}

			while (pwalk->msghead && canwrite) {
				fd_set fdwrite;
				struct timeval tmo;

				/* Check that this peer is ready for writing. */
				FD_ZERO(&fdwrite); FD_SET(pwalk->peersocket, &fdwrite);
				tmo.tv_sec = 0; tmo.tv_usec = 2000;
				n = select(pwalk->peersocket+1, NULL, &fdwrite, NULL, &tmo);
				if (n == -1) {
					errprintf("select() failed: %s\n", strerror(errno));
					canwrite = 0; 
					hasfailed = 1;
					continue;
				}
				else if ((n == 0) || (!FD_ISSET(pwalk->peersocket, &fdwrite))) {
					canwrite = 0;
					continue;
				}

				n = write(pwalk->peersocket, pwalk->msghead->bufp, pwalk->msghead->buflen);
				if (n >= 0) {
					pwalk->msghead->bufp += n;
					pwalk->msghead->buflen -= n;
					if (pwalk->msghead->buflen == 0) flushmessage(pwalk);
				}
				else if (errno == EAGAIN) {
					/*
					 * Write would block ... stop for now. 
					 */
					canwrite = 0;
				}
				else {
					hasfailed = 1;
				}

				if (hasfailed) {
					/* Write failed, or message grew stale */
					errprintf("Peer at %s:%d failed: %s\n",
						  inet_ntoa(pwalk->peeraddr.sin_addr), ntohs(pwalk->peeraddr.sin_port),
						  strerror(errno));
					canwrite = 0;
					shutdownconnection(pwalk);
					if (pwalk->peertype == P_NET) locator_serverdown(pwalk->peername, locatorservice);
					pwalk->peerstatus = P_FAILED;
				}
			}
		}
	}

	/* Detach from channels */
	close_channel(channel, CHAN_CLIENT);

	/* Close peer connections */
	for (handle = rbtBegin(peers); (handle != rbtEnd(peers)); handle = rbtNext(peers, handle)) {
		xymon_peer_t *pwalk = (xymon_peer_t *) gettreeitem(peers, handle);
		shutdownconnection(pwalk);
	}

	/* Remove the PID file */
	if (pidfile) unlink(pidfile);

	return 0;
}
Exemple #13
0
int main(int argc, char *argv[])
{
	char *filedir = NULL;
	char *htmldir = NULL;
	char *htmlextension = "html";
	char *onlytests = NULL;
	char *msg;
	enum role_t role = ROLE_STATUS;
	enum msgchannels_t chnid = C_STATUS;
	int argi;
	int seq;
	int running = 1;

	/* Dont save the error buffer */
	save_errbuf = 0;

	for (argi = 1; (argi < argc); argi++) {
		if (strcmp(argv[argi], "--status") == 0) {
			role = ROLE_STATUS;
			chnid = C_STATUS;
			if (!filedir) filedir = xgetenv("XYMONRAWSTATUSDIR");
		}
		else if (strcmp(argv[argi], "--html") == 0) {
			role = ROLE_STATUS;
			chnid = C_STATUS;
			if (!htmldir) htmldir = xgetenv("XYMONHTMLSTATUSDIR");
		}
		else if (strcmp(argv[argi], "--data") == 0) {
			role = ROLE_DATA;
			chnid = C_DATA;
			if (!filedir) filedir = xgetenv("XYMONDATADIR");
		}
		else if (strcmp(argv[argi], "--notes") == 0) {
			role = ROLE_NOTES;
			chnid = C_NOTES;
			if (!filedir) filedir = xgetenv("XYMONNOTESDIR");
		}
		else if (strcmp(argv[argi], "--enadis") == 0) {
			role = ROLE_ENADIS;
			chnid = C_ENADIS;
			if (!filedir) filedir = xgetenv("XYMONDISABLEDDIR");
		}
		else if (strcmp(argv[argi], "--debug") == 0) {
			debug = 1;
		}
		else if (argnmatch(argv[argi], "--dir=")) {
			filedir = strchr(argv[argi], '=')+1;
		}
		else if (argnmatch(argv[argi], "--htmldir=")) {
			htmldir = strchr(argv[argi], '=')+1;
		}
		else if (argnmatch(argv[argi], "--htmlext=")) {
			htmlextension = strchr(argv[argi], '=')+1;
		}
		else if (argnmatch(argv[argi], "--only=")) {
			char *p = strchr(argv[argi], '=') + 1;
			onlytests = (char *)malloc(3 + strlen(p));
			sprintf(onlytests, ",%s,", p);
		}
		else if (argnmatch(argv[argi], "--multigraphs=")) {
			char *p = strchr(argv[argi], '=');
			multigraphs = (char *)malloc(strlen(p+1) + 3);
			sprintf(multigraphs, ",%s,", p+1);
		}
		else if (argnmatch(argv[argi], "--locator=")) {
			char *p = strchr(argv[argi], '=');
			locator_init(p+1);
			locatorbased = 1;
		}
	}

	if (filedir == NULL) {
		errprintf("No directory given, aborting\n");
		return 1;
	}

	/* For picking up lost children */
	setup_signalhandler("xymond_filestore");
	signal(SIGPIPE, SIG_DFL);

	if (onlytests) dbgprintf("Storing tests '%s' only\n", onlytests);
	else dbgprintf("Storing all tests\n");

	while (running) {
		char *metadata[20] = { NULL, };
		char *statusdata = "";
		char *p;
		int metacount;
		char *hostname, *testname;
		time_t expiretime = 0;
		char logfn[PATH_MAX];

		MEMDEFINE(logfn);

		msg = get_xymond_message(chnid, "filestore", &seq, NULL);
		if (msg == NULL) {
			running = 0;
			MEMUNDEFINE(logfn);
			continue;
		}

		p = strchr(msg, '\n'); 
		if (p) {
			*p = '\0'; 
			statusdata = p+1;
		}

		metacount = 0;
		memset(&metadata, 0, sizeof(metadata));
		p = gettok(msg, "|");
		while (p && (metacount < 20)) {
			metadata[metacount++] = p;
			p = gettok(NULL, "|");
		}

		if ((role == ROLE_STATUS) && (metacount >= 14) && (strncmp(metadata[0], "@@status", 8) == 0)) {
			/* @@status|timestamp|sender|origin|hostname|testname|expiretime|color|testflags|prevcolor|changetime|ackexpiretime|ackmessage|disableexpiretime|disablemessage|clientmsgtstamp|flapping */
			int ltime, flapping = 0;
			time_t logtime = 0, timesincechange = 0, acktime = 0, disabletime = 0;

			hostname = metadata[4];
			testname = metadata[5];
			if (!wantedtest(onlytests, testname)) {
				dbgprintf("Status dropped - not wanted\n");
				MEMUNDEFINE(logfn);
				continue;
			}

			sprintf(logfn, "%s/%s.%s", filedir, commafy(hostname), testname);
			expiretime = atoi(metadata[6]);
			statusdata = msg_data(statusdata);
			sscanf(metadata[1], "%d.%*d", &ltime); logtime = ltime;
			timesincechange = logtime - atoi(metadata[10]);
			update_file(logfn, "w", statusdata, expiretime, metadata[2], timesincechange, seq);
			if (htmldir) {
				char *ackmsg = NULL;
				char *dismsg = NULL;
				char htmllogfn[PATH_MAX];

				MEMDEFINE(htmllogfn);

				if (metadata[11]) acktime = atoi(metadata[11]);
				if (metadata[12] && strlen(metadata[12])) ackmsg = metadata[12];
				if (ackmsg) nldecode(ackmsg);

				if (metadata[13]) disabletime = atoi(metadata[13]);
				if (metadata[14] && strlen(metadata[14]) && (disabletime > 0)) dismsg = metadata[14];
				if (dismsg) nldecode(dismsg);

				flapping = (metadata[16] ? (*metadata[16] == '1') : 0);

				sprintf(htmllogfn, "%s/%s.%s.%s", htmldir, hostname, testname, htmlextension);
				update_htmlfile(htmllogfn, statusdata, hostname, testname, parse_color(metadata[7]), flapping,
						     metadata[2], metadata[8], logtime, timesincechange, 
						     acktime, ackmsg,
						     disabletime, dismsg);

				MEMUNDEFINE(htmllogfn);
			}
		}
		else if ((role == ROLE_DATA) && (metacount > 5) && (strncmp(metadata[0], "@@data", 6) == 0)) {
			/* @@data|timestamp|sender|hostname|testname */
			p = hostname = metadata[4]; while ((p = strchr(p, '.')) != NULL) *p = ',';
			testname = metadata[5];
			if (!wantedtest(onlytests, testname)) {
				dbgprintf("data dropped - not wanted\n");
				MEMUNDEFINE(logfn);
				continue;
			}

			statusdata = msg_data(statusdata); if (*statusdata == '\n') statusdata++;
			sprintf(logfn, "%s/%s.%s", filedir, hostname, testname);
			expiretime = 0;
			update_file(logfn, "a", statusdata, expiretime, NULL, -1, seq);
		}
		else if ((role == ROLE_NOTES) && (metacount > 3) && (strncmp(metadata[0], "@@notes", 7) == 0)) {
			/* @@notes|timestamp|sender|hostname */
			hostname = metadata[3];
			statusdata = msg_data(statusdata); if (*statusdata == '\n') statusdata++;
			sprintf(logfn, "%s/%s", basename(filedir), hostname);
			expiretime = 0;
			update_file(logfn, "w", statusdata, expiretime, NULL, -1, seq);
		}
		else if ((role == ROLE_ENADIS) && (metacount > 5) && (strncmp(metadata[0], "@@enadis", 8) == 0)) {
			/* @@enadis|timestamp|sender|hostname|testname|expiretime */
			p = hostname = metadata[3]; while ((p = strchr(p, '.')) != NULL) *p = ',';
			testname = metadata[4];
			expiretime = atoi(metadata[5]);
			sprintf(logfn, "%s/%s.%s", filedir, hostname, testname);
			update_enable(logfn, expiretime);
		}
		else if (((role == ROLE_STATUS) || (role == ROLE_DATA) || (role == ROLE_ENADIS)) && (metacount > 3) && (strncmp(metadata[0], "@@drophost", 10) == 0)) {
			/* @@drophost|timestamp|sender|hostname */
			DIR *dirfd;
			struct dirent *de;
			char *hostlead;

			p = hostname = metadata[3]; while ((p = strchr(p, '.')) != NULL) *p = ',';
			hostlead = malloc(strlen(hostname) + 2);
			strcpy(hostlead, hostname); strcat(hostlead, ".");

			dirfd = opendir(filedir);
			if (dirfd) {
				while ( (de = readdir(dirfd)) != NULL) {
					if (strncmp(de->d_name, hostlead, strlen(hostlead)) == 0) {
						sprintf(logfn, "%s/%s", filedir, de->d_name);
						unlink(logfn);
					}
				}
				closedir(dirfd);
			}

			xfree(hostlead);
		}
		else if (((role == ROLE_STATUS) || (role == ROLE_DATA) || (role == ROLE_ENADIS)) && (metacount > 4) && (strncmp(metadata[0], "@@droptest", 10) == 0)) {
			/* @@droptest|timestamp|sender|hostname|testname */
			p = hostname = metadata[3]; while ((p = strchr(p, '.')) != NULL) *p = ',';
			testname = metadata[4];
			sprintf(logfn, "%s/%s.%s", filedir, hostname, testname);
			unlink(logfn);
		}
		else if (((role == ROLE_STATUS) || (role == ROLE_DATA) || (role == ROLE_ENADIS)) && (metacount > 4) && (strncmp(metadata[0], "@@renamehost", 12) == 0)) {
			/* @@renamehost|timestamp|sender|hostname|newhostname */
			DIR *dirfd;
			struct dirent *de;
			char *hostlead;
			char *newhostname;
			char newlogfn[PATH_MAX];

			MEMDEFINE(newlogfn);

			p = hostname = metadata[3]; while ((p = strchr(p, '.')) != NULL) *p = ',';
			hostlead = malloc(strlen(hostname) + 2);
			strcpy(hostlead, hostname); strcat(hostlead, ".");
			p = newhostname = metadata[4]; while ((p = strchr(p, '.')) != NULL) *p = ',';

			dirfd = opendir(filedir);
			if (dirfd) {
				while ( (de = readdir(dirfd)) != NULL) {
					if (strncmp(de->d_name, hostlead, strlen(hostlead)) == 0) {
						char *testname = strchr(de->d_name, '.');
						sprintf(logfn, "%s/%s", filedir, de->d_name);
						sprintf(newlogfn, "%s/%s%s", filedir, newhostname, testname);
						rename(logfn, newlogfn);
					}
				}
				closedir(dirfd);
			}
			xfree(hostlead);

			MEMUNDEFINE(newlogfn);
		}
		else if (((role == ROLE_STATUS) || (role == ROLE_DATA) || (role == ROLE_ENADIS)) && (metacount > 5) && (strncmp(metadata[0], "@@renametest", 12) == 0)) {
			/* @@renametest|timestamp|sender|hostname|oldtestname|newtestname */
			char *newtestname;
			char newfn[PATH_MAX];

			MEMDEFINE(newfn);

			p = hostname = metadata[3]; while ((p = strchr(p, '.')) != NULL) *p = ',';
			testname = metadata[4];
			newtestname = metadata[5];
			sprintf(logfn, "%s/%s.%s", filedir, hostname, testname);
			sprintf(newfn, "%s/%s.%s", filedir, hostname, newtestname);
			rename(logfn, newfn);

			MEMUNDEFINE(newfn);
		}
		else if (strncmp(metadata[0], "@@shutdown", 10) == 0) {
			running = 0;
		}
		else if (strncmp(metadata[0], "@@logrotate", 11) == 0) {
			char *fn = xgetenv("XYMONCHANNEL_LOGFILENAME");
			if (fn && strlen(fn)) {
				freopen(fn, "a", stdout);
				freopen(fn, "a", stderr);
			}
			continue;
		}
		else if (strncmp(metadata[0], "@@idle", 6) == 0) {
			/* Ignored */
		}
		else {
			errprintf("Dropping message type %s, metacount=%d\n", metadata[0], metacount);
		}

		MEMUNDEFINE(logfn);
	}

	return 0;
}
Exemple #14
0
/**
 * Main program
 * @param argc argument counter
 * @param argv argument vector
 * @param appdata application data
 * @return application return code to shell
 */
int
oonf_main(int argc, char **argv, const struct oonf_appdata *appdata) {
  int return_code;
  int result;

  /* early initialization */
  return_code = 1;

  _schema_name = NULL;
  _display_schema = false;
  _debug_early = false;
  _ignore_unknown = false;

  /* setup signal handler */
  _end_oonf_signal = false;
  setup_signalhandler();

  /* parse "early" command line arguments */
  parse_early_commandline(argc, argv);

  /* initialize core */
  os_core_init(appdata->app_name);

  /* initialize logger */
  if (oonf_log_init(appdata, _debug_early ? LOG_SEVERITY_DEBUG : LOG_SEVERITY_WARN)) {
    goto oonf_cleanup;
  }

  /* prepare plugin initialization */
  oonf_subsystem_init();

  /* initialize configuration system */
  if (oonf_cfg_init(argc, argv, appdata->default_cfg_handler)) {
    goto oonf_cleanup;
  }

  /* add custom configuration definitions */
  oonf_logcfg_init();

  /* parse command line and read configuration files */
  return_code = parse_commandline(argc, argv, appdata, false);
  if (return_code != -1) {
    /* end OONFd now */
    goto oonf_cleanup;
  }

  /* prepare for an error during initialization */
  return_code = 1;

  /* read global section early */
  if ((result = oonf_cfg_update_globalcfg(true))) {
    OONF_WARN(LOG_MAIN, "Cannot read global configuration section (%d)", result);
    goto oonf_cleanup;
  }

  /* configure logger */
  if (oonf_logcfg_apply(oonf_cfg_get_rawdb())) {
    goto oonf_cleanup;
  }

  /* load plugins */
  if (oonf_cfg_load_subsystems()) {
    goto oonf_cleanup;
  }

  /* show schema if necessary */
  if (_display_schema) {
    return_code = display_schema();
    goto oonf_cleanup;
  }

  /* check if we are root, otherwise stop */
  if (appdata->need_root) {
    if (geteuid() != 0) {
      OONF_WARN(LOG_MAIN, "You must be root(uid = 0) to run %s!\n",
          appdata->app_name);
      goto oonf_cleanup;
    }
  }

  if (appdata->need_lock
      && config_global.lockfile != NULL && *config_global.lockfile != 0) {
    /* create application lock */
    if (os_core_create_lockfile(config_global.lockfile)) {
      OONF_WARN(LOG_MAIN, "Could not acquire application lock '%s'",
          config_global.lockfile);
      goto oonf_cleanup;
    }
  }

  /* call initialization callbacks of dynamic plugins */
  oonf_cfg_initplugins();

  /* apply configuration */
  if (oonf_cfg_apply()) {
    goto oonf_cleanup;
  }

  if (!oonf_cfg_is_running()) {
    /*
     * mayor error during late initialization
     * or maybe the user decided otherwise and pressed CTRL-C
     */
    return_code = _end_oonf_signal ? 0 : 1;
    goto oonf_cleanup;
  }

  if (!_handle_scheduling) {
    OONF_WARN(LOG_MAIN, "No event scheduler present");
    return_code = 1;
    goto oonf_cleanup;
  }
  /* see if we need to fork */
  if (config_global.fork && !_display_schema) {
    /* tell main process that we are finished with initialization */
    if (daemon(0,0) < 0) {
      OONF_WARN(LOG_MAIN, "Could not fork into background: %s (%d)",
          strerror(errno), errno);
      goto oonf_cleanup;
    }

    if (config_global.pidfile && *config_global.pidfile != 0) {
      if (_write_pidfile(config_global.pidfile)) {
        goto oonf_cleanup;
      }
    }
  }

  /* activate mainloop */
  return_code = mainloop(argc, argv, appdata);

  /* tell plugins shutdown is in progress */
  oonf_subsystem_initiate_shutdown();

  /* wait for 500 ms and process socket events */
  while(!_handle_scheduling());

oonf_cleanup:
  /* free plugins */
  oonf_cfg_unconfigure_subsystems();
  oonf_subsystem_cleanup();

  /* free logging/config bridge resources */
  oonf_logcfg_cleanup();

  /* free configuration resources */
  oonf_cfg_cleanup();

  /* free logger resources */
  oonf_log_cleanup();

  /* free core resources */
  os_core_cleanup();

  return return_code;
}