1
0
mirror of https://github.com/apache/httpd.git synced 2025-11-06 16:49:32 +03:00
Files
apache/server/mpm/mpmt_os2/mpmt_os2.c
Ryan Bloom aefc6b1525 Remove commas from the end of the macros that define
directives that are used by MPMs.  Previous to this patch,
you would use these macros without commans, which was unlike
any other directives. Now, after the macro, you must have
a comma.  This makes the macros look more like the rest of the
directives.

I know this is cosmetic, and I was going to leave it alone, but when
I found out that it bothered Cliff too, I decided to fix it after all.

Submitted by:	Ryan Bloom and Cliff Woolley


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@91896 13f79535-47bb-0310-9956-ffa450edef68
2001-11-13 06:04:25 +00:00

584 lines
18 KiB
C

/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
/* Multi-process, multi-threaded MPM for OS/2
*
* Server consists of
* - a main, parent process
* - a small, static number of child processes
*
* The parent process's job is to manage the child processes. This involves
* spawning children as required to ensure there are always ap_daemons_to_start
* processes accepting connections.
*
* Each child process consists of a a pool of worker threads and a
* main thread that accepts connections & passes them to the workers via
* a work queue. The worker thread pool is dynamic, managed by a maintanence
* thread so that the number of idle threads is kept between
* min_spare_threads & max_spare_threads.
*
*/
/*
Todo list
- Enforce MaxClients somehow
*/
#define CORE_PRIVATE
#define INCL_NOPMAPI
#define INCL_DOS
#define INCL_DOSERRORS
#include "ap_config.h"
#include "httpd.h"
#include "mpm_default.h"
#include "http_main.h"
#include "http_log.h"
#include "http_config.h"
#include "http_core.h" /* for get_remote_host */
#include "http_connection.h"
#include "mpm.h"
#include "ap_mpm.h"
#include "ap_listen.h"
#include "apr_portable.h"
#include "mpm_common.h"
#include "apr_strings.h"
#include <os2.h>
#include <process.h>
server_rec *ap_server_conf;
static apr_pool_t *pconf = NULL; /* Pool for config stuff */
static const char *ap_pid_fname=NULL;
/* Config globals */
static int one_process = 0;
static int ap_daemons_to_start = 0;
static int ap_thread_limit = 0;
static int ap_max_requests_per_child = 0;
int ap_min_spare_threads = 0;
int ap_max_spare_threads = 0;
/* Keep track of a few interesting statistics */
int ap_max_daemons_limit = -1;
/* volatile just in case */
static int volatile shutdown_pending;
static int volatile restart_pending;
static int volatile is_graceful = 0;
ap_generation_t volatile ap_my_generation=0; /* Used by the scoreboard */
static int is_parent_process=TRUE;
HMTX ap_mpm_accept_mutex = 0;
/* An array of these is stored in a shared memory area for passing
* sockets from the parent to child processes
*/
typedef struct {
struct sockaddr_in name;
apr_os_sock_t listen_fd;
} listen_socket_t;
typedef struct {
apr_time_t restart_time;
HMTX accept_mutex;
listen_socket_t listeners[1];
} parent_info_t;
static char master_main();
static void spawn_child(int slot);
void ap_mpm_child_main(apr_pool_t *pconf);
static void set_signals();
int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
{
char *listener_shm_name;
parent_info_t *parent_info;
ULONG rc;
pconf = _pconf;
ap_server_conf = s;
restart_pending = 0;
DosSetMaxFH(ap_thread_limit * 2);
listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getppid());
rc = DosGetNamedSharedMem((PPVOID)&parent_info, listener_shm_name, PAG_READ);
is_parent_process = rc != 0;
ap_scoreboard_fname = apr_psprintf(pconf, "/sharemem/httpd/scoreboard.%d", is_parent_process ? getpid() : getppid());
if (rc == 0) {
/* Child process */
ap_listen_rec *lr;
int num_listeners = 0;
ap_restart_time = parent_info->restart_time;
ap_mpm_accept_mutex = parent_info->accept_mutex;
/* Set up a default listener if necessary */
if (ap_listeners == NULL) {
ap_listen_rec *lr = apr_pcalloc(s->process->pool, sizeof(ap_listen_rec));
ap_listeners = lr;
apr_sockaddr_info_get(&lr->bind_addr, "0.0.0.0", APR_UNSPEC,
DEFAULT_HTTP_PORT, 0, s->process->pool);
apr_socket_create(&lr->sd, lr->bind_addr->sa.sin.sin_family,
SOCK_STREAM, s->process->pool);
}
for (lr = ap_listeners; lr; lr = lr->next) {
apr_os_sock_put(&lr->sd, &parent_info->listeners[num_listeners].listen_fd, pconf);
num_listeners++;
}
DosFreeMem(parent_info);
/* Do the work */
ap_mpm_child_main(pconf);
/* Outta here */
return 1;
}
else {
/* Parent process */
char restart;
is_parent_process = TRUE;
if (ap_setup_listeners(ap_server_conf) < 1) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
"no listening sockets available, shutting down");
return 1;
}
ap_log_pid(pconf, ap_pid_fname);
restart = master_main();
++ap_my_generation;
ap_scoreboard_image->global.running_generation = ap_my_generation;
if (!restart) {
const char *pidfile = ap_server_root_relative(pconf, ap_pid_fname);
if (pidfile != NULL && remove(pidfile) == 0) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS,
ap_server_conf, "removed PID file %s (pid=%d)",
pidfile, getpid());
}
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
"caught SIGTERM, shutting down");
return 1;
}
} /* Parent process */
return 0; /* Restart */
}
/* Main processing of the parent process
* returns TRUE if restarting
*/
static char master_main()
{
server_rec *s = ap_server_conf;
ap_listen_rec *lr;
parent_info_t *parent_info;
char *listener_shm_name;
int listener_num, num_listeners, slot;
ULONG rc;
printf("%s \n", ap_get_server_version());
set_signals();
if (ap_setup_listeners(ap_server_conf) < 1) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s,
"no listening sockets available, shutting down");
return FALSE;
}
/* Allocate a shared memory block for the array of listeners */
for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) {
num_listeners++;
}
listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getpid());
rc = DosAllocSharedMem((PPVOID)&parent_info, listener_shm_name,
sizeof(parent_info_t) + num_listeners * sizeof(listen_socket_t),
PAG_READ|PAG_WRITE|PAG_COMMIT);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
"failure allocating shared memory, shutting down");
return FALSE;
}
/* Store the listener sockets in the shared memory area for our children to see */
for (listener_num = 0, lr = ap_listeners; lr; lr = lr->next, listener_num++) {
apr_os_sock_get(&parent_info->listeners[listener_num].listen_fd, lr->sd);
}
/* Create mutex to prevent multiple child processes from detecting
* a connection with apr_poll()
*/
rc = DosCreateMutexSem(NULL, &ap_mpm_accept_mutex, DC_SEM_SHARED, FALSE);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
"failure creating accept mutex, shutting down");
return FALSE;
}
ap_restart_time = apr_time_now();
parent_info->restart_time = ap_restart_time;
parent_info->accept_mutex = ap_mpm_accept_mutex;
/* Allocate shared memory for scoreboard */
if (ap_scoreboard_image == NULL) {
rc = DosAllocSharedMem((PPVOID)&ap_scoreboard_image, ap_scoreboard_fname,
sizeof(scoreboard), PAG_COMMIT|PAG_READ|PAG_WRITE);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"unable to allocate shared memory for scoreboard , exiting");
return FALSE;
}
memset(ap_scoreboard_image, 0, sizeof(scoreboard));
}
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf,
"%s configured -- resuming normal operations",
ap_get_server_version());
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf,
"Server built: %s", ap_get_server_built());
if (one_process) {
ap_scoreboard_image->parent[0].pid = getpid();
ap_mpm_child_main(pconf);
return FALSE;
}
while (!restart_pending && !shutdown_pending) {
RESULTCODES proc_rc;
PID child_pid;
int active_children = 0;
/* Count number of active children */
for (slot=0; slot < HARD_SERVER_LIMIT; slot++) {
active_children += ap_scoreboard_image->parent[slot].pid != 0 &&
!ap_scoreboard_image->parent[slot].quiescing;
}
/* Spawn children if needed */
for (slot=0; slot < HARD_SERVER_LIMIT && active_children < ap_daemons_to_start; slot++) {
if (ap_scoreboard_image->parent[slot].pid == 0) {
spawn_child(slot);
active_children++;
}
}
rc = DosWaitChild(DCWA_PROCESSTREE, DCWW_NOWAIT, &proc_rc, &child_pid, 0);
if (rc == 0) {
/* A child has terminated, remove its scoreboard entry & terminate if necessary */
for (slot=0; ap_scoreboard_image->parent[slot].pid != child_pid && slot < HARD_SERVER_LIMIT; slot++);
if (slot < HARD_SERVER_LIMIT) {
ap_scoreboard_image->parent[slot].pid = 0;
ap_scoreboard_image->parent[slot].quiescing = 0;
if (proc_rc.codeTerminate == TC_EXIT) {
/* Child terminated normally, check its exit code and
* terminate server if child indicates a fatal error
*/
if (proc_rc.codeResult == APEXIT_CHILDFATAL)
break;
}
}
} else if (rc == ERROR_CHILD_NOT_COMPLETE) {
/* No child exited, lets sleep for a while.... */
apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
}
}
/* Signal children to shut down, either gracefully or immediately */
for (slot=0; slot<HARD_SERVER_LIMIT; slot++) {
kill(ap_scoreboard_image->parent[slot].pid, is_graceful ? SIGHUP : SIGTERM);
}
DosFreeMem(parent_info);
return restart_pending;
}
static void spawn_child(int slot)
{
PPIB ppib;
PTIB ptib;
char fail_module[100];
char progname[CCHMAXPATH];
RESULTCODES proc_rc;
ULONG rc;
ap_scoreboard_image->parent[slot].generation = ap_my_generation;
DosGetInfoBlocks(&ptib, &ppib);
DosQueryModuleName(ppib->pib_hmte, sizeof(progname), progname);
rc = DosExecPgm(fail_module, sizeof(fail_module), EXEC_ASYNCRESULT,
ppib->pib_pchcmd, NULL, &proc_rc, progname);
if (rc) {
ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
"error spawning child, slot %d", slot);
}
if (ap_max_daemons_limit < slot) {
ap_max_daemons_limit = slot;
}
ap_scoreboard_image->parent[slot].pid = proc_rc.codeTerminate;
}
/* Signal handling routines */
static void sig_term(int sig)
{
shutdown_pending = 1;
}
static void sig_restart(int sig)
{
if (sig == SIGUSR1) {
is_graceful = 1;
}
restart_pending = 1;
}
static void set_signals()
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sig_term;
if (sigaction(SIGTERM, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
if (sigaction(SIGINT, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
sa.sa_handler = sig_restart;
if (sigaction(SIGHUP, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
if (sigaction(SIGUSR1, &sa, NULL) < 0)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGUSR1)");
}
/* Enquiry functions used get MPM status info */
AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
{
switch (query_code) {
case AP_MPMQ_MAX_DAEMON_USED:
*result = ap_max_daemons_limit;
return APR_SUCCESS;
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_DYNAMIC;
return APR_SUCCESS;
case AP_MPMQ_IS_FORKED:
*result = AP_MPMQ_NOT_SUPPORTED;
return APR_SUCCESS;
case AP_MPMQ_HARD_LIMIT_DAEMONS:
*result = HARD_SERVER_LIMIT;
return APR_SUCCESS;
case AP_MPMQ_HARD_LIMIT_THREADS:
*result = HARD_THREAD_LIMIT;
return APR_SUCCESS;
case AP_MPMQ_MIN_SPARE_DAEMONS:
*result = 0;
return APR_SUCCESS;
case AP_MPMQ_MAX_SPARE_DAEMONS:
*result = 0;
return APR_SUCCESS;
case AP_MPMQ_MAX_REQUESTS_DAEMON:
*result = ap_max_requests_per_child;
return APR_SUCCESS;
}
return APR_ENOTIMPL;
}
int ap_graceful_stop_signalled(void)
{
return is_graceful;
}
/* Configuration handling stuff */
static void mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
{
one_process = ap_exists_config_define("ONE_PROCESS") ||
ap_exists_config_define("DEBUG");
is_graceful = 0;
ap_listen_pre_config();
ap_daemons_to_start = DEFAULT_START_DAEMON;
ap_thread_limit = HARD_THREAD_LIMIT;
ap_pid_fname = DEFAULT_PIDLOG;
ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
ap_extended_status = 0;
ap_min_spare_threads = DEFAULT_MIN_SPARE_THREAD;
ap_max_spare_threads = DEFAULT_MAX_SPARE_THREAD;
}
static void mpmt_os2_hooks(apr_pool_t *p)
{
ap_hook_pre_config(mpmt_os2_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
}
static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_daemons_to_start = atoi(arg);
return NULL;
}
static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy,
const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_min_spare_threads = atoi(arg);
if (ap_min_spare_threads <= 0) {
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"WARNING: detected MinSpareThreads set to non-positive.");
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"Resetting to 1 to avoid almost certain Apache failure.");
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"Please read the documentation.");
ap_min_spare_threads = 1;
}
return NULL;
}
static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy,
const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_max_spare_threads = atoi(arg);
return NULL;
}
static const command_rec mpmt_os2_cmds[] = {
LISTEN_COMMANDS,
AP_INIT_TAKE1( "StartServers", set_daemons_to_start, NULL, RSRC_CONF,
"Number of child processes launched at server startup" ),
AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
"Minimum number of idle children, to handle request spikes"),
AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
"Maximum number of idle children"),
{ NULL }
};
module AP_MODULE_DECLARE_DATA mpm_mpmt_os2_module = {
MPM20_MODULE_STUFF,
NULL, /* hook to run before apache parses args */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
mpmt_os2_cmds, /* command apr_table_t */
mpmt_os2_hooks, /* register_hooks */
};