From dgaudet@arctic.org Mon May 17 09:30:52 1999 Date: Mon, 17 May 1999 09:30:37 -0700 (PDT) From: Dean Gaudet To: new-httpd@apache.org Subject: [PATCH] select-thread-hybrid-01.patch X-Comment: Visit http://www.arctic.org/~dgaudet/legal for information regarding copyright and disclaimer. Reply-To: new-httpd@apache.org http://www.arctic.org/~dgaudet/apache/2.0/select-thread-hybrid-01.patch OK I've managed to serve a few requests with this, so it's time to post ;) Building: Get and build bind-8.x (I'm using bind-8.2). You want the wonderful eventlib which the ISC folks have written for us, and released under a BSD-style license. This is a convenient select/poll wrapper which provides timer and fd events. Paul's comments say he had inputs from lots of folks -- including the X folks... so I'm guessing Jim Gettys' comments were taken into account... and so I'm just trusting the code rather than looking at it. (Redhat users: I tried using the installed bind-devel kit, but it doesn't work, something is bogus about their library.) Get apache-apr... apply the patch. Use some variant of the config.status below to configure the server. Notes: - I tore up http_main.c. Just completely gutted it. I got tired of stubbing out things. We can add things back incrementally. - no signals, no restart, no multiple processes... - no scoreboard -- major rework needed here. We need to divorce ourselves from the concept of a one-to-one mapping between threads/processes and requests. We'll have connections which are being handled by the event loop. I think the more appropriate thing is to split the scoreboard into a "worker" section, and a "connection" section. But even still -- we can potentially have thousands of connections in progress... that's a lot of shared memory to chew up (shared mem is not pageable on many systems). A better solution is required -- such as building the scoreboard only when serving /server-status. - It's similar to one of manoj/ryan's older servers with the fdqueue at the moment, but using eventlib instead... and it should show how I think we should pass events back and forth between workers and event thread. Dean #!/bin/sh ## ## config.status -- APACI auto-generated configuration restore script ## ## Use this shell script to re-run the APACI configure script for ## restoring your configuration. Additional parameters can be supplied. ## CFLAGS="-g -O2 -Wall" \ LIBS="/home/dgaudet/ap/bind/src/lib/libbind_r.a" \ INCLUDES="-I/home/dgaudet/ap/bind/src/include" \ ./configure \ "--with-layout=Apache" \ "--prefix=/home/dgaudet/ev" \ "--disable-module=status" \ "$@" ? out ? a ? main/a ? main/http_main_old.c Index: include/http_conf_globals.h =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/include/http_conf_globals.h,v retrieving revision 1.8 diff -u -r1.8 http_conf_globals.h --- http_conf_globals.h 1999/03/15 15:34:49 1.8 +++ http_conf_globals.h 1999/05/17 16:17:15 @@ -116,6 +116,8 @@ */ extern char ap_coredump_dir[MAX_STRING_LEN]; +server_rec *server_conf; /* XXX this is shared now, prefix with ap_ */ + #ifdef __cplusplus } #endif Index: include/http_event.h =================================================================== RCS file: http_event.h diff -N http_event.h --- /dev/null Mon May 17 09:17:18 1999 +++ http_event.h Mon May 17 09:17:16 1999 @@ -0,0 +1,54 @@ +#ifndef HTTP_EVENT_H_INCLUDED +#define HTTP_EVENT_H_INCLUDED + +/******************************************************************************/ + +/* the event loop --> worker queue */ +/* XXX: generalise this */ +typedef struct workq_t workq_t; +struct workq_t { + workq_t *next; + enum { + WORKQ_NEW_CONNECTION, + WORKQ_MAX + } type; + union { + struct { + int fd; + struct sockaddr sa_client; + } new_connection; + } d; +}; + +void workq_enqueue(workq_t *item); +workq_t *workq_dequeue(void); + + +/******************************************************************************/ + +/* the worker thread --> event loop queue */ + +typedef struct responseq_t responseq_t; +struct responseq_t { + enum { + RESPONSEQ_GOES_HERE, + RESPONSEQ_MAX + } type; + union { + struct { + int blah; + } fill_in_the_blank; + } d; +}; + +void responseq_enqueue(responseq_t *resp); +responseq_t *responseq_dequeue(void); + +/******************************************************************************/ + +void *ap_worker_thread(void *); + +void ap_event_loop(void); + + +#endif Index: include/http_main.h =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/include/http_main.h,v retrieving revision 1.5 diff -u -r1.5 http_main.h --- http_main.h 1999/04/09 04:10:35 1.5 +++ http_main.h 1999/05/17 16:17:16 @@ -120,8 +120,6 @@ int ap_get_timeout(request_rec *r); API_EXPORT(void) ap_child_terminate(request_rec *r); -int ap_update_child_status(int child_num, int thread_num, int status, request_rec *r); -void ap_time_process_request(int child_num, int thread_num, int status); unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x); API_EXPORT(int) ap_check_alarm(void); Index: include/httpd.h =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/include/httpd.h,v retrieving revision 1.14 diff -u -r1.14 httpd.h --- httpd.h 1999/04/14 21:03:19 1.14 +++ httpd.h 1999/05/17 16:17:16 @@ -819,8 +819,6 @@ /* Information about the connection itself */ - int child_num; /* The number of the child handling conn_rec */ - int thread_num; /* number of the thread handling conn_rec */ BUFF *client; /* Connection to the guy */ /* Who is the client? */ @@ -1042,7 +1040,6 @@ API_EXPORT(int) ap_can_exec(const struct stat *); API_EXPORT(void) ap_chdir_file(const char *file); API_EXPORT(int) ap_get_max_daemons(void); -API_EXPORT(const server_rec *) ap_get_server_conf(void); #ifndef HAVE_CANONICAL_FILENAME /* @@ -1152,6 +1149,10 @@ #define BO_TIMEOUT 2 #define ap_is_aborted(abort) (abort->aborted ==1) + +/* XXX get these working again */ +#define NO_OTHER_CHILD +#define NO_RELIABLE_PIPED_LOGS #ifdef __cplusplus } Index: include/scoreboard.h =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/include/scoreboard.h,v retrieving revision 1.7 diff -u -r1.7 scoreboard.h --- scoreboard.h 1999/04/09 04:10:35 1.7 +++ scoreboard.h 1999/05/17 16:17:16 @@ -209,8 +209,13 @@ void increment_counts(int child_num, int thread_num, request_rec *r); void update_scoreboard_global(void); API_EXPORT(int) find_child_by_pid(int pid); -int ap_update_child_status(int child_num, int thread_num, int status, request_rec *r); -void ap_time_process_request(int child_num, int thread_num, int status); +#if 0 +int ap_update_child_status(conn_rec *, int status, request_rec *r); +void ap_time_process_request(conn_rec *, int status); +#else +#define ap_update_child_status(a,b,c) (0) +#define ap_time_process_request(a,b) do {} while(0) +#endif Index: main/Makefile.tmpl =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/main/Makefile.tmpl,v retrieving revision 1.8 diff -u -r1.8 Makefile.tmpl --- Makefile.tmpl 1999/04/17 03:35:54 1.8 +++ Makefile.tmpl 1999/05/17 16:17:16 @@ -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 \ - scoreboard.o rfc1413.o fdqueue.o acceptlock.o http_accept.o + rfc1413.o http_event.o http_worker.o .c.o: $(CC) -c $(INCLUDES) $(CFLAGS) $< Index: main/http_core.c =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/main/http_core.c,v retrieving revision 1.12 diff -u -r1.12 http_core.c --- http_core.c 1999/04/14 21:03:28 1.12 +++ http_core.c 1999/05/17 16:17:18 @@ -595,7 +595,7 @@ && conn->remote_host == NULL && (type == REMOTE_DOUBLE_REV || hostname_lookups != HOSTNAME_LOOKUP_OFF)) { - old_stat = ap_update_child_status(conn->child_num, conn->thread_num, + old_stat = ap_update_child_status(conn, SERVER_BUSY_DNS, (request_rec*)NULL); /* ZZZ change to AP functions. */ iaddr = &(conn->remote_addr.sin_addr); @@ -623,8 +623,7 @@ } } if (old_stat != SERVER_DEAD) { - (void)ap_update_child_status(conn->child_num, conn->thread_num, - old_stat, (request_rec*)NULL); + (void)ap_update_child_status(conn, old_stat, (request_rec*)NULL); } /* Index: main/http_event.c =================================================================== RCS file: http_event.c diff -N http_event.c --- /dev/null Mon May 17 09:17:18 1999 +++ http_event.c Mon May 17 09:17:18 1999 @@ -0,0 +1,312 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. 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 by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#define CORE_PRIVATE + +#include "httpd.h" +#include "http_event.h" +#include "http_log.h" +#include "http_conf_globals.h" + +#include +#include /* thank you ISC! */ + +/* XXX -- do this dynamically */ +#define MAX_WORKERS 10 +static pthread_t worker_thread[MAX_WORKERS]; + +static evContext event_context; +static int response_queue[2]; + +/******************************************************************************/ + +/* the event loop --> worker queue */ +/* XXX: generalise this */ + +static pthread_mutex_t workq_mutex; +static pthread_cond_t workq_cond; +static workq_t *workq_head; +static workq_t **workq_tail; + +static void workq_init(void) +{ + pthread_mutex_init(&workq_mutex, NULL); + pthread_cond_init(&workq_cond, NULL); + workq_head = NULL; + workq_tail = &workq_head; +} + +void workq_enqueue(workq_t *item) +{ + pthread_mutex_lock(&workq_mutex); + item->next = NULL; + *workq_tail = item; + workq_tail = &item->next; + pthread_cond_signal(&workq_cond); + pthread_mutex_unlock(&workq_mutex); +} + +workq_t *workq_dequeue(void) +{ + workq_t *res; + + pthread_mutex_lock(&workq_mutex); + while (workq_head == NULL) { + pthread_cond_wait(&workq_cond, &workq_mutex); + } + res = workq_head; + workq_head = res->next; + if (!workq_head) { + workq_tail = &workq_head; + } + pthread_mutex_unlock(&workq_mutex); + return res; +} + +/******************************************************************************/ + +/* the worker thread --> event loop queue */ + +static int response_queue[2]; + +static void response_event(evContext ctx, void *uap, int fd, int eventmask) +{ + /* no events to handle yet */ +} + +static void responseq_init(void) +{ + /* create the queue for handing responses back to the event thread */ + if (pipe(response_queue)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "pipe(response_queue) failed"); + exit(1); + } + if (evSelectFD(event_context, response_queue[0], EV_READ, response_event, NULL, NULL)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "evSelectFD(response_queue[0]) failed"); + exit(1); + } +} + +void responseq_enqueue(responseq_t *resp) +{ + int rc; + + do { + rc = write(response_queue[1], &resp, sizeof(resp)); + } while (rc < 0 && errno == EINTR); + if (rc != sizeof(resp)) { + /* XXX handle this more gracefully... */ + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "response_queue write failed"); + exit(1); + } +} + +responseq_t *responseq_dequeue(void) +{ + int rc; + responseq_t *resp; + + do { + rc = read(response_queue[0], &resp, sizeof(resp)); + } while (rc < 0 && errno == EINTR); + if (rc < 0) { + if (errno == EAGAIN) { + return NULL; + } + /* XXX handle this more gracefully... */ + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "response_queue read failed"); + exit(1); + } + else if (rc != sizeof(resp)) { + /* XXX handle this more gracefully... */ + ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server_conf, + "response_queue read failed"); + exit(1); + } + return resp; +} + +/******************************************************************************/ + +static void accept_event(evContext ctx, void *uap, int fd, int eventmask) +{ + int csd; + struct sockaddr sa_client; + int clen; + + for (;;) { + clen = sizeof(sa_client); + csd = accept(fd, &sa_client, &clen); + if (csd >= 0) { + workq_t *work = malloc(sizeof(workq_t)); + work->type = WORKQ_NEW_CONNECTION; + work->d.new_connection.fd = csd; + work->d.new_connection.sa_client = sa_client; + workq_enqueue(work); + } + else { + switch (errno) { + case EAGAIN: + return; + case EINTR: + break; + + /* Our old behaviour here was to continue after accept() + * errors. But this leads us into lots of troubles + * because most of the errors are quite fatal. For + * example, EMFILE can be caused by slow descriptor + * leaks (say in a 3rd party module, or libc). It's + * foolish for us to continue after an EMFILE. We also + * seem to tickle kernel bugs on some platforms which + * lead to never-ending loops here. So it seems best + * to just exit in most cases. + */ +#ifdef EPROTO + /* EPROTO on certain older kernels really means + * ECONNABORTED, so we need to ignore it for them. + * See discussion in new-httpd archives nh.9701 + * search for EPROTO. + * + * Also see nh.9603, search for EPROTO: + * There is potentially a bug in Solaris 2.x x<6, + * and other boxes that implement tcp sockets in + * userland (i.e. on top of STREAMS). On these + * systems, EPROTO can actually result in a fatal + * loop. See PR#981 for example. It's hard to + * handle both uses of EPROTO. + */ + case EPROTO: +#endif +#ifdef ECONNABORTED + case ECONNABORTED: +#endif + /* Linux generates the rest of these, other tcp + * stacks (i.e. bsd) tend to hide them behind + * getsockopt() interfaces. They occur when + * the net goes sour or the client disconnects + * after the three-way handshake has been done + * in the kernel but before userland has picked + * up the socket. + */ +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: +#endif +#ifdef ENETUNREACH + case ENETUNREACH: +#endif + break; + default: + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + exit(1); + } + } + } +} + + +/******************************************************************************/ + + +void ap_event_loop(void) +{ + int i; + listen_rec *lr; + + if (evCreate(&event_context)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "evCreate failed"); + exit(1); + } + + /* insert events for the listening sockets */ + for (lr = ap_listeners; lr; lr = lr->next) { + if (evSelectFD(event_context, lr->fd, EV_READ, accept_event, lr, NULL)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "evSelectFD failed"); + exit(1); + } + } + + responseq_init(); + workq_init(); + + /* create worker threads */ + for (i = 0; i < MAX_WORKERS; ++i) { + if (pthread_create(&worker_thread[i], NULL, ap_worker_thread, NULL)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "pthread_create: unable to create event thread"); + exit(1); + } + } + + if (evMainLoop(event_context)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "evMainLoop failed"); + exit(1); + } +} Index: main/http_main.c =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/main/http_main.c,v retrieving revision 1.79 diff -u -r1.79 http_main.c --- http_main.c 1999/04/29 20:09:44 1.79 +++ http_main.c 1999/05/17 16:17:19 @@ -93,24 +93,17 @@ #include "util_uri.h" #include "scoreboard.h" +#if 0 #include "http_accept.h" #include #include +#endif + +#include "http_event.h" + #include -#ifdef USE_SHMGET_SCOREBOARD -#include -#include -#include -#endif - #include "pthread.h" -/*#include initialization if any -#include threadabstractionlayer; -#include networkiolayer -#include lock */ - -#include /* This next function is never used. It is here to ensure that if we * make all the modules into shared libraries that core httpd still @@ -123,14 +116,10 @@ ap_add_cgi_vars(NULL); } -#include "explain.h" - #if !defined(max) #define max(a,b) (a > b ? a : b) #endif -DEF_Explain - /* Defining GPROF when compiling uses the moncontrol() function to * disable gprof profiling in the parent, and enable it only for * request processing in children (or in one_process mode). It's @@ -217,36 +206,9 @@ pool *pchild; /* Pool for httpd child stuff */ -/* thread local storage code that isn't used right now */ - -#if 0 -/* stuff that needs thread local store in main */ -typedef struct { - jmp_buf thread_exit; - int generation; -} tls_main_t; - -static pthread_key_t tls_main_key; /* ZZZZ */ - -static tls_main_t *gettls(pthread_key_t tls_main_key) -{ - tls_main_t *tls_p; - tls_p = pthread_getspecific(tls_main_key); - if (!tls_p) { - tls_p = NULL; - fprintf(stderr, "pthread_getspecific failed\n"); - } - - return tls_p; -} -#define tls() ((tls_main_t *) gettls(tls_main_key)) /* ZZZZZ */ -#endif - - - /* *Non*-shared http_main globals... */ -static server_rec *server_conf; +server_rec *server_conf; /* XXX this is shared now */ static pid_t pgrp; /* one_process --- debugging mode variable; can be set from the command line @@ -346,11 +308,6 @@ return max_daemons_limit; } -API_EXPORT(const server_rec *) ap_get_server_conf(void) -{ - return (server_conf); -} - /* * This routine adds the real server base identity to the version string, @@ -386,12 +343,6 @@ static void clean_child_exit(int code) __attribute__ ((noreturn)); */ void clean_child_exit(int code) { - int child_num = find_child_by_pid(getpid()); - int i; - - for (i = 0; i < ap_threads_per_child + ap_acceptors_per_child; i++) - ap_update_child_status(child_num, i, SERVER_DEAD, (request_rec *) NULL); - if (pchild) { ap_child_exit_modules(pchild, server_conf); ap_destroy_pool(pchild); @@ -399,15 +350,10 @@ exit(code); } - -static void graceful_killer(void) -{ - stop_accepting_connections(pconf); -} +/* XXX huh? */ int ap_get_timeout(request_rec *r) { - if (r->connection->keptalive) { return(r->server->keep_alive_timeout); } @@ -416,1035 +362,207 @@ } } -static void usage(char *bin) + +static int make_sock(pool *p, const struct sockaddr_in *server) + /* abstract sockaddr_in */ { - char pad[MAX_STRING_LEN]; - unsigned i; + int s; + int one = 1; + char addr[512]; - for (i = 0; i < strlen(bin); i++) - pad[i] = ' '; - pad[i] = '\0'; -#ifdef SHARED_CORE - fprintf(stderr, "Usage: %s [-R directory] [-d directory] [-f file]\n", bin); -#else - fprintf(stderr, "Usage: %s [-d directory] [-f file]\n", bin); -#endif - fprintf(stderr, " %s [-C \"directive\"] [-c \"directive\"]\n", pad); - fprintf(stderr, " %s [-v] [-V] [-h] [-l] [-L] [-S] [-t]\n", pad); - fprintf(stderr, "Options:\n"); -#ifdef SHARED_CORE - fprintf(stderr, " -R directory : specify an alternate location for shared object files\n"); + if (server->sin_addr.s_addr != htonl(INADDR_ANY)) + ap_snprintf(addr, sizeof(addr), "address %s port %d", + inet_ntoa(server->sin_addr), ntohs(server->sin_port)); + else + ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port)); + + /* note that because we're about to slack we don't use psocket */ + if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: failed to get a socket for %s", addr); + printf("make_sock: failed to get socket for %s\n", addr); + exit(1); + } + + /* Solaris (probably versions 2.4, 2.5, and 2.5.1 with various levels + * of tcp patches) has some really weird bugs where if you dup the + * socket now it breaks things across SIGHUP restarts. It'll either + * be unable to bind, or it won't respond. + */ +#if defined (SOLARIS2) && SOLARIS2 < 260 +#define WORKAROUND_SOLARIS_BUG #endif - fprintf(stderr, " -D name : define a name for use in directives\n"); - fprintf(stderr, " -d directory : specify an alternate initial ServerRoot\n"); - fprintf(stderr, " -f file : specify an alternate ServerConfigFile\n"); - fprintf(stderr, " -C \"directive\" : process directive before reading config files\n"); - fprintf(stderr, " -c \"directive\" : process directive after reading config files\n"); - fprintf(stderr, " -v : show version number\n"); - fprintf(stderr, " -V : show compile settings\n"); - fprintf(stderr, " -h : list available command line options (this page)\n"); - fprintf(stderr, " -l : list compiled-in modules\n"); - fprintf(stderr, " -L : list available configuration directives\n"); - fprintf(stderr, " -S : show parsed settings (currently only vhost settings)\n"); - fprintf(stderr, " -t : run syntax test for configuration files only\n"); -#ifdef WIN32 - fprintf(stderr, " -k shutdown : tell running Apache to shutdown\n"); - fprintf(stderr, " -k restart : tell running Apache to do a graceful restart\n"); + + /* PR#1282 Unixware 1.x appears to have the same problem as solaris */ +#if defined (UW) && UW < 200 +#define WORKAROUND_SOLARIS_BUG #endif - exit(1); -} -/* - * More machine-dependent networking gooo... on some systems, - * you've got to be *really* sure that all the packets are acknowledged - * before closing the connection, since the client will not be able - * to see the last response if their TCP buffer is flushed by a RST - * packet from us, which is what the server's TCP stack will send - * if it receives any request data after closing the connection. - * - * In an ideal world, this function would be accomplished by simply - * setting the socket option SO_LINGER and handling it within the - * server's TCP stack while the process continues on to the next request. - * Unfortunately, it seems that most (if not all) operating systems - * block the server process on close() when SO_LINGER is used. - * For those that don't, see USE_SO_LINGER below. For the rest, - * we have created a home-brew lingering_close. - * - * Many operating systems tend to block, puke, or otherwise mishandle - * calls to shutdown only half of the connection. You should define - * NO_LINGCLOSE in ap_config.h if such is the case for your system. - */ -#ifndef MAX_SECS_TO_LINGER -#define MAX_SECS_TO_LINGER 30 + + /* PR#1973 NCR SVR4 systems appear to have the same problem */ +#if defined (MPRAS) +#define WORKAROUND_SOLARIS_BUG #endif -#ifdef USE_SO_LINGER -#define NO_LINGCLOSE /* The two lingering options are exclusive */ +#ifndef WORKAROUND_SOLARIS_BUG + s = ap_slack(s, AP_SLACK_HIGH); -static void sock_enable_linger(int s) // ZZZZZ abstract the socket, s -{ - struct linger li; // ZZZZZ SocketOptions... + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#endif - li.l_onoff = 1; - li.l_linger = MAX_SECS_TO_LINGER; +#ifndef MPE +/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */ +#ifndef _OSD_POSIX + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr); + printf("make_sock: failed to setsockopt for %s\n", addr); + close(s); + return 0; + } +#endif /*_OSD_POSIX*/ + one = 1; +#ifndef BEOS +/* BeOS does not support SO_KEEPALIVE */ + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr); + close(s); + return 0; + } +#endif +#endif - if (setsockopt(s, SOL_SOCKET, SO_LINGER, // ZZZZZ abstract, return SUCCESS or not - (char *) &li, sizeof(struct linger)) < 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, - "setsockopt: (SO_LINGER)"); - /* not a fatal error */ + /* + * To send data over high bandwidth-delay connections at full + * speed we must force the TCP window to open wide enough to keep the + * pipe full. The default window size on many systems + * is only 4kB. Cross-country WAN connections of 100ms + * at 1Mb/s are not impossible for well connected sites. + * If we assume 100ms cross-country latency, + * a 4kB buffer limits throughput to 40kB/s. + * + * To avoid this problem I've added the SendBufferSize directive + * to allow the web master to configure send buffer size. + * + * The trade-off of larger buffers is that more kernel memory + * is consumed. YMMV, know your customers and your network! + * + * -John Heidemann 25-Oct-96 + * + * If no size is specified, use the kernel default. + */ +#ifndef BEOS /* BeOS does not support SO_SNDBUF */ + if (server_conf->send_buffer_size) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + (char *) &server_conf->send_buffer_size, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "make_sock: failed to set SendBufferSize for %s, " + "using default", addr); + /* not a fatal error */ + } } -} +#endif -#else -#define sock_enable_linger(s) /* NOOP */ -#endif /* USE_SO_LINGER */ +#ifdef MPE +/* MPE requires CAP=PM and GETPRIVMODE to bind to ports less than 1024 */ + if (ntohs(server->sin_port) < 1024) + GETPRIVMODE(); +#endif + if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: could not bind to %s", addr); +#ifdef MPE + if (ntohs(server->sin_port) < 1024) + GETUSERMODE(); +#endif + close(s); + exit(1); + } +#ifdef MPE + if (ntohs(server->sin_port) < 1024) + GETUSERMODE(); +#endif -#ifndef NO_LINGCLOSE + if (listen(s, ap_listenbacklog) == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "make_sock: unable to listen for connections on %s", addr); + close(s); + exit(1); + } -/* Since many clients will abort a connection instead of closing it, - * attempting to log an error message from this routine will only - * confuse the webmaster. There doesn't seem to be any portable way to - * distinguish between a dropped connection and something that might be - * worth logging. - */ -/*ZZZ this routine needs to be adapted for use with poll()*/ -static void lingering_close(request_rec *r) -{ - /*ZZZ remove the hardwired 512. This is an IO Buffer Size */ - char dummybuf[512]; - struct pollfd pd; - int lsd; - int max_wait; +#ifdef WORKAROUND_SOLARIS_BUG + s = ap_slack(s, AP_SLACK_HIGH); - /* Prevent a slow-drip client from holding us here indefinitely */ + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#endif - max_wait = 30; - ap_bsetopt(r->connection->client, BO_TIMEOUT, &max_wait); +#ifndef WIN32 + /* protect various fd_sets */ + if (s >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "make_sock: problem listening on %s, filedescriptor (%u) " + "larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", addr, s, FD_SETSIZE); + close(s); + return 0; + } +#endif - /* Send any leftover data to the client, but never try to again */ + return s; +} - if (ap_bflush(r->connection->client) == -1) { - ap_bclose(r->connection->client); - return; - } - ap_bsetflag(r->connection->client, B_EOUT, 1); - /* Close our half of the connection --- send the client a FIN */ +/* + * During a restart we keep track of the old listeners here, so that we + * can re-use the sockets. We have to do this because we won't be able + * to re-open the sockets ("Address already in use"). + * + * Unlike the listeners ring, old_listeners is a NULL terminated list. + * + * copy_listeners() makes the copy, find_listener() finds an old listener + * and close_unused_listener() cleans up whatever wasn't used. + */ +static listen_rec *old_listeners; - lsd = r->connection->client->fd; +/* unfortunately copy_listeners may be called before listeners is a ring */ +static void copy_listeners(pool *p) +{ + listen_rec *lr; - if ((shutdown(lsd, 1) != 0) /* ZZZ abstract shutdown */ - || ap_is_aborted(r->connection)) { - ap_bclose(r->connection->client); + ap_assert(old_listeners == NULL); + if (ap_listeners == NULL) { return; } - - /* Set up to wait for readable data on socket... */ - pd.fd = lsd; - pd.events = POLLIN; - - /* Wait for readable data or error condition on socket; - * slurp up any data that arrives... We exit when we go for an - * interval of tv length without getting any more data, get an error - * from poll(), get an error or EOF on a read, or the timer expires. - */ - /* We use a 2 second timeout because current (Feb 97) browsers - * fail to close a connection after the server closes it. Thus, - * to avoid keeping the child busy, we are only lingering long enough - * for a client that is actively sending data on a connection. - * This should be sufficient unless the connection is massively - * losing packets, in which case we might have missed the RST anyway. - * These parameters are reset on each pass, since they might be - * changed by poll. - */ + lr = ap_listeners; do { - pd.revents = 0; - } while ((poll(&pd, 1, 2) == 1) - && read(lsd, dummybuf, sizeof(dummybuf))); - /* && (time() = epoch) < max_wait); */ /* ZZZZ time function is not good... */ - - /* Should now have seen final ack. Safe to finally kill socket */ - ap_bclose(r->connection->client); + listen_rec *nr = malloc(sizeof *nr); + if (nr == NULL) { + fprintf(stderr, "Ouch! malloc failed in copy_listeners()\n"); + exit(1); + } + *nr = *lr; + ap_kill_cleanups_for_socket(p, nr->fd); + nr->next = old_listeners; + old_listeners = nr; + lr = lr->next; + } while (lr && lr != ap_listeners); } -#endif /* ndef NO_LINGCLOSE */ -/***************************************************************** - * dealing with other children - */ -#ifndef NO_OTHER_CHILD -API_EXPORT(void) ap_register_other_child(int pid, - void (*maintenance) (int reason, void *, ap_wait_t status), - void *data, int write_fd) -{ - other_child_rec *ocr; - - ocr = ap_palloc(pconf, sizeof(*ocr)); - ocr->pid = pid; - ocr->maintenance = maintenance; - ocr->data = data; - ocr->write_fd = write_fd; - ocr->next = other_children; - other_children = ocr; -} - -/* note that since this can be called by a maintenance function while we're - * scanning the other_children list, all scanners should protect themself - * by loading ocr->next before calling any maintenance function. - */ -API_EXPORT(void) ap_unregister_other_child(void *data) +static int find_listener(listen_rec *lr) { - other_child_rec **pocr, *nocr; - - for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) { - if ((*pocr)->data == data) { - nocr = (*pocr)->next; - (*(*pocr)->maintenance) (OC_REASON_UNREGISTER, (*pocr)->data, -1); - *pocr = nocr; - /* XXX: um, well we've just wasted some space in pconf ? */ - return; + listen_rec *or; + for (or = old_listeners; or; or = or->next) { + if (!memcmp(&or->local_addr, &lr->local_addr, sizeof(or->local_addr))) { + or->used = 1;+ return or->fd; } } + return -1; } -/* test to ensure that the write_fds are all still writable, otherwise - * invoke the maintenance functions as appropriate */ -static void probe_writable_fds(void) -{ - return; -#if 0 - fd_set writable_fds; - int fd_max; - other_child_rec *ocr, *nocr; - struct timeval tv; - int rc; - if (other_children == NULL) - return; - - fd_max = 0; - FD_ZERO(&writable_fds); - do { - for (ocr = other_children; ocr; ocr = ocr->next) { - if (ocr->write_fd == -1) - continue; - FD_SET(ocr->write_fd, &writable_fds); - if (ocr->write_fd > fd_max) { - fd_max = ocr->write_fd; - } - } - if (fd_max == 0) - return; - - tv.tv_sec = 0; - tv.tv_usec = 0; - rc = ap_select(fd_max + 1, NULL, &writable_fds, NULL, &tv); - } while (rc == -1 && errno == EINTR); - - if (rc == -1) { - /* XXX: uhh this could be really bad, we could have a bad file - * descriptor due to a bug in one of the maintenance routines */ - ap_log_unixerr("probe_writable_fds", "select", - "could not probe writable fds", server_conf); - return; - } - if (rc == 0) - return; - - for (ocr = other_children; ocr; ocr = nocr) { - nocr = ocr->next; - if (ocr->write_fd == -1) - continue; - if (FD_ISSET(ocr->write_fd, &writable_fds)) - continue; - (*ocr->maintenance) (OC_REASON_UNWRITABLE, ocr->data, -1); - } -#endif -} - -/* possibly reap an other_child, return 0 if yes, -1 if not */ -static int reap_other_child(int pid, ap_wait_t status) -{ - other_child_rec *ocr, *nocr; - - for (ocr = other_children; ocr; ocr = nocr) { - nocr = ocr->next; - if (ocr->pid != pid) - continue; - ocr->pid = -1; - (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); - return 0; - } - return -1; -} -#endif - -static void reclaim_child_processes(int terminate) -{ - int i, status; - long int waittime = 1024 * 16; /* in usecs */ - struct timeval tv; - int waitret, tries; - int not_dead_yet; -#ifndef NO_OTHER_CHILD - other_child_rec *ocr, *nocr; -#endif - - ap_sync_scoreboard_image(); - - for (tries = terminate ? 4 : 1; tries <= 9; ++tries) { - /* don't want to hold up progress any more than - * necessary, but we need to allow children a few moments to exit. - * Set delay with an exponential backoff. - */ - tv.tv_sec = waittime / 1000000; - tv.tv_usec = waittime % 1000000; - waittime = waittime * 4; - ap_select(0, NULL, NULL, NULL, &tv); - - /* now see who is done */ - not_dead_yet = 0; - for (i = 0; i < max_daemons_limit; ++i) { - int pid = ap_scoreboard_image->parent[i].pid; - - if (pid == my_pid || pid == 0) - continue; - - waitret = waitpid(pid, &status, WNOHANG); - if (waitret == pid || waitret == -1) { - ap_scoreboard_image->parent[i].pid = 0; - continue; - } - ++not_dead_yet; - switch (tries) { - case 1: /* 16ms */ - case 2: /* 82ms */ - break; - case 3: /* 344ms */ - case 4: /* 16ms */ - case 5: /* 82ms */ - case 6: /* 344ms */ - case 7: /* 1.4sec */ - /* ok, now it's being annoying */ - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, - server_conf, - "child process %d still did not exit, sending a SIGTERM", - pid); - kill(pid, SIGTERM); - break; - case 8: /* 6 sec */ - /* die child scum */ - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, - "child process %d still did not exit, sending a SIGKILL", - pid); - kill(pid, SIGKILL); - break; - case 9: /* 14 sec */ - /* gave it our best shot, but alas... If this really - * is a child we are trying to kill and it really hasn't - * exited, we will likely fail to bind to the port - * after the restart. - */ - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, - "could not make child process %d exit, " - "attempting to continue anyway", pid); - break; - } - } -#ifndef NO_OTHER_CHILD - for (ocr = other_children; ocr; ocr = nocr) { - nocr = ocr->next; - if (ocr->pid == -1) - continue; - - waitret = waitpid(ocr->pid, &status, WNOHANG); - if (waitret == ocr->pid) { - ocr->pid = -1; - (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); - } - else if (waitret == 0) { - (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, -1); - ++not_dead_yet; - } - else if (waitret == -1) { - /* uh what the heck? they didn't call unregister? */ - ocr->pid = -1; - (*ocr->maintenance) (OC_REASON_LOST, ocr->data, -1); - } - } -#endif - if (!not_dead_yet) { - /* nothing left to wait for */ - break; - } - } -} - -/* Finally, this routine is used by the caretaker process to wait for - * a while... - */ - -/* number of calls to wait_or_timeout between writable probes */ -#ifndef INTERVAL_OF_WRITABLE_PROBES -#define INTERVAL_OF_WRITABLE_PROBES 10 -#endif -static int wait_or_timeout_counter; - -static int wait_or_timeout(ap_wait_t *status) -{ - struct timeval tv; - int ret; - - ++wait_or_timeout_counter; - if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) { - wait_or_timeout_counter = 0; -#ifndef NO_OTHER_CHILD - probe_writable_fds(); -#endif - } - ret = waitpid(-1, status, WNOHANG); - if (ret == -1 && errno == EINTR) { - return -1; - } - if (ret > 0) { - return ret; - } -#ifdef NEED_WAITPID - if ((ret = reap_children(status)) > 0) { - return ret; - } -#endif - tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000; - tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000; - ap_select(0, NULL, NULL, NULL, &tv); - return -1; -} - -#if defined(NSIG) -#define NumSIG NSIG -#elif defined(_NSIG) -#define NumSIG _NSIG -#elif defined(__NSIG) -#define NumSIG __NSIG -#else -#define NumSIG 32 /* for 1998's unixes, this is still a good assumption */ -#endif - -#ifdef SYS_SIGLIST /* platform has sys_siglist[] */ -#define INIT_SIGLIST() /*nothing*/ -#else /* platform has no sys_siglist[], define our own */ -#define SYS_SIGLIST ap_sys_siglist -#define INIT_SIGLIST() siglist_init(); - -const char *ap_sys_siglist[NumSIG]; - -static void siglist_init(void) -{ - int sig; - - ap_sys_siglist[0] = "Signal 0"; -#ifdef SIGHUP - ap_sys_siglist[SIGHUP] = "Hangup"; -#endif -#ifdef SIGINT - ap_sys_siglist[SIGINT] = "Interrupt"; -#endif -#ifdef SIGQUIT - ap_sys_siglist[SIGQUIT] = "Quit"; -#endif -#ifdef SIGILL - ap_sys_siglist[SIGILL] = "Illegal instruction"; -#endif -#ifdef SIGTRAP - ap_sys_siglist[SIGTRAP] = "Trace/BPT trap"; -#endif -#ifdef SIGIOT - ap_sys_siglist[SIGIOT] = "IOT instruction"; -#endif -#ifdef SIGABRT - ap_sys_siglist[SIGABRT] = "Abort"; -#endif -#ifdef SIGEMT - ap_sys_siglist[SIGEMT] = "Emulator trap"; -#endif -#ifdef SIGFPE - ap_sys_siglist[SIGFPE] = "Arithmetic exception"; -#endif -#ifdef SIGKILL - ap_sys_siglist[SIGKILL] = "Killed"; -#endif -#ifdef SIGBUS - ap_sys_siglist[SIGBUS] = "Bus error"; -#endif -#ifdef SIGSEGV - ap_sys_siglist[SIGSEGV] = "Segmentation fault"; -#endif -#ifdef SIGSYS - ap_sys_siglist[SIGSYS] = "Bad system call"; -#endif -#ifdef SIGPIPE - ap_sys_siglist[SIGPIPE] = "Broken pipe"; -#endif -#ifdef SIGALRM - ap_sys_siglist[SIGALRM] = "Alarm clock"; -#endif -#ifdef SIGTERM - ap_sys_siglist[SIGTERM] = "Terminated"; -#endif -#ifdef SIGUSR1 - ap_sys_siglist[SIGUSR1] = "User defined signal 1"; -#endif -#ifdef SIGUSR2 - ap_sys_siglist[SIGUSR2] = "User defined signal 2"; -#endif -#ifdef SIGCLD - ap_sys_siglist[SIGCLD] = "Child status change"; -#endif -#ifdef SIGCHLD - ap_sys_siglist[SIGCHLD] = "Child status change"; -#endif -#ifdef SIGPWR - ap_sys_siglist[SIGPWR] = "Power-fail restart"; -#endif -#ifdef SIGWINCH - ap_sys_siglist[SIGWINCH] = "Window changed"; -#endif -#ifdef SIGURG - ap_sys_siglist[SIGURG] = "urgent socket condition"; -#endif -#ifdef SIGPOLL - ap_sys_siglist[SIGPOLL] = "Pollable event occurred"; -#endif -#ifdef SIGIO - ap_sys_siglist[SIGIO] = "socket I/O possible"; -#endif -#ifdef SIGSTOP - ap_sys_siglist[SIGSTOP] = "Stopped (signal)"; -#endif -#ifdef SIGTSTP - ap_sys_siglist[SIGTSTP] = "Stopped"; -#endif -#ifdef SIGCONT - ap_sys_siglist[SIGCONT] = "Continued"; -#endif -#ifdef SIGTTIN - ap_sys_siglist[SIGTTIN] = "Stopped (tty input)"; -#endif -#ifdef SIGTTOU - ap_sys_siglist[SIGTTOU] = "Stopped (tty output)"; -#endif -#ifdef SIGVTALRM - ap_sys_siglist[SIGVTALRM] = "virtual timer expired"; -#endif -#ifdef SIGPROF - ap_sys_siglist[SIGPROF] = "profiling timer expired"; -#endif -#ifdef SIGXCPU - ap_sys_siglist[SIGXCPU] = "exceeded cpu limit"; -#endif -#ifdef SIGXFSZ - ap_sys_siglist[SIGXFSZ] = "exceeded file size limit"; -#endif - for (sig=0; sig < sizeof(ap_sys_siglist)/sizeof(ap_sys_siglist[0]); ++sig) - if (ap_sys_siglist[sig] == NULL) - ap_sys_siglist[sig] = ""; -} -#endif /* platform has sys_siglist[] */ - -/* handle all varieties of core dumping signals */ -static void sig_coredump(int sig) -{ - chdir(ap_coredump_dir); - signal(sig, SIG_DFL); -#ifndef WIN32 - kill(getpid(), sig); -#else - raise(sig); -#endif - /* At this point we've got sig blocked, because we're still inside - * the signal handler. When we leave the signal handler it will - * be unblocked, and we'll take the signal... and coredump or whatever - * is appropriate for this particular Unix. In addition the parent - * will see the real signal we received -- whereas if we called - * abort() here, the parent would only see SIGABRT. - */ -} - -/***************************************************************** - * Connection structures and accounting... - */ - -/* XXX - alarms will need to be blockable at some point */ - -static int alarms_blocked = 0; -static int exit_after_unblock = 0; -static void just_die(int sig) -{ /* SIGHUP to child process??? */ - /* if alarms are blocked we have to wait to die otherwise we might - * end up with corruption in alloc.c's internal structures */ - if (alarms_blocked) { - exit_after_unblock = 1; - } - else { - clean_child_exit(0); - } -} - -static void graceful_sig_handler(int sig) -{ - ap_max_requests_per_child = 1; -} - -/***************************************************************** - * Connection structures and accounting... - */ - -/* volatile just in case */ -static int volatile shutdown_pending; -static int volatile restart_pending; -static int volatile is_graceful; -ap_generation_t volatile ap_my_generation; - -/* - * ap_start_shutdown() and ap_start_restart(), below, are a first stab at - * functions to initiate shutdown or restart without relying on signals. - * Previously this was initiated in sig_term() and restart() signal handlers, - * but we want to be able to start a shutdown/restart from other sources -- - * e.g. on Win32, from the service manager. Now the service manager can - * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that - * these functions can also be called by the child processes, since global - * variables are no longer used to pass on the required action to the parent. - * - * These should only be called from the parent process itself, since the - * parent process will use the shutdown_pending and restart_pending variables - * to determine whether to shutdown or restart. The child process should - * call signal_parent() directly to tell the parent to die -- this will - * cause neither of those variable to be set, which the parent will - * assume means something serious is wrong (which it will be, for the - * child to force an exit) and so do an exit anyway. - */ - -void ap_start_shutdown(void) -{ - if (shutdown_pending == 1) { - /* Um, is this _probably_ not an error, if the user has - * tried to do a shutdown twice quickly, so we won't - * worry about reporting it. - */ - return; - } - shutdown_pending = 1; -} - -/* do a graceful restart if graceful == 1 */ -void ap_start_restart(int graceful) -{ - - if (restart_pending == 1) { - /* Probably not an error - don't bother reporting it */ - return; - } - restart_pending = 1; - is_graceful = graceful; -} - -static void sig_term(int sig) -{ - ap_start_shutdown(); -} - -static void restart(int sig) -{ -#ifndef WIN32 - ap_start_restart(sig == SIGWINCH); -#else - ap_start_restart(1); -#endif -} - -static void set_signals(void) -{ -#ifndef NO_USE_SIGACTION - struct sigaction sa; - - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - - if (!one_process) { - sa.sa_handler = sig_coredump; -#if defined(SA_ONESHOT) - sa.sa_flags = SA_ONESHOT; -#elif defined(SA_RESETHAND) - sa.sa_flags = SA_RESETHAND; -#endif - if (sigaction(SIGSEGV, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGSEGV)"); -#ifdef SIGBUS - if (sigaction(SIGBUS, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGBUS)"); -#endif -#ifdef SIGABORT - if (sigaction(SIGABORT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABORT)"); -#endif -#ifdef SIGABRT - if (sigaction(SIGABRT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABRT)"); -#endif -#ifdef SIGILL - if (sigaction(SIGILL, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGILL)"); -#endif - sa.sa_flags = 0; - } - sa.sa_handler = sig_term; - if (sigaction(SIGTERM, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGTERM)"); -#ifdef SIGINT - if (sigaction(SIGINT, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGINT)"); -#endif -#ifdef SIGXCPU - sa.sa_handler = SIG_DFL; - if (sigaction(SIGXCPU, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXCPU)"); -#endif -#ifdef SIGXFSZ - sa.sa_handler = SIG_DFL; - if (sigaction(SIGXFSZ, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXFSZ)"); -#endif -#ifdef SIGPIPE - sa.sa_handler = SIG_IGN; - if (sigaction(SIGPIPE, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGPIPE)"); -#endif - - /* we want to ignore HUPs and WINCH while we're busy processing one */ - sigaddset(&sa.sa_mask, SIGHUP); - sigaddset(&sa.sa_mask, SIGWINCH); - sa.sa_handler = restart; - if (sigaction(SIGHUP, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGHUP)"); - if (sigaction(SIGWINCH, &sa, NULL) < 0) - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGWINCH)"); -#else - if (!one_process) { - signal(SIGSEGV, sig_coredump); -#ifdef SIGBUS - signal(SIGBUS, sig_coredump); -#endif /* SIGBUS */ -#ifdef SIGABORT - signal(SIGABORT, sig_coredump); -#endif /* SIGABORT */ -#ifdef SIGABRT - signal(SIGABRT, sig_coredump); -#endif /* SIGABRT */ -#ifdef SIGILL - signal(SIGILL, sig_coredump); -#endif /* SIGILL */ -#ifdef SIGXCPU - signal(SIGXCPU, SIG_DFL); -#endif /* SIGXCPU */ -#ifdef SIGXFSZ - signal(SIGXFSZ, SIG_DFL); -#endif /* SIGXFSZ */ - } - - signal(SIGTERM, sig_term); -#ifdef SIGHUP - signal(SIGHUP, restart); -#endif /* SIGHUP */ -#ifdef SIGWINCH - signal(SIGWINCH, restart); -#endif /* SIGWINCH */ -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif /* SIGPIPE */ - -#endif -} - -/***************************************************************** - * Here follows a long bunch of generic server bookkeeping stuff... - */ - -/* check to see if we have the 'suexec' setuid wrapper installed */ -static int init_suexec(void) -{ -/* XXX: how to do suexec test? */ -#if 0 - struct stat wrapper; - - if ((stat(SUEXEC_BIN, &wrapper)) != 0) - return (ap_suexec_enabled); - - if ((wrapper.st_mode & S_ISUID) && wrapper.st_uid == 0) { - ap_suexec_enabled = 1; - } - - return (ap_suexec_enabled); -#else - return 0; -#endif -} - -/***************************************************************** - * Connection structures and accounting... - */ - - -static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout, - const struct sockaddr_in *remaddr, /* ZZZ */ - const struct sockaddr_in *saddr, /* ZZZ */ - int c-{ - conn_rec *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). - */ - - conn->child_num = child_num; - conn->thread_num = thread_num; - - conn->pool = p; - conn->local_addr = *saddr; - conn->server = server; /* just a guess for now */ - ap_update_vhost_given_ip(conn); - conn->base_server = conn->server; - conn->client = inout; - - conn->remote_addr = *remaddr; - conn->remote_ip = ap_pstrdup(conn->pool, - inet_ntoa(conn->remote_addr.sin_addr)); - - return conn; -} - -static void sock_disable_nagle(int s) /* ZZZ abstract */ -{ - /* The Nagle algorithm says that we should delay sending partial - * packets in hopes of getting more data. We don't want to do - * this; we are not telnet. There are bad interactions between - * persistent connections and Nagle's algorithm that have very severe - * performance penalties. (Failing to disable Nagle is not much of a - * problem with simple HTTP.) - * - * In spite of these problems, failure here is not a shooting offense. - */ - int just_say_no = 1; - - if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no, - sizeof(int)) < 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, - "setsockopt: (TCP_NODELAY)"); - } -} - -static int make_sock(pool *p, const struct sockaddr_in *server) - /* abstract sockaddr_in */ -{ - int s; - int one = 1; - char addr[512]; - - if (server->sin_addr.s_addr != htonl(INADDR_ANY)) - ap_snprintf(addr, sizeof(addr), "address %s port %d", - inet_ntoa(server->sin_addr), ntohs(server->sin_port)); - else - ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port)); - - /* note that because we're about to slack we don't use psocket */ - if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, - "make_sock: failed to get a socket for %s", addr); - printf("make_sock: failed to get socket for %s\n", addr); - exit(1); - } - - /* Solaris (probably versions 2.4, 2.5, and 2.5.1 with various levels - * of tcp patches) has some really weird bugs where if you dup the - * socket now it breaks things across SIGHUP restarts. It'll either - * be unable to bind, or it won't respond. - */ -#if defined (SOLARIS2) && SOLARIS2 < 260 -#define WORKAROUND_SOLARIS_BUG -#endif - - /* PR#1282 Unixware 1.x appears to have the same problem as solaris */ -#if defined (UW) && UW < 200 -#define WORKAROUND_SOLARIS_BUG -#endif - - /* PR#1973 NCR SVR4 systems appear to have the same problem */ -#if defined (MPRAS) -#define WORKAROUND_SOLARIS_BUG -#endif - -#ifndef WORKAROUND_SOLARIS_BUG - s = ap_slack(s, AP_SLACK_HIGH); - - ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ -#endif - -#ifndef MPE -/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */ -#ifndef _OSD_POSIX - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) { - ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, - "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr); - printf("make_sock: failed to setsockopt for %s\n", addr); - close(s); - return 0; - } -#endif /*_OSD_POSIX*/ - one = 1; -#ifndef BEOS -/* BeOS does not support SO_KEEPALIVE */ - if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) { - ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, - "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr); - close(s); - return 0; - } -#endif -#endif - - sock_disable_nagle(s); - sock_enable_linger(s); - - /* - * To send data over high bandwidth-delay connections at full - * speed we must force the TCP window to open wide enough to keep the - * pipe full. The default window size on many systems - * is only 4kB. Cross-country WAN connections of 100ms - * at 1Mb/s are not impossible for well connected sites. - * If we assume 100ms cross-country latency, - * a 4kB buffer limits throughput to 40kB/s. - * - * To avoid this problem I've added the SendBufferSize directive - * to allow the web master to configure send buffer size. - * - * The trade-off of larger buffers is that more kernel memory - * is consumed. YMMV, know your customers and your network! - * - * -John Heidemann 25-Oct-96 - * - * If no size is specified, use the kernel default. - */ -#ifndef BEOS /* BeOS does not support SO_SNDBUF */ - if (server_conf->send_buffer_size) { - if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, - (char *) &server_conf->send_buffer_size, sizeof(int)) < 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, - "make_sock: failed to set SendBufferSize for %s, " - "using default", addr); - /* not a fatal error */ - } - } -#endif - -#ifdef MPE -/* MPE requires CAP=PM and GETPRIVMODE to bind to ports less than 1024 */ - if (ntohs(server->sin_port) < 1024) - GETPRIVMODE(); -#endif - if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) { - ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, - "make_sock: could not bind to %s", addr); -#ifdef MPE - if (ntohs(server->sin_port) < 1024) - GETUSERMODE(); -#endif - close(s); - exit(1); - } -#ifdef MPE - if (ntohs(server->sin_port) < 1024) - GETUSERMODE(); -#endif - - if (listen(s, ap_listenbacklog) == -1) { - ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, - "make_sock: unable to listen for connections on %s", addr); - close(s); - exit(1); - } - -#ifdef WORKAROUND_SOLARIS_BUG - s = ap_slack(s, AP_SLACK_HIGH); - - ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ -#endif - -#ifndef WIN32 - /* protect various fd_sets */ - if (s >= FD_SETSIZE) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, - "make_sock: problem listening on %s, filedescriptor (%u) " - "larger than FD_SETSIZE (%u) " - "found, you probably need to rebuild Apache with a " - "larger FD_SETSIZE", addr, s, FD_SETSIZE); - close(s); - return 0; - } -#endif - - return s; -} - - -/* - * During a restart we keep track of the old listeners here, so that we - * can re-use the sockets. We have to do this because we won't be able - * to re-open the sockets ("Address already in use"). - * - * Unlike the listeners ring, old_listeners is a NULL terminated list. - * - * copy_listeners() makes the copy, find_listener() finds an old listener - * and close_unused_listener() cleans up whatever wasn't used. - */ -static listen_rec *old_listeners; - -/* unfortunately copy_listeners may be called before listeners is a ring */ -static void copy_listeners(pool *p) -{ - listen_rec *lr; - - ap_assert(old_listeners == NULL); - if (ap_listeners == NULL) { - return; - } - lr = ap_listeners; - do { - listen_rec *nr = malloc(sizeof *nr); - if (nr == NULL) { - fprintf(stderr, "Ouch! malloc failed in copy_listeners()\n"); - exit(1); - } - *nr = *lr; - ap_kill_cleanups_for_socket(p, nr->fd); - nr->next = old_listeners; - old_listeners = nr; - lr = lr->next; - } while (lr && lr != ap_listeners); -} - - -static int find_listener(listen_rec *lr) -{ - listen_rec *or; - for (or = old_listeners; or; or = or->next) { - if (!memcmp(&or->local_addr, &lr->local_addr, sizeof(or->local_addr))) { - or->used = 1; - return or->fd; - } - } - return -1; -} - - static void close_unused_listeners(void) { listen_rec *or, *next; @@ -1501,132 +619,6 @@ } -static void show_compile_settings(void) -{ - printf("Server version: %s\n", ap_get_server_version()); - printf("Server built: %s\n", ap_get_server_built()); - printf("Server's Module Magic Number: %u:%u\n", - MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR); - printf("Server compiled with....\n"); -#ifdef BIG_SECURITY_HOLE - printf(" -D BIG_SECURITY_HOLE\n"); -#endif -#ifdef SECURITY_HOLE_PASS_AUTHORIZATION - printf(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n"); -#endif -#ifdef HAVE_MMAP - printf(" -D HAVE_MMAP\n"); -#endif -#ifdef HAVE_SHMGET - printf(" -D HAVE_SHMGET\n"); -#endif -#ifdef USE_MMAP_SCOREBOARD - printf(" -D USE_MMAP_SCOREBOARD\n"); -#endif -#ifdef USE_SHMGET_SCOREBOARD - printf(" -D USE_SHMGET_SCOREBOARD\n"); -#endif -#ifdef USE_OS2_SCOREBOARD - printf(" -D USE_OS2_SCOREBOARD\n"); -#endif -#ifdef USE_POSIX_SCOREBOARD - printf(" -D USE_POSIX_SCOREBOARD\n"); -#endif -#ifdef USE_MMAP_FILES - printf(" -D USE_MMAP_FILES\n"); -#ifdef MMAP_SEGMENT_SIZE - printf(" -D MMAP_SEGMENT_SIZE=%ld\n",(long)MMAP_SEGMENT_SIZE); -#endif -#endif /*USE_MMAP_FILES*/ -#ifdef NO_WRITEV - printf(" -D NO_WRITEV\n"); -#endif -#ifdef NO_LINGCLOSE - printf(" -D NO_LINGCLOSE\n"); -#endif -#ifdef USE_FCNTL_SERIALIZED_ACCEPT - printf(" -D USE_FCNTL_SERIALIZED_ACCEPT\n"); -#endif -#ifdef USE_FLOCK_SERIALIZED_ACCEPT - printf(" -D USE_FLOCK_SERIALIZED_ACCEPT\n"); -#endif -#ifdef USE_USLOCK_SERIALIZED_ACCEPT - printf(" -D USE_USLOCK_SERIALIZED_ACCEPT\n"); -#endif -#ifdef USE_SYSVSEM_SERIALIZED_ACCEPT - printf(" -D USE_SYSVSEM_SERIALIZED_ACCEPT\n"); -#endif -#ifdef USE_PTHREAD_SERIALIZED_ACCEPT - printf(" -D USE_PTHREAD_SERIALIZED_ACCEPT\n"); -#endif -#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT - printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n"); -#endif -#ifdef NO_OTHER_CHILD - printf(" -D NO_OTHER_CHILD\n"); -#endif -#ifdef NO_RELIABLE_PIPED_LOGS - printf(" -D NO_RELIABLE_PIPED_LOGS\n"); -#endif -#ifdef BUFFERED_LOGS - printf(" -D BUFFERED_LOGS\n"); -#ifdef PIPE_BUF - printf(" -D PIPE_BUF=%ld\n",(long)PIPE_BUF); -#endif -#endif -#ifdef MULTITHREAD - printf(" -D MULTITHREAD\n"); -#endif -#ifdef CHARSET_EBCDIC - printf(" -D CHARSET_EBCDIC\n"); -#endif -#ifdef NEED_HASHBANG_EMUL - printf(" -D NEED_HASHBANG_EMUL\n"); -#endif -#ifdef SHARED_CORE - printf(" -D SHARED_CORE\n"); -#endif - -/* This list displays the compiled-in default paths: */ -#ifdef HTTPD_ROOT - printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n"); -#endif -#ifdef SUEXEC_BIN - printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n"); -#endif -#ifdef SHARED_CORE_DIR - printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n"); -#endif -#ifdef DEFAULT_PIDLOG - printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n"); -#endif -#ifdef DEFAULT_SCOREBOARD - printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n"); -#endif -#ifdef DEFAULT_LOCKFILE - printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n"); -#endif -#ifdef DEFAULT_XFERLOG - printf(" -D DEFAULT_XFERLOG=\"" DEFAULT_XFERLOG "\"\n"); -#endif -#ifdef DEFAULT_ERRORLOG - printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n"); -#endif -#ifdef TYPES_CONFIG_FILE - printf(" -D TYPES_CONFIG_FILE=\"" TYPES_CONFIG_FILE "\"\n"); -#endif -#ifdef SERVER_CONFIG_FILE - printf(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n"); -#endif -#ifdef ACCESS_CONFIG_FILE - printf(" -D ACCESS_CONFIG_FILE=\"" ACCESS_CONFIG_FILE "\"\n"); -#endif -#ifdef RESOURCE_CONFIG_FILE - printf(" -D RESOURCE_CONFIG_FILE=\"" RESOURCE_CONFIG_FILE "\"\n"); -#endif -} - - /* Some init code that's common between win32 and unix... well actually * some of it is #ifdef'd but was duplicated before anyhow. This stuff * is still a mess. @@ -1649,119 +641,7 @@ ap_server_post_read_config = ap_make_array(pcommands, 1, sizeof(char *)); } -/***************************************************************** - * Child process main loop. - */ - -static void process_socket(pool *p, struct sockaddr *sa_client, int csd, int my_child_num, int my_thread_num) -{ - struct sockaddr sa_server; /* ZZZZ */ - size_t len = sizeof(struct sockaddr); - BUFF *conn_io; - request_rec *r; - conn_rec *current_conn; - - ap_note_cleanups_for_fd(p, csd); - - /* ZZZ change to AP func */ - if (getsockname(csd, &sa_server, &len) < 0) { - ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname"); - return; - } - (void) ap_update_child_status(my_child_num, my_thread_num, - SERVER_BUSY_READ, (request_rec *) NULL); - conn_io = ap_bcreate(p, B_RDWR | B_SOCKET); - ap_bpushfd(conn_io, csd, csd); - - current_conn = new_connection(p, server_conf, conn_io, - (const struct sockaddr_in *) sa_client, - (const struct sockaddr_in *) &sa_server, - my_child_num, my_thread_num); - - /* - * Read and process each request found on our connection - * until no requests are left or we decide to close. - */ - - - while ((r = ap_read_request(current_conn)) != NULL) { - (void) ap_update_child_status(my_child_num, my_thread_num, - SERVER_BUSY_WRITE, r); - if (r->status == HTTP_OK) - ap_process_request(r); - if (ap_extended_status) - increment_counts(my_child_num, my_thread_num, r); - if (!current_conn->keepalive || ap_is_aborted(current_conn)) - break; - ap_destroy_pool(r->pool); - (void) ap_update_child_status(my_child_num, my_thread_num, - SERVER_BUSY_KEEPALIVE, - (request_rec *) NULL); -#if 0 - if (ap_scoreboard_image->global.exit_generation > t->generation) { - ap_bclose(conn_io); - fprintf((stderr,"%d child_main: generation maxed\n",my_child_num)); - clean_child_exit(0); - } -#endif - } - - /* - * Close the connection, being careful to send out whatever is still - * in our buffers. If possible, try to avoid a hard close until the - * client has ACKed our FIN and/or has stopped sending us data. - */ - -#ifdef NO_LINGCLOSE - ap_bclose(conn_io); /* just close it */ -#else - if (r && r->connection - && !ap_is_aborted(r->connection) - && r->connection->client - && (r->connection->client->fd >= 0)) { - - lingering_close(r); - } - else { - ap_bsetflag(conn_io, B_EOUT, 1); - ap_bclose(conn_io); - } -#endif -} - - -static void * worker_thread(void * dummy) -{ - proc_info * ti = dummy; - int process_slot = ti->pid; - int thread_slot = ti->tid; - pool *tpool = ti->tpool; - struct sockaddr sa_client; - int csd; - pool *ptrans; /* Pool for per-transaction stuff */ - - free(ti); - - ptrans = ap_make_sub_pool(tpool); - while (1) { - (void) ap_update_child_status(process_slot, thread_slot, SERVER_READY, - (request_rec *) NULL); - csd = get_connection(&sa_client); - if (csd < 0) { - break; - } - process_socket(ptrans, &sa_client, csd, process_slot, thread_slot); - ap_clear_pool(ptrans); - } - - ap_destroy_pool(tpool); - kill(ap_scoreboard_image->parent[process_slot].pid, SIGWINCH); - ap_update_child_status(process_slot, thread_slot, SERVER_DEAD, - (request_rec *) NULL); - return NULL; -} - /***************************************************************** * Here follows a long bunch of generic server bookkeeping stuff... */ @@ -1879,414 +759,53 @@ "setgid: unable to set group id to Group %u", (unsigned)ap_group_id); clean_child_exit(APEXIT_CHILDFATAL); - } -#endif - } -#endif /* ndef WIN32 */ -} - -static void child_main(int child_num_arg) -{ - sigset_t sig_mask; - int signal_received; - pthread_t thread; - int i, j; - int my_child_num = child_num_arg; - proc_info *my_info = NULL; - - my_pid = getpid(); - pchild = ap_make_sub_pool(pconf); - - /*stuff to do before we switch id's, so we have permissions.*/ - reopen_scoreboard(pchild); - - accept_child_init(pchild, ap_threads_per_child); - - set_group_privs(); - - if (!geteuid() && (setuid(ap_user_id) == -1)) { - ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, - "setuid: unable to change uid"); - clean_child_exit(APEXIT_CHILDFATAL); - } - - ap_child_init_modules(pchild, server_conf); - - /*done with init critical section */ - - /* All threads should mask signals out, accoring to sigwait(2) man page */ - sigemptyset(&sig_mask); - - if (pthread_sigmask(SIG_SETMASK, &sig_mask, NULL) != 0) { - ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, "pthread_sigmask"); - } - - /* Setup worker threads */ - - - my_info = NULL; - - my_info = (proc_info *)malloc(sizeof(proc_info)); - my_info->pid = my_child_num; - my_info->tid = i; - my_info->sd = 0; - my_info->tpool = ap_make_sub_pool(pchild); - - /* We are creating threads right now */ - (void) ap_update_child_status(my_child_num, i, SERVER_STARTING, - (request_rec *) NULL); - if (pthread_create(&thread, NULL, worker_thread, my_info)) { - ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, - "pthread_create: unable to create worker thread"); - /* - * We failed to create a thread. Update the scoreboard, - * or it will say SERVER_STARTING forever. - */ - (void) ap_update_child_status(my_child_num, i, SERVER_DEAD, - (request_rec *) NULL); - exit(1); /* We won't always exit here, but for no this is okay */ - } - - /* We let each thread update it's own scoreboard entry. This is done - * because it let's us deal with tid better. - */ - } - - start_accepting_connections(my_child_num); - - /* This thread will be the one responsible for handling signals */ - sigemptyset(&sig_mask); - sigaddset(&sig_mask, SIGWINCH); - sigaddset(&sig_mask, SIGTERM); - sigaddset(&sig_mask, SIGINT); - sigwait(&sig_mask, &signal_received); - /* XXX - Do the appropriate thing for each signal */ - switch (signal_received) { - case SIGWINCH: - graceful_sig_handler(SIGWINCH); - for (j = 0; j < ap_threads_per_child + ap_acceptors_per_child; - j++) { - /* Useful for debugging */ - if (ap_scoreboard_image->servers[child_num_arg][j].status != SERVER_DEAD) { - ap_scoreboard_image->servers[child_num_arg][j].status = SERVER_GRACEFUL; - } - } - graceful_killer(); - clean_child_exit(0); - break; - case SIGTERM: - just_die(SIGTERM); - break; - case SIGINT: - just_die(SIGINT); - break; - default: - ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, - "received strange signal: %d", signal_received); - - } -} - -static int make_child(server_rec *s, int slot, time_t now) /* ZZZ */ -{ - int pid; - - if (ap_acceptors_per_child + ap_threads_per_child > HARD_THREAD_LIMIT) { - ap_log_error(APLOG_MARK, APLOG_ERR, s, - "Worker threads plus acceptor threads is greater than HARD_THREAD_LIMIT, please correct"); - exit(-1); - } - - - if (slot + 1 > max_daemons_limit) { - max_daemons_limit = slot + 1; - } - - if (one_process) { - set_signals(); - /* Needed even in the one process case because lookup of the process - * pid sometimes must go through the scoreboard */ - ap_scoreboard_image->parent[slot].pid = getpid(); - child_main(slot); - } - - Explain1("Starting new child in slot %d",slot); - if ((pid = fork()) == -1) { - ap_log_error(APLOG_MARK, APLOG_ERR, s, "fork: Unable to fork new process"); - - /* fork didn't succeed. Fix the scoreboard or else - * it will say SERVER_STARTING forever and ever - */ - /* (void) ap_update_child_status(slot, SERVER_DEAD, (request_rec *) NULL); - We never put SERVER_STARTING in scoreboard */ - /* In case system resources are maxxed out, we don't want - Apache running away with the CPU trying to fork over and - over and over again. */ - sleep(10); - - return -1; - } - - if (!pid) { -#ifdef AIX_BIND_PROCESSOR - /* By default, AIX binds to a single processor. This bit unbinds - children which will then bind to another CPU. - */ -#include - int status = bindprocessor(BINDPROCESS, (int)getpid(), - PROCESSOR_CLASS_ANY); - if (status != OK) - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf, - "processor unbind failed %d", status); -#endif - - RAISE_SIGSTOP(MAKE_CHILD); - MONCONTROL(1); - - /* - signal(SIGWINCH, graceful_sig_handler); - signal(SIGTERM, just_die); - */ - child_main(slot); - - return 0; + } +#endif } - /* else */ - ap_scoreboard_image->parent[slot].pid = pid; - return 0; +#endif /* ndef WIN32 */ } -/* start up a bunch of children */ -static void startup_children(int number_to_start) +static void child_main(void) { - int i; + sigset_t sig_mask; - for (i = 0; number_to_start && i < ap_daemons_limit; ++i) { - if (ap_scoreboard_image->parent[i].pid != 0) { - continue; - } - if (make_child(server_conf, i, 0) < 0) { - break; - } - --number_to_start; + my_pid = getpid(); + pchild = ap_make_sub_pool(pconf); + + set_group_privs(); + + if (!geteuid() && (setuid(ap_user_id) == -1)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change uid"); + clean_child_exit(APEXIT_CHILDFATAL); } -} + ap_child_init_modules(pchild, server_conf); -/* - * idle_spawn_rate is the number of children that will be spawned on the - * next maintenance cycle if there aren't enough idle servers. It is - * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by - * without the need to spawn. - */ -static int idle_spawn_rate = 1; -#ifndef MAX_SPAWN_RATE -#define MAX_SPAWN_RATE (32) -#endif -static int hold_off_on_exponential_spawning; - -static void perform_idle_server_maintenance(void) -{ - int i, j; - int to_kill; - int idle_count, idle_thread_count; - thread_score *ss; - time_t now = 0; - int free_length; - int free_slots[MAX_SPAWN_RATE]; - int last_non_dead; - int total_non_dead; - - /* initialize the free_list */ - free_length = 0; - - to_kill = -1; - idle_count = 0; - last_non_dead = -1; - total_non_dead = 0; - - ap_sync_scoreboard_image(); - for (i = 0; i < ap_daemons_limit; ++i) { - /* Initialization to satisfy the compiler. It doesn't know - * that ap_threads_per_child is always > 0 */ - int status = SERVER_DEAD; - int any_dying_threads = 0; - int all_dead_threads = 1; + /*done with init critical section */ - idle_thread_count = 0; - if (i >= max_daemons_limit && free_length == idle_spawn_rate) - break; - for (j = 0; j < ap_threads_per_child; j++) { - ss = &ap_scoreboard_image->servers[i][j]; - status = ss->status; - - any_dying_threads = any_dying_threads || (status == SERVER_DEAD) - || (status == SERVER_GRACEFUL); - all_dead_threads = all_dead_threads && (status == SERVER_DEAD); - - /* We consider a starting server as idle because we started it - * at least a cycle ago, and if it still hasn't finished starting - * then we're just going to swamp things worse by forking more. - * So we hopefully won't need to fork more if we count it. - * This depends on the ordering of SERVER_READY and SERVER_STARTING. - */ - if (status <= SERVER_READY) { - ++ idle_thread_count; - /* always kill the highest numbered child if we have to... - * no really well thought out reason ... other than observing - * the server behaviour under linux where lower numbered - * children tend to service more hits (and hence are more - * likely to have their data in cpu caches). - */ - } - } - if (all_dead_threads && free_length < idle_spawn_rate) { - free_slots[free_length] = i; - ++free_length; - } - if (!all_dead_threads) { - max_daemons_limit = i + 1; - } - if (!any_dying_threads) { - ++total_non_dead; - last_non_dead = i; - - if (idle_thread_count > ap_idle_thread_threshold) { - idle_count++; - to_kill = i; - } - } - } - if (idle_count > ap_daemons_max_free) { - /* kill off one child... we use SIGWINCH because that'll cause it to - * shut down gracefully, in case it happened to pick up a request - * while we were counting - */ - kill(ap_scoreboard_image->parent[to_kill].pid, SIGWINCH); - idle_spawn_rate = 1; - } - else if (idle_count < ap_daemons_min_free) { - /* terminate the free list */ - if (free_length == 0) { - /* only report this condition once */ - static int reported = 0; - - if (!reported) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, - "server reached MaxClients setting, consider" - " raising the MaxClients setting"); - reported = 1; - } - idle_spawn_rate = 1; - } - else { - /* ZZZZ */ - - if (idle_spawn_rate >= 8) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, - "server seems busy, (you may need " - "to increase StartServers, or Min/MaxSpareServers), " - "spawning %d children, there are %d idle, and " - "%d total children", idle_spawn_rate, - idle_count, total_non_dead); - } - for (i = 0; i < free_length; ++i) { - make_child(server_conf, free_slots[i], now); - } - /* the next time around we want to spawn twice as many if this - * wasn't good enough, but not if we've just done a graceful - */ - if (hold_off_on_exponential_spawning) { - --hold_off_on_exponential_spawning; - } - else if (idle_spawn_rate < MAX_SPAWN_RATE) { - idle_spawn_rate *= 2; - } - } - } - else { - idle_spawn_rate = 1; + /* All threads should mask signals out, accoring to sigwait(2) man page */ + sigemptyset(&sig_mask); + + if (pthread_sigmask(SIG_SETMASK, &sig_mask, NULL) != 0) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, "pthread_sigmask"); } + + ap_event_loop(); } + /***************************************************************** * Executive routines. */ -static void server_main_loop(int remaining_children_to_start) -{ - int child_slot; - ap_wait_t status; - int pid; - int i; - - while (!restart_pending && !shutdown_pending) { - pid = wait_or_timeout(&status); - - if (pid >= 0) { - child_slot = find_child_by_pid(pid); - if (child_slot >= 0) { - for (i = 0; i < ap_threads_per_child + ap_acceptors_per_child; i++) - ap_update_child_status(child_slot, i, SERVER_DEAD, (request_rec *) NULL); - - if (remaining_children_to_start - && child_slot < ap_daemons_limit) { - /* we're still doing a 1-for-1 replacement of dead - * children with new children - */ - /* ZZZ abstract out for AP funcs. */ - make_child(server_conf, child_slot, time(NULL)); - --remaining_children_to_start; - } -#ifndef NO_OTHER_CHILD - } - else if (reap_other_child(pid, status) == 0) { - /* handled */ -#endif - } - else if (is_graceful) { - /* Great, we've probably just lost a slot in the - * scoreboard. Somehow we don't know about this - * child. - */ - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf, - "long lost child came home! (pid %d)", pid); - } - /* Don't perform idle maintenance when a child dies, - * only do it when there's a timeout. Remember only a - * finite number of children can die, and it's pretty - * pathological for a lot to die suddenly. - */ - continue; - } - else if (remaining_children_to_start) { - /* we hit a 1 second timeout in which none of the previous - * generation of children needed to be reaped... so assume - * they're all done, and pick up the slack if any is left. - */ - startup_children(remaining_children_to_start); - remaining_children_to_start = 0; - /* In any event we really shouldn't do the code below because - * few of the servers we just started are in the IDLE state - * yet, so we'd mistakenly create an extra server. - */ - continue; - } - - perform_idle_server_maintenance(); - } -} - static void one_config_cycle(void) { - int remaining_children_to_start; int listener_count; copy_listeners(pconf); - if (!is_graceful) { - ap_restart_time = time(NULL); /* ZZZZZ */ - } + ap_restart_time = time(NULL); /* ZZZZZ */ ap_clear_pool(pconf); ptemp = ap_make_sub_pool(pconf); server_conf = ap_read_config(pconf, ptemp, ap_server_confname); @@ -2296,152 +815,192 @@ ap_set_version(); ap_init_modules(pconf, server_conf); version_locked++; - accept_parent_init(pconf, listener_count); - if (!is_graceful) { - reinit_scoreboard(pconf); - } - set_signals(); /* set up get_socket */ - if (ap_daemons_max_free < ap_daemons_min_free + 1) /* Don't thrash... */ - ap_daemons_max_free = ap_daemons_min_free + 1; - - /* If we're doing a graceful_restart then we're going to see a lot - * of children exiting immediately when we get into the main loop - * below (because we just sent them SIGWINCH). This happens pretty - * rapidly... and for each one that exits we'll start a new one until - * we reach at least daemons_min_free. But we may be permitted to - * start more than that, so we'll just keep track of how many we're - * supposed to start up without the 1 second penalty between each fork. - */ - remaining_children_to_start = ap_daemons_to_start; - if (remaining_children_to_start > ap_daemons_limit) { - remaining_children_to_start = ap_daemons_limit; - } - if (!is_graceful) { - startup_children(remaining_children_to_start); - remaining_children_to_start = 0; - } - else { - /* give the system some time to recover before kicking into - * exponential mode */ - hold_off_on_exponential_spawning = 10; - } - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, "%s configured -- resuming normal operations", ap_get_server_version()); ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, "Server built: %s", ap_get_server_built()); - restart_pending = shutdown_pending = 0; /* XXX: we child_init once per process... */ ap_child_init_modules(pconf, server_conf); - server_main_loop(remaining_children_to_start); + child_main(); +} - if (shutdown_pending) { - /* Time to gracefully shut down: - * Kill child processes, tell them to call child_exit, etc... - */ - if (ap_killpg(pgrp, SIGTERM) < 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM"); - } - reclaim_child_processes(1); /* Start with SIGTERM */ - - /* cleanup pid file on normal shutdown */ - { - const char *pidfile = NULL; - pidfile = ap_server_root_relative (pconf, ap_pid_fname); - if ( pidfile != NULL && unlink(pidfile) == 0) - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, - server_conf, - "removed PID file %s (pid=%ld)", - pidfile, (long)getpid()); - } - - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, - "caught SIGTERM, shutting down"); - - clean_parent_exit(0); - } +static void standalone_main(int argc, char **argv) +{ + ap_standalone = 1; - /* we've been told to restart */ - signal(SIGHUP, SIG_IGN); + one_config_cycle(); - if (one_process) { - /* not worth thinking about */ - clean_parent_exit(0); - } +} - /* advance to the next generation */ - /* XXX: we really need to make sure this new generation number isn't in - * use by any of the children. - */ - ++ap_my_generation; - ap_scoreboard_image->global.running_generation = ap_my_generation; - update_scoreboard_global(); - - if (is_graceful) { - int i, j; - - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, - "SIGWINCH received. Doing graceful restart"); - - /* kill off the idle ones */ - if (ap_killpg(pgrp, SIGWINCH) < 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGWINCH"); - } - /* This is mostly for debugging... so that we know what is still - * gracefully dealing with existing request. - */ - - for (i = 0; i < ap_daemons_limit; ++i) { - for (j = 0; j < ap_threads_pe- j++) { - if (ap_scoreboard_image->servers[i][j].status != SERVER_DEAD) { - ap_scoreboard_image->servers[i][j].status = SERVER_GRACEFUL; - } - } - } - } - else { - /* Kill 'em all. Since the child acts the same on the parents SIGTERM - * and a SIGHUP, we may as well use the same signal, because some user - * pthreads are stealing signals from us left and right. - */ - if (ap_killpg(pgrp, SIGTERM) < 0) { - ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM"); - } - reclaim_child_processes(1); /* Start with SIGTERM */ - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, - "SIGHUP received. Attempting to restart"); - } +static void show_compile_settings(void) +{ + printf("Server version: %s\n", ap_get_server_version()); + printf("Server built: %s\n", ap_get_server_built()); + printf("Server's Module Magic Number: %u:%u\n", + MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR); + printf("Server compiled with....\n"); +#ifdef BIG_SECURITY_HOLE + printf(" -D BIG_SECURITY_HOLE\n"); +#endif +#ifdef SECURITY_HOLE_PASS_AUTHORIZATION + printf(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n"); +#endif +#ifdef HAVE_MMAP + printf(" -D HAVE_MMAP\n"); +#endif +#ifdef HAVE_SHMGET + printf(" -D HAVE_SHMGET\n"); +#endif +#ifdef USE_MMAP_SCOREBOARD + printf(" -D USE_MMAP_SCOREBOARD\n"); +#endif +#ifdef USE_SHMGET_SCOREBOARD + printf(" -D USE_SHMGET_SCOREBOARD\n"); +#endif +#ifdef USE_OS2_SCOREBOARD + printf(" -D USE_OS2_SCOREBOARD\n"); +#endif +#ifdef USE_POSIX_SCOREBOARD + printf(" -D USE_POSIX_SCOREBOARD\n"); +#endif +#ifdef USE_MMAP_FILES + printf(" -D USE_MMAP_FILES\n"); +#ifdef MMAP_SEGMENT_SIZE + printf(" -D MMAP_SEGMENT_SIZE=%ld\n",(long)MMAP_SEGMENT_SIZE); +#endif +#endif /*USE_MMAP_FILES*/ +#ifdef NO_WRITEV + printf(" -D NO_WRITEV\n"); +#endif +#ifdef NO_LINGCLOSE + printf(" -D NO_LINGCLOSE\n"); +#endif +#ifdef USE_FCNTL_SERIALIZED_ACCEPT + printf(" -D USE_FCNTL_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_FLOCK_SERIALIZED_ACCEPT + printf(" -D USE_FLOCK_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_USLOCK_SERIALIZED_ACCEPT + printf(" -D USE_USLOCK_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_SYSVSEM_SERIALIZED_ACCEPT + printf(" -D USE_SYSVSEM_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_PTHREAD_SERIALIZED_ACCEPT + printf(" -D USE_PTHREAD_SERIALIZED_ACCEPT\n"); +#endif +#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT + printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n"); +#endif +#ifdef NO_OTHER_CHILD + printf(" -D NO_OTHER_CHILD\n"); +#endif +#ifdef NO_RELIABLE_PIPED_LOGS + printf(" -D NO_RELIABLE_PIPED_LOGS\n"); +#endif +#ifdef BUFFERED_LOGS + printf(" -D BUFFERED_LOGS\n"); +#ifdef PIPE_BUF + printf(" -D PIPE_BUF=%ld\n",(long)PIPE_BUF); +#endif +#endif +#ifdef MULTITHREAD + printf(" -D MULTITHREAD\n"); +#endif +#ifdef CHARSET_EBCDIC + printf(" -D CHARSET_EBCDIC\n"); +#endif +#ifdef NEED_HASHBANG_EMUL + printf(" -D NEED_HASHBANG_EMUL\n"); +#endif +#ifdef SHARED_CORE + printf(" -D SHARED_CORE\n"); +#endif +/* This list displays the compiled-in default paths: */ +#ifdef HTTPD_ROOT + printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n"); +#endif +#ifdef SUEXEC_BIN + printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n"); +#endif +#ifdef SHARED_CORE_DIR + printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n"); +#endif +#ifdef DEFAULT_PIDLOG + printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n"); +#endif +#ifdef DEFAULT_SCOREBOARD + printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n"); +#endif +#ifdef DEFAULT_LOCKFILE + printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n"); +#endif +#ifdef DEFAULT_XFERLOG + printf(" -D DEFAULT_XFERLOG=\"" DEFAULT_XFERLOG "\"\n"); +#endif +#ifdef DEFAULT_ERRORLOG + printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n"); +#endif +#ifdef TYPES_CONFIG_FILE + printf(" -D TYPES_CONFIG_FILE=\"" TYPES_CONFIG_FILE "\"\n"); +#endif +#ifdef SERVER_CONFIG_FILE + printf(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n"); +#endif +#ifdef ACCESS_CONFIG_FILE + printf(" -D ACCESS_CONFIG_FILE=\"" ACCESS_CONFIG_FILE "\"\n"); +#endif +#ifdef RESOURCE_CONFIG_FILE + printf(" -D RESOURCE_CONFIG_FILE=\"" RESOURCE_CONFIG_FILE "\"\n"); +#endif } -static void standalone_main(int argc, char **argv) +static void usage(char *bin) { - ap_standalone = 1; - - is_graceful = 0; - - if (!one_process) { - detach(); - } - else { - MONCONTROL(1); - } - - do { - one_config_cycle(); - ++ap_scoreboard_image->global.running_generation; - } while (restart_pending); + char pad[MAX_STRING_LEN]; + unsigned i; - /*add_common_vars(NULL);*/ -} /* standalone_main */ + for (i = 0; i < strlen(bin); i++) + pad[i] = ' '; + pad[i] = '\0'; +#ifdef SHARED_CORE + fprintf(stderr, "Usage: %s [-R directory] [-d directory] [-f file]\n", bin); +#else + fprintf(stderr, "Usage: %s [-d directory] [-f file]\n", bin); +#endif + fprintf(stderr, " %s [-C \"directive\"] [-c \"directive\"]\n", pad); + fprintf(stderr, " %s [-v] [-V] [-h] [-l] [-L] [-S] [-t]\n", pad); + fprintf(stderr, "Options:\n"); +#ifdef SHARED_CORE + fprintf(stderr, " -R directory : specify an alternate location for shared object files\n"); +#endif + fprintf(stderr, " -D name : define a name for use in directives\n"); + fprintf(stderr, " -d directory : specify an alternate initial ServerRoot\n"); + fprintf(stderr, " -f file : specify an alternate ServerConfigFile\n"); + fprintf(stderr, " -C \"directive\" : process directive before reading config files\n"); + fprintf(stderr, " -c \"directive\" : process directive after reading config files\n"); + fprintf(stderr, " -v : show version number\n"); + fprintf(stderr, " -V : show compile settings\n"); + fprintf(stderr, " -h : list available command line options (this page)\n"); + fprintf(stderr, " -l : list compiled-in modules\n"); + fprintf(stderr, " -L : list available configuration directives\n"); + fprintf(stderr, " -S : show parsed settings (currently only vhost settings)\n"); + fprintf(stderr, " -t : run syntax test for configuration files only\n"); +#ifdef WIN32 + fprintf(stderr, " -k shutdown : tell running Apache to shutdown\n"); + fprintf(stderr, " -k restart : tell running Apache to do a graceful restart\n"); +#endif + exit(1); +} extern char *optarg; extern int optind; @@ -2542,7 +1101,6 @@ printf("%s \n", ap_get_server_version()); #endif - ap_suexec_enabled = init_suexec(); server_conf = ap_read_config(pconf, ptemp, ap_server_confname); if (configtestonly) { Index: main/http_request.c =================================================================== RCS file: /home/cvs/apache-apr/pthreads/src/main/http_request.c,v retrieving revision 1.4 diff -u -r1.4 http_request.c --- http_request.c 1999/03/17 17:01:21 1.4 +++ http_request.c 1999/05/17 16:17:20 @@ -1232,14 +1232,11 @@ int old_stat; if (ap_extended_status) - ap_time_process_request(r->connection->child_num, - r->connection->thread_num, START_PREQUEST); + ap_time_process_request(r->connection, START_PREQUEST); process_request_internal(r); - old_stat = ap_update_child_status(r->connection->child_num, - r->connection->thread_num, - SERVER_BUSY_LOG, r); + old_stat = ap_update_child_status(r->connection, SERVER_BUSY_LOG, r); /* * We want to flush the last packet if this isn't a pipelining connection @@ -1251,11 +1248,9 @@ ap_bhalfduplex(r->connection->client); ap_log_transaction(r); - (void) ap_update_child_status(r->connection->child_num, - r->connection->thread_num, old_stat, r); + (void) ap_update_child_status(r->connection, old_stat, r); if (ap_extended_status) - ap_time_process_request(r->connection->child_num, - r->connection->thread_num, STOP_PREQUEST); + ap_time_process_request(r->connection, STOP_PREQUEST); } Index: main/http_worker.c =================================================================== RCS file: http_worker.c diff -N http_worker.c --- /dev/null Mon May 17 09:17:18 1999 +++ http_worker.c Mon May 17 09:17:20 1999 @@ -0,0 +1,323 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. 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 by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#define CORE_PRIVATE + +#include "httpd.h" +#include "http_event.h" +#include "http_log.h" +#include "http_conf_globals.h" +#include "scoreboard.h" +#include "http_config.h" +#include "http_request.h" +#include "http_vhost.h" +#include "http_protocol.h" + +#include +#include + +/* + * More machine-dependent networking gooo... on some systems, + * you've got to be *really* sure that all the packets are acknowledged + * before closing the connection, since the client will not be able + * to see the last response if their TCP buffer is flushed by a RST + * packet from us, which is what the server's TCP stack will send + * if it receives any request data after closing the connection. + * + * In an ideal world, this function would be accomplished by simply + * setting the socket option SO_LINGER and handling it within the + * server's TCP stack while the process continues on to the next request. + * Unfortunately, it seems that most (if not all) operating systems + * block the server process on close() when SO_LINGER is used. So + * we have created a home-brew lingering_close. + * + * Many operating systems tend to block, puke, or otherwise mishandle + * calls to shutdown only half of the connection. You should define + * NO_LINGCLOSE in ap_config.h if such is the case for your system. + */ +#ifndef MAX_SECS_TO_LINGER +#define MAX_SECS_TO_LINGER 30 +#endif + +#ifndef NO_LINGCLOSE + +/* Since many clients will abort a connection instead of closing it, + * attempting to log an error message from this routine will only + * confuse the webmaster. There doesn't seem to be any portable way to + * distinguish between a dropped connection and something that might be + * worth logging. + */ +/*ZZZ this routine needs to be adapted for use with poll()*/ +static void lingering_close(request_rec *r) +{ + /*ZZZ remove the hardwired 512. This is an IO Buffer Size */ + char dummybuf[512]; + struct pollfd pd; + int lsd; + int max_wait; + + /* Prevent a slow-drip client from holding us here indefinitely */ + + max_wait = 30; + ap_bsetopt(r->connection->client, BO_TIMEOUT, &max_wait); + + /* Send any leftover data to the client, but never try to again */ + + if (ap_bflush(r->connection->client) == -1) { + ap_bclose(r->connection->client); + return; + } + ap_bsetflag(r->connection->client, B_EOUT, 1); + + /* Close our half of the connection --- send the client a FIN */ + + lsd = r->connection->client->fd; + + if ((shutdown(lsd, 1) != 0) /* ZZZ abstract shutdown */ + || ap_is_aborted(r->connection)) { + ap_bclose(r->connection->client); + return; + } + + /* Set up to wait for readable data on socket... */ + pd.fd = lsd; + pd.events = POLLIN; + + /* Wait for readable data or error condition on socket; + * slurp up any data that arrives... We exit when we go for an + * interval of tv length without getting any more data, get an error + * from poll(), get an error or EOF on a read, or the timer expires. + */ + /* We use a 2 second timeout because current (Feb 97) browsers + * fail to close a connection after the server closes it. Thus, + * to avoid keeping the child busy, we are only lingering long enough + * for a client that is actively sending data on a connection. + * This should be sufficient unless the connection is massively + * losing packets, in which case we might have missed the RST anyway. + * These parameters are reset on each pass, since they might be + * changed by poll. + */ + do { + pd.revents = 0; + } while ((poll(&pd, 1, 2) == 1) + && read(lsd, dummybuf, sizeof(dummybuf))); + /* && (time() = epoch) < max_wait); */ /* ZZZZ time function is not good... */ + + /* Should now have seen final ack. Safe to finally kill socket */ + ap_bclose(r->connection->client); +} +#endif /* ndef NO_LINGCLOSE */ + + +/***************************************************************** + * Connection structures and accounting... + */ + + +static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout, + const struct sockaddr_in *remaddr, /* ZZZ */ + const struct sockaddr_in *saddr) /* ZZZ */ +{ + conn_rec *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). + */ + + conn->pool = p; + conn->local_addr = *saddr; + conn->server = server; /* just a guess for now */ + ap_update_vhost_given_ip(conn); + conn->base_server = conn->server; + conn->client = inout; + + conn->remote_addr = *remaddr; + conn->remote_ip = ap_pstrdup(conn->pool, + inet_ntoa(conn->remote_addr.sin_addr)); + + return conn; +} + +static void sock_disable_nagle(int s) /* ZZZ abstract */ +{ + /* The Nagle algorithm says that we should delay sending partial + * packets in hopes of getting more data. We don't want to do + * this; we are not telnet. There are bad interactions between + * persistent connections and Nagle's algorithm that have very severe + * performance penalties. (Failing to disable Nagle is not much of a + * problem with simple HTTP.) + * + * In spite of these problems, failure here is not a shooting offense. + */ + int just_say_no = 1; + + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no, + sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "setsockopt: (TCP_NODELAY)"); + } +} + +/***************************************************************** + * Child process main loop. + */ + +static void process_socket(pool *p, struct sockaddr *sa_client, int csd) +{ + struct sockaddr sa_server; /* ZZZZ */ + size_t len = sizeof(struct sockaddr); + BUFF *conn_io; + request_rec *r; + conn_rec *current_conn; + + sock_disable_nagle(csd); + + ap_note_cleanups_for_fd(p, csd); + + /* ZZZ change to AP func */ + if (getsockname(csd, &sa_server, &len) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname"); + return; + } +#if 0 /* XXX scoreboard */ + (void) ap_update_child_status(SERVER_BUSY_READ, (request_rec *) NULL); +#endif + conn_io = ap_bcreate(p, B_RDWR | B_SOCKET); + ap_bpushfd(conn_io, csd, csd); + + current_conn = new_connection(p, server_conf, conn_io, + (const struct sockaddr_in *) sa_client, + (const struct sockaddr_in *) &sa_server); + + /* + * Read and process each request found on our connection + * until no requests are left or we decide to close. + */ + + + while ((r = ap_read_request(current_conn)) != NULL) { + (void) ap_update_child_status(current_conn, + SERVER_BUSY_WRITE, r); + if (r->status == HTTP_OK) + ap_process_request(r); +#if 0 + if (ap_extended_status) + increment_counts(my_child_num, my_thread_num, r); +#endif + if (!current_conn->keepalive || ap_is_aborted(current_conn)) + break; + ap_destroy_pool(r->pool); + (void) ap_update_child_status(current_conn, + SERVER_BUSY_KEEPALIVE, + (request_rec *) NULL); +#if 0 + if (ap_scoreboard_image->global.exit_generation > t->generation) { + ap_bclose(conn_io); + fprintf((stderr,"%d child_main: generation maxed\n",my_child_num)); + clean_child_exit(0); + } +#endif + } + + /* + * Close the connection, being careful to send out whatever is still + * in our buffers. If possible, try to avoid a hard close until the + * client has ACKed our FIN and/or has stopped sending us data. + */ + +#ifdef NO_LINGCLOSE + ap_bclose(conn_io); /* just close it */ +#else + if (r && r->connection + && !ap_is_aborted(r->connection) + && r->connection->client + && (r->connection->client->fd >= 0)) { + + lingering_close(r); + } + else { + ap_bsetflag(conn_io, B_EOUT, 1); + ap_bclose(conn_io); + } +#endif +} + + +void *ap_worker_thread(void *dummy) +{ + pool *ptrans; /* Pool for per-transaction stuff */ + workq_t *work; + + while (1) { + work = workq_dequeue(); + switch (work->type) { + case WORKQ_NEW_CONNECTION: + /* XXX -- need to synchronize this pool ... 'cause it may be created + * in one thread and destroyed elsewhere... hack hack hack + */ + ptrans = ap_make_root_pool(); + process_socket(ptrans, &work->d.new_connection.sa_client, + work->d.new_connection.fd); + ap_destroy_pool(ptrans); + free(work); + break; + case WORKQ_MAX: + break; + } + } + return NULL; +}