/* * 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 #include #include #include #include /* For strerror */ #include #include #include /* For time() */ #include /* For meta_newfromdict() */ #include /* For conf_new() etc */ #include /* For msg_* */ #include /* Also includes srvtypes.h */ #include /* For chan_handle_* */ #include /* * 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; }