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
8
* http://www.apache.org/licenses/LICENSE-2.0
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.
21
#include "http_main.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"
32
#include "apr_thread_mutex.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"
40
#include "apr_atomic.h"
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.
46
extern apr_shm_t *ap_scoreboard_shm;
47
server_rec *ap_server_conf;
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 */
53
static char ap_coredump_dir[MAX_STRING_LEN];
55
static int one_process = 0;
56
static char const* signal_arg = NULL;
58
OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
60
static DWORD parent_pid;
63
int ap_threads_per_child = 0;
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;
70
/* ap_my_generation are used by the scoreboard code */
71
ap_generation_t volatile ap_my_generation=0;
74
/* shared by service.c as global, although
75
* perhaps it should be private.
80
/* definitions from child.c */
81
void child_main(apr_pool_t *pconf);
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
87
extern apr_proc_mutex_t *start_mutex;
88
extern HANDLE exit_event;
91
/* Stub functions until this MPM supports the connection status API */
93
AP_DECLARE(void) ap_update_connection_status(long conn_id, const char *key, \
99
AP_DECLARE(void) ap_reset_connection_status(long conn_id)
104
AP_DECLARE(apr_array_header_t *) ap_get_status_table(apr_pool_t *p)
114
static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg)
116
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
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,
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;
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;
141
static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg)
143
int tmp_thread_limit;
145
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
150
tmp_thread_limit = atoi(arg);
151
/* you cannot change ThreadLimit across a restart; ignore
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
160
changed_limit_at_restart = 1;
163
thread_limit = tmp_thread_limit;
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;
173
else if (thread_limit < 1) {
174
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
175
"WARNING: Require ThreadLimit > 0, setting to 1");
180
static const char *set_disable_acceptex(cmd_parms *cmd, void *dummy, char *arg)
182
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
188
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
189
"Disabled use of AcceptEx() WinSock2 API");
194
static const command_rec winnt_cmds[] = {
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"),
208
* Signalling Apache on NT.
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
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
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.
232
* On entry, type gives the event to signal. 0 means shutdown, 1 means
236
* Initialise the signal names, in the global variables signal_name_prefix,
237
* signal_restart_name and signal_shutdown_name.
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)
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);
252
int volatile is_graceful = 0;
254
AP_DECLARE(int) ap_graceful_stop_signalled(void)
259
AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type)
264
if (parent_pid == my_pid) {
266
case SIGNAL_PARENT_SHUTDOWN:
268
SetEvent(shutdown_event);
271
/* This MPM supports only graceful restarts right now */
272
case SIGNAL_PARENT_RESTART:
273
case SIGNAL_PARENT_RESTART_GRACEFUL:
276
SetEvent(restart_event);
284
case SIGNAL_PARENT_SHUTDOWN:
286
signal_name = signal_shutdown_name;
289
/* This MPM supports only graceful restarts right now */
290
case SIGNAL_PARENT_RESTART:
291
case SIGNAL_PARENT_RESTART_GRACEFUL:
293
signal_name = signal_restart_name;
301
e = OpenEvent(EVENT_MODIFY_STATE, FALSE, signal_name);
303
/* Um, problem, can't signal the parent, which means we can't
304
* signal ourselves to die. Ignore for now...
306
ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
307
"OpenEvent on %s event", signal_name);
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);
322
* Passed the following handles [in sync with send_handles_to_child()]
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]
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)
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);
350
SetEvent(ready_event);
351
CloseHandle(ready_event);
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);
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);
368
*child_start_mutex = NULL;
369
if ((rv = apr_os_proc_mutex_put(child_start_mutex, &os_start, s->process->pool))
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);
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);
383
*scoreboard_shm = NULL;
384
if ((rv = apr_os_shm_put(scoreboard_shm, &hScore, s->process->pool))
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);
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);
397
/* We must 'initialize' the scoreboard to relink all the
398
* process-local pointer arrays into the shared memory block.
400
ap_init_scoreboard(sb_shared);
402
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
403
"Child %d: Retrieved our scoreboard from the parent.", my_pid);
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,
413
apr_file_t *child_in)
416
HANDLE hCurrentProcess = GetCurrentProcess();
420
apr_size_t BytesWritten;
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");
428
if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
430
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
431
"Parent: Unable to send the exit event handle to the child");
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");
440
if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
442
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
443
"Parent: Unable to send the exit event handle to the child");
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");
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");
457
if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
459
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
460
"Parent: Unable to send the start mutex to the child");
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");
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");
474
if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
476
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
477
"Parent: Unable to send the scoreboard handle to the child");
481
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
482
"Parent: Sent the scoreboard to the child");
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.
493
void get_listeners_from_parent(server_rec *s)
495
WSAPROTOCOL_INFO WSAProtocolInfo;
502
/* Set up a default listener if necessary */
503
if (ap_listeners == NULL) {
505
lr = apr_palloc(s->process->pool, sizeof(ap_listen_rec));
507
lr->next = ap_listeners;
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.
514
pipe = GetStdHandle(STD_INPUT_HANDLE);
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);
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);
531
if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
532
HANDLE hProcess = GetCurrentProcess();
534
if (DuplicateHandle(hProcess, (HANDLE) nsd, hProcess, &dup,
535
0, FALSE, DUPLICATE_SAME_ACCESS)) {
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...
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.
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.");
555
apr_os_sock_put(&lr->sd, &nsd, s->process->pool);
558
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
559
"Child %d: retrieved %d listeners from parent", my_pid, lcnt);
563
static int send_listeners_to_child(apr_pool_t *p, DWORD dwProcessId,
564
apr_file_t *child_in)
569
LPWSAPROTOCOL_INFO lpWSAProtocolInfo;
570
apr_size_t BytesWritten;
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.
576
for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
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",
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 );
590
if ((rv = apr_file_write_full(child_in, lpWSAProtocolInfo,
591
sizeof(WSAPROTOCOL_INFO), &BytesWritten))
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 );
599
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
600
"Parent: Sent %d listeners to child %d", lcnt, dwProcessId);
609
static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_event,
612
/* These NEVER change for the lifetime of this parent
614
static char **args = NULL;
615
static char **env = NULL;
616
static char pidbuf[28];
620
apr_procattr_t *attr;
621
apr_file_t *child_out;
622
apr_file_t *child_err;
623
apr_proc_t new_child;
625
HANDLE waitlist[2]; /* see waitlist_e */
629
apr_pool_create_ex(&ptemp, p, NULL, NULL);
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...
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");
645
/* Build the args array, only once since it won't change
646
* for the lifetime of this parent process.
648
if ((rv = ap_os_proc_filepath(&cmd, ptemp))
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);
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;
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);
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))
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);
688
/* Connect the child's initial stderr to our main server error log
689
* or share our own stderr handle.
691
if (ap_server_conf->error_log) {
692
child_err = ap_server_conf->error_log;
695
rv = apr_file_open_stderr(&child_err, ptemp);
697
if (rv == APR_SUCCESS) {
698
if ((rv = apr_procattr_child_err_set(attr, child_err, NULL))
700
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
701
"Parent: Unable to connect child stderr.");
702
apr_pool_destroy(ptemp);
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);
716
/* Create the child_exit_event */
717
hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
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]);
728
/* Build the env array, only once since it won't change
729
* for the lifetime of this parent process.
732
for (envc = 0; _environ[envc]; ++envc) {
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);
739
env[envc + 1] = NULL;
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);
753
ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
754
"Parent: Created child process %d", new_child.pid);
756
if (send_handles_to_child(ptemp, waitlist[waitlist_ready], hExitEvent,
757
start_mutex, ap_scoreboard_shm,
758
new_child.hproc, new_child.in)) {
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.
764
SetEvent(hExitEvent);
765
apr_pool_destroy(ptemp);
766
CloseHandle(hExitEvent);
767
CloseHandle(waitlist[waitlist_ready]);
768
CloseHandle(new_child.hproc);
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.
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) {
783
* Outch... that isn't a ready signal. It's dead, Jim!
785
SetEvent(hExitEvent);
786
apr_pool_destroy(ptemp);
787
CloseHandle(hExitEvent);
788
CloseHandle(new_child.hproc);
792
if (send_listeners_to_child(ptemp, new_child.pid, new_child.in)) {
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.
798
SetEvent(hExitEvent);
799
apr_pool_destroy(ptemp);
800
CloseHandle(hExitEvent);
801
CloseHandle(new_child.hproc);
805
*child_exit_event = hExitEvent;
806
*child_proc = new_child.hproc;
807
*child_pid = new_child.pid;
812
/***********************************************************************
814
* master_main() runs in the parent process. It creates the child
815
* process which handles HTTP requests then waits on one of three
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.
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.
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
849
* master_main uses the child_exit_event to signal the child process
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)
860
int shutdown_pending;
861
HANDLE child_exit_event;
862
HANDLE event_handles[NUM_WAIT_HANDLES];
865
restart_pending = shutdown_pending = 0;
867
event_handles[SHUTDOWN_HANDLE] = shutdown_event;
868
event_handles[RESTART_HANDLE] = restart_event;
870
/* Create a single child process */
871
rv = create_process(pconf, &event_handles[CHILD_HANDLE],
872
&child_exit_event, &child_pid);
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;
880
if (!strcasecmp(signal_arg, "runservice")) {
881
mpm_service_started();
884
/* Update the scoreboard. Note that there is only a single active
887
ap_scoreboard_image->parent[0].quiescing = 0;
888
ap_scoreboard_image->parent[0].pid = child_pid;
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;
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;
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)");
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.
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.");
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]);
932
/* Don't wait to verify that the child process really exits,
933
* just move on with the restart.
935
CloseHandle(event_handles[CHILD_HANDLE]);
936
event_handles[CHILD_HANDLE] = NULL;
939
/* The child process exited prematurely due to a fatal error. */
941
if (!GetExitCodeProcess(event_handles[CHILD_HANDLE], &exitcode)) {
942
/* HUH? We did exit, didn't we? */
943
exitcode = APEXIT_CHILDFATAL;
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;
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);
961
CloseHandle(event_handles[CHILD_HANDLE]);
962
event_handles[CHILD_HANDLE] = NULL;
964
if (restart_pending) {
966
ap_scoreboard_image->global->running_generation = ap_my_generation;
969
if (shutdown_pending)
971
int timeout = 30000; /* Timeout is milliseconds */
972
winnt_mpm_state = AP_MPMQ_STOPPING;
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.
978
if (!strcasecmp(signal_arg, "runservice")) {
979
mpm_service_stopping();
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]);
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;
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;
1002
return 0; /* Tell the caller we do not want to restart */
1004
winnt_mpm_state = AP_MPMQ_STARTING;
1005
return 1; /* Tell the caller we want a restart */
1008
/* service_nt_main_fn needs to append the StartService() args
1009
* outside of our call stack and thread as the service starts...
1011
apr_array_header_t *mpm_new_argv;
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.
1018
AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
1021
case AP_MPMQ_MAX_DAEMON_USED:
1022
*result = MAXIMUM_WAIT_OBJECTS;
1024
case AP_MPMQ_IS_THREADED:
1025
*result = AP_MPMQ_STATIC;
1027
case AP_MPMQ_IS_FORKED:
1028
*result = AP_MPMQ_NOT_SUPPORTED;
1030
case AP_MPMQ_HARD_LIMIT_DAEMONS:
1031
*result = HARD_SERVER_LIMIT;
1033
case AP_MPMQ_HARD_LIMIT_THREADS:
1034
*result = thread_limit;
1036
case AP_MPMQ_MAX_THREADS:
1037
*result = ap_threads_per_child;
1039
case AP_MPMQ_MIN_SPARE_DAEMONS:
1042
case AP_MPMQ_MIN_SPARE_THREADS:
1045
case AP_MPMQ_MAX_SPARE_DAEMONS:
1048
case AP_MPMQ_MAX_SPARE_THREADS:
1051
case AP_MPMQ_MAX_REQUESTS_DAEMON:
1052
*result = ap_max_requests_per_child;
1054
case AP_MPMQ_MAX_DAEMONS:
1057
case AP_MPMQ_MPM_STATE:
1058
*result = winnt_mpm_state;
1061
return APR_ENOTIMPL;
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;
1071
void winnt_rewrite_args(process_rec *process)
1073
/* Handle the following SCM aspects in this phase:
1075
* -k runservice [transition for WinNT, nothing for Win9x]
1080
* -k shutdown (same as -k stop). Maintained for backward compatability.
1082
* We can't leave this phase until we know our identity
1083
* and modify the command arguments appropriately.
1085
* We do not care if the .conf file exists or is parsable when
1086
* attempting to stop or uninstall a service.
1089
char *def_server_root;
1096
int running_as_service = 1;
1099
pconf = process->pconf;
1101
osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1102
GetVersionEx(&osver);
1104
/* AP_PARENT_PID is only valid in the child */
1105
pid = getenv("AP_PARENT_PID");
1108
/* This is the child */
1109
my_pid = GetCurrentProcessId();
1110
parent_pid = (DWORD) atol(pid);
1112
/* Prevent holding open the (nonexistant) console */
1113
ap_real_exit_code = 0;
1115
/* The parent is responsible for providing the
1116
* COMPLETE ARGUMENTS REQUIRED to the child.
1118
* No further argument parsing is needed, but
1119
* for good measure we will provide a simple
1120
* signal string for later testing.
1122
signal_arg = "runchild";
1126
/* This is the parent, we have a long way to go :-) */
1127
parent_pid = my_pid = GetCurrentProcessId();
1129
/* This behavior is voided by setting real_exit_code to 0 */
1130
atexit(hold_console_open_on_error);
1132
/* Rewrite process->argv[];
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
1138
* The end result will look like:
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
1145
if ((rv = ap_os_proc_filepath(&binpath, process->pconf))
1147
ap_log_error(APLOG_MARK,APLOG_CRIT, rv, NULL,
1148
"Failed to get the full path of %s", process->argv[0]);
1151
/* WARNING: There is an implict assumption here that the
1152
* executable resides in ServerRoot or ServerRoot\bin
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';
1161
apr_filepath_merge(&def_server_root, NULL, binpath,
1162
APR_FILEPATH_TRUENAME, process->pool);
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.
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;
1175
fixed_args = mpm_new_argv->nelts;
1179
apr_getopt_init(&opt, process->pool, process->argc, (char**) process->argv);
1181
while ((rv = apr_getopt(opt, "wn:k:" AP_SERVER_BASEARGS,
1182
optbuf + 1, &optarg)) == APR_SUCCESS) {
1183
switch (optbuf[1]) {
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!
1189
if (ap_real_exit_code)
1190
ap_real_exit_code = 2;
1194
service_set = mpm_service_set_name(process->pool, &service_name,
1199
signal_arg = optarg;
1204
/* Fall through so the Apache main() handles the 'E' arg */
1206
*(const char **)apr_array_push(mpm_new_argv) =
1207
apr_pstrdup(process->pool, optbuf);
1210
*(const char **)apr_array_push(mpm_new_argv) = optarg;
1216
/* back up to capture the bad argument */
1217
if (rv == APR_BADCH || rv == APR_BADARG) {
1221
while (opt->ind < opt->argc) {
1222
*(const char **)apr_array_push(mpm_new_argv) =
1223
apr_pstrdup(process->pool, opt->argv[opt->ind++]);
1226
/* Track the number of args actually entered by the user */
1227
inst_argc = mpm_new_argv->nelts - fixed_args;
1229
/* Provide a default 'run' -k arg to simplify signal_arg tests */
1233
running_as_service = 0;
1236
if (!strcasecmp(signal_arg, "runservice"))
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
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.
1250
apr_filepath_set(def_server_root, process->pool);
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.
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.
1259
if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
1262
mpm_nt_eventlog_stderr_open(service_name, process->pool);
1264
service_to_start_success = mpm_service_to_start(&service_name,
1266
if (service_to_start_success == APR_SUCCESS) {
1267
service_set = APR_SUCCESS;
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);
1278
if (!strcasecmp(signal_arg, "install")) /* -k install */
1280
if (service_set == APR_SUCCESS)
1282
ap_log_error(APLOG_MARK,APLOG_ERR, 0, NULL,
1283
"%s: Service is already installed.", service_name);
1287
else if (running_as_service)
1289
if (service_set == APR_SUCCESS)
1291
/* Attempt to Uninstall, or stop, before
1292
* we can read the arguments or .conf files
1294
if (!strcasecmp(signal_arg, "uninstall")) {
1295
rv = mpm_service_uninstall();
1299
if ((!strcasecmp(signal_arg, "stop")) ||
1300
(!strcasecmp(signal_arg, "shutdown"))) {
1301
mpm_signal_service(process->pool, 0);
1305
rv = mpm_merge_service_args(process->pool, mpm_new_argv,
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);
1313
ap_log_error(APLOG_MARK,APLOG_WARNING, rv, NULL,
1314
"No installed ConfigArgs for the service "
1315
"\"%s\", using Apache defaults.", service_name);
1320
ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL,
1321
"No installed service named \"%s\".", service_name);
1325
if (strcasecmp(signal_arg, "install") && service_set && service_set != SERVICE_UNSET)
1327
ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL,
1328
"No installed service named \"%s\".", service_name);
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.
1336
inst_argv = (const char * const *)mpm_new_argv->elts
1337
+ mpm_new_argv->nelts - inst_argc;
1339
/* Now, do service install or reconfigure then proceed to
1340
* post_config to test the installed configuration.
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) {
1349
fprintf(stderr,"Testing httpd.conf....\n");
1350
fprintf(stderr,"Errors reported here must be corrected before the "
1351
"service can be started.\n");
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) {
1360
fprintf(stderr,"Testing httpd.conf....\n");
1361
fprintf(stderr,"Errors reported here must be corrected before the "
1362
"service can be started.\n");
1365
process->argc = mpm_new_argv->nelts;
1366
process->argv = (const char * const *) mpm_new_argv->elts;
1370
static int winnt_pre_config(apr_pool_t *pconf_, apr_pool_t *plog, apr_pool_t *ptemp)
1372
/* Handle the following SCM aspects in this phase:
1374
* -k runservice [WinNT errors logged from rewrite_args]
1377
/* Initialize shared static objects.
1378
* TODO: Put config related statics into an sconf structure.
1382
if (ap_exists_config_define("ONE_PROCESS") ||
1383
ap_exists_config_define("DEBUG"))
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.",
1395
/* Win9x: disable AcceptEx */
1396
if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
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;
1407
/* use_acceptex which is enabled by default is not available on Win9x.
1409
if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
1413
apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
1418
static int winnt_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec* s)
1420
static int restart_num = 0;
1421
apr_status_t rv = 0;
1423
/* Handle the following SCM aspects in this phase:
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)
1429
* -k runservice [Win95, only once - after we parsed the config]
1431
* because all of these signals are useful _only_ if there
1432
* is a valid conf\httpd.conf environment to start.
1434
* We reached this phase by avoiding errors that would cause
1435
* these options to fail unexpectedly in another process.
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.
1443
apr_pool_destroy(s->process->pool);
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.
1452
apr_pool_destroy(s->process->pool);
1457
if (!strcasecmp(signal_arg, "start")) {
1460
/* Close the listening sockets. */
1461
for (lr = ap_listeners; lr; lr = lr->next) {
1462
apr_socket_close(lr->sd);
1465
rv = mpm_service_start(ptemp, inst_argc, inst_argv);
1466
apr_pool_destroy(s->process->pool);
1471
if (!strcasecmp(signal_arg, "restart")) {
1472
mpm_signal_service(ptemp, 1);
1473
apr_pool_destroy(s->process->pool);
1478
if (parent_pid == my_pid)
1480
if (restart_num++ == 1)
1482
/* This code should be run once in the parent and not run
1485
PSECURITY_ATTRIBUTES sa = GetNullACL(); /* returns NULL if invalid (Win95?) */
1486
setup_signal_names(apr_psprintf(pconf,"ap%d", parent_pid));
1488
ap_log_pid(pconf, ap_pid_fname);
1490
/* Create shutdown event, apPID_shutdown, where PID is the parent
1491
* Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
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;
1501
/* Create restart event, apPID_restart, where PID is the parent
1502
* Apache process ID. Restart is signaled by 'apache -k restart'.
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;
1512
CleanNullACL((void *)sa);
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.
1519
if (!strcasecmp(signal_arg, "runservice"))
1521
if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
1523
rv = mpm_service_to_start(&service_name,
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.",
1529
return HTTP_INTERNAL_SERVER_ERROR;
1533
else /* ! -k runservice */
1535
mpm_start_console_handler();
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.
1542
rv = apr_proc_mutex_create(&start_mutex, NULL,
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.",
1549
return HTTP_INTERNAL_SERVER_ERROR;
1553
else /* parent_pid != my_pid */
1555
mpm_start_child_console_handler();
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.
1563
static int winnt_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
1565
/* Initialize shared static objects.
1569
if (parent_pid != my_pid) {
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.)
1578
if (!strcasecmp(signal_arg, "restart")
1579
|| !strcasecmp(signal_arg, "config")) {
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");
1592
static void winnt_child_init(apr_pool_t *pchild, struct server_rec *s)
1596
setup_signal_names(apr_psprintf(pchild,"ap%d", parent_pid));
1598
/* This is a child process, not in single process mode */
1600
/* Set up events and the scoreboard */
1601
get_handles_from_parent(s, &exit_event, &start_mutex,
1602
&ap_scoreboard_shm);
1604
/* Set up the listeners */
1605
get_listeners_from_parent(s);
1607
ap_my_generation = ap_scoreboard_image->global->running_generation;
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);
1620
/* Borrow the shutdown_even as our _child_ loop exit event */
1621
exit_event = shutdown_event;
1626
AP_DECLARE(int) ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
1628
static int restart = 0; /* Default is "not a restart" */
1631
first_thread_limit = thread_limit;
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 "
1638
changed_limit_at_restart = 0;
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.
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) {
1653
if ((parent_pid != my_pid) || one_process)
1655
/* The child process or in one_process (debug) mode
1657
ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1658
"Child %d: Child process is running", my_pid);
1662
ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1663
"Child %d: Child process is exiting", my_pid);
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());
1675
restart = master_main(ap_server_conf, shutdown_event, restart_event);
1679
/* Shutting down. Clean up... */
1680
const char *pidfile = ap_server_root_relative (pconf, ap_pid_fname);
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());
1687
apr_proc_mutex_destroy(start_mutex);
1689
CloseHandle(restart_event);
1690
CloseHandle(shutdown_event);
1696
return 0; /* Restart */
1699
static void winnt_hooks(apr_pool_t *p)
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
1705
static const char *const aszSucc[] = {"core.c", NULL};
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);
1713
AP_MODULE_DECLARE_DATA module mpm_winnt_module = {
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 */
1724
#endif /* def WIN32 */