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