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