/*
* 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