summaryrefslogtreecommitdiff
path: root/APACHE_1_3_42/src/main/http_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'APACHE_1_3_42/src/main/http_main.c')
-rw-r--r--APACHE_1_3_42/src/main/http_main.c8224
1 files changed, 8224 insertions, 0 deletions
diff --git a/APACHE_1_3_42/src/main/http_main.c b/APACHE_1_3_42/src/main/http_main.c
new file mode 100644
index 0000000000..f68f3e2735
--- /dev/null
+++ b/APACHE_1_3_42/src/main/http_main.c
@@ -0,0 +1,8224 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef SHARED_CORE_BOOTSTRAP
+#ifndef SHARED_CORE_TIESTATIC
+
+#ifdef SHARED_CORE
+#define REALMAIN ap_main
+int ap_main(int argc, char *argv[]);
+#else
+#define REALMAIN main
+#endif
+
+#define CORE_PRIVATE
+
+#include "httpd.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_config.h" /* for read_config */
+#include "http_protocol.h" /* for read_request */
+#include "http_request.h" /* for process_request */
+#include "http_conf_globals.h"
+#include "http_core.h" /* for get_remote_host */
+#include "http_vhost.h"
+#include "util_script.h" /* to force util_script.c linking */
+#include "util_uri.h"
+#include "scoreboard.h"
+#include "multithread.h"
+#include <sys/stat.h>
+#ifdef USE_SHMGET_SCOREBOARD
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+#ifdef SecureWare
+#include <sys/security.h>
+#include <sys/audit.h>
+#include <prot.h>
+#endif
+#ifdef WIN32
+#include "../os/win32/getopt.h"
+#elif !defined(BEOS) && !defined(TPF) && !defined(NETWARE) && !defined(OS390) && !defined(CYGWIN)
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_BSTRING_H
+#include <bstring.h> /* for IRIX, FD_SET calls bzero() */
+#endif
+#ifdef HAVE_SET_DUMPABLE /* certain levels of Linux */
+#include <sys/prctl.h>
+#endif
+
+#ifdef MULTITHREAD
+/* special debug stuff -- PCS */
+
+/* Set this non-zero if you are prepared to put up with more than one log entry per second */
+#define SEVERELY_VERBOSE 0
+
+ /* APD1() to APD5() are macros to help us debug. They can either
+ * log to the screen or the error_log file. In release builds, these
+ * macros do nothing. In debug builds, they send messages at priority
+ * "debug" to the error log file, or if DEBUG_TO_CONSOLE is defined,
+ * to the console.
+ */
+
+# ifdef _DEBUG
+# ifndef DEBUG_TO_CONSOLE
+# define APD1(a) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a)
+# define APD2(a,b) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b)
+# define APD3(a,b,c) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c)
+# define APD4(a,b,c,d) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d)
+# define APD5(a,b,c,d,e) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d,e)
+# else
+# define APD1(a) printf("%s\n",a)
+# define APD2(a,b) do { printf(a,b);putchar('\n'); } while(0);
+# define APD3(a,b,c) do { printf(a,b,c);putchar('\n'); } while(0);
+# define APD4(a,b,c,d) do { printf(a,b,c,d);putchar('\n'); } while(0);
+# define APD5(a,b,c,d,e) do { printf(a,b,c,d,e);putchar('\n'); } while(0);
+# endif
+# else /* !_DEBUG */
+# define APD1(a)
+# define APD2(a,b)
+# define APD3(a,b,c)
+# define APD4(a,b,c,d)
+# define APD5(a,b,c,d,e)
+# endif /* _DEBUG */
+#endif /* MULTITHREAD */
+
+/* This next function is never used. It is here to ensure that if we
+ * make all the modules into shared libraries that core httpd still
+ * includes the full Apache API. Without this function the objects in
+ * main/util_script.c would not be linked into a minimal httpd.
+ * And the extra prototype is to make gcc -Wmissing-prototypes quiet.
+ */
+API_EXPORT(void) ap_force_library_loading(void);
+API_EXPORT(void) ap_force_library_loading(void) {
+ ap_add_cgi_vars(NULL);
+}
+
+#include "explain.h"
+
+#if !defined(max)
+#define max(a,b) (a > b ? a : b)
+#endif
+
+#ifdef WIN32
+#include "../os/win32/service.h"
+#include "../os/win32/registry.h"
+#define DEFAULTSERVICENAME "Apache"
+#define PATHSEPARATOR '\\'
+#else
+#define PATHSEPARATOR '/'
+#endif
+
+
+#ifdef MINT
+long _stksize = 32768;
+#endif
+
+#ifdef USE_OS2_SCOREBOARD
+ /* Add MMAP style functionality to OS/2 */
+#define INCL_DOSMEMMGR
+#define INCL_DOSEXCEPTIONS
+#define INCL_DOSSEMAPHORES
+#include <os2.h>
+#include <umalloc.h>
+#include <stdio.h>
+caddr_t create_shared_heap(const char *, size_t);
+caddr_t get_shared_heap(const char *);
+#endif
+
+DEF_Explain
+
+/* Defining GPROF when compiling uses the moncontrol() function to
+ * disable gprof profiling in the parent, and enable it only for
+ * request processing in children (or in one_process mode). It's
+ * absolutely required to get useful gprof results under linux
+ * because the profile itimers and such are disabled across a
+ * fork(). It's probably useful elsewhere as well.
+ */
+#ifdef GPROF
+extern void moncontrol(int);
+#define MONCONTROL(x) moncontrol(x)
+#else
+#define MONCONTROL(x)
+#endif
+
+#ifndef MULTITHREAD
+/* this just need to be anything non-NULL */
+void *ap_dummy_mutex = &ap_dummy_mutex;
+#endif
+
+/*
+ * Actual definitions of config globals... here because this is
+ * for the most part the only code that acts on 'em. (Hmmm... mod_main.c?)
+ */
+#ifdef NETWARE
+BOOL ap_main_finished = FALSE;
+unsigned int ap_thread_stack_size = 65536;
+#endif
+int ap_thread_count = 0;
+API_VAR_EXPORT int ap_standalone=0;
+API_VAR_EXPORT int ap_configtestonly=0;
+int ap_docrootcheck=1;
+API_VAR_EXPORT uid_t ap_user_id=0;
+API_VAR_EXPORT char *ap_user_name=NULL;
+API_VAR_EXPORT gid_t ap_group_id=0;
+#ifdef MULTIPLE_GROUPS
+gid_t group_id_list[NGROUPS_MAX];
+#endif
+API_VAR_EXPORT int ap_max_requests_per_child=0;
+API_VAR_EXPORT int ap_threads_per_child=0;
+API_VAR_EXPORT int ap_excess_requests_per_child=0;
+API_VAR_EXPORT char *ap_pid_fname=NULL;
+API_VAR_EXPORT char *ap_scoreboard_fname=NULL;
+API_VAR_EXPORT char *ap_lock_fname=NULL;
+API_VAR_EXPORT char *ap_server_argv0=NULL;
+API_VAR_EXPORT struct in_addr ap_bind_address={0};
+API_VAR_EXPORT int ap_daemons_to_start=0;
+API_VAR_EXPORT int ap_daemons_min_free=0;
+API_VAR_EXPORT int ap_daemons_max_free=0;
+API_VAR_EXPORT int ap_daemons_limit=0;
+API_VAR_EXPORT time_t ap_restart_time=0;
+API_VAR_EXPORT int ap_suexec_enabled = 0;
+API_VAR_EXPORT int ap_listenbacklog=0;
+#ifdef AP_ENABLE_EXCEPTION_HOOK
+int ap_exception_hook_enabled=0;
+#endif
+
+struct accept_mutex_methods_s {
+ void (*child_init)(pool *p);
+ void (*init)(pool *p);
+ void (*on)(void);
+ void (*off)(void);
+ char *name;
+};
+typedef struct accept_mutex_methods_s accept_mutex_methods_s;
+accept_mutex_methods_s *amutex;
+
+#ifdef SO_ACCEPTFILTER
+int ap_acceptfilter =
+#ifdef AP_ACCEPTFILTER_OFF
+ 0;
+#else
+ 1;
+#endif
+#endif
+
+int ap_dump_settings = 0;
+API_VAR_EXPORT int ap_extended_status = 0;
+
+/*
+ * The max child slot ever assigned, preserved across restarts. Necessary
+ * to deal with MaxClients changes across SIGUSR1 restarts. We use this
+ * value to optimize routines that have to scan the entire scoreboard.
+ */
+static int max_daemons_limit = -1;
+
+/*
+ * During config time, listeners is treated as a NULL-terminated list.
+ * child_main previously would start at the beginning of the list each time
+ * through the loop, so a socket early on in the list could easily starve out
+ * sockets later on in the list. The solution is to start at the listener
+ * after the last one processed. But to do that fast/easily in child_main it's
+ * way more convenient for listeners to be a ring that loops back on itself.
+ * The routine setup_listeners() is called after config time to both open up
+ * the sockets and to turn the NULL-terminated list into a ring that loops back
+ * on itself.
+ *
+ * head_listener is used by each child to keep track of what they consider
+ * to be the "start" of the ring. It is also set by make_child to ensure
+ * that new children also don't starve any sockets.
+ *
+ * Note that listeners != NULL is ensured by read_config().
+ */
+listen_rec *ap_listeners=NULL;
+static listen_rec *head_listener;
+
+API_VAR_EXPORT char ap_server_root[MAX_STRING_LEN]="";
+API_VAR_EXPORT char ap_server_confname[MAX_STRING_LEN]="";
+API_VAR_EXPORT char ap_coredump_dir[MAX_STRING_LEN]="";
+int ap_coredump_dir_configured=0;
+
+API_VAR_EXPORT array_header *ap_server_pre_read_config=NULL;
+API_VAR_EXPORT array_header *ap_server_post_read_config=NULL;
+API_VAR_EXPORT array_header *ap_server_config_defines=NULL;
+
+/* *Non*-shared http_main globals... */
+
+static server_rec *server_conf;
+#ifndef NETWARE
+static JMP_BUF APACHE_TLS jmpbuffer;
+#endif
+static int sd;
+static fd_set listenfds;
+static int listenmaxfd;
+#ifndef NETWARE
+static pid_t pgrp;
+#endif
+
+/* 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 int do_detach = 1;
+
+/* set if timeouts are to be handled by the children and not by the parent.
+ * i.e. child_timeouts = !standalone || one_process.
+ */
+#ifndef NETWARE
+static int child_timeouts;
+#endif
+
+#ifdef DEBUG_SIGSTOP
+int raise_sigstop_flags;
+#endif
+
+#ifndef NO_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 pool *pglobal; /* Global pool */
+static pool *pconf; /* Pool for config stuff */
+static pool *plog; /* Pool for error-logging files */
+static pool *ptrans; /* Pool for per-transaction stuff */
+static pool *pchild; /* Pool for httpd child stuff */
+static pool *pmutex; /* Pool for accept mutex in child */
+static pool *pcommands; /* Pool for -C and -c switches */
+
+#ifndef NETWARE
+static int APACHE_TLS my_pid; /* it seems silly to call getpid all the time */
+#endif
+#ifndef MULTITHREAD
+static int my_child_num;
+#endif
+
+#ifdef TPF
+pid_t tpf_parent_pid;
+int tpf_child = 0;
+char tpf_server_name[INETD_SERVNAME_LENGTH+1];
+char tpf_mutex_key[TPF_MUTEX_KEY_SIZE];
+#endif /* TPF */
+
+/*
+ * Shared memory scoreboard
+ */
+scoreboard *ap_scoreboard_image = NULL;
+
+/*
+ * Parent process local storage of child pids
+ */
+static int pid_table[HARD_SERVER_LIMIT];
+
+/*
+ * Pieces for managing the contents of the Server response header
+ * field.
+ */
+static char *server_version = NULL;
+static int version_locked = 0;
+
+/* Global, alas, so http_core can talk to us */
+enum server_token_type ap_server_tokens = SrvTk_FULL;
+
+/* Also global, for http_core and http_protocol */
+API_VAR_EXPORT int ap_protocol_req_check = 1;
+
+API_VAR_EXPORT int ap_change_shmem_uid = 0;
+
+/*
+ * Check the pid table to see if the actual pid exists
+ */
+
+static int in_pid_table(int pid) {
+ int i;
+ for (i = 0; i < HARD_SERVER_LIMIT; i++) {
+ if (pid_table[i] == pid) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void set_pid_table(int pid) {
+ int i;
+ for (i = 0; i < HARD_SERVER_LIMIT; i++) {
+ if (pid_table[i] == 0) {
+ pid_table[i] = pid;
+ break;
+ }
+ }
+ /* NOTE: Error detection?? */
+}
+
+static void unset_pid_table(int pid) {
+ int i;
+ for (i = 0; i < HARD_SERVER_LIMIT; i++) {
+ if (pid_table[i] == pid) {
+ pid_table[i] = 0;
+ break;
+ }
+ }
+}
+
+/*
+ * This routine is called when the pconf pool is vacuumed. It resets the
+ * server version string to a known value and [re]enables modifications
+ * (which are disabled by configuration completion).
+ */
+static void reset_version(void *dummy)
+{
+ version_locked = 0;
+ ap_server_tokens = SrvTk_FULL;
+ server_version = NULL;
+}
+
+API_EXPORT(const char *) ap_get_server_version(void)
+{
+ return (server_version ? server_version : SERVER_BASEVERSION);
+}
+
+API_EXPORT(void) ap_add_version_component(const char *component)
+{
+ if (! version_locked) {
+ /*
+ * If the version string is null, register our cleanup to reset the
+ * pointer on pool destruction. We also know that, if NULL,
+ * we are adding the original SERVER_BASEVERSION string.
+ */
+ if (server_version == NULL) {
+ ap_register_cleanup(pconf, NULL, (void (*)(void *))reset_version,
+ ap_null_cleanup);
+ server_version = ap_pstrdup(pconf, component);
+ }
+ else {
+ /*
+ * Tack the given component identifier to the end of
+ * the existing string.
+ */
+ server_version = ap_pstrcat(pconf, server_version, " ",
+ component, NULL);
+ }
+ }
+}
+
+/*
+ * This routine adds the real server base identity to the version string,
+ * and then locks out changes until the next reconfig.
+ */
+static void ap_set_version(void)
+{
+ if (ap_server_tokens == SrvTk_PRODUCT_ONLY) {
+ ap_add_version_component(SERVER_PRODUCT);
+ }
+ else if (ap_server_tokens == SrvTk_MIN) {
+ ap_add_version_component(SERVER_BASEVERSION);
+ }
+ else {
+ ap_add_version_component(SERVER_BASEVERSION " (" PLATFORM ")");
+ }
+ /*
+ * Lock the server_version string if we're not displaying
+ * the full set of tokens
+ */
+ if (ap_server_tokens != SrvTk_FULL) {
+ version_locked++;
+ }
+}
+
+#ifndef NETWARE
+static APACHE_TLS int volatile exit_after_unblock = 0;
+#endif
+
+#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(server_conf->module_config, &core_module);
+ char *dir = sconf->gprof_dir;
+
+ if(dir) {
+ char buf[512];
+ int len = strlen(sconf->gprof_dir) - 1;
+ if(*(dir + len) == '%') {
+ dir[len] = '\0';
+ ap_snprintf(buf, sizeof(buf), "%sgprof.%d", dir, (int)getpid());
+ }
+ dir = ap_server_root_relative(pconf, buf[0] ? buf : dir);
+ if(mkdir(dir, 0755) < 0 && errno != EEXIST) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "gprof: error creating directory %s", dir);
+ }
+ }
+ else {
+ dir = ap_server_root_relative(pconf, "logs");
+ }
+
+ chdir(dir);
+}
+#else
+#define chdir_for_gprof()
+#endif
+
+/* 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)
+{
+#ifdef TPF
+ /* run ptrans cleanups since TPF's sockets don't close upon exit */
+ if (ptrans) {
+ ap_clear_pool(ptrans);
+ }
+#endif /* TPF */
+
+ if (pchild) {
+ /* make sure the accept mutex is released before calling child
+ * exit hooks and cleanups... otherwise, modules can segfault
+ * in such code and, depending on the mutex mechanism, leave
+ * the server deadlocked... even if the module doesn't segfault,
+ * if it performs extensive processing it can temporarily prevent
+ * the server from accepting new connections
+ */
+ ap_clear_pool(pmutex);
+ ap_child_exit_modules(pchild, server_conf);
+ ap_destroy_pool(pchild);
+ }
+ chdir_for_gprof();
+ exit(code);
+}
+
+/*
+ * Start of accept() mutex fluff:
+ * Concept: Each method has it's own distinct set of mutex functions,
+ * which it shoves in a nice struct for us. We then pick
+ * which struct to use. We tell Apache which methods we
+ * support via HAVE_FOO_SERIALIZED_ACCEPT. We can
+ * specify the default via USE_FOO_SERIALIZED_ACCEPT
+ * (this pre-1.3.21 builds which use that at the command-
+ * line during builds work as expected). Without a set
+ * method, we pick the 1st from the following order:
+ * uslock, pthread, sysvsem, fcntl, flock, os2sem, tpfcore and none.
+ */
+
+#if defined(HAVE_FCNTL_SERIALIZED_ACCEPT) || defined(HAVE_FLOCK_SERIALIZED_ACCEPT)
+static void expand_lock_fname(pool *p)
+{
+ /* XXXX possibly bogus cast */
+ ap_lock_fname = ap_psprintf(p, "%s.%lu",
+ ap_server_root_relative(p, ap_lock_fname), (unsigned long)getpid());
+}
+#endif
+
+#if defined (HAVE_USLOCK_SERIALIZED_ACCEPT)
+#include <ulocks.h>
+static ulock_t uslock = NULL;
+
+#define accept_mutex_child_init_uslock(x)
+
+static void accept_mutex_init_uslock(pool *p)
+{
+ ptrdiff_t old;
+ usptr_t *us;
+
+
+ /* default is 8, allocate enough for all the children plus the parent */
+ if ((old = usconfig(CONF_INITUSERS, HARD_SERVER_LIMIT + 1)) == -1) {
+ perror("usconfig(CONF_INITUSERS)");
+ exit(-1);
+ }
+
+ if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) {
+ perror("usconfig(CONF_LOCKTYPE)");
+ exit(-1);
+ }
+ if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) {
+ perror("usconfig(CONF_ARENATYPE)");
+ exit(-1);
+ }
+ if ((us = usinit("/dev/zero")) == NULL) {
+ perror("usinit");
+ exit(-1);
+ }
+
+ if ((uslock = usnewlock(us)) == NULL) {
+ perror("usnewlock");
+ exit(-1);
+ }
+}
+
+static void accept_mutex_on_uslock(void)
+{
+ switch (ussetlock(uslock)) {
+ case 1:
+ /* got lock */
+ break;
+ case 0:
+ fprintf(stderr, "didn't get lock\n");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ case -1:
+ perror("ussetlock");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+static void accept_mutex_off_uslock(void)
+{
+ if (usunsetlock(uslock) == -1) {
+ perror("usunsetlock");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+accept_mutex_methods_s accept_mutex_uslock_s = {
+ NULL,
+ accept_mutex_init_uslock,
+ accept_mutex_on_uslock,
+ accept_mutex_off_uslock,
+ "uslock"
+};
+#endif
+
+#if defined (HAVE_PTHREAD_SERIALIZED_ACCEPT)
+
+/* This code probably doesn't work on many platforms, but it is
+ * known to work really fast (at least on Solaris and AIX).
+ * Note that pthread mutexes are *NOT* released when a task
+ * dies ... the task has to free it itself. So we block signals and
+ * try to be nice about releasing the mutex.
+ */
+
+#include <pthread.h>
+
+static pthread_mutex_t *accept_mutex = (void *)(caddr_t) -1;
+static int have_accept_mutex;
+static sigset_t accept_block_mask;
+static sigset_t accept_previous_mask;
+
+static void accept_mutex_child_cleanup_pthread(void *foo)
+{
+ if (accept_mutex != (void *)(caddr_t)-1
+ && have_accept_mutex) {
+ pthread_mutex_unlock(accept_mutex);
+ }
+}
+
+static void accept_mutex_child_init_pthread(pool *p)
+{
+ ap_register_cleanup(p, NULL, accept_mutex_child_cleanup_pthread, ap_null_cleanup);
+}
+
+static void accept_mutex_cleanup_pthread(void *foo)
+{
+ if (accept_mutex != (void *)(caddr_t)-1
+ && munmap((caddr_t) accept_mutex, sizeof(*accept_mutex))) {
+ perror("munmap");
+ }
+ accept_mutex = (void *)(caddr_t)-1;
+}
+
+/* remove_sync_sigs() is from APR 0.9.4
+ *
+ * It is invalid to block synchronous signals, as such signals must
+ * be delivered on the thread that generated the original error
+ * (e.g., invalid storage reference). Blocking them interferes
+ * with proper recovery.
+ */
+static void remove_sync_sigs(sigset_t *sig_mask)
+{
+#ifdef SIGABRT
+ sigdelset(sig_mask, SIGABRT);
+#endif
+#ifdef SIGBUS
+ sigdelset(sig_mask, SIGBUS);
+#endif
+#ifdef SIGEMT
+ sigdelset(sig_mask, SIGEMT);
+#endif
+#ifdef SIGFPE
+ sigdelset(sig_mask, SIGFPE);
+#endif
+#ifdef SIGILL
+ sigdelset(sig_mask, SIGILL);
+#endif
+#ifdef SIGIOT
+ sigdelset(sig_mask, SIGIOT);
+#endif
+#ifdef SIGPIPE
+ sigdelset(sig_mask, SIGPIPE);
+#endif
+#ifdef SIGSEGV
+ sigdelset(sig_mask, SIGSEGV);
+#endif
+#ifdef SIGSYS
+ sigdelset(sig_mask, SIGSYS);
+#endif
+#ifdef SIGTRAP
+ sigdelset(sig_mask, SIGTRAP);
+#endif
+
+/* APR logic to remove SIGUSR2 not copied */
+}
+
+static void accept_mutex_init_pthread(pool *p)
+{
+ pthread_mutexattr_t mattr;
+ int fd;
+
+ fd = open("/dev/zero", O_RDWR);
+ if (fd == -1) {
+ perror("open(/dev/zero)");
+ exit(APEXIT_INIT);
+ }
+ accept_mutex = (pthread_mutex_t *) mmap((caddr_t) 0, sizeof(*accept_mutex),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (accept_mutex == (void *) (caddr_t) - 1) {
+ perror("mmap /dev/zero");
+ exit(APEXIT_INIT);
+ }
+ close(fd);
+ if ((errno = pthread_mutexattr_init(&mattr))) {
+ perror("pthread_mutexattr_init");
+ exit(APEXIT_INIT);
+ }
+#if !defined(CYGWIN)
+ /* Cygwin has problems with this pthread call claiming that these
+ * are "Invalid arguements", Stipe Tolj <tolj@wapme-systems.de>
+ */
+ if ((errno = pthread_mutexattr_setpshared(&mattr,
+ PTHREAD_PROCESS_SHARED))) {
+ perror("pthread_mutexattr_setpshared");
+ exit(APEXIT_INIT);
+ }
+#endif
+ if ((errno = pthread_mutex_init(accept_mutex, &mattr))) {
+ perror("pthread_mutex_init");
+ exit(APEXIT_INIT);
+ }
+ sigfillset(&accept_block_mask);
+ sigdelset(&accept_block_mask, SIGHUP);
+ sigdelset(&accept_block_mask, SIGTERM);
+ sigdelset(&accept_block_mask, SIGUSR1);
+ remove_sync_sigs(&accept_block_mask);
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_pthread, ap_null_cleanup);
+}
+
+static void accept_mutex_on_pthread(void)
+{
+ int err;
+
+ if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
+ perror("sigprocmask(SIG_BLOCK)");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ /* We need to block alarms here, since if we get killed *right* after
+ * locking the mutex, have_accept_mutex will not be set, and our
+ * child cleanup will not work.
+ */
+ ap_block_alarms();
+ if ((err = pthread_mutex_lock(accept_mutex))) {
+ errno = err;
+ perror("pthread_mutex_lock");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ have_accept_mutex = 1;
+ ap_unblock_alarms();
+}
+
+static void accept_mutex_off_pthread(void)
+{
+ int err;
+
+ /* Have to block alarms here, or else we might have a double-unlock, which
+ * is possible with pthread mutexes, since they are designed to be fast,
+ * and hence not necessarily make checks for ownership or multiple unlocks.
+ */
+ ap_block_alarms();
+ if ((err = pthread_mutex_unlock(accept_mutex))) {
+ errno = err;
+ perror("pthread_mutex_unlock");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ have_accept_mutex = 0;
+ ap_unblock_alarms();
+ if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
+ perror("sigprocmask(SIG_SETMASK)");
+ clean_child_exit(1);
+ }
+}
+
+accept_mutex_methods_s accept_mutex_pthread_s = {
+ accept_mutex_child_init_pthread,
+ accept_mutex_init_pthread,
+ accept_mutex_on_pthread,
+ accept_mutex_off_pthread,
+ "pthread"
+};
+#endif
+
+#if defined (HAVE_SYSVSEM_SERIALIZED_ACCEPT)
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+
+#ifdef NEED_UNION_SEMUN
+union semun {
+ int val;
+ struct semid_ds *buf;
+ ushort *array;
+};
+
+#endif
+
+static int sem_id = -1;
+static struct sembuf op_on;
+static struct sembuf op_off;
+
+/* We get a random semaphore ... the lame sysv semaphore interface
+ * means we have to be sure to clean this up or else we'll leak
+ * semaphores.
+ */
+static void accept_mutex_cleanup_sysvsem(void *foo)
+{
+ union semun ick;
+
+ if (sem_id < 0)
+ return;
+ /* this is ignored anyhow */
+ ick.val = 0;
+ semctl(sem_id, 0, IPC_RMID, ick);
+}
+
+#define accept_mutex_child_init_sysvsem(x)
+
+static void accept_mutex_init_sysvsem(pool *p)
+{
+ union semun ick;
+ struct semid_ds buf;
+
+ /* acquire the semaphore */
+ sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
+ if (sem_id < 0) {
+ perror("semget");
+ exit(APEXIT_INIT);
+ }
+ ick.val = 1;
+ if (semctl(sem_id, 0, SETVAL, ick) < 0) {
+ perror("semctl(SETVAL)");
+ exit(APEXIT_INIT);
+ }
+ if (!getuid()) {
+ /* restrict it to use only by the appropriate user_id ... not that this
+ * stops CGIs from acquiring it and dinking around with it.
+ */
+ buf.sem_perm.uid = ap_user_id;
+ buf.sem_perm.gid = ap_group_id;
+ buf.sem_perm.mode = 0600;
+ ick.buf = &buf;
+ if (semctl(sem_id, 0, IPC_SET, ick) < 0) {
+ perror("semctl(IPC_SET)");
+ exit(APEXIT_INIT);
+ }
+ }
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_sysvsem, ap_null_cleanup);
+
+ /* pre-initialize these */
+ op_on.sem_num = 0;
+ op_on.sem_op = -1;
+ op_on.sem_flg = SEM_UNDO;
+ op_off.sem_num = 0;
+ op_off.sem_op = 1;
+ op_off.sem_flg = SEM_UNDO;
+}
+
+static void accept_mutex_on_sysvsem(void)
+{
+ while (semop(sem_id, &op_on, 1) < 0) {
+ if (errno != EINTR) {
+ perror("accept_mutex_on");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ }
+}
+
+static void accept_mutex_off_sysvsem(void)
+{
+ while (semop(sem_id, &op_off, 1) < 0) {
+ if (errno != EINTR) {
+ perror("accept_mutex_off");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ }
+}
+
+accept_mutex_methods_s accept_mutex_sysvsem_s = {
+ NULL,
+ accept_mutex_init_sysvsem,
+ accept_mutex_on_sysvsem,
+ accept_mutex_off_sysvsem,
+ "sysvsem"
+};
+#endif
+
+#if defined(HAVE_FCNTL_SERIALIZED_ACCEPT)
+static struct flock lock_it;
+static struct flock unlock_it;
+
+static int lock_fd = -1;
+
+#define accept_mutex_child_init_fcntl(x)
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+static void accept_mutex_init_fcntl(pool *p)
+{
+
+ lock_it.l_whence = SEEK_SET; /* from current point */
+ lock_it.l_start = 0; /* -"- */
+ lock_it.l_len = 0; /* until end of file */
+ lock_it.l_type = F_WRLCK; /* set exclusive/write lock */
+ lock_it.l_pid = 0; /* pid not actually interesting */
+ unlock_it.l_whence = SEEK_SET; /* from current point */
+ unlock_it.l_start = 0; /* -"- */
+ unlock_it.l_len = 0; /* until end of file */
+ unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */
+ unlock_it.l_pid = 0; /* pid not actually interesting */
+
+ expand_lock_fname(p);
+ lock_fd = ap_popenf_ex(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0644, 1);
+ if (lock_fd == -1) {
+ perror("open");
+ fprintf(stderr, "Cannot open lock file: %s\n", ap_lock_fname);
+ exit(APEXIT_INIT);
+ }
+ unlink(ap_lock_fname);
+}
+
+static void accept_mutex_on_fcntl(void)
+{
+ int ret;
+
+ while ((ret = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) {
+ /* nop */
+ }
+
+ if (ret < 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "fcntl: F_SETLKW: Error getting accept lock, exiting! "
+ "Perhaps you need to use the LockFile directive to place "
+ "your lock file on a local disk!");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+static void accept_mutex_off_fcntl(void)
+{
+ int ret;
+
+ while ((ret = fcntl(lock_fd, F_SETLKW, &unlock_it)) < 0 && errno == EINTR) {
+ /* nop */
+ }
+ if (ret < 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "fcntl: F_SETLKW: Error freeing accept lock, exiting! "
+ "Perhaps you need to use the LockFile directive to place "
+ "your lock file on a local disk!");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+accept_mutex_methods_s accept_mutex_fcntl_s = {
+ NULL,
+ accept_mutex_init_fcntl,
+ accept_mutex_on_fcntl,
+ accept_mutex_off_fcntl,
+ "fcntl"
+};
+#endif
+
+#if defined(HAVE_FLOCK_SERIALIZED_ACCEPT)
+
+static int flock_fd = -1;
+
+static void accept_mutex_cleanup_flock(void *foo)
+{
+ unlink(ap_lock_fname);
+}
+
+/*
+ * Initialize mutex lock.
+ * Done by each child at it's birth
+ */
+static void accept_mutex_child_init_flock(pool *p)
+{
+
+ flock_fd = ap_popenf_ex(p, ap_lock_fname, O_WRONLY, 0600, 1);
+ if (flock_fd == -1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "Child cannot open lock file: %s", ap_lock_fname);
+ clean_child_exit(APEXIT_CHILDINIT);
+ }
+}
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+static void accept_mutex_init_flock(pool *p)
+{
+ expand_lock_fname(p);
+ unlink(ap_lock_fname);
+ flock_fd = ap_popenf_ex(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0600, 1);
+ if (flock_fd == -1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "Parent cannot open lock file: %s", ap_lock_fname);
+ exit(APEXIT_INIT);
+ }
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_flock, ap_null_cleanup);
+}
+
+static void accept_mutex_on_flock(void)
+{
+ int ret;
+
+ while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR)
+ continue;
+
+ if (ret < 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "flock: LOCK_EX: Error getting accept lock. Exiting!");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+static void accept_mutex_off_flock(void)
+{
+ if (flock(flock_fd, LOCK_UN) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "flock: LOCK_UN: Error freeing accept lock. Exiting!");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+accept_mutex_methods_s accept_mutex_flock_s = {
+ accept_mutex_child_init_flock,
+ accept_mutex_init_flock,
+ accept_mutex_on_flock,
+ accept_mutex_off_flock,
+ "flock"
+};
+#endif
+
+#if defined(HAVE_OS2SEM_SERIALIZED_ACCEPT)
+
+static HMTX lock_sem = -1;
+
+static void accept_mutex_cleanup_os2sem(void *foo)
+{
+ DosReleaseMutexSem(lock_sem);
+ DosCloseMutexSem(lock_sem);
+}
+
+/*
+ * Initialize mutex lock.
+ * Done by each child at it's birth
+ */
+static void accept_mutex_child_init_os2sem(pool *p)
+{
+ int rc = DosOpenMutexSem(NULL, &lock_sem);
+
+ if (rc != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "Child cannot open lock semaphore, rc=%d", rc);
+ clean_child_exit(APEXIT_CHILDINIT);
+ } else {
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_os2sem, ap_null_cleanup);
+ }
+}
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+static void accept_mutex_init_os2sem(pool *p)
+{
+ int rc = DosCreateMutexSem(NULL, &lock_sem, DC_SEM_SHARED, FALSE);
+
+ if (rc != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "Parent cannot create lock semaphore, rc=%d", rc);
+ exit(APEXIT_INIT);
+ }
+
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_os2sem, ap_null_cleanup);
+}
+
+static void accept_mutex_on_os2sem(void)
+{
+ int rc = DosRequestMutexSem(lock_sem, SEM_INDEFINITE_WAIT);
+
+ if (rc != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "OS2SEM: Error %d getting accept lock. Exiting!", rc);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+static void accept_mutex_off_os2sem(void)
+{
+ int rc = DosReleaseMutexSem(lock_sem);
+
+ if (rc != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "OS2SEM: Error %d freeing accept lock. Exiting!", rc);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+accept_mutex_methods_s accept_mutex_os2sem_s = {
+ accept_mutex_child_init_os2sem,
+ accept_mutex_init_os2sem,
+ accept_mutex_on_os2sem,
+ accept_mutex_off_os2sem,
+ "os2sem"
+};
+#endif
+
+#if defined(HAVE_TPF_CORE_SERIALIZED_ACCEPT)
+
+static int tpf_core_held;
+
+static void accept_mutex_cleanup_tpfcore(void *foo)
+{
+ if(tpf_core_held)
+ deqc(tpf_mutex_key, QUAL_S);
+}
+
+#define accept_mutex_init_tpfcore(x)
+
+static void accept_mutex_child_init_tpfcore(pool *p)
+{
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_tpfcore, ap_null_cleanup);
+ tpf_core_held = 0;
+}
+
+static void accept_mutex_on_tpfcore(void)
+{
+ enqc(tpf_mutex_key, ENQ_WAIT, 0, QUAL_S);
+ tpf_core_held = 1;
+ ap_check_signals();
+}
+
+static void accept_mutex_off_tpfcore(void)
+{
+ deqc(tpf_mutex_key, QUAL_S);
+ tpf_core_held = 0;
+ ap_check_signals();
+}
+
+accept_mutex_methods_s accept_mutex_tpfcore_s = {
+ accept_mutex_child_init_tpfcore,
+ NULL,
+ accept_mutex_on_tpfcore,
+ accept_mutex_off_tpfcore,
+ "tpfcore"
+};
+#endif
+
+#ifdef HAVE_BEOS_SERIALIZED_ACCEPT
+static sem_id _sem = -1;
+static int locked = 0;
+
+static void accept_mutex_child_cleanup_beos(void *foo)
+{
+ if (_sem > 0 && locked)
+ release_sem(_sem);
+}
+
+static void accept_mutex_child_init_beos(pool *p)
+{
+ ap_register_cleanup(p, NULL, accept_mutex_child_cleanup_beos, ap_null_cleanup);
+ locked = 0;
+}
+
+static void accept_mutex_cleanup_beos(void *foo)
+{
+ if (_sem > 0)
+ delete_sem(_sem);
+}
+
+static void accept_mutex_init_beos(pool *p)
+{
+ _sem = create_sem(1, "httpd_accept");
+ if (_sem < 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "Parent cannot create lock semaphore, sem=%ld", _sem);
+ exit(APEXIT_INIT);
+ }
+
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup_beos, ap_null_cleanup);
+}
+void accept_mutex_on_beos(void)
+{
+ if (locked == 0) {
+ if (acquire_sem(_sem) == B_OK)
+ locked = 1;
+ }
+}
+
+static void accept_mutex_off_beos(void)
+{
+ if (locked == 1) {
+ if (release_sem(_sem) == B_OK)
+ locked = 0;
+ }
+}
+
+accept_mutex_methods_s accept_mutex_beos_s = {
+ accept_mutex_child_init_beos,
+ accept_mutex_init_beos,
+ accept_mutex_on_beos,
+ accept_mutex_off_beos,
+ "beos_sem"
+};
+#endif /* HAVE_BEOS_SERIALIZED_ACCEPT */
+
+
+/* Generally, HAVE_NONE_SERIALIZED_ACCEPT simply won't work but
+ * for testing purposes, here it is... */
+#if defined HAVE_NONE_SERIALIZED_ACCEPT
+#if !defined(MULTITHREAD)
+/* Multithreaded systems don't complete between processes for
+ * the sockets. */
+#define NO_SERIALIZED_ACCEPT
+#endif
+
+accept_mutex_methods_s accept_mutex_none_s = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "none"
+};
+#endif
+
+#define AP_FPTR1(x,y) { if (x) ((* x)(y)); }
+#define AP_FPTR0(x) { if (x) ((* x)()); }
+
+#define accept_mutex_child_init(x) AP_FPTR1(amutex->child_init,x)
+#define accept_mutex_init(x) AP_FPTR1(amutex->init,x)
+#define accept_mutex_off() AP_FPTR0(amutex->off)
+#define accept_mutex_on() AP_FPTR0(amutex->on)
+
+char *ap_default_mutex_method(void)
+{
+ char *t;
+#if defined USE_USLOCK_SERIALIZED_ACCEPT
+ t = "uslock";
+#elif defined USE_PTHREAD_SERIALIZED_ACCEPT
+ t = "pthread";
+#elif defined USE_SYSVSEM_SERIALIZED_ACCEPT
+ t = "sysvsem";
+#elif defined USE_FCNTL_SERIALIZED_ACCEPT
+ t = "fcntl";
+#elif defined USE_FLOCK_SERIALIZED_ACCEPT
+ t = "flock";
+#elif defined USE_OS2SEM_SERIALIZED_ACCEPT
+ t = "os2sem";
+#elif defined USE_TPF_CORE_SERIALIZED_ACCEPT
+ t = "tpfcore";
+#elif defined USE_BEOS_SERIALIZED_ACCEPT
+ t = "beos_sem";
+#elif defined USE_NONE_SERIALIZED_ACCEPT
+ t = "none";
+#else
+ t = "default";
+#endif
+#if defined HAVE_USLOCK_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"uslock"))))
+ return "uslock";
+#endif
+#if defined HAVE_PTHREAD_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"pthread"))))
+ return "pthread";
+#endif
+#if defined HAVE_SYSVSEM_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"sysvsem"))))
+ return "sysvsem";
+#endif
+#if defined HAVE_FCNTL_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"fcntl"))))
+ return "fcntl";
+#endif
+#if defined HAVE_FLOCK_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"flock"))))
+ return "flock";
+#endif
+#if defined HAVE_OS2SEM_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"os2sem"))))
+ return "os2sem";
+#endif
+#if defined HAVE_TPF_CORE_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"tpfcore"))))
+ return "tpfcore";
+#endif
+#if defined HAVE_BEOS_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"beos_sem"))))
+ return "beos_sem";
+#endif
+#if defined HAVE_NONE_SERIALIZED_ACCEPT
+ if ((!(strcasecmp(t,"default"))) || (!(strcasecmp(t,"none"))))
+ return "none";
+#endif
+
+ fprintf(stderr, "No default accept serialization known!!\n");
+ exit(APEXIT_INIT);
+ /*NOTREACHED */
+ return "unknown";
+}
+
+char *ap_init_mutex_method(char *t)
+{
+ if (!(strcasecmp(t,"default")))
+ t = ap_default_mutex_method();
+
+#if defined HAVE_USLOCK_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"uslock"))) {
+ amutex = &accept_mutex_uslock_s;
+ } else
+#endif
+#if defined HAVE_PTHREAD_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"pthread"))) {
+ amutex = &accept_mutex_pthread_s;
+ } else
+#endif
+#if defined HAVE_SYSVSEM_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"sysvsem"))) {
+ amutex = &accept_mutex_sysvsem_s;
+ } else
+#endif
+#if defined HAVE_FCNTL_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"fcntl"))) {
+ amutex = &accept_mutex_fcntl_s;
+ } else
+#endif
+#if defined HAVE_FLOCK_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"flock"))) {
+ amutex = &accept_mutex_flock_s;
+ } else
+#endif
+#if defined HAVE_OS2SEM_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"os2sem"))) {
+ amutex = &accept_mutex_os2sem_s;
+ } else
+#endif
+#if defined HAVE_TPF_CORE_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"tpfcore"))) {
+ amutex = &accept_mutex_tpfcore_s;
+ } else
+#endif
+#if defined HAVE_BEOS_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"beos_sem"))) {
+ amutex = &accept_mutex_beos_s;
+ } else
+#endif
+#if defined HAVE_NONE_SERIALIZED_ACCEPT
+ if (!(strcasecmp(t,"none"))) {
+ amutex = &accept_mutex_none_s;
+ } else
+#endif
+ {
+/* Ignore this directive on Windows */
+#ifndef WIN32
+ if (server_conf) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "Requested serialization method '%s' not available",t);
+ exit(APEXIT_INIT);
+ } else {
+ fprintf(stderr, "Requested serialization method '%s' not available\n", t);
+ exit(APEXIT_INIT);
+ }
+#endif
+ }
+ return NULL;
+}
+
+/* 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 != ap_listeners) {stmt;}} while(0)
+#else
+#define SAFE_ACCEPT(stmt) do {stmt;} while(0)
+#endif
+
+static void usage(char *bin)
+{
+ char pad[MAX_STRING_LEN];
+ unsigned i;
+
+ for (i = 0; i < strlen(bin); i++)
+ pad[i] = ' ';
+ pad[i] = '\0';
+#ifdef WIN32
+ fprintf(stderr, "Usage: %s [-D name] [-d directory] [-f file] [-n service]\n", bin);
+ fprintf(stderr, " %s [-C \"directive\"] [-c \"directive\"] [-k signal]\n", pad);
+ fprintf(stderr, " %s [-v] [-V] [-h] [-l] [-L] [-S] [-t] [-T]\n", pad);
+#else /* !WIN32 */
+#ifdef SHARED_CORE
+ fprintf(stderr, "Usage: %s [-R directory] [-D name] [-d directory] [-f file]\n", bin);
+#else
+ fprintf(stderr, "Usage: %s [-D name] [-d directory] [-f file]\n", bin);
+#endif
+ fprintf(stderr, " %s [-C \"directive\"] [-c \"directive\"]\n", pad);
+ fprintf(stderr, " %s [-v] [-V] [-h] [-l] [-L] [-S] [-t] [-T] [-F]\n", pad);
+ fprintf(stderr, "Options:\n");
+#ifdef SHARED_CORE
+ fprintf(stderr, " -R directory : specify an alternate location for shared object files\n");
+#endif
+#endif /* !WIN32 */
+ fprintf(stderr, " -D name : define a name for use in <IfDefine name> directives\n");
+ fprintf(stderr, " -d directory : specify an alternate initial ServerRoot\n");
+ fprintf(stderr, " -f file : specify an alternate ServerConfigFile\n");
+ fprintf(stderr, " -C \"directive\" : process directive before reading config files\n");
+ fprintf(stderr, " -c \"directive\" : process directive after reading config files\n");
+ fprintf(stderr, " -v : show version number\n");
+ fprintf(stderr, " -V : show compile settings\n");
+ fprintf(stderr, " -h : list available command line options (this page)\n");
+ fprintf(stderr, " -l : list compiled-in modules\n");
+ fprintf(stderr, " -L : list available configuration directives\n");
+ fprintf(stderr, " -S : show parsed settings (currently only vhost settings)\n");
+#ifdef NETWARE
+ fprintf(stderr, " -e : force the display of configuration file errors to the logger screen\n");
+ fprintf(stderr, " -s : load Apache without a screen\n");
+#endif
+ fprintf(stderr, " -t : run syntax check for config files (with docroot check)\n");
+ fprintf(stderr, " -T : run syntax check for config files (without docroot check)\n");
+#ifndef WIN32
+ fprintf(stderr, " -F : run main process in foreground, for process supervisors\n");
+#endif
+#ifdef WIN32
+ fprintf(stderr, " -n name : name the Apache service for -k options below;\n");
+ fprintf(stderr, " -k stop|shutdown : tell running Apache to shutdown\n");
+ fprintf(stderr, " -k restart : tell running Apache to do a graceful restart\n");
+ fprintf(stderr, " -k start : tell Apache to start\n");
+ fprintf(stderr, " -k install | -i: install an Apache service\n");
+ fprintf(stderr, " -k config : reconfigure an installed Apache service\n");
+ fprintf(stderr, " -k uninstall | -u: uninstall an Apache service\n");
+ fprintf(stderr, " -W service : after -k config|install; Apache starts after 'service'\n");
+ fprintf(stderr, " -w : holds the window open for 30 seconds for fatal errors.\n");
+#endif
+
+#if defined(NETWARE)
+ clean_parent_exit(0);
+#else
+ exit(1);
+#endif
+}
+
+
+
+#ifdef NETWARE
+/* Thread Storage Data */
+typedef struct _TSD {
+ conn_rec* current_conn;
+ int alarms_blocked;
+ int alarm_pending;
+ request_rec* timeout_req;
+ char* timeout_name;
+ JMP_BUF jmpbuffer;
+ int exit_after_unblock;
+ void (*alarm_fn) (int);
+ unsigned int alarm_expiry_time;
+} TSD;
+
+static TSD Tsd;
+
+void init_tsd()
+{
+ int *thread_ptr;
+
+ memset(&Tsd, 0, sizeof(TSD));
+ thread_ptr = __get_thread_data_area_ptr();
+ *thread_ptr = (int) &Tsd;
+}
+
+#define get_tsd TSD* tsd = (TSD*) Thread_Data_Area;
+#define current_conn tsd->current_conn
+#define alarms_blocked tsd->alarms_blocked
+#define alarm_pending tsd->alarm_pending
+#define timeout_req tsd->timeout_req
+#define timeout_name tsd->timeout_name
+#define jmpbuffer tsd->jmpbuffer
+#define exit_after_unblock tsd->exit_after_unblock
+#define alarm_fn tsd->alarm_fn
+#define alarm_expiry_time tsd->alarm_expiry_time
+
+#else
+/*****************************************************************
+ *
+ * Timeout handling. DISTINCTLY not thread-safe, but all this stuff
+ * has to change for threads anyway. Note that this code allows only
+ * one timeout in progress at a time...
+ */
+
+static APACHE_TLS conn_rec *volatile current_conn;
+static APACHE_TLS request_rec *volatile timeout_req;
+static APACHE_TLS const char *volatile timeout_name = NULL;
+static APACHE_TLS int volatile alarms_blocked = 0;
+static APACHE_TLS int volatile alarm_pending = 0;
+#endif
+
+
+static void timeout(int sig)
+{
+ void *dirconf;
+#ifdef NETWARE
+ get_tsd
+#endif
+ if (alarms_blocked) {
+ alarm_pending = 1;
+ return;
+ }
+ if (exit_after_unblock) {
+ clean_child_exit(0);
+ }
+
+ if (!current_conn) {
+ ap_longjmp(jmpbuffer, 1);
+ }
+
+ if (timeout_req != NULL)
+ dirconf = timeout_req->per_dir_config;
+ else
+ dirconf = current_conn->server->lookup_defaults;
+ if (!current_conn->keptalive) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,
+ current_conn->server, "[client %s] %s timed out",
+ current_conn->remote_ip,
+ timeout_name ? timeout_name : "request");
+ }
+
+ if (timeout_req) {
+ /* Someone has asked for this transaction to just be aborted
+ * if it times out...
+ */
+ request_rec *log_req = timeout_req;
+ request_rec *save_req = timeout_req;
+
+ /* avoid looping... if ap_log_transaction started another
+ * timer (say via rfc1413.c) we could loop...
+ */
+ timeout_req = NULL;
+
+ while (log_req->main || log_req->prev) {
+#ifdef NETWARE
+ ThreadSwitch();
+#endif
+ /* Get back to original request... */
+ if (log_req->main)
+ log_req = log_req->main;
+ else
+ log_req = log_req->prev;
+ }
+
+ if (!current_conn->keptalive) {
+ /* in some cases we come here before setting the time */
+ if (log_req->request_time == 0) {
+ log_req->request_time = time(NULL);
+ }
+ ap_log_transaction(log_req);
+ }
+
+ ap_bsetflag(save_req->connection->client, B_EOUT, 1);
+ ap_bclose(save_req->connection->client);
+
+ if (!ap_standalone)
+ exit(0);
+ ap_longjmp(jmpbuffer, 1);
+ }
+ else { /* abort the connection */
+ ap_bsetflag(current_conn->client, B_EOUT, 1);
+ ap_bclose(current_conn->client);
+ current_conn->aborted = 1;
+ }
+}
+
+
+/*
+ * These two called from alloc.c to protect its critical sections...
+ * Note that they can nest (as when destroying the sub_pools of a pool
+ * which is itself being cleared); we have to support that here.
+ */
+
+API_EXPORT(void) ap_block_alarms(void)
+{
+#ifdef NETWARE
+ get_tsd
+#endif
+ ++alarms_blocked;
+}
+
+API_EXPORT(void) ap_unblock_alarms(void)
+{
+#ifdef NETWARE
+ get_tsd
+#endif
+ --alarms_blocked;
+ if (alarms_blocked == 0) {
+ if (exit_after_unblock) {
+ /* We have a couple race conditions to deal with here, we can't
+ * allow a timeout that comes in this small interval to allow
+ * the child to jump back to the main loop. Instead we block
+ * alarms again, and then note that exit_after_unblock is
+ * being dealt with. We choose this way to solve this so that
+ * the common path through unblock_alarms() is really short.
+ */
+ ++alarms_blocked;
+ exit_after_unblock = 0;
+ clean_child_exit(0);
+ }
+ if (alarm_pending) {
+ alarm_pending = 0;
+ timeout(0);
+ }
+ }
+}
+
+#ifndef NETWARE
+static APACHE_TLS void (*volatile alarm_fn) (int) = NULL;
+#endif
+#if defined(WIN32) || defined(CYGWIN_WINSOCK)
+static APACHE_TLS unsigned int alarm_expiry_time = 0;
+#endif /* WIN32 */
+
+#if !defined(WIN32) && !defined(NETWARE)
+static void alrm_handler(int sig)
+{
+#ifdef TPF41
+ signal(sig, exit);
+#endif
+ if (alarm_fn) {
+ (*alarm_fn) (sig);
+ }
+}
+#endif
+
+API_EXPORT(unsigned int) ap_set_callback_and_alarm(void (*fn) (int), int x)
+{
+ unsigned int old;
+
+#if defined(WIN32) || defined(NETWARE)
+ time_t now = time(NULL);
+#ifdef NETWARE
+ get_tsd
+#endif
+ old = alarm_expiry_time;
+
+ if (old)
+ old -= now;
+ if (x == 0) {
+ alarm_fn = NULL;
+ alarm_expiry_time = 0;
+ }
+ else {
+ alarm_fn = fn;
+ alarm_expiry_time = now + x;
+ }
+#else
+ if (alarm_fn && x && fn != alarm_fn) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL,
+ "ap_set_callback_and_alarm: possible nested timer!");
+ }
+ alarm_fn = fn;
+#ifndef OPTIMIZE_TIMEOUTS
+ old = alarm(x);
+#else
+ if (child_timeouts) {
+ old = alarm(x);
+ }
+ else {
+ /* Just note the timeout in our scoreboard, no need to call the system.
+ * We also note that the virtual time has gone forward.
+ */
+ ap_check_signals();
+ old = ap_scoreboard_image->servers[my_child_num].timeout_len;
+ ap_scoreboard_image->servers[my_child_num].timeout_len = x;
+ ++ap_scoreboard_image->servers[my_child_num].cur_vtime;
+ }
+#endif
+#endif
+ return (old);
+}
+
+
+#if defined(WIN32) || defined(NETWARE) || defined(CYGWIN_WINSOCK)
+API_EXPORT(int) ap_check_alarm(void)
+{
+#ifdef NETWARE
+ get_tsd
+#endif
+ if (alarm_expiry_time) {
+ unsigned int t;
+
+ t = time(NULL);
+ if (t >= alarm_expiry_time) {
+ alarm_expiry_time = 0;
+ (*alarm_fn) (0);
+ return (-1);
+ }
+ else {
+ return (alarm_expiry_time - t);
+ }
+ }
+ else
+ return (0);
+}
+#endif /* WIN32 */
+
+#ifdef TPF
+API_EXPORT(int) ap_check_alarm(void)
+{
+ int i;
+
+#ifdef OPTIMIZE_TIMEOUTS
+ /* just pull the timeout from the scoreboard */
+ ap_sync_scoreboard_image();
+ i = ap_scoreboard_image->servers[my_child_num].timeout_len;
+#else
+ i = ap_set_callback_and_alarm(alarm_fn, 3); /* determine time left */
+ /* the 3 seconds is just an arbitrary amount of time to keep the alarm
+ from expiring before it is reset on this next line: */
+ ap_set_callback_and_alarm(alarm_fn, i); /* restore time left */
+#endif
+
+ return i; /* return the time left */
+}
+
+#endif /* TPF */
+
+/* reset_timeout (request_rec *) resets the timeout in effect,
+ * as long as it hasn't expired already.
+ */
+
+API_EXPORT(void) ap_reset_timeout(request_rec *r)
+{
+ int i;
+#ifdef NETWARE
+ get_tsd
+#endif
+ if (timeout_name) { /* timeout has been set */
+ i = ap_set_callback_and_alarm(alarm_fn, r->server->timeout);
+ if (i == 0) /* timeout already expired, so set it back to 0 */
+ ap_set_callback_and_alarm(alarm_fn, 0);
+ }
+}
+
+
+
+
+API_EXPORT(void) ap_keepalive_timeout(char *name, request_rec *r)
+{
+ unsigned int to;
+#ifdef NETWARE
+ get_tsd
+#endif
+ timeout_req = r;
+ timeout_name = name;
+ if (r->connection->keptalive)
+ to = r->server->keep_alive_timeout;
+ else
+ to = r->server->timeout;
+ ap_set_callback_and_alarm(timeout, to);
+}
+
+API_EXPORT(void) ap_hard_timeout(char *name, request_rec *r)
+{
+#ifdef NETWARE
+ get_tsd
+#endif
+ timeout_req = r;
+ timeout_name = name;
+ ap_set_callback_and_alarm(timeout, r->server->timeout);
+}
+
+API_EXPORT(void) ap_soft_timeout(char *name, request_rec *r)
+{
+#ifdef NETWARE
+ get_tsd
+#endif
+ timeout_name = name;
+ ap_set_callback_and_alarm(timeout, r->server->timeout);
+}
+
+API_EXPORT(void) ap_kill_timeout(request_rec *dummy)
+{
+#ifdef NETWARE
+ get_tsd
+#endif
+ ap_check_signals();
+ ap_set_callback_and_alarm(NULL, 0);
+ timeout_req = NULL;
+ timeout_name = NULL;
+}
+
+
+/*
+ * More machine-dependent networking gooo... on some systems,
+ * you've got to be *really* sure that all the packets are acknowledged
+ * before closing the connection, since the client will not be able
+ * to see the last response if their TCP buffer is flushed by a RST
+ * packet from us, which is what the server's TCP stack will send
+ * if it receives any request data after closing the connection.
+ *
+ * In an ideal world, this function would be accomplished by simply
+ * setting the socket option SO_LINGER and handling it within the
+ * server's TCP stack while the process continues on to the next request.
+ * Unfortunately, it seems that most (if not all) operating systems
+ * block the server process on close() when SO_LINGER is used.
+ * For those that don't, see USE_SO_LINGER below. For the rest,
+ * we have created a home-brew lingering_close.
+ *
+ * Many operating systems tend to block, puke, or otherwise mishandle
+ * calls to shutdown only half of the connection. You should define
+ * NO_LINGCLOSE in ap_config.h if such is the case for your system.
+ */
+#ifndef MAX_SECS_TO_LINGER
+#define MAX_SECS_TO_LINGER 30
+#endif
+
+#ifdef USE_SO_LINGER
+#define NO_LINGCLOSE /* The two lingering options are exclusive */
+
+static void sock_enable_linger(int s)
+{
+ struct linger li;
+
+ li.l_onoff = 1;
+ li.l_linger = MAX_SECS_TO_LINGER;
+
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER,
+ (char *) &li, sizeof(struct linger)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "setsockopt: (SO_LINGER)");
+ /* not a fatal error */
+ }
+}
+
+#else
+#define sock_enable_linger(s) /* NOOP */
+#endif /* USE_SO_LINGER */
+
+#ifndef NO_LINGCLOSE
+
+/* Special version of timeout for lingering_close */
+
+static void lingerout(int sig)
+{
+#ifdef NETWARE
+ get_tsd
+#endif
+ if (alarms_blocked) {
+ alarm_pending = 1;
+ return;
+ }
+
+ if (!current_conn) {
+ ap_longjmp(jmpbuffer, 1);
+ }
+ ap_bsetflag(current_conn->client, B_EOUT, 1);
+ current_conn->aborted = 1;
+}
+
+static void linger_timeout(void)
+{
+#ifdef NETWARE
+ get_tsd
+#endif
+ timeout_name = "lingering close";
+ ap_set_callback_and_alarm(lingerout, MAX_SECS_TO_LINGER);
+}
+
+/* Since many clients will abort a connection instead of closing it,
+ * attempting to log an error message from this routine will only
+ * confuse the webmaster. There doesn't seem to be any portable way to
+ * distinguish between a dropped connection and something that might be
+ * worth logging.
+ */
+static void lingering_close(request_rec *r)
+{
+ char dummybuf[512];
+ struct timeval tv;
+ fd_set lfds;
+ int select_rv;
+ int lsd;
+
+ /* Prevent a slow-drip client from holding us here indefinitely */
+
+ linger_timeout();
+
+ /* Send any leftover data to the client, but never try to again */
+
+ if (ap_bflush(r->connection->client) == -1) {
+ ap_kill_timeout(r);
+ ap_bclose(r->connection->client);
+ return;
+ }
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+
+ /* Close our half of the connection --- send the client a FIN */
+
+ lsd = r->connection->client->fd;
+
+ if ((shutdown(lsd, 1) != 0) || r->connection->aborted) {
+ ap_kill_timeout(r);
+ ap_bclose(r->connection->client);
+ return;
+ }
+
+ /* Set up to wait for readable data on socket... */
+
+ FD_ZERO(&lfds);
+
+ /* Wait for readable data or error condition on socket;
+ * slurp up any data that arrives... We exit when we go for an
+ * interval of tv length without getting any more data, get an error
+ * from select(), get an error or EOF on a read, or the timer expires.
+ */
+
+ do {
+ /* We use a 2 second timeout because current (Feb 97) browsers
+ * fail to close a connection after the server closes it. Thus,
+ * to avoid keeping the child busy, we are only lingering long enough
+ * for a client that is actively sending data on a connection.
+ * This should be sufficient unless the connection is massively
+ * losing packets, in which case we might have missed the RST anyway.
+ * These parameters are reset on each pass, since they might be
+ * changed by select.
+ */
+#ifdef NETWARE
+ ThreadSwitch();
+#endif
+
+ FD_SET(lsd, &lfds);
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+
+ select_rv = ap_select(lsd + 1, &lfds, NULL, NULL, &tv);
+
+ } while ((select_rv > 0) &&
+#if defined(WIN32) || defined(NETWARE)
+ (recv(lsd, dummybuf, sizeof(dummybuf), 0) > 0));
+#else
+ (read(lsd, dummybuf, sizeof(dummybuf)) > 0));
+#endif
+
+ /* Should now have seen final ack. Safe to finally kill socket */
+
+ ap_bclose(r->connection->client);
+
+ ap_kill_timeout(r);
+}
+#endif /* ndef NO_LINGCLOSE */
+
+/*****************************************************************
+ * dealing with other children
+ */
+
+#ifndef NO_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, (ap_wait_t)-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)
+{
+ 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, (ap_wait_t)-1);
+ }
+}
+
+/* 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
+
+/*****************************************************************
+ *
+ * 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...
+ */
+
+#ifdef MULTITHREAD
+/*
+ * In the multithreaded mode, have multiple threads - not multiple
+ * processes that need to talk to each other. Just use a simple
+ * malloc. But let the routines that follow, think that you have
+ * shared memory (so they use memcpy etc.)
+ */
+
+static void reinit_scoreboard(pool *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) ap_sync_scoreboard_image(void)
+{
+}
+
+
+#else /* MULTITHREAD */
+#if defined(USE_OS2_SCOREBOARD)
+
+/* The next two routines are used to access shared memory under OS/2. */
+/* This requires EMX v09c to be installed. */
+
+caddr_t create_shared_heap(const char *name, size_t size)
+{
+ ULONG rc;
+ void *mem;
+ Heap_t h;
+
+ rc = DosAllocSharedMem(&mem, name, size,
+ PAG_COMMIT | PAG_READ | PAG_WRITE);
+ if (rc != 0)
+ return NULL;
+ h = _ucreate(mem, size, !_BLOCK_CLEAN, _HEAP_REGULAR | _HEAP_SHARED,
+ NULL, NULL);
+ if (h == NULL)
+ DosFreeMem(mem);
+ return (caddr_t) h;
+}
+
+caddr_t get_shared_heap(const char *Name)
+{
+
+ PVOID BaseAddress; /* Pointer to the base address of
+ the shared memory object */
+ ULONG AttributeFlags; /* Flags describing characteristics
+ of the shared memory object */
+ APIRET rc; /* Return code */
+
+ /* Request read and write access to */
+ /* the shared memory object */
+ AttributeFlags = PAG_WRITE | PAG_READ;
+
+ rc = DosGetNamedSharedMem(&BaseAddress, Name, AttributeFlags);
+
+ if (rc != 0) {
+ printf("DosGetNamedSharedMem error: return code = %ld", rc);
+ return 0;
+ }
+
+ return BaseAddress;
+}
+
+static void setup_shared_mem(pool *p)
+{
+ caddr_t m;
+
+ int rc;
+
+ m = (caddr_t) create_shared_heap("\\SHAREMEM\\SCOREBOARD", SCOREBOARD_SIZE);
+ if (m == 0) {
+ fprintf(stderr, "%s: Could not create OS/2 Shared memory pool.\n",
+ ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+
+ rc = _uopen((Heap_t) m);
+ if (rc != 0) {
+ fprintf(stderr,
+ "%s: Could not uopen() newly created OS/2 Shared memory pool.\n",
+ ap_server_argv0);
+ }
+ ap_scoreboard_image = (scoreboard *) m;
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+ caddr_t m;
+ int rc;
+
+ m = (caddr_t) get_shared_heap("\\SHAREMEM\\SCOREBOARD");
+ if (m == 0) {
+ fprintf(stderr, "%s: Could not find existing OS/2 Shared memory pool.\n",
+ ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+
+ rc = _uopen((Heap_t) m);
+ ap_scoreboard_image = (scoreboard *) m;
+}
+
+#elif defined(USE_POSIX_SCOREBOARD)
+#include <sys/mman.h>
+/*
+ * POSIX 1003.4 style
+ *
+ * Note 1:
+ * As of version 4.23A, shared memory in QNX must reside under /dev/shmem,
+ * where no subdirectories allowed.
+ *
+ * POSIX shm_open() and shm_unlink() will take care about this issue,
+ * but to avoid confusion, I suggest to redefine scoreboard file name
+ * in httpd.conf to cut "logs/" from it. With default setup actual name
+ * will be "/dev/shmem/logs.apache_status".
+ *
+ * If something went wrong and Apache did not unlinked this object upon
+ * exit, you can remove it manually, using "rm -f" command.
+ *
+ * Note 2:
+ * <sys/mman.h> in QNX defines MAP_ANON, but current implementation
+ * does NOT support BSD style anonymous mapping. So, the order of
+ * conditional compilation is important:
+ * this #ifdef section must be ABOVE the next one (BSD style).
+ *
+ * I tested this stuff and it works fine for me, but if it provides
+ * trouble for you, just comment out USE_MMAP_SCOREBOARD in QNX section
+ * of ap_config.h
+ *
+ * June 5, 1997,
+ * Igor N. Kovalenko -- infoh@mail.wplus.net
+ */
+
+static void cleanup_shared_mem(void *d)
+{
+ shm_unlink(ap_scoreboard_fname);
+}
+
+static void setup_shared_mem(pool *p)
+{
+ char buf[512];
+ caddr_t m;
+ int fd;
+
+ fd = shm_open(ap_scoreboard_fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ ap_snprintf(buf, sizeof(buf), "%s: could not open(create) scoreboard",
+ ap_server_argv0);
+ perror(buf);
+ exit(APEXIT_INIT);
+ }
+ if (ltrunc(fd, (off_t) SCOREBOARD_SIZE, SEEK_SET) == -1) {
+ ap_snprintf(buf, sizeof(buf), "%s: could not ltrunc scoreboard",
+ ap_server_argv0);
+ perror(buf);
+ shm_unlink(ap_scoreboard_fname);
+ exit(APEXIT_INIT);
+ }
+ if ((m = (caddr_t) mmap((caddr_t) 0,
+ (size_t) SCOREBOARD_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, (off_t) 0)) == (caddr_t) - 1) {
+ ap_snprintf(buf, sizeof(buf), "%s: cannot mmap scoreboard",
+ ap_server_argv0);
+ perror(buf);
+ shm_unlink(ap_scoreboard_fname);
+ exit(APEXIT_INIT);
+ }
+ close(fd);
+ ap_register_cleanup(p, NULL, cleanup_shared_mem, ap_null_cleanup);
+ ap_scoreboard_image = (scoreboard *) m;
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+}
+
+#elif defined(USE_MMAP_SCOREBOARD)
+
+static void setup_shared_mem(pool *p)
+{
+ caddr_t m;
+
+#if defined(MAP_ANON)
+/* BSD style */
+#ifdef CONVEXOS11
+ /*
+ * 9-Aug-97 - Jeff Venters (venters@convex.hp.com)
+ * ConvexOS maps address space as follows:
+ * 0x00000000 - 0x7fffffff : Kernel
+ * 0x80000000 - 0xffffffff : User
+ * Start mmapped area 1GB above start of text.
+ *
+ * Also, the length requires a pointer as the actual length is
+ * returned (rounded up to a page boundary).
+ */
+ {
+ unsigned len = SCOREBOARD_SIZE;
+
+ m = mmap((caddr_t) 0xC0000000, &len,
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, NOFD, 0);
+ }
+#elif defined(MAP_TMPFILE)
+ {
+ char mfile[] = "/tmp/apache_shmem_XXXX";
+ int fd = mkstemp(mfile);
+ if (fd == -1) {
+ perror("open");
+ fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile);
+ exit(APEXIT_INIT);
+ }
+ m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (m == (caddr_t) - 1) {
+ perror("mmap");
+ fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile);
+ exit(APEXIT_INIT);
+ }
+ close(fd);
+ unlink(mfile);
+ }
+#else
+ m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
+#endif
+ if (m == (caddr_t) - 1) {
+ perror("mmap");
+ fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+#else
+/* Sun style */
+ int fd;
+
+ fd = open("/dev/zero", O_RDWR);
+ if (fd == -1) {
+ perror("open");
+ fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+ m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (m == (caddr_t) - 1) {
+ perror("mmap");
+ fprintf(stderr, "%s: Could not mmap /dev/zero\n", ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+ close(fd);
+#endif
+ ap_scoreboard_image = (scoreboard *) m;
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+}
+
+#elif defined(USE_SHMGET_SCOREBOARD)
+static key_t shmkey = IPC_PRIVATE;
+static int shmid = -1;
+
+static void setup_shared_mem(pool *p)
+{
+ struct shmid_ds shmbuf;
+#ifdef MOVEBREAK
+ char *obrk;
+#endif
+
+ if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT | SHM_R | SHM_W)) == -1) {
+#ifdef LINUX
+ if (errno == ENOSYS) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "Your kernel was built without CONFIG_SYSVIPC\n"
+ "%s: Please consult the Apache FAQ for details",
+ ap_server_argv0);
+ }
+#endif
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "could not call shmget");
+ exit(APEXIT_INIT);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "created shared memory segment #%d", shmid);
+
+#ifdef MOVEBREAK
+ /*
+ * Some SysV systems place the shared segment WAY too close
+ * to the dynamic memory break point (sbrk(0)). This severely
+ * limits the use of malloc/sbrk in the program since sbrk will
+ * refuse to move past that point.
+ *
+ * To get around this, we move the break point "way up there",
+ * attach the segment and then move break back down. Ugly
+ */
+ if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "sbrk() could not move break");
+ }
+#endif
+
+#define BADSHMAT ((scoreboard *)(-1))
+ if ((ap_scoreboard_image = (scoreboard *) shmat(shmid, 0, 0)) == BADSHMAT) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, "shmat error");
+ /*
+ * We exit below, after we try to remove the segment
+ */
+ }
+ /* only worry about permissions if we attached the segment
+ and we want/need to change the uid/gid */
+ else if (ap_change_shmem_uid) {
+ if (shmctl(shmid, IPC_STAT, &shmbuf) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "shmctl() could not stat segment #%d", shmid);
+ }
+ else {
+ shmbuf.shm_perm.uid = ap_user_id;
+ shmbuf.shm_perm.gid = ap_group_id;
+ if (shmctl(shmid, IPC_SET, &shmbuf) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "shmctl() could not set segment #%d", shmid);
+ }
+ }
+ }
+ /*
+ * We must avoid leaving segments in the kernel's
+ * (small) tables.
+ */
+ if (shmctl(shmid, IPC_RMID, NULL) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "shmctl: IPC_RMID: could not remove shared memory segment #%d",
+ shmid);
+ }
+ if (ap_scoreboard_image == BADSHMAT) /* now bailout */
+ exit(APEXIT_INIT);
+
+#ifdef MOVEBREAK
+ if (obrk == (char *) -1)
+ return; /* nothing else to do */
+ if (sbrk(-(MOVEBREAK)) == (char *) -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "sbrk() could not move break back");
+ }
+#endif
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+}
+
+#else
+#define SCOREBOARD_FILE
+static scoreboard _scoreboard_image;
+static int scoreboard_fd = -1;
+
+/* XXX: things are seriously screwed if we ever have to do a partial
+ * read or write ... we could get a corrupted scoreboard
+ */
+static int force_write(int fd, void *buffer, int bufsz)
+{
+ int rv, orig_sz = bufsz;
+
+ do {
+ rv = write(fd, buffer, bufsz);
+ if (rv > 0) {
+ buffer = (char *) buffer + rv;
+ bufsz -= rv;
+ }
+ } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
+
+ return rv < 0 ? rv : orig_sz - bufsz;
+}
+
+static int force_read(int fd, void *buffer, int bufsz)
+{
+ int rv, orig_sz = bufsz;
+
+ do {
+ rv = read(fd, buffer, bufsz);
+ if (rv > 0) {
+ buffer = (char *) buffer + rv;
+ bufsz -= rv;
+ }
+ } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
+
+ return rv < 0 ? rv : orig_sz - bufsz;
+}
+
+static void cleanup_scoreboard_file(void *foo)
+{
+ unlink(ap_scoreboard_fname);
+}
+
+void reopen_scoreboard(pool *p)
+{
+ if (scoreboard_fd != -1)
+ ap_pclosef(p, scoreboard_fd);
+
+#ifdef TPF
+ ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname);
+#endif /* TPF */
+ scoreboard_fd = ap_popenf_ex(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0666, 1);
+ if (scoreboard_fd == -1) {
+ perror(ap_scoreboard_fname);
+ fprintf(stderr, "Cannot open scoreboard file:\n");
+ clean_child_exit(1);
+ }
+}
+#endif
+
+/* Called by parent process */
+static void reinit_scoreboard(pool *p)
+{
+ int running_gen = 0;
+ if (ap_scoreboard_image)
+ running_gen = ap_scoreboard_image->global.running_generation;
+
+#ifndef SCOREBOARD_FILE
+ if (ap_scoreboard_image == NULL) {
+ setup_shared_mem(p);
+ }
+ memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE);
+ ap_scoreboard_image->global.running_generation = running_gen;
+#else
+ ap_scoreboard_image = &_scoreboard_image;
+ ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname);
+
+ scoreboard_fd = ap_popenf_ex(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0644, 1);
+ if (scoreboard_fd == -1) {
+ perror(ap_scoreboard_fname);
+ fprintf(stderr, "Cannot open scoreboard file:\n");
+ exit(APEXIT_INIT);
+ }
+ ap_register_cleanup(p, NULL, cleanup_scoreboard_file, ap_null_cleanup);
+
+ memset((char *) ap_scoreboard_image, 0, sizeof(*ap_scoreboard_image));
+ ap_scoreboard_image->global.running_generation = running_gen;
+ force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image));
+#endif
+}
+
+/* Routines called to deal with the scoreboard image
+ * --- note that we do *not* need write locks, since update_child_status
+ * only updates a *single* record in place, and only one process writes to
+ * a given scoreboard slot at a time (either the child process owning that
+ * slot, or the parent, noting that the child has died).
+ *
+ * As a final note --- setting the score entry to getpid() is always safe,
+ * since when the parent is writing an entry, it's only noting SERVER_DEAD
+ * anyway.
+ */
+
+ap_inline void ap_sync_scoreboard_image(void)
+{
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd, 0L, 0);
+ force_read(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image));
+#endif
+}
+
+#endif /* MULTITHREAD */
+
+API_EXPORT(int) ap_exists_scoreboard_image(void)
+{
+ return (ap_scoreboard_image ? 1 : 0);
+}
+
+static ap_inline void put_scoreboard_info(int child_num,
+ short_score *new_score_rec)
+{
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd, (long) child_num * sizeof(short_score), 0);
+ force_write(scoreboard_fd, new_score_rec, sizeof(short_score));
+#endif
+}
+
+/* a clean exit from the parent with proper cleanup */
+#ifdef NETWARE
+void clean_shutdown_on_exit(void)
+{
+ if (!ap_main_finished) {
+ AMCSocketCleanup();
+ ap_destroy_pool(pcommands);
+ free(ap_loaded_modules);
+ ap_cleanup_method_ptrs();
+ ap_destroy_pool(pglobal);
+ ap_cleanup_alloc();
+ ap_main_finished = TRUE;
+ }
+}
+
+void clean_parent_exit(int code) __attribute__((noreturn));
+void clean_parent_exit(int code)
+#else
+static void clean_parent_exit(int code) __attribute__((noreturn));
+static void clean_parent_exit(int code)
+#endif
+{
+#ifdef NETWARE
+ AMCSocketCleanup();
+ ap_destroy_pool(pcommands);
+ free(ap_loaded_modules);
+ ap_cleanup_method_ptrs();
+ ap_destroy_pool(pglobal);
+ ap_cleanup_alloc();
+ ap_main_finished = TRUE;
+#else
+ /* Clear the pool - including any registered cleanups */
+ ap_destroy_pool(pglobal);
+#endif
+ exit(code);
+}
+
+API_EXPORT(int) ap_update_child_status(int child_num, int status, request_rec *r)
+{
+ int old_status;
+ short_score *ss;
+
+ if (child_num < 0)
+ return -1;
+
+ ap_check_signals();
+
+ ap_sync_scoreboard_image();
+ ss = &ap_scoreboard_image->servers[child_num];
+ old_status = ss->status;
+ ss->status = status;
+#ifdef NETWARE
+ ap_scoreboard_image->parent[child_num].pid = GetThreadID();
+#endif
+
+#ifdef OPTIMIZE_TIMEOUTS
+ ++ss->cur_vtime;
+#endif
+
+ if (ap_extended_status) {
+#ifndef OPTIMIZE_TIMEOUTS
+ ss->last_used = time(NULL);
+#endif
+ 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;
+ }
+ else if (status == SERVER_STARTING) {
+ /* clean out the start_time so that mod_status will print Req=0 */
+ /* Use memset to be independent from the type (struct timeval vs. clock_t) */
+ memset (&ss->start_time, '\0', sizeof ss->start_time);
+ }
+ 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;
+ }
+ }
+ if (status == SERVER_DEAD) {
+ ap_scoreboard_image->parent[child_num].pid = 0;
+ }
+ else if (status == SERVER_STARTING && r == NULL) {
+ /* clean up the slot's vhostrec pointer (maybe re-used)
+ * and mark the slot as belonging to a new generation.
+ */
+ ss->vhostrec = NULL;
+ ap_scoreboard_image->parent[child_num].generation = ap_my_generation;
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[child_num]), 0);
+ force_write(scoreboard_fd, &ap_scoreboard_image->parent[child_num],
+ sizeof(parent_score));
+#endif
+ }
+ put_scoreboard_info(child_num, ss);
+
+ return old_status;
+}
+
+static void update_scoreboard_global(void)
+{
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd,
+ (char *) &ap_scoreboard_image->global -(char *) ap_scoreboard_image, 0);
+ force_write(scoreboard_fd, &ap_scoreboard_image->global,
+ sizeof ap_scoreboard_image->global);
+#endif
+}
+
+void ap_time_process_request(int child_num, int status)
+{
+ short_score *ss;
+#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES)
+ struct tms tms_blk;
+#endif
+
+ if (child_num < 0)
+ return;
+
+ ap_sync_scoreboard_image();
+ ss = &ap_scoreboard_image->servers[child_num];
+
+ if (status == START_PREQUEST) {
+#if defined(NO_GETTIMEOFDAY)
+#ifndef NO_TIMES
+ if ((ss->start_time = times(&tms_blk)) == -1)
+#endif /* NO_TIMES */
+ ss->start_time = (clock_t) 0;
+#else
+ if (gettimeofday(&ss->start_time, (struct timezone *) 0) < 0)
+ ss->start_time.tv_sec =
+ ss->start_time.tv_usec = 0L;
+#endif
+ }
+ else if (status == STOP_PREQUEST) {
+#if defined(NO_GETTIMEOFDAY)
+#ifndef NO_TIMES
+ if ((ss->stop_time = times(&tms_blk)) == -1)
+#endif
+ ss->stop_time = ss->start_time = (clock_t) 0;
+#else
+ if (gettimeofday(&ss->stop_time, (struct timezone *) 0) < 0)
+ ss->stop_time.tv_sec =
+ ss->stop_time.tv_usec =
+ ss->start_time.tv_sec =
+ ss->start_time.tv_usec = 0L;
+#endif
+
+ }
+
+ put_scoreboard_info(child_num, ss);
+}
+
+static void increment_counts(int child_num, request_rec *r)
+{
+ long int bs = 0;
+ short_score *ss;
+
+ ap_sync_scoreboard_image();
+ ss = &ap_scoreboard_image->servers[child_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, ss);
+}
+
+static int find_child_by_pid(int pid)
+{
+ int i;
+
+ for (i = 0; i < max_daemons_limit; ++i)
+ if (ap_scoreboard_image->parent[i].pid == pid)
+ return i;
+
+ return -1;
+}
+
+static void reclaim_child_processes(int terminate)
+{
+#ifndef MULTITHREAD
+ int i, status;
+ long int waittime = 1024 * 16; /* in usecs */
+ struct timeval tv;
+ int waitret, tries;
+ int not_dead_yet;
+ int ret;
+#ifndef NO_OTHER_CHILD
+ other_child_rec *ocr, *nocr;
+#endif
+
+ ap_sync_scoreboard_image();
+
+ for (tries = terminate ? 4 : 1; tries <= 12; ++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. NOTE: if we get
+ * interupted, we'll wait longer than expected...
+ */
+ tv.tv_sec = waittime / 1000000;
+ tv.tv_usec = waittime % 1000000;
+ waittime = waittime * 4;
+ do {
+ ret = ap_select(0, NULL, NULL, NULL, &tv);
+ } while (ret == -1 && errno == EINTR);
+
+ /* 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;
+
+ if (!in_pid_table(pid)) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "Bad pid (%d) in scoreboard slot %d", pid, i);
+ continue;
+ }
+ waitret = waitpid(pid, &status, WNOHANG);
+ if (waitret == pid || waitret == -1) {
+ ap_scoreboard_image->parent[i].pid = 0;
+ unset_pid_table(pid);
+ continue;
+ }
+ ++not_dead_yet;
+ switch (tries) {
+ case 1: /* 16ms */
+ case 2: /* 82ms */
+ break;
+ case 3: /* 344ms */
+ /* perhaps it missed the SIGHUP, lets try again */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING,
+ server_conf,
+ "child process %d did not exit, sending another SIGHUP",
+ pid);
+ kill(pid, SIGHUP);
+ waittime = 1024 * 16;
+ break;
+ case 4: /* 16ms */
+ case 5: /* 82ms */
+ case 6: /* 344ms */
+ break;
+ 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);
+ waittime = 1024 * 16; /* give them some time to die */
+ break;
+ case 9: /* 6 sec */
+ case 10: /* 6.1 sec */
+ case 11: /* 6.4 sec */
+ break;
+ case 12: /* 7.4 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 TPF
+ AP_OS_RECLAIM_LOOP_ADJUSTMENTS
+#endif
+#ifndef NO_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_RESTART, ocr->data, (ap_wait_t)status);
+ }
+ else if (waitret == 0) {
+ (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, (ap_wait_t)-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, (ap_wait_t)-1);
+ }
+ }
+#endif
+ if (!not_dead_yet) {
+ /* nothing left to wait for */
+ break;
+ }
+ }
+#endif /* ndef MULTITHREAD */
+}
+
+
+#if defined(NEED_WAITPID)
+/*
+ Systems without a real waitpid sometimes lose a child's exit while waiting
+ for another. Search through the scoreboard for missing children.
+ */
+int reap_children(ap_wait_t *status)
+{
+ int n, pid;
+
+ for (n = 0; n < max_daemons_limit; ++n) {
+ ap_sync_scoreboard_image();
+ pid = ap_scoreboard_image->parent[n].pid;
+ if (ap_scoreboard_image->servers[n].status != SERVER_DEAD) {
+ if (in_pid_table(pid)) {
+ if (kill(pid, 0) == -1) {
+ ap_update_child_status(n, SERVER_DEAD, NULL);
+ /* just mark it as having a successful exit status */
+ bzero((char *) status, sizeof(ap_wait_t));
+ unset_pid_table(pid); /* to be safe */
+ return(pid);
+ }
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "Bad pid (%d) in scoreboard slot %d", pid, n);
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+/* Finally, this routine is used by the caretaker process to wait for
+ * a while...
+ */
+
+#ifndef NETWARE
+/* 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)
+{
+#ifdef WIN32
+#define MAXWAITOBJ MAXIMUM_WAIT_OBJECTS
+ HANDLE h[MAXWAITOBJ];
+ int e[MAXWAITOBJ];
+ int round, pi, hi, rv, err, pid;
+ for (round = 0; round <= (HARD_SERVER_LIMIT - 1) / MAXWAITOBJ + 1; round++) {
+ hi = 0;
+ for (pi = round * MAXWAITOBJ;
+ (pi < (round + 1) * MAXWAITOBJ) && (pi < HARD_SERVER_LIMIT);
+ pi++) {
+ if (ap_scoreboard_image->servers[pi].status != SERVER_DEAD) {
+ e[hi] = pi;
+ pid = ap_scoreboard_image->parent[pi].pid;
+ if (in_pid_table(pid))
+ h[hi++] = (HANDLE) pid;
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "Bad pid (%d) in scoreboard slot %d", pid, pi);
+ }
+ }
+
+ }
+ if (hi > 0) {
+ rv = WaitForMultipleObjects(hi, h, FALSE, 10000);
+ if (rv == -1)
+ err = GetLastError();
+ if ((WAIT_OBJECT_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_OBJECT_0 + hi)))
+ return (ap_scoreboard_image->parent[e[rv - WAIT_OBJECT_0]].pid);
+ else if ((WAIT_ABANDONED_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_ABANDONED_0 + hi)))
+ return (ap_scoreboard_image->parent[e[rv - WAIT_ABANDONED_0]].pid);
+
+ }
+ }
+ return (-1);
+#else /* WIN32 */
+ struct timeval tv;
+ int ret;
+
+ ++wait_or_timeout_counter;
+ if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
+ wait_or_timeout_counter = 0;
+#ifndef NO_OTHER_CHILD
+ probe_writable_fds();
+#endif
+ }
+ ret = waitpid(-1, status, WNOHANG);
+ if (ret == -1 && errno == EINTR) {
+ return -1;
+ }
+ if (ret > 0) {
+ return ret;
+ }
+#ifdef NEED_WAITPID
+ if ((ret = reap_children(status)) > 0) {
+ return ret;
+ }
+#endif
+ tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000;
+ tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000;
+ ap_select(0, NULL, NULL, NULL, &tv);
+ return -1;
+#endif /* WIN32 */
+}
+#endif
+
+#if defined(NSIG)
+#define NumSIG NSIG
+#elif defined(_NSIG)
+#define NumSIG _NSIG
+#elif defined(__NSIG)
+#define NumSIG __NSIG
+#else
+#define NumSIG 32 /* for 1998's unixes, this is still a good assumption */
+#endif
+
+#ifdef SYS_SIGLIST /* platform has sys_siglist[] */
+#define INIT_SIGLIST() /*nothing*/
+#else /* platform has no sys_siglist[], define our own */
+#define SYS_SIGLIST ap_sys_siglist
+#define INIT_SIGLIST() siglist_init();
+
+const char *ap_sys_siglist[NumSIG];
+
+static void siglist_init(void)
+{
+ int sig;
+
+ ap_sys_siglist[0] = "Signal 0";
+#ifdef SIGHUP
+ ap_sys_siglist[SIGHUP] = "Hangup";
+#endif
+#ifdef SIGINT
+ ap_sys_siglist[SIGINT] = "Interrupt";
+#endif
+#ifdef SIGQUIT
+ ap_sys_siglist[SIGQUIT] = "Quit";
+#endif
+#ifdef SIGILL
+ ap_sys_siglist[SIGILL] = "Illegal instruction";
+#endif
+#ifdef SIGTRAP
+ ap_sys_siglist[SIGTRAP] = "Trace/BPT trap";
+#endif
+#ifdef SIGIOT
+ ap_sys_siglist[SIGIOT] = "IOT instruction";
+#endif
+#ifdef SIGABRT
+ ap_sys_siglist[SIGABRT] = "Abort";
+#endif
+#ifdef SIGEMT
+ ap_sys_siglist[SIGEMT] = "Emulator trap";
+#endif
+#ifdef SIGFPE
+ ap_sys_siglist[SIGFPE] = "Arithmetic exception";
+#endif
+#ifdef SIGKILL
+ ap_sys_siglist[SIGKILL] = "Killed";
+#endif
+#ifdef SIGBUS
+ ap_sys_siglist[SIGBUS] = "Bus error";
+#endif
+#ifdef SIGSEGV
+ ap_sys_siglist[SIGSEGV] = "Segmentation fault";
+#endif
+#ifdef SIGSYS
+ ap_sys_siglist[SIGSYS] = "Bad system call";
+#endif
+#ifdef SIGPIPE
+ ap_sys_siglist[SIGPIPE] = "Broken pipe";
+#endif
+#ifdef SIGALRM
+ ap_sys_siglist[SIGALRM] = "Alarm clock";
+#endif
+#ifdef SIGTERM
+ ap_sys_siglist[SIGTERM] = "Terminated";
+#endif
+#ifdef SIGUSR1
+ ap_sys_siglist[SIGUSR1] = "User defined signal 1";
+#endif
+#ifdef SIGUSR2
+ ap_sys_siglist[SIGUSR2] = "User defined signal 2";
+#endif
+#ifdef SIGCLD
+ ap_sys_siglist[SIGCLD] = "Child status change";
+#endif
+#ifdef SIGCHLD
+ ap_sys_siglist[SIGCHLD] = "Child status change";
+#endif
+#ifdef SIGPWR
+ ap_sys_siglist[SIGPWR] = "Power-fail restart";
+#endif
+#ifdef SIGWINCH
+ ap_sys_siglist[SIGWINCH] = "Window changed";
+#endif
+#ifdef SIGURG
+ ap_sys_siglist[SIGURG] = "urgent socket condition";
+#endif
+#ifdef SIGPOLL
+ ap_sys_siglist[SIGPOLL] = "Pollable event occurred";
+#endif
+#ifdef SIGIO
+ ap_sys_siglist[SIGIO] = "socket I/O possible";
+#endif
+#ifdef SIGSTOP
+ ap_sys_siglist[SIGSTOP] = "Stopped (signal)";
+#endif
+#ifdef SIGTSTP
+ ap_sys_siglist[SIGTSTP] = "Stopped";
+#endif
+#ifdef SIGCONT
+ ap_sys_siglist[SIGCONT] = "Continued";
+#endif
+#ifdef SIGTTIN
+ ap_sys_siglist[SIGTTIN] = "Stopped (tty input)";
+#endif
+#ifdef SIGTTOU
+ ap_sys_siglist[SIGTTOU] = "Stopped (tty output)";
+#endif
+#ifdef SIGVTALRM
+ ap_sys_siglist[SIGVTALRM] = "virtual timer expired";
+#endif
+#ifdef SIGPROF
+ ap_sys_siglist[SIGPROF] = "profiling timer expired";
+#endif
+#ifdef SIGXCPU
+ ap_sys_siglist[SIGXCPU] = "exceeded cpu limit";
+#endif
+#ifdef SIGXFSZ
+ ap_sys_siglist[SIGXFSZ] = "exceeded file size limit";
+#endif
+ for (sig=0; sig < sizeof(ap_sys_siglist)/sizeof(ap_sys_siglist[0]); ++sig)
+ if (ap_sys_siglist[sig] == NULL)
+ ap_sys_siglist[sig] = "";
+}
+#endif /* platform has sys_siglist[] */
+
+#ifdef AP_ENABLE_EXCEPTION_HOOK
+typedef struct except_hook_t {
+ struct except_hook_t *next;
+ void (*fn)(ap_exception_info_t *);
+} except_hook_t;
+
+static except_hook_t *except_hooks;
+
+static void except_hook_cleanup(void *ignored)
+{
+ except_hooks = NULL;
+}
+
+API_EXPORT(int) ap_add_fatal_exception_hook(void (*fn)(ap_exception_info_t *))
+{
+ except_hook_t *new;
+
+ ap_assert(pconf);
+
+ if (!ap_exception_hook_enabled) {
+ return 1;
+ }
+
+ new = ap_palloc(pconf, sizeof(except_hook_t));
+ new->next = except_hooks;
+ new->fn = fn;
+ except_hooks = new;
+
+ return 0;
+}
+
+static void run_fatal_exception_hook(int sig)
+{
+ except_hook_t *cur_hook = except_hooks;
+ ap_exception_info_t ei = {0};
+
+ if (ap_exception_hook_enabled &&
+ geteuid() != 0) {
+ ei.sig = sig;
+ ei.pid = getpid();
+
+ while (cur_hook) {
+ cur_hook->fn(&ei);
+ cur_hook = cur_hook->next;
+ }
+ }
+}
+#endif /* AP_ENABLE_EXCEPTION_HOOK */
+
+/* handle all varieties of core dumping signals */
+static void sig_coredump(int sig)
+{
+ chdir(ap_coredump_dir);
+ signal(sig, SIG_DFL);
+#ifdef AP_ENABLE_EXCEPTION_HOOK
+ run_fatal_exception_hook(sig);
+#endif
+#if !defined(WIN32) && !defined(NETWARE)
+ kill(getpid(), sig);
+#else
+ raise(sig);
+#endif
+ /* 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.
+ */
+}
+
+/*****************************************************************
+ * Connection structures and accounting...
+ */
+
+static void just_die(int sig)
+{ /* SIGHUP to child process??? */
+ /* if alarms are blocked we have to wait to die otherwise we might
+ * end up with corruption in alloc.c's internal structures */
+#ifdef NETWARE
+ get_tsd
+#endif
+ if (alarms_blocked) {
+ exit_after_unblock = 1;
+ }
+ else {
+ clean_child_exit(0);
+ }
+}
+
+static int volatile usr1_just_die = 1;
+static int volatile deferred_die;
+
+static void usr1_handler(int sig)
+{
+ if (usr1_just_die) {
+ just_die(sig);
+ }
+ deferred_die = 1;
+}
+
+/* volatile just in case */
+static int volatile shutdown_pending;
+static int volatile restart_pending;
+static int volatile is_graceful;
+API_VAR_EXPORT ap_generation_t volatile ap_my_generation=0;
+
+#ifdef WIN32
+/*
+ * Signalling Apache on NT.
+ *
+ * Under Unix, Apache can be told to shutdown or restart by sending various
+ * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so
+ * we use "events" instead. The parent apache process goes into a loop
+ * where it waits forever for a set of events. Two of those events are
+ * called
+ *
+ * apPID_shutdown
+ * apPID_restart
+ *
+ * (where PID is the PID of the apache parent process). When one of these
+ * is signalled, the Apache parent performs the appropriate action. The events
+ * can become signalled through internal Apache methods (e.g. if the child
+ * finds a fatal error and needs to kill its parent), via the service
+ * control manager (the control thread will signal the shutdown event when
+ * requested to stop the Apache service), from the -k Apache command line,
+ * or from any external program which finds the Apache PID from the
+ * httpd.pid file.
+ *
+ * The signal_parent() function, below, is used to signal one of these events.
+ * It can be called by any child or parent process, since it does not
+ * rely on global variables.
+ *
+ * On entry, type gives the event to signal. 0 means shutdown, 1 means
+ * graceful restart.
+ */
+
+static void signal_parent(int type)
+{
+ HANDLE e;
+ char *signal_name;
+ extern char signal_shutdown_name[];
+ extern char signal_restart_name[];
+
+ /* after updating the shutdown_pending or restart flags, we need
+ * to wake up the parent process so it can see the changes. The
+ * parent will normally be waiting for either a child process
+ * to die, or for a signal on the "spache-signal" event. So set the
+ * "apache-signal" event here.
+ */
+
+ /* XXX: This is no good, can't we please die in -X mode :-? */
+ if (one_process) {
+ return;
+ }
+
+ switch(type) {
+ case 0: signal_name = signal_shutdown_name; break;
+ case 1: signal_name = signal_restart_name; break;
+ default: return;
+ }
+
+ APD2("signal_parent signalling event \"%s\"", signal_name);
+
+ e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name);
+ if (!e) {
+ /* Um, problem, can't signal the parent, which means we can't
+ * signal ourselves to die. Ignore for now...
+ */
+ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+ "OpenEvent on %s event", signal_name);
+ return;
+ }
+ if (SetEvent(e) == 0) {
+ /* Same problem as above */
+ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+ "SetEvent on %s event", signal_name);
+ CloseHandle(e);
+ return;
+ }
+ CloseHandle(e);
+}
+#endif
+
+/*
+ * 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.
+ */
+
+API_EXPORT(void) ap_start_shutdown(void)
+{
+#ifndef WIN32
+ 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;
+#else
+ signal_parent(0); /* get the parent process to wake up */
+#endif
+}
+
+/* do a graceful restart if graceful == 1 */
+API_EXPORT(void) ap_start_restart(int graceful)
+{
+#ifndef WIN32
+ if (restart_pending == 1) {
+ /* Probably not an error - don't bother reporting it */
+ return;
+ }
+ restart_pending = 1;
+ is_graceful = graceful;
+#else
+ signal_parent(1); /* get the parent process to wake up */
+#endif /* WIN32 */
+}
+
+static void sig_term(int sig)
+{
+ ap_start_shutdown();
+}
+
+static void restart(int sig)
+{
+#ifdef TPF
+ signal(sig, restart);
+#endif
+#if !defined (WIN32) && !defined(NETWARE)
+ ap_start_restart(sig == SIGUSR1);
+#else
+ ap_start_restart(1);
+#endif
+}
+
+static void set_signals(void)
+{
+#ifndef NO_USE_SIGACTION
+ struct sigaction sa;
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ if (!one_process) {
+ sa.sa_handler = sig_coredump;
+#if defined(SA_ONESHOT)
+ sa.sa_flags = SA_ONESHOT;
+#elif defined(SA_RESETHAND)
+ sa.sa_flags = SA_RESETHAND;
+#endif
+ if (sigaction(SIGSEGV, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGSEGV)");
+#ifdef SIGBUS
+ if (sigaction(SIGBUS, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGBUS)");
+#endif
+#ifdef SIGABORT
+ if (sigaction(SIGABORT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABORT)");
+#endif
+#ifdef SIGABRT
+ if (sigaction(SIGABRT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABRT)");
+#endif
+#ifdef SIGILL
+ if (sigaction(SIGILL, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGILL)");
+#endif
+ 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)");
+#ifdef SIGINT
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGINT)");
+#endif
+#ifdef SIGXCPU
+ sa.sa_handler = SIG_DFL;
+ if (sigaction(SIGXCPU, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXCPU)");
+#endif
+#ifdef SIGXFSZ
+ sa.sa_handler = SIG_DFL;
+ if (sigaction(SIGXFSZ, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXFSZ)");
+#endif
+#ifdef SIGPIPE
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGPIPE)");
+#endif
+
+ /* we want to ignore HUPs and USR1 while we're busy processing one */
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sigaddset(&sa.sa_mask, SIGUSR1);
+ sa.sa_handler = restart;
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGHUP)");
+ if (sigaction(SIGUSR1, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGUSR1)");
+#else
+ if (!one_process) {
+ signal(SIGSEGV, sig_coredump);
+#ifdef SIGBUS
+ signal(SIGBUS, sig_coredump);
+#endif /* SIGBUS */
+#ifdef SIGABORT
+ signal(SIGABORT, sig_coredump);
+#endif /* SIGABORT */
+#ifdef SIGABRT
+ signal(SIGABRT, sig_coredump);
+#endif /* SIGABRT */
+#ifdef SIGILL
+ signal(SIGILL, sig_coredump);
+#endif /* SIGILL */
+#ifdef SIGXCPU
+ signal(SIGXCPU, SIG_DFL);
+#endif /* SIGXCPU */
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, SIG_DFL);
+#endif /* SIGXFSZ */
+ }
+#ifndef NETWARE
+ signal(SIGTERM, sig_term);
+#endif
+#ifdef SIGHUP
+ signal(SIGHUP, restart);
+#endif /* SIGHUP */
+#ifdef SIGUSR1
+ signal(SIGUSR1, restart);
+#endif /* SIGUSR1 */
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+
+#endif
+}
+
+
+/*****************************************************************
+ * Here follows a long bunch of generic server bookkeeping stuff...
+ */
+
+static void detach(void)
+{
+#if !defined(WIN32) && !defined(NETWARE)
+ int x;
+
+ chdir("/");
+#if !defined(MPE) && !defined(OS2) && !defined(TPF) && !defined(BEOS) && \
+ !defined(BONE)
+/* Don't detach for MPE because child processes can't survive the death of
+ the parent. */
+ if (do_detach) {
+ if ((x = fork()) > 0)
+ exit(0);
+ else if (x == -1) {
+ perror("fork");
+ fprintf(stderr, "%s: unable to fork new process\n", ap_server_argv0);
+ exit(1);
+ }
+ RAISE_SIGSTOP(DETACH);
+ }
+#endif
+#ifndef NO_SETSID
+ if ((pgrp = setsid()) == -1) {
+ perror("setsid");
+ fprintf(stderr, "%s: setsid failed\n", ap_server_argv0);
+ if (!do_detach)
+ fprintf(stderr, "setsid() failed probably because you aren't "
+ "running under a process management tool like daemontools\n");
+ exit(1);
+ }
+#elif defined(NEXT) || defined(NEWSOS)
+ if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) {
+ perror("setpgrp");
+ fprintf(stderr, "%s: setpgrp or getpgrp failed\n", ap_server_argv0);
+ exit(1);
+ }
+#elif defined(OS2) || defined(TPF)
+ /* OS/2 and TPF don't support process group IDs */
+ pgrp = getpid();
+#elif defined(MPE)
+ /* MPE uses negative pid for process group */
+ pgrp = -getpid();
+#elif defined(CYGWIN)
+ /* Cygwin does not take any argument for setpgrp() */
+ if ((pgrp = setpgrp()) == -1) {
+ perror("setpgrp");
+ fprintf(stderr, "%s: setpgrp failed\n", ap_server_argv0);
+ exit(1);
+ }
+#else
+ if ((pgrp = setpgrp(getpid(), 0)) == -1) {
+ perror("setpgrp");
+ fprintf(stderr, "%s: setpgrp failed\n", ap_server_argv0);
+ exit(1);
+ }
+#endif
+
+ /* close out the standard file descriptors */
+ if (freopen("/dev/null", "r", stdin) == NULL) {
+ fprintf(stderr, "%s: unable to replace stdin with /dev/null: %s\n",
+ ap_server_argv0, strerror(errno));
+ /* continue anyhow -- note we can't close out descriptor 0 because we
+ * have nothing to replace it with, and if we didn't have a descriptor
+ * 0 the next file would be created with that value ... leading to
+ * havoc.
+ */
+ }
+ if (freopen("/dev/null", "w", stdout) == NULL) {
+ fprintf(stderr, "%s: unable to replace stdout with /dev/null: %s\n",
+ ap_server_argv0, strerror(errno));
+ }
+ /* stderr is a tricky one, we really want it to be the error_log,
+ * but we haven't opened that yet. So leave it alone for now and it'll
+ * be reopened moments later.
+ */
+#endif /* ndef WIN32 */
+}
+
+/* Set group privileges.
+ *
+ * Note that we use the username as set in the config files, rather than
+ * the lookup of to uid --- the same uid may have multiple passwd entries,
+ * with different sets of groups for each.
+ */
+
+static void set_group_privs(void)
+{
+#if !defined(WIN32) && !defined(NETWARE) && !defined(BEOS) && !defined(BONE)
+ if (!geteuid()) {
+ char *name;
+
+ /* Get username if passed as a uid */
+
+ if (ap_user_name[0] == '#') {
+ struct passwd *ent;
+ uid_t uid = atoi(&ap_user_name[1]);
+
+ if ((ent = getpwuid(uid)) == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "getpwuid: couldn't determine user name from uid %u, "
+ "you probably need to modify the User directive",
+ (unsigned)uid);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ name = ent->pw_name;
+ }
+ else
+ name = ap_user_name;
+
+#if !defined(OS2) && !defined(TPF)
+ /* OS/2 and TPF don't support groups. */
+
+ /*
+ * Set the GID before initgroups(), since on some platforms
+ * setgid() is known to zap the group list.
+ */
+#ifdef MPE
+ GETPRIVMODE();
+#endif
+ if (setgid(ap_group_id) == -1) {
+#ifdef MPE
+ GETUSERMODE();
+#endif
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setgid: unable to set group id to Group %u",
+ (unsigned)ap_group_id);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+#ifdef MPE
+ GETUSERMODE();
+#endif
+
+ /* Reset `groups' attributes. */
+
+ if (initgroups(name, ap_group_id) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "initgroups: unable to set groups for User %s "
+ "and Group %u", name, (unsigned)ap_group_id);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+#ifdef MULTIPLE_GROUPS
+ if (getgroups(NGROUPS_MAX, group_id_list) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "getgroups: unable to get group list");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+#endif /* MULTIPLE_GROUPS */
+#endif /* !defined(OS2) && !defined(TPF) */
+ }
+#endif /* !defined(WIN32) && !defined(NETWARE) && !defined(BEOS) */
+}
+
+/* check to see if we have the 'suexec' setuid wrapper installed */
+static int init_suexec(void)
+{
+ int result = 0;
+
+#if !defined(WIN32) && !defined(NETWARE) && !defined(TPF)
+ struct stat wrapper;
+
+ if ((stat(SUEXEC_BIN, &wrapper)) != 0) {
+ result = 0;
+ }
+ else if ((wrapper.st_mode & S_ISUID) && (wrapper.st_uid == 0)) {
+ result = 1;
+ }
+#endif /* ndef WIN32 */
+ return result;
+}
+
+/*****************************************************************
+ * Connection structures and accounting...
+ */
+
+
+static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout,
+ const struct sockaddr_in *remaddr,
+ const struct sockaddr_in *saddr,
+ int child_num)
+{
+ conn_rec *conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec));
+
+ /* Got a connection structure, so initialize what fields we can
+ * (the rest are zeroed out by pcalloc).
+ */
+
+ conn->child_num = child_num;
+
+ conn->pool = p;
+ conn->local_addr = *saddr;
+ conn->local_ip = ap_pstrdup(conn->pool,
+ inet_ntoa(conn->local_addr.sin_addr));
+ conn->server = server; /* just a guess for now */
+ ap_update_vhost_given_ip(conn);
+ conn->base_server = conn->server;
+ conn->client = inout;
+
+ conn->remote_addr = *remaddr;
+ conn->remote_ip = ap_pstrdup(conn->pool,
+ inet_ntoa(conn->remote_addr.sin_addr));
+
+ return conn;
+}
+
+#if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF)
+static void sock_disable_nagle(int s, struct sockaddr_in *sin_client)
+{
+ /* The Nagle algorithm says that we should delay sending partial
+ * packets in hopes of getting more data. We don't want to do
+ * this; we are not telnet. There are bad interactions between
+ * persistent connections and Nagle's algorithm that have very severe
+ * performance penalties. (Failing to disable Nagle is not much of a
+ * problem with simple HTTP.)
+ *
+ * In spite of these problems, failure here is not a shooting offense.
+ */
+ int just_say_no = 1;
+
+ if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no,
+ sizeof(int)) < 0) {
+#ifdef NETWARE
+ errno = WSAGetLastError();
+#endif
+ if (sin_client) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, server_conf,
+ "setsockopt: (TCP_NODELAY), client %pA probably "
+ "dropped the connection", &sin_client->sin_addr);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, server_conf,
+ "setsockopt: (TCP_NODELAY)");
+ }
+ }
+}
+
+#else
+#define sock_disable_nagle(s, c) /* NOOP */
+#endif
+
+static int make_sock(pool *p, const struct sockaddr_in *server)
+{
+ int s;
+ int one = 1;
+ char addr[512];
+
+ if (server->sin_addr.s_addr != htonl(INADDR_ANY))
+ ap_snprintf(addr, sizeof(addr), "address %s port %d",
+ inet_ntoa(server->sin_addr), ntohs(server->sin_port));
+ else
+ ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port));
+
+ /* note that because we're about to slack we don't use psocket */
+ ap_block_alarms();
+ if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+ "make_sock: failed to get a socket for %s", addr);
+
+ ap_unblock_alarms();
+ exit(1);
+ }
+
+ /* Solaris (probably versions 2.4, 2.5, and 2.5.1 with various levels
+ * of tcp patches) has some really weird bugs where if you dup the
+ * socket now it breaks things across SIGHUP restarts. It'll either
+ * be unable to bind, or it won't respond.
+ */
+#if defined (SOLARIS2) && SOLARIS2 < 260
+#define WORKAROUND_SOLARIS_BUG
+#endif
+
+ /* PR#1282 Unixware 1.x appears to have the same problem as solaris */
+#if defined (UW) && UW < 200
+#define WORKAROUND_SOLARIS_BUG
+#endif
+
+ /* PR#1973 NCR SVR4 systems appear to have the same problem */
+#if defined (MPRAS)
+#define WORKAROUND_SOLARIS_BUG
+#endif
+
+#ifndef WORKAROUND_SOLARIS_BUG
+#ifndef BEOS /* this won't work for BeOS sockets!! */
+ s = ap_slack(s, AP_SLACK_HIGH);
+#endif
+
+ ap_note_cleanups_for_socket_ex(p, s, 1); /* arrange to close on exec or restart */
+#ifdef TPF
+ os_note_additional_cleanups(p, s);
+#endif /* TPF */
+#endif
+
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) {
+#ifndef _OSD_POSIX
+ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+ "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr);
+ closesocket(s);
+ ap_unblock_alarms();
+ exit(1);
+#endif /*_OSD_POSIX*/
+ }
+ one = 1;
+#if defined(SO_KEEPALIVE) && !defined(MPE)
+ if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+ "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr);
+ closesocket(s);
+
+ ap_unblock_alarms();
+ exit(1);
+ }
+#endif
+
+ sock_disable_nagle(s, NULL);
+ sock_enable_linger(s);
+
+ /*
+ * To send data over high bandwidth-delay connections at full
+ * speed we must force the TCP window to open wide enough to keep the
+ * pipe full. The default window size on many systems
+ * is only 4kB. Cross-country WAN connections of 100ms
+ * at 1Mb/s are not impossible for well connected sites.
+ * If we assume 100ms cross-country latency,
+ * a 4kB buffer limits throughput to 40kB/s.
+ *
+ * To avoid this problem I've added the SendBufferSize directive
+ * to allow the web master to configure send buffer size.
+ *
+ * The trade-off of larger buffers is that more kernel memory
+ * is consumed. YMMV, know your customers and your network!
+ *
+ * -John Heidemann <johnh@isi.edu> 25-Oct-96
+ *
+ * If no size is specified, use the kernel default.
+ */
+#ifndef BEOS /* BeOS does not support SO_SNDBUF */
+ if (server_conf->send_buffer_size) {
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
+ (char *) &server_conf->send_buffer_size, sizeof(int)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "make_sock: failed to set SendBufferSize for %s, "
+ "using default", addr);
+ /* not a fatal error */
+ }
+ }
+#endif
+
+#ifdef MPE
+/* MPE requires CAP=PM and GETPRIVMODE to bind to ports less than 1024 */
+ if (ntohs(server->sin_port) < 1024)
+ GETPRIVMODE();
+#endif
+
+ if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+ "make_sock: could not bind to %s", addr);
+#ifdef MPE
+ if (ntohs(server->sin_port) < 1024)
+ GETUSERMODE();
+#endif
+
+ closesocket(s);
+ ap_unblock_alarms();
+ exit(1);
+ }
+#ifdef MPE
+ if (ntohs(server->sin_port) < 1024)
+ GETUSERMODE();
+#endif
+
+ if (listen(s, ap_listenbacklog) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "make_sock: unable to listen for connections on %s", addr);
+ closesocket(s);
+ ap_unblock_alarms();
+ exit(1);
+ }
+
+#ifdef SO_ACCEPTFILTER
+ if (ap_acceptfilter) {
+#ifndef ACCEPT_FILTER_NAME
+#define ACCEPT_FILTER_NAME "httpready"
+#ifdef __FreeBSD_version
+#if __FreeBSD_version < 411000 /* httpready broken before 4.1.1 */
+#undef ACCEPT_FILTER_NAME
+#define ACCEPT_FILTER_NAME "dataready"
+#endif
+#endif
+#endif /* ! ACCEPT_FILTER_NAME */
+ /*
+ * See htdocs/manual/misc/perf-bsd44.html for a discussion of
+ * how to enable this feature and various issues with it.
+ */
+ struct accept_filter_arg af = {
+ ACCEPT_FILTER_NAME, ""
+ };
+ if (setsockopt(s, SOL_SOCKET, SO_ACCEPTFILTER, &af, sizeof(af)) < 0) {
+ if (errno == ENOPROTOOPT) {
+ ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, server_conf,
+ "socket option SO_ACCEPTFILTER unkown on this machine. Continuing.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_INFO, server_conf,
+ "make_sock: for %s, setsockopt: (SO_ACCEPTFILTER)", addr);
+ }
+ }
+ }
+#endif
+
+#ifdef WORKAROUND_SOLARIS_BUG
+ s = ap_slack(s, AP_SLACK_HIGH);
+
+ ap_note_cleanups_for_socket_ex(p, s, 1); /* arrange to close on exec or restart */
+#endif
+ ap_unblock_alarms();
+
+#ifdef CHECK_FD_SETSIZE
+ /* protect various fd_sets */
+ if (s >= FD_SETSIZE) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+ "make_sock: problem listening on %s, filedescriptor (%u) "
+ "larger than FD_SETSIZE (%u) "
+ "found, you probably need to rebuild Apache with a "
+ "larger FD_SETSIZE", addr, s, FD_SETSIZE);
+ closesocket(s);
+ exit(1);
+ }
+#endif
+
+ return s;
+}
+
+
+/*
+ * During a restart we keep track of the old listeners here, so that we
+ * can re-use the sockets. We have to do this because we won't be able
+ * to re-open the sockets ("Address already in use").
+ *
+ * Unlike the listeners ring, old_listeners is a NULL terminated list.
+ *
+ * copy_listeners() makes the copy, find_listener() finds an old listener
+ * and close_unused_listener() cleans up whatever wasn't used.
+ */
+static listen_rec *old_listeners;
+
+/* unfortunately copy_listeners may be called before listeners is a ring */
+static void copy_listeners(pool *p)
+{
+ listen_rec *lr;
+
+ ap_assert(old_listeners == NULL);
+ if (ap_listeners == NULL) {
+ return;
+ }
+ lr = ap_listeners;
+ do {
+ listen_rec *nr = malloc(sizeof *nr);
+
+ if (nr == NULL) {
+ fprintf(stderr, "Ouch! malloc failed in copy_listeners()\n");
+ exit(1);
+ }
+ *nr = *lr;
+ ap_kill_cleanups_for_socket(p, nr->fd);
+ nr->next = old_listeners;
+ ap_assert(!nr->used);
+ old_listeners = nr;
+ lr = lr->next;
+ } while (lr && lr != ap_listeners);
+}
+
+
+static int find_listener(listen_rec *lr)
+{
+ listen_rec *or;
+
+ for (or = old_listeners; or; or = or->next) {
+ if (!memcmp(&or->local_addr, &lr->local_addr, sizeof(or->local_addr))) {
+ or->used = 1;
+ return or->fd;
+ }
+ }
+ return -1;
+}
+
+
+static void close_unused_listeners(void)
+{
+ listen_rec *or, *next;
+
+ for (or = old_listeners; or; or = next) {
+ next = or->next;
+ if (!or->used)
+ closesocket(or->fd);
+ free(or);
+ }
+ old_listeners = NULL;
+}
+
+#ifdef NONBLOCK_WHEN_MULTI_LISTEN
+/* retrieved from APR */
+static int soblock(int sd)
+{
+#ifdef NETWARE
+ u_long one = 0;
+
+ if (ioctlsocket(sd, FIONBIO, &one) == SOCKET_ERROR) {
+ return -1;
+ }
+#else
+#ifndef BEOS
+ int fd_flags;
+
+ fd_flags = fcntl(sd, F_GETFL, 0);
+#if defined(O_NONBLOCK)
+ fd_flags &= ~O_NONBLOCK;
+#elif defined(O_NDELAY)
+ fd_flags &= ~O_NDELAY;
+#elif defined(FNDELAY)
+ fd_flags &= ~FNDELAY;
+#else
+#error Teach soblock() how to make a socket blocking on your platform.
+#endif
+ if (fcntl(sd, F_SETFL, fd_flags) == -1) {
+ return errno;
+ }
+#else
+ int on = 0;
+ if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0)
+ return errno;
+#endif /* BEOS */
+#endif /* NETWARE */
+ return 0;
+}
+
+static int sononblock(int sd)
+{
+#ifdef NETWARE
+ u_long one = 1;
+
+ if (ioctlsocket(sd, FIONBIO, &one) == SOCKET_ERROR) {
+ return -1;
+ }
+#else
+#ifndef BEOS
+ int fd_flags;
+
+ fd_flags = fcntl(sd, F_GETFL, 0);
+#if defined(O_NONBLOCK)
+ fd_flags |= O_NONBLOCK;
+#elif defined(O_NDELAY)
+ fd_flags |= O_NDELAY;
+#elif defined(FNDELAY)
+ fd_flags |= FNDELAY;
+#else
+#error Teach sononblock() how to make a socket non-blocking on your platform.
+#endif
+ if (fcntl(sd, F_SETFL, fd_flags) == -1) {
+ return errno;
+ }
+#else
+ int on = 1;
+ if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0)
+ return errno;
+#endif /* BEOS */
+#endif /* NETWARE */
+ return 0;
+}
+#endif /* NONBLOCK_WHEN_MULTI_LISTEN */
+
+/* open sockets, and turn the listeners list into a singly linked ring */
+static void setup_listeners(pool *p)
+{
+ listen_rec *lr;
+ int fd;
+
+ listenmaxfd = -1;
+ FD_ZERO(&listenfds);
+ lr = ap_listeners;
+ for (;;) {
+ fd = find_listener(lr);
+ if (fd < 0) {
+ fd = make_sock(p, &lr->local_addr);
+ }
+ else {
+ ap_note_cleanups_for_socket_ex(p, fd, 1);
+ }
+ /* if we get here, (fd >= 0) && (fd < FD_SETSIZE) */
+ FD_SET(fd, &listenfds);
+ if (fd > listenmaxfd)
+ listenmaxfd = fd;
+ lr->fd = fd;
+ if (lr->next == NULL)
+ break;
+ lr = lr->next;
+ }
+ /* turn the list into a ring */
+ lr->next = ap_listeners;
+ head_listener = ap_listeners;
+ close_unused_listeners();
+
+#ifdef NONBLOCK_WHEN_MULTI_LISTEN
+ if (ap_listeners->next != ap_listeners) {
+ lr = ap_listeners;
+ do {
+ if (sononblock(lr->fd) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, NULL,
+ "A listening socket could not be made non-blocking.");
+ exit(APEXIT_INIT);
+ }
+ lr = lr->next;
+ } while (lr != ap_listeners);
+ }
+ else {
+ /* we could be restarting with a single remaining listening
+ * socket, still in non-blocking state from a previous
+ * generation which had more listening sockets
+ */
+ if (soblock(ap_listeners->fd) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, NULL,
+ "A listening socket could not be made blocking.");
+ exit(APEXIT_INIT);
+ }
+ }
+#endif /* NONBLOCK_WHEN_MULTI_LISTEN */
+
+#ifdef NO_SERIALIZED_ACCEPT
+ /* warn them about the starvation problem if they're using multiple
+ * sockets
+ */
+ if (ap_listeners->next != ap_listeners) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, NULL,
+ "You cannot use multiple Listens safely on your system, "
+ "proceeding anyway. See src/PORTING, search for "
+ "SERIALIZED_ACCEPT.");
+ }
+#endif
+}
+
+
+/*
+ * Find a listener which is ready for accept(). This advances the
+ * head_listener global.
+ */
+static ap_inline listen_rec *find_ready_listener(fd_set * main_fds)
+{
+ listen_rec *lr;
+
+ lr = head_listener;
+ do {
+ if (FD_ISSET(lr->fd, main_fds)) {
+ head_listener = lr->next;
+ return (lr);
+ }
+ lr = lr->next;
+ } while (lr != head_listener);
+ return NULL;
+}
+
+
+#if defined(WIN32) || defined(NETWARE)
+static int s_iInitCount = 0;
+
+static int AMCSocketInitialize(void)
+{
+ int iVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ if (s_iInitCount > 0) {
+ s_iInitCount++;
+ return (0);
+ }
+ else if (s_iInitCount < 0)
+ return (s_iInitCount);
+
+ /* s_iInitCount == 0. Do the initailization */
+ iVersionRequested = MAKEWORD(2, 0);
+ err = WSAStartup((WORD) iVersionRequested, &wsaData);
+ if (err) {
+ printf("WSAStartup failed with error %d\n", err);
+ s_iInitCount = -1;
+ return (s_iInitCount);
+ }
+
+ if (LOBYTE(wsaData.wVersion) != 2 ||
+ HIBYTE(wsaData.wVersion) != 0) {
+ printf("Apache requires Winsock 2. Please see the Apache FAQ for more information.\n");
+ s_iInitCount = -2;
+ WSACleanup();
+ return (s_iInitCount);
+ }
+ s_iInitCount++;
+ return (s_iInitCount);
+}
+
+
+static void AMCSocketCleanup(void)
+{
+ if (--s_iInitCount == 0)
+ WSACleanup();
+ return;
+}
+#endif
+
+static void show_compile_settings(void)
+{
+ printf("Server version: %s\n", ap_get_server_version());
+ printf("Server built: %s\n", ap_get_server_built());
+ printf("Server's Module Magic Number: %u:%u\n",
+ MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);
+ printf("Server compiled with....\n");
+#ifdef TPF
+ show_os_specific_compile_settings();
+#endif
+#ifdef BIG_SECURITY_HOLE
+ printf(" -D BIG_SECURITY_HOLE\n");
+#endif
+#ifdef SECURITY_HOLE_PASS_AUTHORIZATION
+ printf(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n");
+#endif
+#ifdef HAVE_MMAP
+ printf(" -D HAVE_MMAP\n");
+#endif
+#ifdef HAVE_SHMGET
+ printf(" -D HAVE_SHMGET\n");
+#endif
+#ifdef USE_MMAP_SCOREBOARD
+ printf(" -D USE_MMAP_SCOREBOARD\n");
+#endif
+#ifdef USE_SHMGET_SCOREBOARD
+ printf(" -D USE_SHMGET_SCOREBOARD\n");
+#endif
+#ifdef USE_OS2_SCOREBOARD
+ printf(" -D USE_OS2_SCOREBOARD\n");
+#endif
+#ifdef USE_POSIX_SCOREBOARD
+ printf(" -D USE_POSIX_SCOREBOARD\n");
+#endif
+#ifdef USE_MMAP_FILES
+ printf(" -D USE_MMAP_FILES\n");
+#ifdef MMAP_SEGMENT_SIZE
+ printf(" -D MMAP_SEGMENT_SIZE=%ld\n",(long)MMAP_SEGMENT_SIZE);
+#endif
+#endif /*USE_MMAP_FILES*/
+#ifdef NO_WRITEV
+ printf(" -D NO_WRITEV\n");
+#endif
+#ifdef NO_LINGCLOSE
+ printf(" -D NO_LINGCLOSE\n");
+#endif
+#ifdef HAVE_FCNTL_SERIALIZED_ACCEPT
+ printf(" -D HAVE_FCNTL_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef HAVE_FLOCK_SERIALIZED_ACCEPT
+ printf(" -D HAVE_FLOCK_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef HAVE_USLOCK_SERIALIZED_ACCEPT
+ printf(" -D HAVE_USLOCK_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef HAVE_SYSVSEM_SERIALIZED_ACCEPT
+ printf(" -D HAVE_SYSVSEM_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef HAVE_PTHREAD_SERIALIZED_ACCEPT
+ printf(" -D HAVE_PTHREAD_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef HAVE_OS2SEM_SERIALIZED_ACCEPT
+ printf(" -D HAVE_OS2SEM_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef HAVE_TPF_CORE_SERIALIZED_ACCEPT
+ printf(" -D HAVE_TPF_CORE_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef HAVE_BEOS_SERIALIZED_ACCEPT
+ printf(" -D HAVE_BEOS_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef HAVE_NONE_SERIALIZED_ACCEPT
+ printf(" -D HAVE_NONE_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+ printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n");
+#endif
+#ifdef NO_OTHER_CHILD
+ printf(" -D NO_OTHER_CHILD\n");
+#endif
+#ifdef NO_RELIABLE_PIPED_LOGS
+ printf(" -D NO_RELIABLE_PIPED_LOGS\n");
+#endif
+#ifdef BUFFERED_LOGS
+ printf(" -D BUFFERED_LOGS\n");
+#ifdef PIPE_BUF
+ printf(" -D PIPE_BUF=%ld\n",(long)PIPE_BUF);
+#endif
+#endif
+ printf(" -D DYNAMIC_MODULE_LIMIT=%ld\n",(long)DYNAMIC_MODULE_LIMIT);
+ printf(" -D HARD_SERVER_LIMIT=%ld\n",(long)HARD_SERVER_LIMIT);
+#ifdef MULTITHREAD
+ printf(" -D MULTITHREAD\n");
+#endif
+#ifdef CHARSET_EBCDIC
+ printf(" -D CHARSET_EBCDIC\n");
+#endif
+#ifdef NEED_HASHBANG_EMUL
+ printf(" -D NEED_HASHBANG_EMUL\n");
+#endif
+#ifdef SHARED_CORE
+ printf(" -D SHARED_CORE\n");
+#endif
+#ifdef SO_ACCEPTFILTER
+ printf(" -D SO_ACCEPTFILTER\n");
+ printf(" -D ACCEPT_FILTER_NAME=\"" ACCEPT_FILTER_NAME "\"\n");
+#endif
+#ifdef AP_ACCEPTFILTER_OFF
+ printf(" -D AP_ACCEPTFILTER_OFF\n");
+#endif
+#ifdef CYGWIN_WINSOCK
+ printf(" -D CYGWIN_WINSOCK\n");
+#endif
+
+/* This list displays the compiled-in default paths: */
+#ifdef HTTPD_ROOT
+ printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n");
+#endif
+#if defined(SUEXEC_BIN) && !defined(TPF)
+ printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n");
+#endif
+#if defined(SHARED_CORE) && defined(SHARED_CORE_DIR)
+ printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n");
+#endif
+#ifdef DEFAULT_PIDLOG
+ printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n");
+#endif
+#ifdef DEFAULT_SCOREBOARD
+ printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n");
+#endif
+#ifdef DEFAULT_LOCKFILE
+ printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n");
+#endif
+#ifdef DEFAULT_ERRORLOG
+ printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n");
+#endif
+#ifdef TYPES_CONFIG_FILE
+ printf(" -D TYPES_CONFIG_FILE=\"" TYPES_CONFIG_FILE "\"\n");
+#endif
+#ifdef SERVER_CONFIG_FILE
+ printf(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n");
+#endif
+#ifdef ACCESS_CONFIG_FILE
+ printf(" -D ACCESS_CONFIG_FILE=\"" ACCESS_CONFIG_FILE "\"\n");
+#endif
+#ifdef RESOURCE_CONFIG_FILE
+ printf(" -D RESOURCE_CONFIG_FILE=\"" RESOURCE_CONFIG_FILE "\"\n");
+#endif
+}
+
+
+/* Some init code that's common between win32 and unix... well actually
+ * some of it is #ifdef'd but was duplicated before anyhow. This stuff
+ * is still a mess.
+ */
+static void common_init(void)
+{
+ int i;
+ INIT_SIGLIST()
+#ifdef AUX3
+ (void) set42sig();
+#endif
+
+#if defined(WIN32) || defined(NETWARE)
+ /* Initialize the stupid sockets */
+ AMCSocketInitialize();
+#endif /* WIN32 */
+
+ pglobal = ap_init_alloc();
+ pconf = ap_make_sub_pool(pglobal);
+#ifdef AP_ENABLE_EXCEPTION_HOOK
+ ap_register_cleanup(pconf, NULL, except_hook_cleanup, ap_null_cleanup);
+#endif
+ plog = ap_make_sub_pool(pglobal);
+ ptrans = ap_make_sub_pool(pconf);
+
+ ap_util_init();
+ ap_util_uri_init();
+
+ pcommands = ap_make_sub_pool(NULL);
+ ap_server_pre_read_config = ap_make_array(pcommands, 1, sizeof(char *));
+ ap_server_post_read_config = ap_make_array(pcommands, 1, sizeof(char *));
+ ap_server_config_defines = ap_make_array(pcommands, 1, sizeof(char *));
+ /* overkill since static */
+ for (i = 0; i < HARD_SERVER_LIMIT; i++) {
+ pid_table[i] = 0;
+ }
+}
+
+#ifndef MULTITHREAD
+/*****************************************************************
+ * 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 int csd;
+static int dupped_csd;
+static int requests_this_child;
+static fd_set main_fds;
+
+API_EXPORT(void) ap_child_terminate(request_rec *r)
+{
+ r->connection->keepalive = 0;
+ requests_this_child = ap_max_requests_per_child = 1;
+}
+
+static void child_main(int child_num_arg)
+{
+ NET_SIZE_T clen;
+ struct sockaddr sa_server;
+ struct sockaddr sa_client;
+ listen_rec *lr;
+
+ /* All of initialization is a critical section, we don't care if we're
+ * told to HUP or USR1 before we're done initializing. For example,
+ * we could be half way through child_init_modules() when a restart
+ * signal arrives, and we'd have no real way to recover gracefully
+ * and exit properly.
+ *
+ * I suppose a module could take forever to initialize, but that would
+ * be either a broken module, or a broken configuration (i.e. network
+ * problems, file locking problems, whatever). -djg
+ */
+ ap_block_alarms();
+
+ my_pid = getpid();
+ csd = -1;
+ dupped_csd = -1;
+ my_child_num = child_num_arg;
+ requests_this_child = 0;
+
+ /* Get a sub pool for global allocations in this child, so that
+ * we can have cleanups occur when the child exits.
+ */
+ pchild = ap_make_sub_pool(pconf);
+ /* associate accept mutex cleanup with a subpool of pchild so we can
+ * make sure the mutex is released before calling module code at
+ * termination
+ */
+ pmutex = ap_make_sub_pool(pchild);
+
+ /* needs to be done before we switch UIDs so we have permissions */
+ reopen_scoreboard(pchild);
+ SAFE_ACCEPT(accept_mutex_child_init(pmutex));
+
+ set_group_privs();
+#ifdef MPE
+ /* No such thing as root on MPE, so try to switch unconditionally */
+ GETPRIVMODE();
+ if (setuid(ap_user_id) == -1) {
+ GETUSERMODE();
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setuid: unable to change to uid: %d", ap_user_id);
+ exit(1);
+ }
+ GETUSERMODE();
+#else
+ /*
+ * Only try to switch if we're running as root
+ * In case of Cygwin we have the special super-user named SYSTEM
+ */
+#ifdef CYGWIN
+ if (getuid() == SYSTEM_UID && (
+#else
+ if (!geteuid() && (
+#endif
+#ifdef _OSD_POSIX
+ os_init_job_environment(server_conf, ap_user_name, one_process) != 0 ||
+#endif
+ setuid(ap_user_id) == -1)) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setuid: unable to change to uid: %ld", (long) ap_user_id);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+#endif
+
+#ifdef HAVE_SET_DUMPABLE
+ if (ap_coredump_dir_configured) {
+ /* user set CoredumpDirectory, so they want to get core dumps
+ */
+ if (prctl(PR_SET_DUMPABLE, 1)) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, NULL,
+ "set dumpable failed - this child will not coredump"
+ " after software errors");
+ }
+ }
+#endif
+
+ ap_child_init_modules(pchild, server_conf);
+
+ /* done with the initialization critical section */
+ ap_unblock_alarms();
+
+ (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL);
+
+ /*
+ * Setup the jump buffers so that we can return here after a timeout
+ */
+ ap_setjmp(jmpbuffer);
+#ifndef OS2
+#ifdef SIGURG
+ signal(SIGURG, timeout);
+#endif
+#endif
+ signal(SIGALRM, alrm_handler);
+#ifdef TPF
+ signal(SIGHUP, just_die);
+ signal(SIGTERM, just_die);
+ signal(SIGUSR1, just_die);
+#endif /* TPF */
+
+#ifdef OS2
+/* Stop Ctrl-C/Ctrl-Break signals going to child processes */
+ {
+ unsigned long ulTimes;
+ DosSetSignalExceptionFocus(0, &ulTimes);
+ }
+#endif
+
+ while (1) {
+ BUFF *conn_io;
+ request_rec *r;
+
+ /* Prepare to receive a SIGUSR1 due to graceful restart so that
+ * we can exit cleanly. Since we're between connections right
+ * now it's the right time to exit, but we might be blocked in a
+ * system call when the graceful restart request is made. */
+ usr1_just_die = 1;
+ signal(SIGUSR1, usr1_handler);
+
+ /*
+ * (Re)initialize this child to a pre-connection state.
+ */
+
+ ap_kill_timeout(0); /* Cancel any outstanding alarms. */
+ current_conn = NULL;
+
+ ap_clear_pool(ptrans);
+
+ ap_sync_scoreboard_image();
+ if (ap_scoreboard_image->global.running_generation != ap_my_generation) {
+ clean_child_exit(0);
+ }
+
+#ifndef WIN32
+ if ((ap_max_requests_per_child > 0
+ && requests_this_child++ >= ap_max_requests_per_child)) {
+ clean_child_exit(0);
+ }
+#else
+ ++requests_this_child;
+#endif
+
+ (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL);
+
+ /*
+ * Wait for an acceptable connection to arrive.
+ */
+
+ /* Lock around "accept", if necessary */
+ SAFE_ACCEPT(accept_mutex_on());
+
+ for (;;) {
+ if (ap_listeners->next != ap_listeners) {
+ /* more than one socket */
+ memcpy(&main_fds, &listenfds, sizeof(fd_set));
+ srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, NULL);
+
+ if (srv < 0 && 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, server_conf, "select: (listen)");
+ clean_child_exit(1);
+ }
+
+ if (srv <= 0)
+ continue;
+
+ lr = find_ready_listener(&main_fds);
+ if (lr == NULL)
+ continue;
+ sd = lr->fd;
+ }
+ else {
+ /* only one socket, just pretend we did the other stuff */
+ sd = ap_listeners->fd;
+ }
+
+ /* if we accept() something we don't want to die, so we have to
+ * defer the exit
+ */
+ deferred_die = 0;
+ usr1_just_die = 0;
+ for (;;) {
+ clen = sizeof(sa_client);
+ csd = ap_accept(sd, &sa_client, &clen);
+ if (csd >= 0 || errno != EINTR)
+ break;
+ if (deferred_die) {
+ /* we didn't get a socket, and we were told to die */
+ clean_child_exit(0);
+ }
+ }
+
+ if (csd >= 0)
+ 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 (errno) {
+
+#if defined(HPUX11) && defined(ENOBUFS)
+ /* On HPUX 11.x, the 'ENOBUFS, No buffer space available'
+ * error occures because the accept() cannot complete.
+ * You will not see ENOBUFS at 10.20 because the kernel
+ * hides any occurrence from being returned from user space.
+ * ENOBUFS at 11.0 TCP/IP is quite possible, and could
+ * occur intermittently. As a work-around, we are going to
+ * ingnore ENOBUFS.
+ */
+ case ENOBUFS:
+#endif
+
+#ifdef EPROTO
+ /* EPROTO on certain older kernels really means
+ * ECONNABORTED, so we need to ignore it for them.
+ * See discussion in new-httpd archives nh.9701
+ * search for EPROTO.
+ *
+ * Also see nh.9603, search for EPROTO:
+ * There is potentially a bug in Solaris 2.x x<6,
+ * and other boxes that implement tcp sockets in
+ * userland (i.e. on top of STREAMS). On these
+ * systems, EPROTO can actually result in a fatal
+ * loop. See PR#981 for example. It's hard to
+ * handle both uses of EPROTO.
+ */
+ case EPROTO:
+#endif
+#ifdef ECONNABORTED
+ case ECONNABORTED:
+#endif
+ /* 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.
+ */
+#ifdef ECONNRESET
+ case ECONNRESET:
+#endif
+#ifdef ETIMEDOUT
+ case ETIMEDOUT:
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH:
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH:
+#endif
+ /* EAGAIN/EWOULDBLOCK can be returned on BSD-derived
+ * TCP stacks when the connection is aborted before
+ * we call connect, but only because our listener
+ * sockets are non-blocking (NONBLOCK_WHEN_MULTI_LISTEN)
+ */
+#ifdef EAGAIN
+ case EAGAIN:
+#endif
+#ifdef EWOULDBLOCK
+#if !defined(EAGAIN) || EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+#endif
+ break;
+#ifdef ENETDOWN
+ 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, server_conf,
+ "accept: giving up.");
+ clean_child_exit(APEXIT_CHILDFATAL);
+#endif /*ENETDOWN*/
+
+#ifdef TPF
+ case EINACT:
+ ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO,
+ server_conf, "offload device inactive");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ break;
+ default:
+ if (getppid() != 1) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO,
+ server_conf, "select/accept error (%u)",
+ errno);
+ }
+ clean_child_exit(APEXIT_CHILDFATAL);
+#else
+ default:
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "accept: (client socket)");
+ clean_child_exit(1);
+#endif
+ }
+ }
+
+ /* go around again, safe to die */
+ usr1_just_die = 1;
+ if (deferred_die) {
+ /* ok maybe not, see ya later */
+ clean_child_exit(0);
+ }
+ /* or maybe we missed a signal, you never know on systems
+ * without reliable signals
+ */
+ ap_sync_scoreboard_image();
+ if (ap_scoreboard_image->global.running_generation != ap_my_generation) {
+ clean_child_exit(0);
+ }
+ }
+
+ SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */
+
+ /* We've got a socket, let's at least process one request off the
+ * socket before we accept a graceful restart request.
+ */
+ signal(SIGUSR1, SIG_IGN);
+
+ ap_note_cleanups_for_socket_ex(ptrans, csd, 1);
+
+ /* protect various fd_sets */
+#ifdef CHECK_FD_SETSIZE
+ if (csd >= FD_SETSIZE) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+ "[csd] filedescriptor (%u) larger than FD_SETSIZE (%u) "
+ "found, you probably need to rebuild Apache with a "
+ "larger FD_SETSIZE", csd, FD_SETSIZE);
+ continue;
+ }
+#endif
+
+ /*
+ * We now have a connection, so set it up with the appropriate
+ * socket options, file descriptors, and read/write buffers.
+ */
+
+#ifdef NONBLOCK_WHEN_MULTI_LISTEN
+ /* This assumes that on this platform the non-blocking setting of
+ * a listening socket is inherited. If that isn't the case,
+ * this is wasted effort.
+ */
+ if (ap_listeners != ap_listeners->next) {
+ if (soblock(csd) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, NULL,
+ "couldn't make socket descriptor (%d) blocking again",
+ csd);
+ continue;
+ }
+ }
+#endif /* NONBLOCK_WHEN_MULTI_LISTEN */
+
+ clen = sizeof(sa_server);
+ if (getsockname(csd, &sa_server, &clen) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, server_conf,
+ "getsockname, client %pA probably dropped the "
+ "connection",
+ &((struct sockaddr_in *)&sa_client)->sin_addr);
+ continue;
+ }
+
+ sock_disable_nagle(csd, (struct sockaddr_in *)&sa_client);
+
+ (void) ap_update_child_status(my_child_num, SERVER_BUSY_READ,
+ (request_rec *) NULL);
+
+ conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
+
+#ifdef B_SFIO
+ (void) sfdisc(conn_io->sf_in, SF_POPDISC);
+ sfdisc(conn_io->sf_in, bsfio_new(conn_io->pool, conn_io));
+ sfsetbuf(conn_io->sf_in, NULL, 0);
+
+ (void) sfdisc(conn_io->sf_out, SF_POPDISC);
+ sfdisc(conn_io->sf_out, bsfio_new(conn_io->pool, conn_io));
+ sfsetbuf(conn_io->sf_out, NULL, 0);
+#endif
+
+ dupped_csd = csd;
+#if defined(NEED_DUPPED_CSD)
+ if ((dupped_csd = dup(csd)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "dup: couldn't duplicate csd");
+ dupped_csd = csd; /* Oh well... */
+ }
+ ap_note_cleanups_for_socket_ex(ptrans, dupped_csd, 1);
+
+ /* protect various fd_sets */
+#ifdef CHECK_FD_SETSIZE
+ if (dupped_csd >= FD_SETSIZE) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+ "[dupped_csd] filedescriptor (%u) larger than FD_SETSIZE (%u) "
+ "found, you probably need to rebuild Apache with a "
+ "larger FD_SETSIZE", dupped_csd, FD_SETSIZE);
+ continue;
+ }
+#endif
+#endif
+ ap_bpushfd(conn_io, csd, dupped_csd);
+
+ current_conn = new_connection(ptrans, server_conf, conn_io,
+ (struct sockaddr_in *) &sa_client,
+ (struct sockaddr_in *) &sa_server,
+ my_child_num);
+
+ /*
+ * Read and process each request found on our connection
+ * until no requests are left or we decide to close.
+ */
+
+ while ((r = ap_read_request(current_conn)) != NULL) {
+
+ /* read_request_line has already done a
+ * signal (SIGUSR1, SIG_IGN);
+ */
+
+ (void) ap_update_child_status(my_child_num, SERVER_BUSY_WRITE, r);
+
+ /* process the request if it was read without error */
+
+ if (r->status == HTTP_OK)
+ ap_process_request(r);
+
+ if(ap_extended_status)
+ increment_counts(my_child_num, r);
+
+#ifdef TPF_HAVE_NSD
+ /* Update the TPF Network Services Database message counters */
+ tpf_tcpip_message_cnt(NSDB_INPUT_CNT,
+ ((struct sockaddr_in *)&sa_server)->sin_port,
+ NSDB_TCP_S, 1);
+
+ tpf_tcpip_message_cnt(NSDB_OUTPUT_CNT,
+ ((struct sockaddr_in *)&sa_server)->sin_port,
+ NSDB_TCP_S, 1);
+#endif /* TPF_HAVE_NSD */
+
+ if (!current_conn->keepalive || current_conn->aborted)
+ break;
+
+ ap_destroy_pool(r->pool);
+ (void) ap_update_child_status(my_child_num, SERVER_BUSY_KEEPALIVE,
+ (request_rec *) NULL);
+
+ ap_sync_scoreboard_image();
+ if (ap_scoreboard_image->global.running_generation != ap_my_generation) {
+ ap_bclose(conn_io);
+ clean_child_exit(0);
+ }
+
+ /* In case we get a graceful restart while we're blocked
+ * waiting for the request.
+ *
+ * XXX: This isn't perfect, we might actually read the
+ * request and then just die without saying anything to
+ * the client. This can be fixed by using deferred_die
+ * but you have to teach buff.c about it so that it can handle
+ * the EINTR properly.
+ *
+ * In practice though browsers (have to) expect keepalive
+ * connections to close before receiving a response because
+ * of network latencies and server timeouts.
+ */
+ usr1_just_die = 1;
+ signal(SIGUSR1, usr1_handler);
+ }
+
+ /*
+ * Close the connection, being careful to send out whatever is still
+ * in our buffers. If possible, try to avoid a hard close until the
+ * client has ACKed our FIN and/or has stopped sending us data.
+ */
+
+#ifdef NO_LINGCLOSE
+ ap_bclose(conn_io); /* just close it */
+#else
+ if (r && r->connection
+ && !r->connection->aborted
+ && r->connection->client
+ && (r->connection->client->fd >= 0)) {
+
+ lingering_close(r);
+ }
+ else {
+ ap_bsetflag(conn_io, B_EOUT, 1);
+ ap_bclose(conn_io);
+ }
+#endif
+ }
+}
+
+#ifdef TPF
+static void reset_tpf_listeners(APACHE_TPF_INPUT *input_parms)
+{
+ int count;
+ listen_rec *lr;
+
+ count = 0;
+ listenmaxfd = -1;
+ FD_ZERO(&listenfds);
+ lr = ap_listeners;
+
+ for(;;) {
+ lr->fd = input_parms->listeners[count];
+ if(lr->fd >= 0) {
+ FD_SET(lr->fd, &listenfds);
+ if(lr->fd > listenmaxfd)
+ listenmaxfd = lr->fd;
+ }
+ if(lr->next == NULL)
+ break;
+ lr = lr->next;
+ count++;
+ }
+ lr->next = ap_listeners;
+ head_listener = ap_listeners;
+ close_unused_listeners();
+}
+
+#endif /* TPF */
+
+static int make_child(server_rec *s, int slot, time_t now)
+{
+ int pid;
+
+ if (slot + 1 > max_daemons_limit) {
+ max_daemons_limit = slot + 1;
+ }
+
+ if (one_process) {
+ signal(SIGHUP, just_die);
+ signal(SIGINT, just_die);
+#ifdef SIGQUIT
+ signal(SIGQUIT, SIG_DFL);
+#endif
+ signal(SIGTERM, just_die);
+ child_main(slot);
+ }
+
+ /* avoid starvation */
+ head_listener = head_listener->next;
+
+ Explain1("Starting new child in slot %d", slot);
+ (void) ap_update_child_status(slot, SERVER_STARTING, (request_rec *) NULL);
+
+
+#ifdef _OSD_POSIX
+ /* BS2000 requires a "special" version of fork() before a setuid() call */
+ if ((pid = os_fork(ap_user_name)) == -1) {
+#elif defined(TPF)
+ if ((pid = os_fork(s, slot)) == -1) {
+#else
+ if ((pid = fork()) == -1) {
+#endif
+ ap_log_error(APLOG_MARK, APLOG_ERR, s, "fork: Unable to fork new process");
+
+ /* fork didn't succeed. Fix the scoreboard or else
+ * it will say SERVER_STARTING forever and ever
+ */
+ (void) ap_update_child_status(slot, SERVER_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. */
+ sleep(10);
+
+ return -1;
+ }
+
+ if (!pid) {
+#ifdef AIX_BIND_PROCESSOR
+/* by default AIX binds to a single processor
+ * this bit unbinds children which will then bind to another cpu
+ */
+#include <sys/processor.h>
+ int status = bindprocessor(BINDPROCESS, (int)getpid(),
+ PROCESSOR_CLASS_ANY);
+ if (status != OK) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf,
+ "processor unbind failed %d", status);
+ }
+#endif
+ RAISE_SIGSTOP(MAKE_CHILD);
+ MONCONTROL(1);
+ /* Disable the restart signal handlers and enable the just_die stuff.
+ * Note that since restart() just notes that a restart has been
+ * requested there's no race condition here.
+ */
+ signal(SIGHUP, just_die);
+ signal(SIGUSR1, just_die);
+ signal(SIGTERM, just_die);
+ child_main(slot);
+ }
+
+#ifdef OPTIMIZE_TIMEOUTS
+ ap_scoreboard_image->parent[slot].last_rtime = now;
+#endif
+ ap_scoreboard_image->parent[slot].pid = pid;
+ set_pid_table(pid);
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[slot]), 0);
+ force_write(scoreboard_fd, &ap_scoreboard_image->parent[slot],
+ sizeof(parent_score));
+#endif
+
+ return 0;
+}
+
+
+/* start up a bunch of children */
+static void startup_children(int number_to_start)
+{
+ int i;
+ time_t now = time(NULL);
+
+ for (i = 0; number_to_start && i < ap_daemons_limit; ++i) {
+ if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) {
+ continue;
+ }
+ if (make_child(server_conf, i, now) < 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;
+
+/*
+ * Define the signal that is used to kill off children if idle_count
+ * is greater then ap_daemons_max_free. Usually we will use SIGUSR1
+ * to gracefully shutdown, but unfortunatly some OS will need other
+ * signals to ensure that the child process is terminated and the
+ * scoreboard pool is not growing to infinity. Also set the signal we
+ * use to kill of childs that exceed timeout. This effect has been
+* seen at least on Cygwin 1.x. -- Stipe Tolj <tolj@wapme-systems.de>
+ */
+#if defined(CYGWIN)
+#define SIG_IDLE_KILL SIGKILL
+#define SIG_TIMEOUT_KILL SIGUSR2
+#else
+#define SIG_IDLE_KILL SIGUSR1
+#define SIG_TIMEOUT_KILL SIGALRM
+#endif
+
+static void perform_idle_server_maintenance(void)
+{
+ int i;
+ int to_kill;
+ int idle_count;
+ int pid;
+ short_score *ss;
+ time_t now = time(NULL);
+ 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_daemons_limit; ++i) {
+ int status;
+
+ if (i >= max_daemons_limit && free_length == idle_spawn_rate)
+ break;
+ ss = &ap_scoreboard_image->servers[i];
+ status = ss->status;
+ if (status == SERVER_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;
+#ifdef OPTIMIZE_TIMEOUTS
+ if (ss->timeout_len) {
+ /* if it's a live server, with a live timeout then
+ * start checking its timeout */
+ parent_score *ps = &ap_scoreboard_image->parent[i];
+ if (ss->cur_vtime != ps->last_vtime) {
+ /* it has made progress, so update its last_rtime,
+ * last_vtime */
+ ps->last_rtime = now;
+ ps->last_vtime = ss->cur_vtime;
+ }
+ else if (ps->last_rtime + ss->timeout_len < now) {
+ /* no progress, and the timeout length has been exceeded */
+ ss->timeout_len = 0;
+ pid = ps->pid;
+ if (in_pid_table(pid)) {
+ kill(pid, SIG_TIMEOUT_KILL);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "Bad pid (%d) in scoreboard slot %d", pid, i);
+ }
+ }
+ }
+#endif
+ }
+ }
+ max_daemons_limit = last_non_dead + 1;
+ if (idle_count > ap_daemons_max_free) {
+ /* kill off one child... we use SIGUSR1 because that'll cause it to
+ * shut down gracefully, in case it happened to pick up a request
+ * while we were counting. Use the define SIG_IDLE_KILL to reflect
+ * which signal should be used on the specific OS.
+ */
+ pid = ap_scoreboard_image->parent[to_kill].pid;
+ if (in_pid_table(pid)) {
+ kill(pid, SIG_IDLE_KILL);
+ idle_spawn_rate = 1;
+#ifdef TPF
+ ap_update_child_status(to_kill, SERVER_DEAD, (request_rec *)NULL);
+#endif
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "Bad pid (%d) in scoreboard slot %d", pid, to_kill);
+ }
+ }
+ else if (idle_count < ap_daemons_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, 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, 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) {
+#ifdef TPF
+ if(make_child(server_conf, free_slots[i], now) == -1) {
+ if(free_length == 1) {
+ shutdown_pending = 1;
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "No active child processes: shutting down");
+ }
+ }
+#else
+ make_child(server_conf, free_slots[i], now);
+#endif /* TPF */
+ }
+ /* 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 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) {
+ /* cleanup pid file -- it is useless after our exiting */
+ const char *pidfile = NULL;
+#ifdef TPF
+ /* safer on TPF to go through normal shutdown process */
+ if (!shutdown_pending) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server_conf,
+ "Child %d returned a Fatal error... \n"
+ "Apache is shutting down!", pid);
+ shutdown_pending = 1;
+ }
+ return;
+#endif
+ 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_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
+ }
+ }
+}
+
+
+/*****************************************************************
+ * Executive routines.
+ */
+
+#ifndef STANDALONE_MAIN
+#define STANDALONE_MAIN standalone_main
+
+static void standalone_main(int argc, char **argv)
+{
+ int remaining_children_to_start;
+
+#ifdef OS2
+ printf("%s \n", ap_get_server_version());
+#endif
+
+ ap_standalone = 1;
+
+ is_graceful = 0;
+
+ if (!one_process) {
+ detach();
+ }
+ else {
+ MONCONTROL(1);
+ }
+
+ my_pid = getpid();
+
+ do {
+ copy_listeners(pconf);
+ if (!is_graceful) {
+ ap_restart_time = time(NULL);
+ }
+#ifdef SCOREBOARD_FILE
+ else if (scoreboard_fd != -1) {
+ ap_kill_cleanup(pconf, NULL, cleanup_scoreboard_file);
+ ap_kill_cleanups_for_fd(pconf, scoreboard_fd);
+ }
+#endif
+ ap_clear_pool(pconf);
+#ifdef AP_ENABLE_EXCEPTION_HOOK
+ ap_register_cleanup(pconf, NULL, except_hook_cleanup, ap_null_cleanup);
+#endif
+ ptrans = ap_make_sub_pool(pconf);
+
+ ap_init_mutex_method(ap_default_mutex_method());
+
+ server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+ setup_listeners(pconf);
+ ap_clear_pool(plog);
+ ap_open_logs(server_conf, plog);
+ ap_log_pid(pconf, ap_pid_fname);
+ ap_set_version(); /* create our server_version string */
+ ap_init_modules(pconf, server_conf);
+ version_locked++; /* no more changes to server_version */
+ SAFE_ACCEPT(accept_mutex_init(pconf));
+ if (!is_graceful) {
+ reinit_scoreboard(pconf);
+ }
+#ifdef SCOREBOARD_FILE
+ else {
+ ap_scoreboard_fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
+ ap_note_cleanups_for_fd_ex(pconf, scoreboard_fd, 1); /* close on exec */
+ }
+#endif
+
+ set_signals();
+
+ if (ap_daemons_max_free < ap_daemons_min_free + 1) /* Don't thrash... */
+ ap_daemons_max_free = ap_daemons_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 SIGUSR1). 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());
+ if (ap_suexec_enabled) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "suEXEC mechanism enabled (wrapper: %s)", SUEXEC_BIN);
+ }
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "Server built: %s", ap_get_server_built());
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "Accept mutex: %s (Default: %s)",
+ amutex->name, ap_default_mutex_method());
+ restart_pending = shutdown_pending = 0;
+
+ while (!restart_pending && !shutdown_pending) {
+ int child_slot;
+ ap_wait_t status;
+ int pid = wait_or_timeout(&status);
+
+ /* 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
+ */
+#ifdef TPF
+ if (shutdown_pending += os_check_server(tpf_server_name)) {
+ break;
+ }
+#endif
+ if (pid >= 0) {
+ unset_pid_table(pid);
+ 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);
+ Explain2("Reaping child %d slot %d", pid, child_slot);
+ if (child_slot >= 0) {
+ (void) ap_update_child_status(child_slot, 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
+ */
+ make_child(server_conf, child_slot, time(NULL));
+ --remaining_children_to_start;
+ }
+#ifndef NO_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();
+ }
+
+ if (shutdown_pending) {
+ /* Time to gracefully shut down:
+ * Kill child processes, tell them to call child_exit, etc...
+ */
+ if (ap_killpg(pgrp, 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");
+ clean_parent_exit(0);
+ }
+
+ /* we've been told to restart */
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGUSR1, SIG_IGN);
+
+ if (one_process) {
+ /* not worth thinking about */
+ clean_parent_exit(0);
+ }
+
+ /* 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) {
+#ifndef SCOREBOARD_FILE
+ int i;
+#endif
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "SIGUSR1 received. Doing graceful restart");
+
+ /* kill off the idle ones */
+ if (ap_killpg(pgrp, SIGUSR1) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGUSR1");
+ }
+#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 (i = 0; i < ap_daemons_limit; ++i) {
+ if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) {
+ ap_scoreboard_image->servers[i].status = SERVER_GRACEFUL;
+ }
+ }
+#endif
+ }
+ else {
+ /* Kill 'em off */
+ if (ap_killpg(pgrp, SIGHUP) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGHUP");
+ }
+ reclaim_child_processes(0); /* Not when just starting up */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "SIGHUP received. Attempting to restart");
+ }
+ } while (restart_pending);
+
+ /*add_common_vars(NULL);*/
+} /* standalone_main */
+#else
+/* prototype */
+void STANDALONE_MAIN(int argc, char **argv);
+#endif /* STANDALONE_MAIN */
+
+extern char *optarg;
+extern int optind;
+
+/* Cygwin 1.x SHARED_CORE support needs REALMAIN to be declared as dllexport,
+ * so we can later while SHARED_CORE_BOOTSTRAP is compiled and linked see the
+ * dllimport for it. -- Stipe Tolj <tolj@wapme-systems.de>
+ */
+
+#if defined(CYGWIN)
+__declspec(dllexport)
+#endif
+
+int REALMAIN(int argc, char *argv[])
+{
+ int c;
+ int sock_in;
+ int sock_out;
+ char *s;
+
+#ifdef SecureWare
+ if (set_auth_parameters(argc, argv) < 0)
+ perror("set_auth_parameters");
+ if (getluid() < 0)
+ if (setluid(getuid()) < 0)
+ perror("setluid");
+ if (setreuid(0, 0) < 0)
+ perror("setreuid");
+#endif
+
+#ifdef SOCKS
+ SOCKSinit(argv[0]);
+#endif
+
+#ifdef TPF
+ EBW_AREA input_parms;
+ ecbptr()->ebrout = PRIMECRAS;
+ input_parms = * (EBW_AREA *)(&(ecbptr()->ebw000));
+#endif
+
+ MONCONTROL(0);
+
+ common_init();
+
+ if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) {
+ ap_server_argv0 = ++s;
+ }
+ else {
+ ap_server_argv0 = argv[0];
+ }
+
+ ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root));
+ ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname));
+
+ ap_setup_prelinked_modules();
+
+ while ((c = getopt(argc, argv,
+ "D:C:c:xXd:Ff:vVlLR:StTh"
+#ifdef DEBUG_SIGSTOP
+ "Z:"
+#endif
+ )) != -1) {
+ char **new;
+ switch (c) {
+ case 'c':
+ new = (char **)ap_push_array(ap_server_post_read_config);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'C':
+ new = (char **)ap_push_array(ap_server_pre_read_config);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'D':
+ new = (char **)ap_push_array(ap_server_config_defines);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'd':
+ ap_cpystrn(ap_server_root, optarg, sizeof(ap_server_root));
+ break;
+ case 'F':
+ do_detach = 0;
+ break;
+ case 'f':
+ ap_cpystrn(ap_server_confname, optarg, sizeof(ap_server_confname));
+ break;
+ case 'v':
+ ap_set_version();
+ printf("Server version: %s\n", ap_get_server_version());
+ printf("Server built: %s\n", ap_get_server_built());
+ exit(0);
+ case 'V':
+ ap_set_version();
+ show_compile_settings();
+ exit(0);
+ case 'l':
+ ap_suexec_enabled = init_suexec();
+ ap_show_modules();
+ exit(0);
+ case 'L':
+ ap_show_directives();
+ exit(0);
+ case 'X':
+ ++one_process; /* Weird debugging mode. */
+ break;
+#ifdef TPF
+ case 'x':
+ os_tpf_child(&input_parms.child);
+ set_signals();
+ break;
+#endif
+#ifdef DEBUG_SIGSTOP
+ case 'Z':
+ raise_sigstop_flags = atoi(optarg);
+ break;
+#endif
+#ifdef SHARED_CORE
+ case 'R':
+ /* just ignore this option here, because it has only
+ * effect when SHARED_CORE is used and then it was
+ * already handled in the Shared Core Bootstrap
+ * program.
+ */
+ break;
+#endif
+ case 'S':
+ ap_dump_settings = 1;
+ break;
+ case 't':
+ ap_configtestonly = 1;
+ ap_docrootcheck = 1;
+ break;
+ case 'T':
+ ap_configtestonly = 1;
+ ap_docrootcheck = 0;
+ break;
+ case 'h':
+ usage(argv[0]);
+ case '?':
+ usage(argv[0]);
+ }
+ }
+
+ ap_suexec_enabled = init_suexec();
+ server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+
+ if (ap_configtestonly) {
+ fprintf(stderr, "Syntax OK\n");
+ exit(0);
+ }
+ if (ap_dump_settings) {
+ exit(0);
+ }
+
+ child_timeouts = !ap_standalone || one_process;
+
+#ifdef BEOS
+ /* make sure we're running in single_process mode - Yuck! */
+ one_process = 1;
+#endif
+
+#ifndef TPF
+ if (ap_standalone) {
+ ap_open_logs(server_conf, plog);
+ ap_set_version();
+ ap_init_modules(pconf, server_conf);
+ version_locked++;
+ STANDALONE_MAIN(argc, argv);
+ }
+#else
+ if (!tpf_child) {
+ memcpy(tpf_server_name, input_parms.parent.servname,
+ INETD_SERVNAME_LENGTH);
+ tpf_server_name[INETD_SERVNAME_LENGTH] = '\0';
+ sprintf(tpf_mutex_key, "%.*x", (int) TPF_MUTEX_KEY_SIZE - 1, getpid());
+ tpf_parent_pid = getppid();
+ ap_open_logs(server_conf, plog);
+ ap_tpf_zinet_checks(ap_standalone, tpf_server_name, server_conf);
+ ap_tpf_save_argv(argc, argv); /* save argv parms for children */
+ }
+ if (ap_standalone) {
+ ap_set_version();
+ ap_init_modules(pconf, server_conf);
+ version_locked++;
+ if(tpf_child) {
+ server_conf->error_log = stderr;
+#ifdef HAVE_SYSLOG
+ /* if ErrorLog is syslog call ap_open_logs from the child since
+ syslog isn't redirected to stderr by the Apache parent */
+ if (strncasecmp(server_conf->error_fname, "syslog", 6) == 0) {
+ ap_open_logs(server_conf, plog);
+ }
+#endif /* HAVE_SYSLOG */
+ copy_listeners(pconf);
+ reset_tpf_listeners(&input_parms.child);
+#ifdef SCOREBOARD_FILE
+ ap_scoreboard_image = &_scoreboard_image;
+#else /* must be USE_SHMGET_SCOREBOARD */
+ ap_scoreboard_image =
+ (scoreboard *)input_parms.child.scoreboard_heap;
+#endif
+ ap_init_mutex_method(ap_default_mutex_method());
+ child_main(input_parms.child.slot);
+ }
+ else
+ STANDALONE_MAIN(argc, argv);
+ }
+#endif
+ else {
+ conn_rec *conn;
+ request_rec *r;
+ struct sockaddr sa_server, sa_client;
+ BUFF *cio;
+ NET_SIZE_T l;
+
+ ap_set_version();
+ /* Yes this is called twice. */
+ ap_init_modules(pconf, server_conf);
+ version_locked++;
+ ap_open_logs(server_conf, plog);
+ ap_init_modules(pconf, server_conf);
+ set_group_privs();
+
+#ifdef MPE
+ /* No such thing as root on MPE, so try to switch unconditionally */
+ GETPRIVMODE();
+ if (setuid(ap_user_id) == -1) {
+ GETUSERMODE();
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setuid: unable to change to uid: %d", ap_user_id);
+ exit(1);
+ }
+ GETUSERMODE();
+#else
+ /*
+ * Only try to switch if we're running as root
+ * In case of Cygwin we have the special super-user named SYSTEM
+ * with a pre-defined uid.
+ */
+#ifdef CYGWIN
+ if ((getuid() == SYSTEM_UID) && setuid(ap_user_id) == -1) {
+#else
+ if (!geteuid() && setuid(ap_user_id) == -1) {
+#endif
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setuid: unable to change to uid: %ld",
+ (long) ap_user_id);
+ exit(1);
+ }
+#endif
+ if (ap_setjmp(jmpbuffer)) {
+ exit(0);
+ }
+
+#ifdef MPE
+/* HP MPE 5.5 inetd only passes the incoming socket as stdin (fd 0), whereas
+ HPUX inetd passes the incoming socket as stdin (fd 0) and stdout (fd 1).
+ Go figure. SR 5003355016 has been submitted to request that the existing
+ functionality be documented, and then to enhance the functionality to be
+ like HPUX. */
+ sock_in = fileno(stdin);
+ sock_out = fileno(stdin);
+#else
+ sock_in = fileno(stdin);
+ sock_out = fileno(stdout);
+#endif
+
+ l = sizeof(sa_client);
+ if ((getpeername(sock_in, &sa_client, &l)) < 0) {
+/* get peername will fail if the input isn't a socket */
+ perror("getpeername");
+ memset(&sa_client, '\0', sizeof(sa_client));
+ }
+
+ l = sizeof(sa_server);
+ if (getsockname(sock_in, &sa_server, &l) < 0) {
+ perror("getsockname");
+ fprintf(stderr, "Error getting local address\n");
+ exit(1);
+ }
+ server_conf->port = ntohs(((struct sockaddr_in *) &sa_server)->sin_port);
+ cio = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
+ cio->fd = sock_out;
+ cio->fd_in = sock_in;
+ conn = new_connection(ptrans, server_conf, cio,
+ (struct sockaddr_in *) &sa_client,
+ (struct sockaddr_in *) &sa_server, -1);
+
+ while ((r = ap_read_request(conn)) != NULL) {
+
+ if (r->status == HTTP_OK)
+ ap_process_request(r);
+
+ if (!conn->keepalive || conn->aborted)
+ break;
+
+ ap_destroy_pool(r->pool);
+ }
+
+ ap_bclose(cio);
+ }
+ exit(0);
+}
+
+#else /* ndef MULTITHREAD */
+
+
+/**********************************************************************
+ * Multithreaded implementation
+ *
+ * This code is fairly specific to Win32.
+ *
+ * The model used to handle requests is a set of threads. One "main"
+ * thread listens for new requests. When something becomes
+ * available, it does a select and places the newly available socket
+ * onto a list of "jobs" (add_job()). Then any one of a fixed number
+ * of "worker" threads takes the top job off the job list with
+ * remove_job() and handles that connection to completion. After
+ * the connection has finished the thread is free to take another
+ * job from the job list.
+ *
+ * In the code, the "main" thread is running within the worker_main()
+ * function. The first thing this function does is create the
+ * worker threads, which operate in the child_sub_main() function. The
+ * main thread then goes into a loop within worker_main() where they
+ * do a select() on the listening sockets. The select times out once
+ * per second so that the thread can check for an "exit" signal
+ * from the parent process (see below). If this signal is set, the
+ * thread can exit, but only after it has accepted all incoming
+ * connections already in the listen queue (since Win32 appears
+ * to through away listened but unaccepted connections when a
+ * process dies).
+ *
+ * Because the main and worker threads exist within a single process
+ * they are vulnerable to crashes or memory leaks (crashes can also
+ * be caused within modules, of course). There also needs to be a
+ * mechanism to perform restarts and shutdowns. This is done by
+ * creating the main & worker threads within a subprocess. A
+ * main process (the "parent process") creates one (or more)
+ * processes to do the work, then the parent sits around waiting
+ * for the working process to die, in which case it starts a new
+ * one. The parent process also handles restarts (by creating
+ * a new working process then signalling the previous working process
+ * exit ) and shutdowns (by signalling the working process to exit).
+ * The parent process operates within the master_main() function. This
+ * process also handles requests from the service manager (NT only).
+ *
+ * Signalling between the parent and working process uses a Win32
+ * event. Each child has a unique name for the event, which is
+ * passed to it with the -Z argument when the child is spawned. The
+ * parent sets (signals) this event to tell the child to die.
+ * At present all children do a graceful die - they finish all
+ * current jobs _and_ empty the listen queue before they exit.
+ * A non-graceful die would need a second event. The -Z argument in
+ * the child is also used to create the shutdown and restart events,
+ * since the prefix (apPID) contains the parent process PID.
+ *
+ * The code below starts with functions at the lowest level -
+ * worker threads, and works up to the top level - the main()
+ * function of the parent process.
+ *
+ * The scoreboard (in process memory) contains details of the worker
+ * threads (within the active working process). There is no shared
+ * "scoreboard" between processes, since only one is ever active
+ * at once (or at most, two, when one has been told to shutdown but
+ * is processes outstanding requests, and a new one has been started).
+ * This is controlled by a "start_mutex" which ensures only one working
+ * process is active at once.
+ **********************************************************************/
+
+/* The code protected by #ifdef UNGRACEFUL_RESTARTS/#endif sections
+ * could implement a sort-of ungraceful restart for Win32. instead of
+ * graceful restarts.
+ *
+ * However it does not work too well because it does not intercept a
+ * connection already in progress (in child_sub_main()). We'd have to
+ * get that to poll on the exit event.
+ */
+
+/*
+ * Definition of jobs, shared by main and worker threads.
+ */
+
+typedef struct joblist_s {
+ struct joblist_s *next;
+ int sock;
+} joblist;
+
+/*
+ * Globals common to main and worker threads. This structure is not
+ * used by the parent process.
+ */
+
+typedef struct globals_s {
+#ifdef UNGRACEFUL_RESTART
+ HANDLE thread_exit_event;
+#else
+ int exit_now;
+#endif
+ semaphore *jobsemaphore;
+ joblist *jobhead;
+ joblist *jobtail;
+ mutex *jobmutex;
+ int jobcount;
+} globals;
+
+globals allowed_globals =
+{0, NULL, NULL, NULL, NULL, 0};
+
+/*
+ * add_job()/remove_job() - add or remove an accepted socket from the
+ * list of sockets connected to clients. allowed_globals.jobmutex protects
+ * against multiple concurrent access to the linked list of jobs.
+ */
+
+void add_job(int sock)
+{
+ joblist *new_job;
+
+ ap_assert(allowed_globals.jobmutex);
+
+ /* TODO: If too many jobs in queue, sleep, check for problems */
+ ap_acquire_mutex(allowed_globals.jobmutex);
+ new_job = (joblist *) malloc(sizeof(joblist));
+ if (new_job == NULL) {
+ fprintf(stderr, "Ouch! Out of memory in add_job()!\n");
+ }
+ new_job->next = NULL;
+ new_job->sock = sock;
+ if (allowed_globals.jobtail != NULL)
+ allowed_globals.jobtail->next = new_job;
+ allowed_globals.jobtail = new_job;
+ if (!allowed_globals.jobhead)
+ allowed_globals.jobhead = new_job;
+ allowed_globals.jobcount++;
+ release_semaphore(allowed_globals.jobsemaphore);
+ ap_release_mutex(allowed_globals.jobmutex);
+}
+
+int remove_job(int csd)
+{
+ static reported = 0;
+ static active_threads = 0;
+ joblist *job;
+ int sock;
+
+ /* Decline decrementing active_threads count on the first call
+ * to remove_job. csd == -1 implies that this is the thread's
+ * first call to remove_job.
+ */
+ if (csd != -1) {
+ active_threads--;
+ }
+
+#ifdef UNGRACEFUL_RESTART
+ HANDLE hObjects[2];
+ int rv;
+
+ hObjects[0] = allowed_globals.jobsemaphore;
+ hObjects[1] = allowed_globals.thread_exit_event;
+
+ rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE);
+ ap_assert(rv != WAIT_FAILED);
+ if (rv == WAIT_OBJECT_0 + 1) {
+ /* thread_exit_now */
+ APD1("thread got exit now event");
+ return -1;
+ }
+ /* must be semaphore */
+#else
+ acquire_semaphore(allowed_globals.jobsemaphore);
+#endif
+ ap_assert(allowed_globals.jobmutex);
+
+#ifdef UNGRACEFUL_RESTART
+ if (!allowed_globals.jobhead) {
+#else
+ ap_acquire_mutex(allowed_globals.jobmutex);
+ if (allowed_globals.exit_now && !allowed_globals.jobhead) {
+#endif
+ ap_release_mutex(allowed_globals.jobmutex);
+ return (-1);
+ }
+
+ job = allowed_globals.jobhead;
+ ap_assert(job);
+ allowed_globals.jobhead = job->next;
+ if (allowed_globals.jobhead == NULL)
+ allowed_globals.jobtail = NULL;
+
+ ap_release_mutex(allowed_globals.jobmutex);
+ sock = job->sock;
+ free(job);
+
+ /* If sock == -1 then the thread is about to exit so
+ * don't count it as active.
+ */
+ if (sock != -1)
+ active_threads++;
+
+ if (!reported && (active_threads == ap_threads_per_child)) {
+ reported = 1;
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "Server ran out of threads to serve requests. Consider "
+ "raising the ThreadsPerChild setting");
+ }
+ return (sock);
+}
+
+/*
+ * child_sub_main() - this is the main loop for the worker threads
+ *
+ * Each thread runs within this function. They wait within remove_job()
+ * for a job to become available, then handle all the requests on that
+ * connection until it is closed, then return to remove_job().
+ *
+ * The worker thread will exit when it removes a job which contains
+ * socket number -1. This provides a graceful thread exit, since
+ * it will never exit during a connection.
+ *
+ * This code in this function is basically equivalent to the child_main()
+ * from the multi-process (Unix) environment, except that we
+ *
+ * - do not call child_init_modules (child init API phase)
+ * - block in remove_job, and when unblocked we have an already
+ * accepted socket, instead of blocking on a mutex or select().
+ */
+
+static void child_sub_main(int child_num)
+{
+ NET_SIZE_T clen;
+ struct sockaddr sa_server;
+ struct sockaddr sa_client;
+ pool *ptrans;
+ int requests_this_child = 0;
+ int csd = -1;
+ int dupped_csd = -1;
+ int srv = 0;
+
+#ifdef NETWARE
+ TSD* tsd = NULL;
+
+ while(tsd == NULL) {
+ tsd = (TSD*) Thread_Data_Area;
+ ThreadSwitchWithDelay();
+ }
+ init_name_space();
+#endif
+ ap_thread_count++;
+ ptrans = ap_make_sub_pool(pconf);
+
+ (void) ap_update_child_status(child_num, SERVER_READY, (request_rec *) NULL);
+
+ /*
+ * Setup the jump buffers so that we can return here after a timeout.
+ */
+#if defined(USE_LONGJMP)
+ setjmp(jmpbuffer);
+#else
+ sigsetjmp(jmpbuffer, 1);
+#endif
+#if defined(SIGURG)
+ signal(SIGURG, timeout);
+#endif
+
+#ifdef NETWARE
+ tsd = (TSD*) Thread_Data_Area;
+#endif
+
+ while (1) {
+ BUFF *conn_io;
+ request_rec *r;
+
+#ifdef NETWARE
+ ThreadSwitch();
+#endif
+ /*
+ * (Re)initialize this child to a pre-connection state.
+ */
+
+ ap_set_callback_and_alarm(NULL, 0); /* Cancel any outstanding alarms */
+ timeout_req = NULL; /* No request in progress */
+ current_conn = NULL;
+ ap_clear_pool(ptrans);
+
+ (void) ap_update_child_status(child_num, SERVER_READY,
+ (request_rec *) NULL);
+
+ /* Get job from the job list. This will block until a job is ready.
+ * If -1 is returned then the main thread wants us to exit.
+ */
+ csd = remove_job(csd);
+ if (csd == -1)
+ break; /* time to exit */
+
+ requests_this_child++;
+
+ ap_note_cleanups_for_socket_ex(ptrans, csd, 1);
+
+ /*
+ * We now have a connection, so set it up with the appropriate
+ * socket options, file descriptors, and read/write buffers.
+ */
+
+ clen = sizeof(sa_server);
+ if (getsockname(csd, &sa_server, &clen) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "getsockname");
+ continue;
+ }
+ clen = sizeof(sa_client);
+ if ((getpeername(csd, &sa_client, &clen)) < 0) {
+ /* get peername will fail if the input isn't a socket */
+ perror("getpeername");
+ memset(&sa_client, '\0', sizeof(sa_client));
+ }
+
+ sock_disable_nagle(csd, (struct sockaddr_in *)&sa_client);
+
+ (void) ap_update_child_status(child_num, SERVER_BUSY_READ,
+ (request_rec *) NULL);
+
+ conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
+ dupped_csd = csd;
+#if defined(NEED_DUPPED_CSD)
+ if ((dupped_csd = dup(csd)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "dup: couldn't duplicate csd");
+ dupped_csd = csd; /* Oh well... */
+ }
+ ap_note_cleanups_for_socket_ex(ptrans, dupped_csd, 1);
+#endif
+ ap_bpushfd(conn_io, csd, dupped_csd);
+
+ current_conn = new_connection(ptrans, server_conf, conn_io,
+ (struct sockaddr_in *) &sa_client,
+ (struct sockaddr_in *) &sa_server,
+ child_num);
+
+ /*
+ * Read and process each request found on our connection
+ * until no requests are left or we decide to close.
+ */
+ while ((r = ap_read_request(current_conn)) != NULL) {
+ (void) ap_update_child_status(child_num, SERVER_BUSY_WRITE, r);
+
+ if (r->status == HTTP_OK)
+ ap_process_request(r);
+
+ if (ap_extended_status)
+ increment_counts(child_num, r);
+ if (!current_conn->keepalive || current_conn->aborted)
+ break;
+ /* If the server is shutting down, do not allow anymore requests
+ * to be handled on the keepalive connection. Leave the thread
+ * alive to drain the job queue. This check is particularly
+ * important on the threaded server to allow the process to be
+ * quickly taken down cleanly.
+ */
+ if (allowed_globals.exit_now)
+ break;
+ ap_destroy_pool(r->pool);
+ (void) ap_update_child_status(child_num, SERVER_BUSY_KEEPALIVE,
+ (request_rec *) NULL);
+
+ ap_sync_scoreboard_image();
+ }
+
+ /*
+ * Close the connection, being careful to send out whatever is still
+ * in our buffers. If possible, try to avoid a hard close until the
+ * client has ACKed our FIN and/or has stopped sending us data.
+ */
+ ap_kill_cleanups_for_socket(ptrans, csd);
+
+#ifdef NO_LINGCLOSE
+ ap_bclose(conn_io); /* just close it */
+#else
+ if (r && r->connection
+ && !r->connection->aborted
+ && r->connection->client
+ && (r->connection->client->fd >= 0)) {
+
+ lingering_close(r);
+ }
+ else {
+ ap_bsetflag(conn_io, B_EOUT, 1);
+ ap_bclose(conn_io);
+ }
+#endif
+ }
+ ap_destroy_pool(ptrans);
+ (void) ap_update_child_status(child_num, SERVER_DEAD, NULL);
+
+ ap_thread_count--;
+}
+
+
+#ifdef NETWARE
+void child_main(void* child_num_arg)
+#else
+void child_main(int child_num_arg)
+#endif
+{
+ /*
+ * Only reason for this function, is to pass in
+ * arguments to child_sub_main() on its stack so
+ * that longjump doesn't try to corrupt its local
+ * variables and I don't need to make those
+ * damn variables static/global
+ */
+#ifdef NETWARE
+ TSD Tsd;
+ int *thread_ptr;
+ memset(&Tsd, 0, sizeof(TSD));
+ thread_ptr = __get_thread_data_area_ptr();
+ *thread_ptr = (int) &Tsd;
+ child_sub_main((int)child_num_arg);
+#else
+ child_sub_main(child_num_arg);
+#endif
+}
+
+
+
+void cleanup_thread(thread **handles, int *thread_cnt, int thread_to_clean)
+{
+ int i;
+
+ free_thread(handles[thread_to_clean]);
+ for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
+ handles[i] = handles[i + 1];
+ (*thread_cnt)--;
+}
+#ifdef WIN32
+/*
+ * The Win32 call WaitForMultipleObjects will only allow you to wait for
+ * a maximum of MAXIMUM_WAIT_OBJECTS (current 64). Since the threading
+ * model in the multithreaded version of apache wants to use this call,
+ * we are restricted to a maximum of 64 threads. This is a simplistic
+ * routine that will increase this size.
+ */
+static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles,
+ DWORD dwSeconds)
+{
+ time_t tStopTime;
+ DWORD dwRet = WAIT_TIMEOUT;
+ DWORD dwIndex=0;
+ BOOL bFirst = TRUE;
+
+ tStopTime = time(NULL) + dwSeconds;
+
+ do {
+ if (!bFirst)
+ Sleep(1000);
+ else
+ bFirst = FALSE;
+
+ for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) {
+ dwRet = WaitForMultipleObjects(
+ min(MAXIMUM_WAIT_OBJECTS,
+ nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)),
+ lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS),
+ 0, 0);
+
+ if (dwRet != WAIT_TIMEOUT) {
+ break;
+ }
+ }
+ } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT));
+
+ return dwRet;
+}
+#endif
+/*****************************************************************
+ * Executive routines.
+ */
+
+extern void main_control_server(void *); /* in hellop.c */
+
+event *exit_event;
+mutex *start_mutex;
+
+#define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */
+char signal_name_prefix[MAX_SIGNAL_NAME];
+char signal_restart_name[MAX_SIGNAL_NAME];
+char signal_shutdown_name[MAX_SIGNAL_NAME];
+
+#define MAX_SELECT_ERRORS 100
+
+/*
+ * Initialise the signal names, in the global variables signal_name_prefix,
+ * signal_restart_name and signal_shutdown_name.
+ */
+
+void setup_signal_names(char *prefix)
+{
+ ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);
+ ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name),
+ "%s_shutdown", signal_name_prefix);
+ ap_snprintf(signal_restart_name, sizeof(signal_restart_name),
+ "%s_restart", signal_name_prefix);
+
+ APD2("signal prefix %s", signal_name_prefix);
+}
+
+#ifndef NETWARE
+static void setup_inherited_listeners(pool *p)
+{
+ HANDLE pipe;
+ listen_rec *lr;
+ int fd;
+ WSAPROTOCOL_INFO WSAProtocolInfo;
+ DWORD BytesRead;
+
+ /* Setup the listeners */
+ listenmaxfd = -1;
+ FD_ZERO(&listenfds);
+
+ /* Open the pipe to the parent process to receive the inherited socket
+ * data. The sockets have been set to listening in the parent process.
+ */
+ pipe = GetStdHandle(STD_INPUT_HANDLE);
+ for (lr = ap_listeners; lr; lr = lr->next) {
+ if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO),
+ &BytesRead, (LPOVERLAPPED) NULL)) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf,
+ "setup_inherited_listeners: Unable to read socket data from parent");
+ signal_parent(0); /* tell parent to die */
+ exit(1);
+ }
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "BytesRead = %d WSAProtocolInfo = %x20", BytesRead, WSAProtocolInfo);
+ fd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
+ &WSAProtocolInfo, 0, 0);
+ if (fd == INVALID_SOCKET) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf,
+ "setup_inherited_listeners: WSASocket failed to open the inherited socket.");
+ signal_parent(0); /* tell parent to die */
+ exit(1);
+ }
+ if (fd >= 0) {
+ FD_SET(fd, &listenfds);
+ if (fd > listenmaxfd)
+ listenmaxfd = fd;
+ }
+ ap_note_cleanups_for_socket_ex(p, fd, 1);
+ lr->fd = fd;
+ if (lr->next == NULL) {
+ /* turn the list into a ring */
+ lr->next = ap_listeners;
+ break;
+ }
+ }
+ head_listener = ap_listeners;
+ close_unused_listeners();
+ CloseHandle(pipe);
+ return;
+}
+#endif
+
+/*
+ * worker_main() is main loop for the child process. The loop in
+ * this function becomes the controlling thread for the actually working
+ * threads (which run in a loop in child_sub_main()).
+ */
+
+#ifdef NETWARE
+void worker_main(void)
+{
+ int nthreads;
+ fd_set main_fds;
+ int srv;
+ int clen;
+ int csd;
+ struct sockaddr_in sa_client;
+ thread **child_handles;
+ int rv;
+ int i;
+ struct timeval tv;
+ int my_pid;
+ int count_select_errors = 0;
+ pool *pchild;
+ module **m;
+ listen_rec* lr;
+
+
+ pchild = ap_make_sub_pool(pconf);
+
+ ap_standalone = 1;
+ sd = -1;
+ nthreads = ap_threads_per_child;
+
+ if (nthreads <= 0)
+ nthreads = 40;
+
+ my_pid = getpid();
+
+ ++ap_my_generation;
+
+ copy_listeners(pconf);
+ ap_restart_time = time(NULL);
+
+ reinit_scoreboard(pconf);
+ setup_listeners(pconf);
+
+ if (listenmaxfd == -1) {
+ /* Help, no sockets were made, better log something and exit */
+ ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL,
+ "No sockets were created for listening");
+
+ ap_destroy_pool(pchild);
+ cleanup_scoreboard();
+ exit(1);
+ }
+
+ set_signals();
+
+ /* Display listening ports */
+ printf(" Listening on port(s):");
+ lr = ap_listeners;
+ do {
+ printf(" %d", ntohs(lr->local_addr.sin_port));
+ lr = lr->next;
+ } while(lr && lr != ap_listeners);
+
+ /* Display dynamic modules loaded */
+ printf("\n");
+ for (m = ap_loaded_modules; *m != NULL; m++) {
+ if (((module*)*m)->dynamic_load_handle) {
+ printf(" Loaded dynamic module %s\n", ap_find_module_name(*m));
+ }
+ }
+
+ /*
+ * - Initialize allowed_globals
+ * - Create the thread table
+ * - Spawn off threads
+ * - Create listen socket set (done above)
+ * - loop {
+ * wait for request
+ * create new job
+ * } while (!time to exit)
+ * - Close all listeners
+ * - Wait for all threads to complete
+ * - Exit
+ */
+
+ ap_child_init_modules(pconf, server_conf);
+ allowed_globals.jobmutex = ap_create_mutex(NULL);
+ allowed_globals.jobsemaphore = create_semaphore(0);
+
+ /* spawn off the threads */
+ child_handles = (thread *) malloc(nthreads * sizeof(int));
+
+ for (i = 0; i < nthreads; i++) {
+ child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i);
+ }
+
+ if (nthreads > max_daemons_limit) {
+ max_daemons_limit = nthreads;
+ }
+
+ while (1) {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ ThreadSwitch();
+
+ if (shutdown_pending)
+ break;
+
+ memcpy(&main_fds, &listenfds, sizeof(fd_set));
+ srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
+
+ if (srv == 0) {
+ count_select_errors = 0; /* reset count of errors */
+ continue;
+ }
+ else if (srv == SOCKET_ERROR) {
+ if (h_errno != WSAEINTR) {
+ /* A "real" error occurred, log it and increment the count of
+ * select errors. This count is used to ensure we don't go into
+ * a busy loop of continuous errors.
+ */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "select failed with errno %d", h_errno);
+ count_select_errors++;
+ if (count_select_errors > MAX_SELECT_ERRORS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server_conf,
+ "Too many errors in select loop. Child process exiting.");
+ break;
+ }
+ }
+ continue;
+ } else {
+ listen_rec *lr;
+
+ lr = find_ready_listener(&main_fds);
+
+ if (lr != NULL) {
+ sd = lr->fd;
+ }
+ }
+
+ do {
+ clen = sizeof(sa_client);
+ csd = accept(sd, (struct sockaddr *) &sa_client, &clen);
+
+ if (csd == INVALID_SOCKET) {
+ csd = -1;
+ }
+ } while (csd < 0 && h_errno == WSAEINTR);
+
+ if (csd == INVALID_SOCKET) {
+ if ((h_errno != WSAECONNABORTED) && (h_errno != WSAEWOULDBLOCK)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "accept: (client socket) failed with errno = %d",h_errno);
+ }
+ }
+ else {
+ u_long one = 0;
+
+ if (soblock(csd) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "%d couldn't make socket descriptor (%d) blocking again.", h_errno, csd);
+ continue;
+ }
+ add_job(csd);
+ }
+ }
+
+ APD2("process PID %d exiting", my_pid);
+
+ /* Get ready to shutdown and exit */
+ allowed_globals.exit_now = 1;
+
+ for (i = 0; i < nthreads; i++) {
+ add_job(-1);
+ }
+
+ APD2("process PID %d waiting for worker threads to exit", my_pid);
+ while(ap_thread_count)
+ ThreadSwitch();
+
+ destroy_semaphore(allowed_globals.jobsemaphore);
+ ap_destroy_mutex(allowed_globals.jobmutex);
+
+ ap_child_exit_modules(pconf, server_conf);
+ ap_destroy_pool(pchild);
+ free(child_handles);
+ cleanup_scoreboard();
+
+ APD2("process PID %d exited", my_pid);
+ clean_parent_exit(0);
+
+}
+#else
+void worker_main(void)
+{
+ int nthreads;
+ fd_set main_fds;
+ int srv;
+ int clen;
+ int csd;
+ struct sockaddr_in sa_client;
+ int total_jobs = 0;
+ thread **child_handles;
+ int rv;
+ time_t end_time;
+ int i;
+ struct timeval tv;
+ int wait_time = 1;
+ int max_jobs_per_exe;
+ int max_jobs_after_exit_request;
+ HANDLE hObjects[2];
+
+ int count_select_errors = 0;
+ pool *pchild;
+
+ pchild = ap_make_sub_pool(pconf);
+
+ ap_standalone = 1;
+ sd = -1;
+ nthreads = ap_threads_per_child;
+ max_jobs_after_exit_request = ap_excess_requests_per_child;
+ max_jobs_per_exe = ap_max_requests_per_child;
+ if (nthreads <= 0)
+ nthreads = 40;
+ if (max_jobs_per_exe <= 0)
+ max_jobs_per_exe = 0;
+ if (max_jobs_after_exit_request <= 0)
+ max_jobs_after_exit_request = max_jobs_per_exe / 10;
+
+ if (!one_process)
+ detach();
+
+ my_pid = getpid();
+
+ ++ap_my_generation;
+
+ copy_listeners(pconf);
+ ap_restart_time = time(NULL);
+
+ reinit_scoreboard(pconf);
+
+ /*
+ * Wait until we have permission to start accepting connections.
+ * start_mutex is used to ensure that only one child ever
+ * goes into the listen/accept loop at once. Also wait on exit_event,
+ * in case we (this child) is told to die before we get a chance to
+ * serve any requests.
+ */
+ hObjects[0] = (HANDLE)start_mutex;
+ hObjects[1] = (HANDLE)exit_event;
+ rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE);
+ if (rv == WAIT_FAILED) {
+ ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "Waiting for start_mutex or exit_event -- process will exit");
+
+ ap_destroy_pool(pchild);
+ cleanup_scoreboard();
+ exit(1);
+ }
+ if (rv == WAIT_OBJECT_0 + 1) {
+ /* exit event signalled - exit now */
+ ap_destroy_pool(pchild);
+ cleanup_scoreboard();
+ exit(0);
+ }
+ /* start_mutex obtained, continue into the select() loop */
+ if (one_process) {
+ setup_listeners(pconf);
+ } else {
+ /* Get listeners from the parent process */
+ setup_inherited_listeners(pconf);
+ }
+
+ if (listenmaxfd == -1) {
+ /* Help, no sockets were made, better log something and exit */
+ ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL,
+ "No sockets were created for listening");
+
+ signal_parent(0); /* tell parent to die */
+
+ ap_destroy_pool(pchild);
+ cleanup_scoreboard();
+ exit(1);
+ }
+ set_signals();
+
+ /*
+ * - Initialize allowed_globals
+ * - Create the thread table
+ * - Spawn off threads
+ * - Create listen socket set (done above)
+ * - loop {
+ * wait for request
+ * create new job
+ * } while (!time to exit)
+ * - Close all listeners
+ * - Wait for all threads to complete
+ * - Exit
+ */
+
+ ap_child_init_modules(pconf, server_conf);
+
+ allowed_globals.jobsemaphore = create_semaphore(0);
+ allowed_globals.jobmutex = ap_create_mutex(NULL);
+
+ /* spawn off the threads */
+ child_handles = (thread *) alloca(nthreads * sizeof(int));
+ for (i = 0; i < nthreads; i++) {
+ child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i);
+ if (child_handles[i] == 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "create_thread rc = %d", errno);
+ }
+ }
+ if (nthreads > max_daemons_limit) {
+ max_daemons_limit = nthreads;
+ }
+
+ while (1) {
+ if (max_jobs_per_exe && (total_jobs > max_jobs_per_exe)) {
+ /* Reached MaxRequestsPerChild. Stop accepting new connections
+ * and signal the parent to start a new child process.
+ */
+ ap_start_restart(1);
+ break;
+ }
+ /* Always check for the exit event being signaled.
+ */
+ rv = WaitForSingleObject(exit_event, 0);
+ ap_assert((rv == WAIT_TIMEOUT) || (rv == WAIT_OBJECT_0));
+ if (rv == WAIT_OBJECT_0) {
+ APD1("child: exit event signalled, exiting");
+ break;
+ }
+
+ tv.tv_sec = wait_time;
+ tv.tv_usec = 0;
+ memcpy(&main_fds, &listenfds, sizeof(fd_set));
+
+ srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
+
+ if (srv == 0) {
+ count_select_errors = 0; /* reset count of errors */
+ continue;
+ }
+ else if (srv == SOCKET_ERROR) {
+ if (h_errno != WSAEINTR) {
+ /* A "real" error occurred, log it and increment the count of
+ * select errors. This count is used to ensure we don't go into
+ * a busy loop of continuous errors.
+ */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "select failed with errno %d", h_errno);
+ count_select_errors++;
+ if (count_select_errors > MAX_SELECT_ERRORS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server_conf,
+ "Too many errors in select loop. Child process exiting.");
+ break;
+ }
+ }
+ continue;
+ } else {
+ listen_rec *lr;
+
+ lr = find_ready_listener(&main_fds);
+ if (lr != NULL) {
+ sd = lr->fd;
+ }
+ }
+ do {
+ clen = sizeof(sa_client);
+ csd = accept(sd, (struct sockaddr *) &sa_client, &clen);
+ if (csd == INVALID_SOCKET) {
+ csd = -1;
+ }
+ } while (csd < 0 && h_errno == WSAEINTR);
+
+ if (csd < 0) {
+ if (h_errno != WSAECONNABORTED) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "accept: (client socket) failed with errno = %d",h_errno);
+ }
+ }
+ else {
+ add_job(csd);
+ total_jobs++;
+ }
+ }
+
+ APD2("process PID %d exiting", my_pid);
+
+ /* Get ready to shutdown and exit */
+ allowed_globals.exit_now = 1;
+ ap_release_mutex(start_mutex);
+
+#ifdef UNGRACEFUL_RESTART
+ SetEvent(allowed_globals.thread_exit_event);
+#else
+ for (i = 0; i < nthreads; i++) {
+ add_job(-1);
+ }
+#endif
+
+ APD2("process PID %d waiting for worker threads to exit", my_pid);
+ /* Wait for all your children */
+ end_time = time(NULL) + 180;
+ while (nthreads) {
+ rv = wait_for_many_objects(nthreads, child_handles,
+ end_time - time(NULL));
+ if (rv != WAIT_TIMEOUT) {
+ rv = rv - WAIT_OBJECT_0;
+ ap_assert((rv >= 0) && (rv < nthreads));
+ cleanup_thread(child_handles, &nthreads, rv);
+ continue;
+ }
+ break;
+ }
+
+ APD2("process PID %d killing remaining worker threads", my_pid);
+ for (i = 0; i < nthreads; i++) {
+ kill_thread(child_handles[i]);
+ free_thread(child_handles[i]);
+ }
+#ifdef UNGRACEFUL_RESTART
+ ap_assert(CloseHandle(allowed_globals.thread_exit_event));
+#endif
+ destroy_semaphore(allowed_globals.jobsemaphore);
+ ap_destroy_mutex(allowed_globals.jobmutex);
+
+ ap_child_exit_modules(pconf, server_conf);
+ ap_destroy_pool(pchild);
+
+ cleanup_scoreboard();
+
+ APD2("process PID %d exited", my_pid);
+ clean_parent_exit(0);
+} /* standalone_main */
+
+/*
+ * Spawn a child Apache process. The child process has the command line arguments from
+ * argc and argv[], plus a -Z argument giving the name of an event. The child should
+ * open and poll or wait on this event. When it is signalled, the child should die.
+ * prefix is a prefix string for the event name.
+ *
+ * The child_num argument on entry contains a serial number for this child (used to create
+ * a unique event name). On exit, this number will have been incremented by one, ready
+ * for the next call.
+ *
+ * On exit, the value pointed to be *ev will contain the event created
+ * to signal the new child process.
+ *
+ * The return value is the handle to the child process if successful, else -1. If -1 is
+ * returned the error will already have been logged by ap_log_error().
+ */
+
+/**********************************************************************
+ * master_main - this is the parent (main) process. We create a
+ * child process to do the work, then sit around waiting for either
+ * the child to exit, or a restart or exit signal. If the child dies,
+ * we just respawn a new one. If we have a shutdown or graceful restart,
+ * tell the child to die when it is ready. If it is a non-graceful
+ * restart, force the child to die immediately.
+ **********************************************************************/
+
+#define MAX_PROCESSES 50 /* must be < MAX_WAIT_OBJECTS-1 */
+
+static void cleanup_process(HANDLE *handles, HANDLE *events, int position, int *processes)
+{
+ int i;
+ int handle = 0;
+
+ CloseHandle(handles[position]);
+ CloseHandle(events[position]);
+
+ handle = (int)handles[position];
+
+ for (i = position; i < (*processes)-1; i++) {
+ handles[i] = handles[i + 1];
+ events[i] = events[i + 1];
+ }
+ (*processes)--;
+
+ APD4("cleanup_processes: removed child in slot %d handle %d, max=%d", position, handle, *processes);
+}
+
+static int create_process(pool *p, HANDLE *handles, HANDLE *events,
+ int *processes, int *child_num, char *kill_event_name, int argc, char **argv)
+{
+
+ int rv, i;
+ HANDLE kill_event;
+ char buf[1024];
+ char exit_event_name[40]; /* apPID_C# */
+ char *pCommand;
+
+ STARTUPINFO si; /* Filled in prior to call to CreateProcess */
+ PROCESS_INFORMATION pi; /* filled in on call to CreateProces */
+ LPWSAPROTOCOL_INFO lpWSAProtocolInfo;
+ listen_rec *lr;
+ DWORD BytesWritten;
+ HANDLE hPipeRead = NULL;
+ HANDLE hPipeWrite = NULL;
+ HANDLE hPipeWriteDup;
+ HANDLE hNullOutput = NULL;
+ HANDLE hShareError = NULL;
+ HANDLE hCurrentProcess;
+ SECURITY_ATTRIBUTES sa = {0};
+
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+
+ /* Build the command line. Should look something like this:
+ * C:/apache/bin/apache.exe -Z exit_event -f ap_server_confname
+ * First, get the path to the executable...
+ */
+ rv = GetModuleFileName(NULL, buf, sizeof(buf));
+ if (rv == sizeof(buf)) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Path to Apache process too long");
+ return -1;
+ } else if (rv == 0) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: GetModuleFileName() returned NULL for current process.");
+ return -1;
+ }
+
+ /* Create the exit event (apPID_C#). Parent signals this event to tell the
+ * child to exit
+ */
+ ap_snprintf(exit_event_name, sizeof(exit_event_name), "%s_C%d", kill_event_name, ++(*child_num));
+ kill_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name);
+ if (!kill_event) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Could not create exit event for child process");
+ return -1;
+ }
+
+ /* service children must be created with the -z option,
+ * while console mode (interactive apache) children are created
+ * with the -Z option
+ */
+ pCommand = ap_psprintf(p, "\"%s\" -%c %s -f \"%s\"", buf,
+ isProcessService() ? 'z' : 'Z',
+ exit_event_name, ap_server_confname);
+
+ for (i = 1; i < argc; i++) {
+ if ((argv[i][0] == '-') && ((argv[i][1] == 'k') || (argv[i][1] == 'n')))
+ ++i;
+ else
+ pCommand = ap_pstrcat(p, pCommand, " \"", argv[i], "\"", NULL);
+ }
+
+ /* Create a pipe to send socket info to the child */
+ if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Unable to create pipe to child process.\n");
+ return -1;
+ }
+
+ /* Open a null handle to soak info from the child */
+ hNullOutput = CreateFile("nul", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sa, OPEN_EXISTING, 0, NULL);
+ if (hNullOutput == INVALID_HANDLE_VALUE) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Unable to create null output pipe for child process.\n");
+ return -1;
+ }
+
+ /* Child's initial stderr -> our main server error log (or, failing that, stderr) */
+ if (server_conf->error_log) {
+ hShareError = (HANDLE)_get_osfhandle(fileno(server_conf->error_log));
+ if (hShareError == INVALID_HANDLE_VALUE) {
+ hShareError = GetStdHandle(STD_ERROR_HANDLE);
+ }
+ }
+
+ hCurrentProcess = GetCurrentProcess();
+ if (DuplicateHandle(hCurrentProcess, hPipeWrite, hCurrentProcess,
+ &hPipeWriteDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ CloseHandle(hPipeWrite);
+ hPipeWrite = hPipeWriteDup;
+ }
+
+ /* Give the read in of the pipe (hPipeRead) to the child as stdin. The
+ * parent will write the socket data to the child on this pipe.
+ */
+ memset(&si, 0, sizeof(si));
+ memset(&pi, 0, sizeof(pi));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_HIDE;
+ si.hStdInput = hPipeRead;
+ si.hStdOutput = hNullOutput;
+ si.hStdError = hShareError;
+
+ if (!CreateProcess(NULL, pCommand, NULL, NULL,
+ TRUE, /* Inherit handles */
+ 0, /* Creation flags */
+ NULL, NULL,
+ &si, &pi)) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Not able to create the child process.");
+ /*
+ * We must close the handles to the new process and its main thread
+ * to prevent handle and memory leaks.
+ */
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ CloseHandle(hPipeRead);
+ CloseHandle(hPipeWrite);
+ CloseHandle(hNullOutput);
+
+ return -1;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf,
+ "Parent: Created child process %d", pi.dwProcessId);
+
+ /* Assume the child process lives. Update the process and event tables */
+ handles[*processes] = pi.hProcess;
+ events[*processes] = kill_event;
+ (*processes)++;
+
+ /* We never store the thread's handle, so close it now. */
+ CloseHandle(pi.hThread);
+
+ /* Run the chain of open sockets. For each socket, duplicate it
+ * for the target process then send the WSAPROTOCOL_INFO
+ * (returned by dup socket) to the child */
+ lr = ap_listeners;
+ while (lr != NULL) {
+ lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf,
+ "Parent: Duplicating socket %d and sending it to child process %d", lr->fd, pi.dwProcessId);
+ if (WSADuplicateSocket(lr->fd,
+ pi.dwProcessId,
+ lpWSAProtocolInfo) == SOCKET_ERROR) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: WSADuplicateSocket failed for socket %d.", lr->fd );
+ return -1;
+ }
+
+ if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO),
+ &BytesWritten,
+ (LPOVERLAPPED) NULL)) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Unable to write duplicated socket %d to the child.", lr->fd );
+ return -1;
+ }
+
+ lr = lr->next;
+ if (lr == ap_listeners)
+ break;
+ }
+ }
+ CloseHandle(hPipeRead);
+ CloseHandle(hPipeWrite);
+ CloseHandle(hNullOutput);
+
+ return 0;
+}
+
+/* To share the semaphores with other processes, we need a NULL ACL
+ * Code from MS KB Q106387
+ */
+
+static PSECURITY_ATTRIBUTES GetNullACL()
+{
+ PSECURITY_DESCRIPTOR pSD;
+ PSECURITY_ATTRIBUTES sa;
+
+ sa = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
+ pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
+ SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (pSD == NULL || sa == NULL) {
+ return NULL;
+ }
+ /*
+ * Win98 returns nonzero on failure; check LastError to make sure.
+ */
+ SetLastError(0);
+ if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)
+ || GetLastError()) {
+ LocalFree( pSD );
+ LocalFree( sa );
+ return NULL;
+ }
+ if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)
+ || GetLastError()) {
+ LocalFree( pSD );
+ LocalFree( sa );
+ return NULL;
+ }
+ sa->nLength = sizeof(sa);
+ sa->lpSecurityDescriptor = pSD;
+ sa->bInheritHandle = TRUE;
+ return sa;
+}
+
+
+static void CleanNullACL( void *sa ) {
+ if( sa ) {
+ LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor);
+ LocalFree( sa );
+ }
+}
+
+int master_main(int argc, char **argv)
+{
+ /* returns NULL if invalid (Win95?) */
+ PSECURITY_ATTRIBUTES sa = GetNullACL();
+ int nchild = ap_daemons_to_start;
+ int child_num = 0;
+ int rv, cld;
+ char signal_prefix_string[100];
+ int i;
+ time_t tmstart;
+ HANDLE signal_shutdown_event; /* used to signal shutdown to parent */
+ HANDLE signal_restart_event; /* used to signal a restart to parent */
+ HANDLE process_handles[MAX_PROCESSES];
+ HANDLE process_kill_events[MAX_PROCESSES];
+ int current_live_processes = 0; /* number of child process we know about */
+ int processes_to_create = 0; /* number of child processes to create */
+ pool *pparent = NULL; /* pool for the parent process. Cleaned on each restart */
+
+ nchild = 1; /* only allowed one child process for current generation */
+ processes_to_create = nchild;
+
+ is_graceful = 0;
+
+ ap_snprintf(signal_prefix_string, sizeof(signal_prefix_string),
+ "ap%d", getpid());
+ setup_signal_names(signal_prefix_string);
+
+ /* Create shutdown event, apPID_shutdown, where PID is the parent
+ * Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
+ */
+ signal_shutdown_event = CreateEvent(sa, TRUE, FALSE, signal_shutdown_name);
+ if (!signal_shutdown_event) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+ "master_main: Cannot create shutdown event %s", signal_shutdown_name);
+ CleanNullACL((void *)sa);
+ exit(1);
+ }
+
+ /* Create restart event, apPID_restart, where PID is the parent
+ * Apache process ID. Restart is signaled by 'apache -k restart'.
+ */
+ signal_restart_event = CreateEvent(sa, TRUE, FALSE, signal_restart_name);
+ if (!signal_restart_event) {
+ CloseHandle(signal_shutdown_event);
+ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+ "master_main: Cannot create restart event %s", signal_restart_name);
+ CleanNullACL((void *)sa);
+ exit(1);
+ }
+ CleanNullACL((void *)sa);
+
+ /* Create the start mutex, apPID, where PID is the parent Apache process ID.
+ * Ths start mutex is used during a restart to prevent more than one
+ * child process from entering the accept loop at once.
+ */
+ start_mutex = ap_create_mutex(signal_prefix_string);
+ restart_pending = shutdown_pending = 0;
+
+ do { /* restart-pending */
+ if (!is_graceful) {
+ ap_restart_time = time(NULL);
+ }
+ copy_listeners(pconf);
+ ap_clear_pool(pconf);
+ pparent = ap_make_sub_pool(pconf);
+
+ server_conf = ap_read_config(pconf, pparent, ap_server_confname);
+ setup_listeners(pconf);
+ ap_clear_pool(plog);
+ ap_open_logs(server_conf, plog);
+ ap_set_version();
+ ap_init_modules(pconf, server_conf);
+ version_locked++;
+ service_set_status(SERVICE_START_PENDING);
+ /* Create child processes */
+ while (processes_to_create--) {
+ if (create_process(pconf, process_handles, process_kill_events,
+ &current_live_processes, &child_num, signal_prefix_string, argc, argv) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "master_main: create child process failed. Exiting.");
+ goto die_now;
+ }
+ }
+ service_set_status(SERVICE_RUNNING);
+ restart_pending = shutdown_pending = 0;
+
+ /* Wait for either the shutdown or restart events to be signaled */
+ process_handles[current_live_processes] = signal_shutdown_event;
+ process_handles[current_live_processes+1] = signal_restart_event;
+ rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE *)process_handles,
+ FALSE, INFINITE);
+ if (rv == WAIT_FAILED) {
+ /* Something serious is wrong */
+ ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, server_conf,
+ "master_main: : WaitForMultipeObjects on process handles and apache-signal -- doing shutdown");
+ shutdown_pending = 1;
+ break;
+ }
+ if (rv == WAIT_TIMEOUT) {
+ /* Hey, this cannot happen */
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT");
+ shutdown_pending = 1;
+ }
+
+ cld = rv - WAIT_OBJECT_0;
+ APD4("main process: wait finished, cld=%d handle %d (max=%d)", cld, process_handles[cld], current_live_processes);
+ if (cld == current_live_processes) {
+ /* apPID_shutdown event signalled, we should exit now */
+ shutdown_pending = 1;
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "master_main: Shutdown event signaled. Shutting the server down.");
+ if (ResetEvent(signal_shutdown_event) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "ResetEvent(signal_shutdown_event)");
+ }
+ /* Signal each child processes to die */
+ for (i = 0; i < current_live_processes; i++) {
+ APD3("master_main: signalling child %d, handle %d to die", i, process_handles[i]);
+ if (SetEvent(process_kill_events[i]) == 0)
+ ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "master_main: SetEvent for child process in slot #%d failed", i);
+ }
+ break;
+ } else if (cld == current_live_processes+1) {
+ /* apPID_restart event signalled.
+ * Signal the child to shutdown and start a new child process.
+ * The restart event can be signaled by a command line restart or
+ * by the child process when it handles MaxRequestPerChild connections.
+ */
+ int children_to_kill = current_live_processes;
+ restart_pending = 1;
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "master_main: Restart event signaled. Doing a graceful restart.");
+ if (ResetEvent(signal_restart_event) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "master_main: ResetEvent(signal_restart_event) failed.");
+ }
+ /* Signal each child process to die */
+ for (i = 0; i < children_to_kill; i++) {
+ APD3("master_main: signalling child #%d handle %d to die", i, process_handles[i]);
+ if (SetEvent(process_kill_events[i]) == 0)
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "master_main: SetEvent for child process in slot #%d failed", i);
+ /* Remove the process (and event) from the process table */
+ cleanup_process(process_handles, process_kill_events, i, &current_live_processes);
+ }
+ processes_to_create = 1;
+ ++ap_my_generation;
+ continue;
+ } else {
+ /* The child process exited premeturely because of a fatal error condition
+ * (eg, seg fault). Cleanup and restart the child process.
+ */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "master_main: Child processed exited prematurely. Restarting the child process.");
+ ap_assert(cld < current_live_processes);
+ cleanup_process(process_handles, process_kill_events, cld, &current_live_processes);
+ APD2("main_process: child in slot %d died", rv);
+ processes_to_create = 1;
+ continue;
+ }
+
+ } while (1);
+
+ /* If we dropped out of the loop we definitly want to die completely. We need to
+ * make sure we wait for all the child process to exit first.
+ */
+
+ APD2("*** main process shutdown, processes=%d ***", current_live_processes);
+
+die_now:
+
+ tmstart = time(NULL);
+ while (current_live_processes && ((tmstart+60) > time(NULL))) {
+ service_set_status(SERVICE_STOP_PENDING);
+ rv = WaitForMultipleObjects(current_live_processes, (HANDLE *)process_handles, FALSE, 2000);
+ if (rv == WAIT_TIMEOUT)
+ continue;
+ ap_assert(rv != WAIT_FAILED);
+ cld = rv - WAIT_OBJECT_0;
+ ap_assert(rv < current_live_processes);
+ APD4("main_process: child in #%d handle %d died, left=%d",
+ rv, process_handles[rv], current_live_processes);
+ cleanup_process(process_handles, process_kill_events, cld, &current_live_processes);
+ }
+ for (i = 0; i < current_live_processes; i++) {
+ ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO, server_conf,
+ "forcing termination of child #%d (handle %d)", i, process_handles[i]);
+ TerminateProcess((HANDLE) process_handles[i], 1);
+ }
+
+ CloseHandle(signal_restart_event);
+ CloseHandle(signal_shutdown_event);
+
+ /* cleanup pid file on normal shutdown */
+ {
+ const char *pidfile = NULL;
+ pidfile = ap_server_root_relative (pparent, 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());
+ }
+
+ if (pparent) {
+ ap_destroy_pool(pparent);
+ }
+
+ ap_destroy_mutex(start_mutex);
+ return (0);
+}
+#endif
+
+/*
+ * Send signal to a running Apache. On entry signal should contain
+ * either "shutdown" or "restart"
+ */
+
+int send_signal(pool *p, char *signal)
+{
+ char prefix[20];
+ FILE *fp;
+ int nread;
+ char *fname;
+ int end;
+
+ fname = ap_server_root_relative (p, ap_pid_fname);
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ printf("Cannot read apache PID file %s\n", fname);
+ return FALSE;
+ }
+ prefix[0] = 'a';
+ prefix[1] = 'p';
+
+ nread = fread(prefix+2, 1, sizeof(prefix)-3, fp);
+ if (nread == 0) {
+ fclose(fp);
+ printf("PID file %s was empty\n", fname);
+ return FALSE;
+ }
+ fclose(fp);
+
+ /* Terminate the prefix string */
+ end = 2 + nread - 1;
+ while (end > 0 && (prefix[end] == '\r' || prefix[end] == '\n'))
+ end--;
+ prefix[end + 1] = '\0';
+
+ setup_signal_names(prefix);
+
+ if (!strcasecmp(signal, "shutdown"))
+ ap_start_shutdown();
+ else if (!strcasecmp(signal, "restart"))
+ ap_start_restart(1);
+ else {
+ printf("Unknown signal name \"%s\". Use either shutdown or restart.\n",
+ signal);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void post_parse_init()
+{
+ ap_set_version();
+ ap_init_modules(pconf, server_conf);
+ ap_suexec_enabled = init_suexec();
+ version_locked++;
+ ap_open_logs(server_conf, plog);
+ set_group_privs();
+}
+
+
+#ifdef NETWARE
+extern char *optarg;
+
+void signal_handler(int sig)
+{
+ switch (sig) {
+ case SIGTERM:
+ shutdown_pending = 1;
+
+ while(!ap_main_finished)
+ ThreadSwitchWithDelay();
+
+ break;
+ }
+ return;
+}
+#endif
+
+#if defined(NETWARE)
+int apache_main(int argc, char *argv[])
+#elif defined(WIN32)
+ __declspec(dllexport)
+int apache_main(int argc, char *argv[])
+#else
+int REALMAIN(int argc, char *argv[])
+#endif
+{
+ int c;
+ int child = 0;
+ char *cp;
+ char *s;
+ int conf_specified = 0;
+
+#ifdef WIN32
+ jmp_buf reparse_args;
+ char *service_name = NULL;
+ int install = 0;
+ int reparsed = 0;
+ int is_child_of_service = 0;
+ char *signal_to_send = NULL;
+
+ /* Service application under WinNT the first time through only...
+ * service_main immediately resets real_exit_code to zero
+ */
+ if (real_exit_code && isWindowsNT())
+ {
+ if (((argc == 1) && isProcessService())
+ || ((argc == 2) && !strcmp(argv[1], "--ntservice")))
+ {
+ service_main(apache_main, argc, argv);
+ /* this was the end of the service control thread...
+ * cleanups already ran when second thread of apache_main
+ * terminated, so simply...
+ */
+ exit(0);
+ }
+ }
+
+ /* This behavior is voided by setting real_exit_code to 0 */
+ atexit(hold_console_open_on_error);
+#endif
+
+#ifdef NETWARE
+ int currentScreen = GetCurrentScreen();
+ /* If top_module is not NULL then APACHEC was not exited cleanly
+ * and is in a bad state. Simply clean up and exit.
+ */
+ check_clean_load (top_module);
+ init_name_space();
+ signal(SIGTERM, signal_handler);
+ atexit(clean_shutdown_on_exit);
+ init_tsd();
+#endif
+
+ /* Console application or a child process. */
+
+ if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) {
+ ap_server_argv0 = ++s;
+ }
+ else {
+ ap_server_argv0 = argv[0];
+ }
+
+ common_init();
+ ap_setup_prelinked_modules();
+
+ /* initialize ap_server_root to the directory of the executable, in case
+ * the user chooses a relative path for the -d serverroot arg a bit later
+ */
+
+#ifdef NETWARE
+ if(!*ap_server_root) {
+ ap_cpystrn(ap_server_root, bslash2slash(remove_filename(argv[0])),
+ sizeof(ap_server_root));
+ }
+#endif
+
+#ifdef WIN32
+ if(!*ap_server_root) {
+ if (GetModuleFileName(NULL, ap_server_root, sizeof(ap_server_root))) {
+ ap_cpystrn(ap_server_root,
+ ap_os_canonical_filename(pcommands, ap_server_root),
+ sizeof(ap_server_root));
+ if (ap_os_is_path_absolute(ap_server_root)
+ && strchr(ap_server_root, '/'))
+ *strrchr(ap_server_root, '/') = '\0';
+ else
+ *ap_server_root = '\0';
+ }
+ }
+#endif
+
+ /* Fallback position if argv[0] wasn't deciphered
+ */
+ if (!*ap_server_root)
+ ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root));
+
+ chdir (ap_server_root);
+
+#ifdef WIN32
+ /* If this is a service, we will need to fall back here and
+ * reparse the entire options list.
+ */
+ if (setjmp(reparse_args)) {
+ /* Reset and reparse the command line */
+ ap_server_pre_read_config = ap_make_array(pcommands, 1, sizeof(char *));
+ ap_server_post_read_config = ap_make_array(pcommands, 1, sizeof(char *));
+ ap_server_config_defines = ap_make_array(pcommands, 1, sizeof(char *));
+
+ /* Reset optreset and optind to allow getopt to work correctly
+ * the second time around, and assure we never come back here.
+ */
+ optreset = 1;
+ optind = 1;
+ reparsed = 1;
+ }
+
+ while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVlLz:Z:wiuStThk:n:W:")) != -1) {
+#else /* !WIN32 */
+ while ((c = getopt(argc, argv, "D:C:c:Xd:Ff:vVlLesStTh")) != -1) {
+#endif
+ char **new;
+ switch (c) {
+ case 'c':
+ new = (char **)ap_push_array(ap_server_post_read_config);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'C':
+ new = (char **)ap_push_array(ap_server_pre_read_config);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'D':
+ new = (char **)ap_push_array(ap_server_config_defines);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+#ifdef WIN32
+ /* Shortcuts; include the -w option to hold the window open on error.
+ * This must not be toggled once we reset real_exit_code to 0!
+ */
+ case 'w':
+ if (real_exit_code)
+ real_exit_code = 2;
+ break;
+ /* service children must be created with the -z option,
+ * while console mode (interactive apache) children are created
+ * with the -Z option
+ */
+ case 'z':
+ is_child_of_service = 1;
+ case 'Z':
+ /* Prevent holding open the (nonexistant) console */
+ real_exit_code = 0;
+ exit_event = open_event(optarg);
+ APD2("child: opened process event %s", optarg);
+ cp = strchr(optarg, '_');
+ ap_assert(cp);
+ *cp = 0;
+ setup_signal_names(optarg);
+ start_mutex = ap_open_mutex(signal_name_prefix);
+ ap_assert(start_mutex);
+ child = 1;
+ break;
+ case 'n':
+ service_name = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'i':
+ install = 2;
+ break;
+ case 'u':
+ install = -1;
+ break;
+ case 'k':
+ if (!strcasecmp(optarg, "stop"))
+ signal_to_send = "shutdown";
+ else if (!strcasecmp(optarg, "install"))
+ install = 2;
+ else if (!strcasecmp(optarg, "config"))
+ install = 1;
+ else if (!strcasecmp(optarg, "uninstall"))
+ install = -1;
+ else
+ signal_to_send = optarg;
+ break;
+ case 'W':
+ /* -With a dependent service */
+ if (install < 1) {
+ fprintf(stderr, "%s: invalid option: -W %s ignored\n"
+ "\t-W only modifies -k install or -k config\n",
+ argv[0], optarg);
+ }
+ else if (!isWindowsNT()) {
+ fprintf(stderr, "%s: invalid option: -W %s ignored\n"
+ "\t-W is only supported for Windows NT/2000\n",
+ argv[0], optarg);
+ }
+ break;
+#endif /* WIN32 */
+#ifdef NETWARE
+ case 'e':
+ {
+ int screenHandle;
+
+ /* Get a screen handle for the console screen. */
+ if ((screenHandle = CreateScreen("System Console", 0)) != NULL)
+ {
+ SetAutoScreenDestructionMode(1);
+ SetCurrentScreen(screenHandle); /* switch to console screen I/O */
+ }
+ }
+ break;
+ case 's':
+ if (DestroyScreen(GetCurrentScreen()) == 0)
+ {
+ int screenHandle;
+
+ /* Create a screen handle for the console screen,
+ even though the console screen exists. */
+ if ((screenHandle = CreateScreen("System Console", 0)) != NULL)
+ {
+ SetCurrentScreen(screenHandle); /* switch to console screen I/O */
+ currentScreen = GetCurrentScreen();
+ }
+ }
+ break;
+#endif
+ case 'S':
+ ap_dump_settings = 1;
+ break;
+ case 'd':
+ optarg = ap_os_canonical_filename(pcommands, optarg);
+ if (!ap_os_is_path_absolute(optarg)) {
+ optarg = ap_pstrcat(pcommands, ap_server_root, "/",
+ optarg, NULL);
+ }
+ ap_cpystrn(ap_server_root, optarg, sizeof(ap_server_root));
+ ap_getparents(ap_server_root);
+ ap_no2slash(ap_server_root);
+ if (ap_server_root[0]
+ && ap_server_root[strlen(ap_server_root) - 1] == '/')
+ ap_server_root[strlen(ap_server_root) - 1] = '\0';
+ break;
+#ifndef WIN32
+ case 'F':
+ do_detach = 0;
+ break;
+#endif
+ case 'f':
+ ap_cpystrn(ap_server_confname,
+ ap_os_canonical_filename(pcommands, optarg),
+ sizeof(ap_server_confname));
+ conf_specified = 1;
+ break;
+ case 'v':
+ ap_set_version();
+ printf("Server version: %s\n", ap_get_server_version());
+ printf("Server built: %s\n", ap_get_server_built());
+#ifdef WIN32
+ clean_parent_exit(1);
+#else
+ clean_parent_exit(0);
+#endif
+
+ case 'V':
+ ap_set_version();
+ show_compile_settings();
+#ifdef WIN32
+ clean_parent_exit(1);
+#else
+ clean_parent_exit(0);
+#endif
+
+ case 'l':
+ ap_show_modules();
+#ifdef WIN32
+ clean_parent_exit(1);
+#else
+ clean_parent_exit(0);
+#endif
+
+ case 'L':
+ ap_show_directives();
+#ifdef WIN32
+ clean_parent_exit(1);
+#else
+ clean_parent_exit(0);
+#endif
+
+ case 'X':
+ ++one_process; /* Weird debugging mode. */
+ break;
+ case 't':
+ ap_configtestonly = 1;
+ ap_docrootcheck = 1;
+ break;
+ case 'T':
+ ap_configtestonly = 1;
+ ap_docrootcheck = 0;
+ break;
+ case 'h':
+ usage(ap_server_argv0);
+ case '?':
+ usage(ap_server_argv0);
+ } /* switch */
+#ifdef NETWARE
+ ThreadSwitch();
+#endif
+ } /* while */
+
+#ifdef WIN32
+
+ if (!service_name && install) {
+ service_name = DEFAULTSERVICENAME;
+ }
+
+ if (service_name) {
+ service_name = get_display_name(service_name);
+ }
+
+ if (service_name && isValidService(service_name))
+ {
+ if (install == 2) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+ "Service \"%s\" is already installed!", service_name);
+ clean_parent_exit(1);
+ }
+ /* Don't proceed if we are configuring, uninstalling
+ * or already merged and reparsed the service args
+ */
+ if (!install && !reparsed)
+ {
+ int svcargc;
+ char **newargv, **svcargv;
+ if (ap_configtestonly)
+ fprintf(stderr, "Default command options for service %s:\n",
+ service_name);
+
+ /* Merge the service's default args */
+ if (ap_registry_get_service_args(pcommands, &svcargc, &svcargv,
+ service_name) > 0) {
+ newargv = (char**)malloc((svcargc + argc + 1) * sizeof(char*));
+ newargv[0] = argv[0]; /* The true executable name */
+ memcpy(newargv + 1, svcargv, svcargc * sizeof(char*));
+ memcpy(newargv + 1 + svcargc, argv + 1,
+ (argc - 1) * sizeof(char*));
+ argc += svcargc; /* Add the startup options args */
+ argv = newargv;
+ argv[argc] = NULL;
+
+ if (ap_configtestonly) {
+ while (svcargc-- > 0) {
+ if ((**svcargv == '-') && strchr("dfDCc", svcargv[0][1])
+ && svcargc) {
+ fprintf(stderr, " %s %s\n",
+ *svcargv, *(svcargv + 1));
+ svcargv += 2; --svcargc;
+ }
+ else
+ fprintf(stderr, " %s\n", *(svcargv++));
+ }
+ }
+ /* Run through the command line args all over again */
+ longjmp(reparse_args, 1);
+ }
+ else if (ap_configtestonly)
+ fprintf (stderr, " (none)\n");
+ }
+ }
+ else if (service_name && (install <= 1))
+ {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+ "Service \"%s\" is not installed!", service_name);
+ clean_parent_exit(1);
+ }
+#endif
+
+ /* ServerRoot/ServerConfFile are found in this order:
+ * (1) serverroot set to Apache.exe's path, or HTTPD_ROOT if unparsable
+ * (2) arguments are grabbed for the -n named service, if given
+ * (3) the -d argument is taken from the given command line
+ * (4) the -d argument is taken from the service's default args
+ * (5) the -f argument is taken from the given command line
+ * (6) the -f argument is taken from the service's default args
+ * (7) if -f is omitted, then initialized to SERVER_CONFIG_FILE
+ * (8) if ap_server_confname is not absolute, then merge it to serverroot
+ */
+
+ if (!conf_specified)
+ ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname));
+
+ if (!ap_os_is_path_absolute(ap_server_confname))
+ ap_cpystrn(ap_server_confname,
+ ap_server_root_relative(pcommands, ap_server_confname),
+ sizeof(ap_server_confname));
+ ap_getparents(ap_server_confname);
+ ap_no2slash(ap_server_confname);
+
+#ifdef WIN32
+ /* Read the conf now unless we are uninstalling the service,
+ * or shutting down a running service
+ * (but do read the conf for the pidfile if we shutdown the console)
+ */
+ if ((install >= 0) && (!service_name || !signal_to_send
+ || strcasecmp(signal_to_send,"shutdown"))) {
+ server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+ }
+
+ if (install) {
+ if (install > 0)
+ InstallService(pconf, service_name, argc, argv, install == 1);
+ else
+ RemoveService(service_name);
+ clean_parent_exit(0);
+ }
+
+ /* All NT signals, and all but the 9x start signal are handled entirely.
+ * Die if we failed, are on NT, or are not "start"ing the service
+ */
+ if (service_name && signal_to_send) {
+ if (send_signal_to_service(service_name, signal_to_send, argc, argv))
+ clean_parent_exit(0);
+ if (isWindowsNT() || strcasecmp(signal_to_send, "start"))
+ clean_parent_exit(1);
+ /* Still here? Then we are hanging around to detach the console
+ * and use this process as the Windows 9x service.
+ */
+ }
+#else /* ndef WIN32 */
+ server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+#endif
+
+ if (ap_configtestonly) {
+ fprintf(stderr, "%s: Syntax OK\n", ap_server_root_relative(pcommands, ap_server_confname));
+#ifdef WIN32
+ clean_parent_exit(1);
+#else
+ clean_parent_exit(0);
+#endif
+ }
+
+ if (ap_dump_settings) {
+#ifdef WIN32
+ clean_parent_exit(1);
+#else
+ clean_parent_exit(0);
+#endif
+ }
+
+#ifdef WIN32
+ /* Non-service Signals. (Ignore -k start for now [with or without -n arg]) */
+ if (signal_to_send && strcasecmp(signal_to_send, "start")) {
+ send_signal(pconf, signal_to_send);
+ clean_parent_exit(0);
+ }
+#endif
+
+#ifndef NETWARE
+ if (!child && !ap_dump_settings) {
+ ap_log_pid(pconf, ap_pid_fname);
+ }
+#endif
+
+ post_parse_init();
+
+#if defined(OS2)
+ printf("%s running...\n", ap_get_server_version());
+#elif defined(WIN32)
+ if (!child) {
+ printf("%s running...\n", ap_get_server_version());
+ }
+#elif defined(NETWARE)
+ if (currentScreen != GetCurrentScreen()) {
+ SetCurrentScreen(currentScreen); /* switch to console screen I/O */
+ SetAutoScreenDestructionMode(0);
+ }
+
+ printf("%s running...\n", ap_get_server_version());
+#endif
+
+#ifndef NETWARE
+ if (one_process && !exit_event)
+ exit_event = create_event(0, 0, NULL);
+ if (one_process && !start_mutex)
+ start_mutex = ap_create_mutex(NULL);
+#endif
+
+#ifdef NETWARE
+ worker_main();
+ destroy_semaphore(allowed_globals.jobsemaphore);
+
+ while((ap_thread_count) || (!shutdown_pending))
+ ThreadSwitchWithDelay();
+#else
+ /*
+ * In the future, the main will spawn off a couple
+ * of children and monitor them. As soon as a child
+ * exits, it spawns off a new one
+ */
+ if (child || one_process) {
+ if (!exit_event || !start_mutex)
+ exit(-1);
+#ifdef WIN32
+ if (child)
+ ap_start_child_console(is_child_of_service);
+ else
+ ap_start_console_monitor();
+#endif
+ worker_main();
+ ap_destroy_mutex(start_mutex);
+ destroy_event(exit_event);
+ }
+#ifdef WIN32
+ /* Windows NT service second time around ... we have all the overrides
+ * from the NT SCM, so go to town and return to the SCM when we quit.
+ */
+ if (isWindowsNT() && isProcessService())
+ {
+ master_main(argc, argv);
+ }
+ else if (service_name && signal_to_send && !isWindowsNT()
+ && !strcasecmp(signal_to_send, "start")) {
+ /* service95_main will call master_main() */
+ service95_main(master_main, argc, argv, service_name);
+ }
+ else
+ {
+ /* Let's go fishing for some signals including ctrl+c, ctrl+break,
+ * logoff, close and shutdown, while the server is running
+ */
+ ap_start_console_monitor();
+ master_main(argc, argv);
+ }
+#else /* ndef WIN32 */
+ else
+ {
+ master_main(argc, argv);
+ }
+#endif /* ndef WIN32 */
+#endif /* ndef NETWARE */
+
+ clean_parent_exit(0);
+ return 0; /* purely to avoid a warning */
+}
+
+#endif /* ndef MULTITHREAD */
+
+#else /* ndef SHARED_CORE_TIESTATIC */
+
+/*
+** Standalone Tie Program for Shared Core support
+**
+** It's purpose is to tie the static libraries and
+** the shared core library under link-time and
+** passing execution control to the real main function
+** in the shared core library under run-time.
+*/
+
+extern int ap_main(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+ return ap_main(argc, argv);
+}
+
+#endif /* ndef SHARED_CORE_TIESTATIC */
+#else /* ndef SHARED_CORE_BOOTSTRAP */
+
+#if defined(OS2) || defined(CYGWIN)
+/* Shared core loader for OS/2 and Cygwin */
+
+#if defined(CYGWIN)
+__declspec(dllimport)
+#endif
+
+
+int ap_main(int argc, char *argv[]); /* Load time linked from cyghttpd.dll */
+
+int main(int argc, char *argv[])
+{
+ return ap_main(argc, argv);
+}
+
+#else
+
+/*
+** Standalone Bootstrap Program for Shared Core support
+**
+** It's purpose is to initialise the LD_LIBRARY_PATH
+** environment variable therewith the Unix loader is able
+** to start the Standalone Tie Program (see above)
+** and then replacing itself with this program by
+** immediately passing execution to it.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ap_config.h"
+#include "httpd.h"
+
+#if defined(HPUX) || defined(HPUX10) || defined(HPUX11)
+#define VARNAME "SHLIB_PATH"
+#else
+#define VARNAME "LD_LIBRARY_PATH"
+#endif
+
+#ifndef SHARED_CORE_DIR
+#define SHARED_CORE_DIR HTTPD_ROOT "/libexec"
+#endif
+
+#ifndef SHARED_CORE_EXECUTABLE_PROGRAM
+#define SHARED_CORE_EXECUTABLE_PROGRAM "lib" TARGET ".ep"
+#endif
+
+extern char *optarg;
+extern int optind;
+
+int main(int argc, char *argv[], char *envp[])
+{
+ char prog[MAX_STRING_LEN];
+ char llp_buf[MAX_STRING_LEN];
+ char **llp_slot;
+ char *llp_existing;
+ char *llp_dir;
+ char **envpnew;
+ int c, i, l;
+
+#ifdef MPE
+ /*
+ * MPE doesn't currently initialize the envp parameter. Instead, we must
+ * use the global variable environ.
+ */
+ envp = environ;
+#endif
+
+ /*
+ * parse argument line,
+ * but only handle the -L option
+ */
+ llp_dir = SHARED_CORE_DIR;
+ while ((c = getopt(argc, argv, "D:C:c:Xd:Ff:vVlLR:SZ:tTh")) != -1) {
+ switch (c) {
+ case 'D':
+ case 'C':
+ case 'c':
+ case 'X':
+ case 'd':
+ case 'F':
+ case 'f':
+ case 'v':
+ case 'V':
+ case 'l':
+ case 'L':
+ case 'S':
+ case 'Z':
+ case 't':
+ case 'T':
+ case 'h':
+ case '?':
+ break;
+ case 'R':
+ llp_dir = strdup(optarg);
+ break;
+ }
+ }
+
+#ifdef MPE
+ /*
+ * MPE doesn't currently initialize the envp parameter. Instead, we must
+ * use the global variable environ.
+ */
+ envp = environ;
+#endif
+
+ /*
+ * create path to SHARED_CORE_EXECUTABLE_PROGRAM
+ */
+ ap_snprintf(prog, sizeof(prog), "%s/%s", llp_dir, SHARED_CORE_EXECUTABLE_PROGRAM);
+
+ /*
+ * adjust process environment therewith the Unix loader
+ * is able to start the SHARED_CORE_EXECUTABLE_PROGRAM.
+ */
+ llp_slot = NULL;
+ llp_existing = NULL;
+ l = strlen(VARNAME);
+ for (i = 0; envp[i] != NULL; i++) {
+ if (strncmp(envp[i], VARNAME "=", l+1) == 0) {
+ llp_slot = &envp[i];
+ llp_existing = strchr(envp[i], '=') + 1;
+ }
+ }
+ if (llp_slot == NULL) {
+ envpnew = (char **)malloc(sizeof(char *)*(i + 2));
+ if (envpnew == NULL) {
+ fprintf(stderr, "Ouch! Out of memory generating envpnew!\n");
+ }
+ memcpy(envpnew, envp, sizeof(char *)*i);
+ envp = envpnew;
+ llp_slot = &envp[i++];
+ envp[i] = NULL;
+ }
+ if (llp_existing != NULL)
+ ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s:%s", VARNAME, llp_dir, llp_existing);
+ else
+ ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s", VARNAME, llp_dir);
+ *llp_slot = strdup(llp_buf);
+
+ /*
+ * finally replace our process with
+ * the SHARED_CORE_EXECUTABLE_PROGRAM
+ */
+ if (execve(prog, argv, envp) == -1) {
+ fprintf(stderr,
+ "%s: Unable to exec Shared Core Executable Program `%s'\n",
+ argv[0], prog);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+#endif /* def OS2 */
+#endif /* ndef SHARED_CORE_BOOTSTRAP */
+
+#ifndef SHARED_CORE_BOOTSTRAP
+#include "httpd.h"
+/*
+ * Force ap_validate_password() into the image so that modules like
+ * mod_auth can use it even if they're dynamically loaded.
+ */
+void suck_in_ap_validate_password(void);
+void suck_in_ap_validate_password(void)
+{
+ ap_validate_password("a", "b");
+}
+#endif
+
+/* force Expat to be linked into the server executable */
+#if defined(USE_EXPAT) && !defined(SHARED_CORE_BOOTSTRAP)
+#include "xmlparse.h"
+const XML_LChar *suck_in_expat(void);
+const XML_LChar *suck_in_expat(void)
+{
+ return XML_ErrorString(XML_ERROR_NONE);
+}
+#endif /* USE_EXPAT */
+