]> granicus.if.org Git - apache/blob - server/mpm/winnt/mpm_winnt.c
Win32: Rename WindowsSocketsWorkaround directive to Win32DisableAcceptEx.
[apache] / server / mpm / winnt / mpm_winnt.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2003 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 #ifdef WIN32
60
61 #define CORE_PRIVATE 
62 #include "httpd.h" 
63 #include "http_main.h" 
64 #include "http_log.h" 
65 #include "http_config.h"        /* for read_config */ 
66 #include "http_core.h"          /* for get_remote_host */ 
67 #include "http_connection.h"
68 #include "apr_portable.h"
69 #include "apr_thread_proc.h"
70 #include "apr_getopt.h"
71 #include "apr_strings.h"
72 #include "apr_lib.h"
73 #include "apr_shm.h"
74 #include "apr_thread_mutex.h"
75 #include "ap_mpm.h"
76 #include "ap_config.h"
77 #include "ap_listen.h"
78 #include "mpm_default.h"
79 #include "mpm_winnt.h"
80 #include "mpm_common.h"
81 #include <malloc.h>
82 #include "apr_atomic.h"
83
84
85 /* scoreboard.c does the heavy lifting; all we do is create the child
86  * score by moving a handle down the pipe into the child's stdin.
87  */
88 extern apr_shm_t *ap_scoreboard_shm;
89 server_rec *ap_server_conf;
90
91 /* Definitions of WINNT MPM specific config globals */
92 static HANDLE shutdown_event;   /* used to signal the parent to shutdown */
93 static HANDLE restart_event;    /* used to signal the parent to restart */
94
95 static char ap_coredump_dir[MAX_STRING_LEN];
96
97 static int one_process = 0;
98 static char const* signal_arg = NULL;
99
100 OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
101
102 static DWORD parent_pid;
103 DWORD my_pid;
104
105 int ap_threads_per_child = 0;
106 int use_acceptex = 1;
107 static int thread_limit = DEFAULT_THREAD_LIMIT;
108 static int first_thread_limit = 0;
109 static int changed_limit_at_restart;
110
111 /* ap_my_generation are used by the scoreboard code */
112 ap_generation_t volatile ap_my_generation=0;
113
114
115 /* shared by service.c as global, although 
116  * perhaps it should be private.
117  */
118 apr_pool_t *pconf;
119
120
121 /* definitions from child.c */
122 void child_main(apr_pool_t *pconf);
123
124 /* used by parent to signal the child to start and exit
125  * NOTE: these are not sophisticated enough for multiple children
126  * so they ultimately should not be shared with child.c
127  */
128 extern apr_proc_mutex_t *start_mutex;
129 extern HANDLE exit_event;  
130
131
132 /* Stub functions until this MPM supports the connection status API */
133
134 AP_DECLARE(void) ap_update_connection_status(long conn_id, const char *key, \
135                                              const char *value)
136 {
137     /* NOP */
138 }
139
140 AP_DECLARE(void) ap_reset_connection_status(long conn_id)
141 {
142     /* NOP */
143 }
144
145 AP_DECLARE(apr_array_header_t *) ap_get_status_table(apr_pool_t *p)
146 {
147     /* NOP */
148     return NULL;
149 }
150
151 /* 
152  * Command processors 
153  */
154
155 static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg) 
156 {
157     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
158     if (err != NULL) {
159         return err;
160     }
161
162     ap_threads_per_child = atoi(arg);
163     if (ap_threads_per_child > thread_limit) {
164         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
165                      "WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
166                      "value of %d threads,", ap_threads_per_child, 
167                      thread_limit);
168         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
169                      " lowering ThreadsPerChild to %d. To increase, please"
170                      " see the", thread_limit);
171         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
172                      " ThreadLimit directive.");
173         ap_threads_per_child = thread_limit;
174     }
175     else if (ap_threads_per_child < 1) {
176         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
177                      "WARNING: Require ThreadsPerChild > 0, setting to 1");
178         ap_threads_per_child = 1;
179     }
180     return NULL;
181 }
182 static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg) 
183 {
184     int tmp_thread_limit;
185     
186     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
187     if (err != NULL) {
188         return err;
189     }
190
191     tmp_thread_limit = atoi(arg);
192     /* you cannot change ThreadLimit across a restart; ignore
193      * any such attempts
194      */
195     if (first_thread_limit &&
196         tmp_thread_limit != thread_limit) {
197         /* how do we log a message?  the error log is a bit bucket at this
198          * point; we'll just have to set a flag so that ap_mpm_run()
199          * logs a warning later
200          */
201         changed_limit_at_restart = 1;
202         return NULL;
203     }
204     thread_limit = tmp_thread_limit;
205     
206     if (thread_limit > MAX_THREAD_LIMIT) {
207        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
208                     "WARNING: ThreadLimit of %d exceeds compile time limit "
209                     "of %d threads,", thread_limit, MAX_THREAD_LIMIT);
210        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
211                     " lowering ThreadLimit to %d.", MAX_THREAD_LIMIT);
212        thread_limit = MAX_THREAD_LIMIT;
213     } 
214     else if (thread_limit < 1) {
215         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
216                      "WARNING: Require ThreadLimit > 0, setting to 1");
217         thread_limit = 1;
218     }
219     return NULL;
220 }
221 static const char *set_disable_acceptex(cmd_parms *cmd, void *dummy, char *arg) 
222 {
223     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
224     if (err != NULL) {
225         return err;
226     }
227     if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
228         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
229                      "Ignoring Win32EnableAcceptEx configuration directive. "
230                      "The directive is not valid on Windows 9x");
231         return NULL;
232     }
233
234     use_acceptex = 0;
235
236     ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
237                          "Disabled use AcceptEx WinSock2 API");
238     return NULL;
239 }
240
241 static const command_rec winnt_cmds[] = {
242 LISTEN_COMMANDS,
243 AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
244   "Number of threads each child creates" ),
245 AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
246   "Maximum worker threads in a server for this run of Apache"),
247 AP_INIT_NO_ARGS("Win32DisableAcceptEx", set_disable_acceptex, NULL, RSRC_CONF,
248   "Disable use of the high performance AcceptEx WinSock2 API to work around buggy VPN or Firewall software"),
249
250 { NULL }
251 };
252
253
254 /*
255  * Signalling Apache on NT.
256  *
257  * Under Unix, Apache can be told to shutdown or restart by sending various
258  * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so
259  * we use "events" instead. The parent apache process goes into a loop
260  * where it waits forever for a set of events. Two of those events are
261  * called
262  *
263  *    apPID_shutdown
264  *    apPID_restart
265  *
266  * (where PID is the PID of the apache parent process). When one of these
267  * is signalled, the Apache parent performs the appropriate action. The events
268  * can become signalled through internal Apache methods (e.g. if the child
269  * finds a fatal error and needs to kill its parent), via the service
270  * control manager (the control thread will signal the shutdown event when
271  * requested to stop the Apache service), from the -k Apache command line,
272  * or from any external program which finds the Apache PID from the
273  * httpd.pid file.
274  *
275  * The signal_parent() function, below, is used to signal one of these events.
276  * It can be called by any child or parent process, since it does not
277  * rely on global variables.
278  *
279  * On entry, type gives the event to signal. 0 means shutdown, 1 means 
280  * graceful restart.
281  */
282 /*
283  * Initialise the signal names, in the global variables signal_name_prefix, 
284  * signal_restart_name and signal_shutdown_name.
285  */
286 #define MAX_SIGNAL_NAME 30  /* Long enough for apPID_shutdown, where PID is an int */
287 char signal_name_prefix[MAX_SIGNAL_NAME];
288 char signal_restart_name[MAX_SIGNAL_NAME]; 
289 char signal_shutdown_name[MAX_SIGNAL_NAME];
290 void setup_signal_names(char *prefix)
291 {
292     apr_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);    
293     apr_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name), 
294         "%s_shutdown", signal_name_prefix);    
295     apr_snprintf(signal_restart_name, sizeof(signal_restart_name), 
296         "%s_restart", signal_name_prefix);    
297 }
298
299 int volatile is_graceful = 0;
300
301 AP_DECLARE(int) ap_graceful_stop_signalled(void)
302 {
303     return is_graceful;
304 }
305
306 AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type)
307 {
308     HANDLE e;
309     char *signal_name;
310     
311     if (parent_pid == my_pid) {
312         switch(type) {
313            case SIGNAL_PARENT_SHUTDOWN: 
314            {
315                SetEvent(shutdown_event); 
316                break;
317            }
318            /* This MPM supports only graceful restarts right now */
319            case SIGNAL_PARENT_RESTART: 
320            case SIGNAL_PARENT_RESTART_GRACEFUL:
321            {
322                is_graceful = 1;
323                SetEvent(restart_event); 
324                break;
325            }
326         }
327         return;
328     }
329
330     switch(type) {
331        case SIGNAL_PARENT_SHUTDOWN: 
332        {
333            signal_name = signal_shutdown_name; 
334            break;
335        }
336        /* This MPM supports only graceful restarts right now */
337        case SIGNAL_PARENT_RESTART: 
338        case SIGNAL_PARENT_RESTART_GRACEFUL:
339        {
340            signal_name = signal_restart_name;     
341            is_graceful = 1;
342            break;
343        }
344        default: 
345            return;
346     }
347
348     e = OpenEvent(EVENT_MODIFY_STATE, FALSE, signal_name);
349     if (!e) {
350         /* Um, problem, can't signal the parent, which means we can't
351          * signal ourselves to die. Ignore for now...
352          */
353         ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
354                      "OpenEvent on %s event", signal_name);
355         return;
356     }
357     if (SetEvent(e) == 0) {
358         /* Same problem as above */
359         ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
360                      "SetEvent on %s event", signal_name);
361         CloseHandle(e);
362         return;
363     }
364     CloseHandle(e);
365 }
366
367
368 /*
369  * Passed the following handles [in sync with send_handles_to_child()]
370  *
371  *   ready event [signal the parent immediately, then close]
372  *   exit event  [save to poll later]
373  *   start mutex [signal from the parent to begin accept()]
374  *   scoreboard shm handle [to recreate the ap_scoreboard]
375  */
376 void get_handles_from_parent(server_rec *s, HANDLE *child_exit_event,
377                              apr_proc_mutex_t **child_start_mutex,
378                              apr_shm_t **scoreboard_shm)
379 {
380     HANDLE pipe;
381     HANDLE hScore;
382     HANDLE ready_event;
383     HANDLE os_start;
384     DWORD BytesRead;
385     void *sb_shared;
386     apr_status_t rv;
387     
388     pipe = GetStdHandle(STD_INPUT_HANDLE);
389     if (!ReadFile(pipe, &ready_event, sizeof(HANDLE),
390                   &BytesRead, (LPOVERLAPPED) NULL)
391         || (BytesRead != sizeof(HANDLE))) {
392         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
393                      "Child %d: Unable to retrieve the ready event from the parent", my_pid);
394         exit(APEXIT_CHILDINIT);
395     }
396
397     SetEvent(ready_event);
398     CloseHandle(ready_event);
399
400     if (!ReadFile(pipe, child_exit_event, sizeof(HANDLE),
401                   &BytesRead, (LPOVERLAPPED) NULL)
402         || (BytesRead != sizeof(HANDLE))) {
403         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
404                      "Child %d: Unable to retrieve the exit event from the parent", my_pid);
405         exit(APEXIT_CHILDINIT);
406     }
407
408     if (!ReadFile(pipe, &os_start, sizeof(os_start),
409                   &BytesRead, (LPOVERLAPPED) NULL)
410         || (BytesRead != sizeof(os_start))) {
411         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
412                      "Child %d: Unable to retrieve the start_mutex from the parent", my_pid);
413         exit(APEXIT_CHILDINIT);
414     }
415     *child_start_mutex = NULL;
416     if ((rv = apr_os_proc_mutex_put(child_start_mutex, &os_start, s->process->pool))
417             != APR_SUCCESS) {
418         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
419                      "Child %d: Unable to access the start_mutex from the parent", my_pid);
420         exit(APEXIT_CHILDINIT);
421     }
422
423     if (!ReadFile(pipe, &hScore, sizeof(hScore),
424                   &BytesRead, (LPOVERLAPPED) NULL)
425         || (BytesRead != sizeof(hScore))) {
426         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
427                      "Child %d: Unable to retrieve the scoreboard from the parent", my_pid);
428         exit(APEXIT_CHILDINIT);
429     }
430     *scoreboard_shm = NULL;
431     if ((rv = apr_os_shm_put(scoreboard_shm, &hScore, s->process->pool)) 
432             != APR_SUCCESS) {
433         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
434                      "Child %d: Unable to access the scoreboard from the parent", my_pid);
435         exit(APEXIT_CHILDINIT);
436     }
437
438     rv = ap_reopen_scoreboard(s->process->pool, scoreboard_shm, 1);
439     if (rv || !(sb_shared = apr_shm_baseaddr_get(*scoreboard_shm))) {
440         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, 
441                      "Child %d: Unable to reopen the scoreboard from the parent", my_pid);
442         exit(APEXIT_CHILDINIT);
443     }
444     /* We must 'initialize' the scoreboard to relink all the
445      * process-local pointer arrays into the shared memory block.
446      */
447     ap_init_scoreboard(sb_shared);
448
449     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
450                  "Child %d: Retrieved our scoreboard from the parent.", my_pid);
451 }
452
453
454 static int send_handles_to_child(apr_pool_t *p, 
455                                  HANDLE child_ready_event,
456                                  HANDLE child_exit_event, 
457                                  apr_proc_mutex_t *child_start_mutex,
458                                  apr_shm_t *scoreboard_shm,
459                                  HANDLE hProcess, 
460                                  apr_file_t *child_in)
461 {
462     apr_status_t rv;
463     HANDLE hCurrentProcess = GetCurrentProcess();
464     HANDLE hDup;
465     HANDLE os_start;
466     HANDLE hScore;
467     DWORD BytesWritten;
468
469     if (!DuplicateHandle(hCurrentProcess, child_ready_event, hProcess, &hDup,
470         EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0)) {
471         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
472                      "Parent: Unable to duplicate the ready event handle for the child");
473         return -1;
474     }
475     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
476             != APR_SUCCESS) {
477         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
478                      "Parent: Unable to send the exit event handle to the child");
479         return -1;
480     }
481     if (!DuplicateHandle(hCurrentProcess, child_exit_event, hProcess, &hDup,
482                          EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0)) {
483         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
484                      "Parent: Unable to duplicate the exit event handle for the child");
485         return -1;
486     }
487     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
488             != APR_SUCCESS) {
489         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
490                      "Parent: Unable to send the exit event handle to the child");
491         return -1;
492     }
493     if ((rv = apr_os_proc_mutex_get(&os_start, child_start_mutex)) != APR_SUCCESS) {
494         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
495                      "Parent: Unable to retrieve the start mutex for the child");
496         return -1;
497     }
498     if (!DuplicateHandle(hCurrentProcess, os_start, hProcess, &hDup,
499                          SYNCHRONIZE, FALSE, 0)) {
500         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
501                      "Parent: Unable to duplicate the start mutex to the child");
502         return -1;
503     }
504     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
505             != APR_SUCCESS) {
506         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
507                      "Parent: Unable to send the start mutex to the child");
508         return -1;
509     }
510     if ((rv = apr_os_shm_get(&hScore, scoreboard_shm)) != APR_SUCCESS) {
511         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
512                      "Parent: Unable to retrieve the scoreboard handle for the child");
513         return -1;
514     }
515     if (!DuplicateHandle(hCurrentProcess, hScore, hProcess, &hDup,
516                          FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0)) {
517         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
518                      "Parent: Unable to duplicate the scoreboard handle to the child");
519         return -1;
520     }
521     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
522             != APR_SUCCESS) {
523         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
524                      "Parent: Unable to send the scoreboard handle to the child");
525         return -1;
526     }
527
528     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
529                  "Parent: Sent the scoreboard to the child");
530     return 0;
531 }
532
533
534 /* 
535  * get_listeners_from_parent()
536  * The listen sockets are opened in the parent. This function, which runs
537  * exclusively in the child process, receives them from the parent and
538  * makes them availeble in the child.
539  */
540 void get_listeners_from_parent(server_rec *s)
541 {
542     WSAPROTOCOL_INFO WSAProtocolInfo;
543     HANDLE pipe;
544     ap_listen_rec *lr;
545     DWORD BytesRead;
546     int lcnt = 0;
547     SOCKET nsd;
548
549     /* Set up a default listener if necessary */
550     if (ap_listeners == NULL) {
551         ap_listen_rec *lr;
552         lr = apr_palloc(s->process->pool, sizeof(ap_listen_rec));
553         lr->sd = NULL;
554         lr->next = ap_listeners;
555         ap_listeners = lr;
556     }
557
558     /* Open the pipe to the parent process to receive the inherited socket
559      * data. The sockets have been set to listening in the parent process.
560      */
561     pipe = GetStdHandle(STD_INPUT_HANDLE);
562
563     for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
564         if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), 
565                       &BytesRead, (LPOVERLAPPED) NULL)) {
566             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
567                          "setup_inherited_listeners: Unable to read socket data from parent");
568             exit(APEXIT_CHILDINIT);
569         }
570         nsd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
571                         &WSAProtocolInfo, 0, 0);
572         if (nsd == INVALID_SOCKET) {
573             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
574                          "Child %d: setup_inherited_listeners(), WSASocket failed to open the inherited socket.", my_pid);
575             exit(APEXIT_CHILDINIT);
576         }
577
578         if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
579             HANDLE hProcess = GetCurrentProcess();
580             HANDLE dup;
581             if (DuplicateHandle(hProcess, (HANDLE) nsd, hProcess, &dup, 
582                                 0, FALSE, DUPLICATE_SAME_ACCESS)) {
583                 closesocket(nsd);
584                 nsd = (SOCKET) dup;
585             }
586         }
587         else {
588             /* A different approach.  Many users report errors such as 
589              * (32538)An operation was attempted on something that is not 
590              * a socket.  : Parent: WSADuplicateSocket failed...
591              *
592              * This appears that the duplicated handle is no longer recognized
593              * as a socket handle.  SetHandleInformation should overcome that
594              * problem by not altering the handle identifier.  But this won't
595              * work on 9x - it's unsupported.
596              */
597             if (!SetHandleInformation((HANDLE)nsd, HANDLE_FLAG_INHERIT, 0)) {
598                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf,
599                              "set_listeners_noninheritable: SetHandleInformation failed.");
600             }
601         }
602         apr_os_sock_put(&lr->sd, &nsd, s->process->pool);
603     }
604
605     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
606                  "Child %d: retrieved %d listeners from parent", my_pid, lcnt);
607 }
608
609
610 static int send_listeners_to_child(apr_pool_t *p, DWORD dwProcessId, 
611                                    apr_file_t *child_in)
612 {
613     apr_status_t rv;
614     int lcnt = 0;
615     ap_listen_rec *lr;
616     LPWSAPROTOCOL_INFO  lpWSAProtocolInfo;
617     DWORD BytesWritten;
618
619     /* Run the chain of open sockets. For each socket, duplicate it 
620      * for the target process then send the WSAPROTOCOL_INFO 
621      * (returned by dup socket) to the child.
622      */
623     for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
624         int nsd;
625         lpWSAProtocolInfo = apr_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
626         apr_os_sock_get(&nsd,lr->sd);
627         ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
628                      "Parent: Duplicating socket %d and sending it to child process %d", 
629                      nsd, dwProcessId);
630         if (WSADuplicateSocket(nsd, dwProcessId,
631                                lpWSAProtocolInfo) == SOCKET_ERROR) {
632             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
633                          "Parent: WSADuplicateSocket failed for socket %d. Check the FAQ.", lr->sd );
634             return -1;
635         }
636
637         if ((rv = apr_file_write_full(child_in, lpWSAProtocolInfo, 
638                                       sizeof(WSAPROTOCOL_INFO), &BytesWritten))
639                 != APR_SUCCESS) {
640             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
641                          "Parent: Unable to write duplicated socket %d to the child.", lr->sd );
642             return -1;
643         }
644     }
645
646     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
647                  "Parent: Sent %d listeners to child %d", lcnt, dwProcessId);
648     return 0;
649 }
650
651 enum waitlist_e {
652     waitlist_ready = 0,
653     waitlist_term = 1
654 };
655
656 static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_event, 
657                           DWORD *child_pid)
658 {
659     /* These NEVER change for the lifetime of this parent 
660      */
661     static char **args = NULL;
662     static char **env = NULL;
663     static char pidbuf[28];
664
665     apr_status_t rv;
666     apr_pool_t *ptemp;
667     apr_procattr_t *attr;
668     apr_file_t *child_out;
669     apr_file_t *child_err;
670     apr_proc_t new_child;
671     HANDLE hExitEvent;
672     HANDLE waitlist[2];  /* see waitlist_e */
673     char *cmd;
674     char *cwd;
675
676     apr_pool_create_ex(&ptemp, p, NULL, NULL);
677
678     /* Build the command line. Should look something like this:
679      * C:/apache/bin/apache.exe -f ap_server_confname 
680      * First, get the path to the executable...
681      */
682     apr_procattr_create(&attr, ptemp);
683     apr_procattr_cmdtype_set(attr, APR_PROGRAM);
684     apr_procattr_detach_set(attr, 1);
685     if (((rv = apr_filepath_get(&cwd, 0, ptemp)) != APR_SUCCESS)
686            || ((rv = apr_procattr_dir_set(attr, cwd)) != APR_SUCCESS)) {
687         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
688                      "Parent: Failed to get the current path");
689     }
690
691     if (!args) {
692         /* Build the args array, only once since it won't change 
693          * for the lifetime of this parent process.
694          */
695         if ((rv = ap_os_proc_filepath(&cmd, ptemp))
696                 != APR_SUCCESS) {
697             ap_log_error(APLOG_MARK, APLOG_CRIT, ERROR_BAD_PATHNAME, ap_server_conf,
698                          "Parent: Failed to get full path of %s", 
699                          ap_server_conf->process->argv[0]);
700             apr_pool_destroy(ptemp);
701             return -1;
702         }
703         
704         args = malloc((ap_server_conf->process->argc + 1) * sizeof (char*));
705         memcpy(args + 1, ap_server_conf->process->argv + 1, 
706                (ap_server_conf->process->argc - 1) * sizeof (char*));
707         args[0] = malloc(strlen(cmd) + 1);
708         strcpy(args[0], cmd);
709         args[ap_server_conf->process->argc] = NULL;
710     }
711     else {
712         cmd = args[0];
713     }
714
715     /* Create a pipe to send handles to the child */
716     if ((rv = apr_procattr_io_set(attr, APR_FULL_BLOCK, 
717                                   APR_NO_PIPE, APR_NO_PIPE)) != APR_SUCCESS) {
718         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
719                         "Parent: Unable to create child stdin pipe.\n");
720         apr_pool_destroy(ptemp);
721         return -1;
722     }
723
724     /* Open a null handle to soak info from the child */
725     if (((rv = apr_file_open(&child_out, "NUL", APR_READ | APR_WRITE, 
726                              APR_OS_DEFAULT, ptemp)) != APR_SUCCESS)
727         || ((rv = apr_procattr_child_out_set(attr, child_out, NULL)) 
728                 != APR_SUCCESS)) {
729         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
730                         "Parent: Unable to connect child stdout to NUL.\n");
731         apr_pool_destroy(ptemp);
732         return -1;
733     }
734
735     /* Connect the child's initial stderr to our main server error log 
736      * or share our own stderr handle.
737      */
738     if (ap_server_conf->error_log) {
739         child_err = ap_server_conf->error_log;
740     }
741     else {
742         rv = apr_file_open_stderr(&child_err, ptemp);
743     }
744     if (rv == APR_SUCCESS) {
745         if ((rv = apr_procattr_child_err_set(attr, child_err, NULL))
746                 != APR_SUCCESS) {
747             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
748                             "Parent: Unable to connect child stderr.\n");
749             apr_pool_destroy(ptemp);
750             return -1;
751         }
752     }
753
754     /* Create the child_ready_event */
755     waitlist[waitlist_ready] = CreateEvent(NULL, TRUE, FALSE, NULL);
756     if (!waitlist[waitlist_ready]) {
757         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
758                      "Parent: Could not create ready event for child process");
759         apr_pool_destroy (ptemp);
760         return -1;
761     }
762
763     /* Create the child_exit_event */
764     hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
765     if (!hExitEvent) {
766         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
767                      "Parent: Could not create exit event for child process");
768         apr_pool_destroy(ptemp);
769         CloseHandle(waitlist[waitlist_ready]);
770         return -1;
771     }
772
773     if (!env) 
774     {
775         /* Build the env array, only once since it won't change 
776          * for the lifetime of this parent process.
777          */
778         int envc;
779         for (envc = 0; _environ[envc]; ++envc) {
780             ;
781         }
782         env = malloc((envc + 2) * sizeof (char*));
783         memcpy(env, _environ, envc * sizeof (char*));
784         apr_snprintf(pidbuf, sizeof(pidbuf), "AP_PARENT_PID=%i", parent_pid);
785         env[envc] = pidbuf;
786         env[envc + 1] = NULL;
787     }
788
789     rv = apr_proc_create(&new_child, cmd, args, env, attr, ptemp);
790     if (rv != APR_SUCCESS) {
791         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
792                      "Parent: Failed to create the child process.");
793         apr_pool_destroy(ptemp);
794         CloseHandle(hExitEvent);
795         CloseHandle(waitlist[waitlist_ready]);
796         CloseHandle(new_child.hproc);
797         return -1;
798     }
799
800     ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
801                  "Parent: Created child process %d", new_child.pid);
802
803     if (send_handles_to_child(ptemp, waitlist[waitlist_ready], hExitEvent,
804                               start_mutex, ap_scoreboard_shm,
805                               new_child.hproc, new_child.in)) {
806         /*
807          * This error is fatal, mop up the child and move on
808          * We toggle the child's exit event to cause this child 
809          * to quit even as it is attempting to start.
810          */
811         SetEvent(hExitEvent);
812         apr_pool_destroy(ptemp);
813         CloseHandle(hExitEvent);
814         CloseHandle(waitlist[waitlist_ready]);
815         CloseHandle(new_child.hproc);
816         return -1;
817     }
818
819     /* Important:
820      * Give the child process a chance to run before dup'ing the sockets.
821      * We have already set the listening sockets noninheritable, but if 
822      * WSADuplicateSocket runs before the child process initializes
823      * the listeners will be inherited anyway.
824      */
825     waitlist[waitlist_term] = new_child.hproc;
826     rv = WaitForMultipleObjects(2, waitlist, FALSE, INFINITE);
827     CloseHandle(waitlist[waitlist_ready]);
828     if (rv != WAIT_OBJECT_0) {
829         /* 
830          * Outch... that isn't a ready signal. It's dead, Jim!
831          */
832         SetEvent(hExitEvent);
833         apr_pool_destroy(ptemp);
834         CloseHandle(hExitEvent);
835         CloseHandle(new_child.hproc);
836         return -1;
837     }
838
839     if (send_listeners_to_child(ptemp, new_child.pid, new_child.in)) {
840         /*
841          * This error is fatal, mop up the child and move on
842          * We toggle the child's exit event to cause this child 
843          * to quit even as it is attempting to start.
844          */
845         SetEvent(hExitEvent);
846         apr_pool_destroy(ptemp);
847         CloseHandle(hExitEvent);
848         CloseHandle(new_child.hproc);
849         return -1;
850     }
851
852     *child_exit_event = hExitEvent;
853     *child_proc = new_child.hproc;
854     *child_pid = new_child.pid;
855
856     return 0;
857 }
858
859 /***********************************************************************
860  * master_main()
861  * master_main() runs in the parent process.  It creates the child 
862  * process which handles HTTP requests then waits on one of three 
863  * events:
864  *
865  * restart_event
866  * -------------
867  * The restart event causes master_main to start a new child process and
868  * tells the old child process to exit (by setting the child_exit_event).
869  * The restart event is set as a result of one of the following:
870  * 1. An apache -k restart command on the command line
871  * 2. A command received from Windows service manager which gets 
872  *    translated into an ap_signal_parent(SIGNAL_PARENT_RESTART)
873  *    call by code in service.c.
874  * 3. The child process calling ap_signal_parent(SIGNAL_PARENT_RESTART)
875  *    as a result of hitting MaxRequestsPerChild.
876  *
877  * shutdown_event 
878  * --------------
879  * The shutdown event causes master_main to tell the child process to 
880  * exit and that the server is shutting down. The shutdown event is
881  * set as a result of one of the following:
882  * 1. An apache -k shutdown command on the command line
883  * 2. A command received from Windows service manager which gets
884  *    translated into an ap_signal_parent(SIGNAL_PARENT_SHUTDOWN)
885  *    call by code in service.c.
886  *
887  * child process handle
888  * --------------------
889  * The child process handle will be signaled if the child process 
890  * exits for any reason. In a normal running server, the signaling
891  * of this event means that the child process has exited prematurely
892  * due to a seg fault or other irrecoverable error. For server
893  * robustness, master_main will restart the child process under this 
894  * condtion.
895  *
896  * master_main uses the child_exit_event to signal the child process
897  * to exit.
898  **********************************************************************/
899 #define NUM_WAIT_HANDLES 3
900 #define CHILD_HANDLE     0
901 #define SHUTDOWN_HANDLE  1
902 #define RESTART_HANDLE   2
903 static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_event)
904 {
905     int rv, cld;
906     int restart_pending;
907     int shutdown_pending;
908     HANDLE child_exit_event;
909     HANDLE event_handles[NUM_WAIT_HANDLES];
910     DWORD child_pid;
911
912     restart_pending = shutdown_pending = 0;
913
914     event_handles[SHUTDOWN_HANDLE] = shutdown_event;
915     event_handles[RESTART_HANDLE] = restart_event;
916
917     /* Create a single child process */
918     rv = create_process(pconf, &event_handles[CHILD_HANDLE], 
919                         &child_exit_event, &child_pid);
920     if (rv < 0) 
921     {
922         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
923                      "master_main: create child process failed. Exiting.");
924         shutdown_pending = 1;
925         goto die_now;
926     }
927     if (!strcasecmp(signal_arg, "runservice")) {
928         mpm_service_started();
929     }
930
931     /* Update the scoreboard. Note that there is only a single active
932      * child at once.
933      */
934     ap_scoreboard_image->parent[0].quiescing = 0;
935     ap_scoreboard_image->parent[0].pid = child_pid;
936
937     /* Wait for shutdown or restart events or for child death */
938     rv = WaitForMultipleObjects(NUM_WAIT_HANDLES, (HANDLE *) event_handles, FALSE, INFINITE);
939     cld = rv - WAIT_OBJECT_0;
940     if (rv == WAIT_FAILED) {
941         /* Something serious is wrong */
942         ap_log_error(APLOG_MARK,APLOG_CRIT, apr_get_os_error(), ap_server_conf,
943                      "master_main: WaitForMultipeObjects WAIT_FAILED -- doing server shutdown");
944         shutdown_pending = 1;
945     }
946     else if (rv == WAIT_TIMEOUT) {
947         /* Hey, this cannot happen */
948         ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
949                      "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT");
950         shutdown_pending = 1;
951     }
952     else if (cld == SHUTDOWN_HANDLE) {
953         /* shutdown_event signalled */
954         shutdown_pending = 1;
955         ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, s, 
956                      "Parent: Received shutdown signal -- Shutting down the server.");
957         if (ResetEvent(shutdown_event) == 0) {
958             ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
959                          "ResetEvent(shutdown_event)");
960         }
961     }
962     else if (cld == RESTART_HANDLE) {
963         /* Received a restart event. Prepare the restart_event to be reused 
964          * then signal the child process to exit. 
965          */
966         restart_pending = 1;
967         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, 
968                      "Parent: Received restart signal -- Restarting the server.");
969         if (ResetEvent(restart_event) == 0) {
970             ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
971                          "Parent: ResetEvent(restart_event) failed.");
972         }
973         if (SetEvent(child_exit_event) == 0) {
974             ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
975                          "Parent: SetEvent for child process %d failed.", 
976                          event_handles[CHILD_HANDLE]);
977         }
978         /* Don't wait to verify that the child process really exits, 
979          * just move on with the restart.
980          */
981         CloseHandle(event_handles[CHILD_HANDLE]);
982         event_handles[CHILD_HANDLE] = NULL;
983     }
984     else {
985         /* The child process exited prematurely due to a fatal error. */
986         DWORD exitcode;
987         if (!GetExitCodeProcess(event_handles[CHILD_HANDLE], &exitcode)) {
988             /* HUH? We did exit, didn't we? */
989             exitcode = APEXIT_CHILDFATAL;
990         }
991         if (   exitcode == APEXIT_CHILDFATAL 
992             || exitcode == APEXIT_CHILDINIT
993             || exitcode == APEXIT_INIT) {
994             ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, 
995                          "Parent: child process exited with status %u -- Aborting.", exitcode);
996         }
997         else {
998             int i;
999             restart_pending = 1;
1000             ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
1001                          "Parent: child process exited with status %u -- Restarting.", exitcode);
1002             for (i = 0; i < ap_threads_per_child; i++) {
1003                 ap_update_child_status_from_indexes(0, i, SERVER_DEAD, NULL);
1004             }
1005         }
1006         CloseHandle(event_handles[CHILD_HANDLE]);
1007         event_handles[CHILD_HANDLE] = NULL;
1008     }
1009     if (restart_pending) {
1010         ++ap_my_generation;
1011         ap_scoreboard_image->global->running_generation = ap_my_generation;
1012     }
1013 die_now:
1014     if (shutdown_pending) 
1015     {
1016         int timeout = 30000;  /* Timeout is milliseconds */
1017
1018         /* This shutdown is only marginally graceful. We will give the 
1019          * child a bit of time to exit gracefully. If the time expires,
1020          * the child will be wacked.
1021          */
1022         if (!strcasecmp(signal_arg, "runservice")) {
1023             mpm_service_stopping();
1024         }
1025         /* Signal the child processes to exit */
1026         if (SetEvent(child_exit_event) == 0) {
1027                 ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_os_error(), ap_server_conf,
1028                              "Parent: SetEvent for child process %d failed", event_handles[CHILD_HANDLE]);
1029         }
1030         if (event_handles[CHILD_HANDLE]) {
1031             rv = WaitForSingleObject(event_handles[CHILD_HANDLE], timeout);
1032             if (rv == WAIT_OBJECT_0) {
1033                 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1034                              "Parent: Child process exited successfully.");
1035                 CloseHandle(event_handles[CHILD_HANDLE]);
1036                 event_handles[CHILD_HANDLE] = NULL;
1037             }
1038             else {
1039                 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1040                              "Parent: Forcing termination of child process %d ", event_handles[CHILD_HANDLE]);
1041                 TerminateProcess(event_handles[CHILD_HANDLE], 1);
1042                 CloseHandle(event_handles[CHILD_HANDLE]);
1043                 event_handles[CHILD_HANDLE] = NULL;
1044             }
1045         }
1046         return 0;  /* Tell the caller we do not want to restart */
1047     }
1048
1049     return 1;      /* Tell the caller we want a restart */
1050 }
1051
1052 /* service_nt_main_fn needs to append the StartService() args 
1053  * outside of our call stack and thread as the service starts...
1054  */
1055 apr_array_header_t *mpm_new_argv;
1056
1057 /* Remember service_to_start failures to log and fail in pre_config.
1058  * Remember inst_argc and inst_argv for installing or starting the
1059  * service after we preflight the config.
1060  */
1061
1062 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
1063 {
1064     switch(query_code){
1065         case AP_MPMQ_MAX_DAEMON_USED:
1066             *result = MAXIMUM_WAIT_OBJECTS;
1067             return APR_SUCCESS;
1068         case AP_MPMQ_IS_THREADED:
1069             *result = AP_MPMQ_STATIC;
1070             return APR_SUCCESS;
1071         case AP_MPMQ_IS_FORKED:
1072             *result = AP_MPMQ_NOT_SUPPORTED;
1073             return APR_SUCCESS;
1074         case AP_MPMQ_HARD_LIMIT_DAEMONS:
1075             *result = HARD_SERVER_LIMIT;
1076             return APR_SUCCESS;
1077         case AP_MPMQ_HARD_LIMIT_THREADS:
1078             *result = thread_limit;
1079             return APR_SUCCESS;
1080         case AP_MPMQ_MAX_THREADS:
1081             *result = ap_threads_per_child;
1082             return APR_SUCCESS;
1083         case AP_MPMQ_MIN_SPARE_DAEMONS:
1084             *result = 0;
1085             return APR_SUCCESS;
1086         case AP_MPMQ_MIN_SPARE_THREADS:    
1087             *result = 0;
1088             return APR_SUCCESS;
1089         case AP_MPMQ_MAX_SPARE_DAEMONS:
1090             *result = 0;
1091             return APR_SUCCESS;
1092         case AP_MPMQ_MAX_SPARE_THREADS:
1093             *result = 0;
1094             return APR_SUCCESS;
1095         case AP_MPMQ_MAX_REQUESTS_DAEMON:
1096             *result = ap_max_requests_per_child;
1097             return APR_SUCCESS;
1098         case AP_MPMQ_MAX_DAEMONS:
1099             *result = 0;
1100             return APR_SUCCESS;
1101     }
1102     return APR_ENOTIMPL;
1103
1104
1105 #define SERVICE_UNSET (-1)
1106 static apr_status_t service_set = SERVICE_UNSET;
1107 static apr_status_t service_to_start_success;
1108 static int inst_argc;
1109 static const char * const *inst_argv;
1110 static char *service_name = NULL;
1111     
1112 void winnt_rewrite_args(process_rec *process) 
1113 {
1114     /* Handle the following SCM aspects in this phase:
1115      *
1116      *   -k runservice [transition for WinNT, nothing for Win9x]
1117      *   -k (!)install [error out if name is not installed]
1118      *   -k uninstall
1119      *   -k stop
1120      *   -k shutdown (same as -k stop). Maintained for backward compatability.
1121      *
1122      * We can't leave this phase until we know our identity
1123      * and modify the command arguments appropriately.
1124      *
1125      * We do not care if the .conf file exists or is parsable when
1126      * attempting to stop or uninstall a service.
1127      */
1128     apr_status_t rv;
1129     char *def_server_root;
1130     char *binpath;
1131     char optbuf[3];
1132     const char *optarg;
1133     int fixed_args;
1134     char *pid;
1135     apr_getopt_t *opt;
1136     int running_as_service = 1;
1137     int errout = 0;
1138
1139     pconf = process->pconf;
1140
1141     osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1142     GetVersionEx(&osver);
1143
1144     /* AP_PARENT_PID is only valid in the child */
1145     pid = getenv("AP_PARENT_PID");
1146     if (pid) 
1147     {
1148         /* This is the child */
1149         my_pid = GetCurrentProcessId();
1150         parent_pid = (DWORD) atol(pid);
1151
1152         /* Prevent holding open the (nonexistant) console */
1153         real_exit_code = 0;
1154
1155         /* The parent is responsible for providing the
1156          * COMPLETE ARGUMENTS REQUIRED to the child.
1157          *
1158          * No further argument parsing is needed, but
1159          * for good measure we will provide a simple
1160          * signal string for later testing.
1161          */
1162         signal_arg = "runchild";
1163         return;
1164     }
1165     
1166     /* This is the parent, we have a long way to go :-) */
1167     parent_pid = my_pid = GetCurrentProcessId();
1168
1169     /* This behavior is voided by setting real_exit_code to 0 */
1170     atexit(hold_console_open_on_error);
1171
1172     /* Rewrite process->argv[]; 
1173      *
1174      * strip out -k signal into signal_arg
1175      * strip out -n servicename and set the names
1176      * add default -d serverroot from the path of this executable
1177      * 
1178      * The end result will look like:
1179      *
1180      * The invocation command (%0)
1181      *     The -d serverroot default from the running executable
1182      *         The requested service's (-n) registry ConfigArgs
1183      *             The WinNT SCM's StartService() args
1184      */
1185     if ((rv = ap_os_proc_filepath(&binpath, process->pconf))
1186             != APR_SUCCESS) {
1187         ap_log_error(APLOG_MARK,APLOG_CRIT, rv, NULL, 
1188                      "Failed to get the full path of %s", process->argv[0]);
1189         exit(APEXIT_INIT);
1190     }
1191     /* WARNING: There is an implict assumption here that the
1192      * executable resides in ServerRoot or ServerRoot\bin
1193      */
1194     def_server_root = (char *) apr_filepath_name_get(binpath);
1195     if (def_server_root > binpath) {
1196         *(def_server_root - 1) = '\0';
1197         def_server_root = (char *) apr_filepath_name_get(binpath);
1198         if (!strcasecmp(def_server_root, "bin"))
1199             *(def_server_root - 1) = '\0';
1200     }
1201     apr_filepath_merge(&def_server_root, NULL, binpath, 
1202                        APR_FILEPATH_TRUENAME, process->pool);
1203
1204     /* Use process->pool so that the rewritten argv
1205      * lasts for the lifetime of the server process,
1206      * because pconf will be destroyed after the 
1207      * initial pre-flight of the config parser.
1208      */
1209     mpm_new_argv = apr_array_make(process->pool, process->argc + 2,
1210                                   sizeof(const char *));
1211     *(const char **)apr_array_push(mpm_new_argv) = process->argv[0];
1212     *(const char **)apr_array_push(mpm_new_argv) = "-d";
1213     *(const char **)apr_array_push(mpm_new_argv) = def_server_root;
1214
1215     fixed_args = mpm_new_argv->nelts;
1216
1217     optbuf[0] = '-';
1218     optbuf[2] = '\0';
1219     apr_getopt_init(&opt, process->pool, process->argc, (char**) process->argv);
1220     opt->errfn = NULL;
1221     while ((rv = apr_getopt(opt, "wn:k:" AP_SERVER_BASEARGS, 
1222                             optbuf + 1, &optarg)) == APR_SUCCESS) {
1223         switch (optbuf[1]) {
1224
1225         /* Shortcuts; include the -w option to hold the window open on error.
1226          * This must not be toggled once we reset real_exit_code to 0!
1227          */
1228         case 'w':
1229             if (real_exit_code)
1230                 real_exit_code = 2;
1231             break;
1232
1233         case 'n':
1234             service_set = mpm_service_set_name(process->pool, &service_name, 
1235                                                optarg);
1236             break;
1237
1238         case 'k':
1239             signal_arg = optarg;
1240             break;
1241
1242         case 'E':
1243             errout = 1;
1244             /* Fall through so the Apache main() handles the 'E' arg */
1245         default:
1246             *(const char **)apr_array_push(mpm_new_argv) =
1247                 apr_pstrdup(process->pool, optbuf);
1248
1249             if (optarg) {
1250                 *(const char **)apr_array_push(mpm_new_argv) = optarg;
1251             }
1252             break;
1253         }
1254     }
1255     
1256     /* back up to capture the bad argument */
1257     if (rv == APR_BADCH || rv == APR_BADARG) {
1258         opt->ind--;
1259     }
1260
1261     while (opt->ind < opt->argc) {
1262         *(const char **)apr_array_push(mpm_new_argv) =
1263             apr_pstrdup(process->pool, opt->argv[opt->ind++]);
1264     }
1265
1266     /* Track the number of args actually entered by the user */
1267     inst_argc = mpm_new_argv->nelts - fixed_args;
1268
1269     /* Provide a default 'run' -k arg to simplify signal_arg tests */
1270     if (!signal_arg)
1271     {
1272         signal_arg = "run";
1273         running_as_service = 0;
1274     }
1275
1276     if (!strcasecmp(signal_arg, "runservice")) 
1277     {
1278         /* Start the NT Service _NOW_ because the WinNT SCM is 
1279          * expecting us to rapidly assume control of our own 
1280          * process, the SCM will tell us our service name, and
1281          * may have extra StartService() command arguments to
1282          * add for us.
1283          *
1284          * The SCM will generally invoke the executable with
1285          * the c:\win\system32 default directory.  This is very
1286          * lethal if folks use ServerRoot /foopath on windows
1287          * without a drive letter.  Change to the default root
1288          * (path to apache root, above /bin) for safety.
1289          */
1290         apr_filepath_set(def_server_root, process->pool);
1291         
1292         /* Any other process has a console, so we don't to begin
1293          * a Win9x service until the configuration is parsed and
1294          * any command line errors are reported.
1295          *
1296          * We hold the return value so that we can die in pre_config
1297          * after logging begins, and the failure can land in the log.
1298          */
1299         if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) 
1300         {
1301             if (!errout) {
1302                 mpm_nt_eventlog_stderr_open(service_name, process->pool);
1303             }
1304             service_to_start_success = mpm_service_to_start(&service_name,
1305                                                             process->pool);
1306             if (service_to_start_success == APR_SUCCESS) {
1307                 service_set = APR_SUCCESS;
1308             }
1309         }
1310     }
1311
1312     /* Get the default for any -k option, except run */
1313     if (service_set == SERVICE_UNSET && strcasecmp(signal_arg, "run")) {
1314         service_set = mpm_service_set_name(process->pool, &service_name,
1315                                            AP_DEFAULT_SERVICE_NAME);
1316     }
1317
1318     if (!strcasecmp(signal_arg, "install")) /* -k install */
1319     {
1320         if (service_set == APR_SUCCESS) 
1321         {
1322             ap_log_error(APLOG_MARK,APLOG_ERR, 0, NULL,
1323                  "%s: Service is already installed.", service_name);
1324             exit(APEXIT_INIT);
1325         }
1326     }
1327     else if (running_as_service)
1328     {
1329         if (service_set == APR_SUCCESS) 
1330         {
1331             /* Attempt to Uninstall, or stop, before 
1332              * we can read the arguments or .conf files
1333              */
1334             if (!strcasecmp(signal_arg, "uninstall")) {
1335                 rv = mpm_service_uninstall();
1336                 exit(rv);
1337             }
1338
1339             if ((!strcasecmp(signal_arg, "stop")) || 
1340                 (!strcasecmp(signal_arg, "shutdown"))) {
1341                 mpm_signal_service(process->pool, 0);
1342                 exit(0);
1343             }
1344
1345             rv = mpm_merge_service_args(process->pool, mpm_new_argv, 
1346                                         fixed_args);
1347             if (rv == APR_SUCCESS) {
1348                 ap_log_error(APLOG_MARK,APLOG_INFO, 0, NULL,
1349                              "Using ConfigArgs of the installed service "
1350                              "\"%s\".", service_name);
1351             }
1352             else  {
1353                 ap_log_error(APLOG_MARK,APLOG_WARNING, rv, NULL,
1354                              "No installed ConfigArgs for the service "
1355                              "\"%s\", using Apache defaults.", service_name);
1356             }
1357         }
1358         else
1359         {
1360             ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL,
1361                  "No installed service named \"%s\".", service_name);
1362             exit(APEXIT_INIT);
1363         }
1364     }
1365     if (strcasecmp(signal_arg, "install") && service_set && service_set != SERVICE_UNSET) 
1366     {
1367         ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL,
1368              "No installed service named \"%s\".", service_name);
1369         exit(APEXIT_INIT);
1370     }
1371     
1372     /* Track the args actually entered by the user.
1373      * These will be used for the -k install parameters, as well as
1374      * for the -k start service override arguments.
1375      */
1376     inst_argv = (const char * const *)mpm_new_argv->elts
1377         + mpm_new_argv->nelts - inst_argc;
1378
1379     process->argc = mpm_new_argv->nelts; 
1380     process->argv = (const char * const *) mpm_new_argv->elts;
1381 }
1382
1383
1384 static int winnt_pre_config(apr_pool_t *pconf_, apr_pool_t *plog, apr_pool_t *ptemp) 
1385 {
1386     /* Handle the following SCM aspects in this phase:
1387      *
1388      *   -k runservice [WinNT errors logged from rewrite_args]
1389      */
1390
1391     /* Initialize shared static objects. 
1392      * TODO: Put config related statics into an sconf structure.
1393      */
1394     pconf = pconf_;
1395
1396     if (ap_exists_config_define("ONE_PROCESS") ||
1397         ap_exists_config_define("DEBUG"))
1398         one_process = -1;
1399
1400     if (!strcasecmp(signal_arg, "runservice")
1401             && (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
1402             && (service_to_start_success != APR_SUCCESS)) {
1403         ap_log_error(APLOG_MARK,APLOG_CRIT, service_to_start_success, NULL, 
1404                      "%s: Unable to start the service manager.",
1405                      service_name);
1406         exit(APEXIT_INIT);
1407     }
1408
1409     ap_listen_pre_config();
1410     ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
1411     ap_pid_fname = DEFAULT_PIDLOG;
1412     ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
1413 #ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
1414         ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
1415 #endif
1416     /* use_acceptex which is enabled by default is not available on Win9x.
1417      */
1418     if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1419         use_acceptex = 0;
1420     }
1421
1422     apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
1423
1424     return OK;
1425 }
1426
1427 static int winnt_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec* s)
1428 {
1429     static int restart_num = 0;
1430     apr_status_t rv = 0;
1431
1432     /* Handle the following SCM aspects in this phase:
1433      *
1434      *   -k install
1435      *   -k config
1436      *   -k start
1437      *   -k restart
1438      *   -k runservice [Win95, only once - after we parsed the config]
1439      *
1440      * because all of these signals are useful _only_ if there
1441      * is a valid conf\httpd.conf environment to start.
1442      *
1443      * We reached this phase by avoiding errors that would cause
1444      * these options to fail unexpectedly in another process.
1445      */
1446
1447     if (!strcasecmp(signal_arg, "install")) {
1448         rv = mpm_service_install(ptemp, inst_argc, inst_argv, 0);
1449         exit (rv);
1450     }
1451     if (!strcasecmp(signal_arg, "config")) {
1452         rv = mpm_service_install(ptemp, inst_argc, inst_argv, 1);
1453         exit (rv);
1454     }
1455
1456     if (!strcasecmp(signal_arg, "start")) {
1457         ap_listen_rec *lr;
1458
1459         /* Close the listening sockets. */
1460         for (lr = ap_listeners; lr; lr = lr->next) {
1461             apr_socket_close(lr->sd);
1462             lr->active = 0;
1463         }
1464         rv = mpm_service_start(ptemp, inst_argc, inst_argv);
1465         exit (rv);
1466     }
1467
1468     if (!strcasecmp(signal_arg, "restart")) {
1469         mpm_signal_service(ptemp, 1);
1470         exit (rv);
1471     }
1472
1473     if (parent_pid == my_pid) 
1474     {
1475         if (restart_num++ == 1) 
1476         {
1477             /* This code should be run once in the parent and not run
1478              * across a restart
1479              */
1480             PSECURITY_ATTRIBUTES sa = GetNullACL();  /* returns NULL if invalid (Win95?) */
1481             setup_signal_names(apr_psprintf(pconf,"ap%d", parent_pid));
1482
1483             ap_log_pid(pconf, ap_pid_fname);
1484             
1485             /* Create shutdown event, apPID_shutdown, where PID is the parent 
1486              * Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
1487              */
1488             shutdown_event = CreateEvent(sa, FALSE, FALSE, signal_shutdown_name);
1489             if (!shutdown_event) {
1490                 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1491                              "Parent: Cannot create shutdown event %s", signal_shutdown_name);
1492                 CleanNullACL((void *)sa);
1493                 return HTTP_INTERNAL_SERVER_ERROR;
1494             }
1495
1496             /* Create restart event, apPID_restart, where PID is the parent 
1497              * Apache process ID. Restart is signaled by 'apache -k restart'.
1498              */
1499             restart_event = CreateEvent(sa, FALSE, FALSE, signal_restart_name);
1500             if (!restart_event) {
1501                 CloseHandle(shutdown_event);
1502                 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1503                              "Parent: Cannot create restart event %s", signal_restart_name);
1504                 CleanNullACL((void *)sa);
1505                 return HTTP_INTERNAL_SERVER_ERROR;
1506             }
1507             CleanNullACL((void *)sa);
1508
1509             /* Now that we are flying at 15000 feet... 
1510              * wipe out the Win95 service console,
1511              * signal the SCM the WinNT service started, or
1512              * if not a service, setup console handlers instead.
1513              */
1514             if (!strcasecmp(signal_arg, "runservice"))
1515             {
1516                 if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT) 
1517                 {
1518                     rv = mpm_service_to_start(&service_name,
1519                                               s->process->pool);
1520                     if (rv != APR_SUCCESS) {
1521                         ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
1522                                      "%s: Unable to start the service manager.",
1523                                      service_name);
1524                         return HTTP_INTERNAL_SERVER_ERROR;
1525                     }
1526                 }
1527             }
1528             else /* ! -k runservice */
1529             {
1530                 mpm_start_console_handler();
1531             }
1532
1533             /* Create the start mutex, as an unnamed object for security.
1534              * Ths start mutex is used during a restart to prevent more than 
1535              * one child process from entering the accept loop at once.
1536              */
1537             rv =  apr_proc_mutex_create(&start_mutex, NULL,
1538                                         APR_LOCK_DEFAULT,
1539                                         ap_server_conf->process->pool);
1540             if (rv != APR_SUCCESS) {
1541                 ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
1542                              "%s: Unable to create the start_mutex.",
1543                              service_name);
1544                 return HTTP_INTERNAL_SERVER_ERROR;
1545             }            
1546         }
1547     }
1548     else /* parent_pid != my_pid */
1549     {
1550         mpm_start_child_console_handler();
1551     }
1552     return OK;
1553 }
1554
1555 /* This really should be a post_config hook, but the error log is already
1556  * redirected by that point, so we need to do this in the open_logs phase.
1557  */
1558 static int winnt_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
1559 {
1560     /* Initialize shared static objects. 
1561      */
1562     ap_server_conf = s;
1563
1564     if (parent_pid != my_pid) {
1565         return OK;
1566     }
1567
1568     /* We cannot initialize our listeners if we are restarting
1569      * (the parent process already has glomed on to them)
1570      * nor should we do so for service reconfiguration 
1571      * (since the service may already be running.)
1572      */
1573     if (!strcasecmp(signal_arg, "restart") 
1574             || !strcasecmp(signal_arg, "config")) {
1575         return OK;
1576     }
1577
1578     if (ap_setup_listeners(s) < 1) {
1579         ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, 
1580                      NULL, "no listening sockets available, shutting down");
1581         return DONE;
1582     }
1583
1584     return OK;
1585 }
1586
1587 static void winnt_child_init(apr_pool_t *pchild, struct server_rec *s)
1588 {
1589     apr_status_t rv;
1590
1591     setup_signal_names(apr_psprintf(pchild,"ap%d", parent_pid));
1592
1593     /* This is a child process, not in single process mode */
1594     if (!one_process) {
1595         /* Set up events and the scoreboard */
1596         get_handles_from_parent(s, &exit_event, &start_mutex, 
1597                                 &ap_scoreboard_shm);
1598
1599         /* Set up the listeners */
1600         get_listeners_from_parent(s);
1601
1602         ap_my_generation = ap_scoreboard_image->global->running_generation;
1603     }
1604     else {
1605         /* Single process mode - this lock doesn't even need to exist */
1606         rv = apr_proc_mutex_create(&start_mutex, signal_name_prefix, 
1607                                    APR_LOCK_DEFAULT, s->process->pool);
1608         if (rv != APR_SUCCESS) {
1609             ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
1610                          "%s child %d: Unable to init the start_mutex.",
1611                          service_name, my_pid);
1612             exit(APEXIT_CHILDINIT);
1613         }
1614         
1615         /* Borrow the shutdown_even as our _child_ loop exit event */
1616         exit_event = shutdown_event;
1617     }
1618 }
1619
1620
1621 AP_DECLARE(int) ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
1622 {
1623     static int restart = 0;            /* Default is "not a restart" */
1624
1625     if (!restart) {
1626         first_thread_limit = thread_limit;
1627     }
1628
1629     if (changed_limit_at_restart) {
1630         ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, ap_server_conf,
1631                      "WARNING: Attempt to change ThreadLimit ignored "
1632                      "during restart");
1633         changed_limit_at_restart = 0;
1634     }
1635     
1636     /* ### If non-graceful restarts are ever introduced - we need to rerun 
1637      * the pre_mpm hook on subsequent non-graceful restarts.  But Win32 
1638      * has only graceful style restarts - and we need this hook to act 
1639      * the same on Win32 as on Unix.
1640      */
1641     if (!restart && ((parent_pid == my_pid) || one_process)) {
1642         /* Set up the scoreboard. */
1643         if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
1644             return 1;
1645         }
1646     }
1647     
1648     if ((parent_pid != my_pid) || one_process) 
1649     {
1650         /* The child process or in one_process (debug) mode 
1651          */
1652         ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1653                      "Child %d: Child process is running", my_pid);
1654
1655         child_main(pconf);
1656
1657         ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1658                      "Child %d: Child process is exiting", my_pid);        
1659         return 1;
1660     }
1661     else 
1662     {
1663         /* A real-honest to goodness parent */
1664         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
1665                      "%s configured -- resuming normal operations",
1666                      ap_get_server_version());
1667         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
1668                      "Server built: %s", ap_get_server_built());
1669
1670         restart = master_main(ap_server_conf, shutdown_event, restart_event);
1671
1672         if (!restart) 
1673         {
1674             /* Shutting down. Clean up... */
1675             const char *pidfile = ap_server_root_relative (pconf, ap_pid_fname);
1676
1677             if (pidfile != NULL && unlink(pidfile) == 0) {
1678                 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS,
1679                              ap_server_conf, "removed PID file %s (pid=%ld)",
1680                              pidfile, GetCurrentProcessId());
1681             }
1682             apr_proc_mutex_destroy(start_mutex);
1683
1684             CloseHandle(restart_event);
1685             CloseHandle(shutdown_event);
1686
1687             return 1;
1688         }
1689     }
1690
1691     return 0; /* Restart */
1692 }
1693
1694 static void winnt_hooks(apr_pool_t *p)
1695 {
1696     /* The prefork open_logs phase must run before the core's, or stderr
1697      * will be redirected to a file, and the messages won't print to the
1698      * console.
1699      */
1700     static const char *const aszSucc[] = {"core.c", NULL};
1701
1702     ap_hook_pre_config(winnt_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
1703     ap_hook_post_config(winnt_post_config, NULL, NULL, 0);
1704     ap_hook_child_init(winnt_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1705     ap_hook_open_logs(winnt_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
1706 }
1707
1708 AP_MODULE_DECLARE_DATA module mpm_winnt_module = {
1709     MPM20_MODULE_STUFF,
1710     winnt_rewrite_args,         /* hook to run before apache parses args */
1711     NULL,                       /* create per-directory config structure */
1712     NULL,                       /* merge per-directory config structures */
1713     NULL,                       /* create per-server config structure */
1714     NULL,                       /* merge per-server config structures */
1715     winnt_cmds,                 /* command apr_table_t */
1716     winnt_hooks                 /* register_hooks */
1717 };
1718
1719 #endif /* def WIN32 */