From 4dce9f29bc40e348d81bbb81dbbd368c38a20c22 Mon Sep 17 00:00:00 2001 From: David Reid Date: Fri, 8 Oct 1999 19:02:36 +0000 Subject: [PATCH] Well here is the first attempt at an mpm for BeOS. It's based on the mpmt_pthread module but is totally threaded as that what BeOS needs. There's a lot that can be improved but now that it's committed others can start looking and helping. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@83951 13f79535-47bb-0310-9956-ffa450edef68 --- server/mpm/mpmt_beos/Makefile.libdir | 4 + server/mpm/mpmt_beos/mpm_default.h | 121 ++ server/mpm/mpmt_beos/mpmt_beos.c | 1532 ++++++++++++++++++++++++++ server/mpm/mpmt_beos/mpmt_beos.h | 68 ++ server/mpm/mpmt_beos/poll.c | 139 +++ server/mpm/mpmt_beos/poll.h | 89 ++ server/mpm/mpmt_beos/scoreboard.c | 182 +++ server/mpm/mpmt_beos/scoreboard.h | 233 ++++ 8 files changed, 2368 insertions(+) create mode 100644 server/mpm/mpmt_beos/Makefile.libdir create mode 100644 server/mpm/mpmt_beos/mpm_default.h create mode 100644 server/mpm/mpmt_beos/mpmt_beos.c create mode 100644 server/mpm/mpmt_beos/mpmt_beos.h create mode 100644 server/mpm/mpmt_beos/poll.c create mode 100644 server/mpm/mpmt_beos/poll.h create mode 100644 server/mpm/mpmt_beos/scoreboard.c create mode 100644 server/mpm/mpmt_beos/scoreboard.h diff --git a/server/mpm/mpmt_beos/Makefile.libdir b/server/mpm/mpmt_beos/Makefile.libdir new file mode 100644 index 0000000000..7b5254013a --- /dev/null +++ b/server/mpm/mpmt_beos/Makefile.libdir @@ -0,0 +1,4 @@ +This is a place-holder which indicates to Configure that it shouldn't +provide the default targets when building the Makefile in this directory. +Instead it'll just prepend all the important variable definitions, and +copy the Makefile.tmpl onto the end. diff --git a/server/mpm/mpmt_beos/mpm_default.h b/server/mpm/mpmt_beos/mpm_default.h new file mode 100644 index 0000000000..a60f122266 --- /dev/null +++ b/server/mpm/mpmt_beos/mpm_default.h @@ -0,0 +1,121 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. 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. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" 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 names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``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 GROUP 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 Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_MPM_DEFAULT_H +#define APACHE_MPM_DEFAULT_H + +/* 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 5 +#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 10 +#endif + +/* Minimum --- fewer than this, and more will be created */ + +#ifndef DEFAULT_MIN_FREE_DAEMON +#define DEFAULT_MIN_FREE_DAEMON 5 +#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. + */ +#ifdef NO_THREADS +#define HARD_SERVER_LIMIT 256 +#endif +#ifndef HARD_SERVER_LIMIT +#define HARD_SERVER_LIMIT 8 +#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. + */ +#ifdef NO_THREADS +#define HARD_THREAD_LIMIT 1 +#endif +#ifndef HARD_THREAD_LIMIT +#define HARD_THREAD_LIMIT 64 +#endif + +#ifdef NO_THREADS +#define DEFAULT_THREADS_PER_CHILD 1 +#endif +#ifndef DEFAULT_THREADS_PER_CHILD +#define DEFAULT_THREADS_PER_CHILD 10 +#endif + +#endif /* AP_MPM_DEFAULT_H */ diff --git a/server/mpm/mpmt_beos/mpmt_beos.c b/server/mpm/mpmt_beos/mpmt_beos.c new file mode 100644 index 0000000000..f6044c0bd5 --- /dev/null +++ b/server/mpm/mpmt_beos/mpmt_beos.c @@ -0,0 +1,1532 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. 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. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" 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 names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``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 GROUP 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 Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* This module is effectivly mpmt_pthread much modified to + * allow it work on BeOS. It's stable and works OK. + */ + +#define CORE_PRIVATE + +#include "apr_portable.h" +#include "httpd.h" +#include "http_main.h" +#include "http_log.h" +#include "http_config.h" /* for read_config */ +#include "http_core.h" /* for get_remote_host */ +#include "http_connection.h" +#include "ap_mpm.h" +#include "beosd.h" +#include "iol_socket.h" +#include "ap_listen.h" +#include "scoreboard.h" +#include "acceptlock.h" +#include "mutex.h" +#include "poll.h" + +/* + * Actual definitions of config globals + */ + +int ap_threads_per_child=0; /* Worker threads per child */ +int ap_max_requests_per_child=0; +static char *ap_pid_fname=NULL; +static char *ap_scoreboard_fname=NULL; +static int ap_daemons_to_start=0; +static int min_spare_threads=0; +static int max_spare_threads=0; +static int ap_daemons_limit=0; +static time_t ap_restart_time=0; +API_VAR_EXPORT int ap_extended_status = 0; +static int workers_may_exit = 0; +static int requests_this_child; +static int num_listenfds = 0; +static struct pollfd *listenfds; + +/* The structure used to pass unique initialization info to each thread */ +typedef struct { + int pid; + thread_id tid; + int sd; + ap_context_t *tpool; /* "pthread" would be confusing */ +} proc_info; + +#if 0 +#define SAFE_ACCEPT(stmt) do {if (ap_listeners->next != NULL) {stmt;}} while (0) +#else +#define SAFE_ACCEPT(stmt) do {stmt;} while (0) +#endif + +/* + * 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. + */ +static int max_daemons_limit = -1; +static char ap_coredump_dir[MAX_STRING_LEN]; +port_id port_of_death; + +/* *Non*-shared http_main globals... */ + +static server_rec *server_conf; + +/* 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; + +#ifdef DEBUG_SIGSTOP +int raise_sigstop_flags; +#endif + +#ifdef HAS_OTHER_CHILD +/* used to maintain list of children which aren't part of the scoreboard */ +typedef struct other_child_rec other_child_rec; +struct other_child_rec { + other_child_rec *next; + int pid; + void (*maintenance) (int, void *, ap_wait_t); + void *data; + int write_fd; +}; +static other_child_rec *other_children; +#endif + +static ap_context_t *pconf; /* Pool for config stuff */ +static ap_context_t *pchild; /* Pool for httpd child stuff */ + +static int my_pid; /* Linux getpid() doesn't work except in main thread. Use + this instead */ +/* Keep track of the number of worker threads currently active */ +static int worker_thread_count; +static be_mutex_t worker_thread_count_mutex; + +/* Global, alas, so http_core can talk to us */ +enum server_token_type ap_server_tokens = SrvTk_FULL; + +API_EXPORT(const server_rec *) ap_get_server_conf(void) +{ + return (server_conf); +} + +API_EXPORT(int) ap_get_max_daemons(void) +{ + return max_daemons_limit; +} + +/* a clean exit from a child with proper cleanup + static void clean_child_exit(int code) __attribute__ ((noreturn)); */ +void clean_child_exit(int code) +{ + if (pchild) { + ap_destroy_pool(pchild); + } + exit(code); +} + +/***************************************************************** + * dealing with other children + */ + +#ifdef HAS_OTHER_CHILD +API_EXPORT(void) ap_register_other_child(int pid, + void (*maintenance) (int reason, void *, ap_wait_t status), + void *data, int write_fd) +{ + other_child_rec *ocr; + + ocr = ap_palloc(pconf, sizeof(*ocr)); + ocr->pid = pid; + ocr->maintenance = maintenance; + ocr->data = data; + ocr->write_fd = write_fd; + ocr->next = other_children; + other_children = ocr; +} + +/* note that since this can be called by a maintenance function while we're + * scanning the other_children list, all scanners should protect themself + * by loading ocr->next before calling any maintenance function. + */ +API_EXPORT(void) ap_unregister_other_child(void *data) +{ + other_child_rec **pocr, *nocr; + + for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) { + if ((*pocr)->data == data) { + nocr = (*pocr)->next; + (*(*pocr)->maintenance) (OC_REASON_UNREGISTER, (*pocr)->data, -1); + *pocr = nocr; + /* XXX: um, well we've just wasted some space in pconf ? */ + return; + } + } +} + +/* test to ensure that the write_fds are all still writable, otherwise + * invoke the maintenance functions as appropriate */ +static void probe_writable_fds(void) +{ + return; +#if 0 + fd_set writable_fds; + int fd_max; + other_child_rec *ocr, *nocr; + struct timeval tv; + int rc; + + if (other_children == NULL) + return; + + fd_max = 0; + FD_ZERO(&writable_fds); + do { + for (ocr = other_children; ocr; ocr = ocr->next) { + if (ocr->write_fd == -1) + continue; + FD_SET(ocr->write_fd, &writable_fds); + if (ocr->write_fd > fd_max) { + fd_max = ocr->write_fd; + } + } + if (fd_max == 0) + return; + + tv.tv_sec = 0; + tv.tv_usec = 0; + rc = ap_select(fd_max + 1, NULL, &writable_fds, NULL, &tv); + } while (rc == -1 && errno == EINTR); + + if (rc == -1) { + /* XXX: uhh this could be really bad, we could have a bad file + * descriptor due to a bug in one of the maintenance routines */ + ap_log_unixerr("probe_writable_fds", "select", + "could not probe writable fds", server_conf); + return; + } + if (rc == 0) + return; + + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->write_fd == -1) + continue; + if (FD_ISSET(ocr->write_fd, &writable_fds)) + continue; + (*ocr->maintenance) (OC_REASON_UNWRITABLE, ocr->data, -1); + } +#endif +} + +/* possibly reap an other_child, return 0 if yes, -1 if not */ +static int reap_other_child(int pid, ap_wait_t status) +{ + other_child_rec *ocr, *nocr; + + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->pid != pid) + continue; + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); + return 0; + } + return -1; +} +#endif + +static void reclaim_child_processes(int terminate) +{ + int i, status; + long int waittime = 1024 * 16; /* in usecs */ + struct timeval tv; + int waitret, tries; + int not_dead_yet; +#ifdef HAS_OTHER_CHILD + other_child_rec *ocr, *nocr; +#endif + + ap_sync_scoreboard_image(); + + for (tries = terminate ? 4 : 1; tries <= 9; ++tries) { + /* don't want to hold up progress any more than + * necessary, but we need to allow children a few moments to exit. + * Set delay with an exponential backoff. + */ + tv.tv_sec = waittime / 1000000; + tv.tv_usec = waittime % 1000000; + waittime = waittime * 4; + ap_select(0, NULL, NULL, NULL, &tv); + + /* now see who is done */ + not_dead_yet = 0; + for (i = 0; i < max_daemons_limit; ++i) { + int pid = ap_scoreboard_image->parent[i].pid; + + if (pid == my_pid || pid == 0) + continue; + + waitret = waitpid(pid, &status, WNOHANG); + if (waitret == pid || waitret == -1) { + ap_scoreboard_image->parent[i].pid = 0; + continue; + } + ++not_dead_yet; + switch (tries) { + case 1: /* 16ms */ + case 2: /* 82ms */ + break; + case 3: /* 344ms */ + case 4: /* 16ms */ + case 5: /* 82ms */ + case 6: /* 344ms */ + case 7: /* 1.4sec */ + /* ok, now it's being annoying */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, + server_conf, + "child process %d still did not exit, sending a SIGTERM", + pid); + kill(pid, SIGTERM); + break; + case 8: /* 6 sec */ + /* die child scum */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "child process %d still did not exit, sending a SIGKILL", + pid); + kill(pid, SIGKILL); + break; + case 9: /* 14 sec */ + /* gave it our best shot, but alas... If this really + * is a child we are trying to kill and it really hasn't + * exited, we will likely fail to bind to the port + * after the restart. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "could not make child process %d exit, " + "attempting to continue anyway", pid); + break; + } + } +#ifdef HAS_OTHER_CHILD + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->pid == -1) + continue; + + waitret = waitpid(ocr->pid, &status, WNOHANG); + if (waitret == ocr->pid) { + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); + } + else if (waitret == 0) { + (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, -1); + ++not_dead_yet; + } + else if (waitret == -1) { + /* uh what the heck? they didn't call unregister? */ + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_LOST, ocr->data, -1); + } + } +#endif + if (!not_dead_yet) { + /* nothing left to wait for */ + break; + } + } +} + +/* Finally, this routine is used by the caretaker process to wait for + * a while... + */ + +/* number of calls to wait_or_timeout between writable probes */ +#ifndef INTERVAL_OF_WRITABLE_PROBES +#define INTERVAL_OF_WRITABLE_PROBES 10 +#endif +static int wait_or_timeout_counter; + +static int wait_or_timeout(ap_wait_t *status) +{ + struct timeval tv; + int ret; + + ++wait_or_timeout_counter; + if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) { + wait_or_timeout_counter = 0; +#ifdef HAS_OTHER_CHILD + probe_writable_fds(); +#endif + } + ret = waitpid(-1, status, WNOHANG); + if (ret == -1 && errno == EINTR) { + return -1; + } + if (ret > 0) { + return ret; + } + tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000; + tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000; + ap_select(0, NULL, NULL, NULL, &tv); + return -1; +} + +/* handle all varieties of core dumping signals */ +static void sig_coredump(int sig) +{ + chdir(ap_coredump_dir); + signal(sig, SIG_DFL); + kill(my_pid, sig); + /* At this point we've got sig blocked, because we're still inside + * the signal handler. When we leave the signal handler it will + * be unblocked, and we'll take the signal... and coredump or whatever + * is appropriate for this particular Unix. In addition the parent + * will see the real signal we received -- whereas if we called + * abort() here, the parent would only see SIGABRT. + */ +} + +static void just_die(int sig) +{ + clean_child_exit(0); +} + +/***************************************************************** + * Connection structures and accounting... + */ + +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful; +ap_generation_t volatile ap_my_generation; + +/* + * ap_start_shutdown() and ap_start_restart(), below, are a first stab at + * functions to initiate shutdown or restart without relying on signals. + * Previously this was initiated in sig_term() and restart() signal handlers, + * but we want to be able to start a shutdown/restart from other sources -- + * e.g. on Win32, from the service manager. Now the service manager can + * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that + * these functions can also be called by the child processes, since global + * variables are no longer used to pass on the required action to the parent. + * + * These should only be called from the parent process itself, since the + * parent process will use the shutdown_pending and restart_pending variables + * to determine whether to shutdown or restart. The child process should + * call signal_parent() directly to tell the parent to die -- this will + * cause neither of those variable to be set, which the parent will + * assume means something serious is wrong (which it will be, for the + * child to force an exit) and so do an exit anyway. + */ + +void ap_start_shutdown(void) +{ + 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; +} + +/* do a graceful restart if graceful == 1 */ +void ap_start_restart(int graceful) +{ + + if (restart_pending == 1) { + /* Probably not an error - don't bother reporting it */ + return; + } + restart_pending = 1; + is_graceful = graceful; +} + +static void sig_term(int sig) +{ + ap_start_shutdown(); +} + +static void restart(int sig) +{ + ap_start_restart(sig == SIGWINCH); +} + +static void set_signals(void) +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + if (!one_process) { + sa.sa_handler = sig_coredump; + + if (sigaction(SIGSEGV, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGSEGV)"); + if (sigaction(SIGBUS, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGBUS)"); + if (sigaction(SIGABRT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABRT)"); + if (sigaction(SIGILL, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGILL)"); + sa.sa_flags = 0; + } + sa.sa_handler = sig_term; + if (sigaction(SIGTERM, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGTERM)"); + if (sigaction(SIGINT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGINT)"); + + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGPIPE)"); + + /* we want to ignore HUPs and WINCH while we're busy processing one */ + sigaddset(&sa.sa_mask, SIGHUP); + sigaddset(&sa.sa_mask, SIGWINCH); + sa.sa_handler = restart; + if (sigaction(SIGHUP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGHUP)"); + if (sigaction(SIGWINCH, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGWINCH)"); +} + +static void process_child_status(int pid, ap_wait_t status) +{ + /* Child died... if it died due to a fatal error, + * we should simply bail out. + */ + if ((WIFEXITED(status)) && + WEXITSTATUS(status) == APEXIT_CHILDFATAL) { + ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server_conf, + "Child %d returned a Fatal error... \n" + "Apache is exiting!", + pid); + exit(APEXIT_CHILDFATAL); + } + if (WIFSIGNALED(status)) { + switch (WTERMSIG(status)) { + case SIGTERM: + case SIGHUP: + case SIGUSR1: + case SIGKILL: + break; + default: +#ifdef SYS_SIGLIST +#ifdef WCOREDUMP + if (WCOREDUMP(status)) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %s (%d), " + "possible coredump in %s", + pid, (WTERMSIG(status) >= NumSIG) ? "" : + SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status), + ap_coredump_dir); + } + else { +#endif + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %s (%d)", pid, + SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status)); +#ifdef WCOREDUMP + } +#endif +#else + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %d", + pid, WTERMSIG(status)); +#endif + } + } +} + +static int setup_listeners(ap_context_t *pconf, server_rec *s) +{ + ap_listen_rec *lr; + int num_listeners = 0; + if (ap_listen_open(pconf, s->port)) { + return 0; + } + for (lr = ap_listeners; lr; lr = lr->next) { + num_listeners++; + } + return num_listeners; +} + +/***************************************************************** + * Here follows a long bunch of generic server bookkeeping stuff... + */ + +#define sock_disable_nagle(s) /* NOOP */ + +int ap_graceful_stop_signalled(void) +{ + /* XXX - Does this really work? - Manoj */ + return is_graceful; +} + +/***************************************************************** + * Child process main loop. + */ + +static void process_socket(ap_context_t *p, struct sockaddr *sa_client, int csd, int my_child_num, int my_thread_num) +{ + struct sockaddr sa_server; /* ZZZZ */ + int len = sizeof(struct sockaddr); + BUFF *conn_io; + conn_rec *current_conn; + ap_iol *iol; + long conn_id = my_child_num * HARD_THREAD_LIMIT + my_thread_num; + + if (getsockname(csd, &sa_server, &len) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname"); + closesocket(csd); + return; + } + + sock_disable_nagle(csd); + + iol = beos_attach_socket(csd); + if (iol == NULL) { + if (errno == EBADF) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "filedescriptor (%u) larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", csd, FD_SETSIZE); + } + else { + ap_log_error(APLOG_MARK, APLOG_WARNING, NULL, + "error attaching to socket"); + } + closesocket(csd); + return; + } + + (void) ap_update_child_status(my_child_num, my_thread_num, + SERVER_BUSY_READ, (request_rec *) NULL); + conn_io = ap_bcreate(p, B_RDWR); + ap_bpush_iol(conn_io, iol); + + current_conn = ap_new_connection(p, server_conf, conn_io, + (const struct sockaddr_in *) sa_client, + (const struct sockaddr_in *) &sa_server, + conn_id); + + ap_process_connection(current_conn); +} + +static int32 worker_thread(void * dummy) +{ + proc_info * ti = dummy; + int process_slot = ti->pid; + int thread_slot = ti->tid; + ap_context_t *tpool = ti->tpool; + struct sockaddr sa_client; + ap_socket_t *csd = NULL; + ap_context_t *ptrans; /* Pool for per-transaction stuff */ + ap_socket_t *sd = NULL; + int srv; + int curr_pollfd, last_pollfd = 0; + int thesock; + sigset_t sig_mask; + + free(ti); + + /* block the signals for this thread */ + sigfillset(&sig_mask); + sigprocmask(SIG_BLOCK, &sig_mask, NULL); + + ap_create_context(&ptrans, tpool); + + be_mutex_lock(&worker_thread_count_mutex); + worker_thread_count++; + be_mutex_unlock(&worker_thread_count_mutex); + + /* TODO: Switch to a system where threads reuse the results from earlier + poll calls - manoj */ + while (!workers_may_exit) { + workers_may_exit |= (ap_max_requests_per_child != 0) && (requests_this_child <= 0); + if (workers_may_exit) break; + + (void) ap_update_child_status(process_slot, thread_slot, SERVER_READY, + (request_rec *) NULL); + SAFE_ACCEPT(intra_mutex_on(0)); + if (workers_may_exit) { + SAFE_ACCEPT(intra_mutex_off(0)); + break; + } + SAFE_ACCEPT(accept_mutex_on(0)); + while (!workers_may_exit) { + srv = poll(listenfds, num_listenfds + 1, -1); + if (srv < 0) { + if (errno == EINTR) { + continue; + } + + /* poll() will only return errors in catastrophic + * circumstances. Let's try exiting gracefully, for now. */ + ap_log_error(APLOG_MARK, APLOG_ERR, (const server_rec *) + ap_get_server_conf(), "poll: (listen)"); + workers_may_exit = 1; + } + + if (workers_may_exit) break; + + if (num_listenfds == 1) { + sd = ap_listeners->sd; + goto got_fd; + } + else { + /* find a listener */ + curr_pollfd = last_pollfd; + do { + curr_pollfd++; + if (curr_pollfd > num_listenfds) { + curr_pollfd = 1; + } + /* XXX: Should we check for POLLERR? */ + if (listenfds[curr_pollfd].revents & POLLIN) { + last_pollfd = curr_pollfd; + ap_put_os_sock(&sd, &listenfds[curr_pollfd].fd, tpool); + goto got_fd; + } + } while (curr_pollfd != last_pollfd); + } + } + got_fd: + if (!workers_may_exit) { + ap_accept(&csd, sd); + SAFE_ACCEPT(accept_mutex_off(0)); + SAFE_ACCEPT(intra_mutex_off(0)); + } + else { + SAFE_ACCEPT(accept_mutex_off(0)); + SAFE_ACCEPT(intra_mutex_off(0)); + break; + } + ap_get_os_sock(csd, &thesock); + process_socket(ptrans, &sa_client, thesock, process_slot, thread_slot); + ap_clear_pool(ptrans); + requests_this_child--; + } + + ap_destroy_pool(tpool); + ap_update_child_status(process_slot, thread_slot, SERVER_DEAD, + (request_rec *) NULL); + be_mutex_lock(&worker_thread_count_mutex); + worker_thread_count--; + if (worker_thread_count == 0) { + /* All the threads have exited, now finish the shutdown process + * by signalling the sigwait thread */ + kill(my_pid, SIGTERM); + } + be_mutex_unlock(&worker_thread_count_mutex); + + return (0); +} + + +static int32 child_main(void * data) +{ + int child_num_arg = (int) data; + sigset_t sig_mask; + thread_id thread; + int i; + int my_child_num = child_num_arg; + proc_info *my_info = NULL; + ap_listen_rec *lr; + struct sigaction sa; + int32 msg; + char buf; + + my_pid = getpid(); + ap_create_context(&pchild, pconf); + + /*stuff to do before we switch id's, so we have permissions.*/ + reopen_scoreboard(pchild); + + SAFE_ACCEPT(intra_mutex_init(pchild, 1)); + SAFE_ACCEPT(accept_mutex_child_init(pchild)); + + if (beosd_setup_child()) { + clean_child_exit(APEXIT_CHILDFATAL); + } + + ap_child_init_hook(pchild, server_conf); + + /*done with init critical section */ + + /* All threads should mask signals out, accoring to sigwait(2) man page */ + sigfillset(&sig_mask); + sigprocmask(SIG_BLOCK, &sig_mask, NULL); + + requests_this_child = ap_max_requests_per_child; + + /* Set up the pollfd array */ + listenfds = ap_palloc(pchild, sizeof(struct pollfd) * (num_listenfds)); + for (lr = ap_listeners, i = 0; i < num_listenfds; lr = lr->next, ++i) { + ap_get_os_sock(lr->sd, &listenfds[i].fd); + listenfds[i].events = POLLIN; /* should we add POLLPRI ?*/ + listenfds[i].revents = 0; + } + + /* Setup worker threads */ + + worker_thread_count = 0; + be_mutex_init(&worker_thread_count_mutex, NULL); + + for (i=0; i < ap_threads_per_child; i++) { + + my_info = (proc_info *)malloc(sizeof(proc_info)); + if (my_info == NULL) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "malloc: out of memory"); + clean_child_exit(APEXIT_CHILDFATAL); + } + my_info->pid = my_child_num; + my_info->tid = i; + my_info->sd = 0; + ap_create_context(&my_info->tpool, pchild); + + /* We are creating threads right now */ + (void) ap_update_child_status(my_child_num, i, SERVER_STARTING, + (request_rec *) NULL); + + if ((thread = spawn_thread(worker_thread, "httpd_worker_thread", + B_NORMAL_PRIORITY, my_info)) < B_NO_ERROR) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "spawn_thread: unable to create worker thread"); + /* 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 if we exit. */ + sleep(10); + clean_child_exit(APEXIT_CHILDFATAL); + } + resume_thread(thread); + + /* We let each thread update it's own scoreboard entry. This is done + * because it let's us deal with tid better. + */ + } + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = just_die; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + /* this blocks until it gets a message... */ + read_port(port_of_death, &msg, &buf, 1); + + return (0); +} + +static int make_child(server_rec *s, int slot, time_t now) /* ZZZ */ +{ + thread_id tid; + + if (slot + 1 > max_daemons_limit) { + max_daemons_limit = slot + 1; + } + + if (one_process) { + set_signals(); + ap_scoreboard_image->parent[slot].pid = getpid(); + //child_main(slot); + } + + tid = spawn_thread(child_main, "httpd_child", B_NORMAL_PRIORITY, + (void*)slot); + if (tid < B_NO_ERROR) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, "spawn_thread: Unable to fork new process"); + /* 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. */ + sleep(10); + + return -1; + } + resume_thread(tid); + +/* if (!pid) { + RAISE_SIGSTOP(MAKE_CHILD); + signal(SIGTERM, just_die); + child_main(slot); + return 0; + }*/ + /* else */ + ap_scoreboard_image->parent[slot].pid = tid; + return 0; +} + +/* start up a bunch of children */ +static void startup_children(int number_to_start) +{ + int i; + + for (i = 0; number_to_start && i < ap_daemons_limit; ++i) { + if (ap_scoreboard_image->parent[i].pid != 0) { + continue; + } + if (make_child(server_conf, i, 0) < 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(void) +{ + int i, j; + int idle_thread_count; + thread_score *ss; + time_t now = 0; + int free_length; + int free_slots[MAX_SPAWN_RATE]; + int last_non_dead; + int total_non_dead; + + /* initialize the free_list */ + free_length = 0; + + idle_thread_count = 0; + last_non_dead = -1; + total_non_dead = 0; + + ap_check_signals(); + + ap_sync_scoreboard_image(); + for (i = 0; i < ap_daemons_limit; ++i) { + /* Initialization to satisfy the compiler. It doesn't know + * that ap_threads_per_child is always > 0 */ + int status = SERVER_DEAD; + int any_dying_threads = 0; + int all_dead_threads = 1; + int idle_thread_addition = 0; + + if (i >= max_daemons_limit && free_length == idle_spawn_rate) + break; + for (j = 0; j < ap_threads_per_child; j++) { + ss = &ap_scoreboard_image->servers[i][j]; + status = ss->status; + + any_dying_threads = any_dying_threads || (status == SERVER_DEAD) + || (status == SERVER_GRACEFUL); + all_dead_threads = all_dead_threads && (status == SERVER_DEAD); + + /* 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_thread_addition; + } + } + if (all_dead_threads && free_length < idle_spawn_rate) { + free_slots[free_length] = i; + ++free_length; + } + if (!all_dead_threads) { + last_non_dead = i; + } + if (!any_dying_threads) { + ++total_non_dead; + idle_thread_count += idle_thread_addition; + } + } + max_daemons_limit = last_non_dead + 1; + + if (idle_thread_count > max_spare_threads) { + /* Kill off one child */ + char char_of_death = '!'; + if (write_port(port_of_death, 99, &char_of_death, 1) != B_OK) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "write pipe_of_death"); + } + idle_spawn_rate = 1; + } + else if (idle_thread_count < min_spare_threads) { + /* 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, server_conf, + "server reached MaxClients setting, consider" + " raising the MaxClients setting"); + reported = 1; + } + idle_spawn_rate = 1; + } + else { + /* ZZZZ */ + + if (idle_spawn_rate >= 8) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "server seems busy, (you may need " + "to increase StartServers, ThreadsPerChild " + "or Min/MaxSparetThreads), " + "spawning %d children, there are around %d idle " + "threads, and %d total children", idle_spawn_rate, + idle_thread_count, total_non_dead); + } + for (i = 0; i < free_length; ++i) { + make_child(server_conf, free_slots[i], now); + } + /* 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 void server_main_loop(int remaining_children_to_start) +{ + int child_slot; + ap_wait_t status; + int pid; + int i; + + while (!restart_pending && !shutdown_pending) { + pid = wait_or_timeout(&status); + + if (pid >= 0) { + process_child_status(pid, status); + /* non-fatal death... note that it's gone in the scoreboard. */ + child_slot = find_child_by_pid(pid); + if (child_slot >= 0) { + for (i = 0; i < ap_threads_per_child; i++) + ap_update_child_status(child_slot, i, SERVER_DEAD, (request_rec *) NULL); + + if (remaining_children_to_start + && child_slot < ap_daemons_limit) { + /* we're still doing a 1-for-1 replacement of dead + * children with new children + */ + /* ZZZ abstract out for AP funcs. */ + make_child(server_conf, child_slot, time(NULL)); + --remaining_children_to_start; + } +#ifdef HAS_OTHER_CHILD + } + else if (reap_other_child(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, server_conf, + "long lost child came home! (pid %d)", 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_children_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_children_to_start); + remaining_children_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(); + } +} + +int ap_mpm_run(ap_context_t *_pconf, ap_context_t *plog, server_rec *s) +{ + int remaining_children_to_start; + + pconf = _pconf; + server_conf = s; + port_of_death = create_port(1, "httpd_port_of_death"); + + if ((num_listenfds = setup_listeners(pconf, server_conf)) < 1) { + /* XXX: hey, what's the right way for the mpm to indicate a fatal error? */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s, + "no listening sockets available, shutting down"); + return 1; + } + ap_log_pid(pconf, ap_pid_fname); + SAFE_ACCEPT(accept_mutex_init(pconf, 1)); + if (!is_graceful) { + reinit_scoreboard(pconf); + } + + set_signals(); + /* Don't thrash... */ + if (max_spare_threads < min_spare_threads + ap_threads_per_child) + max_spare_threads = min_spare_threads + ap_threads_per_child; + + /* 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_children_to_start = ap_daemons_to_start; + if (remaining_children_to_start > ap_daemons_limit) { + remaining_children_to_start = ap_daemons_limit; + } + if (!is_graceful) { + startup_children(remaining_children_to_start); + remaining_children_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, server_conf, + "%s configured -- resuming normal operations", + ap_get_server_version()); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "Server built: %s", ap_get_server_built()); + restart_pending = shutdown_pending = 0; + + server_main_loop(remaining_children_to_start); + + if (shutdown_pending) { + /* Time to gracefully shut down: + * Kill child processes, tell them to call child_exit, etc... + */ + if (ap_killpg(getpgrp(), SIGTERM) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM"); + } + reclaim_child_processes(1); /* Start with SIGTERM */ + + /* cleanup pid file on normal shutdown */ + { + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pconf, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "caught SIGTERM, shutting down"); + + return 1; + } + + /* we've been told to restart */ + 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) { + int i, j; + char char_of_death = '!'; + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "SIGWINCH received. Doing graceful restart"); + + /* give the children the signal to die */ + for (i = 0; i < ap_daemons_limit;) { + if (write_port(port_of_death, 99, &char_of_death, 1) != B_OK) { + if (errno == EINTR) continue; + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "write pipe_of_death"); + } + i++; + } + + /* This is mostly for debugging... so that we know what is still + * gracefully dealing with existing request. + */ + + for (i = 0; i < ap_daemons_limit; ++i) { + for (j = 0; j < ap_threads_per_child; j++) { + if (ap_scoreboard_image->servers[i][j].status != SERVER_DEAD) { + ap_scoreboard_image->servers[i][j].status = SERVER_GRACEFUL; + } + } + } + } + else { + /* Kill 'em all. Since the child acts the same on the parents SIGTERM + * and a SIGHUP, we may as well use the same signal, because some user + * pthreads are stealing signals from us left and right. + */ + if (ap_killpg(getpgrp(), SIGTERM) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM"); + } + reclaim_child_processes(1); /* Start with SIGTERM */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "SIGHUP received. Attempting to restart"); + } + if (!is_graceful) { + ap_restart_time = time(NULL); /* ZZZZZ */ + } + delete_port(port_of_death); + return 0; +} + +static void mpmt_beos_pre_config(ap_context_t *pconf, ap_context_t *plog, ap_context_t *ptemp) +{ + static int restart_num = 0; + + one_process = !!getenv("ONE_PROCESS"); + + /* sigh, want this only the second time around */ + if (restart_num++ == 1) { + is_graceful = 0; + + if (!one_process) { + beosd_detach(); + } + + my_pid = getpid(); + } + + beosd_pre_config(); + ap_listen_pre_config(); + ap_daemons_to_start = DEFAULT_START_DAEMON; + min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD; + max_spare_threads = DEFAULT_MAX_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD; + ap_daemons_limit = HARD_SERVER_LIMIT; + ap_threads_per_child = DEFAULT_THREADS_PER_CHILD; + ap_pid_fname = DEFAULT_PIDLOG; + ap_scoreboard_fname = DEFAULT_SCOREBOARD; + ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + ap_extended_status = 0; + + ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); +} + +static void mpmt_beos_hooks(void) +{ + ap_hook_pre_config(mpmt_beos_pre_config,NULL,NULL,HOOK_MIDDLE); + INIT_SIGLIST() + one_process = 0; +} + + +static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (cmd->server->is_virtual) { + return "PidFile directive not allowed in "; + } + ap_pid_fname = arg; + return NULL; +} + +static const char *set_scoreboard(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_scoreboard_fname = arg; + return NULL; +} + +static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_to_start = atoi(arg); + return NULL; +} + +static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + min_spare_threads = atoi(arg); + if (min_spare_threads <= 0) { + fprintf(stderr, "WARNING: detected MinSpareThreads set to non-positive.\n"); + fprintf(stderr, "Resetting to 1 to avoid almost certain Apache failure.\n"); + fprintf(stderr, "Please read the documentation.\n"); + min_spare_threads = 1; + } + + return NULL; +} + +static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + max_spare_threads = atoi(arg); + return NULL; +} + +static const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_limit = atoi(arg); + if (ap_daemons_limit > HARD_SERVER_LIMIT) { + fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit " + "of %d servers,\n", ap_daemons_limit, HARD_SERVER_LIMIT); + fprintf(stderr, " lowering MaxClients to %d. To increase, please " + "see the\n", HARD_SERVER_LIMIT); + fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n"); + ap_daemons_limit = HARD_SERVER_LIMIT; + } + else if (ap_daemons_limit < 1) { + fprintf(stderr, "WARNING: Require MaxClients > 0, setting to 1\n"); + ap_daemons_limit = 1; + } + return NULL; +} + +static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_threads_per_child = atoi(arg); + if (ap_threads_per_child > HARD_THREAD_LIMIT) { + fprintf(stderr, "WARNING: ThreadsPerChild of %d exceeds compile time" + "limit of %d threads,\n", ap_threads_per_child, + HARD_THREAD_LIMIT); + fprintf(stderr, " lowering ThreadsPerChild to %d. To increase, please" + "see the\n", HARD_THREAD_LIMIT); + fprintf(stderr, " HARD_THREAD_LIMIT define in src/include/httpd.h.\n"); + } + else if (ap_threads_per_child < 1) { + fprintf(stderr, "WARNING: Require ThreadsPerChild > 0, setting to 1\n"); + ap_threads_per_child = 1; + } + return NULL; +} + +static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_max_requests_per_child = atoi(arg); + + return NULL; +} + +static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg) +{ + struct stat finfo; + const char *fname; + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + fname = ap_server_root_relative(cmd->pool, arg); + /* ZZZ change this to the AP func FileInfo*/ + if ((stat(fname, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) { + return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, + " does not exist or is not a directory", NULL); + } + ap_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir)); + return NULL; +} + +struct ap_thread_mutex { + be_mutex_t mutex; +}; + +API_EXPORT(ap_thread_mutex *) ap_thread_mutex_new(void) +{ + ap_thread_mutex *mtx; + + mtx = malloc(sizeof(ap_thread_mutex)); + be_mutex_init(&(mtx->mutex), NULL); + return mtx; +} + +API_EXPORT(void) ap_thread_mutex_lock(ap_thread_mutex *mtx) +{ + /* Ignoring error conditions here. :( */ + be_mutex_lock(&(mtx->mutex)); +} + +API_EXPORT(void) ap_thread_mutex_unlock(ap_thread_mutex *mtx) +{ + /* Here too. */ + be_mutex_unlock(&(mtx->mutex)); +} + +API_EXPORT(void) ap_thread_mutex_destroy(ap_thread_mutex *mtx) +{ + /* Here too. */ + be_mutex_destroy(&(mtx->mutex)); + free(mtx); +} + + +static const command_rec mpmt_beos_cmds[] = { +UNIX_DAEMON_COMMANDS +LISTEN_COMMANDS +{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1, + "A file for logging the server process ID"}, +{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1, + "A file for Apache to maintain runtime process management information"}, +{ "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1, + "Number of child processes launched at server startup" }, +{ "MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF, TAKE1, + "Minimum number of idle children, to handle request spikes" }, +{ "MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF, TAKE1, + "Maximum number of idle children" }, +{ "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1, + "Maximum number of children alive at the same time" }, +{ "ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF, TAKE1, + "Number of threads each child creates" }, +{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, + "Maximum number of requests a particular child serves before dying." }, +{ "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1, + "The location of the directory Apache changes to before dumping core" }, +{ NULL } +}; + +module MODULE_VAR_EXPORT mpm_mpmt_beos_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + mpmt_beos_cmds, /* command ap_table_t */ + NULL, /* handlers */ + mpmt_beos_hooks /* register_hooks */ +}; + diff --git a/server/mpm/mpmt_beos/mpmt_beos.h b/server/mpm/mpmt_beos/mpmt_beos.h new file mode 100644 index 0000000000..0c6891958c --- /dev/null +++ b/server/mpm/mpmt_beos/mpmt_beos.h @@ -0,0 +1,68 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. 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. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" 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 names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``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 GROUP 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 Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_MPM_MPMT_PTHREAD_H +#define APACHE_MPM_MPMT_PTHREAD_H + +extern int ap_threads_per_child; +extern int ap_max_requests_per_child; +extern int ap_pipe_of_death[2]; +extern void clean_child_exit(int); +extern int ap_extended_status; +extern void clean_child_exit(int); + +#endif /* APACHE_MPM_MPMT_PTHREAD_H */ diff --git a/server/mpm/mpmt_beos/poll.c b/server/mpm/mpmt_beos/poll.c new file mode 100644 index 0000000000..a8c471e2db --- /dev/null +++ b/server/mpm/mpmt_beos/poll.c @@ -0,0 +1,139 @@ +/* ==================================================================== + * Copyright (c) 1999 Ralf S. Engelschall. 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. + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``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 RALF S. ENGELSCHALL 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. + * ==================================================================== + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "poll.h" + +/* + * Emulate SysV poll(2) with BSD select(2) + * Written in June 1999 by Ralf S. Engelschall + */ + +int poll(struct pollfd *pfd, unsigned int nfd, int timeout) +{ + fd_set rfds, wfds, efds; + struct timeval tv, *ptv; + int maxfd, rc, i, ok; + char data[64]; + + /* poll(2) semantics */ + if (pfd == NULL) { + errno = EFAULT; + return -1; + } + + /* convert timeout number into a timeval structure */ + ptv = &tv; + if (timeout == 0) { + /* return immediately */ + ptv->tv_sec = 0; + ptv->tv_usec = 0; + } + else if (timeout == INFTIM) { + /* wait forever */ + ptv = NULL; + } + else { + /* return after timeout */ + ptv->tv_sec = timeout / 1000; + ptv->tv_usec = (timeout % 1000) * 1000; + } + + /* clean illegal fd set and (re)enter the repeat loop */ + + /* create fd sets and determine max fd */ + maxfd = 0; + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + for(i = 0; i < nfd; i++) { + if (pfd[i].fd < 0) { + continue; + } + if (pfd[i].events & POLLIN) + FD_SET(pfd[i].fd, &rfds); + if (pfd[i].events & POLLOUT) + FD_SET(pfd[i].fd, &wfds); + if (pfd[i].events & POLLPRI) + FD_SET(pfd[i].fd, &efds); + if (pfd[i].fd >= maxfd && (pfd[i].events & (POLLIN|POLLOUT|POLLPRI))) + maxfd = pfd[i].fd; + } + + /* examine fd sets */ + rc = select(maxfd+1, &rfds, &wfds, &efds, ptv); + + /* establish results */ + if (rc > 0) { + rc = 0; + for (i = 0; i < nfd; i++) { + ok = 0; + pfd[i].revents = 0; + if (pfd[i].fd < 0) { + /* support for POLLNVAL */ + pfd[i].revents |= POLLNVAL; + continue; + } + if (FD_ISSET(pfd[i].fd, &rfds)) { + pfd[i].revents |= POLLIN; + ok++; + /* support for POLLHUP */ + if (recv(pfd[i].fd, data, 0, 0) == -1) { + if ( errno == ESHUTDOWN || errno == ECONNRESET + || errno == ECONNABORTED || errno == ENETRESET) { + pfd[i].revents &= ~(POLLIN); + pfd[i].revents |= POLLHUP; + ok--; + } + } + } + if (FD_ISSET(pfd[i].fd, &wfds)) { + pfd[i].revents |= POLLOUT; + ok++; + } + if (FD_ISSET(pfd[i].fd, &efds)) { + pfd[i].revents |= POLLPRI; + ok++; + } + if (ok) + rc++; + } + } + return rc; +} + diff --git a/server/mpm/mpmt_beos/poll.h b/server/mpm/mpmt_beos/poll.h new file mode 100644 index 0000000000..07c882e5e2 --- /dev/null +++ b/server/mpm/mpmt_beos/poll.h @@ -0,0 +1,89 @@ +/* ==================================================================== + * Copyright (c) 1999 Ralf S. Engelschall. 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. + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``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 RALF S. ENGELSCHALL 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. + * ==================================================================== + */ + +#ifndef _POLL_H_ +#define _POLL_H_ + +#define LIBPOLL_VERSION 19990812 + +#ifndef POLLIN +#define POLLIN 0x0001 /* any readable data available */ +#endif +#ifndef POLLPRI +#define POLLPRI 0x0002 /* OOB/Urgent readable data */ +#endif +#ifndef POLLOUT +#define POLLOUT 0x0004 /* file descriptor is writeable */ +#endif + +#ifndef POLLERR +#define POLLERR 0x0008 /* some poll error occurred */ +#endif +#ifndef POLLHUP +#define POLLHUP 0x0010 /* file descriptor was "hung up" */ +#endif +#ifndef POLLNVAL +#define POLLNVAL 0x0020 /* requested events "invalid" */ +#endif + +#ifndef POLLRDNORM +#define POLLRDNORM POLLIN +#endif +#ifndef POLLRDBAND +#define POLLRDBAND POLLIN +#endif +#ifndef POLLWRNORM +#define POLLWRNORM POLLOUT +#endif +#ifndef POLLWRBAND +#define POLLWRBAND POLLOUT +#endif + +#ifndef INFTIM +#define INFTIM (-1) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct pollfd { + int fd; /* which file descriptor to poll */ + short events; /* events we are interested in */ + short revents; /* events found on return */ +}; + +int poll(struct pollfd *, unsigned int, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _POLL_H_ */ diff --git a/server/mpm/mpmt_beos/scoreboard.c b/server/mpm/mpmt_beos/scoreboard.c new file mode 100644 index 0000000000..74dafa1573 --- /dev/null +++ b/server/mpm/mpmt_beos/scoreboard.c @@ -0,0 +1,182 @@ +#include "httpd.h" +#include "http_log.h" +#include "http_main.h" +#include "http_core.h" +#include "http_config.h" +#include "beosd.h" +#include "http_conf_globals.h" +#include "mpmt_beos.h" +#include "scoreboard.h" + +scoreboard *ap_scoreboard_image = NULL; +//static char *ap_server_argv0=NULL; +extern ap_context_t * pconf; + +/***************************************************************** + * + * Dealing with the scoreboard... a lot of these variables are global + * only to avoid getting clobbered by the longjmp() that happens when + * a hard timeout expires... + * + * We begin with routines which deal with the file itself... + */ + +void reinit_scoreboard(ap_context_t *p) +{ + ap_assert(!ap_scoreboard_image); + ap_scoreboard_image = (scoreboard *) malloc(SCOREBOARD_SIZE); + if (ap_scoreboard_image == NULL) { + fprintf(stderr, "Ouch! Out of memory reiniting scoreboard!\n"); + } + memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); +} + +void cleanup_scoreboard(void) +{ + ap_assert(ap_scoreboard_image); + free(ap_scoreboard_image); + ap_scoreboard_image = NULL; +} + +API_EXPORT(void) reopen_scoreboard(ap_context_t *p) +{ +} + +API_EXPORT(void) ap_sync_scoreboard_image(void) +{ +} + +API_EXPORT(int) ap_exists_scoreboard_image(void) +{ + return (ap_scoreboard_image ? 1 : 0); +} + +static ap_inline void put_scoreboard_info(int child_num, int thread_num, + thread_score *new_score_rec) +{ + /* XXX - needs to be fixed to account for threads */ +} + +void update_scoreboard_global(void) +{ +} + +void increment_counts(int child_num, int thread_num, request_rec *r) +{ + long int bs = 0; + thread_score *ss; + + ss = &ap_scoreboard_image->servers[child_num][thread_num]; + + if (r->sent_bodyct) + ap_bgetopt(r->connection->client, BO_BYTECT, &bs); + +#ifndef NO_TIMES + times(&ss->times); +#endif + ss->access_count++; + ss->my_access_count++; + ss->conn_count++; + ss->bytes_served += (unsigned long) bs; + ss->my_bytes_served += (unsigned long) bs; + ss->conn_bytes += (unsigned long) bs; + + put_scoreboard_info(child_num, thread_num, ss); + +} + +API_EXPORT(int) find_child_by_pid(int pid) +{ + int i; + int max_daemons_limit = ap_get_max_daemons(); + + for (i = 0; i < max_daemons_limit; ++i) + if (ap_scoreboard_image->parent[i].pid == pid) + return i; + + return -1; +} + +int ap_update_child_status(int child_num, int thread_num, int status, request_rec *r) +{ + int old_status; + thread_score *ss; + parent_score *ps; + + if (child_num < 0) + return -1; + + ss = &ap_scoreboard_image->servers[child_num][thread_num]; + old_status = ss->status; + ss->status = status; + + ps = &ap_scoreboard_image->parent[child_num]; + + if ((status == SERVER_READY || status == SERVER_ACCEPTING) + && old_status == SERVER_STARTING) { + ss->tid = find_thread(NULL); + ps->worker_threads = ap_threads_per_child; + } + + if (ap_extended_status) { + if (status == SERVER_READY || status == SERVER_DEAD) { + /* + * Reset individual counters + */ + if (status == SERVER_DEAD) { + ss->my_access_count = 0L; + ss->my_bytes_served = 0L; + } + ss->conn_count = (unsigned short) 0; + ss->conn_bytes = (unsigned long) 0; + } + if (r) { + conn_rec *c = r->connection; + ap_cpystrn(ss->client, ap_get_remote_host(c, r->per_dir_config, + REMOTE_NOLOOKUP), sizeof(ss->client)); + if (r->the_request == NULL) { + ap_cpystrn(ss->request, "NULL", sizeof(ss->request)); + } else if (r->parsed_uri.password == NULL) { + ap_cpystrn(ss->request, r->the_request, sizeof(ss->request)); + } else { + /* Don't reveal the password in the server-status view */ + ap_cpystrn(ss->request, ap_pstrcat(r->pool, r->method, " ", + ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD), + r->assbackwards ? NULL : " ", r->protocol, NULL), + sizeof(ss->request)); + } + ss->vhostrec = r->server; + } + } + + put_scoreboard_info(child_num, thread_num, ss); + return old_status; +} + +void ap_time_process_request(int child_num, int thread_num, int status) +{ + thread_score *ss; + + if (child_num < 0) + return; + + ss = &ap_scoreboard_image->servers[child_num][thread_num]; + + if (status == START_PREQUEST) { + /*ss->start_time = GetCurrentTime(); ZZZ return time in uS since the + epoch. Some platforms do not support gettimeofday. Create a routine + to get the current time is some useful units. */ + if (gettimeofday(&ss->start_time, (struct timezone *) 0) < 0) { + ss->start_time.tv_sec = ss->start_time.tv_usec = 0L; + } + } + else if (status == STOP_PREQUEST) { + /*ss->stop_time = GetCurrentTime(); + ZZZ return time in uS since the epoch */ + + if (gettimeofday(&ss->stop_time, (struct timezone *) 0) < 0) { + ss->start_time.tv_sec = ss->start_time.tv_usec = 0L; + } + } + put_scoreboard_info(child_num, thread_num, ss); +} diff --git a/server/mpm/mpmt_beos/scoreboard.h b/server/mpm/mpmt_beos/scoreboard.h new file mode 100644 index 0000000000..aac12747f3 --- /dev/null +++ b/server/mpm/mpmt_beos/scoreboard.h @@ -0,0 +1,233 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. 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. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" 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 names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``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 GROUP 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 Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_SCOREBOARD_H +#define APACHE_SCOREBOARD_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WIN32 +#ifdef TPF +#include +#else +#include +#endif /* TPF */ +#endif + +#include "mpm_default.h" /* For HARD_.*_LIMIT */ + +/* Scoreboard info on a process is, for now, kept very brief --- + * just status value and pid (the latter so that the caretaker process + * can properly update the scoreboard when a process dies). We may want + * to eventually add a separate set of long_score structures which would + * give, for each process, the number of requests serviced, and info on + * the current, or most recent, request. + * + * Status values: + */ + +#define SERVER_DEAD 0 +#define SERVER_STARTING 1 /* Server Starting up */ +#define SERVER_READY 2 /* Waiting for connection (or accept() lock) */ +#define SERVER_BUSY_READ 3 /* Reading a client request */ +#define SERVER_BUSY_WRITE 4 /* Processing a client request */ +#define SERVER_BUSY_KEEPALIVE 5 /* Waiting for more requests via keepalive */ +#define SERVER_BUSY_LOG 6 /* Logging the request */ +#define SERVER_BUSY_DNS 7 /* Looking up a hostname */ +#define SERVER_GRACEFUL 8 /* server is gracefully finishing request */ +#define SERVER_ACCEPTING 9 /* thread is accepting connections */ +#define SERVER_QUEUEING 10 /* thread is putting connection on the queue */ +#define SERVER_NUM_STATUS 11 /* number of status settings */ + +/* A "virtual time" is simply a counter that indicates that a child is + * making progress. The parent checks up on each child, and when they have + * made progress it resets the last_rtime element. But when the child hasn't + * made progress in a time that's roughly timeout_len seconds long, it is + * sent a SIGALRM. + * + * vtime is an optimization that is used only when the scoreboard is in + * shared memory (it's not easy/feasible to do it in a scoreboard file). + * The essential observation is that timeouts rarely occur, the vast majority + * of hits finish before any timeout happens. So it really sucks to have to + * ask the operating system to set up and destroy alarms many times during + * a request. + */ +typedef unsigned vtime_t; + +/* Type used for generation indicies. Startup and every restart cause a + * new generation of children to be spawned. Children within the same + * generation share the same configuration information -- pointers to stuff + * created at config time in the parent are valid across children. For + * example, the vhostrec pointer in the scoreboard below is valid in all + * children of the same generation. + * + * The safe way to access the vhost pointer is like this: + * + * short_score *ss = pointer to whichver slot is interesting; + * parent_score *ps = pointer to whichver slot is interesting; + * server_rec *vh = ss->vhostrec; + * + * if (ps->generation != ap_my_generation) { + * vh = NULL; + * } + * + * then if vh is not NULL it's valid in this child. + * + * This avoids various race conditions around restarts. + */ +typedef int ap_generation_t; + +/* stuff which is thread specific */ +typedef struct { +#ifdef OPTIMIZE_TIMEOUTS + vtime_t cur_vtime; /* the child's current vtime */ + unsigned short timeout_len; /* length of the timeout */ +#endif + thread_id tid; + unsigned char status; + unsigned long access_count; + unsigned long bytes_served; + unsigned long my_access_count; + unsigned long my_bytes_served; + unsigned long conn_bytes; + unsigned short conn_count; +#if defined(NO_GETTIMEOFDAY) + clock_t start_time; + clock_t stop_time; +#else + struct timeval start_time; + struct timeval stop_time; +#endif +#ifndef NO_TIMES + struct tms times; +#endif +#ifndef OPTIMIZE_TIMEOUTS + time_t last_used; +#endif + char client[32]; /* Keep 'em small... */ + char request[64]; /* We just want an idea... */ + server_rec *vhostrec; /* What virtual host is being accessed? */ + /* SEE ABOVE FOR SAFE USAGE! */ +} thread_score; + +typedef struct { + ap_generation_t running_generation; /* the generation of children which + * should still be serving requests. */ +} global_score; + +/* stuff which the parent generally writes and the children rarely read */ +typedef struct { + pid_t pid; + ap_generation_t generation; /* generation of this child */ + int worker_threads; +#ifdef OPTIMIZE_TIMEOUTS + time_t last_rtime; /* time(0) of the last change */ + vtime_t last_vtime; /* the last vtime the parent has seen */ +#endif +} parent_score; + +typedef struct { + thread_score servers[HARD_SERVER_LIMIT][HARD_THREAD_LIMIT]; + parent_score parent[HARD_SERVER_LIMIT]; + global_score global; +} scoreboard; + +#define SCOREBOARD_SIZE sizeof(scoreboard) +#ifdef TPF +#define SCOREBOARD_NAME "SCOREBRD" +#define SCOREBOARD_FRAMES SCOREBOARD_SIZE/4095 + 1 +#endif + +API_EXPORT(int) ap_exists_scoreboard_image(void); +void reinit_scoareboard(ap_context_t *p); +void cleanup_scoreboard(void); +API_EXPORT(void) ap_sync_scoreboard_image(void); + +#if defined(USE_OS2_SCOREBOARD) +caddr_t create_shared_heap(const char *name, size_t size); +caddr_t get_shared_heap(const char *Name); +#elif defined(USE_POSIX_SCOREBOARD) +static void cleanup_shared_mem(void *d); +#else +void reinit_scoreboard(ap_context_t *p); +#endif + +API_EXPORT(void) reopen_scoreboard(ap_context_t *p); + +ap_inline void ap_sync_scoreboard_image(void); +void increment_counts(int child_num, int thread_num, request_rec *r); +void update_scoreboard_global(void); +API_EXPORT(int) find_child_by_pid(int pid); +int ap_update_child_status(int child_num, int thread_num, int status, request_rec *r); +void ap_time_process_request(int child_num, int thread_num, int status); + + + +API_VAR_EXPORT extern scoreboard *ap_scoreboard_image; + +API_VAR_EXPORT extern ap_generation_t volatile ap_my_generation; + +/* for time_process_request() in http_main.c */ +#define START_PREQUEST 1 +#define STOP_PREQUEST 2 + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_SCOREBOARD_H */ -- 2.40.0