From: Bradley Nicholes Date: Tue, 16 Oct 2001 23:27:04 +0000 (+0000) Subject: First cut at the MPM for NetWare X-Git-Tag: 2.0.27~114 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=4f0494a877202763f176af739d086e705ca4810d;p=thirdparty%2Fapache%2Fhttpd.git First cut at the MPM for NetWare git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@91514 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/server/mpm/netware/mpm.h b/server/mpm/netware/mpm.h new file mode 100644 index 00000000000..1ce59331e5d --- /dev/null +++ b/server/mpm/netware/mpm.h @@ -0,0 +1,87 @@ +/* ==================================================================== + * 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 + * . + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ +#include "scoreboard.h" +//#include "unixd.h" + +#ifndef APACHE_MPM_THREADED_H +#define APACHE_MPM_THREADED_H + +#define THREADED_MPM + +#define MPM_NAME "NetWare_Threaded" + +//#define AP_MPM_WANT_RECLAIM_CHILD_PROCESSES +#define AP_MPM_WANT_WAIT_OR_TIMEOUT +//#define AP_MPM_WANT_PROCESS_CHILD_STATUS +#define AP_MPM_WANT_SET_PIDFILE +#define AP_MPM_WANT_SET_SCOREBOARD +#define AP_MPM_WANT_SET_LOCKFILE +#define AP_MPM_WANT_SET_MAX_REQUESTS +#define AP_MPM_WANT_SET_COREDUMPDIR +//#define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH + +#define MPM_SYNC_CHILD_TABLE() (ap_sync_scoreboard_image()) +#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid) +#define MPM_NOTE_CHILD_KILLED(i) (MPM_CHILD_PID(i) = 0) + +extern int ap_threads_per_child; +extern int ap_thread_stack_size; +extern int ap_max_workers_limit; +extern server_rec *ap_server_conf; + +#endif /* APACHE_MPM_THREADED_H */ diff --git a/server/mpm/netware/mpm_default.h b/server/mpm/netware/mpm_default.h new file mode 100644 index 00000000000..6ae8d1a81bd --- /dev/null +++ b/server/mpm/netware/mpm_default.h @@ -0,0 +1,145 @@ +/* ==================================================================== + * 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 + * . + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +#ifndef APACHE_MPM_DEFAULT_H +#define APACHE_MPM_DEFAULT_H + +#define AP_ID_FROM_CHILD_THREAD(c, t) ((c * HARD_THREAD_LIMIT) + t) +#define AP_CHILD_THREAD_FROM_ID(i) (0), (i) + +/* Number of servers to spawn off by default --- also, if fewer than + * this free when the caretaker checks, it will spawn more. + */ +#ifndef DEFAULT_START_DAEMON +#define DEFAULT_START_DAEMON 1 +#endif + +/* Maximum number of *free* server processes --- more than this, and + * they will die off. + */ + +#ifndef DEFAULT_MAX_FREE_DAEMON +#define DEFAULT_MAX_FREE_DAEMON 1 +#endif + +/* Minimum --- fewer than this, and more will be created */ + +#ifndef DEFAULT_MIN_FREE_DAEMON +#define DEFAULT_MIN_FREE_DAEMON 1 +#endif + +/* Limit on the total --- clients will be locked out if more servers than + * this are needed. It is intended solely to keep the server from crashing + * when things get out of hand. + * + * We keep a hard maximum number of servers, for two reasons --- first off, + * in case something goes seriously wrong, we want to stop the fork bomb + * short of actually crashing the machine we're running on by filling some + * kernel table. Secondly, it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#ifndef HARD_SERVER_LIMIT +#define HARD_SERVER_LIMIT 1 +#endif + +/* Limit on the threads per process. Clients will be locked out if more than + * this * HARD_SERVER_LIMIT are needed. + * + * We keep this for one reason it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#ifndef HARD_THREAD_LIMIT +#define HARD_THREAD_LIMIT 2048 +#endif + +#ifndef DEFAULT_THREADS_PER_CHILD +#define DEFAULT_THREADS_PER_CHILD 50 +#endif + +/* File used for accept locking, when we use a file */ +#ifndef DEFAULT_LOCKFILE +#define DEFAULT_LOCKFILE "logs/accept.lock" +#endif + +/* Scoreboard file, if there is one */ +#ifndef DEFAULT_SCOREBOARD +#define DEFAULT_SCOREBOARD "logs/apache_runtime_status" +#endif + +/* Where the main/parent process's pid is logged */ +#ifndef DEFAULT_PIDLOG +#define DEFAULT_PIDLOG "logs/httpd.pid" +#endif + +/* + * Interval, in microseconds, between scoreboard maintenance. + */ +#ifndef SCOREBOARD_MAINTENANCE_INTERVAL +#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000 +#endif + +/* Number of requests to try to handle in a single process. If <= 0, + * the children don't die off. + */ +#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD +#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000 +#endif + +#endif /* AP_MPM_DEFAULT_H */ diff --git a/server/mpm/netware/mpm_netware.c b/server/mpm/netware/mpm_netware.c new file mode 100644 index 00000000000..e16a7ab5921 --- /dev/null +++ b/server/mpm/netware/mpm_netware.c @@ -0,0 +1,1255 @@ +/* ==================================================================== + * 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 + * . + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +/* + * httpd.c: simple http daemon for answering WWW file requests + * + * + * 03-21-93 Rob McCool wrote original code (up to NCSA HTTPd 1.3) + * + * 03-06-95 blong + * changed server number for child-alone processes to 0 and changed name + * of processes + * + * 03-10-95 blong + * Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu) + * including set group before fork, and call gettime before to fork + * to set up libraries. + * + * 04-14-95 rst / rh + * Brandon's code snarfed from NCSA 1.4, but tinkered to work with the + * Apache server, and also to have child processes do accept() directly. + * + * April-July '95 rst + * Extensive rework for Apache. + */ + +/* TODO: this is a cobbled together prefork MPM example... it should mostly + * TODO: behave like apache-1.3... here's a short list of things I think + * TODO: need cleaning up still: + */ + +#include "apr.h" +#include "apr_portable.h" +#include "apr_strings.h" +#include "apr_thread_proc.h" +#include "apr_signal.h" +#include "apr_tables.h" +#include "apr_getopt.h" + +#define APR_WANT_STDIO +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#if APR_HAVE_UNISTD_H +#include +#endif +#if APR_HAVE_SYS_TYPES_H +#include +#endif + +#define CORE_PRIVATE + +#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 "scoreboard.h" +#include "ap_mpm.h" +#include "mpm_common.h" +#include "ap_listen.h" +#include "ap_mmn.h" + +#ifdef HAVE_TIME_H +#include +#endif + +#include + +#define WORKER_DEAD SERVER_DEAD +#define WORKER_STARTING SERVER_STARTING +#define WORKER_READY SERVER_READY + +/* config globals */ + +int ap_threads_per_child=0; /* Worker threads per child */ +int ap_thread_stack_size=65536; +static apr_lock_t *accept_lock; +static int ap_threads_to_start=0; +static int ap_threads_min_free=0; +static int ap_threads_max_free=0; +static int ap_threads_limit=0; + +/* + * The max child slot ever assigned, preserved across restarts. Necessary + * to deal with MaxClients changes across SIGWINCH restarts. We use this + * value to optimize routines that have to scan the entire scoreboard. + */ +int ap_max_workers_limit = -1; +server_rec *ap_server_conf; + +/* *Non*-shared http_main globals... */ + +static apr_socket_t *sd; +static fd_set listenfds; +static int listenmaxfd; + +/* one_process --- debugging mode variable; can be set from the command line + * with the -X flag. If set, this gets you the child_main loop running + * in the process which originally started up (no detach, no make_child), + * which is a pretty nice debugging environment. (You'll get a SIGHUP + * early in standalone_main; just continue through. This is the server + * trying to kill off any child processes which it might have lying + * around --- Apache doesn't keep track of their pids, it just sends + * SIGHUP to the process group, ignoring it in the root process. + * Continue through and you'll be fine.). + */ + +static int one_process = 0; + +static apr_pool_t *pconf; /* Pool for config stuff */ +static apr_pool_t *pmain; /* Pool for httpd child stuff */ + +static pid_t ap_my_pid; /* it seems silly to call getpid all the time */ +static pid_t parent_pid; +#ifndef MULTITHREAD +static int my_child_num; +#endif + +static int die_now = 0; +static apr_lock_t *accept_mutex = NULL; + +/* Keep track of the number of worker threads currently active */ +static int worker_thread_count; +static apr_lock_t *worker_thread_count_mutex; + + +#ifdef GPROF +/* + * change directory for gprof to plop the gmon.out file + * configure in httpd.conf: + * GprofDir logs/ -> $ServerRoot/logs/gmon.out + * GprofDir logs/% -> $ServerRoot/logs/gprof.$pid/gmon.out + */ +static void chdir_for_gprof(void) +{ + core_server_config *sconf = + ap_get_module_config(ap_server_conf->module_config, &core_module); + char *dir = sconf->gprof_dir; + const char *use_dir; + + if(dir) { + apr_status_t res; + char buf[512]; + int len = strlen(sconf->gprof_dir) - 1; + if(*(dir + len) == '%') { + dir[len] = '\0'; + apr_snprintf(buf, sizeof(buf), "%sgprof.%d", dir, (int)getpid()); + } + use_dir = ap_server_root_relative(pconf, buf[0] ? buf : dir); + res = apr_dir_make(use_dir, 0755, pconf); + if(res != APR_SUCCESS && !APR_STATUS_IS_EEXIST(res)) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, ap_server_conf, + "gprof: error creating directory %s", dir); + } + } + else { + use_dir = ap_server_root_relative(pconf, "logs"); + } + + chdir(dir); +} +#else +#define chdir_for_gprof() +#endif + +/* XXX - I don't know if TPF will ever use this module or not, so leave + * the ap_check_signals calls in but disable them - manoj */ +#define ap_check_signals() + +/* a clean exit from a child with proper cleanup */ +static void clean_child_exit(int code) __attribute__ ((noreturn)); +static void clean_child_exit(int code) +{ + apr_thread_mutex_lock(worker_thread_count_mutex); + worker_thread_count--; + apr_thread_mutex_unlock(worker_thread_count_mutex); + NXThreadExit((void*)&code); +} + +static apr_status_t accept_mutex_child_cleanup(void *foo) +{ + return apr_thread_mutex_unlock(accept_mutex); +} + +/* Initialize mutex lock. + * Done by each child at its birth + */ +static void accept_mutex_child_init(apr_pool_t *p) +{ + apr_pool_cleanup_register(p, NULL, accept_mutex_child_cleanup, apr_pool_cleanup_null); +} + +static void accept_mutex_on(void) +{ + apr_status_t rc = apr_thread_mutex_lock(accept_mutex); + + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rc, ap_server_conf, + "Error getting accept lock. Exiting!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + apr_status_t rc = apr_thread_mutex_unlock(accept_mutex); + + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rc, ap_server_conf, + "Error freeing accept lock. Exiting!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +/* On some architectures it's safe to do unserialized accept()s in the single + * Listen case. But it's never safe to do it in the case where there's + * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT + * when it's safe in the single Listen case. + */ +#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#define SAFE_ACCEPT(stmt) do {if (ap_listeners->next) {stmt;}} while(0) +#else +#define SAFE_ACCEPT(stmt) do {stmt;} while(0) +#endif + +//#ifdef NO_SERIALIZED_ACCEPT +//#define SAFE_ACCEPT(stmt) APR_SUCCESS +//#else +//#define SAFE_ACCEPT(stmt) (stmt) +//#endif + +AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result) +{ + switch(query_code){ + case AP_MPMQ_MAX_DAEMON_USED: + *result = ap_threads_limit; + return APR_SUCCESS; + case AP_MPMQ_IS_THREADED: + *result = AP_MPMQ_NOT_SUPPORTED; + return APR_SUCCESS; + case AP_MPMQ_IS_FORKED: + *result = AP_MPMQ_DYNAMIC; + 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_MAX_THREADS: + *result = 0; + return APR_SUCCESS; + case AP_MPMQ_MIN_SPARE_DEAMONS: + *result = ap_threads_min_free; + return APR_SUCCESS; + case AP_MPMQ_MIN_SPARE_THREADS: + *result = 0; + return APR_SUCCESS; + case AP_MPMQ_MAX_SPARE_DAEMONS: + *result = ap_threads_max_free; + return APR_SUCCESS; + case AP_MPMQ_MAX_SPARE_THREADS: + *result = 0; + return APR_SUCCESS; + case AP_MPMQ_MAX_REQUESTS_DEAMON: + *result = ap_max_requests_per_child; + return APR_SUCCESS; + case AP_MPMQ_MAX_DAEMONS: + *result = ap_threads_limit; + return APR_SUCCESS; + } + return APR_ENOTIMPL; +} + + +/***************************************************************** + * Connection structures and accounting... + */ + +static void just_die(int sig) +{ + clean_child_exit(0); +} + +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful; +static int volatile wait_to_finish=1; +ap_generation_t volatile ap_my_generation=0; + +static void sig_term(int sig) +{ + if (shutdown_pending == 1) { + /* Um, is this _probably_ not an error, if the user has + * tried to do a shutdown twice quickly, so we won't + * worry about reporting it. + */ + return; + } + shutdown_pending = 1; + + while (wait_to_finish) + delay(500); +// NXThreadYield(); + delay(2000); +// The shut down flag wait_to_finish needs to be set in +// the atexit() routine when it is finally working. +} + +/* restart() is the signal handler for SIGHUP and SIGWINCH + * in the parent process, unless running in ONE_PROCESS mode + */ +static void restart(int sig) +{ + if (restart_pending == 1) { + /* Probably not an error - don't bother reporting it */ + return; + } + restart_pending = 1; +} + +static void set_signals(void) +{ + apr_signal(SIGTERM, sig_term); +} + +/***************************************************************** + * Child process main loop. + * The following vars are static to avoid getting clobbered by longjmp(); + * they are really private to child_main. + */ + +//static int srv; +//static apr_socket_t *csd; +//static int requests_this_child; +static fd_set main_fds; + +int ap_graceful_stop_signalled(void) +{ + /* not ever called anymore... */ + return 0; +} + +static int setup_listen_poll(apr_pool_t *pmain, apr_pollfd_t **listen_poll) +{ + ap_listen_rec *lr; + int numfds = 0; + + for (lr = ap_listeners; lr; lr = lr->next) { + numfds++; + } + + apr_poll_setup(listen_poll, numfds, pmain); + + for (lr = ap_listeners; lr; lr = lr->next) { + apr_poll_socket_add(*listen_poll, lr->sd, APR_POLLIN); + } + return 0; +} + + +static void worker_main(void *arg) +{ + ap_listen_rec *lr; + ap_listen_rec *last_lr; + ap_listen_rec *first_lr; + apr_pool_t *ptrans; + conn_rec *current_conn; + apr_status_t stat = APR_EINIT; + int sockdes; + int worker_num_arg = *((int*)arg); + apr_pollfd_t *listen_poll; + int nsds, rv; + + int my_worker_num = worker_num_arg; + apr_socket_t *csd = NULL; + int requests_this_child = 0; + int srv; + struct timeval tv; + + last_lr = NULL; + tv.tv_sec = 1; + tv.tv_usec = 0; + + apr_pool_create(&ptrans, pmain); + + apr_thread_mutex_lock(worker_thread_count_mutex); + worker_thread_count++; + apr_thread_mutex_unlock(worker_thread_count_mutex); + + if (setup_listen_poll(pmain, &listen_poll)) { + clean_child_exit(1); + } + + ap_update_child_status(AP_CHILD_THREAD_FROM_ID(my_child_num), WORKER_READY, (request_rec *) NULL); + +// ap_sync_scoreboard_image(); + while (!die_now) { + /* + * (Re)initialize this child to a pre-connection state. + */ + current_conn = NULL; + apr_pool_clear(ptrans); + + if ((ap_max_requests_per_child > 0 + && requests_this_child++ >= ap_max_requests_per_child)) { + clean_child_exit(0); + } + + ap_update_child_status(AP_CHILD_THREAD_FROM_ID(my_child_num), WORKER_READY, (request_rec *) NULL); + + /* + * Wait for an acceptable connection to arrive. + */ + + /* Lock around "accept", if necessary */ + SAFE_ACCEPT(accept_mutex_on()); + + for (;;) { + if (shutdown_pending) { +printf ("Thread %d is shutting down\n", getpid()); + SAFE_ACCEPT(accept_mutex_off()); + clean_child_exit(0); + } + + /* more than one socket */ + memcpy(&main_fds, &listenfds, sizeof(fd_set)); + srv = select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv); + + if (srv < 0 && h_errno != EINTR) { + /* Single Unix documents select as returning errnos + * EBADF, EINTR, and EINVAL... and in none of those + * cases does it make sense to continue. In fact + * on Linux 2.0.x we seem to end up with EFAULT + * occasionally, and we'd loop forever due to it. + */ + ap_log_error(APLOG_MARK, APLOG_ERR, h_errno, ap_server_conf, "select: (listen)"); + clean_child_exit(1); + } + + if (srv <= 0) + continue; + + /* we remember the last_lr we searched last time around so that + we don't end up starving any particular listening socket */ + if (last_lr == NULL) { + lr = ap_listeners; + } + else { + lr = last_lr->next; + if (!lr) + lr = ap_listeners; + } + first_lr = lr; + do { + apr_os_sock_get(&sockdes, lr->sd); + if (FD_ISSET(sockdes, &main_fds)) + goto got_listener; + lr = lr->next; + if (!lr) + lr = ap_listeners; + } while (lr != first_lr); + /* FIXME: if we get here, something bad has happened, and we're + probably gonna spin forever. + */ + continue; +got_listener: + last_lr = lr; + sd = lr->sd; + + /* if we accept() something we don't want to die, so we have to + * defer the exit + */ + for (;;) { +// ap_sync_scoreboard_image(); + stat = apr_accept(&csd, sd, ptrans); + if (stat == APR_SUCCESS || !APR_STATUS_IS_EINTR(stat)) + break; + } + + if (stat == APR_SUCCESS) + break; /* We have a socket ready for reading */ + else { + /* Our old behaviour here was to continue after accept() + * errors. But this leads us into lots of troubles + * because most of the errors are quite fatal. For + * example, EMFILE can be caused by slow descriptor + * leaks (say in a 3rd party module, or libc). It's + * foolish for us to continue after an EMFILE. We also + * seem to tickle kernel bugs on some platforms which + * lead to never-ending loops here. So it seems best + * to just exit in most cases. + */ + switch (stat) { + + /* Linux generates the rest of these, other tcp + * stacks (i.e. bsd) tend to hide them behind + * getsockopt() interfaces. They occur when + * the net goes sour or the client disconnects + * after the three-way handshake has been done + * in the kernel but before userland has picked + * up the socket. + */ + case ECONNRESET: + case ETIMEDOUT: + case EHOSTUNREACH: + case ENETUNREACH: + break; + + case ENETDOWN: + /* + * When the network layer has been shut down, there + * is not much use in simply exiting: the parent + * would simply re-create us (and we'd fail again). + * Use the CHILDFATAL code to tear the server down. + * @@@ Martin's idea for possible improvement: + * A different approach would be to define + * a new APEXIT_NETDOWN exit code, the reception + * of which would make the parent shutdown all + * children, then idle-loop until it detected that + * the network is up again, and restart the children. + * Ben Hyde noted that temporary ENETDOWN situations + * occur in mobile IP. + */ + ap_log_error(APLOG_MARK, APLOG_EMERG, stat, ap_server_conf, + "apr_accept: giving up."); + clean_child_exit(APEXIT_CHILDFATAL); + + default: + ap_log_error(APLOG_MARK, APLOG_ERR, stat, ap_server_conf, + "apr_accept: (client socket)"); + clean_child_exit(1); + } + } + +// ap_sync_scoreboard_image(); + } + + SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ + + /* + * We now have a connection, so set it up with the appropriate + * socket options, file descriptors, and read/write buffers. + */ + + apr_os_sock_get(&sockdes, csd); + + if (sockdes >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, NULL, + "new file descriptor %d is too large; you probably need " + "to rebuild Apache with a larger FD_SETSIZE " + "(currently %d)", + sockdes, FD_SETSIZE); + apr_socket_close(csd); +// ap_sync_scoreboard_image(); + continue; + } + + ap_sock_disable_nagle(csd); + + current_conn = ap_new_connection(ptrans, ap_server_conf, csd, + my_child_num); + if (current_conn) { + ap_process_connection(current_conn); + ap_lingering_close(current_conn); + } + +// ap_sync_scoreboard_image(); + } + clean_child_exit(0); +} + + +static int make_child(server_rec *s, int slot) +{ + int tid; + int err=0; + NXContext_t ctx; + + if (slot + 1 > ap_max_workers_limit) { + ap_max_workers_limit = slot + 1; + } + + if (one_process) { + apr_signal(SIGINT, just_die); + apr_signal(SIGTERM, just_die); + worker_main((void*)&slot); + } + + ap_update_child_status(AP_CHILD_THREAD_FROM_ID(slot), WORKER_STARTING, (request_rec *) NULL); + + if (ctx = NXContextAlloc((void (*)(void *)) worker_main, &slot, NX_PRIO_MED, ap_thread_stack_size, NX_CTX_NORMAL, &err)) { + char threadName[32]; + + sprintf (threadName, "Apache_Worker %d", slot); + NXContextSetName(ctx, threadName); + err = NXThreadCreate(ctx, NX_THR_BIND_CONTEXT, &tid); + if (err) { + NXContextFree (ctx); + } + } + + if (err) { + /* create thread didn't succeed. Fix the scoreboard or else + * it will say SERVER_STARTING forever and ever + */ + ap_update_child_status(AP_CHILD_THREAD_FROM_ID(slot), WORKER_DEAD, (request_rec *) NULL); + + /* In case system resources are maxxed out, we don't want + Apache running away with the CPU trying to fork over and + over and over again. */ + apr_thread_yield(); + + return -1; + } + + ap_scoreboard_image->servers[0][slot].tid = tid; + + return 0; +} + + +/* start up a bunch of worker threads */ +static void startup_workers(int number_to_start) +{ + int i; + + for (i = 0; number_to_start && i < ap_threads_limit; ++i) { + if (ap_scoreboard_image->servers[0][i].status != WORKER_DEAD) { + continue; + } + if (make_child(ap_server_conf, i) < 0) { + break; + } + --number_to_start; + } +} + + +/* + * idle_spawn_rate is the number of children that will be spawned on the + * next maintenance cycle if there aren't enough idle servers. It is + * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by + * without the need to spawn. + */ +static int idle_spawn_rate = 1; +#ifndef MAX_SPAWN_RATE +#define MAX_SPAWN_RATE (32) +#endif +static int hold_off_on_exponential_spawning; + +static void perform_idle_server_maintenance(apr_pool_t *p) +{ + int i; + int to_kill; + int idle_count; + worker_score *ws; + int free_length; + int free_slots[MAX_SPAWN_RATE]; + int last_non_dead; + int total_non_dead; + + /* initialize the free_list */ + free_length = 0; + + to_kill = -1; + idle_count = 0; + last_non_dead = -1; + total_non_dead = 0; + + ap_sync_scoreboard_image(); + for (i = 0; i < ap_threads_limit; ++i) { + int status; + + if (i >= ap_max_workers_limit && free_length == idle_spawn_rate) + break; + ws = &ap_scoreboard_image->servers[i][0]; + status = ws->status; + if (status == WORKER_DEAD) { + /* try to keep children numbers as low as possible */ + if (free_length < idle_spawn_rate) { + free_slots[free_length] = i; + ++free_length; + } + } + else { + /* We consider a starting server as idle because we started it + * at least a cycle ago, and if it still hasn't finished starting + * then we're just going to swamp things worse by forking more. + * So we hopefully won't need to fork more if we count it. + * This depends on the ordering of SERVER_READY and SERVER_STARTING. + */ + if (status <= SERVER_READY) { + ++ idle_count; + /* always kill the highest numbered child if we have to... + * no really well thought out reason ... other than observing + * the server behaviour under linux where lower numbered children + * tend to service more hits (and hence are more likely to have + * their data in cpu caches). + */ + to_kill = i; + } + + ++total_non_dead; + last_non_dead = i; + } + } + ap_max_workers_limit = last_non_dead + 1; + if (idle_count > ap_threads_max_free) { + /* kill off one child... we use the pod because that'll cause it to + * shut down gracefully, in case it happened to pick up a request + * while we were counting + */ + idle_spawn_rate = 1; + } + else if (idle_count < ap_threads_min_free) { + /* terminate the free list */ + if (free_length == 0) { + /* only report this condition once */ + static int reported = 0; + + if (!reported) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ap_server_conf, + "server reached MaxClients setting, consider" + " raising the MaxClients setting"); + reported = 1; + } + idle_spawn_rate = 1; + } + else { + if (idle_spawn_rate >= 8) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf, + "server seems busy, (you may need " + "to increase StartServers, or Min/MaxSpareServers), " + "spawning %d children, there are %d idle, and " + "%d total children", idle_spawn_rate, + idle_count, total_non_dead); + } + for (i = 0; i < free_length; ++i) { + make_child(ap_server_conf, free_slots[i]); + } + /* the next time around we want to spawn twice as many if this + * wasn't good enough, but not if we've just done a graceful + */ + if (hold_off_on_exponential_spawning) { + --hold_off_on_exponential_spawning; + } + else if (idle_spawn_rate < MAX_SPAWN_RATE) { + idle_spawn_rate *= 2; + } + } + } + else { + idle_spawn_rate = 1; + } +} + +static int setup_listeners(server_rec *s) +{ + ap_listen_rec *lr; + int sockdes; + + if (ap_setup_listeners(s) < 1 ) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s, + "no listening sockets available, shutting down"); + return -1; + } + + listenmaxfd = -1; + FD_ZERO(&listenfds); + for (lr = ap_listeners; lr; lr = lr->next) { + apr_os_sock_get(&sockdes, lr->sd); + FD_SET(sockdes, &listenfds); + if (sockdes > listenmaxfd) { + listenmaxfd = sockdes; + } + } + return 0; +} + +/***************************************************************** + * Executive routines. + */ + +int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) +{ + int index; + int remaining_workers_to_start; + apr_status_t status=0; + + pconf = _pconf; + ap_server_conf = s; + + if (setup_listeners(s)) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, status, s, + "no listening sockets available, shutting down"); + return -1; + } + + ap_log_pid(pconf, ap_pid_fname); + + worker_thread_count = 0; + apr_thread_mutex_create(&worker_thread_count_mutex, pconf); + apr_thread_mutex_create(&accept_mutex, pconf); + if (!is_graceful) { + ap_run_pre_mpm(pconf, SB_NOT_SHARED); + } + + set_signals(); + +/* Normal child main stuff */ + + apr_pool_create(&pmain, pconf); + + /* needs to be done before we switch UIDs so we have permissions */ + reopen_scoreboard(pmain); + + ap_run_child_init(pmain, ap_server_conf); + + +/* End Normal child main stuff */ + + if (ap_threads_max_free < ap_threads_min_free + 1) /* Don't thrash... */ + ap_threads_max_free = ap_threads_min_free + 1; + + /* If we're doing a graceful_restart then we're going to see a lot + * of children exiting immediately when we get into the main loop + * below (because we just sent them SIGWINCH). This happens pretty + * rapidly... and for each one that exits we'll start a new one until + * we reach at least daemons_min_free. But we may be permitted to + * start more than that, so we'll just keep track of how many we're + * supposed to start up without the 1 second penalty between each fork. + */ + remaining_workers_to_start = ap_threads_to_start; + if (remaining_workers_to_start > ap_threads_limit) { + remaining_workers_to_start = ap_threads_limit; + } + if (!is_graceful) { + startup_workers(remaining_workers_to_start); + remaining_workers_to_start = 0; + } + else { + /* give the system some time to recover before kicking into + * exponential mode */ + hold_off_on_exponential_spawning = 10; + } + + 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()); + restart_pending = shutdown_pending = 0; + + printf("%s \n", ap_get_server_version()); + + while (!restart_pending && !shutdown_pending) { + int worker_slot; + apr_wait_t status; + +// /* this is a memory leak, but I'll fix it later. */ +// apr_proc_t pid; +// +// ap_wait_or_timeout(&status, &pid, pconf); +// +// /* XXX: if it takes longer than 1 second for all our children +// * to start up and get into IDLE state then we may spawn an +// * extra child +// */ +// if (pid.pid != -1) { +// ap_process_child_status(&pid, status); +// /* non-fatal death... note that it's gone in the scoreboard. */ +// ap_sync_scoreboard_image(); +// child_slot = find_child_by_pid(&pid); +// if (child_slot >= 0) { +// ap_update_child_status(AP_CHILD_THREAD_FROM_ID(child_slot), WORKER_DEAD, +// (request_rec *) NULL); +// if (remaining_workers_to_start && child_slot < ap_threads_limit) { +// /* we're still doing a 1-for-1 replacement of dead +// * children with new children +// */ +// make_child(ap_server_conf, child_slot); +// --remaining_workers_to_start; +// } +//#if APR_HAS_OTHER_CHILD +// } +// else if (apr_proc_other_child_read(&pid, status) == 0) { +// /* handled */ +//#endif +// } +// else if (is_graceful) { +// /* Great, we've probably just lost a slot in the +// * scoreboard. Somehow we don't know about this +// * child. +// */ +// ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, +// 0, ap_server_conf, +// "long lost child came home! (pid %ld)", (long)pid.pid); +// } +// /* Don't perform idle maintenance when a child dies, +// * only do it when there's a timeout. Remember only a +// * finite number of children can die, and it's pretty +// * pathological for a lot to die suddenly. +// */ +// continue; +// } +// else if (remaining_workers_to_start) { +// /* we hit a 1 second timeout in which none of the previous +// * generation of children needed to be reaped... so assume +// * they're all done, and pick up the slack if any is left. +// */ +// startup_children(remaining_workers_to_start); +// remaining_workers_to_start = 0; +// /* In any event we really shouldn't do the code below because +// * few of the servers we just started are in the IDLE state +// * yet, so we'd mistakenly create an extra server. +// */ +// continue; +// } + +// perform_idle_server_maintenance(pconf); + apr_thread_yield(); + } + + if (shutdown_pending) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, + "caught SIGTERM, shutting down"); + + while (worker_thread_count > 0) + apr_thread_yield(); + + printf ("Press any key to continue..."); + getc(stdin); + wait_to_finish = 0; + return 1; + } + + /* we've been told to restart */ +// apr_signal(SIGHUP, SIG_IGN); + if (one_process) { + /* not worth thinking about */ + return 1; + } + + /* advance to the next generation */ + /* XXX: we really need to make sure this new generation number isn't in + * use by any of the children. + */ + ++ap_my_generation; + ap_scoreboard_image->global.running_generation = ap_my_generation; + update_scoreboard_global(); + + if (is_graceful) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, + "Graceful restart requested, doing restart"); + + /* kill off the idle ones */ + +#ifndef SCOREBOARD_FILE + /* This is mostly for debugging... so that we know what is still + * gracefully dealing with existing request. But we can't really + * do it if we're in a SCOREBOARD_FILE because it'll cause + * corruption too easily. + */ + ap_sync_scoreboard_image(); + for (index = 0; index < ap_threads_limit; ++index) { + if (ap_scoreboard_image->servers[0][index].status != WORKER_DEAD) { + ap_scoreboard_image->servers[0][index].status = SERVER_GRACEFUL; + } + } +#endif + } + else { + /* Kill 'em off */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, + "SIGHUP received. Attempting to restart"); + } + + return 0; +} + +static void netware_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) +{ + static int restart_num = 0; + int no_detach, debug; + + debug = ap_exists_config_define("DEBUG"); + + if (debug) + no_detach = one_process = 1; + else + { + no_detach = ap_exists_config_define("NO_DETACH"); + one_process = ap_exists_config_define("ONE_PROCESS"); + } + + /* sigh, want this only the second time around */ + if (restart_num++ == 1) { + is_graceful = 0; + + if (!one_process && !no_detach) { + apr_proc_detach(); + } + + parent_pid = ap_my_pid = getpid(); + } + + ap_listen_pre_config(); + ap_threads_to_start = DEFAULT_START_DAEMON; + ap_threads_min_free = DEFAULT_MIN_FREE_DAEMON; + ap_threads_max_free = DEFAULT_MAX_FREE_DAEMON; + ap_threads_limit = HARD_THREAD_LIMIT; + ap_pid_fname = DEFAULT_PIDLOG; + ap_scoreboard_fname = DEFAULT_SCOREBOARD; + ap_lock_fname = DEFAULT_LOCKFILE; + ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + ap_extended_status = 0; + + apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); +} + +static void netware_mpm_hooks(apr_pool_t *p) +{ + ap_hook_pre_config(netware_pre_config, NULL, NULL, APR_HOOK_MIDDLE); +} + +void netware_rewrite_args(process_rec *process) +{ + char *def_server_root; + char optbuf[3]; + const char *optarg; + apr_getopt_t *opt; + apr_array_header_t *mpm_new_argv; + + + /* Rewrite process->argv[]; + * + * add default -d serverroot from the path of this executable + * + * The end result will look like: + * The -d serverroot default from the running executable + */ + if (process->argc > 0) { + char *s = apr_pstrdup (process->pconf, process->argv[0]); + if (s) { + int i, len = strlen(s); + + for (i=len; i; i--) { + if (s[i] == '\\' || s[i] == '/') { + s[i] = NULL; + apr_filepath_merge(&def_server_root, NULL, s, + APR_FILEPATH_TRUENAME, process->pool); + break; + } + } + /* Use process->pool so that the rewritten argv + * lasts for the lifetime of the server process, + * because pconf will be destroyed after the + * initial pre-flight of the config parser. + */ + mpm_new_argv = apr_array_make(process->pool, process->argc + 2, + sizeof(const char *)); + *(const char **)apr_array_push(mpm_new_argv) = process->argv[0]; + *(const char **)apr_array_push(mpm_new_argv) = "-d"; + *(const char **)apr_array_push(mpm_new_argv) = def_server_root; + + optbuf[0] = '-'; + optbuf[2] = '\0'; + apr_getopt_init(&opt, process->pool, process->argc, (char**) process->argv); + while (apr_getopt(opt, AP_SERVER_BASEARGS, optbuf + 1, &optarg) == APR_SUCCESS) { + switch (optbuf[1]) { + default: + *(const char **)apr_array_push(mpm_new_argv) = + apr_pstrdup(process->pool, optbuf); + + if (optarg) { + *(const char **)apr_array_push(mpm_new_argv) = optarg; + } + break; + } + } + process->argc = mpm_new_argv->nelts; + process->argv = (const char * const *) mpm_new_argv->elts; + } + } +} + +static const char *set_threads_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_threads_to_start = atoi(arg); + return NULL; +} + +static const char *set_min_free_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_threads_min_free = atoi(arg); + if (ap_threads_min_free <= 0) { + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "WARNING: detected MinSpareServers 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_threads_min_free = 1; + } + + return NULL; +} + +static const char *set_max_free_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_threads_max_free = atoi(arg); + return NULL; +} + +static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_threads_limit = atoi(arg); + if (ap_threads_limit > HARD_THREAD_LIMIT) { + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "WARNING: MaxClients of %d exceeds compile time limit " + "of %d servers,", ap_threads_limit, HARD_SERVER_LIMIT); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + " lowering MaxClients to %d. To increase, please " + "see the", HARD_SERVER_LIMIT); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + " HARD_SERVER_LIMIT define in %s.", + AP_MPM_HARD_LIMITS_FILE); + ap_threads_limit = HARD_THREAD_LIMIT; + } + else if (ap_threads_limit < 1) { + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "WARNING: Require MaxClients > 0, setting to 1"); + ap_threads_limit = 1; + } + return NULL; +} + +static const char *set_thread_stacksize(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_thread_stack_size = atoi(arg); + return NULL; +} + +static const command_rec netware_mpm_cmds[] = { +AP_INIT_TAKE1("ThreadStackSize", set_thread_stacksize, NULL, RSRC_CONF, + "Stack size each created thread will use."), +LISTEN_COMMANDS +AP_INIT_TAKE1("StartThreads", set_threads_to_start, NULL, RSRC_CONF, + "Number of worker threads launched at server startup"), +AP_INIT_TAKE1("MinSpareThreads", set_min_free_threads, NULL, RSRC_CONF, + "Minimum number of idle threads, to handle request spikes"), +AP_INIT_TAKE1("MaxSpareThreads", set_max_free_threads, NULL, RSRC_CONF, + "Maximum number of idle threads"), +AP_INIT_TAKE1("MaxThreads", set_thread_limit, NULL, RSRC_CONF, + "Maximum number of worker threads alive at the same time"), +{ NULL } +}; + +module AP_MODULE_DECLARE_DATA mpm_netware_module = { + MPM20_MODULE_STUFF, + netware_rewrite_args, /* 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 */ + netware_mpm_cmds, /* command apr_table_t */ + netware_mpm_hooks, /* register hooks */ +};