stoddard 99/08/05 14:08:26
Added: mpm/src/modules/mpm/winnt winnt.h winnt.c mpm_default.h Log: Win32: Start work on the winnt mpm. This is all horribly broken and hacked right now Revision Changes Path 1.1 apache-2.0/mpm/src/modules/mpm/winnt/winnt.h Index: winnt.h =================================================================== /* ==================================================================== * 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 * [EMAIL PROTECTED] * * 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 <http://www.apache.org/>. * */ #ifndef APACHE_MPM_WINNT_H #define APACHE_MPM_WINNT_H extern int ap_threads_per_child; extern int ap_max_requests_per_child; extern void clean_child_exit(int); extern int ap_extended_status; extern void clean_child_exit(int); #endif /* APACHE_MPM_WINNT_H */ 1.1 apache-2.0/mpm/src/modules/mpm/winnt/winnt.c Index: winnt.c =================================================================== /* ==================================================================== * 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 * [EMAIL PROTECTED] * * 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 <http://www.apache.org/>. * */ #define CORE_PRIVATE #include "httpd.h" #include "http_main.h" #include "http_log.h" #include "http_config.h" /* for read_config */ #include "http_core.h" /* for get_remote_host */ #include "http_connection.h" #include "ap_mpm.h" #include "ap_config.h" #include "ap_listen.h" #include "multithread.h" #include "../os/win32/getopt.h" #include "mpm_default.h" /* * Actual definitions of WINNT MPM specific config globals */ int ap_max_requests_per_child=0; int ap_daemons_to_start=0; static char *mpm_pid_fname=NULL; static int ap_threads_per_child = 0; //static int ap_max_threads_per_child = 0; static int workers_may_exit = 0; static int max_requests_per_child = 0; static int requests_this_child; static int num_listenfds = 0; static struct pollfd *listenfds; static pool *pconf; /* Pool for config stuff */ static char ap_coredump_dir[MAX_STRING_LEN]; static server_rec *server_conf; /*static int sd; ZZZ why is the global...? Only seems to be needed on Win32*/ /* one_process --- debugging mode variable; can be set from the command line * with the -X flag. If set, this gets you the child_main loop running * in the process which originally started up (no detach, no make_child), * which is a pretty nice debugging environment. (You'll get a SIGHUP * early in standalone_main; just continue through. This is the server * trying to kill off any child processes which it might have lying * around --- Apache doesn't keep track of their pids, it just sends * SIGHUP to the process group, ignoring it in the root process. * Continue through and you'll be fine.). */ static int one_process = 0; #ifdef DEBUG_SIGSTOP int raise_sigstop_flags; #endif /* *Non*-shared winnt.c globals... */ event *exit_event; mutex *start_mutex; int my_pid; int parent_pid; int listenmaxfd; /* a clean exit from a child with proper cleanup static void clean_child_exit(int code) __attribute__ ((noreturn)); void clean_child_exit(int code) { if (pchild) { ap_destroy_pool(pchild); } exit(code); } */ /***************************************************************** * Connection structures and accounting... */ /* volatile just in case */ static int volatile shutdown_pending; static int volatile restart_pending; static int volatile is_graceful; /* * 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) { ap_start_restart(1); } static ap_listen_rec *old_listeners; static ap_listen_rec *head_listener; static int setup_listeners(pool *pconf, server_rec *s) { ap_listen_rec *lr; int num_listeners = 0; if (ap_listen_open(pconf, s->port)) { return 0; } for (lr = ap_listeners; lr; lr = lr->next) { num_listeners++; } return num_listeners; } static int find_listener(ap_listen_rec *lr) { ap_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) { ap_listen_rec *or, *next; for (or = old_listeners; or; or = next) { next = or->next; // if (!or->used) closesocket(or->fd); free(or); } old_listeners = NULL; } /* * Find a listener which is ready for accept(). This advances the * head_listener global. */ static ap_inline ap_listen_rec *find_ready_listener(fd_set * main_fds) //static ap_listen_rec *find_ready_listener(fd_set * main_fds) { ap_listen_rec *lr; lr = head_listener; do { if (FD_ISSET(lr->fd, main_fds)) { head_listener = lr->next; return (lr); } lr = lr->next; } while (lr != head_listener); return NULL; } /* * Signalling Apache on NT. * * Under Unix, Apache can be told to shutdown or restart by sending various * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so * we use "events" instead. The parent apache process goes into a loop * where it waits forever for a set of events. Two of those events are * called * * apPID_shutdown * apPID_restart * * (where PID is the PID of the apache parent process). When one of these * is signalled, the Apache parent performs the appropriate action. The events * can become signalled through internal Apache methods (e.g. if the child * finds a fatal error and needs to kill its parent), via the service * control manager (the control thread will signal the shutdown event when * requested to stop the Apache service), from the -k Apache command line, * or from any external program which finds the Apache PID from the * httpd.pid file. * * The signal_parent() function, below, is used to signal one of these events. * It can be called by any child or parent process, since it does not * rely on global variables. * * On entry, type gives the event to signal. 0 means shutdown, 1 means * graceful restart. */ static void signal_parent(int type) { HANDLE e; char *signal_name; extern char signal_shutdown_name[]; extern char signal_restart_name[]; /* after updating the shutdown_pending or restart flags, we need * to wake up the parent process so it can see the changes. The * parent will normally be waiting for either a child process * to die, or for a signal on the "spache-signal" event. So set the * "apache-signal" event here. */ if (one_process) { return; } switch(type) { case 0: signal_name = signal_shutdown_name; break; case 1: signal_name = signal_restart_name; break; default: return; } // APD2("signal_parent signalling event \"%s\"", signal_name); e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name); if (!e) { /* Um, problem, can't signal the parent, which means we can't * signal ourselves to die. Ignore for now... */ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, "OpenEvent on %s event", signal_name); return; } if (SetEvent(e) == 0) { /* Same problem as above */ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, "SetEvent on %s event", signal_name); CloseHandle(e); return; } CloseHandle(e); } /***************************************************************** * Here follows a long bunch of generic server bookkeeping stuff... */ 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)"); } } /********************************************************************** * Multithreaded implementation * * This code is fairly specific to Win32. * * The model used to handle requests is a set of threads. One "main" * thread listens for new requests. When something becomes * available, it does a select and places the newly available socket * onto a list of "jobs" (add_job()). Then any one of a fixed number * of "worker" threads takes the top job off the job list with * remove_job() and handles that connection to completion. After * the connection has finished the thread is free to take another * job from the job list. * * In the code, the "main" thread is running within the worker_main() * function. The first thing this function does is create the * worker threads, which operate in the child_sub_main() function. The * main thread then goes into a loop within worker_main() where they * do a select() on the listening sockets. The select times out once * per second so that the thread can check for an "exit" signal * from the parent process (see below). If this signal is set, the * thread can exit, but only after it has accepted all incoming * connections already in the listen queue (since Win32 appears * to through away listened but unaccepted connections when a * process dies). * * Because the main and worker threads exist within a single process * they are vulnerable to crashes or memory leaks (crashes can also * be caused within modules, of course). There also needs to be a * mechanism to perform restarts and shutdowns. This is done by * creating the main & worker threads within a subprocess. A * main process (the "parent process") creates one (or more) * processes to do the work, then the parent sits around waiting * for the working process to die, in which case it starts a new * one. The parent process also handles restarts (by creating * a new working process then signalling the previous working process * exit ) and shutdowns (by signalling the working process to exit). * The parent process operates within the master_main() function. This * process also handles requests from the service manager (NT only). * * Signalling between the parent and working process uses a Win32 * event. Each child has a unique name for the event, which is * passed to it with the -Z argument when the child is spawned. The * parent sets (signals) this event to tell the child to die. * At present all children do a graceful die - they finish all * current jobs _and_ empty the listen queue before they exit. * A non-graceful die would need a second event. The -Z argument in * the child is also used to create the shutdown and restart events, * since the prefix (apPID) contains the parent process PID. * * The code below starts with functions at the lowest level - * worker threads, and works up to the top level - the main() * function of the parent process. * * The scoreboard (in process memory) contains details of the worker * threads (within the active working process). There is no shared * "scoreboard" between processes, since only one is ever active * at once (or at most, two, when one has been told to shutdown but * is processes outstanding requests, and a new one has been started). * This is controlled by a "start_mutex" which ensures only one working * process is active at once. **********************************************************************/ /* The code protected by #ifdef UNGRACEFUL_RESTARTS/#endif sections * could implement a sort-of ungraceful restart for Win32. instead of * graceful restarts. * * However it does not work too well because it does not intercept a * connection already in progress (in child_sub_main()). We'd have to * get that to poll on the exit event. */ /* * Definition of jobs, shared by main and worker threads. */ typedef struct joblist_s { struct joblist_s *next; int sock; } joblist; /* * Globals common to main and worker threads. This structure is not * used by the parent process. */ typedef struct globals_s { #ifdef UNGRACEFUL_RESTART HANDLE thread_exit_event; #else int exit_now; #endif semaphore *jobsemaphore; joblist *jobhead; joblist *jobtail; mutex *jobmutex; int jobcount; } globals; globals allowed_globals = {0, NULL, NULL, NULL, NULL, 0}; /* * add_job()/remove_job() - add or remove an accepted socket from the * list of sockets connected to clients. allowed_globals.jobmutex protects * against multiple concurrent access to the linked list of jobs. */ void add_job(int sock) { joblist *new_job; ap_assert(allowed_globals.jobmutex); /* TODO: If too many jobs in queue, sleep, check for problems */ ap_acquire_mutex(allowed_globals.jobmutex); new_job = (joblist *) malloc(sizeof(joblist)); if (new_job == NULL) { fprintf(stderr, "Ouch! Out of memory in add_job()!\n"); } new_job->next = NULL; new_job->sock = sock; if (allowed_globals.jobtail != NULL) allowed_globals.jobtail->next = new_job; allowed_globals.jobtail = new_job; if (!allowed_globals.jobhead) allowed_globals.jobhead = new_job; allowed_globals.jobcount++; release_semaphore(allowed_globals.jobsemaphore); ap_release_mutex(allowed_globals.jobmutex); } int remove_job(void) { joblist *job; int sock; #ifdef UNGRACEFUL_RESTART HANDLE hObjects[2]; int rv; hObjects[0] = allowed_globals.jobsemaphore; hObjects[1] = allowed_globals.thread_exit_event; rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); ap_assert(rv != WAIT_FAILED); if (rv == WAIT_OBJECT_0 + 1) { /* thread_exit_now */ // APD1("thread got exit now event"); return -1; } /* must be semaphore */ #else acquire_semaphore(allowed_globals.jobsemaphore); #endif ap_assert(allowed_globals.jobmutex); #ifdef UNGRACEFUL_RESTART if (!allowed_globals.jobhead) { #else ap_acquire_mutex(allowed_globals.jobmutex); if (allowed_globals.exit_now && !allowed_globals.jobhead) { #endif ap_release_mutex(allowed_globals.jobmutex); return (-1); } job = allowed_globals.jobhead; ap_assert(job); allowed_globals.jobhead = job->next; if (allowed_globals.jobhead == NULL) allowed_globals.jobtail = NULL; ap_release_mutex(allowed_globals.jobmutex); sock = job->sock; free(job); return (sock); } /* * child_sub_main() - this is the main loop for the worker threads * * Each thread runs within this function. They wait within remove_job() * for a job to become available, then handle all the requests on that * connection until it is closed, then return to remove_job(). * * The worker thread will exit when it removes a job which contains * socket number -1. This provides a graceful thread exit, since * it will never exit during a connection. * * This code in this function is basically equivalent to the child_main() * from the multi-process (Unix) environment, except that we * * - do not call child_init_modules (child init API phase) * - block in remove_job, and when unblocked we have an already * accepted socket, instead of blocking on a mutex or select(). */ static void child_sub_main(int child_num) { NET_SIZE_T clen; struct sockaddr sa_server; struct sockaddr sa_client; pool *ptrans; int requests_this_child = 0; int csd = -1; int dupped_csd = -1; int srv = 0; /* Note: current_conn used to be a defined at file scope as follows... Since the signal code is not being used in WIN32, make the variable local */ // static APACHE_TLS conn_rec *volatile current_conn; conn_rec *current_conn; ptrans = ap_make_sub_pool(pconf); #if 0 /* ZZZ scoreboard */ (void) ap_update_child_status(child_num, SERVER_READY, (request_rec *) NULL); #endif /* * Setup the jump buffers so that we can return here after a timeout. */ #if 0 /* ZZZ */ #if defined(USE_LONGJMP) setjmp(jmpbuffer); #else sigsetjmp(jmpbuffer, 1); #endif #ifdef SIGURG signal(SIGURG, timeout); #endif #endif while (1) { BUFF *conn_io; request_rec *r; /* * (Re)initialize this child to a pre-connection state. */ #if 0 /* ZZZ Alarms... */ ap_set_callback_and_alarm(NULL, 0); /* Cancel any outstanding alarms */ #endif #if 0 /* ZZZ what is this? It's not thread safe! */ timeout_req = NULL; /* No request in progress */ #endif current_conn = NULL; ap_clear_pool(ptrans); #if 0 /* ZZZ scoreboard */ (void) ap_update_child_status(child_num, SERVER_READY, (request_rec *) NULL); #endif /* Get job from the job list. This will block until a job is ready. * If -1 is returned then the main thread wants us to exit. */ csd = remove_job(); if (csd == -1) break; /* time to exit */ requests_this_child++; ap_note_cleanups_for_socket(ptrans, csd); /* * We now have a connection, so set it up with the appropriate * socket options, file descriptors, and read/write buffers. */ clen = sizeof(sa_server); if (getsockname(csd, &sa_server, &clen) < 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "getsockname"); continue; } clen = sizeof(sa_client); if ((getpeername(csd, &sa_client, &clen)) < 0) { /* get peername will fail if the input isn't a socket */ perror("getpeername"); memset(&sa_client, '\0', sizeof(sa_client)); } sock_disable_nagle(csd); #if 0 /* ZZZ scoreboard */ (void) ap_update_child_status(child_num, SERVER_BUSY_READ, (request_rec *) NULL); #endif /* ZZZ . This will break CGIs since they need to know whether a fd is a socket or a file handle... Fix with APR */ conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET); // conn_io = ap_bcreate(ptrans, B_RDWR); dupped_csd = csd; #if defined(NEED_DUPPED_CSD) if ((dupped_csd = dup(csd)) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "dup: couldn't duplicate csd"); dupped_csd = csd; /* Oh well... */ } ap_note_cleanups_for_socket(ptrans, dupped_csd); #endif ap_bpushfd(conn_io, csd); /* ap_bpushfd(conn_io, csd, dupped_csd); why did we drop duped fd? */ current_conn = ap_new_connection(ptrans, server_conf, conn_io, (struct sockaddr_in *) &sa_client, (struct sockaddr_in *) &sa_server, child_num, 0); /* Set my_thread_num to 0 for now */ ap_process_connection(current_conn); } } void child_main(int child_num_arg) { /* * Only reason for this function, is to pass in * arguments to child_sub_main() on its stack so * that longjump doesn't try to corrupt its local * variables and I don't need to make those * damn variables static/global */ child_sub_main(child_num_arg); } void cleanup_thread(thread **handles, int *thread_cnt, int thread_to_clean) { int i; free_thread(handles[thread_to_clean]); for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++) handles[i] = handles[i + 1]; (*thread_cnt)--; } /* * The Win32 call WaitForMultipleObjects will only allow you to wait for * a maximum of MAXIMUM_WAIT_OBJECTS (current 64). Since the threading * model in the multithreaded version of apache wants to use this call, * we are restricted to a maximum of 64 threads. This is a simplistic * routine that will increase this size. */ static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, DWORD dwSeconds) { time_t tStopTime; DWORD dwRet = WAIT_TIMEOUT; DWORD dwIndex=0; BOOL bFirst = TRUE; tStopTime = time(NULL) + dwSeconds; do { if (!bFirst) Sleep(1000); else bFirst = FALSE; for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) { dwRet = WaitForMultipleObjects( min(MAXIMUM_WAIT_OBJECTS, nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)), lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), 0, 0); if (dwRet != WAIT_TIMEOUT) { break; } } } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT)); return dwRet; } //extern void main_control_server(void *); /* in hellop.c */ #define MAX_SELECT_ERRORS 100 /* * Initialise the signal names, in the global variables signal_name_prefix, * signal_restart_name and signal_shutdown_name. */ #define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */ char signal_name_prefix[MAX_SIGNAL_NAME]; char signal_restart_name[MAX_SIGNAL_NAME]; char signal_shutdown_name[MAX_SIGNAL_NAME]; static void setup_signal_names(char *prefix) { ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix); ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name), "%s_shutdown", signal_name_prefix); ap_snprintf(signal_restart_name, sizeof(signal_restart_name), "%s_restart", signal_name_prefix); } static void setup_inherited_listeners(pool *p) { HANDLE pipe; ap_listen_rec *lr; int fd; WSAPROTOCOL_INFO WSAProtocolInfo; DWORD BytesRead; /* Open the pipe to the parent process to receive the inherited socket * data. The sockets have been set to listening in the parent process. */ pipe = GetStdHandle(STD_INPUT_HANDLE); /* Setup the listeners */ listenmaxfd = -1; FD_ZERO(&listenfds); lr = ap_listeners; FD_ZERO(&listenfds); for (;;) { fd = find_listener(lr); if (fd < 0) { if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), &BytesRead, (LPOVERLAPPED) NULL)){ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, "setup_inherited_listeners: Unable to read socket data from parent"); exit(1); } fd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &WSAProtocolInfo, 0, 0); if (fd == INVALID_SOCKET) { ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, "setup_inherited_listeners: WSASocket failed to get inherit the socket."); exit(1); } // APD2("setup_inherited_listeners: WSASocket() returned socket %d", fd); } else { ap_note_cleanups_for_socket(p, fd); } if (fd >= 0) { FD_SET(fd, &listenfds); if (fd > listenmaxfd) listenmaxfd = fd; } lr->fd = fd; if (lr->next == NULL) break; lr = lr->next; } /* turn the list into a ring */ lr->next = ap_listeners; head_listener = ap_listeners; close_unused_listeners(); CloseHandle(pipe); return; } /* * worker_main() is main loop for the child process. The loop in * this function becomes the controlling thread for the actually working * threads (which run in a loop in child_sub_main()). */ void worker_main(void) { int nthreads; fd_set main_fds; int srv; int clen; int csd; int sd = -1; // made this local and not global struct sockaddr_in sa_client; int total_jobs = 0; thread **child_handles; int rv; time_t end_time; int i; struct timeval tv; int wait_time = 1; HANDLE hObjects[2]; int count_select_errors = 0; pool *pchild; pchild = ap_make_sub_pool(pconf); // ap_standalone = 1; // sd = -1; ?? this variable is global in 1.3.x! nthreads = ap_threads_per_child; if (nthreads <= 0) /* maybe this is not needed... Should be checked in config... */ nthreads = 1; my_pid = getpid(); ap_restart_time = time(NULL); // reinit_scoreboard(pconf); /* * Wait until we have permission to start accepting connections. * start_mutex is used to ensure that only one child ever * goes into the listen/accept loop at once. Also wait on exit_event, * in case we (this child) is told to die before we get a chance to * serve any requests. */ hObjects[0] = (HANDLE)start_mutex; hObjects[1] = (HANDLE)exit_event; rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); if (rv == WAIT_FAILED) { ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf, "Waiting for start_mutex or exit_event -- process will exit"); ap_destroy_pool(pchild); // cleanup_scoreboard(); exit(0); } if (rv == WAIT_OBJECT_0 + 1) { /* exit event signalled - exit now */ ap_destroy_pool(pchild); // cleanup_scoreboard(); exit(0); } /* start_mutex obtained, continue into the select() loop */ if (one_process) { setup_listeners(pconf, server_conf); } else { /* Get listeners from the parent process */ setup_inherited_listeners(pconf); } if (listenmaxfd == -1) { /* Help, no sockets were made, better log something and exit */ ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL, "No sockets were created for listening"); signal_parent(0); /* tell parent to die */ ap_destroy_pool(pchild); // cleanup_scoreboard(); exit(0); } // set_signals(); // ap_child_init_modules(pconf, server_conf); allowed_globals.jobsemaphore = create_semaphore(0); allowed_globals.jobmutex = ap_create_mutex(NULL); /* spawn off the threads */ child_handles = (thread *) alloca(nthreads * sizeof(int)); for (i = 0; i < nthreads; i++) { child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i); } while (1) { if (ap_max_requests_per_child && (total_jobs > ap_max_requests_per_child)) { /* MaxRequestsPerChild hit... */ break; } /* Always check for the exit event being signaled. */ rv = WaitForSingleObject(exit_event, 0); ap_assert((rv == WAIT_TIMEOUT) || (rv == WAIT_OBJECT_0)); if (rv == WAIT_OBJECT_0) { // APD1("child: exit event signalled, exiting"); break; } tv.tv_sec = wait_time; tv.tv_usec = 0; memcpy(&main_fds, &listenfds, sizeof(fd_set)); srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv); #ifdef WIN32 if (srv == SOCKET_ERROR) { /* Map the Win32 error into a standard Unix error condition */ errno = WSAGetLastError(); srv = -1; } #endif /* WIN32 */ if (srv < 0) { /* Error occurred - if EINTR, loop around with problem */ if (errno != EINTR) { /* A "real" error occurred, log it and increment the count of * select errors. This count is used to ensure we don't go into * a busy loop of continuous errors. */ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); count_select_errors++; if (count_select_errors > MAX_SELECT_ERRORS) { ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server_conf, "Too many errors in select loop. Child process exiting."); break; } } continue; } count_select_errors = 0; /* reset count of errors */ if (srv == 0) { continue; } { ap_listen_rec *lr; lr = find_ready_listener(&main_fds); if (lr != NULL) { sd = lr->fd; } } do { clen = sizeof(sa_client); csd = accept(sd, (struct sockaddr *) &sa_client, &clen); #ifdef WIN32 if (csd == INVALID_SOCKET) { csd = -1; errno = WSAGetLastError(); } #endif /* WIN32 */ } while (csd < 0 && errno == EINTR); if (csd < 0) { #if defined(EPROTO) && defined(ECONNABORTED) if ((errno != EPROTO) && (errno != ECONNABORTED)) #elif defined(EPROTO) if (errno != EPROTO) #elif defined(ECONNABORTED) if (errno != ECONNABORTED) #endif ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "accept: (client socket)"); } else { add_job(csd); total_jobs++; } } // APD2("process PID %d exiting", my_pid); /* Get ready to shutdown and exit */ allowed_globals.exit_now = 1; ap_release_mutex(start_mutex); #ifdef UNGRACEFUL_RESTART SetEvent(allowed_globals.thread_exit_event); #else for (i = 0; i < nthreads; i++) { add_job(-1); } #endif // APD2("process PID %d waiting for worker threads to exit", my_pid); /* Wait for all your children */ end_time = time(NULL) + 180; while (nthreads) { rv = wait_for_many_objects(nthreads, child_handles, end_time - time(NULL)); if (rv != WAIT_TIMEOUT) { rv = rv - WAIT_OBJECT_0; ap_assert((rv >= 0) && (rv < nthreads)); cleanup_thread(child_handles, &nthreads, rv); continue; } break; } // APD2("process PID %d killing remaining worker threads", my_pid); for (i = 0; i < nthreads; i++) { kill_thread(child_handles[i]); free_thread(child_handles[i]); } #ifdef UNGRACEFUL_RESTART ap_assert(CloseHandle(allowed_globals.thread_exit_event)); #endif destroy_semaphore(allowed_globals.jobsemaphore); ap_destroy_mutex(allowed_globals.jobmutex); // ap_child_exit_modules(pconf, server_conf); ap_destroy_pool(pchild); // cleanup_scoreboard(); // APD2("process PID %d exited", my_pid); // clean_parent_exit(0); } /* standalone_main */ /* * Spawn a child Apache process. The child process has the command line arguments from * argc and argv[], plus a -Z argument giving the name of an event. The child should * open and poll or wait on this event. When it is signalled, the child should die. * prefix is a prefix string for the event name. * * The child_num argument on entry contains a serial number for this child (used to create * a unique event name). On exit, this number will have been incremented by one, ready * for the next call. * * On exit, the value pointed to be *ev will contain the event created * to signal the new child process. * * The return value is the handle to the child process if successful, else -1. If -1 is * returned the error will already have been logged by ap_log_error(). */ /********************************************************************** * master_main - this is the parent (main) process. We create a * child process to do the work, then sit around waiting for either * the child to exit, or a restart or exit signal. If the child dies, * we just respawn a new one. If we have a shutdown or graceful restart, * tell the child to die when it is ready. If it is a non-graceful * restart, force the child to die immediately. **********************************************************************/ #define MAX_PROCESSES 50 /* must be < MAX_WAIT_OBJECTS-1 */ static void cleanup_process(HANDLE *handles, HANDLE *events, int position, int *processes) { int i; int handle = 0; CloseHandle(handles[position]); CloseHandle(events[position]); handle = (int)handles[position]; for (i = position; i < (*processes)-1; i++) { handles[i] = handles[i + 1]; events[i] = events[i + 1]; } (*processes)--; // APD4("cleanup_processes: removed child in slot %d handle %d, max=%d", position, handle, *processes); } static int create_process(pool *p, HANDLE *handles, HANDLE *events, int *processes, int *child_num, char *kill_event_name) { int rv, i; HANDLE kill_event; char buf[1024]; char exit_event_name[40]; /* apPID_C# */ char *pCommand; char *pEnvBlock; STARTUPINFO si; /* Filled in prior to call to CreateProcess */ PROCESS_INFORMATION pi; /* filled in on call to CreateProces */ LPWSAPROTOCOL_INFO lpWSAProtocolInfo; ap_listen_rec *lr; DWORD BytesWritten; HANDLE hPipeRead = NULL; HANDLE hPipeWrite = NULL; SECURITY_ATTRIBUTES sa = {0}; sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; /* Build the command line. Should look something like this: * C:/apache/bin/apache.exe -f ap_server_confname * First, get the path to the executable... */ rv = GetModuleFileName(NULL, buf, sizeof(buf)); if (rv == sizeof(buf)) { ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, "Parent: Path to Apache process too long"); return -1; } else if (rv == 0) { ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, "Parent: GetModuleFileName() returned NULL for current process."); return -1; } /* Create the exit event (apPID_C#). Parent signals this event to tell the * child to exit */ ap_snprintf(exit_event_name, sizeof(exit_event_name), "%s_C%d", kill_event_name, ++(*child_num)); kill_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name); if (!kill_event) { ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, "Parent: Could not create exit event for child process"); return -1; } // pCommand = ap_psprintf(p, "\"%s\" -f \"%s\"", buf, ap_server_confname); pCommand = ap_psprintf(p, "\"%s\" -f \"%s\"", buf, SERVER_CONFIG_FILE); /* Create a pipe to send socket info to the child */ if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) { ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, "Parent: Unable to create pipe to child process.\n"); return -1; } pEnvBlock = ap_psprintf(p, "AP_PARENT_PID=%d\0AP_CHILD_NUM=%d\0\0",parent_pid,*child_num); /* Give the read in of the pipe (hPipeRead) to the child as stdin. The * parent will write the socket data to the child on this pipe. */ memset(&si, 0, sizeof(si)); memset(&pi, 0, sizeof(pi)); si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.wShowWindow = SW_HIDE; si.hStdInput = hPipeRead; if (!CreateProcess(NULL, pCommand, NULL, NULL, TRUE, /* Inherit handles */ 0, /* Creation flags */ pEnvBlock, /* Environment block */ NULL, &si, &pi)) { ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, "Parent: Not able to create the child process."); /* * We must close the handles to the new process and its main thread * to prevent handle and memory leaks. */ CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return -1; } else { ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf, "Parent: Created child process %d", pi.dwProcessId); /* Assume the child process lives. Update the process and event tables */ handles[*processes] = pi.hProcess; events[*processes] = kill_event; (*processes)++; /* We never store the thread's handle, so close it now. */ CloseHandle(pi.hThread); /* Run the chain of open sockets. For each socket, duplicate it * for the target process then send the WSAPROTOCOL_INFO * (returned by dup socket) to the child */ lr = ap_listeners; while (lr != NULL) { lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO)); ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf, "Parent: Duplicating socket %d and sending it to child process %d", lr->fd, pi.dwProcessId); if (WSADuplicateSocket(lr->fd, pi.dwProcessId, lpWSAProtocolInfo) == SOCKET_ERROR) { ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, "Parent: WSADuplicateSocket failed for socket %d.", lr->fd ); return -1; } if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO), &BytesWritten, (LPOVERLAPPED) NULL)) { ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, "Parent: Unable to write duplicated socket %d to the child.", lr->fd ); return -1; } lr = lr->next; if (lr == ap_listeners) break; } } CloseHandle(hPipeRead); CloseHandle(hPipeWrite); return 0; } /* To share the semaphores with other processes, we need a NULL ACL * Code from MS KB Q106387 */ static PSECURITY_ATTRIBUTES GetNullACL() { PSECURITY_DESCRIPTOR pSD; PSECURITY_ATTRIBUTES sa; sa = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES)); pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); if (pSD == NULL || sa == NULL) { return NULL; } if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) || GetLastError()) { LocalFree( pSD ); LocalFree( sa ); return NULL; } if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE) || GetLastError()) { LocalFree( pSD ); LocalFree( sa ); return NULL; } sa->nLength = sizeof(sa); sa->lpSecurityDescriptor = pSD; sa->bInheritHandle = TRUE; return sa; } static void CleanNullACL( void *sa ) { if( sa ) { LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor); LocalFree( sa ); } } static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_event) { int remaining_children_to_start; int child_num = 0; int rv, cld; HANDLE process_handles[MAX_PROCESSES]; HANDLE process_kill_events[MAX_PROCESSES]; int current_live_processes = 0; /* number of child process we know about */ int processes_to_create = 0; /* number of child processes to create */ pool *pparent = NULL; /* pool for the parent process. Cleaned on each restart */ restart_pending = shutdown_pending = 0; remaining_children_to_start = ap_daemons_to_start; /* Create child process * Should only be one in this version of Apache for WIN32 */ while (remaining_children_to_start--) { if (create_process(pconf, process_handles, process_kill_events, ¤t_live_processes, &child_num, signal_prefix_string) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "master_main: create child process failed. Exiting."); shutdown_pending = 1; goto die_now; } } /* service_set_status(SERVICE_RUNNING);*/ restart_pending = shutdown_pending = 0; /* Wait for shutdown or restart events or for child death */ process_handles[current_live_processes] = shutdown_event; process_handles[current_live_processes+1] = restart_event; rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE *)process_handles, FALSE, INFINITE); if (rv == WAIT_FAILED) { /* Something serious is wrong */ ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, server_conf, "master_main: : WaitForMultipeObjects on process handles and apache-signal -- doing shutdown"); shutdown_pending = 1; break; } if (rv == WAIT_TIMEOUT) { /* Hey, this cannot happen */ ap_log_error(APLOG_MARK, APLOG_ERR, s, "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT"); shutdown_pending = 1; } cld = rv - WAIT_OBJECT_0; // APD4("main process: wait finished, cld=%d handle %d (max=%d)", cld, process_handles[cld], current_live_processes); if (cld == current_live_processes) { /* shutdown_event signalled */ shutdown_pending = 1; ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, s, "master_main: Shutdown event signaled. Shutting the server down."); if (ResetEvent(shutdown_event) == 0) { ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, s, "ResetEvent(shutdown_event)"); } /* Signal each child processes to die */ for (i = 0; i < current_live_processes; i++) { // APD3("master_main: signalling child %d, handle %d to die", i, process_handles[i]); if (SetEvent(process_kill_events[i]) == 0) ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf, "master_main: SetEvent for child process in slot #%d failed", i); } break; } else if (cld == current_live_processes+1) { /* restart_event signalled */ int children_to_kill = current_live_processes; restart_pending = 1; ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, s, "master_main: Restart event signaled. Doing a graceful restart."); if (ResetEvent(restart_event) == 0) { ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, s, "master_main: ResetEvent(restart_event) failed."); } /* Signal each child process to die */ for (i = 0; i < children_to_kill; i++) { // APD3("master_main: signalling child #%d handle %d to die", i, process_handles[i]); if (SetEvent(process_kill_events[i]) == 0) ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, s, "master_main: SetEvent for child process in slot #%d failed", i); /* Remove the process (and event) from the process table */ cleanup_process(process_handles, process_kill_events, i, ¤t_live_processes); } processes_to_create = ap_daemons_to_start; } else { /* A child process must have exited because of MaxRequestPerChild being hit * or a fatal error condition (seg fault, etc.). Remove the dead process * from the process_handles and process_kill_events table and create a new * child process. * TODO: Consider restarting the child immediately without looping through http_main * This will become necesasary if we ever support multiple children. * One option, create a parent thread which waits on child death and restarts it. */ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, "master_main: Child processed exited (due to MaxRequestsPerChild?). Restarting the child process."); ap_assert(cld < current_live_processes); cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes); // APD2("main_process: child in slot %d died", rv); remaining_children_to_start = 1; continue; } /* If we dropped out of the loop we definitly want to die completely. We need to * make sure we wait for all the child process to exit first. */ die_now: if (shutdown_pending) { tmstart = time(NULL); while (current_live_processes && ((tmstart+60) > time(NULL))) { rv = WaitForMultipleObjects(current_live_processes, (HANDLE *)process_handles, FALSE, 2000); if (rv == WAIT_TIMEOUT) continue; ap_assert(rv != WAIT_FAILED); cld = rv - WAIT_OBJECT_0; ap_assert(rv < current_live_processes); // APD4("main_process: child in #%d handle %d died, left=%d", // rv, process_handles[rv], current_live_processes); cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes); } for (i = 0; i < current_live_processes; i++) { ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO, server_conf, "forcing termination of child #%d (handle %d)", i, process_handles[i]); TerminateProcess((HANDLE) process_handles[i], 1); } } CloseHandle(restart_event); CloseHandle(shutdown_event); if (pparent) { ap_destroy_pool(pparent); } return (0); } /* * winnt_pre_config() * Gets called twice at startup. */ static void winnt_pre_config(pool *pconf, pool *plog, pool *ptemp) { char *pid; one_process = !!getenv("ONE_PROCESS"); /* Track parent/child pids... */ pid = getenv("AP_PARENT_PID"); if (pid) { /* AP_PARENT_PID is only valid in the child */ parent_pid = atoi(pid); my_pid = getpid(); } else { /* This is the parent... */ parent_pid = my_pid = getpid(); ap_log_pid(pconf, mpm_pid_fname); } ap_listen_pre_config(); ap_daemons_to_start = DEFAULT_NUM_DAEMON ap_threads_per_child = DEFAULT_START_THREAD; mpm_pid_fname = DEFAULT_PIDLOG; max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); } /* Need to register this hook if we want it... */ static void winnt_post_config(pool *pconf, pool *plog, pool *ptemp, server_rec* server_conf) { server_conf = server_conf; } API_EXPORT(int) ap_mpm_run(pool *_pconf, pool *plog, server_rec *s ) { int child_num; char* exit_event_name; // char signal_prefix_string[100]; int i; time_t tmstart; HANDLE shutdown_event; /* used to signal shutdown to parent */ HANDLE restart_event; /* used to signal a restart to parent */ pconf = _pconf; server_conf = s; if ((parent_pid != my_pid) || one_process) { /* Child process */ child_num = atoi(getenv("AP_CHILD_NUM")); exit_event_name = ap_psprintf(pconf, "ap_%d_C%d", parent_pid, child_num); exit_event = open_event(exit_event_name); setup_signal_names(ap_psprintf(pconf,"ap_%d", parent_pid)); start_mutex = ap_open_mutex(signal_name_prefix); ap_assert(start_mutex); worker_main(); destroy_event(exit_event); } else { /* Parent process */ static int restart = 0; PSECURITY_ATTRIBUTES sa = GetNullACL(); /* returns NULL if invalid (Win95?) */ ap_clear_pool(plog); ap_open_logs(server_conf, plog); if (!restart) { /* service_set_status(SERVICE_START_PENDING);*/ setup_signal_names(ap_psprintf(pconf,"ap_%d", parent_pid)); /* Create shutdown event, apPID_shutdown, where PID is the parent * Apache process ID. Shutdown is signaled by 'apache -k shutdown'. */ shutdown_event = CreateEvent(sa, TRUE, FALSE, signal_shutdown_name); if (!shutdown_event) { ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, s, "master_main: Cannot create shutdown event %s", signal_shutdown_name); CleanNullACL((void *)sa); exit(1); } /* Create restart event, apPID_restart, where PID is the parent * Apache process ID. Restart is signaled by 'apache -k restart'. */ restart_event = CreateEvent(sa, TRUE, FALSE, signal_restart_name); if (!restart_event) { CloseHandle(shutdown_event); ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, s, "master_main: Cannot create restart event %s", signal_restart_name); CleanNullACL((void *)sa); exit(1); } CleanNullACL((void *)sa); /* Create the start mutex, apPID, where PID is the parent Apache process ID. * Ths start mutex is used during a restart to prevent more than one * child process from entering the accept loop at once. */ start_mutex = ap_create_mutex(signal_prefix_string); /* TOTD: Add some code to detect failure */ } /* Go to work... */ restart = master_main(server_conf, shutdown_event, restart_event); if (!restart) { const char *pidfile = NULL; /* Shutting down. Clean up... */ pidfile = ap_server_root_relative (pconf, mpm_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_destroy_mutex(start_mutex); /* service_set_status(SERVICE_STOPPED); */ } return !restart; } static void winnt_hooks(void) { // INIT_SIGLIST() one_process = 0; /* Configuration hooks implemented by http_config.c ... */ ap_hook_pre_config(winnt_pre_config, NULL, NULL, HOOK_MIDDLE); /* ap_hook_post_config()...); ap_hook_open_logs(xxx,NULL,NULL,HOOK_MIDDLE); */ /* ap_hook_translate_name(xxx,NULL,NULL,HOOK_REALLY_LAST); ap_hook_process_connection(xxx,NULL,NULL, HOOK_REALLY_LAST); ap_hook_http_method(xxx,NULL,NULL,HOOK_REALLY_LAST); ap_hook_default_port(xxx,NULL,NULL,HOOK_REALLY_LAST); /* FIXME: I suspect we can eliminate the need for these - Ben */ ap_hook_type_checker(xxx,NULL,NULL,HOOK_REALLY_LAST); */ } /* * Command processors */ static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg) { const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } if (cmd->server->is_virtual) { return "PidFile directive not allowed in <VirtualHost>"; } mpm_pid_fname = arg; return NULL; } static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg) { const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } ap_threads_per_child = atoi(arg); if (ap_threads_per_child > HARD_THREAD_LIMIT) { fprintf(stderr, "WARNING: ThreadsPerChild of %d exceeds compile time" "limit of %d threads,\n", ap_threads_per_child, HARD_THREAD_LIMIT); fprintf(stderr, " lowering ThreadsPerChild to %d. To increase, please" "see the\n", HARD_THREAD_LIMIT); fprintf(stderr, " HARD_THREAD_LIMIT define in src/include/httpd.h.\n"); } else if (ap_threads_per_child < 1) { fprintf(stderr, "WARNING: Require ThreadsPerChild > 0, setting to 1\n"); ap_threads_per_child = 1; } return NULL; } static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) { const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } max_requests_per_child = atoi(arg); return NULL; } static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg) { struct stat finfo; const char *fname; const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } fname = ap_server_root_relative(cmd->pool, arg); /* ZZZ change this to the AP func FileInfo*/ if ((stat(fname, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) { return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, " does not exist or is not a directory", NULL); } ap_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir)); return NULL; } static const command_rec winnt_cmds[] = { LISTEN_COMMANDS { "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1, "A file for logging the server process ID"}, //{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1, // "A file for Apache to maintain runtime process management information"}, { "ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF, TAKE1, "Number of threads each child creates" }, { "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, "Maximum number of requests a particular child serves before dying." }, { "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1, "The location of the directory Apache changes to before dumping core" }, { NULL } }; module MODULE_VAR_EXPORT mpm_winnt_module = { STANDARD20_MODULE_STUFF, NULL, /* child_init */ NULL, /* create per-directory config structure */ NULL, /* merge per-directory config structures */ NULL, /* create per-server config structure */ NULL, /* merge per-server config structures */ winnt_cmds, /* command table */ NULL, /* handlers */ NULL, /* check auth */ NULL, /* check access */ winnt_hooks /* register_hooks */ }; /* force Expat to be linked into the server executable */ #if defined(USE_EXPAT) && !defined(SHARED_CORE_BOOTSTRAP) #include "xmlparse.h" const XML_LChar *suck_in_expat(void); const XML_LChar *suck_in_expat(void) { return XML_ErrorString(XML_ERROR_NONE); } #endif /* USE_EXPAT */ 1.1 apache-2.0/mpm/src/modules/mpm/winnt/mpm_default.h Index: mpm_default.h =================================================================== /* ==================================================================== * 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 * [EMAIL PROTECTED] * * 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 <http://www.apache.org/>. * */ #ifndef APACHE_MPM_DEFAULT_H #define APACHE_MPM_DEFAULT_H /* Number of threads to spawn off by default --- also, if fewer than * this free when the caretaker checks, it will spawn more. */ #ifndef DEFAULT_START_THREAD #define DEFAULT_START_THREAD 5 #endif /* Maximum number of *free* server threads --- more than this, and * they will die off. #ifndef DEFAULT_MAX_SPARE_THREAD #define DEFAULT_MAX_SPARE_THREAD 10 #endif */ /* Minimum --- fewer than this, and more will be created */ /* #ifndef DEFAULT_MIN_SPARE_THREAD #define DEFAULT_MIN_SPARE_THREAD 5 #endif */ /* Limit on the threads per process. Clients will be locked out if more than * this * HARD_SERVER_LIMIT are needed. * * We keep this for one reason it keeps the size of the scoreboard file small * enough that we can read the whole thing without worrying too much about * the overhead. */ #ifndef HARD_THREAD_LIMIT #define HARD_THREAD_LIMIT 64 #endif /* Number of servers to spawn off by default */ #ifndef DEFAULT_NUM_DAEMON #define DEFAULT_NUM_DAEMON 1 #endif /* Limit on the total --- clients will be locked out if more servers than * this are needed. It is intended solely to keep the server from crashing * when things get out of hand. * * We keep a hard maximum number of servers, for two reasons --- first off, * in case something goes seriously wrong, we want to stop the fork bomb * short of actually crashing the machine we're running on by filling some * kernel table. Secondly, it keeps the size of the scoreboard file small * enough that we can read the whole thing without worrying too much about * the overhead. */ #ifndef HARD_SERVER_LIMIT #define HARD_SERVER_LIMIT 8 #endif #endif /* AP_MPM_DEFAULT_H */