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