]> granicus.if.org Git - apache/blob - server/mpm/winnt/mpm_winnt.c
A bit more ap_regkey and apr-ization. Fixes some new bugs where the
[apache] / server / mpm / winnt / mpm_winnt.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2002 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 "httpd.h" 
61 #include "http_main.h" 
62 #include "http_log.h" 
63 #include "http_config.h"        /* for read_config */ 
64 #include "http_core.h"          /* for get_remote_host */ 
65 #include "http_connection.h"
66 #include "apr_portable.h"
67 #include "apr_thread_proc.h"
68 #include "apr_getopt.h"
69 #include "apr_strings.h"
70 #include "apr_lib.h"
71 #include "apr_shm.h"
72 #include "apr_thread_mutex.h"
73 #include "ap_mpm.h"
74 #include "ap_config.h"
75 #include "ap_listen.h"
76 #include "mpm_default.h"
77 #include "mpm_winnt.h"
78 #include "mpm_common.h"
79 #include <malloc.h>
80 #include "apr_atomic.h"
81
82 /* Limit on the threads per process.  Clients will be locked out if more than
83  * this  * HARD_SERVER_LIMIT are needed.
84  *
85  * We keep this for one reason it keeps the size of the scoreboard file small
86  * enough that we can read the whole thing without worrying too much about
87  * the overhead.
88  */
89 #ifndef HARD_THREAD_LIMIT
90 #define HARD_THREAD_LIMIT 1920
91 #endif
92
93 /* Limit on the total --- clients will be locked out if more servers than
94  * this are needed.  It is intended solely to keep the server from crashing
95  * when things get out of hand.
96  *
97  * We keep a hard maximum number of servers, for two reasons --- first off,
98  * in case something goes seriously wrong, we want to stop the fork bomb
99  * short of actually crashing the machine we're running on by filling some
100  * kernel table.  Secondly, it keeps the size of the scoreboard file small
101  * enough that we can read the whole thing without worrying too much about
102  * the overhead.
103  */
104 #define HARD_SERVER_LIMIT 1
105
106 /* scoreboard.c does the heavy lifting; all we do is create the child
107  * score by moving a handle down the pipe into the child's stdin.
108  */
109 extern apr_shm_t *ap_scoreboard_shm;
110 server_rec *ap_server_conf;
111 typedef HANDLE thread;
112
113 /* Definitions of WINNT MPM specific config globals */
114 apr_pool_t *pconf;
115 static apr_pool_t *pchild = NULL;
116 static int workers_may_exit = 0;
117 static int shutdown_in_progress = 0;
118 static unsigned int g_blocked_threads = 0;
119
120 static HANDLE shutdown_event;   /* used to signal the parent to shutdown */
121 static HANDLE restart_event;    /* used to signal the parent to restart */
122 static HANDLE exit_event;       /* used by parent to signal the child to exit */
123 static HANDLE max_requests_per_child_event;
124
125 static char ap_coredump_dir[MAX_STRING_LEN];
126
127 static int one_process = 0;
128 static char const* signal_arg = NULL;
129
130 OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
131
132 apr_proc_mutex_t *start_mutex;
133 static DWORD my_pid;
134 static DWORD parent_pid;
135
136 int ap_threads_per_child = 0;
137
138 /* ap_my_generation are used by the scoreboard code */
139 ap_generation_t volatile ap_my_generation=0;
140
141 /* Queue for managing the passing of COMP_CONTEXTs between
142  * the accept and worker threads.
143  */
144 static apr_thread_mutex_t  *qlock;
145 static PCOMP_CONTEXT qhead = NULL;
146 static PCOMP_CONTEXT qtail = NULL;
147 static int num_completion_contexts = 0;
148 static HANDLE ThreadDispatchIOCP = NULL;
149
150 /* Stub functions until this MPM supports the connection status API */
151
152 AP_DECLARE(void) ap_update_connection_status(long conn_id, const char *key, \
153                                              const char *value)
154 {
155     /* NOP */
156 }
157
158 AP_DECLARE(void) ap_reset_connection_status(long conn_id)
159 {
160     /* NOP */
161 }
162
163 AP_DECLARE(apr_array_header_t *) ap_get_status_table(apr_pool_t *p)
164 {
165     /* NOP */
166     return NULL;
167 }
168
169 /* 
170  * Command processors 
171  */
172
173 static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg) 
174 {
175     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
176     if (err != NULL) {
177         return err;
178     }
179
180     ap_threads_per_child = atoi(arg);
181     if (ap_threads_per_child > HARD_THREAD_LIMIT) {
182         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
183                      "WARNING: ThreadsPerChild of %d exceeds compile time"
184                      " limit of %d threads,", ap_threads_per_child, 
185                      HARD_THREAD_LIMIT);
186         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
187                      " lowering ThreadsPerChild to %d. To increase, please"
188                      " see the  HARD_THREAD_LIMIT define in %s.", 
189                      HARD_THREAD_LIMIT, AP_MPM_HARD_LIMITS_FILE);
190         ap_threads_per_child = HARD_THREAD_LIMIT;
191     }
192     else if (ap_threads_per_child < 1) {
193         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
194                      "WARNING: Require ThreadsPerChild > 0, setting to 1");
195         ap_threads_per_child = 1;
196     }
197     return NULL;
198 }
199
200 static const command_rec winnt_cmds[] = {
201 LISTEN_COMMANDS,
202 { "ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF, TAKE1,
203   "Number of threads each child creates" },
204 { NULL }
205 };
206
207
208 AP_DECLARE(void) mpm_recycle_completion_context(PCOMP_CONTEXT context)
209 {
210     /* Recycle the completion context.
211      * - clear the ptrans pool
212      * - put the context on the queue to be consumed by the accept thread
213      * Note: 
214      * context->accept_socket may be in a disconnected but reusable 
215      * state so -don't- close it.
216      */
217     if (context) {
218         apr_pool_clear(context->ptrans);
219         context->next = NULL;
220         ResetEvent(context->Overlapped.hEvent);
221         apr_thread_mutex_lock(qlock);
222         if (qtail)
223             qtail->next = context;
224         else
225             qhead = context;
226         qtail = context;
227         apr_thread_mutex_unlock(qlock);
228     }
229 }
230
231 AP_DECLARE(PCOMP_CONTEXT) mpm_get_completion_context(void)
232 {
233     apr_status_t rv;
234     PCOMP_CONTEXT context = NULL;
235
236     /* Grab a context off the queue */
237     apr_thread_mutex_lock(qlock);
238     if (qhead) {
239         context = qhead;
240         qhead = qhead->next;
241         if (!qhead)
242             qtail = NULL;
243     }
244     apr_thread_mutex_unlock(qlock);
245
246     /* If we failed to grab a context off the queue, alloc one out of 
247      * the child pool. There may be up to ap_threads_per_child contexts
248      * in the system at once.
249      */
250     if (!context) {
251         if (num_completion_contexts >= ap_threads_per_child) {
252             static int reported = 0;
253             if (!reported) {
254                 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
255                              "Server ran out of threads to serve requests. Consider "
256                              "raising the ThreadsPerChild setting");
257                 reported = 1;
258             }
259             return NULL;
260         }
261         /* Note:
262          * Multiple failures in the next two steps will cause the pchild pool
263          * to 'leak' storage. I don't think this is worth fixing...
264          */
265         context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
266
267         context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
268         if (context->Overlapped.hEvent == NULL) {
269             /* Hopefully this is a temporary condition ... */
270             ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf,
271                          "mpm_get_completion_context: CreateEvent failed.");
272             return NULL;
273         }
274
275         /* Create the tranaction pool */
276         if ((rv = apr_pool_create(&context->ptrans, pchild)) != APR_SUCCESS) {
277             ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
278                          "mpm_get_completion_context: Failed to create the transaction pool.");
279             CloseHandle(context->Overlapped.hEvent);
280             return NULL;
281         }
282         apr_pool_tag(context->ptrans, "ptrans");
283
284         context->accept_socket = INVALID_SOCKET;
285         context->ba = apr_bucket_alloc_create(pchild);
286         apr_atomic_inc(&num_completion_contexts);
287     }
288
289     return context;
290 }
291
292 AP_DECLARE(apr_status_t) mpm_post_completion_context(PCOMP_CONTEXT context, 
293                                                      io_state_e state)
294 {
295     LPOVERLAPPED pOverlapped;
296     if (context)
297         pOverlapped = &context->Overlapped;
298     else
299         pOverlapped = NULL;
300
301     PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, state, pOverlapped);
302     return APR_SUCCESS;
303 }
304
305 /* This is the helper code to resolve late bound entry points 
306  * missing from one or more releases of the Win32 API...
307  * but it sure would be nice if we didn't duplicate this code
308  * from the APR ;-)
309  */
310 static const char* const lateDllName[DLL_defined] = {
311     "kernel32", "advapi32", "mswsock",  "ws2_32"  };
312 static HMODULE lateDllHandle[DLL_defined] = {
313     NULL,       NULL,       NULL,       NULL      };
314
315 FARPROC ap_load_dll_func(ap_dlltoken_e fnLib, char* fnName, int ordinal)
316 {
317     if (!lateDllHandle[fnLib]) { 
318         lateDllHandle[fnLib] = LoadLibrary(lateDllName[fnLib]);
319         if (!lateDllHandle[fnLib])
320             return NULL;
321     }
322     if (ordinal)
323         return GetProcAddress(lateDllHandle[fnLib], (char *) ordinal);
324     else
325         return GetProcAddress(lateDllHandle[fnLib], fnName);
326 }
327
328 /* To share the semaphores with other processes, we need a NULL ACL
329  * Code from MS KB Q106387
330  */
331 static PSECURITY_ATTRIBUTES GetNullACL()
332 {
333     PSECURITY_DESCRIPTOR pSD;
334     PSECURITY_ATTRIBUTES sa;
335
336     sa  = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
337     sa->nLength = sizeof(sizeof(SECURITY_ATTRIBUTES));
338
339     pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
340     sa->lpSecurityDescriptor = pSD;
341
342     if (pSD == NULL || sa == NULL) {
343         return NULL;
344     }
345     apr_set_os_error(0);
346     if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)
347         || apr_get_os_error()) {
348         LocalFree( pSD );
349         LocalFree( sa );
350         return NULL;
351     }
352     if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)
353         || apr_get_os_error()) {
354         LocalFree( pSD );
355         LocalFree( sa );
356         return NULL;
357     }
358
359     sa->bInheritHandle = TRUE;
360     return sa;
361 }
362
363 static void CleanNullACL( void *sa ) {
364     if( sa ) {
365         LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor);
366         LocalFree( sa );
367     }
368 }
369
370 /*
371  * The Win32 call WaitForMultipleObjects will only allow you to wait for 
372  * a maximum of MAXIMUM_WAIT_OBJECTS (current 64).  Since the threading 
373  * model in the multithreaded version of apache wants to use this call, 
374  * we are restricted to a maximum of 64 threads.  This is a simplistic 
375  * routine that will increase this size.
376  */
377 static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, 
378                                    DWORD dwSeconds)
379 {
380     time_t tStopTime;
381     DWORD dwRet = WAIT_TIMEOUT;
382     DWORD dwIndex=0;
383     BOOL bFirst = TRUE;
384   
385     tStopTime = time(NULL) + dwSeconds;
386   
387     do {
388         if (!bFirst)
389             Sleep(1000);
390         else
391             bFirst = FALSE;
392           
393         for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) {
394             dwRet = WaitForMultipleObjects( 
395                 min(MAXIMUM_WAIT_OBJECTS, nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)),
396                 lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), 
397                 0, 0);
398                                            
399             if (dwRet != WAIT_TIMEOUT) {                                          
400               break;
401             }
402         }
403     } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT));
404     
405     return dwRet;
406 }
407
408 /*
409  * Signalling Apache on NT.
410  *
411  * Under Unix, Apache can be told to shutdown or restart by sending various
412  * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so
413  * we use "events" instead. The parent apache process goes into a loop
414  * where it waits forever for a set of events. Two of those events are
415  * called
416  *
417  *    apPID_shutdown
418  *    apPID_restart
419  *
420  * (where PID is the PID of the apache parent process). When one of these
421  * is signalled, the Apache parent performs the appropriate action. The events
422  * can become signalled through internal Apache methods (e.g. if the child
423  * finds a fatal error and needs to kill its parent), via the service
424  * control manager (the control thread will signal the shutdown event when
425  * requested to stop the Apache service), from the -k Apache command line,
426  * or from any external program which finds the Apache PID from the
427  * httpd.pid file.
428  *
429  * The signal_parent() function, below, is used to signal one of these events.
430  * It can be called by any child or parent process, since it does not
431  * rely on global variables.
432  *
433  * On entry, type gives the event to signal. 0 means shutdown, 1 means 
434  * graceful restart.
435  */
436 /*
437  * Initialise the signal names, in the global variables signal_name_prefix, 
438  * signal_restart_name and signal_shutdown_name.
439  */
440 #define MAX_SIGNAL_NAME 30  /* Long enough for apPID_shutdown, where PID is an int */
441 char signal_name_prefix[MAX_SIGNAL_NAME];
442 char signal_restart_name[MAX_SIGNAL_NAME]; 
443 char signal_shutdown_name[MAX_SIGNAL_NAME];
444 void setup_signal_names(char *prefix)
445 {
446     apr_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);    
447     apr_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name), 
448         "%s_shutdown", signal_name_prefix);    
449     apr_snprintf(signal_restart_name, sizeof(signal_restart_name), 
450         "%s_restart", signal_name_prefix);    
451 }
452
453 static int volatile is_graceful = 0;
454 AP_DECLARE(int) ap_graceful_stop_signalled(void)
455 {
456     return is_graceful;
457 }
458
459 AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type)
460 {
461     HANDLE e;
462     char *signal_name;
463     
464     if (parent_pid == my_pid) {
465         switch(type) {
466            case SIGNAL_PARENT_SHUTDOWN: 
467            {
468                SetEvent(shutdown_event); 
469                break;
470            }
471            /* This MPM supports only graceful restarts right now */
472            case SIGNAL_PARENT_RESTART: 
473            case SIGNAL_PARENT_RESTART_GRACEFUL:
474            {
475                is_graceful = 1;
476                SetEvent(restart_event); 
477                break;
478            }
479         }
480         return;
481     }
482
483     switch(type) {
484        case SIGNAL_PARENT_SHUTDOWN: 
485        {
486            signal_name = signal_shutdown_name; 
487            break;
488        }
489        /* This MPM supports only graceful restarts right now */
490        case SIGNAL_PARENT_RESTART: 
491        case SIGNAL_PARENT_RESTART_GRACEFUL:
492        {
493            signal_name = signal_restart_name;     
494            is_graceful = 1;
495            break;
496        }
497        default: 
498            return;
499     }
500
501     e = OpenEvent(EVENT_MODIFY_STATE, FALSE, signal_name);
502     if (!e) {
503         /* Um, problem, can't signal the parent, which means we can't
504          * signal ourselves to die. Ignore for now...
505          */
506         ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
507                      "OpenEvent on %s event", signal_name);
508         return;
509     }
510     if (SetEvent(e) == 0) {
511         /* Same problem as above */
512         ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
513                      "SetEvent on %s event", signal_name);
514         CloseHandle(e);
515         return;
516     }
517     CloseHandle(e);
518 }
519
520 /* set_listeners_noninheritable()
521  * Make the listening socket handles noninheritable by processes
522  * started out of this process.
523  */
524 static int set_listeners_noninheritable(apr_pool_t *p) 
525 {
526     ap_listen_rec *lr;
527     HANDLE dup;
528     SOCKET nsd;
529     HANDLE hProcess = GetCurrentProcess();
530
531     for (lr = ap_listeners; lr; lr = lr->next) {
532         apr_os_sock_get(&nsd,lr->sd);
533         if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
534             if (!DuplicateHandle(hProcess, (HANDLE) nsd, hProcess, &dup, 
535                                  0, FALSE, DUPLICATE_SAME_ACCESS)) {
536                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf,
537                              "set_listeners_noninheritable: DuplicateHandle failed.");
538             }
539             else {
540                 closesocket(nsd);
541                 nsd = (SOCKET) dup;
542                 apr_os_sock_put(&lr->sd, &nsd, p);
543             }
544         }
545         else {
546             /* A different approach.  Many users report errors such as 
547              * (32538)An operation was attempted on something that is not 
548              * a socket.  : Parent: WSADuplicateSocket failed...
549              *
550              * This appears that the duplicated handle is no longer recognized
551              * as a socket handle.  SetHandleInformation should overcome that
552              * problem by not altering the handle identifier.  But this won't
553              * work on 9x - it's unsupported.
554              */
555             if (!SetHandleInformation((HANDLE)nsd, HANDLE_FLAG_INHERIT, 0)) {
556                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf,
557                              "set_listeners_noninheritable: SetHandleInformation failed.");
558             }
559         }
560     }
561
562     if (my_pid == parent_pid) {
563         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
564                      "Parent: Marked listeners as not inheritable.");
565     } else {
566         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
567                      "Child %d: Marked listeners as not inheritable.", my_pid);
568     }
569     return 1;
570 }
571
572 /*
573  * find_ready_listener()
574  * Only used by Win9* and should go away when the win9*_accept() function is 
575  * reimplemented using apr_poll().
576  */
577 static ap_listen_rec *head_listener;
578 static APR_INLINE ap_listen_rec *find_ready_listener(fd_set * main_fds)
579 {
580     ap_listen_rec *lr;
581     SOCKET nsd;
582
583     for (lr = head_listener; lr ; lr = lr->next) {
584         apr_os_sock_get(&nsd, lr->sd);
585         if (FD_ISSET(nsd, main_fds)) {
586             head_listener = lr->next;
587             if (head_listener == NULL)
588                 head_listener = ap_listeners;
589
590             return (lr);
591         }
592     }
593     return NULL;
594 }
595
596 /*
597  *
598  */
599 void get_handles_from_parent(server_rec *s)
600 {
601     HANDLE pipe;
602     HANDLE hScore;
603     DWORD BytesRead;
604     void *sb_shared;
605     apr_status_t rv;
606     
607     pipe = GetStdHandle(STD_INPUT_HANDLE);
608     if (!ReadFile(pipe, &exit_event, sizeof(HANDLE),
609                   &BytesRead, (LPOVERLAPPED) NULL)
610         || (BytesRead != sizeof(HANDLE))) {
611         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
612                      "Child %d: Unable to retrieve the exit event from the parent", my_pid);
613         exit(APEXIT_CHILDINIT);
614     }
615
616     if (!ReadFile(pipe, &hScore, sizeof(hScore),
617                   &BytesRead, (LPOVERLAPPED) NULL)
618         || (BytesRead != sizeof(hScore))) {
619         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
620                      "Child %d: Unable to retrieve the scoreboard from the parent", my_pid);
621         exit(APEXIT_CHILDINIT);
622     }
623
624     if ((rv = apr_os_shm_put(&ap_scoreboard_shm, &hScore, s->process->pool)) 
625             != APR_SUCCESS) {
626         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
627                      "Child %d: Unable to access the scoreboard from the parent", my_pid);
628         exit(APEXIT_CHILDINIT);
629     }
630
631     rv = ap_reopen_scoreboard(s->process->pool, &ap_scoreboard_shm, 1);
632     if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) {
633         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, 
634                      "Child %d: Unable to reopen the scoreboard from the parent", my_pid);
635         exit(APEXIT_CHILDINIT);
636     }
637     /* We must 'initialize' the scoreboard to relink all the
638      * process-local pointer arrays into the shared memory block.
639      */
640     ap_init_scoreboard(sb_shared);
641
642     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
643                  "Child %d: Retrieved our scoreboard from the parent.", my_pid);
644 }
645
646 /* 
647  * get_listeners_from_parent()
648  * The listen sockets are opened in the parent. This function, which runs
649  * exclusively in the child process, receives them from the parent and
650  * makes them availeble in the child.
651  */
652 void get_listeners_from_parent(server_rec *s)
653 {
654     WSAPROTOCOL_INFO WSAProtocolInfo;
655     HANDLE pipe;
656     ap_listen_rec *lr;
657     DWORD BytesRead;
658     int lcnt = 0;
659     SOCKET nsd;
660
661     /* Set up a default listener if necessary */
662     if (ap_listeners == NULL) {
663         ap_listen_rec *lr;
664         lr = apr_palloc(s->process->pool, sizeof(ap_listen_rec));
665         lr->sd = NULL;
666         lr->next = ap_listeners;
667         ap_listeners = lr;
668     }
669
670     /* Open the pipe to the parent process to receive the inherited socket
671      * data. The sockets have been set to listening in the parent process.
672      */
673     pipe = GetStdHandle(STD_INPUT_HANDLE);
674
675     for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
676         if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), 
677                       &BytesRead, (LPOVERLAPPED) NULL)) {
678             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
679                          "setup_inherited_listeners: Unable to read socket data from parent");
680             exit(APEXIT_CHILDINIT);
681         }
682         nsd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
683                         &WSAProtocolInfo, 0, 0);
684         if (nsd == INVALID_SOCKET) {
685             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
686                          "Child %d: setup_inherited_listeners(), WSASocket failed to open the inherited socket.", my_pid);
687             exit(APEXIT_CHILDINIT);
688         }
689         apr_os_sock_put(&lr->sd, &nsd, s->process->pool);
690     }
691
692     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
693                  "Child %d: retrieved %d listeners from parent", my_pid, lcnt);
694
695     if (!set_listeners_noninheritable(s->process->pool)) {
696         exit(APEXIT_CHILDINIT);
697     }
698 }
699
700
701 /* Windows 9x specific code...
702  * Accept processing for on Windows 95/98 uses a producer/consumer queue 
703  * model. A single thread accepts connections and queues the accepted socket 
704  * to the accept queue for consumption by a pool of worker threads.
705  *
706  * win9x_accept()
707  *    The accept threads runs this function, which accepts connections off 
708  *    the network and calls add_job() to queue jobs to the accept_queue.
709  * add_job()/remove_job()
710  *    Add or remove an accepted socket from the list of sockets 
711  *    connected to clients. allowed_globals.jobmutex protects
712  *    against multiple concurrent access to the linked list of jobs.
713  * win9x_get_connection()
714  *    Calls remove_job() to pull a job from the accept queue. All the worker 
715  *    threads block on remove_job.
716  */
717
718 typedef struct joblist_s {
719     struct joblist_s *next;
720     int sock;
721 } joblist;
722
723 typedef struct globals_s {
724     HANDLE jobsemaphore;
725     joblist *jobhead;
726     joblist *jobtail;
727     apr_thread_mutex_t *jobmutex;
728     int jobcount;
729 } globals;
730
731 globals allowed_globals = {NULL, NULL, NULL, NULL, 0};
732 #define MAX_SELECT_ERRORS 100
733
734 static void add_job(int sock)
735 {
736     joblist *new_job;
737
738     new_job = (joblist *) malloc(sizeof(joblist));
739     if (new_job == NULL) {
740         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
741                      "Ouch!  Out of memory in add_job()!");
742         return;
743     }
744     new_job->next = NULL;
745     new_job->sock = sock;
746
747     apr_thread_mutex_lock(allowed_globals.jobmutex);
748
749     if (allowed_globals.jobtail != NULL)
750         allowed_globals.jobtail->next = new_job;
751     allowed_globals.jobtail = new_job;
752     if (!allowed_globals.jobhead)
753         allowed_globals.jobhead = new_job;
754     allowed_globals.jobcount++;
755     ReleaseSemaphore(allowed_globals.jobsemaphore, 1, NULL);
756
757     apr_thread_mutex_unlock(allowed_globals.jobmutex);
758 }
759
760 static int remove_job(void)
761 {
762     joblist *job;
763     int sock;
764
765     WaitForSingleObject(allowed_globals.jobsemaphore, INFINITE);
766     apr_thread_mutex_lock(allowed_globals.jobmutex);
767
768     if (shutdown_in_progress && !allowed_globals.jobhead) {
769         apr_thread_mutex_unlock(allowed_globals.jobmutex);
770         return (-1);
771     }
772     job = allowed_globals.jobhead;
773     ap_assert(job);
774     allowed_globals.jobhead = job->next;
775     if (allowed_globals.jobhead == NULL)
776         allowed_globals.jobtail = NULL;
777     apr_thread_mutex_unlock(allowed_globals.jobmutex);
778     sock = job->sock;
779     free(job);
780
781     return (sock);
782 }
783
784 static void win9x_accept(void * dummy)
785 {
786     struct timeval tv;
787     fd_set main_fds;
788     int wait_time = 1;
789     int csd;
790     SOCKET nsd = INVALID_SOCKET;
791     struct sockaddr_in sa_client;
792     int count_select_errors = 0;
793     int rc;
794     int clen;
795     ap_listen_rec *lr;
796     struct fd_set listenfds;
797     SOCKET listenmaxfd = INVALID_SOCKET;
798
799     /* Setup the listeners 
800      * ToDo: Use apr_poll()
801      */
802     FD_ZERO(&listenfds);
803     for (lr = ap_listeners; lr; lr = lr->next) {
804         if (lr->sd != NULL) {
805             apr_os_sock_get(&nsd, lr->sd);
806             FD_SET(nsd, &listenfds);
807             if (listenmaxfd == INVALID_SOCKET || nsd > listenmaxfd) {
808                 listenmaxfd = nsd;
809             }
810         }
811     }
812     head_listener = ap_listeners;
813
814     while (!shutdown_in_progress) {
815         tv.tv_sec = wait_time;
816         tv.tv_usec = 0;
817         memcpy(&main_fds, &listenfds, sizeof(fd_set));
818
819         rc = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
820
821         if (rc == 0 || (rc == SOCKET_ERROR && APR_STATUS_IS_EINTR(apr_get_netos_error()))) {
822             count_select_errors = 0;    /* reset count of errors */            
823             continue;
824         }
825         else if (rc == SOCKET_ERROR) {
826             /* A "real" error occurred, log it and increment the count of
827              * select errors. This count is used to ensure we don't go into
828              * a busy loop of continuous errors.
829              */
830             ap_log_error(APLOG_MARK, APLOG_INFO, apr_get_netos_error(), ap_server_conf, 
831                          "select failed with error %d", apr_get_netos_error());
832             count_select_errors++;
833             if (count_select_errors > MAX_SELECT_ERRORS) {
834                 shutdown_in_progress = 1;
835                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
836                              "Too many errors in select loop. Child process exiting.");
837                 break;
838             }
839         } else {
840             ap_listen_rec *lr;
841
842             lr = find_ready_listener(&main_fds);
843             if (lr != NULL) {
844                 /* fetch the native socket descriptor */
845                 apr_os_sock_get(&nsd, lr->sd);
846             }
847         }
848
849         do {
850             clen = sizeof(sa_client);
851             csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);
852             if (csd == INVALID_SOCKET) {
853                 csd = -1;
854             }
855         } while (csd < 0 && APR_STATUS_IS_EINTR(apr_get_netos_error()));
856
857         if (csd < 0) {
858             if (APR_STATUS_IS_ECONNABORTED(apr_get_netos_error())) {
859                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
860                             "accept: (client socket)");
861             }
862         }
863         else {
864             add_job(csd);
865         }
866     }
867     SetEvent(exit_event);
868 }
869
870 static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
871 {
872     apr_os_sock_info_t sockinfo;
873     int len;
874
875     if (context == NULL) {
876         /* allocate the completion context and the transaction pool */
877         context = apr_pcalloc(pconf, sizeof(COMP_CONTEXT));
878         apr_pool_create(&context->ptrans, pchild);
879         apr_pool_tag(context->ptrans, "ptrans");
880         context->ba = apr_bucket_alloc_create(pchild);
881     }
882     
883     while (1) {
884         apr_pool_clear(context->ptrans);        
885         context->accept_socket = remove_job();
886         if (context->accept_socket == -1) {
887             return NULL;
888         }
889         len = sizeof(struct sockaddr);
890         context->sa_server = apr_palloc(context->ptrans, len);
891         if (getsockname(context->accept_socket, 
892                         context->sa_server, &len)== SOCKET_ERROR) {
893             ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, 
894                          "getsockname failed");
895             continue;
896         }
897         len = sizeof(struct sockaddr);
898         context->sa_client = apr_palloc(context->ptrans, len);
899         if ((getpeername(context->accept_socket,
900                          context->sa_client, &len)) == SOCKET_ERROR) {
901             ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, 
902                          "getpeername failed");
903             memset(&context->sa_client, '\0', sizeof(context->sa_client));
904         }
905         sockinfo.os_sock = &context->accept_socket;
906         sockinfo.local   = context->sa_server;
907         sockinfo.remote  = context->sa_client;
908         sockinfo.family  = APR_INET;
909         sockinfo.type    = SOCK_STREAM;
910         apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
911
912         return context;
913     }
914 }
915 /* Windows NT/2000 specific code...
916  * Accept processing for on Windows NT uses a producer/consumer queue 
917  * model. An accept thread accepts connections off the network then issues
918  * PostQueuedCompletionStatus() to awake a thread blocked on the ThreadDispatch 
919  * IOCompletionPort.
920  *
921  * winnt_accept()
922  *    One or more accept threads run in this function, each of which accepts 
923  *    connections off the network and calls PostQueuedCompletionStatus() to
924  *    queue an io completion packet to the ThreadDispatch IOCompletionPort.
925  * winnt_get_connection()
926  *    Worker threads block on the ThreadDispatch IOCompletionPort awaiting 
927  *    connections to service.
928  */
929 static void winnt_accept(void *lr_) 
930 {
931     ap_listen_rec *lr = (ap_listen_rec *)lr_;
932     apr_os_sock_info_t sockinfo;
933     PCOMP_CONTEXT context = NULL;
934     DWORD BytesRead;
935     SOCKET nlsd;
936     int rv;
937
938     apr_os_sock_get(&nlsd, lr->sd);
939
940     while (!shutdown_in_progress) {
941         if (!context) {
942             context = mpm_get_completion_context();
943             if (!context) {
944                 /* Temporary resource constraint? */
945                 Sleep(0);
946                 continue;
947             }
948         }
949
950         /* Create and initialize the accept socket */
951         if (context->accept_socket == INVALID_SOCKET) {
952             context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
953             if (context->accept_socket == INVALID_SOCKET) {
954                 /* Another temporary condition? */
955                 ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
956                              "winnt_accept: Failed to allocate an accept socket. "
957                              "Temporary resource constraint? Try again.");
958                 Sleep(100);
959                 continue;
960             }
961         }
962
963         /* AcceptEx on the completion context. The completion context will be 
964          * signaled when a connection is accepted. 
965          */
966         if (!AcceptEx(nlsd, context->accept_socket,
967                       context->buff,
968                       0,
969                       PADDED_ADDR_SIZE, 
970                       PADDED_ADDR_SIZE,
971                       &BytesRead,
972                       &context->Overlapped)) {
973             rv = apr_get_netos_error();
974             if (rv == APR_FROM_OS_ERROR(WSAEINVAL)) {
975                 /* Hack alert. Occasionally, TransmitFile will not recycle the 
976                  * accept socket (usually when the client disconnects early). 
977                  * Get a new socket and try the call again.
978                  */
979                 closesocket(context->accept_socket);
980                 context->accept_socket = INVALID_SOCKET;
981                 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
982                        "winnt_accept: AcceptEx failed due to early client "
983                        "disconnect. Reallocate the accept socket and try again.");
984                 continue;
985             }
986             else if (rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
987                 ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
988                              "winnt_accept: AcceptEx failed. Attempting to recover.");
989                 closesocket(context->accept_socket);
990                 context->accept_socket = INVALID_SOCKET;
991                 Sleep(100);
992                 continue;
993             }
994
995             /* Wait for pending i/o. Wake up once per second to check for shutdown */
996             while (1) {
997                 rv = WaitForSingleObject(context->Overlapped.hEvent, 1000);
998                 if (rv == WAIT_OBJECT_0) {
999                     if (!GetOverlappedResult(context->Overlapped.hEvent, 
1000                                              &context->Overlapped, 
1001                                              &BytesRead, FALSE)) {
1002                         ap_log_error(APLOG_MARK,APLOG_WARNING, GetLastError(), ap_server_conf,
1003                                      "winnt_accept: Asynchronous AcceptEx failed.");
1004                         closesocket(context->accept_socket);
1005                         context->accept_socket = INVALID_SOCKET;
1006                     }
1007                     break;
1008                 }
1009                 /* WAIT_TIMEOUT */
1010                 if (shutdown_in_progress) {
1011                     closesocket(context->accept_socket);
1012                     context->accept_socket = INVALID_SOCKET;
1013                     break;
1014                 }
1015             }
1016             if (context->accept_socket == INVALID_SOCKET) {
1017                 continue;
1018             }
1019         }
1020
1021         /* Inherit the listen socket settings. Required for 
1022          * shutdown() to work 
1023          */
1024         if (setsockopt(context->accept_socket, SOL_SOCKET,
1025                        SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
1026                        sizeof(nlsd))) {
1027             ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
1028                          "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
1029             /* Not a failure condition. Keep running. */
1030         }
1031
1032         /* Get the local & remote address */
1033         GetAcceptExSockaddrs(context->buff,
1034                              0,
1035                              PADDED_ADDR_SIZE,
1036                              PADDED_ADDR_SIZE,
1037                              &context->sa_server,
1038                              &context->sa_server_len,
1039                              &context->sa_client,
1040                              &context->sa_client_len);
1041
1042         sockinfo.os_sock = &context->accept_socket;
1043         sockinfo.local   = context->sa_server;
1044         sockinfo.remote  = context->sa_client;
1045         sockinfo.family  = APR_INET;
1046         sockinfo.type    = SOCK_STREAM;
1047         apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
1048
1049         /* When a connection is received, send an io completion notification to
1050          * the ThreadDispatchIOCP. This function could be replaced by
1051          * mpm_post_completion_context(), but why do an extra function call...
1052          */
1053         PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED,
1054                                    &context->Overlapped);
1055         context = NULL;
1056     }
1057     if (!shutdown_in_progress) {
1058         /* Yow, hit an irrecoverable error! Tell the child to die. */
1059         SetEvent(exit_event);
1060     }
1061     ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
1062                  "Child %d: Accept thread exiting.", my_pid);
1063 }
1064 static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
1065 {
1066     int rc;
1067     DWORD BytesRead;
1068     DWORD CompKey;
1069     LPOVERLAPPED pol;
1070
1071     mpm_recycle_completion_context(context);
1072
1073     apr_atomic_inc(&g_blocked_threads);
1074     while (1) {
1075         if (workers_may_exit) {
1076             apr_atomic_dec(&g_blocked_threads);
1077             return NULL;
1078         }
1079         rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey,
1080                                        &pol, INFINITE);
1081         if (!rc) {
1082             rc = apr_get_os_error();
1083             ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, ap_server_conf,
1084                              "Child %d: GetQueuedComplationStatus returned %d", my_pid, rc);
1085             continue;
1086         }
1087
1088         switch (CompKey) {
1089         case IOCP_CONNECTION_ACCEPTED:
1090             context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped);
1091             break;
1092         case IOCP_SHUTDOWN:
1093             apr_atomic_dec(&g_blocked_threads);
1094             return NULL;
1095         default:
1096             apr_atomic_dec(&g_blocked_threads);
1097             return NULL;
1098         }
1099         break;
1100     }
1101     apr_atomic_dec(&g_blocked_threads);
1102
1103     return context;
1104 }
1105
1106 /*
1107  * worker_main()
1108  * Main entry point for the worker threads. Worker threads block in 
1109  * win*_get_connection() awaiting a connection to service.
1110  */
1111 static void worker_main(long thread_num)
1112 {
1113     static int requests_this_child = 0;
1114     PCOMP_CONTEXT context = NULL;
1115     ap_sb_handle_t *sbh;
1116
1117     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
1118                  "Child %d: Worker thread %d starting.", my_pid, thread_num);
1119     while (1) {
1120         conn_rec *c;
1121         apr_int32_t disconnected;
1122
1123         ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL);
1124
1125         /* Grab a connection off the network */
1126         if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1127             context = win9x_get_connection(context);
1128         }
1129         else {
1130             context = winnt_get_connection(context);
1131         }
1132         if (!context) {
1133             /* Time for the thread to exit */
1134             break;
1135         }
1136
1137         /* Have we hit MaxRequestPerChild connections? */
1138         if (ap_max_requests_per_child) {
1139             requests_this_child++;
1140             if (requests_this_child > ap_max_requests_per_child) {
1141                 SetEvent(max_requests_per_child_event);
1142             }
1143         }
1144
1145         ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num);
1146         c = ap_run_create_connection(context->ptrans, ap_server_conf,
1147                                      context->sock, thread_num, sbh,
1148                                      context->ba);
1149
1150         if (c) {
1151             ap_process_connection(c, context->sock);
1152             apr_getsocketopt(context->sock, APR_SO_DISCONNECTED, &disconnected);
1153             if (!disconnected) {
1154                 context->accept_socket = INVALID_SOCKET;
1155                 ap_lingering_close(c);
1156             }
1157         }
1158         else {
1159             /* ap_run_create_connection closes the socket on failure */
1160             context->accept_socket = INVALID_SOCKET;
1161         }
1162     }
1163
1164     ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD, 
1165                                         (request_rec *) NULL);
1166
1167     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf,
1168                  "Child %d: Worker thread %d exiting.", my_pid, thread_num);
1169 }
1170
1171 static void cleanup_thread(thread *handles, int *thread_cnt, int thread_to_clean)
1172 {
1173     int i;
1174
1175     CloseHandle(handles[thread_to_clean]);
1176     for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
1177         handles[i] = handles[i + 1];
1178     (*thread_cnt)--;
1179 }
1180
1181 /*
1182  * child_main() 
1183  * Entry point for the main control thread for the child process. 
1184  * This thread creates the accept thread, worker threads and
1185  * monitors the child process for maintenance and shutdown
1186  * events.
1187  */
1188 static void create_listener_thread()
1189 {
1190     int tid;
1191     if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1192         _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) win9x_accept,
1193                        NULL, 0, &tid);
1194     } else {
1195         /* Start an accept thread per listener 
1196          * XXX: Why would we have a NULL sd in our listeners?
1197          */
1198         ap_listen_rec *lr;
1199         for (lr = ap_listeners; lr; lr = lr->next) {
1200             if (lr->sd != NULL) {
1201                 _beginthreadex(NULL, 1000, (LPTHREAD_START_ROUTINE) winnt_accept,
1202                                (void *) lr, 0, &tid);
1203             }
1204         }
1205     }
1206 }
1207 static void child_main()
1208 {
1209     apr_status_t status;
1210     apr_hash_t *ht;
1211     ap_listen_rec *lr;
1212     HANDLE child_events[2];
1213     int threads_created = 0;
1214     int listener_started = 0;
1215     int tid;
1216     thread *child_handles;
1217     int rv;
1218     time_t end_time;
1219     int i;
1220     int cld;
1221
1222     apr_pool_create(&pchild, pconf);
1223     apr_pool_tag(pchild, "pchild");
1224
1225     ap_run_child_init(pchild, ap_server_conf);
1226     ht = apr_hash_make(pchild);
1227
1228     /* Initialize the child_events */
1229     max_requests_per_child_event = CreateEvent(NULL, TRUE, FALSE, NULL);
1230     if (!max_requests_per_child_event) {
1231         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1232                      "Child %d: Failed to create a max_requests event.", my_pid);
1233         exit(APEXIT_CHILDINIT);
1234     }
1235     child_events[0] = exit_event;
1236     child_events[1] = max_requests_per_child_event;
1237
1238     allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0, 1000000, NULL);
1239     apr_thread_mutex_create(&allowed_globals.jobmutex, 
1240                             APR_THREAD_MUTEX_DEFAULT, pchild);
1241
1242     /*
1243      * Wait until we have permission to start accepting connections.
1244      * start_mutex is used to ensure that only one child ever
1245      * goes into the listen/accept loop at once.
1246      */
1247     status = apr_proc_mutex_lock(start_mutex);
1248     if (status != APR_SUCCESS) {
1249         ap_log_error(APLOG_MARK,APLOG_ERR, status, ap_server_conf,
1250                      "Child %d: Failed to acquire the start_mutex. Process will exit.", my_pid);
1251         exit(APEXIT_CHILDINIT);
1252     }
1253     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
1254                  "Child %d: Acquired the start mutex.", my_pid);
1255
1256     /*
1257      * Create the worker thread dispatch IOCompletionPort
1258      * on Windows NT/2000
1259      */
1260     if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) {
1261         /* Create the worker thread dispatch IOCP */
1262         ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
1263                                                     NULL,
1264                                                     0,
1265                                                     0); /* CONCURRENT ACTIVE THREADS */
1266         apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
1267     }
1268
1269     /* 
1270      * Create the pool of worker threads
1271      */
1272     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
1273                  "Child %d: Starting %d worker threads.", my_pid, ap_threads_per_child);
1274     child_handles = (thread) apr_pcalloc(pchild, ap_threads_per_child * sizeof(int));
1275     while (1) {
1276         for (i = 0; i < ap_threads_per_child; i++) {
1277             int *score_idx;
1278             int status = ap_scoreboard_image->servers[0][i].status;
1279             if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
1280                 continue;
1281             }
1282             ap_update_child_status_from_indexes(0, i, SERVER_STARTING, NULL);
1283             child_handles[i] = (thread) _beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE) worker_main,
1284                                                        (void *) i, 0, &tid);
1285             if (child_handles[i] == 0) {
1286                 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1287                              "Child %d: _beginthreadex failed. Unable to create all worker threads. "
1288                              "Created %d of the %d threads requested with the ThreadsPerChild configuration directive.", 
1289                              threads_created, ap_threads_per_child);
1290                 ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
1291                 goto shutdown;
1292             }
1293             threads_created++;
1294             /* Save the score board index in ht keyed to the thread handle. We need this 
1295              * when cleaning up threads down below...
1296              */
1297             score_idx = apr_pcalloc(pchild, sizeof(int));
1298             *score_idx = i;
1299             apr_hash_set(ht, &child_handles[i], sizeof(thread), score_idx);
1300         }
1301         /* Start the listener only when workers are available */
1302         if (!listener_started && threads_created) {
1303             create_listener_thread();
1304             listener_started = 1;
1305         }
1306         if (threads_created == ap_threads_per_child) {
1307             break;
1308         }
1309         /* Check to see if the child has been told to exit */
1310         if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) {
1311             break;
1312         }
1313         /* wait for previous generation to clean up an entry in the scoreboard */
1314         apr_sleep(1 * APR_USEC_PER_SEC);
1315     }
1316
1317     /* Wait for one of three events:
1318      * exit_event: 
1319      *    The exit_event is signaled by the parent process to notify 
1320      *    the child that it is time to exit.
1321      *
1322      * max_requests_per_child_event: 
1323      *    This event is signaled by the worker threads to indicate that
1324      *    the process has handled MaxRequestsPerChild connections.
1325      *
1326      * TIMEOUT:
1327      *    To do periodic maintenance on the server (check for thread exits,
1328      *    number of completion contexts, etc.)
1329      */
1330     while (1) {
1331         rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, 1000);
1332         cld = rv - WAIT_OBJECT_0;
1333         if (rv == WAIT_FAILED) {
1334             /* Something serious is wrong */
1335             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1336                          "Child %d: WAIT_FAILED -- shutting down server");
1337             break;
1338         }
1339         else if (rv == WAIT_TIMEOUT) {
1340             apr_proc_other_child_check();
1341         }
1342         else if (cld == 0) {
1343             /* Exit event was signaled */
1344             ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1345                          "Child %d: Exit event signaled. Child process is ending.", my_pid);
1346             break;
1347         }
1348         else {
1349             /* MaxRequestsPerChild event set by the worker threads.
1350              * Signal the parent to restart
1351              */
1352             ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1353                          "Child %d: Process exiting because it reached "
1354                          "MaxRequestsPerChild. Signaling the parent to "
1355                          "restart a new child process.", my_pid);
1356             ap_signal_parent(SIGNAL_PARENT_RESTART);
1357             break;
1358         }
1359     }
1360
1361     /* 
1362      * Time to shutdown the child process 
1363      */
1364
1365  shutdown:
1366     /* Setting is_graceful will cause threads handling keep-alive connections 
1367      * to close the connection after handling the current request.
1368      */
1369     is_graceful = 1;
1370
1371     /* Close the listening sockets. Note, we must close the listeners
1372      * before closing any accept sockets pending in AcceptEx to prevent
1373      * memory leaks in the kernel.
1374      */
1375     for (lr = ap_listeners; lr ; lr = lr->next) {
1376         apr_socket_close(lr->sd);
1377     }
1378
1379     /* Shutdown listener threads and pending AcceptEx socksts 
1380      * but allow the worker threads to continue consuming from
1381      * the queue of accepted connections.
1382      */
1383     shutdown_in_progress = 1;
1384
1385     Sleep(1000);
1386
1387     /* Tell the worker threads to exit */
1388     workers_may_exit = 1;
1389
1390     /* Release the start_mutex to let the new process (in the restart
1391      * scenario) a chance to begin accepting and servicing requests 
1392      */
1393     rv = apr_proc_mutex_unlock(start_mutex);
1394     if (rv == APR_SUCCESS) {
1395         ap_log_error(APLOG_MARK,APLOG_NOTICE, rv, ap_server_conf, 
1396                      "Child %d: Released the start mutex", my_pid);
1397     }
1398     else {
1399         ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf, 
1400                      "Child %d: Failure releasing the start mutex", my_pid);
1401     }
1402
1403     /* Shutdown the worker threads */
1404     if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1405         for (i = 0; i < threads_created; i++) {
1406             add_job(-1);
1407         }
1408     }
1409     else { /* Windows NT/2000 */
1410         /* Post worker threads blocked on the ThreadDispatch IOCompletion port */
1411         while (g_blocked_threads > 0) {
1412             ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, ap_server_conf, 
1413                          "Child %d: %d threads blocked on the completion port", my_pid, g_blocked_threads);
1414             for (i=g_blocked_threads; i > 0; i--) {
1415                 PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_SHUTDOWN, NULL);
1416             }
1417             Sleep(1000);
1418         }
1419         /* Empty the accept queue of completion contexts */
1420         apr_thread_mutex_lock(qlock);
1421         while (qhead) {
1422             CloseHandle(qhead->Overlapped.hEvent);
1423             closesocket(qhead->accept_socket);
1424             qhead = qhead->next;
1425         }
1426         apr_thread_mutex_unlock(qlock);
1427     }
1428
1429     /* Give busy worker threads a chance to service their connections */
1430     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
1431                  "Child %d: Waiting for %d worker threads to exit.", my_pid, threads_created);
1432     end_time = time(NULL) + 180;
1433     while (threads_created) {
1434         rv = wait_for_many_objects(threads_created, child_handles, end_time - time(NULL));
1435         if (rv != WAIT_TIMEOUT) {
1436             rv = rv - WAIT_OBJECT_0;
1437             ap_assert((rv >= 0) && (rv < threads_created));
1438             cleanup_thread(child_handles, &threads_created, rv);
1439             continue;
1440         }
1441         break;
1442     }
1443
1444     /* Kill remaining threads off the hard way */
1445     if (threads_created) {
1446         ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
1447                      "Child %d: Terminating %d threads that failed to exit.", my_pid);
1448     }
1449     for (i = 0; i < threads_created; i++) {
1450         int *score_idx;
1451         TerminateThread(child_handles[i], 1);
1452         CloseHandle(child_handles[i]);
1453         /* Reset the scoreboard entry for the thread we just whacked */
1454         score_idx = apr_hash_get(ht, &child_handles[i], sizeof(thread));
1455         ap_update_child_status_from_indexes(0, *score_idx, SERVER_DEAD, NULL);        
1456     }
1457     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
1458                  "Child %d: All worker threads have exited.", my_pid);
1459
1460     CloseHandle(allowed_globals.jobsemaphore);
1461     apr_thread_mutex_destroy(allowed_globals.jobmutex);
1462     if (osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
1463         apr_thread_mutex_destroy(qlock);
1464
1465     apr_pool_destroy(pchild);
1466     CloseHandle(exit_event);
1467 }
1468
1469 static int send_handles_to_child(apr_pool_t *p, HANDLE child_exit_event, HANDLE hProcess, apr_file_t *child_in)
1470 {
1471     apr_status_t rv;
1472     HANDLE hScore;
1473     HANDLE hDup;
1474     HANDLE hCurrentProcess = GetCurrentProcess();
1475     DWORD BytesWritten;
1476
1477     if (!DuplicateHandle(hCurrentProcess, child_exit_event, hProcess, &hDup,
1478                          EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0)) {
1479         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1480                      "Parent: Unable to duplicate the exit event handle for the child");
1481         return -1;
1482     }
1483     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
1484             != APR_SUCCESS) {
1485         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
1486                      "Parent: Unable to send the exit event handle to the child");
1487         return -1;
1488     }
1489
1490     if ((rv = apr_os_shm_get(&hScore, ap_scoreboard_shm)) != APR_SUCCESS) {
1491         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
1492                      "Parent: Unable to retrieve the scoreboard handle for the child");
1493         return -1;
1494     }
1495     if (!DuplicateHandle(hCurrentProcess, hScore, hProcess, &hDup,
1496                          FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0)) {
1497         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1498                      "Parent: Unable to duplicate the scoreboard handle to the child");
1499         return -1;
1500     }
1501     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
1502             != APR_SUCCESS) {
1503         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
1504                      "Parent: Unable to send the scoreboard handle to the child");
1505         return -1;
1506     }
1507
1508     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
1509                  "Parent: Sent the scoreboard to the child");
1510     return 0;
1511 }
1512
1513 static int send_listeners_to_child(apr_pool_t *p, DWORD dwProcessId, 
1514                                    apr_file_t *child_in)
1515 {
1516     apr_status_t rv;
1517     int lcnt = 0;
1518     ap_listen_rec *lr;
1519     LPWSAPROTOCOL_INFO  lpWSAProtocolInfo;
1520     DWORD BytesWritten;
1521
1522     /* Run the chain of open sockets. For each socket, duplicate it 
1523      * for the target process then send the WSAPROTOCOL_INFO 
1524      * (returned by dup socket) to the child.
1525      */
1526     for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
1527         int nsd;
1528         lpWSAProtocolInfo = apr_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
1529         apr_os_sock_get(&nsd,lr->sd);
1530         ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
1531                      "Parent: Duplicating socket %d and sending it to child process %d", 
1532                      nsd, dwProcessId);
1533         if (WSADuplicateSocket(nsd, dwProcessId,
1534                                lpWSAProtocolInfo) == SOCKET_ERROR) {
1535             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
1536                          "Parent: WSADuplicateSocket failed for socket %d. Check the FAQ.", lr->sd );
1537             return -1;
1538         }
1539
1540         if ((rv = apr_file_write_full(child_in, lpWSAProtocolInfo, 
1541                                       sizeof(WSAPROTOCOL_INFO), &BytesWritten))
1542                 != APR_SUCCESS) {
1543             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
1544                          "Parent: Unable to write duplicated socket %d to the child.", lr->sd );
1545             return -1;
1546         }
1547     }
1548
1549     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
1550                  "Parent: Sent %d listeners to child %d", lcnt, dwProcessId);
1551     return 0;
1552 }
1553
1554 static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_event, 
1555                           DWORD *child_pid)
1556 {
1557     /* These NEVER change for the lifetime of this parent 
1558      */
1559     static char **args = NULL;
1560     static char **env = NULL;
1561     static char pidbuf[28];
1562
1563     apr_status_t rv;
1564     apr_pool_t *ptemp;
1565     apr_procattr_t *attr;
1566     apr_file_t *child_out;
1567     apr_file_t *child_err;
1568     apr_proc_t new_child;
1569     HANDLE hExitEvent;
1570     char *cmd;
1571     char *cwd;
1572
1573     apr_pool_sub_make(&ptemp, p, NULL);
1574
1575     /* Build the command line. Should look something like this:
1576      * C:/apache/bin/apache.exe -f ap_server_confname 
1577      * First, get the path to the executable...
1578      */
1579     apr_procattr_create(&attr, ptemp);
1580     apr_procattr_cmdtype_set(attr, APR_PROGRAM);
1581     apr_procattr_detach_set(attr, 1);
1582     if (((rv = apr_filepath_get(&cwd, 0, ptemp)) != APR_SUCCESS)
1583            || ((rv = apr_procattr_dir_set(attr, cwd)) != APR_SUCCESS)) {
1584         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
1585                      "Parent: Failed to get the current path");
1586     }
1587
1588     if (!args) {
1589         /* Build the args array, only once since it won't change 
1590          * for the lifetime of this parent process.
1591          */
1592         if ((rv = ap_os_proc_filepath(&cmd, ptemp))
1593                 != APR_SUCCESS) {
1594             ap_log_error(APLOG_MARK, APLOG_CRIT, ERROR_BAD_PATHNAME, ap_server_conf,
1595                          "Parent: Failed to get full path of %s", 
1596                          ap_server_conf->process->argv[0]);
1597             apr_pool_destroy(ptemp);
1598             return -1;
1599         }
1600         
1601         args = malloc((ap_server_conf->process->argc + 1) * sizeof (char*));
1602         memcpy(args + 1, ap_server_conf->process->argv + 1, 
1603                (ap_server_conf->process->argc - 1) * sizeof (char*));
1604         args[0] = malloc(strlen(cmd) + 1);
1605         strcpy(args[0], cmd);
1606         args[ap_server_conf->process->argc] = NULL;
1607     }
1608     else {
1609         cmd = args[0];
1610     }
1611
1612     /* Create a pipe to send handles to the child */
1613     if ((rv = apr_procattr_io_set(attr, APR_FULL_BLOCK, 
1614                                   APR_NO_PIPE, APR_NO_PIPE)) != APR_SUCCESS) {
1615         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
1616                         "Parent: Unable to create child stdin pipe.\n");
1617         apr_pool_destroy(ptemp);
1618         return -1;
1619     }
1620
1621     /* Open a null handle to soak info from the child */
1622     if (((rv = apr_file_open(&child_out, "NUL", APR_READ | APR_WRITE, 
1623                              APR_OS_DEFAULT, ptemp)) != APR_SUCCESS)
1624         || ((rv = apr_procattr_child_out_set(attr, child_out, NULL)) 
1625                 != APR_SUCCESS)) {
1626         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
1627                         "Parent: Unable to connect child stdout to NUL.\n");
1628         apr_pool_destroy(ptemp);
1629         return -1;
1630     }
1631
1632     /* Connect the child's initial stderr to our main server error log 
1633      * or share our own stderr handle.
1634      */
1635     if (ap_server_conf->error_log) {
1636         child_err = ap_server_conf->error_log;
1637     }
1638     else {
1639         rv = apr_file_open_stderr(&child_err, ptemp);
1640     }
1641     if (rv == APR_SUCCESS) {
1642         if ((rv = apr_procattr_child_err_set(attr, child_err, NULL))
1643                 != APR_SUCCESS) {
1644             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
1645                             "Parent: Unable to connect child stderr.\n");
1646             apr_pool_destroy(ptemp);
1647             return -1;
1648         }
1649     }
1650
1651     /* Create the child_exit_event */
1652     hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1653     if (!hExitEvent) {
1654         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1655                      "Parent: Could not create exit event for child process");
1656         apr_pool_destroy(ptemp);
1657         return -1;
1658     }
1659
1660     if (!env) 
1661     {
1662         /* Build the env array, only once since it won't change 
1663          * for the lifetime of this parent process.
1664          */
1665         int envc;
1666         for (envc = 0; _environ[envc]; ++envc) {
1667             ;
1668         }
1669         env = malloc((envc + 2) * sizeof (char*));
1670         memcpy(env, _environ, envc * sizeof (char*));
1671         apr_snprintf(pidbuf, sizeof(pidbuf), "AP_PARENT_PID=%i", parent_pid);
1672         env[envc] = pidbuf;
1673         env[envc + 1] = NULL;
1674     }
1675
1676     rv = apr_proc_create(&new_child, cmd, args, env, attr, ptemp);
1677     if (rv != APR_SUCCESS) {
1678         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
1679                      "Parent: Failed to create the child process.");
1680         apr_pool_destroy(ptemp);
1681         CloseHandle(hExitEvent);
1682         CloseHandle(new_child.hproc);
1683         return -1;
1684     }
1685
1686     ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1687                  "Parent: Created child process %d", new_child.pid);
1688
1689     if (send_handles_to_child(ptemp, hExitEvent, new_child.hproc, new_child.in)) {
1690         /*
1691          * This error is fatal, mop up the child and move on
1692          * We toggle the child's exit event to cause this child 
1693          * to quit even as it is attempting to start.
1694          */
1695         SetEvent(hExitEvent);
1696         apr_pool_destroy(ptemp);
1697         CloseHandle(hExitEvent);
1698         CloseHandle(new_child.hproc);
1699         return -1;
1700     }
1701
1702     /* Important:
1703      * Give the child process a chance to run before dup'ing the sockets.
1704      * We have already set the listening sockets noninheritable, but if 
1705      * WSADuplicateSocket runs before the child process initializes
1706      * the listeners will be inherited anyway.
1707      *
1708      * XXX: This is badness; needs some mutex interlocking
1709      */
1710     Sleep(1000);
1711
1712     if (send_listeners_to_child(ptemp, new_child.pid, new_child.in)) {
1713         /*
1714          * This error is fatal, mop up the child and move on
1715          * We toggle the child's exit event to cause this child 
1716          * to quit even as it is attempting to start.
1717          */
1718         SetEvent(hExitEvent);
1719         apr_pool_destroy(ptemp);
1720         CloseHandle(hExitEvent);
1721         CloseHandle(new_child.hproc);
1722         return -1;
1723     }
1724
1725     *child_exit_event = hExitEvent;
1726     *child_proc = new_child.hproc;
1727     *child_pid = new_child.pid;
1728
1729     return 0;
1730 }
1731
1732 /***********************************************************************
1733  * master_main()
1734  * master_main() runs in the parent process.  It creates the child 
1735  * process which handles HTTP requests then waits on one of three 
1736  * events:
1737  *
1738  * restart_event
1739  * -------------
1740  * The restart event causes master_main to start a new child process and
1741  * tells the old child process to exit (by setting the child_exit_event).
1742  * The restart event is set as a result of one of the following:
1743  * 1. An apache -k restart command on the command line
1744  * 2. A command received from Windows service manager which gets 
1745  *    translated into an ap_signal_parent(SIGNAL_PARENT_RESTART)
1746  *    call by code in service.c.
1747  * 3. The child process calling ap_signal_parent(SIGNAL_PARENT_RESTART)
1748  *    as a result of hitting MaxRequestsPerChild.
1749  *
1750  * shutdown_event 
1751  * --------------
1752  * The shutdown event causes master_main to tell the child process to 
1753  * exit and that the server is shutting down. The shutdown event is
1754  * set as a result of one of the following:
1755  * 1. An apache -k shutdown command on the command line
1756  * 2. A command received from Windows service manager which gets
1757  *    translated into an ap_signal_parent(SIGNAL_PARENT_SHUTDOWN)
1758  *    call by code in service.c.
1759  *
1760  * child process handle
1761  * --------------------
1762  * The child process handle will be signaled if the child process 
1763  * exits for any reason. In a normal running server, the signaling
1764  * of this event means that the child process has exited prematurely
1765  * due to a seg fault or other irrecoverable error. For server
1766  * robustness, master_main will restart the child process under this 
1767  * condtion.
1768  *
1769  * master_main uses the child_exit_event to signal the child process
1770  * to exit.
1771  **********************************************************************/
1772 #define NUM_WAIT_HANDLES 3
1773 #define CHILD_HANDLE     0
1774 #define SHUTDOWN_HANDLE  1
1775 #define RESTART_HANDLE   2
1776 static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_event)
1777 {
1778     int rv, cld;
1779     int restart_pending;
1780     int shutdown_pending;
1781     HANDLE child_exit_event;
1782     HANDLE event_handles[NUM_WAIT_HANDLES];
1783     DWORD child_pid;
1784
1785     restart_pending = shutdown_pending = 0;
1786
1787     event_handles[SHUTDOWN_HANDLE] = shutdown_event;
1788     event_handles[RESTART_HANDLE] = restart_event;
1789
1790     /* Create a single child process */
1791     rv = create_process(pconf, &event_handles[CHILD_HANDLE], 
1792                         &child_exit_event, &child_pid);
1793     if (rv < 0) 
1794     {
1795         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1796                      "master_main: create child process failed. Exiting.");
1797         shutdown_pending = 1;
1798         goto die_now;
1799     }
1800     if (!strcasecmp(signal_arg, "runservice")) {
1801         mpm_service_started();
1802     }
1803
1804     /* Update the scoreboard. Note that there is only a single active
1805      * child at once.
1806      */
1807     ap_scoreboard_image->parent[0].quiescing = 0;
1808     ap_scoreboard_image->parent[0].pid = child_pid;
1809
1810     /* Wait for shutdown or restart events or for child death */
1811     rv = WaitForMultipleObjects(NUM_WAIT_HANDLES, (HANDLE *) event_handles, FALSE, INFINITE);
1812     cld = rv - WAIT_OBJECT_0;
1813     if (rv == WAIT_FAILED) {
1814         /* Something serious is wrong */
1815         ap_log_error(APLOG_MARK,APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1816                      "master_main: WaitForMultipeObjects WAIT_FAILED -- doing server shutdown");
1817         shutdown_pending = 1;
1818     }
1819     else if (rv == WAIT_TIMEOUT) {
1820         /* Hey, this cannot happen */
1821         ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
1822                      "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT");
1823         shutdown_pending = 1;
1824     }
1825     else if (cld == SHUTDOWN_HANDLE) {
1826         /* shutdown_event signalled */
1827         shutdown_pending = 1;
1828         ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, s, 
1829                      "Parent: Received shutdown signal -- Shutting down the server.");
1830         if (ResetEvent(shutdown_event) == 0) {
1831             ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
1832                          "ResetEvent(shutdown_event)");
1833         }
1834     }
1835     else if (cld == RESTART_HANDLE) {
1836         /* Received a restart event. Prepare the restart_event to be reused 
1837          * then signal the child process to exit. 
1838          */
1839         restart_pending = 1;
1840         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, 
1841                      "Parent: Received restart signal -- Restarting the server.");
1842         if (ResetEvent(restart_event) == 0) {
1843             ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
1844                          "Parent: ResetEvent(restart_event) failed.");
1845         }
1846         if (SetEvent(child_exit_event) == 0) {
1847             ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
1848                          "Parent: SetEvent for child process %d failed.", 
1849                          event_handles[CHILD_HANDLE]);
1850         }
1851         /* Don't wait to verify that the child process really exits, 
1852          * just move on with the restart.
1853          */
1854         CloseHandle(event_handles[CHILD_HANDLE]);
1855         event_handles[CHILD_HANDLE] = NULL;
1856     }
1857     else {
1858         /* The child process exited prematurely due to a fatal error. */
1859         DWORD exitcode;
1860         if (!GetExitCodeProcess(event_handles[CHILD_HANDLE], &exitcode)) {
1861             /* HUH? We did exit, didn't we? */
1862             exitcode = APEXIT_CHILDFATAL;
1863         }
1864         if (   exitcode == APEXIT_CHILDFATAL 
1865             || exitcode == APEXIT_CHILDINIT
1866             || exitcode == APEXIT_INIT) {
1867             ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, 
1868                          "Parent: child process exited with status %u -- Aborting.", exitcode);
1869         }
1870         else {
1871             int i;
1872             restart_pending = 1;
1873             ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
1874                          "Parent: child process exited with status %u -- Restarting.", exitcode);
1875             for (i = 0; i < ap_threads_per_child; i++) {
1876                 ap_update_child_status_from_indexes(0, i, SERVER_DEAD, NULL);
1877             }
1878         }
1879         CloseHandle(event_handles[CHILD_HANDLE]);
1880         event_handles[CHILD_HANDLE] = NULL;
1881     }
1882     if (restart_pending) {
1883         ++ap_my_generation;
1884         ap_scoreboard_image->global->running_generation = ap_my_generation;
1885     }
1886 die_now:
1887     if (shutdown_pending) 
1888     {
1889         int timeout = 30000;  /* Timeout is milliseconds */
1890
1891         /* This shutdown is only marginally graceful. We will give the 
1892          * child a bit of time to exit gracefully. If the time expires,
1893          * the child will be wacked.
1894          */
1895         if (!strcasecmp(signal_arg, "runservice")) {
1896             mpm_service_stopping();
1897         }
1898         /* Signal the child processes to exit */
1899         if (SetEvent(child_exit_event) == 0) {
1900                 ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_os_error(), ap_server_conf,
1901                              "Parent: SetEvent for child process %d failed", event_handles[CHILD_HANDLE]);
1902         }
1903         if (event_handles[CHILD_HANDLE]) {
1904             rv = WaitForSingleObject(event_handles[CHILD_HANDLE], timeout);
1905             if (rv == WAIT_OBJECT_0) {
1906                 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1907                              "Parent: Child process exited successfully.");
1908                 CloseHandle(event_handles[CHILD_HANDLE]);
1909                 event_handles[CHILD_HANDLE] = NULL;
1910             }
1911             else {
1912                 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1913                              "Parent: Forcing termination of child process %d ", event_handles[CHILD_HANDLE]);
1914                 TerminateProcess(event_handles[CHILD_HANDLE], 1);
1915                 CloseHandle(event_handles[CHILD_HANDLE]);
1916                 event_handles[CHILD_HANDLE] = NULL;
1917             }
1918         }
1919         return 0;  /* Tell the caller we do not want to restart */
1920     }
1921
1922     return 1;      /* Tell the caller we want a restart */
1923 }
1924
1925 /* service_nt_main_fn needs to append the StartService() args 
1926  * outside of our call stack and thread as the service starts...
1927  */
1928 apr_array_header_t *mpm_new_argv;
1929
1930 /* Remember service_to_start failures to log and fail in pre_config.
1931  * Remember inst_argc and inst_argv for installing or starting the
1932  * service after we preflight the config.
1933  */
1934
1935 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
1936 {
1937     switch(query_code){
1938         case AP_MPMQ_MAX_DAEMON_USED:
1939             *result = MAXIMUM_WAIT_OBJECTS;
1940             return APR_SUCCESS;
1941         case AP_MPMQ_IS_THREADED:
1942             *result = AP_MPMQ_STATIC;
1943             return APR_SUCCESS;
1944         case AP_MPMQ_IS_FORKED:
1945             *result = AP_MPMQ_NOT_SUPPORTED;
1946             return APR_SUCCESS;
1947         case AP_MPMQ_HARD_LIMIT_DAEMONS:
1948             *result = HARD_SERVER_LIMIT;
1949             return APR_SUCCESS;
1950         case AP_MPMQ_HARD_LIMIT_THREADS:
1951             *result = HARD_THREAD_LIMIT;
1952             return APR_SUCCESS;
1953         case AP_MPMQ_MAX_THREADS:
1954             *result = ap_threads_per_child;
1955             return APR_SUCCESS;
1956         case AP_MPMQ_MIN_SPARE_DAEMONS:
1957             *result = 0;
1958             return APR_SUCCESS;
1959         case AP_MPMQ_MIN_SPARE_THREADS:    
1960             *result = 0;
1961             return APR_SUCCESS;
1962         case AP_MPMQ_MAX_SPARE_DAEMONS:
1963             *result = 0;
1964             return APR_SUCCESS;
1965         case AP_MPMQ_MAX_SPARE_THREADS:
1966             *result = 0;
1967             return APR_SUCCESS;
1968         case AP_MPMQ_MAX_REQUESTS_DAEMON:
1969             *result = ap_max_requests_per_child;
1970             return APR_SUCCESS;
1971         case AP_MPMQ_MAX_DAEMONS:
1972             *result = 0;
1973             return APR_SUCCESS;
1974     }
1975     return APR_ENOTIMPL;
1976
1977
1978 #define SERVICE_UNSET (-1)
1979 static apr_status_t service_set = SERVICE_UNSET;
1980 static apr_status_t service_to_start_success;
1981 static int inst_argc;
1982 static const char * const *inst_argv;
1983 static char *service_name = NULL;
1984     
1985 void winnt_rewrite_args(process_rec *process) 
1986 {
1987     /* Handle the following SCM aspects in this phase:
1988      *
1989      *   -k runservice [transition for WinNT, nothing for Win9x]
1990      *   -k (!)install [error out if name is not installed]
1991      *   -k uninstall
1992      *   -k stop
1993      *   -k shutdown (same as -k stop). Maintained for backward compatability.
1994      *
1995      * We can't leave this phase until we know our identity
1996      * and modify the command arguments appropriately.
1997      *
1998      * We do not care if the .conf file exists or is parsable when
1999      * attempting to stop or uninstall a service.
2000      */
2001     apr_status_t rv;
2002     char *def_server_root;
2003     char *binpath;
2004     char optbuf[3];
2005     const char *optarg;
2006     int fixed_args;
2007     char *pid;
2008     apr_getopt_t *opt;
2009     int running_as_service = 1;
2010     int errout = 0;
2011
2012     pconf = process->pconf;
2013
2014     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
2015     GetVersionEx(&osver);
2016
2017     /* AP_PARENT_PID is only valid in the child */
2018     pid = getenv("AP_PARENT_PID");
2019     if (pid) 
2020     {
2021         /* This is the child */
2022         my_pid = GetCurrentProcessId();
2023         parent_pid = (DWORD) atol(pid);
2024
2025         /* Prevent holding open the (nonexistant) console */
2026         real_exit_code = 0;
2027
2028         /* The parent is responsible for providing the
2029          * COMPLETE ARGUMENTS REQUIRED to the child.
2030          *
2031          * No further argument parsing is needed, but
2032          * for good measure we will provide a simple
2033          * signal string for later testing.
2034          */
2035         signal_arg = "runchild";
2036         return;
2037     }
2038     
2039     /* This is the parent, we have a long way to go :-) */
2040     parent_pid = my_pid = GetCurrentProcessId();
2041     
2042     /* Rewrite process->argv[]; 
2043      *
2044      * strip out -k signal into signal_arg
2045      * strip out -n servicename and set the names
2046      * add default -d serverroot from the path of this executable
2047      * 
2048      * The end result will look like:
2049      *
2050      * The invocation command (%0)
2051      *     The -d serverroot default from the running executable
2052      *         The requested service's (-n) registry ConfigArgs
2053      *             The WinNT SCM's StartService() args
2054      */
2055     if ((rv = ap_os_proc_filepath(&binpath, process->pconf))
2056             != APR_SUCCESS) {
2057         ap_log_error(APLOG_MARK,APLOG_CRIT, rv, NULL, 
2058                      "Failed to get the full path of %s", process->argv[0]);
2059         exit(APEXIT_INIT);
2060     }
2061     /* WARNING: There is an implict assumption here that the
2062      * executable resides in ServerRoot or ServerRoot\bin
2063      */
2064     def_server_root = (char *) apr_filename_of_pathname(binpath);
2065     if (def_server_root > binpath) {
2066         *(def_server_root - 1) = '\0';
2067         def_server_root = (char *) apr_filename_of_pathname(binpath);
2068         if (!strcasecmp(def_server_root, "bin"))
2069             *(def_server_root - 1) = '\0';
2070     }
2071     apr_filepath_merge(&def_server_root, NULL, binpath, 
2072                        APR_FILEPATH_TRUENAME, process->pool);
2073
2074     /* Use process->pool so that the rewritten argv
2075      * lasts for the lifetime of the server process,
2076      * because pconf will be destroyed after the 
2077      * initial pre-flight of the config parser.
2078      */
2079     mpm_new_argv = apr_array_make(process->pool, process->argc + 2,
2080                                   sizeof(const char *));
2081     *(const char **)apr_array_push(mpm_new_argv) = process->argv[0];
2082     *(const char **)apr_array_push(mpm_new_argv) = "-d";
2083     *(const char **)apr_array_push(mpm_new_argv) = def_server_root;
2084
2085     fixed_args = mpm_new_argv->nelts;
2086
2087     optbuf[0] = '-';
2088     optbuf[2] = '\0';
2089     apr_getopt_init(&opt, process->pool, process->argc, (char**) process->argv);
2090     opt->errfn = NULL;
2091     while ((rv = apr_getopt(opt, "wn:k:" AP_SERVER_BASEARGS, 
2092                             optbuf + 1, &optarg)) == APR_SUCCESS) {
2093         switch (optbuf[1]) {
2094
2095         /* Shortcuts; include the -w option to hold the window open on error.
2096          * This must not be toggled once we reset real_exit_code to 0!
2097          */
2098         case 'w':
2099             if (real_exit_code)
2100                 real_exit_code = 2;
2101             break;
2102
2103         case 'n':
2104             service_set = mpm_service_set_name(process->pool, &service_name, 
2105                                                optarg);
2106             break;
2107
2108         case 'k':
2109             signal_arg = optarg;
2110             break;
2111
2112         case 'E':
2113             errout = 1;
2114             /* Fall through so the Apache main() handles the 'E' arg */
2115         default:
2116             *(const char **)apr_array_push(mpm_new_argv) =
2117                 apr_pstrdup(process->pool, optbuf);
2118
2119             if (optarg) {
2120                 *(const char **)apr_array_push(mpm_new_argv) = optarg;
2121             }
2122             break;
2123         }
2124     }
2125     
2126     /* back up to capture the bad argument */
2127     if (rv == APR_BADCH || rv == APR_BADARG) {
2128         opt->ind--;
2129     }
2130
2131     while (opt->ind < opt->argc) {
2132         *(const char **)apr_array_push(mpm_new_argv) =
2133             apr_pstrdup(process->pool, opt->argv[opt->ind++]);
2134     }
2135
2136     /* Track the number of args actually entered by the user */
2137     inst_argc = mpm_new_argv->nelts - fixed_args;
2138
2139     /* Provide a default 'run' -k arg to simplify signal_arg tests */
2140     if (!signal_arg)
2141     {
2142         signal_arg = "run";
2143         running_as_service = 0;
2144     }
2145
2146     if (!strcasecmp(signal_arg, "runservice")) 
2147     {
2148         /* Start the NT Service _NOW_ because the WinNT SCM is 
2149          * expecting us to rapidly assume control of our own 
2150          * process, the SCM will tell us our service name, and
2151          * may have extra StartService() command arguments to
2152          * add for us.
2153          *
2154          * The SCM will generally invoke the executable with
2155          * the c:\win\system32 default directory.  This is very
2156          * lethal if folks use ServerRoot /foopath on windows
2157          * without a drive letter.  Change to the default root
2158          * (path to apache root, above /bin) for safety.
2159          */
2160         apr_filepath_set(def_server_root, process->pool);
2161         
2162         /* Any other process has a console, so we don't to begin
2163          * a Win9x service until the configuration is parsed and
2164          * any command line errors are reported.
2165          *
2166          * We hold the return value so that we can die in pre_config
2167          * after logging begins, and the failure can land in the log.
2168          */
2169         if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) 
2170         {
2171             if (!errout) {
2172                 mpm_nt_eventlog_stderr_open(service_name, process->pool);
2173             }
2174             service_to_start_success = mpm_service_to_start(&service_name,
2175                                                             process->pool);
2176             if (service_to_start_success == APR_SUCCESS) {
2177                 service_set = APR_SUCCESS;
2178             }
2179         }
2180     }
2181
2182     /* Get the default for any -k option, except run */
2183     if (service_set == SERVICE_UNSET && strcasecmp(signal_arg, "run")) {
2184         service_set = mpm_service_set_name(process->pool, &service_name,
2185                                            AP_DEFAULT_SERVICE_NAME);
2186     }
2187
2188     if (!strcasecmp(signal_arg, "install")) /* -k install */
2189     {
2190         if (service_set == APR_SUCCESS) 
2191         {
2192             ap_log_error(APLOG_MARK,APLOG_ERR, 0, NULL,
2193                  "%s: Service is already installed.", service_name);
2194             exit(APEXIT_INIT);
2195         }
2196     }
2197     else if (running_as_service)
2198     {
2199         if (service_set == APR_SUCCESS) 
2200         {
2201             /* Attempt to Uninstall, or stop, before 
2202              * we can read the arguments or .conf files
2203              */
2204             if (!strcasecmp(signal_arg, "uninstall")) {
2205                 rv = mpm_service_uninstall();
2206                 exit(rv);
2207             }
2208
2209             if ((!strcasecmp(signal_arg, "stop")) || 
2210                 (!strcasecmp(signal_arg, "shutdown"))) {
2211                 mpm_signal_service(process->pool, 0);
2212                 exit(0);
2213             }
2214
2215             rv = mpm_merge_service_args(process->pool, mpm_new_argv, 
2216                                         fixed_args);
2217             if (rv == APR_SUCCESS) {
2218                 ap_log_error(APLOG_MARK,APLOG_INFO, 0, NULL,
2219                              "Using ConfigArgs of the installed service "
2220                              "\"%s\".", service_name);
2221             }
2222             else  {
2223                 ap_log_error(APLOG_MARK,APLOG_WARNING, rv, NULL,
2224                              "No installed ConfigArgs for the service "
2225                              "\"%s\", using Apache defaults.", service_name);
2226             }
2227         }
2228         else
2229         {
2230             ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL,
2231                  "No installed service named \"%s\".", service_name);
2232             exit(APEXIT_INIT);
2233         }
2234     }
2235     if (strcasecmp(signal_arg, "install") && service_set && service_set != SERVICE_UNSET) 
2236     {
2237         ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL,
2238              "No installed service named \"%s\".", service_name);
2239         exit(APEXIT_INIT);
2240     }
2241     
2242     /* Track the args actually entered by the user.
2243      * These will be used for the -k install parameters, as well as
2244      * for the -k start service override arguments.
2245      */
2246     inst_argv = (const char * const *)mpm_new_argv->elts
2247         + mpm_new_argv->nelts - inst_argc;
2248
2249     process->argc = mpm_new_argv->nelts; 
2250     process->argv = (const char * const *) mpm_new_argv->elts;
2251 }
2252
2253
2254 static int winnt_pre_config(apr_pool_t *pconf_, apr_pool_t *plog, apr_pool_t *ptemp) 
2255 {
2256     /* Handle the following SCM aspects in this phase:
2257      *
2258      *   -k runservice [WinNT errors logged from rewrite_args]
2259      */
2260
2261     /* Initialize shared static objects. 
2262      */
2263     pconf = pconf_;
2264
2265     /* This behavior is voided by setting real_exit_code to 0 */
2266     atexit(hold_console_open_on_error);
2267
2268     if (ap_exists_config_define("ONE_PROCESS") ||
2269         ap_exists_config_define("DEBUG"))
2270         one_process = -1;
2271
2272     if (!strcasecmp(signal_arg, "runservice")
2273             && (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
2274             && (service_to_start_success != APR_SUCCESS)) {
2275         ap_log_error(APLOG_MARK,APLOG_CRIT, service_to_start_success, NULL, 
2276                      "%s: Unable to start the service manager.",
2277                      service_name);
2278         exit(APEXIT_INIT);
2279     }
2280
2281     ap_listen_pre_config();
2282     ap_threads_per_child = DEFAULT_START_THREAD;
2283     ap_pid_fname = DEFAULT_PIDLOG;
2284     ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
2285
2286     apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
2287
2288     return OK;
2289 }
2290
2291 static int winnt_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec* s)
2292 {
2293     static int restart_num = 0;
2294     apr_status_t rv = 0;
2295
2296     /* Handle the following SCM aspects in this phase:
2297      *
2298      *   -k install
2299      *   -k config
2300      *   -k start
2301      *   -k restart
2302      *   -k runservice [Win95, only once - after we parsed the config]
2303      *
2304      * because all of these signals are useful _only_ if there
2305      * is a valid conf\httpd.conf environment to start.
2306      *
2307      * We reached this phase by avoiding errors that would cause
2308      * these options to fail unexpectedly in another process.
2309      */
2310
2311     if (!strcasecmp(signal_arg, "install")) {
2312         rv = mpm_service_install(ptemp, inst_argc, inst_argv, 0);
2313         exit (rv);
2314     }
2315     if (!strcasecmp(signal_arg, "config")) {
2316         rv = mpm_service_install(ptemp, inst_argc, inst_argv, 1);
2317         exit (rv);
2318     }
2319
2320     if (!strcasecmp(signal_arg, "start")) {
2321         ap_listen_rec *lr;
2322
2323         /* Close the listening sockets. */
2324         for (lr = ap_listeners; lr; lr = lr->next) {
2325             apr_socket_close(lr->sd);
2326             lr->active = 0;
2327         }
2328         rv = mpm_service_start(ptemp, inst_argc, inst_argv);
2329         exit (rv);
2330     }
2331
2332     if (!strcasecmp(signal_arg, "restart")) {
2333         mpm_signal_service(ptemp, 1);
2334         exit (rv);
2335     }
2336
2337     if (parent_pid == my_pid) 
2338     {
2339         if (restart_num++ == 1) 
2340         {
2341             /* This code should be run once in the parent and not run
2342              * across a restart
2343              */
2344             PSECURITY_ATTRIBUTES sa = GetNullACL();  /* returns NULL if invalid (Win95?) */
2345             setup_signal_names(apr_psprintf(pconf,"ap%d", parent_pid));
2346
2347             ap_log_pid(pconf, ap_pid_fname);
2348             
2349             /* Create shutdown event, apPID_shutdown, where PID is the parent 
2350              * Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
2351              */
2352             shutdown_event = CreateEvent(sa, FALSE, FALSE, signal_shutdown_name);
2353             if (!shutdown_event) {
2354                 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
2355                              "Parent: Cannot create shutdown event %s", signal_shutdown_name);
2356                 CleanNullACL((void *)sa);
2357                 return HTTP_INTERNAL_SERVER_ERROR;
2358             }
2359
2360             /* Create restart event, apPID_restart, where PID is the parent 
2361              * Apache process ID. Restart is signaled by 'apache -k restart'.
2362              */
2363             restart_event = CreateEvent(sa, FALSE, FALSE, signal_restart_name);
2364             if (!restart_event) {
2365                 CloseHandle(shutdown_event);
2366                 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
2367                              "Parent: Cannot create restart event %s", signal_restart_name);
2368                 CleanNullACL((void *)sa);
2369                 return HTTP_INTERNAL_SERVER_ERROR;
2370             }
2371             CleanNullACL((void *)sa);
2372
2373             /* Now that we are flying at 15000 feet... 
2374              * wipe out the Win95 service console,
2375              * signal the SCM the WinNT service started, or
2376              * if not a service, setup console handlers instead.
2377              */
2378             if (!strcasecmp(signal_arg, "runservice"))
2379             {
2380                 if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT) 
2381                 {
2382                     rv = mpm_service_to_start(&service_name,
2383                                               s->process->pool);
2384                     if (rv != APR_SUCCESS) {
2385                         ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
2386                                      "%s: Unable to start the service manager.",
2387                                      service_name);
2388                         return HTTP_INTERNAL_SERVER_ERROR;
2389                     }            
2390                 }
2391             }
2392             else /* ! -k runservice */
2393             {
2394                 mpm_start_console_handler();
2395             }
2396
2397             /* Create the start mutex, apPID, where PID is the parent Apache process ID.
2398              * Ths start mutex is used during a restart to prevent more than one 
2399              * child process from entering the accept loop at once.
2400              */
2401             rv =  apr_proc_mutex_create(&start_mutex, 
2402                                         signal_name_prefix,
2403                                         APR_LOCK_DEFAULT,
2404                                         ap_server_conf->process->pool);
2405             if (rv != APR_SUCCESS) {
2406                 ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
2407                              "%s: Unable to create the start_mutex.",
2408                              service_name);
2409                 return HTTP_INTERNAL_SERVER_ERROR;
2410             }            
2411         }
2412     }
2413     else /* parent_pid != my_pid */
2414     {
2415         mpm_start_child_console_handler();
2416     }
2417     return OK;
2418 }
2419
2420 /* This really should be a post_config hook, but the error log is already
2421  * redirected by that point, so we need to do this in the open_logs phase.
2422  */
2423 static int winnt_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
2424 {
2425     /* Initialize shared static objects. 
2426      */
2427     ap_server_conf = s;
2428
2429     if (parent_pid != my_pid) {
2430         return OK;
2431     }
2432
2433     /* We cannot initialize our listeners if we are restarting
2434      * (the parent process already has glomed on to them)
2435      * nor should we do so for service reconfiguration 
2436      * (since the service may already be running.)
2437      */
2438     if (!strcasecmp(signal_arg, "restart") 
2439             || !strcasecmp(signal_arg, "config")) {
2440         return OK;
2441     }
2442
2443     if (ap_setup_listeners(s) < 1) {
2444         ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, 
2445                      NULL, "no listening sockets available, shutting down");
2446         return DONE;
2447     }
2448
2449     if (!set_listeners_noninheritable(s->process->pool)) {
2450         return 1;
2451     }
2452
2453     return OK;
2454 }
2455
2456 static void winnt_child_init(apr_pool_t *pchild, struct server_rec *s)
2457 {
2458     apr_status_t rv;
2459
2460     setup_signal_names(apr_psprintf(pchild,"ap%d", parent_pid));
2461
2462     /* This is a child process, not in single process mode */
2463     if (!one_process) {
2464         /* Set up events and the scoreboard */
2465         get_handles_from_parent(s);
2466
2467         /* Set up the listeners */
2468         get_listeners_from_parent(s);
2469
2470         ap_my_generation = ap_scoreboard_image->global->running_generation;
2471         rv = apr_proc_mutex_child_init(&start_mutex, signal_name_prefix, 
2472                                        s->process->pool);
2473     }
2474     else {
2475         /* Single process mode - this lock doesn't even need to exist */
2476         rv = apr_proc_mutex_create(&start_mutex, signal_name_prefix, 
2477                                    APR_LOCK_DEFAULT, s->process->pool);
2478         
2479         /* Borrow the shutdown_even as our _child_ loop exit event */
2480         exit_event = shutdown_event;
2481     }
2482     if (rv != APR_SUCCESS) {
2483         ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
2484                      "%s child %d: Unable to init the start_mutex.",
2485                      service_name, my_pid);
2486         exit(APEXIT_CHILDINIT);
2487     }
2488 }
2489
2490
2491 AP_DECLARE(int) ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
2492 {
2493     static int restart = 0;            /* Default is "not a restart" */
2494
2495     /* ### If non-graceful restarts are ever introduced - we need to rerun 
2496      * the pre_mpm hook on subsequent non-graceful restarts.  But Win32 
2497      * has only graceful style restarts - and we need this hook to act 
2498      * the same on Win32 as on Unix.
2499      */
2500     if (!restart && ((parent_pid == my_pid) || one_process)) {
2501         /* Set up the scoreboard. */
2502         if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
2503             return 1;
2504         }
2505     }
2506     
2507     if ((parent_pid != my_pid) || one_process) 
2508     {
2509         /* The child process or in one_process (debug) mode 
2510          */
2511         ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
2512                      "Child %d: Child process is running", my_pid);
2513
2514         child_main();
2515
2516         ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
2517                      "Child %d: Child process is exiting", my_pid);        
2518         return 1;
2519     }
2520     else 
2521     {
2522         /* A real-honest to goodness parent */
2523
2524         restart = master_main(ap_server_conf, shutdown_event, restart_event);
2525
2526         if (!restart) 
2527         {
2528             /* Shutting down. Clean up... */
2529             const char *pidfile = ap_server_root_relative (pconf, ap_pid_fname);
2530
2531             if (pidfile != NULL && unlink(pidfile) == 0) {
2532                 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS,
2533                              ap_server_conf, "removed PID file %s (pid=%ld)",
2534                              pidfile, GetCurrentProcessId());
2535             }
2536             apr_proc_mutex_destroy(start_mutex);
2537
2538             CloseHandle(restart_event);
2539             CloseHandle(shutdown_event);
2540
2541             return 1;
2542         }
2543     }
2544
2545     return 0; /* Restart */
2546 }
2547
2548 static void winnt_hooks(apr_pool_t *p)
2549 {
2550     /* The prefork open_logs phase must run before the core's, or stderr
2551      * will be redirected to a file, and the messages won't print to the
2552      * console.
2553      */
2554     static const char *const aszSucc[] = {"core.c", NULL};
2555
2556     ap_hook_pre_config(winnt_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
2557     ap_hook_post_config(winnt_post_config, NULL, NULL, 0);
2558     ap_hook_child_init(winnt_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2559     ap_hook_open_logs(winnt_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
2560 }
2561
2562 AP_MODULE_DECLARE_DATA module mpm_winnt_module = {
2563     MPM20_MODULE_STUFF,
2564     winnt_rewrite_args,         /* hook to run before apache parses args */
2565     NULL,                       /* create per-directory config structure */
2566     NULL,                       /* merge per-directory config structures */
2567     NULL,                       /* create per-server config structure */
2568     NULL,                       /* merge per-server config structures */
2569     winnt_cmds,                 /* command apr_table_t */
2570     winnt_hooks                 /* register_hooks */
2571 };