After having daemonized, write the PID of the new process to
'PIDFILE'.
+SIGNALS
+-------
+
+SIGTERM, SIGINT::
+
+ Upon first reception, `htparser` closes all listening ports
+ and the socket to the root handler, but continues to serve all
+ currently ongoing requests until none remain. Upon second
+ reception, `htparser` shuts down completely.
+
EXAMPLES
--------
};
static int epfd = -1, fdln = 0;
+static int exitstatus;
static struct blocker **fdlist;
static int regfd(struct blocker *bl)
flog(LOG_ERR, "epoll_mod on fd %i: %s", bl->fd, strerror(errno));
}
}
+ bl->reg = 0;
}
int block(int fd, int ev, time_t to)
return(rv);
}
-void ioloop(void)
+int ioloop(void)
{
struct blocker *bl, *nbl;
struct epoll_event evr[16];
int i, fd, nev, ev, toval;
time_t now, timeout;
+ exitstatus = 0;
epfd = epoll_create(128);
fcntl(epfd, F_SETFD, FD_CLOEXEC);
for(bl = blockers; bl; bl = nbl) {
toval = (timeout - now) * 1000;
else
toval = 1000;
+ if(exitstatus)
+ break;
nev = epoll_wait(epfd, evr, sizeof(evr) / sizeof(*evr), toval);
if(nev < 0) {
if(errno != EINTR) {
resume(bl->th, 0);
}
}
+ for(bl = blockers; bl; bl = bl->n)
+ remfd(bl);
close(epfd);
epfd = -1;
+ return(exitstatus);
+}
+
+void exitioloop(int status)
+{
+ exitstatus = status;
}
#include <mtio.h>
static struct blocker *blockers;
+static int exitstatus;
struct blocker {
struct blocker *n, *p;
return(rv);
}
-void ioloop(void)
+int ioloop(void)
{
int ret;
fd_set rfds, wfds, efds;
int maxfd;
int ev;
+ exitstatus = 0;
while(blockers != NULL) {
FD_ZERO(&rfds);
FD_ZERO(&wfds);
if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to)))
timeout = bl->to;
}
+ if(exitstatus)
+ return(exitstatus);
toval.tv_sec = timeout - now;
toval.tv_usec = 0;
ret = select(maxfd + 1, &rfds, &wfds, &efds, timeout?(&toval):NULL);
* probably is. */
sleep(1);
}
- }
- now = time(NULL);
- for(bl = blockers; bl; bl = nbl) {
- nbl = bl->n;
- ev = 0;
- if(FD_ISSET(bl->fd, &rfds))
- ev |= EV_READ;
- if(FD_ISSET(bl->fd, &wfds))
- ev |= EV_WRITE;
- if(FD_ISSET(bl->fd, &efds))
- ev = -1;
- if((ev < 0) || (ev & bl->ev))
- resume(bl->th, ev);
- else if((bl->to != 0) && (bl->to <= now))
- resume(bl->th, 0);
+ } else {
+ now = time(NULL);
+ for(bl = blockers; bl; bl = nbl) {
+ nbl = bl->n;
+ ev = 0;
+ if(FD_ISSET(bl->fd, &rfds))
+ ev |= EV_READ;
+ if(FD_ISSET(bl->fd, &wfds))
+ ev |= EV_WRITE;
+ if(FD_ISSET(bl->fd, &efds))
+ ev = -1;
+ if((ev < 0) || (ev & bl->ev))
+ resume(bl->th, ev);
+ else if((bl->to != 0) && (bl->to <= now))
+ resume(bl->th, 0);
+ }
}
}
+ return(0);
+}
+
+void exitioloop(int status)
+{
+ exitstatus = status;
}
#define EV_WRITE 2
int block(int fd, int ev, time_t to);
-void ioloop(void);
+int ioloop(void);
+void exitioloop(int status);
FILE *mtstdopen(int fd, int issock, int timeout, char *mode);
#endif
#define bufinit(buf) memset(&(buf), 0, sizeof(buf))
#define buffree(buf) do { if((buf).b != NULL) {free((buf).b);} bufinit(buf); } while(0)
#define sizebuf(buf, wanted) (_sizebuf((struct buffer *)&(buf), (wanted), sizeof(*((buf).b))))
+#define bufdel(buf, i) (memmove((buf).b + (i), (buf).b + (i) + 1, (--((buf).d) - (i)) * sizeof(*((buf).b))))
#define bufadd(buf, new) \
do { \
_sizebuf((struct buffer *)&(buf), (buf).d + 1, sizeof(*((buf).b))); \
static int plex;
static char *pidfile = NULL;
static int daemonize, usesyslog;
+struct mtbuf listeners;
static void trimx(struct hthead *req)
{
out = NULL;
req = resp = NULL;
- while(1) {
+ while(plex >= 0) {
if((req = parsereq(in)) == NULL)
break;
if(!canonreq(req))
if((conn->initreq != NULL) && conn->initreq(conn, req))
break;
- if(block(plex, EV_WRITE, 60) <= 0)
+ if((plex < 0) || block(plex, EV_WRITE, 60) <= 0)
break;
if(socketpair(PF_UNIX, SOCK_STREAM, 0, pfds))
break;
{
vavar(int, fd);
char *buf;
- int ret;
+ int i, ret;
while(1) {
- block(fd, EV_READ, 0);
+ if(block(fd, EV_READ, 0) == 0)
+ break;
buf = smalloc(65536);
ret = recv(fd, buf, 65536, 0);
if(ret < 0) {
flog(LOG_WARNING, "received error on rootplex read channel: %s", strerror(errno));
exit(1);
} else if(ret == 0) {
- exit(0);
+ free(buf);
+ break;
}
/* Maybe I'd like to implement some protocol in this direction
* some day... */
free(buf);
}
+ close(plex);
+ plex = -1;
+ for(i = 0; i < listeners.d; i++) {
+ if(listeners.b[i] == muth)
+ bufdel(listeners, i);
+ }
+ flog(LOG_INFO, "root handler exited, so shutting down listening...");
+ while(listeners.d > 0)
+ resume(listeners.b[0], 0);
}
static void initroot(void *uu)
buffree(vals);
}
+static void sighandler(int sig)
+{
+ exitioloop(1);
+}
+
int main(int argc, char **argv)
{
- int c;
+ int c, d;
int i, s1;
char *root;
FILE *pidout;
flog(LOG_ERR, "could not spawn root multiplexer: %s", strerror(errno));
return(1);
}
- mustart(plexwatch, plex);
+ bufadd(listeners, mustart(plexwatch, plex));
pidout = NULL;
if(pidfile != NULL) {
if((pidout = fopen(pidfile, "w")) == NULL) {
}
}
signal(SIGPIPE, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
if(daemonize) {
daemon(0, 0);
}
fprintf(pidout, "%i\n", getpid());
fclose(pidout);
}
- ioloop();
+ d = 0;
+ while(!d) {
+ switch(ioloop()) {
+ case 0:
+ d = 1;
+ break;
+ case 1:
+ if(listeners.d > 0) {
+ while(listeners.d > 0)
+ resume(listeners.b[0], 0);
+ flog(LOG_INFO, "no longer listening");
+ } else {
+ d = 1;
+ }
+ break;
+ }
+ }
return(0);
}
void *pdata;
};
+struct mtbuf {
+ struct muth **b;
+ size_t s, d;
+};
+
void serve(FILE *in, struct conn *conn);
int listensock4(int port);
void handlegnussl(int argc, char **argp, char **argv);
#endif
+extern struct mtbuf listeners;
+
#endif
static void listenloop(struct muth *muth, va_list args)
{
vavar(struct tcpport *, tcp);
- int ns;
+ int i, ns;
struct sockaddr_storage name;
socklen_t namelen;
while(1) {
namelen = sizeof(name);
- block(tcp->fd, EV_READ, 0);
+ if(block(tcp->fd, EV_READ, 0) == 0)
+ goto out;
ns = accept(tcp->fd, (struct sockaddr *)&name, &namelen);
if(ns < 0) {
flog(LOG_ERR, "accept: %s", strerror(errno));
out:
close(tcp->fd);
free(tcp);
+ for(i = 0; i < listeners.d; i++) {
+ if(listeners.b[i] == muth)
+ bufdel(listeners, i);
+ }
}
void handleplain(int argc, char **argp, char **argv)
omalloc(tcp);
tcp->fd = fd;
tcp->sport = port;
- mustart(listenloop, tcp);
+ bufadd(listeners, mustart(listenloop, tcp));
if((fd = listensock4(port)) < 0) {
if(errno != EADDRINUSE) {
flog(LOG_ERR, "could not listen on IPv4 (port %i): %s", port, strerror(errno));
omalloc(tcp);
tcp->fd = fd;
tcp->sport = port;
- mustart(listenloop, tcp);
+ bufadd(listeners, mustart(listenloop, tcp));
}
}
static void listenloop(struct muth *muth, va_list args)
{
vavar(struct sslport *, pd);
- int ns;
+ int i, ns;
struct sockaddr_storage name;
socklen_t namelen;
while(1) {
namelen = sizeof(name);
- block(pd->fd, EV_READ, 0);
+ if(block(pd->fd, EV_READ, 0) == 0)
+ goto out;
ns = accept(pd->fd, (struct sockaddr *)&name, &namelen);
if(ns < 0) {
flog(LOG_ERR, "accept: %s", strerror(errno));
out:
close(pd->fd);
free(pd);
+ for(i = 0; i < listeners.d; i++) {
+ if(listeners.b[i] == muth)
+ bufdel(listeners, i);
+ }
}
static gnutls_dh_params_t dhparams(void)
pd->sport = port;
pd->creds = creds;
pd->ncreds = ncreds.b;
- mustart(listenloop, pd);
+ bufadd(listeners, mustart(listenloop, pd));
if((fd = listensock4(port)) < 0) {
if(errno != EADDRINUSE) {
flog(LOG_ERR, "could not listen on IPv4 port (port %i): %s", port, strerror(errno));
pd->fd = fd;
pd->sport = port;
pd->creds = creds;
- mustart(listenloop, pd);
+ bufadd(listeners, mustart(listenloop, pd));
}
}