/*
 * BIGTEST - Testing all modules together, in a simple server that generates
 * 	     lots of debugging output. Test things here and then move your 
 * 	     code to the right places.
 *
 * Author:
 * Emile van Bergen, emile@evbergen.xs4all.nl
 *
 * Permission to redistribute an original or modified version of this program
 * in source, intermediate or object code form is hereby granted exclusively
 * under the terms of the GNU General Public License, version 2. Please see the
 * file COPYING for details, or refer to http://www.gnu.org/copyleft/gpl.html.
 *
 * History:
 * 2001/07/20 - EvB - Created
 */

char bigtest_id[] = "BIGTEST - Copyright (C) 2001 Emile van Bergen.";


/*
 * INCLUDES & DEFINES
 */


#include <sys/time.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <signal.h>
#include <string.h>		/* For strerror */
#include <stdlib.h>
#include <errno.h>
#include <time.h>		/* For time() */

#include <metadict.h>		/* For meta_newfromdict() */
#include <config.h>		/* For conf_new() etc */
#include <debug.h>		/* For msg_* */

#include <jobs.h>		/* Also includes srvtypes.h */
#include <channels.h>		/* For chan_handle_* */

#include <defs.h>


/*
 * MAIN
 */


volatile int sighup = 0, sigint = 0, sigterm = 0, sigchld = 0;

void sighandler(int sig)
{
	switch(sig) {
		case SIGHUP: sighup++; break;
		case SIGINT: sigint++; break;
		case SIGTERM: sigterm++; break;
		case SIGCHLD: sigchld++; break;
	}
	signal(sig, sighandler);
}


int main()
{
	META *m;
	CONF *c;
	SOCK *s;
	JOB *j;
	IFACE *i;
	PROC *p;
	PROC_SET ps;
	fd_set rfds, wfds;
	struct timeval tv, *tvp;
	time_t t;
	int status, n, sockhighest;

	msg_init(-1, 0);
	msg_setthresh(F_MISC, L_DEBUG);
	msg_setthresh(F_TEXT, L_DEBUG);
	msg_setthresh(F_LANG, L_DEBUG);
	msg_setthresh(F_PROC, L_DEBUG);
	msg_setthresh(F_RECV, L_DEBUG);
	msg_setthresh(F_SEND, L_DEBUG);

	/* Open dictionary */
	m = meta_newfromdict(RADDB);
	if (!m) { msg(F_MISC, L_ERR, "main: ERROR: Could not create "
				     "dictionary!\n"); return 1; }

	/* Open configuration */
	c = conf_new(m, RADDB, MODULES);
	if (!c) { msg(F_MISC, L_ERR, "main: ERROR: Could not process "
				     "configuration!\n"); return 1; }

	/* On your marks... (define sighandlers, initialize procset) */
	signal(SIGHUP, sighandler);
	signal(SIGINT, sighandler);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGTERM, sighandler);
	signal(SIGCHLD, sighandler);
	memset(&ps, 0, sizeof(ps));
	ps.p = c->procs;

	/* ...ready... (start subprocesses, define initial fd set and timers) */
	time(&t);
	n = conf_start(c, t);
	if (n == -1) { msg(F_MISC, L_ERR, "main: ERROR: Problem with "
					  "configuration!\n"); return 1; }

	FD_ZERO(&(ps.rfds)); FD_ZERO(&(ps.wfds)); sockhighest = -1;
	for(s = c->sources; s; s = s->next) {
		FD_SET(s->fd, &(ps.rfds));
		sockhighest = MAX(sockhighest, s->fd);
	}
	ps.highestfd = sockhighest;

	/* ... Go. */
	for(;;) {

		msg(F_MISC, L_DEBUG, "main: Top, sigchld=%d, sigint=%d, "
				     "sigterm=%d.\n", sigchld, sigint, sigterm);

		/* Check for signals that occured in the mean time */
		if (sigchld) {

			/* Handle exited childs, if any */
			while((n = waitpid(0, &status, WNOHANG)) > 0) {

				/* Find out which of our subprocesses exited */
				for(p = c->procs; p && p->pid != n; p=p->next);
				if (!p) { msg(F_PROC, L_ERR, "main: BUG: Unknow"
					       "n child exited!\n"); continue; }

				/* Clear this proc's FDs from the set */
				if (p->rfd != -1) FD_CLR(p->rfd, &(ps.rfds));
				if (p->wfd != -1) FD_CLR(p->wfd, &(ps.wfds));

				/* Call the exit event handler */
				proc_handle_end(p, t, status);

				/* Take a timer, if set, into account */
				if (p->timer && (p->timer < ps.firsttimer ||
						 !ps.firsttimer)) {
					ps.firsttimer = p->timer;
				}
			}
			sigchld = 0;
		}
		if (sigterm || sigint) break;

		/* select according to ps.rfds, ps.wfds and ps.firsttimer */
		rfds = ps.rfds; wfds = ps.wfds; tvp = 0;
		if (ps.firsttimer) {
			tv.tv_sec = ps.firsttimer - t; tv.tv_usec = 0;
			if (tv.tv_sec < 0) tv.tv_sec = 0, tv.tv_usec = 1;
			tvp = &tv;
		}

		D1(msg(F_MISC, L_DEBUG, "main: Selecting, highest=%d, time=%d"
					  ", firsttimer=%d, timeout=%d\n",
		       ps.highestfd, t, ps.firsttimer, tvp ? tvp->tv_sec : 0));

		n = select(ps.highestfd + 1, &rfds, &wfds, 0, tvp);
		if (n == -1) { if (errno == EINTR) continue;
			       msg(F_MISC, L_ERR, "main: FATAL: Select() said: "
					    "%s!\n", strerror(errno)); break; }
		time(&t);

		/* Loop through the sources for rx readyness */
		for(s = c->sources; s; s = s->next) if (FD_ISSET(s->fd, &rfds)){

			/* Create new job from this source */
			j = job_new_fromsock(s, t); if (!j) continue;

			/* Run the expression on this job till we have replied
			   or we got an interface call */
			i = job_run(c, j); if (!i) continue;

			/* Handle this interface call appropriately, adding
			   the job to the interface's shared send queue or to
			   an idle channel's receive queue, after sending the
			   message to that idle channel's subprocess */
			job_toiface(i, j, t);
		}

		/* Loop through the subprocs and handle their pending events;
		   also always clear _all_ proc-related fds from the fd sets. */
		for(p = ps.p; p; p = p->next) {

			/* If this proc has a reading fd, del it at first, and
			   test it for readyness. We add it again if needed. */
			if (p->rfd != -1) {
				FD_CLR(p->rfd, &(ps.rfds));
				if (FD_ISSET(p->rfd, &rfds)) 
					chan_handle_read(p->chan, t);
			}

			/* If this proc has a writing fd, del it at first, and
			   test it for readyness. We add it again if needed. */
			if (p->wfd != -1) {
				FD_CLR(p->wfd, &(ps.wfds));
				if (FD_ISSET(p->wfd, &wfds)) 
					chan_handle_write(p->chan, t);
			}

			/* Next, see if this proc's watchdog timed out */
			if (p->timer && t >= p->timer) proc_handle_timeout(p,t);
		}

		/* Loop through subprocs again to get new event mask. This loop
		   is separate because a chan_handle_ function may do something
		   to another proc's state as well. */
		ps.firsttimer = 0;
		ps.highestfd = sockhighest;
		for(p = ps.p; p; p = p->next) {

			/* Now update this proc's bits in the fd sets */
			if (p->rfd != -1 && (p->state & PRS_RECEIVING)) {
				FD_SET(p->rfd, &(ps.rfds));
				ps.highestfd = MAX(ps.highestfd, p->rfd);
			}
			if (p->wfd != -1 && (p->state & PRS_SENDING)) {
				FD_SET(p->wfd, &(ps.wfds));
				ps.highestfd = MAX(ps.highestfd, p->wfd);
			}

			/* And maintain the first expiring timer */
			if (p->timer && (p->timer < ps.firsttimer ||
					 !ps.firsttimer)) {
				ps.firsttimer = p->timer;
			}
		}
	}

	conf_del(c);
	meta_del(m);
	return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1