]> granicus.if.org Git - apache/commitdiff
First cut at the MPM for NetWare
authorBradley Nicholes <bnicholes@apache.org>
Tue, 16 Oct 2001 23:27:04 +0000 (23:27 +0000)
committerBradley Nicholes <bnicholes@apache.org>
Tue, 16 Oct 2001 23:27:04 +0000 (23:27 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@91514 13f79535-47bb-0310-9956-ffa450edef68

server/mpm/netware/mpm.h [new file with mode: 0644]
server/mpm/netware/mpm_default.h [new file with mode: 0644]
server/mpm/netware/mpm_netware.c [new file with mode: 0644]

diff --git a/server/mpm/netware/mpm.h b/server/mpm/netware/mpm.h
new file mode 100644 (file)
index 0000000..1ce5933
--- /dev/null
@@ -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
+ * <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.
+ */
+#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 (file)
index 0000000..6ae8d1a
--- /dev/null
@@ -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
+ * <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.
+ */
+
+#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 (file)
index 0000000..e16a7ab
--- /dev/null
@@ -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
+ * <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.
+ */
+
+/*
+ * 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 <unistd.h>
+#endif
+#if APR_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#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 <time.h>
+#endif
+
+#include <signal.h>
+
+#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 */
+};