diff -ruN apache_1.3.19/README.PARENTAL_ACCEPT apache_1.3.19+PA/README.PARENTAL_ACCEPT --- apache_1.3.19/README.PARENTAL_ACCEPT Wed Dec 31 19:00:00 1969 +++ apache_1.3.19+PA/README.PARENTAL_ACCEPT Sun Mar 11 14:39:21 2001 @@ -0,0 +1,92 @@ +LICENSE at bottom ;-) + +This code is released under the same license as Apache. + +It _should_ work on Linux, BSDs and Solaris out of the box. + +It (the patch) will apply without failure to Apache 1.3.19 (<19, YMMV) +As it is a patch, I assume that you will want it enabled, so the +"PARENTALACCEPT" Apache Rule is enabled by default. You should notice +a comment about adding PARENTAL_ACCEPT code in during the ./configure step. + +It provides a parent process that handles all accept system calls and +postpones allocation to an Apache child until the accepted socket contains +data for reading. This is done via System Vish IPC and requires the creation +of one or more (depending on OS) AF_UNIX socket files to be created. These +files are created in /tmp. I had it use /tmp and hardcoded because: + o I was too lazy to add a configuration directive + o I didn't want to require any changes to the httpd.conf + o You can edit src/main/apue.h and change PARENTAL_ACCEPT_DIR + or specify on in the extra CFLAGS if you want something else + (like /var/run/apache) + +In addition to acting as accept filter, the patch will take all idle sessions +(in a keep alive state with no data immediately available) and hand them +back to the parent so you can free up the child. + +My testing shows that I can server several hundred simultaneous connections +with 5 or 6 children. + +The scoreboard and mod_status modules have been modified to give a little +insight as to what the current state it. + +Have lots of fun!!! + +Problems: mail me at jesus@omniti.com + +LICENSE: + +/* ==================================================================== + * Copyright (c) 1999-2000 Theo Schlossnagle + * Copyright (c) 1999-2000 OmniTI, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed at The Center for + * Networking and Distributed Systems at The Johns Hopkins University + * for use in the Backhand project (http://www.cnds.jhu.edu/backhand). + * Creator: Theo Schlossnagle + * Guidance: Yair Amir " + * + * 4. The names "Backhand" and "mod_backhand" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * backhand@cnds.jhu.edu. + * + * 5. Products derived from this software may not be called "mod_backhand" + * nor may "mod_backhand" appear in their names without prior written + * permission. For written permission, please contact + * backhand@cnds.jhu.edu. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed at OmniTI, Inc. for use + * in the Apache web server project. + * Creator: Theo Schlossnagle ." + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff -ruN apache_1.3.19/src/Configuration.tmpl apache_1.3.19+PA/src/Configuration.tmpl --- apache_1.3.19/src/Configuration.tmpl Sat Aug 14 04:35:21 1999 +++ apache_1.3.19+PA/src/Configuration.tmpl Sat Mar 10 18:05:13 2001 @@ -167,6 +167,7 @@ # if the directory is not present. # +Rule PARENTALACCEPT=yes Rule SOCKS4=no Rule SOCKS5=no Rule IRIXNIS=no diff -ruN apache_1.3.19/src/Configure apache_1.3.19+PA/src/Configure --- apache_1.3.19/src/Configure Mon Feb 19 08:32:32 2001 +++ apache_1.3.19+PA/src/Configure Sat Mar 10 18:05:14 2001 @@ -229,6 +229,7 @@ ## RULE_WANTHSREGEX=`./helpers/CutRule WANTHSREGEX $file` RULE_STATUS=`./helpers/CutRule STATUS $file` +RULE_PARENTALACCEPT=`./helpers/CutRule PARENTALACCEPT $file` RULE_SOCKS4=`./helpers/CutRule SOCKS4 $file` RULE_SOCKS5=`./helpers/CutRule SOCKS5 $file` RULE_IRIXNIS=`./helpers/CutRule IRIXNIS $file` @@ -1601,6 +1603,12 @@ fi ;; esac + +# PARENTAL ACCEPT +if [ "x$RULE_PARENTALACCEPT" = "xyes" ]; then + echo " + enabling PARENTAL_ACCEPT code" + CFLAGS="$CFLAGS -DPARENTAL_ACCEPT" +fi # SOCKS4 support: # We assume that if they are using SOCKS4, then they've diff -ruN apache_1.3.19/src/include/scoreboard.h apache_1.3.19+PA/src/include/scoreboard.h --- apache_1.3.19/src/include/scoreboard.h Mon Jan 15 12:04:17 2001 +++ apache_1.3.19+PA/src/include/scoreboard.h Sun Mar 11 14:06:55 2001 @@ -166,11 +166,19 @@ typedef struct { ap_generation_t running_generation; /* the generation of children which * should still be serving requests. */ +#ifdef PARENTAL_ACCEPT + int connected; + int keptalive; + int serving; +#endif } global_score; /* stuff which the parent generally writes and the children rarely read */ typedef struct { pid_t pid; +#ifdef PARENTAL_ACCEPT + int tether; +#endif #ifdef OPTIMIZE_TIMEOUTS time_t last_rtime; /* time(0) of the last change */ vtime_t last_vtime; /* the last vtime the parent has seen */ diff -ruN apache_1.3.19/src/main/Makefile.tmpl apache_1.3.19+PA/src/main/Makefile.tmpl --- apache_1.3.19/src/main/Makefile.tmpl Thu Jun 22 19:22:05 2000 +++ apache_1.3.19+PA/src/main/Makefile.tmpl Sat Mar 10 18:05:14 2001 @@ -11,7 +11,7 @@ http_config.o http_core.o http_log.o \ http_main.o http_protocol.o http_request.o http_vhost.o \ util.o util_date.o util_script.o util_uri.o util_md5.o \ - rfc1413.o + rfc1413.o apue.o .c.o: $(CC) -c $(INCLUDES) $(CFLAGS) $< diff -ruN apache_1.3.19/src/main/apue.c apache_1.3.19+PA/src/main/apue.c --- apache_1.3.19/src/main/apue.c Wed Dec 31 19:00:00 1969 +++ apache_1.3.19+PA/src/main/apue.c Sun Mar 11 14:27:03 2001 @@ -0,0 +1,460 @@ +/* ====================================================================== + * Copyright (c) 1998-1999 The Johns Hopkins University. + * All rights reserved. + * The following code was written by Theo Schlossnagle for use in the + * Backhand project at The Center for Networking and Distributed Systems + * at The Johns Hopkins University. + * Please refer to the LICENSE file before using this software. + * ====================================================================== +*/ + +/* NOTE:: Some of this is taken from Stevens' Advanced + Programming in the Unix Evironment */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apue.h" + +#define STALE 30 +#define SERVPATH "/tmp/apache_parent" +#define CLI_PATH PARENTAL_ACCEPT_DIR /* +5 for pid = 20 chars */ +#define CLI_PERM S_IRWXU /* rwx for user only */ + +#define err_sys(A) fprintf(stderr, A) +#define err_dump(A) err_sys(A) +#define err_ret(A) err_sys(A) +#ifndef MAXLINE +#define MAXLINE 4096 +#endif + +int signal_parent(int a, int fd, char *conndata) { + pid_t pid; + + return pa_send_fd(a, fd, conndata); + + pid = getpid(); + if(write(a, &pid, sizeof(pid_t)) != sizeof(pid_t)) + return -1; + return a; +} + +#ifdef LINUX +#define _BSDISH +#endif +#ifdef BSD +#define _BSDISH +#endif +#ifdef SOLARIS2 +#define _SYSVISH +#endif + +#ifdef _SYSVISH +#include +#include +int pa_recv_fd(int servfd, char **conndata) { + int newfd, nread, flag, status; + int method; + static int allocedsize; + static char *alloced; + char *ptr, buf[100]; + struct strbuf dat; + struct strrecvfd recvfd; + + status = -1; + *conndata = NULL; + if(read(servfd, buf, 1) != 1) + return -1; + method = *buf; + for( ; ; ) { + dat.buf = buf; + dat.maxlen = MAXLINE; + flag = 0; + if (getmsg(servfd, NULL, &dat, &flag) < 0) + err_sys("getmsg error"); + nread = dat.len; + if(nread == 0) { + err_ret("connection closed by server"); + return(-1); + } + for(ptr = buf; ptr < &buf[nread]; ) { + if(*ptr++ == 0) { + if(ptr != &buf[nread-1]) + err_dump("message format error"); + status = *ptr & 255; + if(status == 0) { + if(ioctl(servfd, I_RECVFD, &recvfd) < 0) + return(-1); + newfd = recvfd.fd; + } else + newfd = -status; + nread -= 2; + } + } + if(status >= 0) { + if(method == 1) { + /* We need to read the serialized connection data */ + if(read(servfd, &nread, sizeof(int)) != sizeof(int)) + return -1; + if(nread > allocedsize) { + if(alloced) free(alloced); + alloced = (char *)malloc(nread); + if(!alloced) { close(newfd); return -10; } + allocedsize = nread; + } + memcpy(alloced, &nread, sizeof(int)); + nread -= sizeof(int); + while(nread > 0) { + int bread; + bread = read(servfd, alloced+(allocedsize-nread), nread); + if(bread < 0) { + close(newfd); + return -11; + } + nread -= bread; + } + *conndata = alloced; + } + return(newfd); + } + } +} + +int pa_send_fd(int clifd, int fd, char *conndata) { + int method; + char buf[2]; + buf[0] = (conndata)?1:0; + if(write(clifd, buf, 1) != 1) + return -1; + method = buf[0]; + buf[0] = 0; + if(fd < 0) { + buf[1] = -fd; + if(buf[1] == 0) + buf[1] = 1; + } else { + buf[1] = 0; + } + if(write(clifd, buf, 2) != 2) + return(-1); + if((fd >= 0) && + (ioctl(clifd, I_SENDFD, fd) < 0)) + return(-1); + if(method == 1) { + int towrite; + int written=0; + /* Data to send */ + memcpy(&towrite, conndata, sizeof(int)); + if(write(clifd, &towrite, sizeof(int)) != sizeof(int)) + return -10; + towrite -= sizeof(int); + written += sizeof(int); + while(towrite > 0) { + int retval; + retval = write(clifd, conndata+written, towrite); + if(retval < 0) return -11; + towrite -= retval; + written += retval; + } + } + return(0); +} +int pa_serv_accept(int listenfd, pid_t *pidptr) { + struct strrecvfd recvfd; + + if(ioctl(listenfd, I_RECVFD, &recvfd) < 0) + return(-1); + /* We need th PID not the UID? How do we get that? */ + if(pidptr != NULL) + *pidptr = (pid_t)recvfd.uid; + return(recvfd.fd); +} +#define FIFO_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) +int pa_serv_listen(const char *name) { + int tempfd, fd[2], len; + + if(!name) + name = SERVPATH; + unlink(name); + if((tempfd = creat(name, FIFO_MODE)) < 0) + return(-1); + if (close(tempfd) < 0) + return(-2); + + if(pipe(fd) < 0) + return(-3); + if(ioctl(fd[1], I_PUSH, "connld") < 0) + return(-4); + if(fattach(fd[1], name) < 0) + return(-5); + + return(fd[0]); +} +int pa_cli_conn(const char *name) { + int fd; + pid_t pid; + + if((fd = open(name, O_RDWR)) < 0) + return(-1); + if(isastream(fd) == 0) + return(-2); + pid = getpid(); + if(write(fd, &pid, sizeof(pid_t)) != sizeof(pid_t)) { + close(fd); + return(-3); + } + return(fd); +} + +#else +#ifdef _BSDISH +static struct cmsghdr *cmptr = NULL; +#define CONTROLLEN (sizeof(struct cmsghdr) + sizeof(int)) + +int pa_recv_fd(int servfd, char **conndata) { + int newfd, nread, status; + int method; + static int allocedsize; + static char *alloced; + char *ptr, buf[100]; + struct iovec iov[1]; + struct msghdr msg; + + status = -1; + *conndata = NULL; + if(read(servfd, buf, 1) != 1) + return -1; + method = *buf; + for( ; ; ) { + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(buf); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + if(cmptr == NULL && + (cmptr = (struct cmsghdr *)malloc(CONTROLLEN)) == NULL) + return(-1); + msg.msg_control = (caddr_t) cmptr; + msg.msg_controllen = CONTROLLEN; + if((nread = recvmsg(servfd, &msg, 0)) < 0) + err_sys("recvmsg error"); + else if(nread == 0) { + err_ret("connection closed by server"); + return(-1); + } + for(ptr = buf; ptr < &buf[nread]; ) { + if(*ptr++ == 0) { + if(ptr != &buf[nread-1]) + err_dump("message format error"); + status = *ptr & 255; + if(status == 0) { + if(msg.msg_controllen != CONTROLLEN) + err_dump("status = 0 but no fd"); + newfd = *(int *)CMSG_DATA(cmptr); + } else + newfd = -status; + nread -= 2; + } + } + if(status >= 0) { + if(method == 1) { + int toread; + /* We need to read the serialized connection data */ + if(read(servfd, &nread, sizeof(int)) != sizeof(int)) + return -1; + if(nread > allocedsize) { + if(alloced) free(alloced); + alloced = (char *)malloc(nread); + if(!alloced) { close(newfd); return -10; } + allocedsize = nread; + } + memcpy(alloced, &nread, sizeof(int)); + toread = nread; + nread -= sizeof(int); + while(nread > 0) { + int bread; + bread = read(servfd, alloced+(toread-nread), nread); + if(bread < 0) { + close(newfd); + return -11; + } + nread -= bread; + } + *conndata = alloced; + } + return(newfd); + } + } +} + +int pa_send_fd(int clifd, int fd, char *conndata) { + struct iovec iov[1]; + struct msghdr msg; + int method; + char buf[2]; + + buf[0] = (conndata)?1:0; + if(write(clifd, buf, 1) != 1) + return -1; + method = buf[0]; + iov[0].iov_base = buf; + iov[0].iov_len = 2; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + if(fd < 0) { + msg.msg_control = NULL; + msg.msg_controllen = 0; + buf[1] = -fd; + if(buf[1] == 0) + buf[1] = 1; + } else { + if(cmptr == NULL && + (cmptr = (struct cmsghdr *)malloc(CONTROLLEN)) == NULL) + return(-1); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + cmptr->cmsg_len = CONTROLLEN; + msg.msg_control = (caddr_t) cmptr; + msg.msg_controllen = CONTROLLEN; + *(int *)CMSG_DATA(cmptr) = fd; + buf[1] = 0; + } + buf[0] = 0; + if(sendmsg(clifd, &msg, 0) != 2) + return(-1); + if(method == 1) { + int towrite; + int written=0; + /* Data to send */ + memcpy(&towrite, conndata, sizeof(int)); + if(write(clifd, &towrite, sizeof(int)) != sizeof(int)) + return -10; + towrite -= sizeof(int); + written += sizeof(int); + while(towrite > 0) { + int retval; + retval = write(clifd, conndata+written, towrite); + if(retval < 0) return -11; + towrite -= retval; + written += retval; + } + } + return(0); +} + +int pa_serv_accept(int listenfd, pid_t *pidptr) { + int clifd, len; + time_t staletime; + struct sockaddr_un unix_addr; + struct stat statbuf; + char *cp; + + len = sizeof(unix_addr); + if((clifd = accept(listenfd, (struct sockaddr *) &unix_addr, &len)) < 0) + return(-1); + + len = SUN_LEN(&unix_addr) - sizeof(unix_addr.sun_family); + + unix_addr.sun_path[len] = 0; + if(stat(unix_addr.sun_path, &statbuf) < 0) + return(-2); +#ifdef S_ISSOCK + if(S_ISSOCK(statbuf.st_mode) == 0) + return(-3); +#endif + if((statbuf.st_mode & (S_IRWXG | S_IRWXO)) || + (statbuf.st_mode & S_IRWXU) != S_IRWXU) + return(-4); + + staletime = time(NULL) - STALE; + if(statbuf.st_atime < staletime || + statbuf.st_ctime < staletime || + statbuf.st_mtime < staletime) + return(-5); + + cp = (unix_addr.sun_path + strlen(unix_addr.sun_path) -1); + while(*cp != '-' && cp != unix_addr.sun_path) + cp--; + if(*cp!='-') + return -4; + *pidptr = atoi(cp+1); + unlink(unix_addr.sun_path); + return(clifd); +} + +int pa_serv_listen(const char *name) { + int fd, len; + struct sockaddr_un unix_addr; + + /* create a Unix domain stream socket */ + if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return(-1); + if(!name) + name = SERVPATH; + unlink(name); + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + strcpy(unix_addr.sun_path, name); + len = SUN_LEN(&unix_addr); + /* bind the name to the descriptor */ + if(bind(fd, (struct sockaddr *) &unix_addr, len) < 0) + return(-1); + if(listen(fd, 5) < 0) + return(-1); + return(fd); +} + +int pa_cli_conn(const char *name, const char *from) { + int fd, len; + pid_t pid; + struct sockaddr_un unix_addr; + + /* create a Unix domain stream socket */ + if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return(-1); + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + if(from) + sprintf(unix_addr.sun_path, "%s/bchild-%05d", from, getpid()); + else + sprintf(unix_addr.sun_path, "%s/apache-%05d", CLI_PATH, getpid()); + len = SUN_LEN(&unix_addr); + unlink(unix_addr.sun_path); + if(bind(fd, (struct sockaddr *)&unix_addr, len) < 0) + return(-1); + if(chmod(unix_addr.sun_path, CLI_PERM) < 0) + return(-1); + + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + strcpy(unix_addr.sun_path, name); + unix_addr.sun_path[strlen(name)]='\0'; + len = SUN_LEN(&unix_addr); + + if(connect(fd, (struct sockaddr *) &unix_addr, len) < 0) + return(-1); + pid = getpid(); + if(write(fd, &pid, sizeof(pid_t)) != sizeof(pid_t)) { + close(fd); + return(-3); + } + return(fd); +} +#else +#error Not a supported platform... patch it and mail me.. or mail me uname -a +#endif +#endif diff -ruN apache_1.3.19/src/main/apue.h apache_1.3.19+PA/src/main/apue.h --- apache_1.3.19/src/main/apue.h Wed Dec 31 19:00:00 1969 +++ apache_1.3.19+PA/src/main/apue.h Sun Mar 11 14:32:37 2001 @@ -0,0 +1,33 @@ +/* ====================================================================== + * Copyright (c) 1998-1999 The Johns Hopkins University. + * All rights reserved. + * The following code was written by Theo Schlossnagle for use in the + * Backhand project at The Center for Networking and Distributed Systems + * at The Johns Hopkins University. + * Please refer to the LICENSE file before using this software. + * ====================================================================== +*/ + +#ifndef _APUE_H_ +#define _APUE_H_ + +#ifndef PARENTAL_ACCEPT_DIR +#define PARENTAL_ACCEPT_DIR "/tmp" +#endif + +#include + +#define reestablish_umbilical() pa_cli_conn(PARENTAL_ACCEPT_DIR"/apache_parent", NULL) +#define wait_parent(a, b) pa_recv_fd(a, b) +#define attach_umbilical(a) pa_serv_listen(a) +#define accept_child(a, b) pa_serv_accept(a, b) + +int signal_parent(int, int, char *); +int pa_recv_fd(int, char **); +int pa_send_fd(int, int, char *); +int pa_serv_accept(int, pid_t *); +int pa_serv_listen(const char *); +int pa_cli_conn(const char *, const char *); + + +#endif diff -ruN apache_1.3.19/src/main/http_main.c apache_1.3.19+PA/src/main/http_main.c --- apache_1.3.19/src/main/http_main.c Thu Feb 22 11:06:54 2001 +++ apache_1.3.19+PA/src/main/http_main.c Sun Mar 11 14:24:33 2001 @@ -104,6 +104,9 @@ #include "util_uri.h" #include "scoreboard.h" #include "multithread.h" +#ifdef PARENTAL_ACCEPT +#include "apue.h" /* For nonblocking parental accept and handoff */ +#endif #include #ifdef USE_SHMGET_SCOREBOARD #include @@ -2148,6 +2151,9 @@ /* Called by parent process */ static void reinit_scoreboard(pool *p) { +#ifdef PARENTAL_ACCEPT + int i; +#endif int running_gen = 0; if (ap_scoreboard_image) running_gen = ap_scoreboard_image->global.running_generation; @@ -2174,6 +2180,10 @@ ap_scoreboard_image->global.running_generation = running_gen; force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); #endif +#ifdef PARENTAL_ACCEPT + for(i=0;iparent[i].tether = -1; +#endif } /* Routines called to deal with the scoreboard image @@ -3199,15 +3209,149 @@ /***************************************************************** * Connection structures and accounting... */ - +static int serialize_string(int *prepend, char *string) { + if(!string) { + *prepend = -1; + return sizeof(int); + } + *prepend = strlen(string); + return sizeof(int)+*prepend; +} +static conn_rec *unserialize_connection(pool *p, char *conndata) { + conn_rec *nc; + BUFF *buf; + int size, c=0; + char *cp, *ocp; + cp = conndata; +#define getfrom(a) memcpy(&(a), cp, sizeof(a)); cp+=sizeof(a); +#define newstring(a) { int size; \ + memcpy(&size, cp, sizeof(int)); cp+=sizeof(int); \ + if(size==-1) (a)=NULL; \ + else { \ + (a) = ap_palloc(p, size+1); \ + memcpy((a), cp, size); \ + (a)[size] = '\0'; \ + cp += size; \ + }} + nc = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec)); + getfrom(size); + nc->pool = p; + getfrom(nc->server); + getfrom(nc->base_server); + getfrom(nc->local_addr); + getfrom(nc->remote_addr); + newstring(nc->remote_ip); + newstring(nc->remote_host); + newstring(nc->remote_logname); + newstring(nc->user); + newstring(nc->ap_auth_type); + nc->aborted = *cp; cp++; + nc->keepalive = *cp; cp++; + nc->keptalive = *cp; cp++; + nc->double_reverse = *cp; cp++; + getfrom(nc->keepalives); + newstring(nc->local_ip); + newstring(nc->local_host); + nc->client = (BUFF *) ap_pcalloc(p, sizeof(BUFF)); + buf = nc->client; + buf->pool = p; + getfrom(buf->flags); + getfrom(buf->incnt); + getfrom(buf->outchunk); + getfrom(buf->outcnt); + getfrom(buf->bufsiz); + if(buf->flags & B_RD) { + buf->inbase = (char *)ap_palloc(p, buf->bufsiz); + } else { + buf->inbase = NULL; + } + buf->inptr = buf->inbase; + if(buf->flags & B_WR) { + buf->outbase = (char *)ap_palloc(p, buf->bufsiz + 2); + } else { + buf->outbase = NULL; + } + getfrom(buf->bytes_sent); + return nc; +} +static char *serialize_connection(conn_rec *rec) { + int finalsize = 0; + char *cp, *ocp; + static int allocedsize = 0; + static char *alloced = NULL; + int substrsize, c=0; + BUFF *buf = rec->client; + finalsize = sizeof(finalsize); + finalsize += + sizeof(rec->server) + sizeof(rec->base_server) + + sizeof(rec->local_addr) + sizeof(rec->remote_addr) + + serialize_string(&substrsize, rec->remote_ip) + + serialize_string(&substrsize, rec->remote_host) + + serialize_string(&substrsize, rec->remote_logname) + + serialize_string(&substrsize, rec->user) + + serialize_string(&substrsize, rec->ap_auth_type) + + (4*sizeof(char)) + /* bitfields */ + sizeof(rec->keepalives) + + serialize_string(&substrsize, rec->local_ip) + + serialize_string(&substrsize, rec->local_host); + finalsize += + sizeof(buf->flags) + sizeof(buf->incnt) + + sizeof(buf->outchunk) + sizeof(buf->outcnt) + sizeof(buf->bufsiz) + + sizeof(buf->bytes_sent); + + if(finalsize > allocedsize) { + /* reallocate! */ + if(alloced) free(alloced); + alloced = malloc(finalsize); + if(!alloced) { + ap_log_error(APLOG_MARK, APLOG_CRIT, rec->server, + "malloc() failed during connection serialization"); + exit(1); + } + allocedsize = finalsize; + } + cp = alloced; +#define addon(a) memcpy(cp, &(a), sizeof(a)); cp+=sizeof(a) +#define addonstring(a) { int size, subsize; size = serialize_string(&subsize, (a)); memcpy(cp, &subsize, sizeof(int)); if(subsize>0) memcpy(cp+sizeof(int), (a), size-sizeof(int)); cp+=size; } + addon(finalsize); + addon(rec->server); + addon(rec->base_server); + addon(rec->local_addr); + addon(rec->remote_addr); + addonstring(rec->remote_ip); + addonstring(rec->remote_host); + addonstring(rec->remote_logname); + addonstring(rec->user); + addonstring(rec->ap_auth_type); + *cp = rec->aborted; cp++; + *cp = rec->keepalive; cp++; + *cp = rec->keptalive; cp++; + *cp = rec->double_reverse; cp++; + addon(rec->keepalives); + addonstring(rec->local_ip); + addonstring(rec->local_host); + addon(buf->flags); + addon(buf->incnt); + addon(buf->outchunk); + addon(buf->outcnt); + addon(buf->bufsiz); + addon(buf->bytes_sent); + return alloced; +} static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout, const struct sockaddr_in *remaddr, const struct sockaddr_in *saddr, - int child_num) + int child_num, conn_rec *oconn) { - conn_rec *conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec)); + conn_rec *conn; + if(oconn) { + conn = oconn; + conn->pool = p; + ap_update_vhost_given_ip(conn); + } else { + conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec)); /* Got a connection structure, so initialize what fields we can * (the rest are zeroed out by pcalloc). */ @@ -3226,7 +3370,7 @@ conn->remote_addr = *remaddr; conn->remote_ip = ap_pstrdup(conn->pool, inet_ntoa(conn->remote_addr.sin_addr)); - + } return conn; } @@ -3827,6 +3971,10 @@ struct sockaddr sa_server; struct sockaddr sa_client; listen_rec *lr; + int parental_socket = -1; +#ifdef PARENTAL_ACCEPT + char *conndata; +#endif /* All of initialization is a critical section, we don't care if we're * told to HUP or USR1 before we're done initializing. For example, @@ -3950,6 +4098,61 @@ * Wait for an acceptable connection to arrive. */ +#ifdef PARENTAL_ACCEPT + deferred_die = 0; + usr1_just_die = 0; + /* Snag a pre-accept()ed fd from the parent. But, we no that + there is immediate data to be read on this socket!!! Fast? */ + { + fd_set readset; + FD_ZERO(&readset); + if(parental_socket < 0) { + if((parental_socket = reestablish_umbilical()) < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "Fatal child error, couldn't connect with parent"); + exit(0); + } + } + FD_SET(parental_socket, &readset); + ap_select(parental_socket+1, &readset, NULL, NULL, NULL); + } + + csd = wait_parent(parental_socket, &conndata); + if(conndata) { + /* unserialize the conn_rec data into current_conn */ +/* ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "Receiving keptalive session from parent"); */ + current_conn = unserialize_connection(ptrans, conndata); + conn_io = current_conn->client; +/* ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "unserialized conneciton"); */ + } + /* Then do the pertinent usual stuff */ + if (!(csd >= 0 || errno != EINTR)) { + if (deferred_die) { + /* we didn't get a socket, and we were told to die */ + clean_child_exit(0); + } + } else if(csd < 0) { + /* Socket error */ + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "child: parent->child handoff error"); + clean_child_exit(APEXIT_CHILDFATAL); + } + /* go around again, safe to die */ + usr1_just_die = 1; + if (deferred_die) { + /* ok maybe not, see ya later */ + clean_child_exit(0); + } + /* or maybe we missed a signal, you never know on systems + * without reliable signals + */ + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { + clean_child_exit(0); + } +#else /* Lock around "accept", if necessary */ SAFE_ACCEPT(accept_mutex_on()); @@ -4110,12 +4313,13 @@ } SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ +#endif /* PARENTAL_ACCEPT */ #ifdef TPF if (csd == 0) /* 0 is invalid socket for TPF */ continue; #endif - + if(!current_conn) { /* We've got a socket, let's at least process one request off the * socket before we accept a graceful restart request. */ @@ -4161,7 +4365,7 @@ sfdisc(conn_io->sf_out, bsfio_new(conn_io->pool, conn_io)); sfsetbuf(conn_io->sf_out, NULL, 0); #endif - + } dupped_csd = csd; #if defined(NEED_DUPPED_CSD) if ((dupped_csd = dup(csd)) < 0) { @@ -4183,11 +4387,10 @@ #endif #endif ap_bpushfd(conn_io, csd, dupped_csd); - current_conn = new_connection(ptrans, server_conf, conn_io, (struct sockaddr_in *) &sa_client, (struct sockaddr_in *) &sa_server, - my_child_num); + my_child_num, current_conn); /* * Read and process each request found on our connection @@ -4214,6 +4417,22 @@ break; ap_destroy_pool(r->pool); +#ifdef PARENTAL_ACCEPT + if(conn_io->incnt == 0) { + conndata = serialize_connection(current_conn); +/* ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, + "Serializing conn_rec back to parent (%d bytes)", + *((int *)conndata)); */ + ap_bflush(conn_io); + signal_parent(parental_socket, csd, conndata); + ap_bclose(conn_io); + close(csd); + close(dupped_csd); + csd = -1; + break; + } +#endif + (void) ap_update_child_status(my_child_num, SERVER_BUSY_KEEPALIVE, (request_rec *) NULL); @@ -4240,6 +4459,8 @@ signal(SIGUSR1, usr1_handler); } + if(csd == -1) + continue; /* * Close the connection, being careful to send out whatever is still * in our buffers. If possible, try to avoid a hard close until the @@ -4261,6 +4482,11 @@ ap_bclose(conn_io); } #endif +#ifdef PARENTAL_ACCEPT + /* We don't give a fd back to the parent, but we need to tell it + that we are again available to process requests */ + signal_parent(parental_socket, -1, NULL); +#endif } } @@ -4598,8 +4824,306 @@ } } } +#ifdef PARENTAL_ACCEPT + +#define PA_IGNORE 0 +#define PA_UMBILICAL 1 +#define PA_CHILD_READY 2 +#define PA_CHILD_BUSY 3 +#define PA_LISTEN 4 +#define PA_CLIENT_ACCEPT 5 +#define PA_CLIENT_READY 6 + +int find_server_by_pid(pid_t pid) { + int i; + for(i=0;iparent[i].pid == pid) + return i; + return -1; +} + +int find_available_child(int *fds, fd_set *writeset) { + int i; + for(i=0;iparent[i].tether > 0) && + (ap_scoreboard_image->servers[i].status == SERVER_READY) && + FD_ISSET(ap_scoreboard_image->parent[i].tether, writeset)) + return ap_scoreboard_image->parent[i].tether; + return -1; +} + +#define MAXACCEPT 700 +/* We define this to be the maximum number of accept()ed connections that + have not yet been allocated to children... So we don't compete with the + file descriptors that are needed for each Apache child. */ + +int parent_acceptor() { + int readyclients=0; + int activeclients=0; + int i, umbilical; + int timeout; + int fdsetsize = 1024; + int *fds; + char **fdinfo; + int *fd_times; + struct timeval pa_timeout; + + if(!geteuid() && (setuid(ap_user_id) == -1)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "setuid: unable to change uid"); + exit(-1); + } + + fds = malloc(sizeof(int)*fdsetsize); + memset(fds, PA_IGNORE, fdsetsize); + fdinfo = malloc(sizeof(char *)*fdsetsize); + memset(fdinfo, 0, fdsetsize); + fd_times = malloc(sizeof(int)*fdsetsize); + memset(fd_times, 0, fdsetsize); + + /* FIRST: register our TCP listening sockets */ + if (ap_listeners->next != ap_listeners) { + for(i=0;ifd] = PA_LISTEN; + } + /* SECOND: Parent AF_UNIX socket */ + umbilical = attach_umbilical(PARENTAL_ACCEPT_DIR "/apache_parent"); + if(umbilical<0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "Couldn't create parent socket"); + exit(1); + } + fds[umbilical] = PA_UMBILICAL; + /* LASTLY: event loop! */ + for (;;) { + int now; + /* more than one socket */ + fd_set readset, writeset; + FD_ZERO(&readset); + FD_ZERO(&writeset); + for(i=0;i 0) + if(activeclients>0) + srv = ap_select(fdsetsize, &readset, &writeset, NULL, &pa_timeout); + else + srv = ap_select(fdsetsize, &readset, &writeset, NULL, NULL); + else { + if(activeclients>0) + srv = ap_select(fdsetsize, &readset, NULL, NULL, &pa_timeout); + else + srv = ap_select(fdsetsize, &readset, NULL, NULL, NULL); + pa_timeout.tv_sec = 0L; + pa_timeout.tv_usec = 0L; + ap_select(fdsetsize, NULL, &writeset, NULL, &pa_timeout); + } + if (srv < 0 && errno != EINTR) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); + exit(1); + } + now = time(NULL); /* Granularity of 2 seconds is great!!! ;-) */ + + /* hang up on old people here */ + for(i=0;i server_conf->keep_alive_timeout) { + if(fdinfo[i]) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "Disposing of keptalive sessions (timeout)"); + free(fdinfo[i]); + fdinfo[i] = NULL; + ap_scoreboard_image->global.keptalive--; + } else + ap_scoreboard_image->global.connected--; + close(i); /* No soup for you! */ + activeclients--; + fds[i] = PA_IGNORE; + } + } + } + + if (srv <= 0) { + continue; + } + for (i=0;iparent[server].tether = childfd; + fds[childfd] = PA_CHILD_READY; + } + } + } + } else if(fds[i] == PA_CHILD_BUSY) { + int clifd; + char *conndata; + clifd = pa_recv_fd(i, &conndata); + if(clifd < -9) { /* error reading data */ + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "parent: protocol deviation reading from child"); + fds[i] = PA_IGNORE; + close(i); + } else { + if(clifd >= 0) { + if(conndata) { + int datasize; + memcpy(&datasize, conndata, sizeof(int)); +/* ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "Received serialized conn_rec (%d bytes)", + datasize); */ + fdinfo[clifd] = malloc(datasize); + memcpy(fdinfo[clifd], conndata, datasize); + activeclients++; + fd_times[clifd] = now; + fds[clifd] = PA_CLIENT_ACCEPT; + ap_scoreboard_image->global.keptalive++; + } else + close(clifd); + } + fds[i] = PA_CHILD_READY; + } + ap_scoreboard_image->global.serving--; + } else if(fds[i] == PA_CLIENT_ACCEPT) { + /* find available child and send it there */ + /* If no child exists, then write back a Server busy error */ + activeclients--; + readyclients++; + fds[i] = PA_CLIENT_READY; + } + if(fds[i] == PA_CLIENT_READY) { + int childfd; + if((childfd = find_available_child(fds, &writeset)) >= 0) { + fds[childfd] = PA_CHILD_BUSY; + fds[i] = PA_IGNORE; +/* if(fdinfo[i]) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "Sending keptalive session to child"); */ + if(fdinfo[i]) + ap_scoreboard_image->global.keptalive--; + else + ap_scoreboard_image->global.connected--; + ap_scoreboard_image->global.serving++; + pa_send_fd(childfd, i, fdinfo[i]); + close(i); + fdinfo[i] = NULL; + readyclients--; + } else { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "parent: cannot find available child!"); + } + } else if(fds[i] == PA_LISTEN && + ((readyclients+activeclients) < MAXACCEPT)) { + NET_SIZE_T clen; + struct sockaddr sa_client; + clen = sizeof(sa_client); + csd = ap_accept(i, &sa_client, &clen); + if (csd >= 0 || errno != EINTR) { + activeclients++; + ap_scoreboard_image->global.connected++; + fd_times[csd] = now; + fds[csd] = PA_CLIENT_ACCEPT; + continue; + } + else { + switch (errno) { +#ifdef EPROTO + case EPROTO: +#endif +#ifdef ECONNABORTED + case ECONNABORTED: +#endif +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: +#endif +#ifdef ENETUNREACH + case ENETUNREACH: +#endif + break; +#ifdef ENETDOWN + case ENETDOWN: + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "accept: giving up."); + exit(-1); +#endif /*ENETDOWN*/ +#ifdef TPF + case EINACT: + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "offload device inactive"); + exit(-1); + break; + default: + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "select/accept error (%u)", errno); + exit(-1); +#else + default: + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + exit(1); +#endif + } + } + } + } /* else if(FD_ISSET(i, &writeset)) { + This is actually handled in-band above. + } */ + } + } +} +#endif /***************************************************************** * Executive routines. */ @@ -4648,6 +5172,9 @@ ap_open_logs(server_conf, plog); ap_log_pid(pconf, ap_pid_fname); ap_set_version(); /* create our server_version string */ +#ifdef PARENTAL_ACCEPT + ap_add_version_component("pa/1.0"); +#endif ap_init_modules(pconf, server_conf); version_locked++; /* no more changes to server_version */ SAFE_ACCEPT(accept_mutex_init(pconf)); @@ -4660,7 +5187,10 @@ ap_note_cleanups_for_fd(pconf, scoreboard_fd); } #endif - +#ifdef PARENTAL_ACCEPT + ap_spawn_child(pconf, parent_acceptor, NULL, kill_never, + NULL, NULL, NULL); +#endif set_signals(); if (ap_daemons_max_free < ap_daemons_min_free + 1) /* Don't thrash... */ @@ -5107,7 +5637,7 @@ cio->fd_in = sock_in; conn = new_connection(ptrans, server_conf, cio, (struct sockaddr_in *) &sa_client, - (struct sockaddr_in *) &sa_server, -1); + (struct sockaddr_in *) &sa_server, -1, NULL); while ((r = ap_read_request(conn)) != NULL) { diff -ruN apache_1.3.19/src/modules/standard/mod_status.c apache_1.3.19+PA/src/modules/standard/mod_status.c --- apache_1.3.19/src/modules/standard/mod_status.c Mon Jan 15 12:05:28 2001 +++ apache_1.3.19+PA/src/modules/standard/mod_status.c Sun Mar 11 14:20:44 2001 @@ -425,6 +425,19 @@ else ap_rprintf(r, "BusyServers: %d\nIdleServers: %d\n", busy, ready); + if (!short_report) + ap_rprintf(r, "
Accepted: %d, Keptalive: %d, Serving: %d, Total clients: %d\n", + ap_scoreboard_image->global.connected, + ap_scoreboard_image->global.keptalive, + ap_scoreboard_image->global.serving, + ap_scoreboard_image->global.connected+ap_scoreboard_image->global.keptalive+ap_scoreboard_image->global.serving); + else + ap_rprintf(r, "Accepted: %d\nKeptalive: %d\nServing: %d\nTotal clients: %d\n", + ap_scoreboard_image->global.connected, + ap_scoreboard_image->global.keptalive, + ap_scoreboard_image->global.serving, + ap_scoreboard_image->global.connected+ap_scoreboard_image->global.keptalive+ap_scoreboard_image->global.serving); + /* send the scoreboard 'table' out */ if (!short_report)