]> granicus.if.org Git - apache/blob - server/mpm/winnt/mpm_winnt.c
Win32: Graceful restart is working again, and better than before.
[apache] / server / mpm / winnt / mpm_winnt.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 #define CORE_PRIVATE 
60 #include "apr_portable.h"
61 #include "httpd.h" 
62 #include "http_main.h" 
63 #include "http_log.h" 
64 #include "http_config.h"        /* for read_config */ 
65 #include "http_core.h"          /* for get_remote_host */ 
66 #include "http_connection.h"
67 #include "ap_mpm.h"
68 #include "ap_config.h"
69 #include "ap_listen.h"
70 #include "mpm_default.h"
71 #include "service.h"
72 #include "iol_socket.h"
73 #include "winnt.h"
74
75
76 /*
77  * Definitions of WINNT MPM specific config globals
78  */
79
80 static char *ap_pid_fname = NULL;
81 static int ap_threads_per_child = 0;
82 static int workers_may_exit = 0;
83 static int max_requests_per_child = 0;
84 static HANDLE shutdown_event;   /* used to signal shutdown to parent */
85 static HANDLE restart_event;    /* used to signal a restart to parent */
86
87 static struct fd_set listenfds;
88 static int num_listenfds = 0;
89 static SOCKET listenmaxfd = INVALID_SOCKET;
90
91 static ap_pool_t *pconf;                /* Pool for config stuff */
92
93 static char ap_coredump_dir[MAX_STRING_LEN];
94
95 static server_rec *server_conf;
96 static HANDLE AcceptExCompPort = NULL;
97
98 static int one_process = 0;
99
100 static OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
101
102 int ap_max_requests_per_child=0;
103 int ap_daemons_to_start=0;
104
105 static event *exit_event;
106 HANDLE maintenance_event;
107 ap_lock_t *start_mutex;
108 int my_pid;
109 int parent_pid;
110
111 static ap_status_t socket_cleanup(void *sock)
112 {
113     ap_socket_t *thesocket = sock;
114     SOCKET sd;
115     if (ap_get_os_sock(&sd, thesocket) == APR_SUCCESS) {
116         closesocket(sd);
117     }
118     return APR_SUCCESS;
119 }
120
121 /* A bunch or routines from os/win32/multithread.c that need to be merged into APR
122  * or thrown out entirely...
123  */
124
125
126 typedef void semaphore;
127 typedef void event;
128
129 static semaphore *
130 create_semaphore(int initial)
131 {
132     return(CreateSemaphore(NULL, initial, 1000000, NULL));
133 }
134
135 static void acquire_semaphore(semaphore *semaphore_id)
136 {
137     int rv;
138     
139     rv = WaitForSingleObject(semaphore_id, INFINITE);
140     
141     return;
142 }
143
144 static int release_semaphore(semaphore *semaphore_id)
145 {
146     return(ReleaseSemaphore(semaphore_id, 1, NULL));
147 }
148
149 static void destroy_semaphore(semaphore *semaphore_id)
150 {
151     CloseHandle(semaphore_id);
152 }
153
154
155 /* To share the semaphores with other processes, we need a NULL ACL
156  * Code from MS KB Q106387
157  */
158 static PSECURITY_ATTRIBUTES GetNullACL()
159 {
160     PSECURITY_DESCRIPTOR pSD;
161     PSECURITY_ATTRIBUTES sa;
162
163     sa  = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
164     pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
165                                             SECURITY_DESCRIPTOR_MIN_LENGTH);
166     if (pSD == NULL || sa == NULL) {
167         return NULL;
168     }
169     if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)
170         || GetLastError()) {
171         LocalFree( pSD );
172         LocalFree( sa );
173         return NULL;
174     }
175     if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)
176         || GetLastError()) {
177         LocalFree( pSD );
178         LocalFree( sa );
179         return NULL;
180     }
181     sa->nLength = sizeof(sa);
182     sa->lpSecurityDescriptor = pSD;
183     sa->bInheritHandle = TRUE;
184     return sa;
185 }
186
187 static void CleanNullACL( void *sa ) {
188     if( sa ) {
189         LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor);
190         LocalFree( sa );
191     }
192 }
193
194 /*
195  * The Win32 call WaitForMultipleObjects will only allow you to wait for 
196  * a maximum of MAXIMUM_WAIT_OBJECTS (current 64).  Since the threading 
197  * model in the multithreaded version of apache wants to use this call, 
198  * we are restricted to a maximum of 64 threads.  This is a simplistic 
199  * routine that will increase this size.
200  */
201 static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, 
202                                    DWORD dwSeconds)
203 {
204     time_t tStopTime;
205     DWORD dwRet = WAIT_TIMEOUT;
206     DWORD dwIndex=0;
207     BOOL bFirst = TRUE;
208   
209     tStopTime = time(NULL) + dwSeconds;
210   
211     do {
212         if (!bFirst)
213             Sleep(1000);
214         else
215             bFirst = FALSE;
216           
217         for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) {
218             dwRet = WaitForMultipleObjects( 
219                 min(MAXIMUM_WAIT_OBJECTS, nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)),
220                 lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), 
221                 0, 0);
222                                            
223             if (dwRet != WAIT_TIMEOUT) {                                          
224               break;
225             }
226         }
227     } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT));
228     
229     return dwRet;
230 }
231
232 /*
233  * Signalling Apache on NT.
234  *
235  * Under Unix, Apache can be told to shutdown or restart by sending various
236  * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so
237  * we use "events" instead. The parent apache process goes into a loop
238  * where it waits forever for a set of events. Two of those events are
239  * called
240  *
241  *    apPID_shutdown
242  *    apPID_restart
243  *
244  * (where PID is the PID of the apache parent process). When one of these
245  * is signalled, the Apache parent performs the appropriate action. The events
246  * can become signalled through internal Apache methods (e.g. if the child
247  * finds a fatal error and needs to kill its parent), via the service
248  * control manager (the control thread will signal the shutdown event when
249  * requested to stop the Apache service), from the -k Apache command line,
250  * or from any external program which finds the Apache PID from the
251  * httpd.pid file.
252  *
253  * The signal_parent() function, below, is used to signal one of these events.
254  * It can be called by any child or parent process, since it does not
255  * rely on global variables.
256  *
257  * On entry, type gives the event to signal. 0 means shutdown, 1 means 
258  * graceful restart.
259  */
260 static void signal_parent(int type)
261 {
262     HANDLE e;
263     char *signal_name;
264     extern char signal_shutdown_name[];
265     extern char signal_restart_name[];
266
267     /* after updating the shutdown_pending or restart flags, we need
268      * to wake up the parent process so it can see the changes. The
269      * parent will normally be waiting for either a child process
270      * to die, or for a signal on the "spache-signal" event. So set the
271      * "apache-signal" event here.
272      */
273     if (one_process) {
274         return;
275     }
276
277     switch(type) {
278     case 0: signal_name = signal_shutdown_name; break;
279     case 1: signal_name = signal_restart_name; break;
280     default: return;
281     }
282     e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name);
283     if (!e) {
284         /* Um, problem, can't signal the parent, which means we can't
285          * signal ourselves to die. Ignore for now...
286          */
287         ap_log_error(APLOG_MARK, APLOG_EMERG, GetLastError(), server_conf,
288                      "OpenEvent on %s event", signal_name);
289         return;
290     }
291     if (SetEvent(e) == 0) {
292         /* Same problem as above */
293         ap_log_error(APLOG_MARK, APLOG_EMERG, GetLastError(), server_conf,
294                      "SetEvent on %s event", signal_name);
295         CloseHandle(e);
296         return;
297     }
298     CloseHandle(e);
299 }
300 static int volatile is_graceful = 0;
301 API_EXPORT(int) ap_graceful_stop_signalled(void)
302 {
303     return is_graceful;
304 }
305 void ap_start_shutdown(void)
306 {
307     signal_parent(0);
308 }
309 /*
310  * Initialise the signal names, in the global variables signal_name_prefix, 
311  * signal_restart_name and signal_shutdown_name.
312  */
313
314 #define MAX_SIGNAL_NAME 30  /* Long enough for apPID_shutdown, where PID is an int */
315 char signal_name_prefix[MAX_SIGNAL_NAME];
316 char signal_restart_name[MAX_SIGNAL_NAME]; 
317 char signal_shutdown_name[MAX_SIGNAL_NAME];
318 static void setup_signal_names(char *prefix)
319 {
320     ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);    
321     ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name), 
322         "%s_shutdown", signal_name_prefix);    
323     ap_snprintf(signal_restart_name, sizeof(signal_restart_name), 
324         "%s_restart", signal_name_prefix);    
325 }
326
327 /*
328  * Routines that deal with sockets, some are WIN32 specific...
329  */
330 static int s_iInitCount = 0;
331 static int AMCSocketInitialize(void)
332 {
333     int iVersionRequested;
334     WSADATA wsaData;
335     int err;
336
337     if (s_iInitCount > 0) {
338         s_iInitCount++;
339         return (0);
340     }
341     else if (s_iInitCount < 0)
342         return (s_iInitCount);
343
344     /* s_iInitCount == 0. Do the initailization */
345     iVersionRequested = MAKEWORD(2, 0);
346     err = WSAStartup((WORD) iVersionRequested, &wsaData);
347     if (err) {
348         s_iInitCount = -1;
349         return (s_iInitCount);
350     }
351     if (LOBYTE(wsaData.wVersion) != 1 ||
352         HIBYTE(wsaData.wVersion) != 1) {
353         s_iInitCount = -2;
354         WSACleanup();
355         return (s_iInitCount);
356     }
357
358     s_iInitCount++;
359     return (s_iInitCount);
360
361 }
362 static void AMCSocketCleanup(void)
363 {
364     if (--s_iInitCount == 0)
365         WSACleanup();
366     return;
367 }
368
369 static void sock_disable_nagle(int s) 
370 {
371     /* The Nagle algorithm says that we should delay sending partial
372      * packets in hopes of getting more data.  We don't want to do
373      * this; we are not telnet.  There are bad interactions between
374      * persistent connections and Nagle's algorithm that have very severe
375      * performance penalties.  (Failing to disable Nagle is not much of a
376      * problem with simple HTTP.)
377      *
378      * In spite of these problems, failure here is not a shooting offense.
379      */
380     int just_say_no = 1;
381
382     if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no,
383                    sizeof(int)) < 0) {
384         ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, server_conf,
385                     "setsockopt: (TCP_NODELAY)");
386     }
387 }
388
389 /*
390  * Routines to deal with managing the list of listening sockets.
391  */
392 static ap_listen_rec *head_listener;
393 static ap_inline ap_listen_rec *find_ready_listener(fd_set * main_fds)
394 {
395     ap_listen_rec *lr;
396     SOCKET nsd;
397
398     for (lr = head_listener; lr ; lr = lr->next) {
399         ap_get_os_sock(&nsd, lr->sd);
400         if (FD_ISSET(nsd, main_fds)) {
401             head_listener = lr->next;
402             if (head_listener == NULL)
403                 head_listener = ap_listeners;
404
405             return (lr);
406         }
407     }
408     return NULL;
409 }
410 static int setup_listeners(server_rec *s)
411 {
412     ap_listen_rec *lr;
413     int num_listeners = 0;
414     SOCKET nsd;
415
416     /* Setup the listeners */
417     FD_ZERO(&listenfds);
418
419     if (ap_listen_open(s->process, s->port)) {
420        return 0;
421     }
422     for (lr = ap_listeners; lr; lr = lr->next) {
423         num_listeners++;
424         if (lr->sd != NULL) {
425             ap_get_os_sock(&nsd, lr->sd);
426             FD_SET(nsd, &listenfds);
427             if (listenmaxfd == INVALID_SOCKET || nsd > listenmaxfd) {
428                 listenmaxfd = nsd;
429             }
430         }
431         lr->count = 0;
432     }
433
434     head_listener = ap_listeners;
435
436     return num_listeners;
437 }
438
439 static int setup_inherited_listeners(server_rec *s)
440 {
441     WSAPROTOCOL_INFO WSAProtocolInfo;
442     HANDLE pipe;
443     ap_listen_rec *lr;
444     DWORD BytesRead;
445     int num_listeners = 0;
446     SOCKET nsd;
447
448     /* Setup the listeners */
449     FD_ZERO(&listenfds);
450
451     /* Set up a default listener if necessary */
452
453     if (ap_listeners == NULL) {
454         ap_listen_rec *lr;
455         lr = ap_palloc(s->process->pool, sizeof(ap_listen_rec));
456         if (!lr)
457             return 0;
458         lr->sd = NULL;
459         lr->next = ap_listeners;
460         ap_listeners = lr;
461     }
462
463     /* Open the pipe to the parent process to receive the inherited socket
464      * data. The sockets have been set to listening in the parent process.
465      */
466     pipe = GetStdHandle(STD_INPUT_HANDLE);
467     for (lr = ap_listeners; lr; lr = lr->next) {
468         if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), 
469                       &BytesRead, (LPOVERLAPPED) NULL)) {
470             ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
471                          "setup_inherited_listeners: Unable to read socket data from parent");
472             signal_parent(0);   /* tell parent to die */
473             exit(1);
474         }
475         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, server_conf,
476                      "Child %d: setup_inherited_listener() read = %d bytes of WSAProtocolInfo.", my_pid);
477         nsd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
478                         &WSAProtocolInfo, 0, 0);
479         if (nsd == INVALID_SOCKET) {
480             ap_log_error(APLOG_MARK, APLOG_CRIT, WSAGetLastError(), server_conf,
481                          "Child %d: setup_inherited_listeners(), WSASocket failed to open the inherited socket.", my_pid);
482             signal_parent(0);   /* tell parent to die */
483             exit(1);
484         }
485         if (nsd >= 0) {
486             FD_SET(nsd, &listenfds);
487             if (listenmaxfd == INVALID_SOCKET || nsd > listenmaxfd) {
488                 listenmaxfd = nsd;
489             }
490         }
491 //        ap_register_cleanup(p, (void *)lr->sd, socket_cleanup, ap_null_cleanup);
492         ap_put_os_sock(&lr->sd, &nsd, pconf);
493         lr->count = 0;
494     }
495     /* Now, read the AcceptExCompPort from the parent */
496     ReadFile(pipe, &AcceptExCompPort, sizeof(AcceptExCompPort), &BytesRead, (LPOVERLAPPED) NULL);
497     CloseHandle(pipe);
498
499     for (lr = ap_listeners; lr; lr = lr->next) {
500         num_listeners++;
501     }
502
503     head_listener = ap_listeners;
504     return num_listeners;
505 }
506
507 static void bind_listeners_to_completion_port()
508 {
509     /* Associate the open listeners with the completion port.
510      * Bypass the operation for Windows 95/98
511      */
512     ap_listen_rec *lr;
513
514     if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
515         for (lr = ap_listeners; lr; lr = lr->next) {
516             int nsd;
517             ap_get_os_sock(&nsd,lr->sd);
518             CreateIoCompletionPort((HANDLE) nsd, AcceptExCompPort, 0, 0);
519         }
520     }
521 }
522
523 /**********************************************************************
524  * Multithreaded implementation
525  *
526  * This code is fairly specific to Win32.
527  *
528  * The model used to handle requests is a set of threads. One "main"
529  * thread listens for new requests. When something becomes
530  * available, it does a select and places the newly available socket
531  * onto a list of "jobs" (add_job()). Then any one of a fixed number
532  * of "worker" threads takes the top job off the job list with
533  * remove_job() and handles that connection to completion. After
534  * the connection has finished the thread is free to take another
535  * job from the job list.
536  *
537  * In the code, the "main" thread is running within the child_main()
538  * function. The first thing this function does is create the
539  * worker threads, which operate in the child_sub_main() function. The
540  * main thread then goes into a loop within child_main() where they
541  * do a select() on the listening sockets. The select times out once
542  * per second so that the thread can check for an "exit" signal
543  * from the parent process (see below). If this signal is set, the 
544  * thread can exit, but only after it has accepted all incoming
545  * connections already in the listen queue (since Win32 appears
546  * to through away listened but unaccepted connections when a 
547  * process dies).
548  *
549  * Because the main and worker threads exist within a single process
550  * they are vulnerable to crashes or memory leaks (crashes can also
551  * be caused within modules, of course). There also needs to be a 
552  * mechanism to perform restarts and shutdowns. This is done by
553  * creating the main & worker threads within a subprocess. A
554  * main process (the "parent process") creates one (or more) 
555  * processes to do the work, then the parent sits around waiting
556  * for the working process to die, in which case it starts a new
557  * one. The parent process also handles restarts (by creating
558  * a new working process then signalling the previous working process 
559  * exit ) and shutdowns (by signalling the working process to exit).
560  * The parent process operates within the master_main() function. This
561  * process also handles requests from the service manager (NT only).
562  *
563  * Signalling between the parent and working process uses a Win32
564  * event. Each child has a unique name for the event, which is
565  * passed to it with the -Z argument when the child is spawned. The
566  * parent sets (signals) this event to tell the child to die.
567  * At present all children do a graceful die - they finish all
568  * current jobs _and_ empty the listen queue before they exit.
569  * A non-graceful die would need a second event. The -Z argument in
570  * the child is also used to create the shutdown and restart events,
571  * since the prefix (apPID) contains the parent process PID.
572  *
573  * The code below starts with functions at the lowest level -
574  * worker threads, and works up to the top level - the main()
575  * function of the parent process.
576  *
577  * The scoreboard (in process memory) contains details of the worker
578  * threads (within the active working process). There is no shared
579  * "scoreboard" between processes, since only one is ever active
580  * at once (or at most, two, when one has been told to shutdown but
581  * is processes outstanding requests, and a new one has been started).
582  * This is controlled by a "start_mutex" which ensures only one working
583  * process is active at once.
584  **********************************************************************/
585
586
587 /*
588  * Definition of jobs, shared by main and worker threads.
589  */
590
591 typedef struct joblist_s {
592     struct joblist_s *next;
593     int sock;
594 } joblist;
595
596 /*
597  * Globals common to main and worker threads. This structure is not
598  * used by the parent process.
599  */
600
601 typedef struct globals_s {
602     semaphore *jobsemaphore;
603     joblist *jobhead;
604     joblist *jobtail;
605     ap_lock_t *jobmutex;
606     int jobcount;
607 } globals;
608
609 globals allowed_globals =
610 {NULL, NULL, NULL, NULL, 0};
611 #define MAX_SELECT_ERRORS 100
612 #define PADDED_ADDR_SIZE sizeof(SOCKADDR_IN)+16
613
614 /* Windows 9x specific code...
615  * Accept processing for on Windows 95/98 uses a producer/consumer queue 
616  * model. A single thread accepts connections and queues the accepted socket 
617  * to the accept queue for consumption by a pool of worker threads.
618  *
619  * win9x_get_connection()
620  *    Calls remove_job() to pull a job from the accept queue. All the worker 
621  *    threads block on remove_job.
622  * accept_and_queue_connections()
623  *    The accept threads runs this function, which accepts connections off 
624  *    the network and calls add_job() to queue jobs to the accept_queue.
625  * add_job()/remove_job()
626  *    Add or remove an accepted socket from the list of sockets 
627  *    connected to clients. allowed_globals.jobmutex protects
628  *    against multiple concurrent access to the linked list of jobs.
629  */
630 static void add_job(int sock)
631 {
632     joblist *new_job;
633
634     new_job = (joblist *) malloc(sizeof(joblist));
635     if (new_job == NULL) {
636         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
637                      "Ouch!  Out of memory in add_job()!");
638         return;
639     }
640     new_job->next = NULL;
641     new_job->sock = sock;
642
643     ap_lock(allowed_globals.jobmutex);
644
645     if (allowed_globals.jobtail != NULL)
646         allowed_globals.jobtail->next = new_job;
647     allowed_globals.jobtail = new_job;
648     if (!allowed_globals.jobhead)
649         allowed_globals.jobhead = new_job;
650     allowed_globals.jobcount++;
651     release_semaphore(allowed_globals.jobsemaphore);
652
653     ap_unlock(allowed_globals.jobmutex);
654 }
655
656 static int remove_job(void)
657 {
658     joblist *job;
659     int sock;
660
661     acquire_semaphore(allowed_globals.jobsemaphore);
662     ap_lock(allowed_globals.jobmutex);
663
664     if (workers_may_exit && !allowed_globals.jobhead) {
665         ap_unlock(allowed_globals.jobmutex);
666         return (-1);
667     }
668     job = allowed_globals.jobhead;
669     ap_assert(job);
670     allowed_globals.jobhead = job->next;
671     if (allowed_globals.jobhead == NULL)
672         allowed_globals.jobtail = NULL;
673     ap_unlock(allowed_globals.jobmutex);
674     sock = job->sock;
675     free(job);
676
677     return (sock);
678 }
679
680 static void accept_and_queue_connections(void * dummy)
681 {
682     int requests_this_child = 0;
683     struct timeval tv;
684     fd_set main_fds;
685     int wait_time = 1;
686     int csd;
687     int nsd = INVALID_SOCKET;
688     struct sockaddr_in sa_client;
689     int count_select_errors = 0;
690     int rc;
691     int clen;
692
693     while (!workers_may_exit) {
694         if (ap_max_requests_per_child && (requests_this_child > ap_max_requests_per_child)) {
695             break;
696         }
697
698         tv.tv_sec = wait_time;
699         tv.tv_usec = 0;
700         memcpy(&main_fds, &listenfds, sizeof(fd_set));
701
702 //      rc = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
703         rc = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
704
705         if (rc == 0 || (rc == SOCKET_ERROR && h_errno == WSAEINTR)) {
706             count_select_errors = 0;    /* reset count of errors */            
707             continue;
708         }
709         else if (rc == SOCKET_ERROR) {
710             /* A "real" error occurred, log it and increment the count of
711              * select errors. This count is used to ensure we don't go into
712              * a busy loop of continuous errors.
713              */
714             ap_log_error(APLOG_MARK, APLOG_INFO, h_errno, server_conf, 
715                          "select failed with errno %d", h_errno);
716             count_select_errors++;
717             if (count_select_errors > MAX_SELECT_ERRORS) {
718                 workers_may_exit = 1;
719                 ap_log_error(APLOG_MARK, APLOG_ERR, h_errno, server_conf,
720                              "Too many errors in select loop. Child process exiting.");
721                 break;
722             }
723         } else {
724             ap_listen_rec *lr;
725
726             lr = find_ready_listener(&main_fds);
727             if (lr != NULL) {
728                 /* fetch the native socket descriptor */
729                 ap_get_os_sock(&nsd, lr->sd);
730             }
731         }
732
733         do {
734             clen = sizeof(sa_client);
735             csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);
736             if (csd == INVALID_SOCKET) {
737                 csd = -1;
738             }
739         } while (csd < 0 && h_errno == WSAEINTR);
740
741         if (csd < 0) {
742             if (h_errno != WSAECONNABORTED) {
743                 ap_log_error(APLOG_MARK, APLOG_ERR, h_errno, server_conf,
744                             "accept: (client socket)");
745             }
746         }
747         else {
748             add_job(csd);
749             requests_this_child++;
750         }
751     }
752     SetEvent(exit_event);
753 }
754 static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
755 {
756     int len;
757
758     if (context == NULL) {
759         /* allocate the completion context and the transaction pool */
760         context = ap_pcalloc(pconf, sizeof(COMP_CONTEXT));
761         if (!context) {
762             ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
763                          "win9x_get_connection: ap_pcalloc() failed. Process will exit.");
764             return NULL;
765         }
766         ap_create_pool(&context->ptrans, pconf);
767     }
768     
769
770     while (1) {
771         ap_clear_pool(context->ptrans);        
772         context->accept_socket = remove_job();
773         if (context->accept_socket == -1) {
774             return NULL;
775         }
776         //ap_note_cleanups_for_socket(ptrans, csd);
777         len = sizeof(struct sockaddr);
778         context->sa_server = ap_palloc(context->ptrans, len);
779         if (getsockname(context->accept_socket, 
780                         context->sa_server, &len)== SOCKET_ERROR) {
781             ap_log_error(APLOG_MARK, APLOG_WARNING, WSAGetLastError(), server_conf, 
782                          "getsockname failed");
783             continue;
784         }
785         len = sizeof(struct sockaddr);
786         context->sa_client = ap_palloc(context->ptrans, len);
787         if ((getpeername(context->accept_socket,
788                          context->sa_client, &len)) == SOCKET_ERROR) {
789             ap_log_error(APLOG_MARK, APLOG_WARNING, h_errno, server_conf, 
790                          "getpeername failed with error %d\n", WSAGetLastError());
791             memset(&context->sa_client, '\0', sizeof(context->sa_client));
792         }
793
794         context->conn_io = ap_bcreate(context->ptrans, B_RDWR);
795
796         /* do we NEED_DUPPED_CSD ?? */
797         
798         return context;
799     }
800 }
801
802 /* 
803  * Windows 2000/NT specific code...
804  * create_acceptex_context()
805  * reset_acceptex_context()
806  * drain_acceptex_complport()
807  * winnt_get_connection()
808  *
809  * TODO: Insert a discussion of 'completion contexts' and what these function do here...
810  */
811 static void drain_acceptex_complport(HANDLE hComplPort, BOOLEAN bCleanUp) 
812 {
813     LPOVERLAPPED pol;
814     PCOMP_CONTEXT context;
815     int rc;
816     DWORD BytesRead;
817     DWORD CompKey;
818     int lastError;
819
820     while (1) {
821         context = NULL;
822         rc = GetQueuedCompletionStatus(hComplPort, &BytesRead, &CompKey,
823                                        &pol, 1000);
824         if (!rc) {
825             lastError = GetLastError();
826             if (lastError == ERROR_OPERATION_ABORTED) {
827                 ap_log_error(APLOG_MARK,APLOG_INFO,lastError, server_conf,
828                              "Child %d: - Draining a packet off the completion port.", my_pid);
829                 continue;
830             }
831             break;
832         }
833         ap_log_error(APLOG_MARK,APLOG_INFO,APR_SUCCESS, server_conf,
834                      "Child %d: - Nuking an active connection. context = %x", my_pid, context);
835         context = (PCOMP_CONTEXT) pol;
836         if (context && bCleanUp) {
837             /* It is only valid to clean-up in the process that initiated the I/O */
838             closesocket(context->accept_socket);
839             CloseHandle(context->Overlapped.hEvent);
840         }
841     }
842 }
843 static int create_acceptex_context(ap_pool_t *_pconf, ap_listen_rec *lr) 
844 {
845     PCOMP_CONTEXT context;
846     DWORD BytesRead;
847     SOCKET nsd;
848     int lasterror;
849
850     /* allocate the completion context */
851     context = ap_pcalloc(_pconf, sizeof(COMP_CONTEXT));
852     if (!context) {
853         ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
854                      "create_acceptex_context: ap_pcalloc() failed. Process will exit.");
855         return -1;
856     }
857
858     /* initialize the completion context */
859     context->lr = lr;
860     context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
861     if (context->Overlapped.hEvent == NULL) {
862         ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
863                      "create_acceptex_context: CreateEvent() failed. Process will exit.");
864         return -1;
865     }
866
867     /* create and initialize the accept socket */
868     ap_get_os_sock(&nsd, context->lr->sd);
869     context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
870     if (context->accept_socket == INVALID_SOCKET) {
871         ap_log_error(APLOG_MARK,APLOG_ERR, WSAGetLastError(), server_conf,
872                      "create_acceptex_context: socket() failed. Process will exit.");
873         return -1;
874     }
875
876     /* SO_UPDATE_ACCEPT_CONTEXT is required for shutdown() to work */
877     if (setsockopt(context->accept_socket, SOL_SOCKET,
878                    SO_UPDATE_ACCEPT_CONTEXT, (char *)&nsd,
879                    sizeof(nsd))) {
880         ap_log_error(APLOG_MARK, APLOG_ERR, WSAGetLastError(), server_conf,
881                      "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
882         /* Not a failure condition. Keep running. */
883     }
884
885     ap_create_pool(&context->ptrans, _pconf);
886     context->conn_io = ap_bcreate(context->ptrans, B_RDWR);
887     context->recv_buf = context->conn_io->inbase;
888     context->recv_buf_size = context->conn_io->bufsiz - 2*PADDED_ADDR_SIZE;
889
890
891     /* AcceptEx on the completion context. The completion context will be signaled
892      * when a connection is accepted. */
893     if (!AcceptEx(nsd, context->accept_socket,
894                   context->recv_buf, 
895                   0, //context->recv_buf_size,
896                   PADDED_ADDR_SIZE, PADDED_ADDR_SIZE,
897                   &BytesRead,
898                   (LPOVERLAPPED) context)) {
899         lasterror = WSAGetLastError();
900         if (lasterror != ERROR_IO_PENDING) {
901             ap_log_error(APLOG_MARK,APLOG_ERR, WSAGetLastError(), server_conf,
902                          "create_acceptex_context: AcceptEx failed. Process will exit.");
903             return -1;
904         }
905
906     }
907     lr->count++;
908
909     return 0;
910 }
911 static ap_inline int reset_acceptex_context(PCOMP_CONTEXT context) 
912 {
913     DWORD BytesRead;
914     SOCKET nsd;
915     int lasterror;
916
917     context->lr->count++;
918
919     /* recreate and initialize the accept socket if it is not being reused */
920     ap_get_os_sock(&nsd, context->lr->sd);
921     if (context->accept_socket == INVALID_SOCKET) {
922         context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
923         if (context->accept_socket == INVALID_SOCKET) {
924             ap_log_error(APLOG_MARK,APLOG_ERR, WSAGetLastError(), server_conf,
925                          "reset_acceptex_context: socket() failed. Process will exit.");
926             return -1;
927         }
928         
929         /* SO_UPDATE_ACCEPT_CONTEXT is required for shutdown() to work */
930         if (setsockopt(context->accept_socket, SOL_SOCKET,
931                        SO_UPDATE_ACCEPT_CONTEXT, (char *)&nsd,
932                        sizeof(nsd))) {
933             ap_log_error(APLOG_MARK, APLOG_WARNING, WSAGetLastError(),
934                          server_conf,
935                          "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
936             /* Not a failure condition. Keep running. */
937         }
938     }
939
940     /* reset the completion context */
941     ap_clear_pool(context->ptrans);
942     context->sock = NULL;
943     context->conn_io = ap_bcreate(context->ptrans, B_RDWR);
944     context->recv_buf = context->conn_io->inbase;
945     context->recv_buf_size = context->conn_io->bufsiz - 2*PADDED_ADDR_SIZE;
946
947     /* AcceptEx on the completion context. The completion context will be signaled
948      * when a connection is accepted. */
949     if (!AcceptEx(nsd, context->accept_socket, 
950                   context->recv_buf, 
951                   0, //context->recv_buf_size,
952                   PADDED_ADDR_SIZE, PADDED_ADDR_SIZE,
953                   &BytesRead, (LPOVERLAPPED) context)) {
954         lasterror = WSAGetLastError();
955         if (lasterror != ERROR_IO_PENDING) {
956             ap_log_error(APLOG_MARK,APLOG_ERR, WSAGetLastError(), server_conf,
957                          "reset_acceptex_context: AcceptEx failed. Leaving the process running.");
958             return -1;
959         }
960     }
961
962     return 0;
963 }
964 static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
965 {
966     int requests_this_child = 0;
967     int rc;
968     LPOVERLAPPED pol;
969     DWORD CompKey;
970     DWORD BytesRead;
971
972     if (context != NULL) {
973         /* If child shutdown has been signaled, clean-up the completion context */
974         if (workers_may_exit) {
975             CloseHandle(context->Overlapped.hEvent);
976             /* destroy pool */
977         }
978         else {
979             context->accept_socket = INVALID_SOCKET; /* Don't reuse the accept_socket */
980             if (reset_acceptex_context(context) == -1) {
981                 if (context->accept_socket != -1)
982                     closesocket(context->accept_socket);
983                 CloseHandle(context->Overlapped.hEvent);
984                 return NULL;
985             }
986         }
987     }
988
989     while (1) {
990         rc = GetQueuedCompletionStatus(AcceptExCompPort,
991                                        &BytesRead,
992                                        &CompKey,
993                                        &pol,
994                                        INFINITE);
995         if (!rc) {
996             ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
997                          "Child %d: - GetQueuedCompletionStatus() failed", my_pid);
998             /* During a restart, the new child process can catch 
999              * ERROR_OPERATION_ABORTED completion packets
1000              * posted by the old child process. Just continue...
1001              */
1002             continue;
1003         }
1004
1005         /* Check the Completion Key.
1006          * == my_pid indicate this process wants to exit
1007          * == 0 implies valid i/o completion
1008          * != 0 implies a posted completion packet by an old
1009          *     process. Just ignore it.
1010          */
1011         if (CompKey == my_pid) {
1012             return NULL;
1013         }
1014         if (CompKey != 0)
1015             continue;
1016
1017         context = (PCOMP_CONTEXT) pol;
1018         break;
1019     }
1020
1021     /* Check to see if we need to create more completion contexts,
1022      * but only if we are not in the process of shutting down
1023      */
1024     if (!workers_may_exit) {
1025         ap_lock(allowed_globals.jobmutex);
1026         context->lr->count--;
1027         if (context->lr->count < 2) {
1028             SetEvent(maintenance_event);
1029         }
1030         ap_unlock(allowed_globals.jobmutex);
1031     }
1032
1033     /* Received a connection */
1034     context->conn_io->incnt = BytesRead;
1035     GetAcceptExSockaddrs(context->recv_buf, 
1036                          0, //context->recv_buf_size,
1037                          PADDED_ADDR_SIZE,
1038                          PADDED_ADDR_SIZE,
1039                          &context->sa_server,
1040                          &context->sa_server_len,
1041                          &context->sa_client,
1042                          &context->sa_client_len);
1043
1044     return context;
1045
1046 }
1047 /*
1048  * worker_main() - this is the main loop for the worker threads
1049  *
1050  * Windows 95/98
1051  * Each thread runs within this function. They wait within remove_job()
1052  * for a job to become available, then handle all the requests on that
1053  * connection until it is closed, then return to remove_job().
1054  *
1055  * The worker thread will exit when it removes a job which contains
1056  * socket number -1. This provides a graceful thread exit, since
1057  * it will never exit during a connection.
1058  *
1059  * This code in this function is basically equivalent to the child_main()
1060  * from the multi-process (Unix) environment, except that we
1061  *
1062  *  - do not call child_init_modules (child init API phase)
1063  *  - block in remove_job, and when unblocked we have an already
1064  *    accepted socket, instead of blocking on a mutex or select().
1065  */
1066
1067 static void worker_main(int child_num)
1068 {
1069     PCOMP_CONTEXT context = NULL;
1070
1071     while (1) {
1072         conn_rec *current_conn;
1073         ap_iol *iol;
1074
1075         /* Grab a connection off the network */
1076         if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1077             context = win9x_get_connection(context);
1078         }
1079         else {
1080             context = winnt_get_connection(context);
1081         }
1082
1083         if (!context)
1084             break;
1085         sock_disable_nagle(context->accept_socket);
1086         ap_put_os_sock(&context->sock, &context->accept_socket, context->ptrans);
1087 //        ap_register_cleanup(context->ptrans, context->sock, socket_cleanup, ap_null_cleanup);
1088         iol = win32_attach_socket(context->ptrans, context->sock);
1089         if (iol == NULL) {
1090             ap_log_error(APLOG_MARK, APLOG_ERR, APR_ENOMEM, server_conf,
1091                          "worker_main: attach_socket() failed. Continuing...");
1092             closesocket(context->accept_socket);
1093             continue;
1094         }
1095         ap_bpush_iol(context->conn_io, iol);
1096         current_conn = ap_new_connection(context->ptrans, server_conf, context->conn_io,
1097                                          (struct sockaddr_in *) context->sa_client,
1098                                          (struct sockaddr_in *) context->sa_server,
1099                                          child_num);
1100
1101         ap_process_connection(current_conn);
1102     }
1103
1104     ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
1105                  "Child %d: Thread exiting.", my_pid);
1106 #if 0
1107
1108     SetEvent(exit_event);
1109 #endif
1110     /* TODO: Add code to clean-up completion contexts here */
1111 }
1112
1113 static void cleanup_thread(thread **handles, int *thread_cnt, int thread_to_clean)
1114 {
1115     int i;
1116
1117     CloseHandle(handles[thread_to_clean]);
1118     for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
1119         handles[i] = handles[i + 1];
1120     (*thread_cnt)--;
1121 }
1122
1123 static void create_listeners() 
1124 {
1125 #define NUM_LISTENERS 5
1126     ap_listen_rec *lr;
1127     for (lr = ap_listeners; lr != NULL; lr = lr->next) {
1128         while (lr->count < NUM_LISTENERS) {
1129             if (create_acceptex_context(pconf, lr) == -1) {
1130                 ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
1131                              "Unable to create an AcceptEx completion context -- process will exit");
1132                 signal_parent(0);       /* tell parent to die */
1133             }
1134         }
1135     }
1136 }
1137 /*
1138  * child_main() runs the main control thread for the child process. 
1139  *
1140  * The control thread:
1141  * - sets up the worker thread pool
1142  * - starts the accept thread (Win 9x)
1143  * - creates AcceptEx contexts (Win NT)
1144  * - waits for exit_event, maintenance_event or maintenance timeout
1145  *   and does the right thing depending on which event is received.
1146  */
1147 static void child_main()
1148 {
1149     ap_status_t status;
1150     HANDLE child_events[2];
1151     char* exit_event_name;
1152     int nthreads = ap_threads_per_child;
1153     int thread_id;
1154     thread **child_handles;
1155     int rv;
1156     time_t end_time;
1157     int i;
1158     int cld;
1159     ap_pool_t *pchild;
1160
1161
1162     /* This is the child process or we are running in single process
1163      * mode.
1164      */
1165     exit_event_name = ap_psprintf(pconf, "apC%d", my_pid);
1166     setup_signal_names(ap_psprintf(pconf,"ap%d", parent_pid));
1167
1168     if (one_process) {
1169         /* Single process mode */
1170         ap_create_lock(&start_mutex,APR_MUTEX, APR_CROSS_PROCESS,signal_name_prefix,pconf);
1171         exit_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name);
1172
1173         setup_listeners(server_conf);
1174         bind_listeners_to_completion_port();
1175     }
1176     else {
1177         /* Child process mode */
1178         ap_child_init_lock(&start_mutex, signal_name_prefix, pconf);
1179         exit_event = OpenEvent(EVENT_ALL_ACCESS, FALSE, exit_event_name);
1180         ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
1181                      "Child %d: exit_event_name = %s", my_pid, exit_event_name);
1182
1183         setup_inherited_listeners(server_conf);
1184     }
1185
1186     /* Initialize the child_events */
1187     maintenance_event = CreateEvent(NULL, TRUE, FALSE, NULL);
1188     child_events[0] = exit_event;
1189     child_events[1] = maintenance_event;
1190
1191     ap_assert(start_mutex);
1192     ap_assert(exit_event);
1193     ap_assert(maintenance_event);
1194
1195     ap_create_pool(&pchild, pconf);
1196     allowed_globals.jobsemaphore = create_semaphore(0);
1197     ap_create_lock(&allowed_globals.jobmutex, APR_MUTEX, APR_INTRAPROCESS, NULL, pchild);
1198
1199     /*
1200      * Wait until we have permission to start accepting connections.
1201      * start_mutex is used to ensure that only one child ever
1202      * goes into the listen/accept loop at once.
1203      */
1204     status = ap_lock(start_mutex);
1205     if (status != APR_SUCCESS) {
1206         ap_log_error(APLOG_MARK,APLOG_ERR, status, server_conf,
1207                      "Child %d: Failed to acquire the start_mutex. Process will exit.", my_pid);
1208         signal_parent(0);       /* tell parent to die */
1209         exit(0);
1210     }
1211     ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, 
1212                  "Child %d: Acquired the start mutex.", my_pid);
1213
1214     /* Create the worker thread pool */
1215     ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, 
1216                  "Child %d: Starting %d worker threads.", my_pid, nthreads);
1217     child_handles = (thread *) alloca(nthreads * sizeof(int));
1218     for (i = 0; i < nthreads; i++) {
1219         child_handles[i] = (thread *) _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) worker_main,
1220                                                      NULL, 0, &thread_id);
1221     }
1222
1223     /* Begin accepting connections */
1224     if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1225         /* Win95/98: Start the accept thread */
1226         _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) accept_and_queue_connections,
1227                        (void *) i, 0, &thread_id);
1228     } else {
1229         /* Windows NT/2000: Create AcceptEx completion contexts */
1230         create_listeners();
1231     }
1232
1233     /* Wait for one of three events:
1234      * exit_event: 
1235      *    The exit_event is signaled by the parent process to notify 
1236      *    the child that it is time to exit.
1237      *
1238      * maintenance_event: 
1239      *    This event is signaled by the worker thread pool to direct 
1240      *    this thread to create more completion contexts.
1241      *
1242      * TIMEOUT:
1243      *    To do periodic maintenance on the server (check for thread exits,
1244      *    number of completion contexts, etc.)
1245      */
1246     while (!workers_may_exit) {
1247         rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, INFINITE);
1248         cld = rv - WAIT_OBJECT_0;
1249         if (rv == WAIT_FAILED) {
1250             /* Something serious is wrong */
1251             workers_may_exit = 1;
1252             ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
1253                          "Child %d: WaitForMultipeObjects WAIT_FAILED -- doing server shutdown");
1254             /* Give a busy server the chance to drain AcceptEx completion contexts
1255              * by servicing connections. Note that the setting of workers_may_exit
1256              * prevents new AcceptEx completion contexts from being created.
1257              */
1258             Sleep(1000);
1259
1260             /* Drain any remaining contexts. May loose a few connections here. */
1261             drain_acceptex_complport(AcceptExCompPort, FALSE);
1262         }
1263         else if (rv == WAIT_TIMEOUT) {
1264             /* Hey, this cannot happen */
1265             ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
1266                          "Child %d: Server maintenance...", my_pid);
1267         }
1268         else if (cld == 0) {
1269             /* Exit event was signaled */
1270             workers_may_exit = 1;
1271             ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
1272                          "Child %d: Exit event signaled. Child process is ending.", my_pid);
1273         }
1274         else {
1275             /* Child maintenance event signaled */
1276             if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
1277                 create_listeners();
1278             }
1279             ResetEvent(maintenance_event);
1280             ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
1281                          "Child %d: Child maintenance event signaled...", my_pid);
1282         }
1283     }
1284
1285     /* Shutdown the worker threads */
1286     if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1287         for (i = 0; i < nthreads; i++) {
1288             add_job(-1);
1289         }
1290     }
1291     else { /* Windows NT/2000 */
1292         /* Sometimes posted completion contexts intended for this process
1293          * are consumed by the new process (during restart). Send a few 
1294          * extra to compensate. At worst, this process may hang around
1295          * for several minutes because a thread is not exiting because
1296          * it is blocked on GetQueuedCompletionStatus.
1297          */
1298         for (i=0; i < 2*nthreads; i++) {
1299             if (!PostQueuedCompletionStatus(AcceptExCompPort, 0, my_pid, NULL)) {
1300                 ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, 
1301                              "PostQueuedCompletionStatus failed");
1302             }
1303         }
1304     }
1305
1306     /* Release the start_mutex to let the new process (in the restart
1307      * scenario) a chance to begin servicing requests 
1308      */
1309     ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, 
1310                  "Child %d: Releasing the start mutex", my_pid);
1311     ap_unlock(start_mutex);
1312
1313     /* Give busy worker threads a chance to service their connections.
1314      * Kill them off if they take too long
1315      */
1316     ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, 
1317                  "Child %d: Waiting for %d threads to die.", my_pid, nthreads);
1318     end_time = time(NULL) + 180;
1319     while (nthreads) {
1320         rv = wait_for_many_objects(nthreads, child_handles, end_time - time(NULL));
1321         if (rv != WAIT_TIMEOUT) {
1322             rv = rv - WAIT_OBJECT_0;
1323             ap_assert((rv >= 0) && (rv < nthreads));
1324             cleanup_thread(child_handles, &nthreads, rv);
1325             continue;
1326         }
1327         break;
1328     }
1329     for (i = 0; i < nthreads; i++) {
1330         TerminateThread(child_handles[i], 1);
1331         CloseHandle(child_handles[i]);
1332     }
1333     ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, server_conf, 
1334                  "Child %d: All worker threads have ended.", my_pid);
1335
1336     CloseHandle(AcceptExCompPort);
1337     destroy_semaphore(allowed_globals.jobsemaphore);
1338     ap_destroy_lock(allowed_globals.jobmutex);
1339
1340     ap_destroy_pool(pchild);
1341     CloseHandle(exit_event);
1342 }
1343
1344 /*
1345  * Spawn a child Apache process. The child process has the command line arguments from
1346  * argc and argv[], plus a -Z argument giving the name of an event. The child should
1347  * open and poll or wait on this event. When it is signalled, the child should die.
1348  * prefix is a prefix string for the event name.
1349  * 
1350  * The child_num argument on entry contains a serial number for this child (used to create
1351  * a unique event name). On exit, this number will have been incremented by one, ready
1352  * for the next call. 
1353  *
1354  * On exit, the value pointed to be *ev will contain the event created
1355  * to signal the new child process.
1356  *
1357  * The return value is the handle to the child process if successful, else -1. If -1 is
1358  * returned the error will already have been logged by ap_log_error().
1359  */
1360
1361 /**********************************************************************
1362  * master_main - this is the parent (main) process. We create a
1363  * child process to do the work, then sit around waiting for either
1364  * the child to exit, or a restart or exit signal. If the child dies,
1365  * we just respawn a new one. If we have a shutdown or graceful restart,
1366  * tell the child to die when it is ready. If it is a non-graceful
1367  * restart, force the child to die immediately.
1368  **********************************************************************/
1369
1370 #define MAX_PROCESSES 50 /* must be < MAX_WAIT_OBJECTS-1 */
1371
1372 static void cleanup_process(HANDLE *handles, HANDLE *events, int position, int *processes)
1373 {
1374     int i;
1375     int handle = 0;
1376
1377     CloseHandle(handles[position]);
1378     CloseHandle(events[position]);
1379
1380     handle = (int)handles[position];
1381
1382     for (i = position; i < (*processes)-1; i++) {
1383         handles[i] = handles[i + 1];
1384         events[i] = events[i + 1];
1385     }
1386     (*processes)--;
1387 }
1388
1389 static int create_process(ap_pool_t *p, HANDLE *handles, HANDLE *events, int *processes)
1390 {
1391
1392     int rv;
1393     char buf[1024];
1394     char *pCommand;
1395     int i;
1396     STARTUPINFO si;           /* Filled in prior to call to CreateProcess */
1397     PROCESS_INFORMATION pi;   /* filled in on call to CreateProces */
1398
1399     ap_listen_rec *lr;
1400     DWORD BytesWritten;
1401     HANDLE hPipeRead = NULL;
1402     HANDLE hPipeWrite = NULL;
1403     SECURITY_ATTRIBUTES sa = {0};  
1404
1405     sa.nLength = sizeof(sa);
1406     sa.bInheritHandle = TRUE;
1407     sa.lpSecurityDescriptor = NULL;
1408
1409     /* Build the command line. Should look something like this:
1410      * C:/apache/bin/apache.exe -f ap_server_confname 
1411      * First, get the path to the executable...
1412      */
1413     rv = GetModuleFileName(NULL, buf, sizeof(buf));
1414     if (rv == sizeof(buf)) {
1415         ap_log_error(APLOG_MARK, APLOG_CRIT, ERROR_BAD_PATHNAME, server_conf,
1416                      "Parent: Path to Apache process too long");
1417         return -1;
1418     } else if (rv == 0) {
1419         ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
1420                      "Parent: GetModuleFileName() returned NULL for current process.");
1421         return -1;
1422     }
1423
1424     /* Build the command line */
1425     pCommand = ap_psprintf(p, "\"%s\"", buf);  
1426     for (i = 1; i < server_conf->process->argc; i++) {
1427         pCommand = ap_pstrcat(p, pCommand, " \"", server_conf->process->argv[i], "\"", NULL);
1428     }
1429
1430     /* Create a pipe to send socket info to the child */
1431     if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) {
1432         ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
1433                      "Parent: Unable to create pipe to child process.\n");
1434         return -1;
1435     }
1436
1437     SetEnvironmentVariable("AP_PARENT_PID",ap_psprintf(p,"%d",parent_pid));
1438
1439     /* Give the read end of the pipe (hPipeRead) to the child as stdin. The 
1440      * parent will write the socket data to the child on this pipe.
1441      */
1442     memset(&si, 0, sizeof(si));
1443     memset(&pi, 0, sizeof(pi));
1444     si.cb = sizeof(si);
1445     si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
1446     si.wShowWindow = SW_HIDE;
1447     si.hStdInput   = hPipeRead;
1448
1449     if (!CreateProcess(NULL, pCommand, NULL, NULL, 
1450                        TRUE,               /* Inherit handles */
1451                        CREATE_SUSPENDED,   /* Creation flags */
1452                        NULL,               /* Environment block */
1453                        NULL,
1454                        &si, &pi)) {
1455         ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
1456                      "Parent: Not able to create the child process.");
1457         /*
1458          * We must close the handles to the new process and its main thread
1459          * to prevent handle and memory leaks.
1460          */ 
1461         CloseHandle(pi.hProcess);
1462         CloseHandle(pi.hThread);
1463         return -1;
1464     }
1465     else {
1466         HANDLE kill_event;
1467         LPWSAPROTOCOL_INFO  lpWSAProtocolInfo;
1468         HANDLE hDupedCompPort;
1469
1470         ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
1471                      "Parent: Created child process %d", pi.dwProcessId);
1472
1473         SetEnvironmentVariable("AP_PARENT_PID",NULL);
1474
1475         /* Create the exit_event, apCchild_pid */
1476         sa.nLength = sizeof(sa);
1477         sa.bInheritHandle = TRUE;
1478         sa.lpSecurityDescriptor = NULL;        
1479         kill_event = CreateEvent(&sa, TRUE, FALSE, ap_psprintf(pconf,"apC%d", pi.dwProcessId));
1480         if (!kill_event) {
1481             ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
1482                          "Parent: Could not create exit event for child process");
1483             CloseHandle(pi.hProcess);
1484             CloseHandle(pi.hThread);
1485             return -1;
1486         }
1487         
1488         /* Assume the child process lives. Update the process and event tables */
1489         handles[*processes] = pi.hProcess;
1490         events[*processes] = kill_event;
1491         (*processes)++;
1492
1493         /* We never store the thread's handle, so close it now. */
1494         ResumeThread(pi.hThread);
1495         CloseHandle(pi.hThread);
1496
1497         /* Run the chain of open sockets. For each socket, duplicate it 
1498          * for the target process then send the WSAPROTOCOL_INFO 
1499          * (returned by dup socket) to the child */
1500         for (lr = ap_listeners; lr; lr = lr->next) {
1501             int nsd;
1502             lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
1503             ap_get_os_sock(&nsd,lr->sd);
1504             ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
1505                          "Parent: Duplicating socket %d and sending it to child process %d", nsd, pi.dwProcessId);
1506             if (WSADuplicateSocket(nsd, pi.dwProcessId,
1507                                    lpWSAProtocolInfo) == SOCKET_ERROR) {
1508                 ap_log_error(APLOG_MARK, APLOG_CRIT, h_errno, server_conf,
1509                              "Parent: WSADuplicateSocket failed for socket %d.", lr->sd );
1510                 return -1;
1511             }
1512
1513             if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO),
1514                            &BytesWritten,
1515                            (LPOVERLAPPED) NULL)) {
1516                 ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
1517                              "Parent: Unable to write duplicated socket %d to the child.", lr->sd );
1518                 return -1;
1519             }
1520             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, server_conf,
1521                          "Parent: BytesWritten = %d WSAProtocolInfo = %x20", BytesWritten, *lpWSAProtocolInfo);
1522         }
1523         if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
1524             /* Now, send the AcceptEx completion port to the child */
1525             if (!DuplicateHandle(GetCurrentProcess(), AcceptExCompPort, 
1526                                  pi.hProcess, &hDupedCompPort,  0,
1527                                  TRUE, DUPLICATE_SAME_ACCESS)) {
1528                 ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
1529                              "Parent: Unable to duplicate AcceptEx completion port. Shutting down.");
1530                 return -1;
1531             }
1532
1533             WriteFile(hPipeWrite, &hDupedCompPort, (DWORD) sizeof(hDupedCompPort), &BytesWritten, (LPOVERLAPPED) NULL);
1534         }
1535     }
1536
1537     CloseHandle(hPipeRead);
1538     CloseHandle(hPipeWrite);        
1539
1540     return 0;
1541 }
1542
1543 static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_event)
1544 {
1545     int remaining_children_to_start = ap_daemons_to_start;
1546     int i;
1547     int rv, cld;
1548     int child_num = 0;
1549     int restart_pending = 0;
1550     int shutdown_pending = 0;
1551     int current_live_processes = 0; /* number of child process we know about */
1552
1553     HANDLE process_handles[MAX_PROCESSES];
1554     HANDLE process_kill_events[MAX_PROCESSES];
1555
1556     setup_listeners(s);
1557     bind_listeners_to_completion_port();
1558
1559     /* Create child process 
1560      * Should only be one in this version of Apache for WIN32 
1561      */
1562     service_set_status(SERVICE_START_PENDING);
1563     while (remaining_children_to_start--) {
1564         if (create_process(pconf, process_handles, process_kill_events, 
1565                            &current_live_processes) < 0) {
1566             ap_log_error(APLOG_MARK, APLOG_CRIT, GetLastError(), server_conf,
1567                          "master_main: create child process failed. Exiting.");
1568             shutdown_pending = 1;
1569             goto die_now;
1570         }
1571     }
1572     service_set_status(SERVICE_RUNNING);
1573
1574     restart_pending = shutdown_pending = 0;
1575     
1576     /* Wait for shutdown or restart events or for child death */
1577     process_handles[current_live_processes] = shutdown_event;
1578     process_handles[current_live_processes+1] = restart_event;
1579
1580     rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE *)process_handles, 
1581                                 FALSE, INFINITE);
1582     cld = rv - WAIT_OBJECT_0;
1583     if (rv == WAIT_FAILED) {
1584         /* Something serious is wrong */
1585         ap_log_error(APLOG_MARK,APLOG_CRIT, GetLastError(), server_conf,
1586                      "master_main: WaitForMultipeObjects WAIT_FAILED -- doing server shutdown");
1587         shutdown_pending = 1;
1588     }
1589     else if (rv == WAIT_TIMEOUT) {
1590         /* Hey, this cannot happen */
1591         ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), s,
1592                      "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT");
1593         shutdown_pending = 1;
1594     }
1595     else if (cld == current_live_processes) {
1596         /* shutdown_event signalled */
1597         shutdown_pending = 1;
1598         printf("shutdown event signaled\n");
1599         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, s, 
1600                      "master_main: Shutdown event signaled -- doing server shutdown.");
1601         if (ResetEvent(shutdown_event) == 0) {
1602             ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), s,
1603                          "ResetEvent(shutdown_event)");
1604         }
1605
1606     }
1607     else if (cld == current_live_processes+1) {
1608         /* restart_event signalled */
1609         int children_to_kill = current_live_processes;
1610         restart_pending = 1;
1611         ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, s, 
1612                      "master_main: Restart event signaled. Doing a graceful restart.");
1613         if (ResetEvent(restart_event) == 0) {
1614             ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), s,
1615                          "master_main: ResetEvent(restart_event) failed.");
1616         }
1617         /* Signal each child process to die 
1618          * We are making a big assumption here that the child process, once signaled,
1619          * will REALLY go away. Since this is a restart, we do not want to hold the 
1620          * new child process up waiting for the old child to die. Remove the old 
1621          * child out of the process_handles ap_table_t and hope for the best...
1622          */
1623         for (i = 0; i < children_to_kill; i++) {
1624             if (SetEvent(process_kill_events[i]) == 0)
1625                 ap_log_error(APLOG_MARK, APLOG_ERR, GetLastError(), s,
1626                              "master_main: SetEvent for child process in slot #%d failed", i);
1627             cleanup_process(process_handles, process_kill_events, i, &current_live_processes);
1628         }
1629     } 
1630     else {
1631         /* A child process must have exited because of a fatal error condition (seg fault, etc.). 
1632          * Remove the dead process 
1633          * from the process_handles and process_kill_events ap_table_t and create a new
1634          * child process.
1635          * TODO: Consider restarting the child immediately without looping through http_main
1636          * and without rereading the configuration. Will need this if we ever support multiple 
1637          * children. One option, create a parent thread which waits on child death and restarts it.
1638          */
1639         restart_pending = 1;
1640         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, server_conf, 
1641                      "master_main: Child process failed. Restarting the child process.");
1642         ap_assert(cld < current_live_processes);
1643         cleanup_process(process_handles, process_kill_events, cld, &current_live_processes);
1644         /* APD2("main_process: child in slot %d died", rv); */
1645         /* restart_child(process_hancles, process_kill_events, cld, &current_live_processes); */
1646
1647         /* Drain the AcceptEx completion port of any outstanding I/O pending for the dead 
1648          * process. */
1649         drain_acceptex_complport(AcceptExCompPort, FALSE);
1650     }
1651
1652 die_now:
1653     if (shutdown_pending) {
1654         int tmstart = time(NULL);
1655         /* Signal each child processes to die */
1656         for (i = 0; i < current_live_processes; i++) {
1657             printf("SetEvent handle = %d\n", process_kill_events[i]);
1658             if (SetEvent(process_kill_events[i]) == 0)
1659                 ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
1660                              "master_main: SetEvent for child process in slot #%d failed", i);
1661         }
1662
1663         while (current_live_processes && ((tmstart+60) > time(NULL))) {
1664             rv = WaitForMultipleObjects(current_live_processes, (HANDLE *)process_handles, FALSE, 2000);
1665             if (rv == WAIT_TIMEOUT)
1666                 continue;
1667             ap_assert(rv != WAIT_FAILED);
1668             cld = rv - WAIT_OBJECT_0;
1669             ap_assert(rv < current_live_processes);
1670             cleanup_process(process_handles, process_kill_events, cld, &current_live_processes);
1671         }
1672         for (i = 0; i < current_live_processes; i++) {
1673             ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO, APR_SUCCESS, server_conf,
1674                          "forcing termination of child #%d (handle %d)", i, process_handles[i]);
1675             TerminateProcess((HANDLE) process_handles[i], 1);
1676         }
1677         return 0;  /* Tell the caller we do not want to restart */
1678     }
1679
1680     return 1;      /* Tell the caller we want a restart */
1681 }
1682
1683 /* 
1684  * winnt_pre_config() hook
1685  */
1686 static void winnt_pre_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp) 
1687 {
1688     char *pid;
1689
1690     one_process = !!getenv("ONE_PROCESS");
1691
1692     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1693     GetVersionEx(&osver);
1694
1695     /* AP_PARENT_PID is only valid in the child */
1696     pid = getenv("AP_PARENT_PID");
1697     if (pid) {
1698         /* This is the child */
1699         parent_pid = atoi(pid);
1700         my_pid = getpid();
1701     }
1702     else {
1703         /* This is the parent */
1704         parent_pid = my_pid = getpid();
1705     }
1706
1707     ap_listen_pre_config();
1708     ap_daemons_to_start = DEFAULT_NUM_DAEMON;
1709     ap_threads_per_child = DEFAULT_START_THREAD;
1710     ap_pid_fname = DEFAULT_PIDLOG;
1711     max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
1712
1713     ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
1714
1715 }
1716
1717 static void winnt_post_config(ap_pool_t *pconf, ap_pool_t *plog, ap_pool_t *ptemp, server_rec* server_conf)
1718 {
1719     static int restart_num = 0;
1720     server_conf = server_conf;
1721
1722     if (parent_pid == my_pid) {
1723         if (restart_num++ == 1) {
1724             /* This code should be run once in the parent and not run
1725              * accross a restart
1726              */
1727             PSECURITY_ATTRIBUTES sa = GetNullACL();  /* returns NULL if invalid (Win95?) */
1728             setup_signal_names(ap_psprintf(pconf,"ap%d", parent_pid));
1729             if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
1730                 /* Create the AcceptEx IoCompletionPort once in the parent.
1731                  * The completion port persists across restarts. 
1732                  */
1733                 AcceptExCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
1734                                                           NULL,
1735                                                           0,
1736                                                           0); /* CONCURRENT ACTIVE THREADS */
1737                 if (AcceptExCompPort == NULL) {
1738                     ap_log_error(APLOG_MARK,APLOG_ERR, GetLastError(), server_conf,
1739                                  "Parent: Unable to create the AcceptExCompletionPort -- process will exit");
1740                     exit(1);
1741                 }
1742             }
1743
1744             ap_log_pid(pconf, ap_pid_fname);
1745             service_set_status(SERVICE_START_PENDING);
1746
1747             AMCSocketInitialize();
1748         
1749             /* Create shutdown event, apPID_shutdown, where PID is the parent 
1750              * Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
1751              */
1752             shutdown_event = CreateEvent(sa, FALSE, FALSE, signal_shutdown_name);
1753             if (!shutdown_event) {
1754                 ap_log_error(APLOG_MARK, APLOG_EMERG, GetLastError(), server_conf,
1755                              "Parent: Cannot create shutdown event %s", signal_shutdown_name);
1756                 CleanNullACL((void *)sa);
1757                 exit(1);
1758             }
1759
1760             /* Create restart event, apPID_restart, where PID is the parent 
1761              * Apache process ID. Restart is signaled by 'apache -k restart'.
1762              */
1763             restart_event = CreateEvent(sa, FALSE, FALSE, signal_restart_name);
1764             if (!restart_event) {
1765                 CloseHandle(shutdown_event);
1766                 ap_log_error(APLOG_MARK, APLOG_EMERG, GetLastError(), server_conf,
1767                              "Parent: Cannot create restart event %s", signal_restart_name);
1768                 CleanNullACL((void *)sa);
1769                 exit(1);
1770             }
1771             CleanNullACL((void *)sa);
1772             
1773             /* Create the start mutex, apPID, where PID is the parent Apache process ID.
1774              * Ths start mutex is used during a restart to prevent more than one 
1775              * child process from entering the accept loop at once.
1776              */
1777             ap_create_lock(&start_mutex,APR_MUTEX, APR_CROSS_PROCESS, signal_name_prefix,
1778                                server_conf->process->pool);
1779         }
1780     }
1781 }
1782
1783 API_EXPORT(int) ap_mpm_run(ap_pool_t *_pconf, ap_pool_t *plog, server_rec *s )
1784 {
1785     static int restart = 0;            /* Default is "not a restart" */
1786
1787     pconf = _pconf;
1788     server_conf = s;
1789
1790     if ((parent_pid != my_pid) || one_process) {
1791         /* Running as Child process or in one_process (debug) mode */
1792         ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
1793                      "Child %d: Child process is running", my_pid);
1794         AMCSocketInitialize();
1795         child_main();
1796         AMCSocketCleanup();
1797         ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, server_conf,
1798                      "Child %d: Child process is exiting", my_pid);        
1799
1800         return 1;
1801     }  /* Child or single process */
1802     else { /* Parent process */
1803         restart = master_main(server_conf, shutdown_event, restart_event);
1804
1805         if (!restart) {
1806             /* Shutting down. Clean up... */
1807             const char *pidfile = ap_server_root_relative (pconf, ap_pid_fname);
1808
1809             if (pidfile != NULL && unlink(pidfile) == 0) {
1810                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,APR_SUCCESS,
1811                              server_conf, "removed PID file %s (pid=%ld)",
1812                              pidfile, (long)getpid());
1813             }
1814             ap_destroy_lock(start_mutex);
1815
1816             CloseHandle(restart_event);
1817             CloseHandle(shutdown_event);
1818             AMCSocketCleanup();
1819
1820             service_set_status(SERVICE_STOPPED);
1821
1822             return 1;
1823         }
1824     }  /* Parent process */
1825
1826     return 0; /* Restart */
1827 }
1828
1829 static void winnt_hooks(void)
1830 {
1831     one_process = 0;
1832     ap_hook_post_config(winnt_post_config, NULL, NULL, 0);
1833 }
1834
1835 /* 
1836  * Command processors 
1837  */
1838 static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg) 
1839 {
1840     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1841     if (err != NULL) {
1842         return err;
1843     }
1844
1845     if (cmd->server->is_virtual) {
1846         return "PidFile directive not allowed in <VirtualHost>";
1847     }
1848     ap_pid_fname = arg;
1849     return NULL;
1850 }
1851
1852 static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg) 
1853 {
1854     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1855     if (err != NULL) {
1856         return err;
1857     }
1858
1859     ap_threads_per_child = atoi(arg);
1860     if (ap_threads_per_child > HARD_THREAD_LIMIT) {
1861         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1862                      "WARNING: ThreadsPerChild of %d exceeds compile time"
1863                      " limit of %d threads,", ap_threads_per_child, 
1864                      HARD_THREAD_LIMIT);
1865         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
1866                      " lowering ThreadsPerChild to %d. To increase, please"
1867                      " see the  HARD_THREAD_LIMIT define in src/include/httpd.h.", 
1868                      HARD_THREAD_LIMIT);
1869     }
1870     else if (ap_threads_per_child < 1) {
1871         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, 
1872                      "WARNING: Require ThreadsPerChild > 0, setting to 1");
1873         ap_threads_per_child = 1;
1874     }
1875     return NULL;
1876 }
1877
1878
1879 static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) 
1880 {
1881     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1882     if (err != NULL) {
1883         return err;
1884     }
1885
1886     max_requests_per_child = atoi(arg);
1887
1888     return NULL;
1889 }
1890
1891 static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg) 
1892 {
1893     struct stat finfo;
1894     const char *fname;
1895     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1896     if (err != NULL) {
1897         return err;
1898     }
1899
1900     fname = ap_server_root_relative(cmd->pool, arg);
1901     if ((stat(fname, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) {
1902         return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, 
1903                           " does not exist or is not a directory", NULL);
1904     }
1905     ap_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
1906     return NULL;
1907 }
1908
1909 /* Stub functions until this MPM supports the connection status API */
1910
1911 API_EXPORT(void) ap_update_connection_status(long conn_id, const char *key, \
1912                                              const char *value)
1913 {
1914     /* NOP */
1915 }
1916
1917 API_EXPORT(void) ap_reset_connection_status(long conn_id)
1918 {
1919     /* NOP */
1920 }
1921
1922 API_EXPORT(ap_array_header_t *) ap_get_status_table(ap_pool_t *p)
1923 {
1924     /* NOP */
1925     return NULL;
1926 }
1927
1928 static const command_rec winnt_cmds[] = {
1929 LISTEN_COMMANDS
1930 { "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1,
1931     "A file for logging the server process ID"},
1932 { "ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF, TAKE1,
1933   "Number of threads each child creates" },
1934 { "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1,
1935   "Maximum number of requests a particular child serves before dying." },
1936 { "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1,
1937   "The location of the directory Apache changes to before dumping core" },
1938 { NULL }
1939 };
1940
1941 module MODULE_VAR_EXPORT mpm_winnt_module = {
1942     MPM20_MODULE_STUFF,
1943     winnt_pre_config,           /* hook run before configuration is read */
1944     NULL,                       /* create per-directory config structure */
1945     NULL,                       /* merge per-directory config structures */
1946     NULL,                       /* create per-server config structure */
1947     NULL,                       /* merge per-server config structures */
1948     winnt_cmds,                 /* command ap_table_t */
1949     NULL,                       /* handlers */
1950     winnt_hooks                 /* register_hooks */
1951 };