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.
17
/* Multi-process, multi-threaded MPM for OS/2
20
* - a main, parent process
21
* - a small, static number of child processes
23
* The parent process's job is to manage the child processes. This involves
24
* spawning children as required to ensure there are always ap_daemons_to_start
25
* processes accepting connections.
27
* Each child process consists of a a pool of worker threads and a
28
* main thread that accepts connections & passes them to the workers via
29
* a work queue. The worker thread pool is dynamic, managed by a maintanence
30
* thread so that the number of idle threads is kept between
31
* min_spare_threads & max_spare_threads.
37
- Enforce MaxClients somehow
42
#define INCL_DOSERRORS
44
#include "ap_config.h"
46
#include "mpm_default.h"
47
#include "http_main.h"
49
#include "http_config.h"
50
#include "http_core.h" /* for get_remote_host */
51
#include "http_connection.h"
54
#include "ap_listen.h"
55
#include "apr_portable.h"
56
#include "mpm_common.h"
57
#include "apr_strings.h"
61
/* We don't need many processes,
62
* they're only for redundancy in the event of a crash
64
#define HARD_SERVER_LIMIT 10
66
/* Limit on the total number of threads per process
68
#ifndef HARD_THREAD_LIMIT
69
#define HARD_THREAD_LIMIT 256
72
server_rec *ap_server_conf;
73
static apr_pool_t *pconf = NULL; /* Pool for config stuff */
74
static const char *ap_pid_fname=NULL;
77
static int one_process = 0;
78
static int ap_daemons_to_start = 0;
79
static int ap_thread_limit = 0;
80
static int ap_max_requests_per_child = 0;
81
int ap_min_spare_threads = 0;
82
int ap_max_spare_threads = 0;
84
/* Keep track of a few interesting statistics */
85
int ap_max_daemons_limit = -1;
87
/* volatile just in case */
88
static int volatile shutdown_pending;
89
static int volatile restart_pending;
90
static int volatile is_graceful = 0;
91
ap_generation_t volatile ap_my_generation=0; /* Used by the scoreboard */
92
static int is_parent_process=TRUE;
93
HMTX ap_mpm_accept_mutex = 0;
95
/* An array of these is stored in a shared memory area for passing
96
* sockets from the parent to child processes
99
struct sockaddr_in name;
100
apr_os_sock_t listen_fd;
105
listen_socket_t listeners[1];
108
static char master_main();
109
static void spawn_child(int slot);
110
void ap_mpm_child_main(apr_pool_t *pconf);
111
static void set_signals();
114
int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
116
char *listener_shm_name;
117
parent_info_t *parent_info;
123
DosSetMaxFH(ap_thread_limit * 2);
124
listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getppid());
125
rc = DosGetNamedSharedMem((PPVOID)&parent_info, listener_shm_name, PAG_READ);
126
is_parent_process = rc != 0;
127
ap_scoreboard_fname = apr_psprintf(pconf, "/sharemem/httpd/scoreboard.%d", is_parent_process ? getpid() : getppid());
132
int num_listeners = 0;
134
ap_mpm_accept_mutex = parent_info->accept_mutex;
136
/* Set up a default listener if necessary */
137
if (ap_listeners == NULL) {
138
ap_listen_rec *lr = apr_pcalloc(s->process->pool, sizeof(ap_listen_rec));
140
apr_sockaddr_info_get(&lr->bind_addr, "0.0.0.0", APR_UNSPEC,
141
DEFAULT_HTTP_PORT, 0, s->process->pool);
142
apr_socket_create(&lr->sd, lr->bind_addr->family,
143
SOCK_STREAM, 0, s->process->pool);
146
for (lr = ap_listeners; lr; lr = lr->next) {
148
apr_os_sock_put(&lr->sd, &parent_info->listeners[num_listeners].listen_fd, pconf);
149
apr_socket_addr_get(&sa, APR_LOCAL, lr->sd);
153
DosFreeMem(parent_info);
156
ap_mpm_child_main(pconf);
164
is_parent_process = TRUE;
166
if (ap_setup_listeners(ap_server_conf) < 1) {
167
ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
168
"no listening sockets available, shutting down");
172
ap_log_pid(pconf, ap_pid_fname);
174
restart = master_main();
176
ap_scoreboard_image->global->running_generation = ap_my_generation;
179
const char *pidfile = ap_server_root_relative(pconf, ap_pid_fname);
181
if (pidfile != NULL && remove(pidfile) == 0) {
182
ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS,
183
ap_server_conf, "removed PID file %s (pid=%d)",
187
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
188
"caught SIGTERM, shutting down");
191
} /* Parent process */
193
return 0; /* Restart */
198
/* Main processing of the parent process
199
* returns TRUE if restarting
201
static char master_main()
203
server_rec *s = ap_server_conf;
205
parent_info_t *parent_info;
206
char *listener_shm_name;
207
int listener_num, num_listeners, slot;
210
printf("%s \n", ap_get_server_version());
213
if (ap_setup_listeners(ap_server_conf) < 1) {
214
ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
215
"no listening sockets available, shutting down");
219
/* Allocate a shared memory block for the array of listeners */
220
for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
224
listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getpid());
225
rc = DosAllocSharedMem((PPVOID)&parent_info, listener_shm_name,
226
sizeof(parent_info_t) + num_listeners * sizeof(listen_socket_t),
227
PAG_READ|PAG_WRITE|PAG_COMMIT);
230
ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
231
"failure allocating shared memory, shutting down");
235
/* Store the listener sockets in the shared memory area for our children to see */
236
for (listener_num = 0, lr = ap_listeners; lr; lr = lr->next, listener_num++) {
237
apr_os_sock_get(&parent_info->listeners[listener_num].listen_fd, lr->sd);
240
/* Create mutex to prevent multiple child processes from detecting
241
* a connection with apr_poll()
244
rc = DosCreateMutexSem(NULL, &ap_mpm_accept_mutex, DC_SEM_SHARED, FALSE);
247
ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
248
"failure creating accept mutex, shutting down");
252
parent_info->accept_mutex = ap_mpm_accept_mutex;
254
/* Allocate shared memory for scoreboard */
255
if (ap_scoreboard_image == NULL) {
257
rc = DosAllocSharedMem(&sb_mem, ap_scoreboard_fname,
258
ap_calc_scoreboard_size(),
259
PAG_COMMIT|PAG_READ|PAG_WRITE);
262
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
263
"unable to allocate shared memory for scoreboard , exiting");
267
ap_init_scoreboard(sb_mem);
270
ap_scoreboard_image->global->restart_time = apr_time_now();
271
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
272
"%s configured -- resuming normal operations",
273
ap_get_server_version());
274
ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
275
"Server built: %s", ap_get_server_built());
276
#ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
277
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
278
"AcceptMutex: %s (default: %s)",
279
apr_proc_mutex_name(accept_mutex),
280
apr_proc_mutex_defname());
283
ap_scoreboard_image->parent[0].pid = getpid();
284
ap_mpm_child_main(pconf);
288
while (!restart_pending && !shutdown_pending) {
291
int active_children = 0;
293
/* Count number of active children */
294
for (slot=0; slot < HARD_SERVER_LIMIT; slot++) {
295
active_children += ap_scoreboard_image->parent[slot].pid != 0 &&
296
!ap_scoreboard_image->parent[slot].quiescing;
299
/* Spawn children if needed */
300
for (slot=0; slot < HARD_SERVER_LIMIT && active_children < ap_daemons_to_start; slot++) {
301
if (ap_scoreboard_image->parent[slot].pid == 0) {
307
rc = DosWaitChild(DCWA_PROCESSTREE, DCWW_NOWAIT, &proc_rc, &child_pid, 0);
310
/* A child has terminated, remove its scoreboard entry & terminate if necessary */
311
for (slot=0; ap_scoreboard_image->parent[slot].pid != child_pid && slot < HARD_SERVER_LIMIT; slot++);
313
if (slot < HARD_SERVER_LIMIT) {
314
ap_scoreboard_image->parent[slot].pid = 0;
315
ap_scoreboard_image->parent[slot].quiescing = 0;
317
if (proc_rc.codeTerminate == TC_EXIT) {
318
/* Child terminated normally, check its exit code and
319
* terminate server if child indicates a fatal error
321
if (proc_rc.codeResult == APEXIT_CHILDFATAL)
325
} else if (rc == ERROR_CHILD_NOT_COMPLETE) {
326
/* No child exited, lets sleep for a while.... */
327
apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
331
/* Signal children to shut down, either gracefully or immediately */
332
for (slot=0; slot<HARD_SERVER_LIMIT; slot++) {
333
kill(ap_scoreboard_image->parent[slot].pid, is_graceful ? SIGHUP : SIGTERM);
336
DosFreeMem(parent_info);
337
return restart_pending;
342
static void spawn_child(int slot)
346
char fail_module[100];
347
char progname[CCHMAXPATH];
351
ap_scoreboard_image->parent[slot].generation = ap_my_generation;
352
DosGetInfoBlocks(&ptib, &ppib);
353
DosQueryModuleName(ppib->pib_hmte, sizeof(progname), progname);
354
rc = DosExecPgm(fail_module, sizeof(fail_module), EXEC_ASYNCRESULT,
355
ppib->pib_pchcmd, NULL, &proc_rc, progname);
358
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
359
"error spawning child, slot %d", slot);
362
if (ap_max_daemons_limit < slot) {
363
ap_max_daemons_limit = slot;
366
ap_scoreboard_image->parent[slot].pid = proc_rc.codeTerminate;
371
/* Signal handling routines */
373
static void sig_term(int sig)
375
shutdown_pending = 1;
376
signal(SIGTERM, SIG_DFL);
381
static void sig_restart(int sig)
383
if (sig == SIGUSR1) {
392
static void set_signals()
396
sigemptyset(&sa.sa_mask);
398
sa.sa_handler = sig_term;
400
if (sigaction(SIGTERM, &sa, NULL) < 0)
401
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
403
if (sigaction(SIGINT, &sa, NULL) < 0)
404
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
406
sa.sa_handler = sig_restart;
408
if (sigaction(SIGHUP, &sa, NULL) < 0)
409
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
410
if (sigaction(SIGUSR1, &sa, NULL) < 0)
411
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGUSR1)");
416
/* Enquiry functions used get MPM status info */
418
AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
420
switch (query_code) {
421
case AP_MPMQ_MAX_DAEMON_USED:
422
*result = ap_max_daemons_limit;
424
case AP_MPMQ_IS_THREADED:
425
*result = AP_MPMQ_DYNAMIC;
427
case AP_MPMQ_IS_FORKED:
428
*result = AP_MPMQ_NOT_SUPPORTED;
430
case AP_MPMQ_HARD_LIMIT_DAEMONS:
431
*result = HARD_SERVER_LIMIT;
433
case AP_MPMQ_HARD_LIMIT_THREADS:
434
*result = HARD_THREAD_LIMIT;
436
case AP_MPMQ_MIN_SPARE_DAEMONS:
439
case AP_MPMQ_MAX_SPARE_DAEMONS:
442
case AP_MPMQ_MAX_REQUESTS_DAEMON:
443
*result = ap_max_requests_per_child;
451
int ap_graceful_stop_signalled(void)
458
/* Configuration handling stuff */
460
static int mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
462
one_process = ap_exists_config_define("ONE_PROCESS") ||
463
ap_exists_config_define("DEBUG");
465
ap_listen_pre_config();
466
ap_daemons_to_start = DEFAULT_START_DAEMON;
467
ap_thread_limit = HARD_THREAD_LIMIT;
468
ap_pid_fname = DEFAULT_PIDLOG;
469
ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
470
ap_extended_status = 0;
471
ap_min_spare_threads = DEFAULT_MIN_SPARE_THREAD;
472
ap_max_spare_threads = DEFAULT_MAX_SPARE_THREAD;
473
#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
474
ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
482
static void mpmt_os2_hooks(apr_pool_t *p)
484
ap_hook_pre_config(mpmt_os2_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
489
static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const char *arg)
491
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
497
ap_daemons_to_start = atoi(arg);
503
static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy,
506
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
512
ap_min_spare_threads = atoi(arg);
514
if (ap_min_spare_threads <= 0) {
515
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
516
"WARNING: detected MinSpareThreads set to non-positive.");
517
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
518
"Resetting to 1 to avoid almost certain Apache failure.");
519
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
520
"Please read the documentation.");
521
ap_min_spare_threads = 1;
529
static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy,
532
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
538
ap_max_spare_threads = atoi(arg);
544
static const char *ignore_cmd(cmd_parms *cmd, void *dummy, const char *arg)
551
static const command_rec mpmt_os2_cmds[] = {
553
AP_INIT_TAKE1( "StartServers", set_daemons_to_start, NULL, RSRC_CONF,
554
"Number of child processes launched at server startup" ),
555
AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
556
"Minimum number of idle children, to handle request spikes"),
557
AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
558
"Maximum number of idle children"),
559
AP_INIT_TAKE1("User", ignore_cmd, NULL, RSRC_CONF,
560
"Not applicable on this platform"),
561
AP_INIT_TAKE1("Group", ignore_cmd, NULL, RSRC_CONF,
562
"Not applicable on this platform"),
563
AP_INIT_TAKE1("ScoreBoardFile", ignore_cmd, NULL, RSRC_CONF, \
564
"Not applicable on this platform"),
568
module AP_MODULE_DECLARE_DATA mpm_mpmt_os2_module = {
570
NULL, /* hook to run before apache parses args */
571
NULL, /* create per-directory config structure */
572
NULL, /* merge per-directory config structures */
573
NULL, /* create per-server config structure */
574
NULL, /* merge per-server config structures */
575
mpmt_os2_cmds, /* command apr_table_t */
576
mpmt_os2_hooks, /* register_hooks */