1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * ====================================================================
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
54 * Portions of this software are based upon public domain software
55 * originally written at the National Center for Supercomputing Applications,
56 * University of Illinois, Urbana-Champaign.
59 /* Multi-process, multi-threaded MPM for OS/2
62 * - a main, parent process
63 * - a small, static number of child processes
65 * The parent process's job is to manage the child processes. This involves
66 * spawning children as required to ensure there are always ap_daemons_to_start
67 * processes accepting connections.
69 * Each child process consists of a a pool of worker threads and a
70 * main thread that accepts connections & passes them to the workers via
71 * a work queue. The worker thread pool is dynamic, managed by a maintanence
72 * thread so that the number of idle threads is kept between
73 * min_spare_threads & max_spare_threads.
79 - Enforce MaxClients somehow
84 #define INCL_DOSERRORS
86 #include "ap_config.h"
88 #include "mpm_default.h"
89 #include "http_main.h"
91 #include "http_config.h"
92 #include "http_core.h" /* for get_remote_host */
93 #include "http_connection.h"
96 #include "ap_listen.h"
97 #include "apr_portable.h"
98 #include "mpm_common.h"
99 #include "apr_strings.h"
103 /* We don't need many processes,
104 * they're only for redundancy in the event of a crash
106 #define HARD_SERVER_LIMIT 10
108 /* Limit on the total number of threads per process
110 #ifndef HARD_THREAD_LIMIT
111 #define HARD_THREAD_LIMIT 256
114 server_rec *ap_server_conf;
115 static apr_pool_t *pconf = NULL; /* Pool for config stuff */
116 static const char *ap_pid_fname=NULL;
119 static int one_process = 0;
120 static int ap_daemons_to_start = 0;
121 static int ap_thread_limit = 0;
122 static int ap_max_requests_per_child = 0;
123 int ap_min_spare_threads = 0;
124 int ap_max_spare_threads = 0;
126 /* Keep track of a few interesting statistics */
127 int ap_max_daemons_limit = -1;
129 /* volatile just in case */
130 static int volatile shutdown_pending;
131 static int volatile restart_pending;
132 static int volatile is_graceful = 0;
133 ap_generation_t volatile ap_my_generation=0; /* Used by the scoreboard */
134 static int is_parent_process=TRUE;
135 HMTX ap_mpm_accept_mutex = 0;
137 /* An array of these is stored in a shared memory area for passing
138 * sockets from the parent to child processes
141 struct sockaddr_in name;
142 apr_os_sock_t listen_fd;
147 listen_socket_t listeners[1];
150 static char master_main();
151 static void spawn_child(int slot);
152 void ap_mpm_child_main(apr_pool_t *pconf);
153 static void set_signals();
156 int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
158 char *listener_shm_name;
159 parent_info_t *parent_info;
165 DosSetMaxFH(ap_thread_limit * 2);
166 listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getppid());
167 rc = DosGetNamedSharedMem((PPVOID)&parent_info, listener_shm_name, PAG_READ);
168 is_parent_process = rc != 0;
169 ap_scoreboard_fname = apr_psprintf(pconf, "/sharemem/httpd/scoreboard.%d", is_parent_process ? getpid() : getppid());
174 int num_listeners = 0;
176 ap_mpm_accept_mutex = parent_info->accept_mutex;
178 /* Set up a default listener if necessary */
179 if (ap_listeners == NULL) {
180 ap_listen_rec *lr = apr_pcalloc(s->process->pool, sizeof(ap_listen_rec));
182 apr_sockaddr_info_get(&lr->bind_addr, "0.0.0.0", APR_UNSPEC,
183 DEFAULT_HTTP_PORT, 0, s->process->pool);
184 apr_socket_create(&lr->sd, lr->bind_addr->family,
185 SOCK_STREAM, 0, s->process->pool);
188 for (lr = ap_listeners; lr; lr = lr->next) {
190 apr_os_sock_put(&lr->sd, &parent_info->listeners[num_listeners].listen_fd, pconf);
191 apr_socket_addr_get(&sa, APR_LOCAL, lr->sd);
195 DosFreeMem(parent_info);
198 ap_mpm_child_main(pconf);
206 is_parent_process = TRUE;
208 if (ap_setup_listeners(ap_server_conf) < 1) {
209 ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
210 "no listening sockets available, shutting down");
214 ap_log_pid(pconf, ap_pid_fname);
216 restart = master_main();
218 ap_scoreboard_image->global->running_generation = ap_my_generation;
221 const char *pidfile = ap_server_root_relative(pconf, ap_pid_fname);
223 if (pidfile != NULL && remove(pidfile) == 0) {
224 ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS,
225 ap_server_conf, "removed PID file %s (pid=%d)",
229 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
230 "caught SIGTERM, shutting down");
233 } /* Parent process */
235 return 0; /* Restart */
240 /* Main processing of the parent process
241 * returns TRUE if restarting
243 static char master_main()
245 server_rec *s = ap_server_conf;
247 parent_info_t *parent_info;
248 char *listener_shm_name;
249 int listener_num, num_listeners, slot;
252 printf("%s \n", ap_get_server_version());
255 if (ap_setup_listeners(ap_server_conf) < 1) {
256 ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
257 "no listening sockets available, shutting down");
261 /* Allocate a shared memory block for the array of listeners */
262 for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
266 listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getpid());
267 rc = DosAllocSharedMem((PPVOID)&parent_info, listener_shm_name,
268 sizeof(parent_info_t) + num_listeners * sizeof(listen_socket_t),
269 PAG_READ|PAG_WRITE|PAG_COMMIT);
272 ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
273 "failure allocating shared memory, shutting down");
277 /* Store the listener sockets in the shared memory area for our children to see */
278 for (listener_num = 0, lr = ap_listeners; lr; lr = lr->next, listener_num++) {
279 apr_os_sock_get(&parent_info->listeners[listener_num].listen_fd, lr->sd);
282 /* Create mutex to prevent multiple child processes from detecting
283 * a connection with apr_poll()
286 rc = DosCreateMutexSem(NULL, &ap_mpm_accept_mutex, DC_SEM_SHARED, FALSE);
289 ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
290 "failure creating accept mutex, shutting down");
294 parent_info->accept_mutex = ap_mpm_accept_mutex;
296 /* Allocate shared memory for scoreboard */
297 if (ap_scoreboard_image == NULL) {
299 rc = DosAllocSharedMem(&sb_mem, ap_scoreboard_fname,
300 ap_calc_scoreboard_size(),
301 PAG_COMMIT|PAG_READ|PAG_WRITE);
304 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
305 "unable to allocate shared memory for scoreboard , exiting");
309 ap_init_scoreboard(sb_mem);
312 ap_scoreboard_image->global->restart_time = apr_time_now();
313 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
314 "%s configured -- resuming normal operations",
315 ap_get_server_version());
316 ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
317 "Server built: %s", ap_get_server_built());
318 #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
319 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
320 "AcceptMutex: %s (default: %s)",
321 apr_proc_mutex_name(accept_mutex),
322 apr_proc_mutex_defname());
325 ap_scoreboard_image->parent[0].pid = getpid();
326 ap_mpm_child_main(pconf);
330 while (!restart_pending && !shutdown_pending) {
333 int active_children = 0;
335 /* Count number of active children */
336 for (slot=0; slot < HARD_SERVER_LIMIT; slot++) {
337 active_children += ap_scoreboard_image->parent[slot].pid != 0 &&
338 !ap_scoreboard_image->parent[slot].quiescing;
341 /* Spawn children if needed */
342 for (slot=0; slot < HARD_SERVER_LIMIT && active_children < ap_daemons_to_start; slot++) {
343 if (ap_scoreboard_image->parent[slot].pid == 0) {
349 rc = DosWaitChild(DCWA_PROCESSTREE, DCWW_NOWAIT, &proc_rc, &child_pid, 0);
352 /* A child has terminated, remove its scoreboard entry & terminate if necessary */
353 for (slot=0; ap_scoreboard_image->parent[slot].pid != child_pid && slot < HARD_SERVER_LIMIT; slot++);
355 if (slot < HARD_SERVER_LIMIT) {
356 ap_scoreboard_image->parent[slot].pid = 0;
357 ap_scoreboard_image->parent[slot].quiescing = 0;
359 if (proc_rc.codeTerminate == TC_EXIT) {
360 /* Child terminated normally, check its exit code and
361 * terminate server if child indicates a fatal error
363 if (proc_rc.codeResult == APEXIT_CHILDFATAL)
367 } else if (rc == ERROR_CHILD_NOT_COMPLETE) {
368 /* No child exited, lets sleep for a while.... */
369 apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
373 /* Signal children to shut down, either gracefully or immediately */
374 for (slot=0; slot<HARD_SERVER_LIMIT; slot++) {
375 kill(ap_scoreboard_image->parent[slot].pid, is_graceful ? SIGHUP : SIGTERM);
378 DosFreeMem(parent_info);
379 return restart_pending;
384 static void spawn_child(int slot)
388 char fail_module[100];
389 char progname[CCHMAXPATH];
393 ap_scoreboard_image->parent[slot].generation = ap_my_generation;
394 DosGetInfoBlocks(&ptib, &ppib);
395 DosQueryModuleName(ppib->pib_hmte, sizeof(progname), progname);
396 rc = DosExecPgm(fail_module, sizeof(fail_module), EXEC_ASYNCRESULT,
397 ppib->pib_pchcmd, NULL, &proc_rc, progname);
400 ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
401 "error spawning child, slot %d", slot);
404 if (ap_max_daemons_limit < slot) {
405 ap_max_daemons_limit = slot;
408 ap_scoreboard_image->parent[slot].pid = proc_rc.codeTerminate;
413 /* Signal handling routines */
415 static void sig_term(int sig)
417 shutdown_pending = 1;
418 signal(SIGTERM, SIG_DFL);
423 static void sig_restart(int sig)
425 if (sig == SIGUSR1) {
434 static void set_signals()
438 sigemptyset(&sa.sa_mask);
440 sa.sa_handler = sig_term;
442 if (sigaction(SIGTERM, &sa, NULL) < 0)
443 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
445 if (sigaction(SIGINT, &sa, NULL) < 0)
446 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
448 sa.sa_handler = sig_restart;
450 if (sigaction(SIGHUP, &sa, NULL) < 0)
451 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
452 if (sigaction(SIGUSR1, &sa, NULL) < 0)
453 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGUSR1)");
458 /* Enquiry functions used get MPM status info */
460 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
462 switch (query_code) {
463 case AP_MPMQ_MAX_DAEMON_USED:
464 *result = ap_max_daemons_limit;
466 case AP_MPMQ_IS_THREADED:
467 *result = AP_MPMQ_DYNAMIC;
469 case AP_MPMQ_IS_FORKED:
470 *result = AP_MPMQ_NOT_SUPPORTED;
472 case AP_MPMQ_HARD_LIMIT_DAEMONS:
473 *result = HARD_SERVER_LIMIT;
475 case AP_MPMQ_HARD_LIMIT_THREADS:
476 *result = HARD_THREAD_LIMIT;
478 case AP_MPMQ_MIN_SPARE_DAEMONS:
481 case AP_MPMQ_MAX_SPARE_DAEMONS:
484 case AP_MPMQ_MAX_REQUESTS_DAEMON:
485 *result = ap_max_requests_per_child;
493 int ap_graceful_stop_signalled(void)
500 /* Configuration handling stuff */
502 static int mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
504 one_process = ap_exists_config_define("ONE_PROCESS") ||
505 ap_exists_config_define("DEBUG");
507 ap_listen_pre_config();
508 ap_daemons_to_start = DEFAULT_START_DAEMON;
509 ap_thread_limit = HARD_THREAD_LIMIT;
510 ap_pid_fname = DEFAULT_PIDLOG;
511 ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
512 ap_extended_status = 0;
513 ap_min_spare_threads = DEFAULT_MIN_SPARE_THREAD;
514 ap_max_spare_threads = DEFAULT_MAX_SPARE_THREAD;
515 #ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
516 ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
524 static void mpmt_os2_hooks(apr_pool_t *p)
526 ap_hook_pre_config(mpmt_os2_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
531 static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const char *arg)
533 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
539 ap_daemons_to_start = atoi(arg);
545 static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy,
548 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
554 ap_min_spare_threads = atoi(arg);
556 if (ap_min_spare_threads <= 0) {
557 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
558 "WARNING: detected MinSpareThreads set to non-positive.");
559 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
560 "Resetting to 1 to avoid almost certain Apache failure.");
561 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
562 "Please read the documentation.");
563 ap_min_spare_threads = 1;
571 static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy,
574 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
580 ap_max_spare_threads = atoi(arg);
586 static const char *ignore_cmd(cmd_parms *cmd, void *dummy, const char *arg)
593 static const command_rec mpmt_os2_cmds[] = {
595 AP_INIT_TAKE1( "StartServers", set_daemons_to_start, NULL, RSRC_CONF,
596 "Number of child processes launched at server startup" ),
597 AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
598 "Minimum number of idle children, to handle request spikes"),
599 AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
600 "Maximum number of idle children"),
601 AP_INIT_TAKE1("User", ignore_cmd, NULL, RSRC_CONF,
602 "Not applicable on this platform"),
603 AP_INIT_TAKE1("Group", ignore_cmd, NULL, RSRC_CONF,
604 "Not applicable on this platform"),
605 AP_INIT_TAKE1("ScoreBoardFile", ignore_cmd, NULL, RSRC_CONF, \
606 "Not applicable on this platform"),
610 module AP_MODULE_DECLARE_DATA mpm_mpmt_os2_module = {
612 NULL, /* hook to run before apache parses args */
613 NULL, /* create per-directory config structure */
614 NULL, /* merge per-directory config structures */
615 NULL, /* create per-server config structure */
616 NULL, /* merge per-server config structures */
617 mpmt_os2_cmds, /* command apr_table_t */
618 mpmt_os2_hooks, /* register_hooks */