]> granicus.if.org Git - apache/blob - server/mpm/winnt/mpm_winnt.c
Win32: Tweak Jeff's patch to correctly handle setting server state in the parent...
[apache] / server / mpm / winnt / mpm_winnt.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2004 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 int winnt_mpm_state = AP_MPMQ_STARTING;
111
112 /* ap_my_generation are used by the scoreboard code */
113 ap_generation_t volatile ap_my_generation=0;
114
115
116 /* shared by service.c as global, although 
117  * perhaps it should be private.
118  */
119 apr_pool_t *pconf;
120
121
122 /* definitions from child.c */
123 void child_main(apr_pool_t *pconf);
124
125 /* used by parent to signal the child to start and exit
126  * NOTE: these are not sophisticated enough for multiple children
127  * so they ultimately should not be shared with child.c
128  */
129 extern apr_proc_mutex_t *start_mutex;
130 extern HANDLE exit_event;  
131
132
133 /* Stub functions until this MPM supports the connection status API */
134
135 AP_DECLARE(void) ap_update_connection_status(long conn_id, const char *key, \
136                                              const char *value)
137 {
138     /* NOP */
139 }
140
141 AP_DECLARE(void) ap_reset_connection_status(long conn_id)
142 {
143     /* NOP */
144 }
145
146 AP_DECLARE(apr_array_header_t *) ap_get_status_table(apr_pool_t *p)
147 {
148     /* NOP */
149     return NULL;
150 }
151
152 /* 
153  * Command processors 
154  */
155
156 static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg) 
157 {
158     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
159     if (err != NULL) {
160         return err;
161     }
162
163     ap_threads_per_child = atoi(arg);
164     if (ap_threads_per_child > thread_limit) {
165         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
166                      "WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
167                      "value of %d threads,", ap_threads_per_child, 
168                      thread_limit);
169         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
170                      " lowering ThreadsPerChild to %d. To increase, please"
171                      " see the", thread_limit);
172         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
173                      " ThreadLimit directive.");
174         ap_threads_per_child = thread_limit;
175     }
176     else if (ap_threads_per_child < 1) {
177         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
178                      "WARNING: Require ThreadsPerChild > 0, setting to 1");
179         ap_threads_per_child = 1;
180     }
181     return NULL;
182 }
183 static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg) 
184 {
185     int tmp_thread_limit;
186     
187     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
188     if (err != NULL) {
189         return err;
190     }
191
192     tmp_thread_limit = atoi(arg);
193     /* you cannot change ThreadLimit across a restart; ignore
194      * any such attempts
195      */
196     if (first_thread_limit &&
197         tmp_thread_limit != thread_limit) {
198         /* how do we log a message?  the error log is a bit bucket at this
199          * point; we'll just have to set a flag so that ap_mpm_run()
200          * logs a warning later
201          */
202         changed_limit_at_restart = 1;
203         return NULL;
204     }
205     thread_limit = tmp_thread_limit;
206     
207     if (thread_limit > MAX_THREAD_LIMIT) {
208        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
209                     "WARNING: ThreadLimit of %d exceeds compile time limit "
210                     "of %d threads,", thread_limit, MAX_THREAD_LIMIT);
211        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
212                     " lowering ThreadLimit to %d.", MAX_THREAD_LIMIT);
213        thread_limit = MAX_THREAD_LIMIT;
214     } 
215     else if (thread_limit < 1) {
216         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, 
217                      "WARNING: Require ThreadLimit > 0, setting to 1");
218         thread_limit = 1;
219     }
220     return NULL;
221 }
222 static const char *set_disable_acceptex(cmd_parms *cmd, void *dummy, char *arg) 
223 {
224     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
225     if (err != NULL) {
226         return err;
227     }
228     if (use_acceptex) {
229         use_acceptex = 0;
230         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, 
231                      "Disabled use of AcceptEx() WinSock2 API");
232     }
233     return NULL;
234 }
235
236 static const command_rec winnt_cmds[] = {
237 LISTEN_COMMANDS,
238 AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
239   "Number of threads each child creates" ),
240 AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
241   "Maximum worker threads in a server for this run of Apache"),
242 AP_INIT_NO_ARGS("Win32DisableAcceptEx", set_disable_acceptex, NULL, RSRC_CONF,
243   "Disable use of the high performance AcceptEx WinSock2 API to work around buggy VPN or Firewall software"),
244
245 { NULL }
246 };
247
248
249 /*
250  * Signalling Apache on NT.
251  *
252  * Under Unix, Apache can be told to shutdown or restart by sending various
253  * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so
254  * we use "events" instead. The parent apache process goes into a loop
255  * where it waits forever for a set of events. Two of those events are
256  * called
257  *
258  *    apPID_shutdown
259  *    apPID_restart
260  *
261  * (where PID is the PID of the apache parent process). When one of these
262  * is signalled, the Apache parent performs the appropriate action. The events
263  * can become signalled through internal Apache methods (e.g. if the child
264  * finds a fatal error and needs to kill its parent), via the service
265  * control manager (the control thread will signal the shutdown event when
266  * requested to stop the Apache service), from the -k Apache command line,
267  * or from any external program which finds the Apache PID from the
268  * httpd.pid file.
269  *
270  * The signal_parent() function, below, is used to signal one of these events.
271  * It can be called by any child or parent process, since it does not
272  * rely on global variables.
273  *
274  * On entry, type gives the event to signal. 0 means shutdown, 1 means 
275  * graceful restart.
276  */
277 /*
278  * Initialise the signal names, in the global variables signal_name_prefix, 
279  * signal_restart_name and signal_shutdown_name.
280  */
281 #define MAX_SIGNAL_NAME 30  /* Long enough for apPID_shutdown, where PID is an int */
282 char signal_name_prefix[MAX_SIGNAL_NAME];
283 char signal_restart_name[MAX_SIGNAL_NAME]; 
284 char signal_shutdown_name[MAX_SIGNAL_NAME];
285 void setup_signal_names(char *prefix)
286 {
287     apr_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);    
288     apr_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name), 
289         "%s_shutdown", signal_name_prefix);    
290     apr_snprintf(signal_restart_name, sizeof(signal_restart_name), 
291         "%s_restart", signal_name_prefix);    
292 }
293
294 int volatile is_graceful = 0;
295
296 AP_DECLARE(int) ap_graceful_stop_signalled(void)
297 {
298     return is_graceful;
299 }
300
301 AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type)
302 {
303     HANDLE e;
304     char *signal_name;
305     
306     if (parent_pid == my_pid) {
307         switch(type) {
308            case SIGNAL_PARENT_SHUTDOWN: 
309            {
310                SetEvent(shutdown_event); 
311                break;
312            }
313            /* This MPM supports only graceful restarts right now */
314            case SIGNAL_PARENT_RESTART: 
315            case SIGNAL_PARENT_RESTART_GRACEFUL:
316            {
317                is_graceful = 1;
318                SetEvent(restart_event); 
319                break;
320            }
321         }
322         return;
323     }
324
325     switch(type) {
326        case SIGNAL_PARENT_SHUTDOWN: 
327        {
328            signal_name = signal_shutdown_name; 
329            break;
330        }
331        /* This MPM supports only graceful restarts right now */
332        case SIGNAL_PARENT_RESTART: 
333        case SIGNAL_PARENT_RESTART_GRACEFUL:
334        {
335            signal_name = signal_restart_name;     
336            is_graceful = 1;
337            break;
338        }
339        default: 
340            return;
341     }
342
343     e = OpenEvent(EVENT_MODIFY_STATE, FALSE, signal_name);
344     if (!e) {
345         /* Um, problem, can't signal the parent, which means we can't
346          * signal ourselves to die. Ignore for now...
347          */
348         ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
349                      "OpenEvent on %s event", signal_name);
350         return;
351     }
352     if (SetEvent(e) == 0) {
353         /* Same problem as above */
354         ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
355                      "SetEvent on %s event", signal_name);
356         CloseHandle(e);
357         return;
358     }
359     CloseHandle(e);
360 }
361
362
363 /*
364  * Passed the following handles [in sync with send_handles_to_child()]
365  *
366  *   ready event [signal the parent immediately, then close]
367  *   exit event  [save to poll later]
368  *   start mutex [signal from the parent to begin accept()]
369  *   scoreboard shm handle [to recreate the ap_scoreboard]
370  */
371 void get_handles_from_parent(server_rec *s, HANDLE *child_exit_event,
372                              apr_proc_mutex_t **child_start_mutex,
373                              apr_shm_t **scoreboard_shm)
374 {
375     HANDLE pipe;
376     HANDLE hScore;
377     HANDLE ready_event;
378     HANDLE os_start;
379     DWORD BytesRead;
380     void *sb_shared;
381     apr_status_t rv;
382     
383     pipe = GetStdHandle(STD_INPUT_HANDLE);
384     if (!ReadFile(pipe, &ready_event, sizeof(HANDLE),
385                   &BytesRead, (LPOVERLAPPED) NULL)
386         || (BytesRead != sizeof(HANDLE))) {
387         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
388                      "Child %d: Unable to retrieve the ready event from the parent", my_pid);
389         exit(APEXIT_CHILDINIT);
390     }
391
392     SetEvent(ready_event);
393     CloseHandle(ready_event);
394
395     if (!ReadFile(pipe, child_exit_event, sizeof(HANDLE),
396                   &BytesRead, (LPOVERLAPPED) NULL)
397         || (BytesRead != sizeof(HANDLE))) {
398         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
399                      "Child %d: Unable to retrieve the exit event from the parent", my_pid);
400         exit(APEXIT_CHILDINIT);
401     }
402
403     if (!ReadFile(pipe, &os_start, sizeof(os_start),
404                   &BytesRead, (LPOVERLAPPED) NULL)
405         || (BytesRead != sizeof(os_start))) {
406         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
407                      "Child %d: Unable to retrieve the start_mutex from the parent", my_pid);
408         exit(APEXIT_CHILDINIT);
409     }
410     *child_start_mutex = NULL;
411     if ((rv = apr_os_proc_mutex_put(child_start_mutex, &os_start, s->process->pool))
412             != APR_SUCCESS) {
413         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
414                      "Child %d: Unable to access the start_mutex from the parent", my_pid);
415         exit(APEXIT_CHILDINIT);
416     }
417
418     if (!ReadFile(pipe, &hScore, sizeof(hScore),
419                   &BytesRead, (LPOVERLAPPED) NULL)
420         || (BytesRead != sizeof(hScore))) {
421         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
422                      "Child %d: Unable to retrieve the scoreboard from the parent", my_pid);
423         exit(APEXIT_CHILDINIT);
424     }
425     *scoreboard_shm = NULL;
426     if ((rv = apr_os_shm_put(scoreboard_shm, &hScore, s->process->pool)) 
427             != APR_SUCCESS) {
428         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
429                      "Child %d: Unable to access the scoreboard from the parent", my_pid);
430         exit(APEXIT_CHILDINIT);
431     }
432
433     rv = ap_reopen_scoreboard(s->process->pool, scoreboard_shm, 1);
434     if (rv || !(sb_shared = apr_shm_baseaddr_get(*scoreboard_shm))) {
435         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, 
436                      "Child %d: Unable to reopen the scoreboard from the parent", my_pid);
437         exit(APEXIT_CHILDINIT);
438     }
439     /* We must 'initialize' the scoreboard to relink all the
440      * process-local pointer arrays into the shared memory block.
441      */
442     ap_init_scoreboard(sb_shared);
443
444     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
445                  "Child %d: Retrieved our scoreboard from the parent.", my_pid);
446 }
447
448
449 static int send_handles_to_child(apr_pool_t *p, 
450                                  HANDLE child_ready_event,
451                                  HANDLE child_exit_event, 
452                                  apr_proc_mutex_t *child_start_mutex,
453                                  apr_shm_t *scoreboard_shm,
454                                  HANDLE hProcess, 
455                                  apr_file_t *child_in)
456 {
457     apr_status_t rv;
458     HANDLE hCurrentProcess = GetCurrentProcess();
459     HANDLE hDup;
460     HANDLE os_start;
461     HANDLE hScore;
462     DWORD BytesWritten;
463
464     if (!DuplicateHandle(hCurrentProcess, child_ready_event, hProcess, &hDup,
465         EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0)) {
466         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
467                      "Parent: Unable to duplicate the ready event handle for the child");
468         return -1;
469     }
470     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
471             != APR_SUCCESS) {
472         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
473                      "Parent: Unable to send the exit event handle to the child");
474         return -1;
475     }
476     if (!DuplicateHandle(hCurrentProcess, child_exit_event, hProcess, &hDup,
477                          EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0)) {
478         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
479                      "Parent: Unable to duplicate the exit event handle for the child");
480         return -1;
481     }
482     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
483             != APR_SUCCESS) {
484         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
485                      "Parent: Unable to send the exit event handle to the child");
486         return -1;
487     }
488     if ((rv = apr_os_proc_mutex_get(&os_start, child_start_mutex)) != APR_SUCCESS) {
489         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
490                      "Parent: Unable to retrieve the start mutex for the child");
491         return -1;
492     }
493     if (!DuplicateHandle(hCurrentProcess, os_start, hProcess, &hDup,
494                          SYNCHRONIZE, FALSE, 0)) {
495         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
496                      "Parent: Unable to duplicate the start mutex to the child");
497         return -1;
498     }
499     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
500             != APR_SUCCESS) {
501         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
502                      "Parent: Unable to send the start mutex to the child");
503         return -1;
504     }
505     if ((rv = apr_os_shm_get(&hScore, scoreboard_shm)) != APR_SUCCESS) {
506         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
507                      "Parent: Unable to retrieve the scoreboard handle for the child");
508         return -1;
509     }
510     if (!DuplicateHandle(hCurrentProcess, hScore, hProcess, &hDup,
511                          FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0)) {
512         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
513                      "Parent: Unable to duplicate the scoreboard handle to the child");
514         return -1;
515     }
516     if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
517             != APR_SUCCESS) {
518         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
519                      "Parent: Unable to send the scoreboard handle to the child");
520         return -1;
521     }
522
523     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
524                  "Parent: Sent the scoreboard to the child");
525     return 0;
526 }
527
528
529 /* 
530  * get_listeners_from_parent()
531  * The listen sockets are opened in the parent. This function, which runs
532  * exclusively in the child process, receives them from the parent and
533  * makes them availeble in the child.
534  */
535 void get_listeners_from_parent(server_rec *s)
536 {
537     WSAPROTOCOL_INFO WSAProtocolInfo;
538     HANDLE pipe;
539     ap_listen_rec *lr;
540     DWORD BytesRead;
541     int lcnt = 0;
542     SOCKET nsd;
543
544     /* Set up a default listener if necessary */
545     if (ap_listeners == NULL) {
546         ap_listen_rec *lr;
547         lr = apr_palloc(s->process->pool, sizeof(ap_listen_rec));
548         lr->sd = NULL;
549         lr->next = ap_listeners;
550         ap_listeners = lr;
551     }
552
553     /* Open the pipe to the parent process to receive the inherited socket
554      * data. The sockets have been set to listening in the parent process.
555      */
556     pipe = GetStdHandle(STD_INPUT_HANDLE);
557
558     for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
559         if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), 
560                       &BytesRead, (LPOVERLAPPED) NULL)) {
561             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
562                          "setup_inherited_listeners: Unable to read socket data from parent");
563             exit(APEXIT_CHILDINIT);
564         }
565         nsd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
566                         &WSAProtocolInfo, 0, 0);
567         if (nsd == INVALID_SOCKET) {
568             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
569                          "Child %d: setup_inherited_listeners(), WSASocket failed to open the inherited socket.", my_pid);
570             exit(APEXIT_CHILDINIT);
571         }
572
573         if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
574             HANDLE hProcess = GetCurrentProcess();
575             HANDLE dup;
576             if (DuplicateHandle(hProcess, (HANDLE) nsd, hProcess, &dup, 
577                                 0, FALSE, DUPLICATE_SAME_ACCESS)) {
578                 closesocket(nsd);
579                 nsd = (SOCKET) dup;
580             }
581         }
582         else {
583             /* A different approach.  Many users report errors such as 
584              * (32538)An operation was attempted on something that is not 
585              * a socket.  : Parent: WSADuplicateSocket failed...
586              *
587              * This appears that the duplicated handle is no longer recognized
588              * as a socket handle.  SetHandleInformation should overcome that
589              * problem by not altering the handle identifier.  But this won't
590              * work on 9x - it's unsupported.
591              */
592             if (!SetHandleInformation((HANDLE)nsd, HANDLE_FLAG_INHERIT, 0)) {
593                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf,
594                              "set_listeners_noninheritable: SetHandleInformation failed.");
595             }
596         }
597         apr_os_sock_put(&lr->sd, &nsd, s->process->pool);
598     }
599
600     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
601                  "Child %d: retrieved %d listeners from parent", my_pid, lcnt);
602 }
603
604
605 static int send_listeners_to_child(apr_pool_t *p, DWORD dwProcessId, 
606                                    apr_file_t *child_in)
607 {
608     apr_status_t rv;
609     int lcnt = 0;
610     ap_listen_rec *lr;
611     LPWSAPROTOCOL_INFO  lpWSAProtocolInfo;
612     DWORD BytesWritten;
613
614     /* Run the chain of open sockets. For each socket, duplicate it 
615      * for the target process then send the WSAPROTOCOL_INFO 
616      * (returned by dup socket) to the child.
617      */
618     for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
619         int nsd;
620         lpWSAProtocolInfo = apr_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
621         apr_os_sock_get(&nsd,lr->sd);
622         ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
623                      "Parent: Duplicating socket %d and sending it to child process %d", 
624                      nsd, dwProcessId);
625         if (WSADuplicateSocket(nsd, dwProcessId,
626                                lpWSAProtocolInfo) == SOCKET_ERROR) {
627             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
628                          "Parent: WSADuplicateSocket failed for socket %d. Check the FAQ.", lr->sd );
629             return -1;
630         }
631
632         if ((rv = apr_file_write_full(child_in, lpWSAProtocolInfo, 
633                                       sizeof(WSAPROTOCOL_INFO), &BytesWritten))
634                 != APR_SUCCESS) {
635             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
636                          "Parent: Unable to write duplicated socket %d to the child.", lr->sd );
637             return -1;
638         }
639     }
640
641     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
642                  "Parent: Sent %d listeners to child %d", lcnt, dwProcessId);
643     return 0;
644 }
645
646 enum waitlist_e {
647     waitlist_ready = 0,
648     waitlist_term = 1
649 };
650
651 static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_event, 
652                           DWORD *child_pid)
653 {
654     /* These NEVER change for the lifetime of this parent 
655      */
656     static char **args = NULL;
657     static char **env = NULL;
658     static char pidbuf[28];
659
660     apr_status_t rv;
661     apr_pool_t *ptemp;
662     apr_procattr_t *attr;
663     apr_file_t *child_out;
664     apr_file_t *child_err;
665     apr_proc_t new_child;
666     HANDLE hExitEvent;
667     HANDLE waitlist[2];  /* see waitlist_e */
668     char *cmd;
669     char *cwd;
670
671     apr_pool_create_ex(&ptemp, p, NULL, NULL);
672
673     /* Build the command line. Should look something like this:
674      * C:/apache/bin/apache.exe -f ap_server_confname 
675      * First, get the path to the executable...
676      */
677     apr_procattr_create(&attr, ptemp);
678     apr_procattr_cmdtype_set(attr, APR_PROGRAM);
679     apr_procattr_detach_set(attr, 1);
680     if (((rv = apr_filepath_get(&cwd, 0, ptemp)) != APR_SUCCESS)
681            || ((rv = apr_procattr_dir_set(attr, cwd)) != APR_SUCCESS)) {
682         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
683                      "Parent: Failed to get the current path");
684     }
685
686     if (!args) {
687         /* Build the args array, only once since it won't change 
688          * for the lifetime of this parent process.
689          */
690         if ((rv = ap_os_proc_filepath(&cmd, ptemp))
691                 != APR_SUCCESS) {
692             ap_log_error(APLOG_MARK, APLOG_CRIT, ERROR_BAD_PATHNAME, ap_server_conf,
693                          "Parent: Failed to get full path of %s", 
694                          ap_server_conf->process->argv[0]);
695             apr_pool_destroy(ptemp);
696             return -1;
697         }
698         
699         args = malloc((ap_server_conf->process->argc + 1) * sizeof (char*));
700         memcpy(args + 1, ap_server_conf->process->argv + 1, 
701                (ap_server_conf->process->argc - 1) * sizeof (char*));
702         args[0] = malloc(strlen(cmd) + 1);
703         strcpy(args[0], cmd);
704         args[ap_server_conf->process->argc] = NULL;
705     }
706     else {
707         cmd = args[0];
708     }
709
710     /* Create a pipe to send handles to the child */
711     if ((rv = apr_procattr_io_set(attr, APR_FULL_BLOCK, 
712                                   APR_NO_PIPE, APR_NO_PIPE)) != APR_SUCCESS) {
713         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
714                         "Parent: Unable to create child stdin pipe.\n");
715         apr_pool_destroy(ptemp);
716         return -1;
717     }
718
719     /* Open a null handle to soak info from the child */
720     if (((rv = apr_file_open(&child_out, "NUL", APR_READ | APR_WRITE, 
721                              APR_OS_DEFAULT, ptemp)) != APR_SUCCESS)
722         || ((rv = apr_procattr_child_out_set(attr, child_out, NULL)) 
723                 != APR_SUCCESS)) {
724         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
725                         "Parent: Unable to connect child stdout to NUL.\n");
726         apr_pool_destroy(ptemp);
727         return -1;
728     }
729
730     /* Connect the child's initial stderr to our main server error log 
731      * or share our own stderr handle.
732      */
733     if (ap_server_conf->error_log) {
734         child_err = ap_server_conf->error_log;
735     }
736     else {
737         rv = apr_file_open_stderr(&child_err, ptemp);
738     }
739     if (rv == APR_SUCCESS) {
740         if ((rv = apr_procattr_child_err_set(attr, child_err, NULL))
741                 != APR_SUCCESS) {
742             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
743                             "Parent: Unable to connect child stderr.\n");
744             apr_pool_destroy(ptemp);
745             return -1;
746         }
747     }
748
749     /* Create the child_ready_event */
750     waitlist[waitlist_ready] = CreateEvent(NULL, TRUE, FALSE, NULL);
751     if (!waitlist[waitlist_ready]) {
752         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
753                      "Parent: Could not create ready event for child process");
754         apr_pool_destroy (ptemp);
755         return -1;
756     }
757
758     /* Create the child_exit_event */
759     hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
760     if (!hExitEvent) {
761         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
762                      "Parent: Could not create exit event for child process");
763         apr_pool_destroy(ptemp);
764         CloseHandle(waitlist[waitlist_ready]);
765         return -1;
766     }
767
768     if (!env) 
769     {
770         /* Build the env array, only once since it won't change 
771          * for the lifetime of this parent process.
772          */
773         int envc;
774         for (envc = 0; _environ[envc]; ++envc) {
775             ;
776         }
777         env = malloc((envc + 2) * sizeof (char*));
778         memcpy(env, _environ, envc * sizeof (char*));
779         apr_snprintf(pidbuf, sizeof(pidbuf), "AP_PARENT_PID=%i", parent_pid);
780         env[envc] = pidbuf;
781         env[envc + 1] = NULL;
782     }
783
784     rv = apr_proc_create(&new_child, cmd, args, env, attr, ptemp);
785     if (rv != APR_SUCCESS) {
786         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
787                      "Parent: Failed to create the child process.");
788         apr_pool_destroy(ptemp);
789         CloseHandle(hExitEvent);
790         CloseHandle(waitlist[waitlist_ready]);
791         CloseHandle(new_child.hproc);
792         return -1;
793     }
794
795     ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
796                  "Parent: Created child process %d", new_child.pid);
797
798     if (send_handles_to_child(ptemp, waitlist[waitlist_ready], hExitEvent,
799                               start_mutex, ap_scoreboard_shm,
800                               new_child.hproc, new_child.in)) {
801         /*
802          * This error is fatal, mop up the child and move on
803          * We toggle the child's exit event to cause this child 
804          * to quit even as it is attempting to start.
805          */
806         SetEvent(hExitEvent);
807         apr_pool_destroy(ptemp);
808         CloseHandle(hExitEvent);
809         CloseHandle(waitlist[waitlist_ready]);
810         CloseHandle(new_child.hproc);
811         return -1;
812     }
813
814     /* Important:
815      * Give the child process a chance to run before dup'ing the sockets.
816      * We have already set the listening sockets noninheritable, but if 
817      * WSADuplicateSocket runs before the child process initializes
818      * the listeners will be inherited anyway.
819      */
820     waitlist[waitlist_term] = new_child.hproc;
821     rv = WaitForMultipleObjects(2, waitlist, FALSE, INFINITE);
822     CloseHandle(waitlist[waitlist_ready]);
823     if (rv != WAIT_OBJECT_0) {
824         /* 
825          * Outch... that isn't a ready signal. It's dead, Jim!
826          */
827         SetEvent(hExitEvent);
828         apr_pool_destroy(ptemp);
829         CloseHandle(hExitEvent);
830         CloseHandle(new_child.hproc);
831         return -1;
832     }
833
834     if (send_listeners_to_child(ptemp, new_child.pid, new_child.in)) {
835         /*
836          * This error is fatal, mop up the child and move on
837          * We toggle the child's exit event to cause this child 
838          * to quit even as it is attempting to start.
839          */
840         SetEvent(hExitEvent);
841         apr_pool_destroy(ptemp);
842         CloseHandle(hExitEvent);
843         CloseHandle(new_child.hproc);
844         return -1;
845     }
846
847     *child_exit_event = hExitEvent;
848     *child_proc = new_child.hproc;
849     *child_pid = new_child.pid;
850
851     return 0;
852 }
853
854 /***********************************************************************
855  * master_main()
856  * master_main() runs in the parent process.  It creates the child 
857  * process which handles HTTP requests then waits on one of three 
858  * events:
859  *
860  * restart_event
861  * -------------
862  * The restart event causes master_main to start a new child process and
863  * tells the old child process to exit (by setting the child_exit_event).
864  * The restart event is set as a result of one of the following:
865  * 1. An apache -k restart command on the command line
866  * 2. A command received from Windows service manager which gets 
867  *    translated into an ap_signal_parent(SIGNAL_PARENT_RESTART)
868  *    call by code in service.c.
869  * 3. The child process calling ap_signal_parent(SIGNAL_PARENT_RESTART)
870  *    as a result of hitting MaxRequestsPerChild.
871  *
872  * shutdown_event 
873  * --------------
874  * The shutdown event causes master_main to tell the child process to 
875  * exit and that the server is shutting down. The shutdown event is
876  * set as a result of one of the following:
877  * 1. An apache -k shutdown command on the command line
878  * 2. A command received from Windows service manager which gets
879  *    translated into an ap_signal_parent(SIGNAL_PARENT_SHUTDOWN)
880  *    call by code in service.c.
881  *
882  * child process handle
883  * --------------------
884  * The child process handle will be signaled if the child process 
885  * exits for any reason. In a normal running server, the signaling
886  * of this event means that the child process has exited prematurely
887  * due to a seg fault or other irrecoverable error. For server
888  * robustness, master_main will restart the child process under this 
889  * condtion.
890  *
891  * master_main uses the child_exit_event to signal the child process
892  * to exit.
893  **********************************************************************/
894 #define NUM_WAIT_HANDLES 3
895 #define CHILD_HANDLE     0
896 #define SHUTDOWN_HANDLE  1
897 #define RESTART_HANDLE   2
898 static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_event)
899 {
900     int rv, cld;
901     int restart_pending;
902     int shutdown_pending;
903     HANDLE child_exit_event;
904     HANDLE event_handles[NUM_WAIT_HANDLES];
905     DWORD child_pid;
906
907     restart_pending = shutdown_pending = 0;
908
909     event_handles[SHUTDOWN_HANDLE] = shutdown_event;
910     event_handles[RESTART_HANDLE] = restart_event;
911
912     /* Create a single child process */
913     rv = create_process(pconf, &event_handles[CHILD_HANDLE], 
914                         &child_exit_event, &child_pid);
915     if (rv < 0) 
916     {
917         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
918                      "master_main: create child process failed. Exiting.");
919         shutdown_pending = 1;
920         goto die_now;
921     }
922     if (!strcasecmp(signal_arg, "runservice")) {
923         mpm_service_started();
924     }
925
926     /* Update the scoreboard. Note that there is only a single active
927      * child at once.
928      */
929     ap_scoreboard_image->parent[0].quiescing = 0;
930     ap_scoreboard_image->parent[0].pid = child_pid;
931
932     /* Wait for shutdown or restart events or for child death */
933     winnt_mpm_state = AP_MPMQ_RUNNING;
934     rv = WaitForMultipleObjects(NUM_WAIT_HANDLES, (HANDLE *) event_handles, FALSE, INFINITE);
935     cld = rv - WAIT_OBJECT_0;
936     if (rv == WAIT_FAILED) {
937         /* Something serious is wrong */
938         ap_log_error(APLOG_MARK,APLOG_CRIT, apr_get_os_error(), ap_server_conf,
939                      "master_main: WaitForMultipeObjects WAIT_FAILED -- doing server shutdown");
940         shutdown_pending = 1;
941     }
942     else if (rv == WAIT_TIMEOUT) {
943         /* Hey, this cannot happen */
944         ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
945                      "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT");
946         shutdown_pending = 1;
947     }
948     else if (cld == SHUTDOWN_HANDLE) {
949         /* shutdown_event signalled */
950         shutdown_pending = 1;
951         ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, s, 
952                      "Parent: Received shutdown signal -- Shutting down the server.");
953         if (ResetEvent(shutdown_event) == 0) {
954             ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
955                          "ResetEvent(shutdown_event)");
956         }
957     }
958     else if (cld == RESTART_HANDLE) {
959         /* Received a restart event. Prepare the restart_event to be reused 
960          * then signal the child process to exit. 
961          */
962         restart_pending = 1;
963         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, 
964                      "Parent: Received restart signal -- Restarting the server.");
965         if (ResetEvent(restart_event) == 0) {
966             ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
967                          "Parent: ResetEvent(restart_event) failed.");
968         }
969         if (SetEvent(child_exit_event) == 0) {
970             ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
971                          "Parent: SetEvent for child process %d failed.", 
972                          event_handles[CHILD_HANDLE]);
973         }
974         /* Don't wait to verify that the child process really exits, 
975          * just move on with the restart.
976          */
977         CloseHandle(event_handles[CHILD_HANDLE]);
978         event_handles[CHILD_HANDLE] = NULL;
979     }
980     else {
981         /* The child process exited prematurely due to a fatal error. */
982         DWORD exitcode;
983         if (!GetExitCodeProcess(event_handles[CHILD_HANDLE], &exitcode)) {
984             /* HUH? We did exit, didn't we? */
985             exitcode = APEXIT_CHILDFATAL;
986         }
987         if (   exitcode == APEXIT_CHILDFATAL 
988             || exitcode == APEXIT_CHILDINIT
989             || exitcode == APEXIT_INIT) {
990             ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, 
991                          "Parent: child process exited with status %u -- Aborting.", exitcode);
992         }
993         else {
994             int i;
995             restart_pending = 1;
996             ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, 
997                          "Parent: child process exited with status %u -- Restarting.", exitcode);
998             for (i = 0; i < ap_threads_per_child; i++) {
999                 ap_update_child_status_from_indexes(0, i, SERVER_DEAD, NULL);
1000             }
1001         }
1002         CloseHandle(event_handles[CHILD_HANDLE]);
1003         event_handles[CHILD_HANDLE] = NULL;
1004     }
1005     if (restart_pending) {
1006         ++ap_my_generation;
1007         ap_scoreboard_image->global->running_generation = ap_my_generation;
1008     }
1009 die_now:
1010     if (shutdown_pending) 
1011     {
1012         int timeout = 30000;  /* Timeout is milliseconds */
1013         winnt_mpm_state = AP_MPMQ_STOPPING;
1014
1015         /* This shutdown is only marginally graceful. We will give the 
1016          * child a bit of time to exit gracefully. If the time expires,
1017          * the child will be wacked.
1018          */
1019         if (!strcasecmp(signal_arg, "runservice")) {
1020             mpm_service_stopping();
1021         }
1022         /* Signal the child processes to exit */
1023         if (SetEvent(child_exit_event) == 0) {
1024                 ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_os_error(), ap_server_conf,
1025                              "Parent: SetEvent for child process %d failed", event_handles[CHILD_HANDLE]);
1026         }
1027         if (event_handles[CHILD_HANDLE]) {
1028             rv = WaitForSingleObject(event_handles[CHILD_HANDLE], timeout);
1029             if (rv == WAIT_OBJECT_0) {
1030                 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1031                              "Parent: Child process exited successfully.");
1032                 CloseHandle(event_handles[CHILD_HANDLE]);
1033                 event_handles[CHILD_HANDLE] = NULL;
1034             }
1035             else {
1036                 ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1037                              "Parent: Forcing termination of child process %d ", event_handles[CHILD_HANDLE]);
1038                 TerminateProcess(event_handles[CHILD_HANDLE], 1);
1039                 CloseHandle(event_handles[CHILD_HANDLE]);
1040                 event_handles[CHILD_HANDLE] = NULL;
1041             }
1042         }
1043         return 0;  /* Tell the caller we do not want to restart */
1044     }
1045     winnt_mpm_state = AP_MPMQ_STARTING;
1046     return 1;      /* Tell the caller we want a restart */
1047 }
1048
1049 /* service_nt_main_fn needs to append the StartService() args 
1050  * outside of our call stack and thread as the service starts...
1051  */
1052 apr_array_header_t *mpm_new_argv;
1053
1054 /* Remember service_to_start failures to log and fail in pre_config.
1055  * Remember inst_argc and inst_argv for installing or starting the
1056  * service after we preflight the config.
1057  */
1058
1059 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
1060 {
1061     switch(query_code){
1062         case AP_MPMQ_MAX_DAEMON_USED:
1063             *result = MAXIMUM_WAIT_OBJECTS;
1064             return APR_SUCCESS;
1065         case AP_MPMQ_IS_THREADED:
1066             *result = AP_MPMQ_STATIC;
1067             return APR_SUCCESS;
1068         case AP_MPMQ_IS_FORKED:
1069             *result = AP_MPMQ_NOT_SUPPORTED;
1070             return APR_SUCCESS;
1071         case AP_MPMQ_HARD_LIMIT_DAEMONS:
1072             *result = HARD_SERVER_LIMIT;
1073             return APR_SUCCESS;
1074         case AP_MPMQ_HARD_LIMIT_THREADS:
1075             *result = thread_limit;
1076             return APR_SUCCESS;
1077         case AP_MPMQ_MAX_THREADS:
1078             *result = ap_threads_per_child;
1079             return APR_SUCCESS;
1080         case AP_MPMQ_MIN_SPARE_DAEMONS:
1081             *result = 0;
1082             return APR_SUCCESS;
1083         case AP_MPMQ_MIN_SPARE_THREADS:    
1084             *result = 0;
1085             return APR_SUCCESS;
1086         case AP_MPMQ_MAX_SPARE_DAEMONS:
1087             *result = 0;
1088             return APR_SUCCESS;
1089         case AP_MPMQ_MAX_SPARE_THREADS:
1090             *result = 0;
1091             return APR_SUCCESS;
1092         case AP_MPMQ_MAX_REQUESTS_DAEMON:
1093             *result = ap_max_requests_per_child;
1094             return APR_SUCCESS;
1095         case AP_MPMQ_MAX_DAEMONS:
1096             *result = 0;
1097             return APR_SUCCESS;
1098         case AP_MPMQ_MPM_STATE:
1099             *result = winnt_mpm_state;
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 */