diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/.gdbinit apachen2/src/.gdbinit --- apachen/src/.gdbinit Sat Apr 18 21:20:30 1998 +++ apachen2/src/.gdbinit Sat Apr 18 22:02:44 1998 @@ -13,3 +13,12 @@ document dump_table Print the key/value pairs in a table. end + +handle SIGUSR1 noprint nostop +handle SIGUSR2 noprint nostop + +handle SIGUSR1 noprint nostop +handle SIGUSR2 noprint nostop + +handle SIGUSR1 noprint nostop +handle SIGUSR2 noprint nostop diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/Configure apachen2/src/Configure --- apachen/src/Configure Mon Apr 20 12:11:30 1998 +++ apachen2/src/Configure Mon Apr 20 12:13:08 1998 @@ -409,6 +409,7 @@ OS='Linux' CFLAGS="$CFLAGS -DLINUX=2" LIBS="$LIBS -lm" + OSDIR="os/pthread" ;; *-linux1) DEF_WANTHSREGEX=yes diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/Makefile.tmpl apachen2/src/Makefile.tmpl --- apachen/src/Makefile.tmpl Sat Apr 18 21:20:39 1998 +++ apachen2/src/Makefile.tmpl Mon Apr 20 12:15:20 1998 @@ -106,9 +106,9 @@ $(OBJS): Makefile # DO NOT REMOVE -buildmark.o: buildmark.c include/conf.h os/unix/os.h include/hsregex.h \ - include/httpd.h include/alloc.h include/buff.h include/ap.h \ - include/util_uri.h -modules.o: modules.c include/httpd.h include/conf.h os/unix/os.h \ +buildmark.o: buildmark.c include/conf.h os/pthread/os.h \ + include/hsregex.h include/httpd.h include/alloc.h include/buff.h \ + include/ap.h include/util_uri.h +modules.o: modules.c include/httpd.h include/conf.h os/pthread/os.h \ include/hsregex.h include/alloc.h include/buff.h include/ap.h \ include/util_uri.h include/http_config.h diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/ap/Makefile.tmpl apachen2/src/ap/Makefile.tmpl --- apachen/src/ap/Makefile.tmpl Sat Apr 18 21:20:40 1998 +++ apachen2/src/ap/Makefile.tmpl Mon Apr 20 12:15:22 1998 @@ -39,21 +39,21 @@ # DO NOT REMOVE ap_cpystrn.o: ap_cpystrn.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h ap_execve.o: ap_execve.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h ap_signal.o: ap_signal.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h ap_slack.o: ap_slack.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_log.h ap_snprintf.o: ap_snprintf.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h ap_strings.o: ap_strings.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/include/conf.h apachen2/src/include/conf.h --- apachen/src/include/conf.h Sat Apr 18 21:20:42 1998 +++ apachen2/src/include/conf.h Sat Apr 18 22:02:44 1998 @@ -378,6 +378,10 @@ /* flock is faster ... but hasn't been tested on 1.x systems */ #define USE_FLOCK_SERIALIZED_ACCEPT +#ifdef LTHREAD +#include +#endif + #else #define USE_FCNTL_SERIALIZED_ACCEPT #endif @@ -1114,6 +1118,11 @@ #endif #ifdef NEED_DIFFTIME extern double difftime(time_t time1, time_t time0); +#endif + +#ifdef LTHREAD +#undef SIGUSR1 +#define SIGUSR1 SIGWINCH #endif #endif /* !APACHE_CONF_H */ diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/include/http_config.h apachen2/src/include/http_config.h --- apachen/src/include/http_config.h Sat Apr 18 21:20:42 1998 +++ apachen2/src/include/http_config.h Sat Apr 18 22:02:44 1998 @@ -281,6 +281,11 @@ API_EXPORT(void *) ap_get_module_config(void *conf_vector, module *m); API_EXPORT(void) ap_set_module_config(void *conf_vector, module *m, void *val); +#define ap_get_module_config(v,m) \ + (((void **)(v))[(m)->module_index]) +#define ap_set_module_config(v,m,val) \ + ((((void **)(v))[(m)->module_index]) = (val)) + /* Generic command handling function... */ API_EXPORT_NONSTD(const char *) ap_set_string_slot(cmd_parms *, char *, char *); diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/include/httpd.h apachen2/src/include/httpd.h --- apachen/src/include/httpd.h Sat Apr 18 21:20:43 1998 +++ apachen2/src/include/httpd.h Sun Apr 19 00:24:29 1998 @@ -58,6 +58,14 @@ #ifndef APACHE_HTTPD_H #define APACHE_HTTPD_H +#ifdef TOP_FUEL +#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#define BUFFERED_LOGS +#define DYNAMIC_MODULE_LIMIT 0 +#define SHARED_TIME +#define NO_GRACEFUL +#endif + /* * httpd.h: header for simple (ha! not anymore) http daemon */ @@ -816,8 +824,8 @@ extern API_VAR_EXPORT const char ap_month_snames[12][4]; extern API_VAR_EXPORT const char ap_day_snames[7][4]; -API_EXPORT(struct tm *) ap_get_gmtoff(int *tz); -API_EXPORT(char *) ap_get_time(void); +API_EXPORT(struct tm *) ap_get_gmtoff(int *tz, struct tm *); +API_EXPORT(char *) ap_get_time(char *); API_EXPORT(char *) ap_ht_time(pool *p, time_t t, const char *fmt, int gmt); API_EXPORT(char *) ap_gm_timestr_822(pool *p, time_t t); @@ -970,6 +978,14 @@ #define OPTIMIZE_TIMEOUTS #endif +#ifdef SHARED_TIME +#ifndef OPTIMIZE_TIMEOUTS +# error "you need OPTIMIZE_TIMEOUTS for SHARED_TIME" +#endif +extern time_t ap_shared_time(time_t *); +#define time(x) ap_shared_time(x) +#endif + /* A set of flags which indicate places where the server should raise(SIGSTOP). * This is useful for debugging, because you can then attach to that process * with gdb and continue. This is important in cases where one_process @@ -997,5 +1013,13 @@ #undef strtoul #endif #define strtoul strtoul_is_not_a_portable_function_use_strtol_instead + + +#ifdef LTHREAD +#define DPRINTF(x) fprintf x +#else +#define DPRINTF(x) do {} while(0) +#endif + #endif /* !APACHE_HTTPD_H */ diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/include/multithread.h apachen2/src/include/multithread.h --- apachen/src/include/multithread.h Sat Apr 18 21:20:43 1998 +++ apachen2/src/include/multithread.h Mon Apr 20 12:02:05 1998 @@ -41,6 +41,72 @@ int reset_event(event *event_id); void destroy_event(event *event_id); +#elif defined(LTHREAD) + +typedef struct { + pthread_mutex_t px; + pid_t pid; +} lthread_mutex_t; + +extern inline mutex *ap_create_mutex(char *name); +extern inline mutex *ap_create_mutex(char *name) +{ + static const pthread_mutex_t def = PTHREAD_MUTEX_INITIALIZER; + lthread_mutex_t *mx; + int rc; + + mx = malloc(sizeof(*mx)); + if (mx == NULL) { + fprintf(stderr, "out of memory!\n"); + abort(); + } + + mx->px = def; + mx->pid = 0; + if ((rc = pthread_mutex_init(&mx->px, NULL))) { + fprintf(stderr, "pthread_mutex_init: %s\n", strerror(rc)); + abort(); + } + + return mx; +} + +extern inline int ap_acquire_mutex(mutex *vmx); +extern inline int ap_acquire_mutex(mutex *vmx) +{ + lthread_mutex_t *mx = vmx; + pid_t me = getpid(); + + if (mx->pid == me) { + return MULTI_OK; + } + if (pthread_mutex_lock(&mx->px)) { + return MULTI_ERR; + } + mx->pid = me; + return MULTI_OK; +} + +extern inline int ap_release_mutex(mutex *vmx); +extern inline int ap_release_mutex(mutex *vmx) +{ + lthread_mutex_t *mx = vmx; + + mx->pid = 0; + if (pthread_mutex_unlock(&mx->px)) { + return MULTI_ERR; + } + return MULTI_OK; +} + +extern inline int ap_destroy_mutex(mutex *vmx); +extern inline int ap_destroy_mutex(mutex *vmx) +{ + lthread_mutex_t *mx = vmx; + pthread_mutex_destroy(&mx->px); + free(mx); +} + #else /* ndef MULTITHREAD */ #define APACHE_TLS diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/include/scoreboard.h apachen2/src/include/scoreboard.h --- apachen/src/include/scoreboard.h Sat Apr 18 21:20:43 1998 +++ apachen2/src/include/scoreboard.h Sat Apr 18 22:02:44 1998 @@ -134,6 +134,9 @@ typedef struct { int exit_generation; /* Set by the main process if a graceful restart is required */ +#ifdef SHARED_TIME + time_t right_now; /* the current time */ +#endif } global_score; /* stuff which the parent generally writes and the children rarely read */ diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/main/Makefile.tmpl apachen2/src/main/Makefile.tmpl --- apachen/src/main/Makefile.tmpl Sat Apr 18 21:20:43 1998 +++ apachen2/src/main/Makefile.tmpl Mon Apr 20 12:15:25 1998 @@ -60,28 +60,28 @@ $(OBJS): Makefile # DO NOT REMOVE -alloc.o: alloc.c $(INCDIR)/httpd.h $(INCDIR)/conf.h ../os/unix/os.h \ - $(INCDIR)/hsregex.h $(INCDIR)/alloc.h $(INCDIR)/buff.h \ - $(INCDIR)/ap.h $(INCDIR)/util_uri.h $(INCDIR)/multithread.h \ - $(INCDIR)/http_log.h -buff.o: buff.c $(INCDIR)/httpd.h $(INCDIR)/conf.h ../os/unix/os.h \ +alloc.o: alloc.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/multithread.h $(INCDIR)/http_log.h +buff.o: buff.c $(INCDIR)/httpd.h $(INCDIR)/conf.h ../os/pthread/os.h \ $(INCDIR)/hsregex.h $(INCDIR)/alloc.h $(INCDIR)/buff.h \ $(INCDIR)/ap.h $(INCDIR)/util_uri.h $(INCDIR)/http_main.h \ $(INCDIR)/http_log.h -fnmatch.o: fnmatch.c $(INCDIR)/conf.h ../os/unix/os.h \ +fnmatch.o: fnmatch.c $(INCDIR)/conf.h ../os/pthread/os.h \ $(INCDIR)/hsregex.h $(INCDIR)/fnmatch.h gen_test_char.o: gen_test_char.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h gen_uri_delims.o: gen_uri_delims.c http_config.o: http_config.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_core.h $(INCDIR)/http_log.h \ $(INCDIR)/http_request.h $(INCDIR)/http_conf_globals.h \ $(INCDIR)/http_vhost.h $(INCDIR)/explain.h http_core.o: http_core.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_core.h \ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ @@ -90,20 +90,13 @@ $(INCDIR)/util_md5.h $(INCDIR)/md5.h $(INCDIR)/scoreboard.h \ $(INCDIR)/fnmatch.h http_log.o: http_log.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_core.h $(INCDIR)/http_log.h \ $(INCDIR)/http_main.h -http_main.o: http_main.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ - $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ - $(INCDIR)/http_main.h $(INCDIR)/http_log.h $(INCDIR)/http_config.h \ - $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ - $(INCDIR)/http_conf_globals.h $(INCDIR)/http_core.h \ - $(INCDIR)/http_vhost.h $(INCDIR)/util_script.h \ - $(INCDIR)/scoreboard.h $(INCDIR)/multithread.h $(INCDIR)/explain.h +http_main.o: http_main.c http_protocol.o: http_protocol.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_core.h \ $(INCDIR)/http_protocol.h $(INCDIR)/http_main.h \ @@ -111,36 +104,36 @@ $(INCDIR)/http_log.h $(INCDIR)/util_date.h \ $(INCDIR)/http_conf_globals.h http_request.o: http_request.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_request.h \ $(INCDIR)/http_core.h $(INCDIR)/http_protocol.h \ $(INCDIR)/http_log.h $(INCDIR)/http_main.h $(INCDIR)/scoreboard.h \ $(INCDIR)/fnmatch.h http_vhost.o: http_vhost.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ $(INCDIR)/http_log.h $(INCDIR)/http_vhost.h \ $(INCDIR)/http_protocol.h -md5c.o: md5c.c $(INCDIR)/conf.h ../os/unix/os.h $(INCDIR)/hsregex.h \ - $(INCDIR)/md5.h +md5c.o: md5c.c $(INCDIR)/conf.h ../os/pthread/os.h \ + $(INCDIR)/hsregex.h $(INCDIR)/md5.h rfc1413.o: rfc1413.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_log.h $(INCDIR)/rfc1413.h $(INCDIR)/http_main.h -util.o: util.c $(INCDIR)/httpd.h $(INCDIR)/conf.h ../os/unix/os.h \ +util.o: util.c $(INCDIR)/httpd.h $(INCDIR)/conf.h ../os/pthread/os.h \ $(INCDIR)/hsregex.h $(INCDIR)/alloc.h $(INCDIR)/buff.h \ $(INCDIR)/ap.h $(INCDIR)/util_uri.h $(INCDIR)/http_conf_globals.h \ - $(INCDIR)/http_log.h test_char.h -util_date.o: util_date.c $(INCDIR)/conf.h ../os/unix/os.h \ + $(INCDIR)/http_log.h $(INCDIR)/multithread.h test_char.h +util_date.o: util_date.c $(INCDIR)/conf.h ../os/pthread/os.h \ $(INCDIR)/hsregex.h $(INCDIR)/util_date.h util_md5.o: util_md5.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/util_md5.h $(INCDIR)/md5.h util_script.o: util_script.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ $(INCDIR)/http_main.h $(INCDIR)/http_log.h \ @@ -148,6 +141,6 @@ $(INCDIR)/http_request.h $(INCDIR)/util_script.h \ $(INCDIR)/util_date.h util_uri.o: util_uri.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_log.h $(INCDIR)/http_conf_globals.h uri_delims.h diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/main/alloc.c apachen2/src/main/alloc.c --- apachen/src/main/alloc.c Sat Apr 18 21:20:43 1998 +++ apachen2/src/main/alloc.c Mon Apr 20 12:01:03 1998 @@ -459,10 +459,10 @@ { ap_block_alarms(); - (void) ap_acquire_mutex(alloc_mutex); + //(void) ap_acquire_mutex(alloc_mutex); while (a->sub_pools) ap_destroy_pool(a->sub_pools); - (void) ap_release_mutex(alloc_mutex); + //(void) ap_release_mutex(alloc_mutex); /* Don't hold the mutex during cleanups. */ run_cleanups(a->cleanups); a->cleanups = NULL; @@ -865,7 +865,7 @@ *ps.vbuff.curpos++ = '\0'; ptr = ps.base; /* shrink */ - ptr = realloc(ptr, ps.vbuff.curpos - ptr); + ptr = realloc(ptr, ps.vbuff.curpos - (char *)ptr); if (ptr == NULL) { fputs("Ouch! Out of memory!\n", stderr); exit(1); @@ -1492,7 +1492,9 @@ int save_errno; ap_block_alarms(); - fd = open(name, flg, mode); + do { + fd = open(name, flg, mode); + } while (fd == -1 && errno == EINTR); save_errno = errno; if (fd >= 0) { fd = ap_slack(fd, AP_SLACK_HIGH); diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/main/http_config.c apachen2/src/main/http_config.c --- apachen/src/main/http_config.c Sat Apr 18 21:20:45 1998 +++ apachen2/src/main/http_config.c Mon Apr 20 12:01:39 1998 @@ -115,17 +115,21 @@ * overridden). */ +#ifndef ap_get_module_config API_EXPORT(void *) ap_get_module_config(void *conf_vector, module *m) { void **confv = (void **) conf_vector; return confv[m->module_index]; } +#endif +#ifndef ap_set_module_config API_EXPORT(void) ap_set_module_config(void *conf_vector, module *m, void *val) { void **confv = (void **) conf_vector; confv[m->module_index] = val; } +#endif static void *create_empty_config(pool *p) { diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/main/http_core.c apachen2/src/main/http_core.c --- apachen/src/main/http_core.c Mon Apr 20 12:11:34 1998 +++ apachen2/src/main/http_core.c Mon Apr 20 12:11:50 1998 @@ -1542,7 +1542,7 @@ if (err != NULL) return err; ap_daemons_min_free = atoi (arg); - if (ap_daemons_min_free <= 0) { + if (ap_daemons_min_free < 0) { fprintf(stderr, "WARNING: detected MinSpareServers set to non-positive.\n"); fprintf(stderr, "Resetting to 1 to avoid almost certain Apache failure.\n"); fprintf(stderr, "Please read the documentation.\n"); diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/main/http_log.c apachen2/src/main/http_log.c --- apachen/src/main/http_log.c Mon Apr 20 12:11:35 1998 +++ apachen2/src/main/http_log.c Mon Apr 20 12:11:50 1998 @@ -315,7 +315,12 @@ } if (logf) { - len = ap_snprintf(errstr, sizeof(errstr), "[%s] ", ap_get_time()); + errstr[0] = '['; + ap_get_time(errstr+1); + len = strlen(errstr); + errstr[len++] = ']'; + errstr[len++] = ' '; + errstr[len] = 0; } else { len = 0; } @@ -445,8 +450,10 @@ API_EXPORT(void) ap_log_assert (const char *szExp, const char *szFile, int nLine) { + char timebuf[MAX_STRING_LEN]; + fprintf(stderr, "[%s] file %s, line %d, assertion \"%s\" failed\n", - ap_get_time(), szFile, nLine, szExp); + ap_get_time(timebuf), szFile, nLine, szExp); #ifndef WIN32 /* unix assert does an abort leading to a core dump */ abort(); diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/main/http_main.c apachen2/src/main/http_main.c --- apachen/src/main/http_main.c Sat Apr 18 21:45:41 1998 +++ apachen2/src/main/http_main.c Mon Apr 20 12:42:10 1998 @@ -1,3 +1,4 @@ +#ifndef LTHREAD /* ==================================================================== * Copyright (c) 1995-1998 The Apache Group. All rights reserved. * @@ -5308,3 +5309,9 @@ #endif /* ndef SHARED_CORE_BOOTSTRAP */ +#else + +extern void http_main_is_not_here(void); +void http_main_is_not_here(void) {} + +#endif diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/main/http_protocol.c apachen2/src/main/http_protocol.c --- apachen/src/main/http_protocol.c Sat Apr 18 21:20:50 1998 +++ apachen2/src/main/http_protocol.c Mon Apr 20 12:01:50 1998 @@ -654,7 +654,7 @@ } } /* we've probably got something to do, ignore graceful restart requests */ -#ifdef SIGUSR1 +#if defined(SIGUSR1) && !defined(NO_GRACEFUL) signal(SIGUSR1, SIG_IGN); #endif /* SIGUSR1 */ ap_bsetflag(conn->client, B_SAFEREAD, 0); diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/main/util.c apachen2/src/main/util.c --- apachen/src/main/util.c Sat Apr 18 21:20:52 1998 +++ apachen2/src/main/util.c Sat Apr 18 22:54:44 1998 @@ -71,6 +71,8 @@ #include "httpd.h" #include "http_conf_globals.h" /* for user_id & group_id */ #include "http_log.h" +#include "multithread.h" + #if defined(SUNOS4) /* stdio.h has been read in conf.h already. Add missing prototypes here: */ extern int fgetc(FILE *); @@ -108,13 +110,13 @@ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; -API_EXPORT(char *) ap_get_time() +API_EXPORT(char *) ap_get_time(char *buf) { time_t t; char *time_string; t = time(NULL); - time_string = ctime(&t); + time_string = ctime_r(&t, buf); time_string[strlen(time_string) - 1] = '\0'; return (time_string); } @@ -123,41 +125,47 @@ { char ts[MAX_STRING_LEN]; struct tm *tms; + char *res; + struct tm tmstore; - tms = (gmt ? gmtime(&t) : localtime(&t)); + tms = (gmt ? gmtime_r(&t, &tmstore) : localtime_r(&t, &tmstore)); /* check return code? */ strftime(ts, MAX_STRING_LEN, fmt, tms); ts[MAX_STRING_LEN - 1] = '\0'; - return ap_pstrdup(p, ts); + res = ap_pstrdup(p, ts); + return res; } API_EXPORT(char *) ap_gm_timestr_822(pool *p, time_t sec) { struct tm *tms; + char *res; + struct tm tmstore; - tms = gmtime(&sec); + tms = gmtime_r(&sec, &tmstore); /* RFC date format; as strftime '%a, %d %b %Y %T GMT' */ - return ap_psprintf(p, + res = ap_psprintf(p, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[tms->tm_wday], tms->tm_mday, ap_month_snames[tms->tm_mon], tms->tm_year + 1900, tms->tm_hour, tms->tm_min, tms->tm_sec); + return res; } /* What a pain in the ass. */ #if defined(HAVE_GMTOFF) -API_EXPORT(struct tm *) ap_get_gmtoff(int *tz) +API_EXPORT(struct tm *) ap_get_gmtoff(int *tz, struct tm *tmstore) { time_t tt = time(NULL); struct tm *t; - t = localtime(&tt); + t = localtime_r(&tt, tmstore); *tz = (int) (t->tm_gmtoff / 60); return t; } #else -API_EXPORT(struct tm *) ap_get_gmtoff(int *tz) +API_EXPORT(struct tm *) ap_get_gmtoff(int *tz, struct tm *tmstore) { time_t tt = time(NULL); struct tm gmt; @@ -165,8 +173,8 @@ int days, hours, minutes; /* Assume we are never more than 24 hours away. */ - gmt = *gmtime(&tt); /* remember gmtime/localtime return ptr to static */ - t = localtime(&tt); /* buffer... so be careful */ + gmtime_r(&tt, &gmt); /* remember gmtime/localtime return ptr to static */ + t = localtime_r(&tt, tmstore); /* buffer... so be careful */ days = t->tm_yday - gmt.tm_yday; hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) + t->tm_hour - gmt.tm_hour); diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/modules/proxy/proxy_util.c apachen2/src/modules/proxy/proxy_util.c --- apachen/src/modules/proxy/proxy_util.c Sat Apr 18 21:20:58 1998 +++ apachen2/src/modules/proxy/proxy_util.c Sat Apr 18 22:02:44 1998 @@ -769,9 +769,10 @@ void ap_proxy_log_uerror(const char *routine, const char *file, const char *err, server_rec *s) { + char timebuf[MAX_STRING_LEN]; char *p, *q; - q = ap_get_time(); + q = ap_get_time(timebuf); p = strerror(errno); if (err != NULL) { diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/modules/standard/Makefile.tmpl apachen2/src/modules/standard/Makefile.tmpl --- apachen/src/modules/standard/Makefile.tmpl Sat Apr 18 21:20:58 1998 +++ apachen2/src/modules/standard/Makefile.tmpl Mon Apr 20 12:15:32 1998 @@ -16,184 +16,184 @@ # DO NOT REMOVE mod_access.o: mod_access.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_core.h $(INCDIR)/http_config.h \ $(INCDIR)/http_log.h $(INCDIR)/http_request.h mod_actions.o: mod_actions.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_request.h $(INCDIR)/http_core.h \ $(INCDIR)/http_protocol.h $(INCDIR)/http_main.h \ $(INCDIR)/http_log.h $(INCDIR)/util_script.h mod_alias.o: mod_alias.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h mod_asis.o: mod_asis.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_protocol.h \ $(INCDIR)/http_log.h $(INCDIR)/util_script.h \ $(INCDIR)/http_main.h $(INCDIR)/http_request.h mod_auth.o: mod_auth.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_core.h \ $(INCDIR)/http_log.h $(INCDIR)/http_protocol.h mod_auth_anon.o: mod_auth_anon.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_core.h $(INCDIR)/http_log.h \ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h mod_auth_db.o: mod_auth_db.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_core.h $(INCDIR)/http_log.h \ $(INCDIR)/http_protocol.h mod_auth_dbm.o: mod_auth_dbm.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_core.h $(INCDIR)/http_log.h \ $(INCDIR)/http_protocol.h mod_autoindex.o: mod_autoindex.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_core.h $(INCDIR)/http_request.h \ $(INCDIR)/http_protocol.h $(INCDIR)/http_log.h \ $(INCDIR)/http_main.h $(INCDIR)/util_script.h mod_cern_meta.o: mod_cern_meta.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/util_script.h $(INCDIR)/http_log.h \ $(INCDIR)/http_request.h mod_cgi.o: mod_cgi.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_request.h \ $(INCDIR)/http_core.h $(INCDIR)/http_protocol.h \ $(INCDIR)/http_main.h $(INCDIR)/http_log.h \ $(INCDIR)/util_script.h $(INCDIR)/http_conf_globals.h mod_digest.o: mod_digest.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_core.h \ $(INCDIR)/http_log.h $(INCDIR)/http_protocol.h \ $(INCDIR)/util_md5.h $(INCDIR)/md5.h mod_dir.o: mod_dir.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_core.h \ $(INCDIR)/http_request.h $(INCDIR)/http_protocol.h \ $(INCDIR)/http_log.h $(INCDIR)/http_main.h \ $(INCDIR)/util_script.h mod_env.o: mod_env.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h mod_expires.o: mod_expires.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_log.h mod_headers.o: mod_headers.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h mod_imap.o: mod_imap.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_request.h \ $(INCDIR)/http_core.h $(INCDIR)/http_protocol.h \ $(INCDIR)/http_main.h $(INCDIR)/http_log.h \ $(INCDIR)/util_script.h mod_include.o: mod_include.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_request.h $(INCDIR)/http_core.h \ $(INCDIR)/http_protocol.h $(INCDIR)/http_log.h \ $(INCDIR)/http_main.h $(INCDIR)/util_script.h mod_info.o: mod_info.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_core.h \ $(INCDIR)/http_log.h $(INCDIR)/http_main.h \ $(INCDIR)/http_protocol.h $(INCDIR)/util_script.h \ $(INCDIR)/http_conf_globals.h mod_log_agent.o: mod_log_agent.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h mod_log_config.o: mod_log_config.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_core.h $(INCDIR)/http_log.h mod_log_referer.o: mod_log_referer.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h mod_mime.o: mod_mime.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h mod_mime_magic.o: mod_mime_magic.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_request.h $(INCDIR)/http_core.h \ $(INCDIR)/http_log.h $(INCDIR)/http_protocol.h mod_negotiation.o: mod_negotiation.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_request.h $(INCDIR)/http_core.h \ $(INCDIR)/http_log.h $(INCDIR)/util_script.h mod_rewrite.o: mod_rewrite.c mod_rewrite.h $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_request.h $(INCDIR)/http_core.h \ $(INCDIR)/http_log.h $(INCDIR)/http_vhost.h mod_setenvif.o: mod_setenvif.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_core.h $(INCDIR)/http_log.h mod_so.o: mod_so.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_log.h mod_speling.o: mod_speling.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_core.h \ $(INCDIR)/http_config.h $(INCDIR)/http_log.h mod_status.o: mod_status.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ - ../../os/unix/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ $(INCDIR)/http_config.h $(INCDIR)/http_core.h \ $(INCDIR)/http_protocol.h $(INCDIR)/http_main.h \ $(INCDIR)/util_script.h $(INCDIR)/scoreboard.h \ $(INCDIR)/http_log.h mod_unique_id.o: mod_unique_id.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_log.h $(INCDIR)/multithread.h mod_userdir.o: mod_userdir.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h mod_usertrack.o: mod_usertrack.c $(INCDIR)/httpd.h \ - $(INCDIR)/conf.h ../../os/unix/os.h $(INCDIR)/hsregex.h \ + $(INCDIR)/conf.h ../../os/pthread/os.h $(INCDIR)/hsregex.h \ $(INCDIR)/alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \ $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \ $(INCDIR)/http_core.h diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/modules/standard/mod_cgi.c apachen2/src/modules/standard/mod_cgi.c --- apachen/src/modules/standard/mod_cgi.c Sat Apr 18 21:21:01 1998 +++ apachen2/src/modules/standard/mod_cgi.c Sat Apr 18 22:02:44 1998 @@ -168,6 +168,7 @@ { FILE *f; struct stat finfo; + char timebuf[MAX_STRING_LEN]; ap_log_error(APLOG_MARK, show_errno|APLOG_ERR, r->server, "%s, reason: %s", error, r->filename); @@ -181,7 +182,7 @@ } /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ - fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri, + fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(timebuf), r->method, r->uri, r->args ? "?" : "", r->args ? r->args : "", r->protocol); /* "%% 500 /usr/local/apache/cgi-bin */ fprintf(f, "%%%% %d %s\n", ret, r->filename); @@ -216,7 +217,7 @@ } /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ - fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri, + fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(argsbuffer), r->method, r->uri, r->args ? "?" : "", r->args ? r->args : "", r->protocol); /* "%% 500 /usr/local/apache/cgi-bin" */ fprintf(f, "%%%% %d %s\n", ret, r->filename); diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/modules/standard/mod_include.c apachen2/src/modules/standard/mod_include.c --- apachen/src/modules/standard/mod_include.c Sat Apr 18 21:21:05 1998 +++ apachen2/src/modules/standard/mod_include.c Sat Apr 18 22:02:44 1998 @@ -94,6 +94,13 @@ #include "util_script.h" #endif +#ifdef TOP_FUEL +#ifdef ap_chdir_file +#undef ap_chdir_file +#endif +#define ap_chdir_file(x) do { } while(0) +#endif + #define STARTING_SEQUENCE "" #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]" @@ -113,31 +120,57 @@ /* ------------------------ Environment function -------------------------- */ -static void add_include_vars(request_rec *r, char *timefmt) +static const char INCLUDE_TIME_FORMAT[] = "INCLUDE_TIME_FORMAT"; + +static const char *get_envvar(request_rec *r, const char *name) { + const char *res; + table *e; + + e = r->subprocess_env; + res = ap_table_get(e, name); + if (res) { + return res; + } + if (!strcmp(name, "LAST_MODIFIED")) { + res = ap_ht_time(r->pool, r->finfo.st_mtime, + ap_table_get(e, INCLUDE_TIME_FORMAT), 0); + } + else if (!strcmp(name, "DATE_LOCAL")) { + res = ap_ht_time(r->pool, r->request_time, + ap_table_get(e, INCLUDE_TIME_FORMAT), 0); + } + else if (!strcmp(name, "DATE_GMT")) { + res = ap_ht_time(r->pool, r->request_time, + ap_table_get(e, INCLUDE_TIME_FORMAT), 1); + } #ifndef WIN32 - struct passwd *pw; + else if (!strcmp(name, "USER_NAME")) { + struct passwd *pw; + pw = getpwuid(r->finfo.st_uid); + if (pw) { + res = ap_pstrdup(r->pool, pw->pw_name); + } + else { + res = ap_psprintf(r->pool, "user#%lu", + (unsigned long) r->finfo.st_uid); + } + } #endif /* ndef WIN32 */ + if (res) { + ap_table_set(e, name, res); + } + return res; +} + +static void add_include_vars(request_rec *r) +{ table *e = r->subprocess_env; char *t; - time_t date = r->request_time; - ap_table_setn(e, "DATE_LOCAL", ap_ht_time(r->pool, date, timefmt, 0)); - ap_table_setn(e, "DATE_GMT", ap_ht_time(r->pool, date, timefmt, 1)); - ap_table_setn(e, "LAST_MODIFIED", - ap_ht_time(r->pool, r->finfo.st_mtime, timefmt, 0)); + ap_table_setn(e, INCLUDE_TIME_FORMAT, DEFAULT_TIME_FORMAT); ap_table_setn(e, "DOCUMENT_URI", r->uri); ap_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info); -#ifndef WIN32 - pw = getpwuid(r->finfo.st_uid); - if (pw) { - ap_table_setn(e, "USER_NAME", ap_pstrdup(r->pool, pw->pw_name)); - } - else { - ap_table_setn(e, "USER_NAME", ap_psprintf(r->pool, "user#%lu", - (unsigned long) r->finfo.st_uid)); - } -#endif /* ndef WIN32 */ if ((t = strrchr(r->filename, '/'))) { ap_table_setn(e, "DOCUMENT_NAME", ++t); @@ -536,7 +569,7 @@ memcpy(var, start_of_var_name, l); var[l] = '\0'; - val = ap_table_get(r->subprocess_env, var); + val = get_envvar(r, var); if (val) { expansion = val; l = strlen(expansion); @@ -879,7 +912,7 @@ return 1; } if (!strcmp(tag, "var")) { - char *val = ap_table_get(r->subprocess_env, tag_val); + const char *val = get_envvar(r, tag_val); if (val) { ap_rputs(val, r); @@ -934,16 +967,26 @@ } #endif + +static void set_timefmt(request_rec *r, char *value) +{ + table *e = r->subprocess_env; + + ap_table_setn(e, INCLUDE_TIME_FORMAT, ap_pstrdup(r->pool, value)); + ap_table_unset(e, "DATE_LOCAL"); + ap_table_unset(e, "DATE_GMT"); + ap_table_unset(e, "LAST_MODIFIED"); +} + + /* error and tf must point to a string with room for at * least MAX_STRING_LEN characters */ -static int handle_config(FILE *in, request_rec *r, char *error, char *tf, - int *sizefmt) +static int handle_config(FILE *in, request_rec *r, char *error, int *sizefmt) { char tag[MAX_STRING_LEN]; char *tag_val; char parsed_string[MAX_STRING_LEN]; - table *env = r->subprocess_env; while (1) { if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0))) { @@ -953,13 +996,8 @@ parse_string(r, tag_val, error, MAX_STRING_LEN, 0); } else if (!strcmp(tag, "timefmt")) { - time_t date = r->request_time; - - parse_string(r, tag_val, tf, MAX_STRING_LEN, 0); - ap_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, tf, 0)); - ap_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, tf, 1)); - ap_table_setn(env, "LAST_MODIFIED", - ap_ht_time(r->pool, r->finfo.st_mtime, tf, 0)); + parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0); + set_timefmt(r, parsed_string); } else if (!strcmp(tag, "sizefmt")) { parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); @@ -1072,7 +1110,7 @@ } } -static int handle_flastmod(FILE *in, request_rec *r, const char *error, const char *tf) +static int handle_flastmod(FILE *in, request_rec *r, const char *error) { char tag[MAX_STRING_LEN]; char *tag_val; @@ -1089,7 +1127,8 @@ else { parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) { - ap_rputs(ap_ht_time(r->pool, finfo.st_mtime, tf, 0), r); + ap_rputs(ap_ht_time(r->pool, finfo.st_mtime, + ap_table_get(r->subprocess_env, INCLUDE_TIME_FORMAT), 0), r); } } } @@ -2018,7 +2057,12 @@ return -1; } parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); - ap_table_setn(r->subprocess_env, var, ap_pstrdup(r->pool, parsed_string)); + if (strcmp(var, INCLUDE_TIME_FORMAT)) { + ap_table_setn(r->subprocess_env, var, ap_pstrdup(r->pool, parsed_string)); + } + else { + set_timefmt(r, parsed_string); + } } else { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server, @@ -2041,6 +2085,11 @@ return 1; } else if (!strcmp(tag, "done")) { + /* force these to be defined */ + get_envvar(r, "DATE_LOCAL"); + get_envvar(r, "DATE_GMT"); + get_envvar(r, "LAST_MODIFIED"); + get_envvar(r, "USER_NAME"); for (i = 0; i < arr->nelts; ++i) { ap_rvputs(r, elts[i].key, "=", elts[i].val, "\n", NULL); } @@ -2064,7 +2113,6 @@ static void send_parsed_content(FILE *f, request_rec *r) { char directive[MAX_STRING_LEN], error[MAX_STRING_LEN]; - char timefmt[MAX_STRING_LEN]; int noexec = ap_allow_options(r) & OPT_INCNOEXEC; int ret, sizefmt; int if_nesting; @@ -2072,7 +2120,6 @@ int conditional_status; ap_cpystrn(error, DEFAULT_ERROR_MSG, sizeof(error)); - ap_cpystrn(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt)); sizefmt = SIZEFMT_KMG; /* Turn printing on */ @@ -2151,7 +2198,7 @@ } } else if (!strcmp(directive, "config")) { - ret = handle_config(f, r, error, timefmt, &sizefmt); + ret = handle_config(f, r, error, &sizefmt); } else if (!strcmp(directive, "set")) { ret = handle_set(f, r, error); @@ -2166,7 +2213,7 @@ ret = handle_fsize(f, r, error, sizefmt); } else if (!strcmp(directive, "flastmod")) { - ret = handle_flastmod(f, r, error, timefmt); + ret = handle_flastmod(f, r, error); } else if (!strcmp(directive, "printenv")) { ret = handle_printenv(f, r, error); @@ -2311,7 +2358,7 @@ * environment */ ap_add_common_vars(r); ap_add_cgi_vars(r); - add_include_vars(r, DEFAULT_TIME_FORMAT); + add_include_vars(r); } /* XXX: this is bogus, at some point we're going to do a subrequest, * and when we do it we're going to be subjecting code that doesn't diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/modules/standard/mod_log_config.c apachen2/src/modules/standard/mod_log_config.c --- apachen/src/modules/standard/mod_log_config.c Sat Apr 18 21:21:06 1998 +++ apachen2/src/modules/standard/mod_log_config.c Sun Apr 19 00:02:59 1998 @@ -370,9 +370,10 @@ { int timz; struct tm *t; + struct tm tmstore; char tstr[MAX_STRING_LEN]; - t = ap_get_gmtoff(&timz); + t = ap_get_gmtoff(&timz, &tmstore); if (a && *a) { /* Custom format */ strftime(tstr, MAX_STRING_LEN, a, t); diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/modules/standard/mod_rewrite.c apachen2/src/modules/standard/mod_rewrite.c --- apachen/src/modules/standard/mod_rewrite.c Sat Apr 18 21:21:13 1998 +++ apachen2/src/modules/standard/mod_rewrite.c Sat Apr 18 22:55:22 1998 @@ -2976,10 +2976,11 @@ { int timz; struct tm *t; + struct tm tmstore; char tstr[80]; char sign; - t = ap_get_gmtoff(&timz); + t = ap_get_gmtoff(&timz, &tmstore); sign = (timz < 0 ? '-' : '+'); if(timz < 0) timz = -timz; diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/os/pthread/Makefile.tmpl apachen2/src/os/pthread/Makefile.tmpl --- apachen/src/os/pthread/Makefile.tmpl Wed Dec 31 16:00:00 1969 +++ apachen2/src/os/pthread/Makefile.tmpl Mon Apr 20 12:15:20 1998 @@ -0,0 +1,52 @@ +CFLAGS=$(OPTIM) $(CFLAGS1) $(EXTRA_CFLAGS) +LIBS=$(EXTRA_LIBS) $(LIBS1) +INCLUDES=$(INCLUDES1) $(INCLUDES0) $(EXTRA_INCLUDES) +LDFLAGS=$(LDFLAGS1) $(EXTRA_LDFLAGS) + +OBJS= os.o os-inline.o http_main.o + +LIB= libos.a + +all: $(LIB) + +$(LIB): $(OBJS) + rm -f $@ + ar cr $@ $(OBJS) + $(RANLIB) $@ + +.c.o: + $(CC) -c $(INCLUDES) $(CFLAGS) $(SPACER) $< + +clean: + rm -f $(OBJS) $(LIB) + +distclean: clean + -rm -f Makefile + +# We really don't expect end users to use this rule. It works only with +# gcc, and rebuilds Makefile.tmpl. You have to re-run Configure after +# using it. +depend: + cp Makefile.tmpl Makefile.tmpl.bak \ + && sed -ne '1,/^# DO NOT REMOVE/p' Makefile.tmpl > Makefile.new \ + && gcc -MM $(INCLUDES) $(CFLAGS) *.c >> Makefile.new \ + && sed -e '1,$$s: $(INCDIR)/: $$(INCDIR)/:g' Makefile.new \ + > Makefile.tmpl \ + && rm Makefile.new + +$(OBJS): Makefile + +# DO NOT REMOVE +http_main.o: http_main.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \ + ../../os/pthread/os.h $(INCDIR)/hsregex.h $(INCDIR)/alloc.h \ + $(INCDIR)/buff.h $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_log.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_protocol.h \ + $(INCDIR)/http_request.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_core.h $(INCDIR)/http_vhost.h \ + $(INCDIR)/util_script.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/multithread.h $(INCDIR)/explain.h +os-inline.o: os-inline.c $(INCDIR)/conf.h ../../os/pthread/os.h \ + $(INCDIR)/hsregex.h +os.o: os.c $(INCDIR)/conf.h ../../os/pthread/os.h \ + $(INCDIR)/hsregex.h os.h diff -Nru -x CVS -x *.orig -x .#* -x *.bak -x Makefile apachen/src/os/pthread/http_main.c apachen2/src/os/pthread/http_main.c --- apachen/src/os/pthread/http_main.c Wed Dec 31 16:00:00 1969 +++ apachen2/src/os/pthread/http_main.c Mon Apr 20 12:02:15 1998 @@ -0,0 +1,3105 @@ +/* ==================================================================== + * Copyright (c) 1995-1998 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * 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 +#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 +#ifdef USE_SHMGET_SCOREBOARD +#include +#include +#include +#endif +#ifdef SecureWare +#include +#include +#include +#endif +#include + +#ifdef HAVE_BSTRING_H +#include /* for IRIX, FD_SET calls bzero() */ +#endif + +/* 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. + */ +extern void ap_force_library_loading(void); +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 + +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?) + */ + +int ap_standalone; +uid_t ap_user_id; +char *ap_user_name; +gid_t ap_group_id; +#ifdef MULTIPLE_GROUPS +gid_t group_id_list[NGROUPS_MAX]; +#endif +int ap_max_requests_per_child; +int ap_threads_per_child; +int ap_excess_requests_per_child; +char *ap_pid_fname; +char *ap_scoreboard_fname; +char *ap_lock_fname; +char *ap_server_argv0; +struct in_addr ap_bind_address; +int ap_daemons_to_start; +int ap_daemons_min_free; +int ap_daemons_max_free; +int ap_daemons_limit; +time_t ap_restart_time; +int ap_suexec_enabled = 0; +int ap_listenbacklog; + +/* + * 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; + +API_VAR_EXPORT char ap_server_root[MAX_STRING_LEN]; +char ap_server_confname[MAX_STRING_LEN]; +char ap_coredump_dir[MAX_STRING_LEN]; + +array_header *ap_server_pre_read_config; +array_header *ap_server_post_read_config; + +/* stuff that needs thread local store in main */ +typedef struct { + JMP_BUF jmpbuffer; + int my_pid; /* it seems silly to call getpid all the time */ + int volatile exit_after_unblock; + conn_rec *volatile current_conn; + request_rec *volatile timeout_req; + const char *volatile timeout_name; + int volatile alarms_blocked; + int volatile alarm_pending; + void (*volatile alarm_fn) (int); + listen_rec *head_listener; + int sd; + pool *pchild; /* Pool for httpd child stuff */ +#ifndef MULTITHREAD + int my_child_num; +#endif + int volatile usr1_just_die; + int volatile deferred_die; +#ifndef MULTITHREAD + /* The following vars are static to avoid getting clobbered by longjmp(); + * they are really private to child_main. */ + int srv; + int csd; + int dupped_csd; + int requests_this_child; + fd_set main_fds; +#endif +} tls_main_t; + +static pthread_key_t tls_main_key; +#define tls() ((tls_main_t *)pthread_getspecific(tls_main_key)) + +/* *Non*-shared http_main globals... */ + +static server_rec *server_conf; +static fd_set listenfds; +static int listenmaxfd; +static pid_t pgrp; + +/* 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; + +/* set if timeouts are to be handled by the children and not by the parent. + * i.e. child_timeouts = !standalone || one_process. + */ +static int child_timeouts; + +#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 *, int); + void *data; + int write_fd; +}; +static other_child_rec *other_children; +#endif + +static pool *pconf; /* Pool for config stuff */ +static pool *ptemp; /* Pool for temporary config stuff */ +static pool *pcommands; /* Pool for -C and -c switches */ + +scoreboard *ap_scoreboard_image = NULL; + +static pthread_attr_t child_attr; + +/* a clean exit from a child with proper cleanup */ +static void __attribute__((noreturn)) clean_child_exit(int code) +{ + if (tls()->pchild) { + ap_child_exit_modules(tls()->pchild, server_conf); + ap_destroy_pool(tls()->pchild); + } + pthread_exit((void *)code); +} + +/* Default --- no serialization. Other methods *could* go here, + * as #elifs... + */ +/* Multithreaded systems don't complete between processes for + * the sockets. */ +#define NO_SERIALIZED_ACCEPT +#define accept_mutex_child_init(x) +#define accept_mutex_init(x) +#define accept_mutex_on() +#define accept_mutex_off() + +/* 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]; + int i; + + for (i = 0; i < strlen(bin); i++) + pad[i] = ' '; + pad[i] = '\0'; +#ifdef SHARED_CORE + fprintf(stderr, "Usage: %s [-L directory] [-d directory] [-f file]\n", bin); +#else + fprintf(stderr, "Usage: %s [-d directory] [-f file]\n", bin); +#endif + fprintf(stderr, " %s [-C \"directive\"] [-c \"directive\"]\n", pad); + fprintf(stderr, " %s [-v] [-V] [-h] [-l]\n", pad); + fprintf(stderr, "Options:\n"); +#ifdef SHARED_CORE + fprintf(stderr, " -L directory : specify an alternate location for shared object files\n"); +#endif + 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 configuration directives\n"); + fprintf(stderr, " -l : list compiled-in modules\n"); + exit(1); +} + +/***************************************************************** + * + * 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 void timeout(int sig) +{ /* Also called on SIGPIPE */ + void *dirconf; + + signal(SIGPIPE, SIG_IGN); /* Block SIGPIPE */ + if (tls()->alarms_blocked) { + tls()->alarm_pending = 1; + return; + } + if (tls()->exit_after_unblock) { + clean_child_exit(0); + } + + if (!tls()->current_conn) { + ap_longjmp(tls()->jmpbuffer, 1); + } + + if (tls()->timeout_req != NULL) + dirconf = tls()->timeout_req->per_dir_config; + else + dirconf = tls()->current_conn->server->lookup_defaults; + if (!tls()->current_conn->keptalive) { + if (sig == SIGPIPE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + tls()->current_conn->server, + "%s client stopped connection before %s completed", + ap_get_remote_host(tls()->current_conn, dirconf, REMOTE_NAME), + tls()->timeout_name ? tls()->timeout_name : "request"); + } + else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + tls()->current_conn->server, + "%s timed out for %s", + tls()->timeout_name ? tls()->timeout_name : "request", + ap_get_remote_host(tls()->current_conn, dirconf, REMOTE_NAME)); + } + } + + if (tls()->timeout_req) { + /* Someone has asked for this transaction to just be aborted + * if it times out... + */ + + request_rec *log_req = tls()->timeout_req; + + while (log_req->main || log_req->prev) { + /* Get back to original request... */ + if (log_req->main) + log_req = log_req->main; + else + log_req = log_req->prev; + } + + if (!tls()->current_conn->keptalive) + ap_log_transaction(log_req); + + ap_bsetflag(tls()->timeout_req->connection->client, B_EOUT, 1); + ap_bclose(tls()->timeout_req->connection->client); + + if (!ap_standalone) + exit(0); + + ap_longjmp(tls()->jmpbuffer, 1); + } + else { /* abort the connection */ + ap_bsetflag(tls()->current_conn->client, B_EOUT, 1); + tls()->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() +{ + ++tls()->alarms_blocked; +} + +API_EXPORT(void) ap_unblock_alarms() +{ + --tls()->alarms_blocked; + if (tls()->alarms_blocked == 0) { + if (tls()->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 tls()->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. + */ + ++tls()->alarms_blocked; + tls()->exit_after_unblock = 0; + clean_child_exit(0); + } + if (tls()->alarm_pending) { + tls()->alarm_pending = 0; + timeout(0); + } + } +} + + +static void alrm_handler(int sig) +{ + if (tls()->alarm_fn) { + (*tls()->alarm_fn) (sig); + } +} + +unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x) +{ + unsigned int old; + + if (x) { + tls()->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. + */ + old = ap_scoreboard_image->servers[tls()->my_child_num].timeout_len; + ap_scoreboard_image->servers[tls()->my_child_num].timeout_len = x; + ++ap_scoreboard_image->servers[tls()->my_child_num].cur_vtime; + } +#endif + return (old); +} + + +/* 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; + + if (tls()->timeout_name) { /* timeout has been set */ + i = ap_set_callback_and_alarm(tls()->alarm_fn, r->server->timeout); + if (i == 0) /* timeout already expired, so set it back to 0 */ + ap_set_callback_and_alarm(tls()->alarm_fn, 0); + } +} + +void ap_keepalive_timeout(char *name, request_rec *r) +{ + unsigned int to; + + tls()->timeout_req = r; + tls()->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) +{ + tls()->timeout_req = r; + tls()->timeout_name = name; + + ap_set_callback_and_alarm(timeout, r->server->timeout); + +} + +API_EXPORT(void) ap_soft_timeout(char *name, request_rec *r) +{ + tls()->timeout_name = name; + + ap_set_callback_and_alarm(timeout, r->server->timeout); + +} + +API_EXPORT(void) ap_kill_timeout(request_rec *dummy) +{ + ap_set_callback_and_alarm(NULL, 0); + tls()->timeout_req = NULL; + tls()->timeout_name = NULL; +} + +#ifdef SHARED_TIME +#undef time +time_t ap_shared_time(time_t *t) +{ + time_t res; + + if (child_timeouts || ap_scoreboard_image == NULL) { + return time(0); + } + res = ap_scoreboard_image->global.right_now; + if (t) *t = res; + return res; +} +#endif + +/* + * 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 conf.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) +{ + if (tls()->alarms_blocked) { + tls()->alarm_pending = 1; + return; + } + + if (!tls()->current_conn) { + ap_longjmp(tls()->jmpbuffer, 1); + } + ap_bsetflag(tls()->current_conn->client, B_EOUT, 1); + tls()->current_conn->aborted = 1; +} + +static void linger_timeout(void) +{ + tls()->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[2048]; + struct timeval tv; + fd_set lfds, fds_read, fds_err; + int select_rv = 0; + 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); + FD_SET(lsd, &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 exception on lsd, 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. + */ + tv.tv_sec = 2; + tv.tv_usec = 0; + fds_read = lfds; + fds_err = lfds; + + select_rv = ap_select(lsd + 1, &fds_read, NULL, &fds_err, &tv); + } while ((select_rv > 0) && /* Something to see on socket */ + !FD_ISSET(lsd, &fds_err) && /* that isn't an error condition */ + FD_ISSET(lsd, &fds_read) && /* and is worth trying to read */ + (read(lsd, dummybuf, sizeof dummybuf) > 0)); + + /* 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 *, int status), + void *data, int write_fd) +{ + other_child_rec *ocr; + + ocr = ap_palloc(pconf, sizeof(*ocr)); + ocr->pid = pid; + ocr->maintenance = maintenance; + ocr->data = data; + ocr->write_fd = write_fd; + ocr->next = other_children; + other_children = ocr; +} + +/* note that since this can be called by a maintenance function while we're + * scanning the other_children list, all scanners should protect themself + * by loading ocr->next before calling any maintenance function. + */ +API_EXPORT(void) ap_unregister_other_child(void *data) +{ + other_child_rec **pocr, *nocr; + + for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) { + if ((*pocr)->data == data) { + nocr = (*pocr)->next; + (*(*pocr)->maintenance) (OC_REASON_UNREGISTER, (*pocr)->data, -1); + *pocr = nocr; + /* XXX: um, well we've just wasted some space in pconf ? */ + return; + } + } +} + +/* test to ensure that the write_fds are all still writable, otherwise + * invoke the maintenance functions as appropriate */ +static void probe_writable_fds(void) +{ + 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); + 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; + + do { + tv.tv_sec = 0; + tv.tv_usec = 0; + rc = ap_select(fd_max + 1, NULL, &writable_fds, NULL, &tv); + } while (rc == -1 && errno == EINTR); + + if (rc == -1) { + /* XXX: uhh this could be really bad, we could have a bad file + * descriptor due to a bug in one of the maintenance routines */ + ap_log_unixerr("probe_writable_fds", "select", + "could not probe writable fds", server_conf); + return; + } + if (rc == 0) + return; + + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->write_fd == -1) + continue; + if (FD_ISSET(ocr->write_fd, &writable_fds)) + continue; + (*ocr->maintenance) (OC_REASON_UNWRITABLE, ocr->data, -1); + } +} + +/* possibly reap an other_child, return 0 if yes, -1 if not */ +static int reap_other_child(int pid, int 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... + */ + +static void reinit_scoreboard(pool *p) +{ + ap_assert(!ap_scoreboard_image); + ap_scoreboard_image = (scoreboard *) malloc(SCOREBOARD_SIZE); + memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); +} + +API_EXPORT(void) ap_sync_scoreboard_image() +{ +} + +static void reopen_scoreboard(pool *p) +{ +} + + +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) +{ +} + +/* a clean exit from the parent with proper cleanup */ +static void __attribute__((noreturn)) clean_parent_exit(int code) +{ + /* Clear the pool - including any registered cleanups */ + ap_destroy_pool(pconf); + exit(code); +} + +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_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + old_status = ss->status; + ss->status = status; +#ifdef OPTIMIZE_TIMEOUTS + ++ss->cur_vtime; +#endif + +#if defined(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; + } + 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)); + ap_cpystrn(ss->request, (r->the_request ? r->the_request : + "NULL"), sizeof(ss->request)); + ap_cpystrn(ss->vhost, r->server->server_hostname, sizeof(ss->vhost)); + } +#endif + + put_scoreboard_info(child_num, ss); + + return old_status; +} + +static void update_scoreboard_global(void) +{ +} + +#if defined(STATUS) +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); +} + +#endif + + +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; +#ifndef NO_OTHER_CHILD + other_child_rec *ocr, *nocr; +#endif + + ap_sync_scoreboard_image(); + + for (tries = terminate ? 4 : 1; tries <= 9; ++tries) { + /* don't want to hold up progress any more than + * necessary, but we need to allow children a few moments to exit. + * Set delay with an exponential backoff. + */ + tv.tv_sec = waittime / 1000000; + tv.tv_usec = waittime % 1000000; + waittime = waittime * 4; + ap_select(0, NULL, NULL, NULL, &tv); + + /* now see who is done */ + not_dead_yet = 0; + for (i = 0; i < max_daemons_limit; ++i) { + int pid = ap_scoreboard_image->parent[i].pid; + + if (pid == tls()->my_pid || pid == 0) + continue; + + waitret = waitpid(pid, &status, WNOHANG); + if (waitret == pid || waitret == -1) { + ap_scoreboard_image->parent[i].pid = 0; + continue; + } + ++not_dead_yet; + switch (tries) { + case 1: /* 16ms */ + case 2: /* 82ms */ + break; + case 3: /* 344ms */ + /* 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); + break; + case 9: /* 14 sec */ + /* gave it our best shot, but alas... If this really + * is a child we are trying to kill and it really hasn't + * exited, we will likely fail to bind to the port + * after the restart. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "could not make child process %d exit, " + "attempting to continue anyway", pid); + break; + } + } +#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_DEATH, ocr->data, status); + } + else if (waitret == 0) { + (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, -1); + ++not_dead_yet; + } + else if (waitret == -1) { + /* uh what the heck? they didn't call unregister? */ + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_LOST, ocr->data, -1); + } + } +#endif + if (!not_dead_yet) { + /* nothing left to wait for */ + break; + } + } +#endif /* ndef MULTITHREAD */ +} + + +#if defined(BROKEN_WAIT) || defined(NEED_WAITPID) +/* + Some systems appear to fail to deliver dead children to wait() at times. + This sorts them out. In fact, this may have been caused by a race condition + in wait_or_timeout(). But this routine is still useful for systems with no + waitpid(). + */ +int reap_children(void) +{ + int status, n; + int ret = 0; + + for (n = 0; n < max_daemons_limit; ++n) { + if (ap_scoreboard_image->servers[n].status != SERVER_DEAD + && waitpid(ap_scoreboard_image->parent[n].pid, &status, WNOHANG) + == -1 + && errno == ECHILD) { + ap_sync_scoreboard_image(); + ap_update_child_status(n, SERVER_DEAD, NULL); + ret = 1; + } + } + return ret; +} +#endif + +/* Finally, this routine is used by the caretaker process to wait for + * a while... + */ + +/* number of calls to wait_or_timeout between writable probes */ +#ifndef INTERVAL_OF_WRITABLE_PROBES +#define INTERVAL_OF_WRITABLE_PROBES 10 +#endif +static int wait_or_timeout_counter; + +static int wait_or_timeout(int *status) +{ + 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; + } + tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000; + tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000; + ap_select(0, NULL, NULL, NULL, &tv); + return -1; +} + + +/* handle all varieties of core dumping signals */ +static void sig_coredump(int sig) +{ + char emsg[256]; + const char *s; + + /* Must protect against a nested signal, otherwise we could end up in + * an infinite loop. + */ + signal(SIGSEGV, SIG_DFL); +#ifdef SIGBUS + signal(SIGBUS, SIG_DFL); +#endif +#ifdef SIGABORT + signal(SIGABORT, SIG_DFL); +#endif +#ifdef SIGABRT + signal(SIGABRT, SIG_DFL); +#endif + + s = "SIGSEGV"; +#ifdef SIGBUS + if (sig == SIGBUS) { + s = "SIGBUS"; + } +#endif +#ifdef SIGABORT + if (sig == SIGABORT) { + s = "SIGABORT"; + } +#endif +#ifdef SIGABRT + if (sig == SIGABRT) { + s = "SIGABRT"; + } +#endif + + ap_snprintf(emsg, sizeof(emsg), + "httpd: caught %s, attempting to dump core in %s", + s, ap_coredump_dir); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, emsg); + chdir(ap_coredump_dir); + abort(); + exit(1); +} + +/***************************************************************** + * 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 */ + if (tls()->alarms_blocked) { + tls()->exit_after_unblock = 1; + } + else { + clean_child_exit(0); + } +} + +#ifndef NO_GRACEFUL +static void usr1_handler(int sig) +{ + if (tls()->usr1_just_die) { + just_die(sig); + } + tls()->deferred_die = 1; +} +#endif + +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful; +static int volatile generation; + + +/* + * start_shutdown() and 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 start_shutdown() or start_restart() as appropiate. + * + * These should only be called from the parent process itself, since the + * parent process will use the shutdown_pending and restart_pending variables + * to determine whether to shutdown or restart. The child process should + * call signal_parent() directly to tell the parent to die -- this will + * cause neither of those variable to be set, which the parent will + * assume means something serious is wrong (which it will be, for the + * child to force an exit) and so do an exit anyway. + */ + +void ap_start_shutdown(void) +{ + if (shutdown_pending == 1) { + /* Um, is this _probably_ not an error, if the user has + * tried to do a shutdown twice quickly, so we won't + * worry about reporting it. + */ + return; + } + shutdown_pending = 1; +} + +/* do a graceful restart if graceful == 1 */ +void ap_start_restart(int graceful) +{ + if (restart_pending == 1) { + /* Probably not an error - don't bother reporting it */ + return; + } + restart_pending = 1; + is_graceful = graceful; +} + +static void sig_term(int sig) +{ + ap_start_shutdown(); +} + +static void restart(int sig) +{ + ap_start_restart(sig == SIGUSR1); +} + +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 + 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 + + /* 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 SIGXCPU + signal(SIGXCPU, SIG_DFL); +#endif /* SIGXCPU */ +#ifdef SIGXFSZ + signal(SIGXFSZ, SIG_DFL); +#endif /* SIGXFSZ */ + } + + signal(SIGTERM, sig_term); +#ifdef SIGHUP + signal(SIGHUP, restart); +#endif /* SIGHUP */ +#ifdef SIGUSR1 + signal(SIGUSR1, restart); +#endif /* SIGUSR1 */ +#endif +} + + +/***************************************************************** + * Here follows a long bunch of generic server bookkeeping stuff... + */ + +static void detach(void) +{ + int x; + + chdir("/"); +#if !defined(MPE) && !defined(__EMX__) +/* Don't detach for MPE because child processes can't survive the death of + the parent. */ + if ((x = fork()) > 0) + exit(0); + else if (x == -1) { + perror("fork"); + fprintf(stderr, "httpd: unable to fork new process\n"); + exit(1); + } + RAISE_SIGSTOP(DETACH); +#endif +#ifndef NO_SETSID + if ((pgrp = setsid()) == -1) { + perror("setsid"); + fprintf(stderr, "httpd: setsid failed\n"); + exit(1); + } +#elif defined(NEXT) || defined(NEWSOS) + if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "httpd: setpgrp or getpgrp failed\n"); + exit(1); + } +#elif defined(__EMX__) + /* OS/2 don't support process group IDs */ + pgrp = getpid(); +#elif defined(MPE) + /* MPE uses negative pid for process group */ + pgrp = -getpid(); +#else + if ((pgrp = setpgrp(getpid(), 0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "httpd: setpgrp failed\n"); + exit(1); + } +#endif + + /* close out the standard file descriptors */ + if (freopen("/dev/null", "r", stdin) == NULL) { + fprintf(stderr, "httpd: unable to replace stdin with /dev/null: %s\n", + 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, "httpd: unable to replace stdout with /dev/null: %s\n", + 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. + */ +} + +/* 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 (!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); + exit(1); + } + + name = ent->pw_name; + } + else + name = ap_user_name; + +#ifndef __EMX__ + /* OS/2 dosen't support groups. */ + + /* 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); + exit(1); + } +#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"); + exit(1); + } +#endif + if (setgid(ap_group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setgid: unable to set group id to Group %u", + (unsigned)ap_group_id); + exit(1); + } +#endif + } +} + +/* check to see if we have the 'suexec' setuid wrapper installed */ +static int init_suexec(void) +{ + struct stat wrapper; + + if ((stat(SUEXEC_BIN, &wrapper)) != 0) + return (ap_suexec_enabled); + + if ((wrapper.st_mode & S_ISUID) && wrapper.st_uid == 0) { + ap_suexec_enabled = 1; + fprintf(stderr, "Configuring Apache for use with suexec wrapper.\n"); + } + return (ap_suexec_enabled); +} + +/***************************************************************** + * 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->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) +static void sock_disable_nagle(int s) +{ + /* 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) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "setsockopt: (TCP_NODELAY)"); + } +} + +#else +#define sock_disable_nagle(s) /* 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 + +#ifndef WORKAROUND_SOLARIS_BUG + s = ap_slack(s, AP_SLACK_HIGH); + + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#endif + +#ifndef MPE +/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */ +#ifndef _OSD_POSIX + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr); + close(s); + ap_unblock_alarms(); + return -1; + } +#endif /*_OSD_POSIX*/ + one = 1; +#ifndef BEOS +/* BeOS does not support SO_KEEPALIVE */ + 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); + close(s); + ap_unblock_alarms(); + return -1; + } +#endif +#endif + + sock_disable_nagle(s); + 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 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 + close(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); + close(s); + ap_unblock_alarms(); + exit(1); + } + +#ifdef WORKAROUND_SOLARIS_BUG + s = ap_slack(s, AP_SLACK_HIGH); + + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#endif + ap_unblock_alarms(); + + /* 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); + close(s); + return -1; + } + + 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; +} + + +/* 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(p, fd); + } + if (fd >= 0) { + 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; + tls()->head_listener = ap_listeners; + close_unused_listeners(); + +#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 + * tls()->head_listener global. + */ +static ap_inline listen_rec *find_ready_listener(fd_set * main_fds) +{ + listen_rec *lr; + + lr = tls()->head_listener; + do { + if (FD_ISSET(lr->fd, main_fds)) { + tls()->head_listener = lr->next; + return (lr); + } + lr = lr->next; + } while (lr != tls()->head_listener); + return NULL; +} + + +static void show_compile_settings(void) +{ +#ifdef SERVER_SUBVERSION + printf("Server base version: %s\n", SERVER_BASEVERSION); + printf("Server sub-version: %s\n", SERVER_SUBVERSION); + printf("Server built: %s\n", ap_get_server_built()); +#else + printf("Server version: %s\n", ap_get_server_version()); + printf("Server built: %s\n", ap_get_server_built()); +#endif + printf("Server's Module Magic Number: %u\n", MODULE_MAGIC_NUMBER); + printf("Server compiled with....\n"); +#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 HTTPD_ROOT + printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\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 USE_FCNTL_SERIALIZED_ACCEPT + printf(" -D USE_FCNTL_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_FLOCK_SERIALIZED_ACCEPT + printf(" -D USE_FLOCK_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_USLOCK_SERIALIZED_ACCEPT + printf(" -D USE_USLOCK_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_SYSVSEM_SERIALIZED_ACCEPT + printf(" -D USE_SYSVSEM_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_PTHREAD_SERIALIZED_ACCEPT + printf(" -D USE_PTHREAD_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 +#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 +} + + +/* 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) +{ +#ifdef AUX3 + (void) set42sig(); +#endif + +#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 + + pconf = ap_init_alloc(); + ptemp = 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 *)); +} + +/***************************************************************** + * Child process main loop. + */ + + +API_EXPORT(void) ap_child_terminate(request_rec *r) +{ + r->connection->keepalive = 0; + tls()->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; + pool *ptrans; + + /* 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(); + + tls()->my_pid = getpid(); + tls()->csd = -1; + tls()->dupped_csd = -1; + tls()->my_child_num = child_num_arg; + tls()->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. + */ +#ifdef LTHREAD + tls()->pchild = ap_make_sub_pool(NULL); +#else + tls()->pchild = ap_make_sub_pool(pconf); +#endif + + /* needs to be done before we switch UIDs so we have permissions */ + reopen_scoreboard(tls()->pchild); + SAFE_ACCEPT(accept_mutex_child_init(tls()->pchild)); + + set_group_privs(); +#ifdef MPE + /* Only try to switch if we're running as MANAGER.SYS */ + if (geteuid() == 1 && ap_user_id > 1) { + GETPRIVMODE(); + if (setuid(ap_user_id) == -1) { + GETUSERMODE(); + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change uid"); + exit(1); + } + GETUSERMODE(); + } +#else + /* Only try to switch if we're running as root */ + if (!geteuid() && setuid(ap_user_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change uid"); + clean_child_exit(1); + } +#endif + + ap_child_init_modules(tls()->pchild, server_conf); + + /* done with the initialization critical section */ + ap_unblock_alarms(); + + (void) ap_update_child_status(tls()->my_child_num, SERVER_READY, (request_rec *) NULL); + + /* + * Setup the jump buffers so that we can return here after + * a signal or a timeout (yeah, I know, same thing). + */ + ap_setjmp(tls()->jmpbuffer); +#ifndef __EMX__ +#ifdef SIGURG + signal(SIGURG, timeout); +#endif +#endif + signal(SIGPIPE, timeout); + signal(SIGALRM, alrm_handler); + + ptrans = ap_make_sub_pool(tls()->pchild); + + while (1) { + BUFF *conn_io; + request_rec *r; + +#ifndef NO_GRACEFUL + /* 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. */ + tls()->usr1_just_die = 1; + signal(SIGUSR1, usr1_handler); +#endif + + /* + * (Re)initialize this child to a pre-connection state. + */ + + ap_kill_timeout(0); /* Cancel any outstanding alarms. */ + tls()->timeout_req = NULL; /* No request in progress */ + tls()->current_conn = NULL; + + ap_clear_pool(ptrans); + + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->global.exit_generation >= generation) { + clean_child_exit(0); + } + + if ((ap_max_requests_per_child > 0 + && ++tls()->requests_this_child >= ap_max_requests_per_child)) { + clean_child_exit(0); + } + + (void) ap_update_child_status(tls()->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(&tls()->main_fds, &listenfds, sizeof(fd_set)); + tls()->srv = ap_select(listenmaxfd + 1, &tls()->main_fds, NULL, NULL, NULL); + + if (tls()->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 (tls()->srv <= 0) + continue; + + lr = find_ready_listener(&tls()->main_fds); + if (lr == NULL) + continue; + tls()->sd = lr->fd; + } + else { + /* only one socket, just pretend we did the other stuff */ + tls()->sd = ap_listeners->fd; + } + + /* if we accept() something we don't want to die, so we have to + * defer the exit + */ + tls()->deferred_die = 0; + tls()->usr1_just_die = 0; + for (;;) { + clen = sizeof(sa_client); + tls()->csd = accept(tls()->sd, &sa_client, &clen); + if (tls()->csd >= 0 || errno != EINTR) + break; + if (tls()->deferred_die) { + /* we didn't get a socket, and we were told to die */ + clean_child_exit(0); + } + } + + if (tls()->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) { +#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 + break; + + default: + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + clean_child_exit(1); + } + } + + /* go around again, safe to die */ + tls()->usr1_just_die = 1; + if (tls()->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.exit_generation >= generation) { + clean_child_exit(0); + } + } + + SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ + +#ifndef NO_GRACEFUL + /* 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); +#endif + + ap_note_cleanups_for_fd(ptrans, tls()->csd); + + /* protect various fd_sets */ + if (tls()->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", tls()->csd, FD_SETSIZE); + continue; + } + + /* + * 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(tls()->csd, &sa_server, &clen) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname"); + continue; + } + + sock_disable_nagle(tls()->csd); + + (void) ap_update_child_status(tls()->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 + + tls()->dupped_csd = tls()->csd; +#if defined(NEED_DUPPED_CSD) + if ((tls()->dupped_csd = dup(tls()->csd)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "dup: couldn't duplicate csd"); + tls()->dupped_csd = tls()->csd; /* Oh well... */ + } + ap_note_cleanups_for_fd(ptrans, tls()->dupped_csd); + + /* protect various fd_sets */ + if (tls()->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", tls()->dupped_csd, FD_SETSIZE); + continue; + } +#endif + ap_bpushfd(conn_io, tls()->csd, tls()->dupped_csd); + + tls()->current_conn = new_connection(ptrans, server_conf, conn_io, + (struct sockaddr_in *) &sa_client, + (struct sockaddr_in *) &sa_server, + tls()->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(tls()->current_conn)) != NULL) { + + /* read_request_line has already done a + * signal (SIGUSR1, SIG_IGN); + */ + + (void) ap_update_child_status(tls()->my_child_num, SERVER_BUSY_WRITE, r); + + ap_process_request(r); + +#if defined(STATUS) + increment_counts(tls()->my_child_num, r); +#endif + + if (!tls()->current_conn->keepalive || tls()->current_conn->aborted) + break; + + ap_destroy_pool(r->pool); + (void) ap_update_child_status(tls()->my_child_num, SERVER_BUSY_KEEPALIVE, + (request_rec *) NULL); + + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->global.exit_generation >= generation) { + ap_bclose(conn_io); + clean_child_exit(0); + } + +#ifndef NO_GRACEFUL + /* 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 tls()->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. + */ + tls()->usr1_just_die = 1; + signal(SIGUSR1, usr1_handler); +#endif + } + + /* + * 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 + } +} + + +static void *child_starter(void *dummy) +{ + tls_main_t *t; + int rc; + + signal(SIGHUP, just_die); + signal(SIGUSR1, just_die); + signal(SIGTERM, just_die); + + t = malloc(sizeof(*t)); + memset(t, 0, sizeof(*t)); + t->head_listener = ap_listeners; + t->usr1_just_die = 1; + if ((rc = pthread_setspecific(tls_main_key, t))) { + fprintf(stderr, "pthread_setspecific: %s\n", strerror(rc)); + exit(1); + } + + child_main((int)dummy); + return NULL; +} + +static int make_child(server_rec *s, int slot, time_t now) +{ + pthread_t tid; + int rc; + + if (slot + 1 > max_daemons_limit) { + max_daemons_limit = slot + 1; + } + + if (one_process) { + signal(SIGHUP, just_die); + signal(SIGINT, just_die); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, just_die); + child_main(slot); + } + + (void) ap_update_child_status(slot, SERVER_STARTING, (request_rec *) NULL); + + if ((rc = pthread_create(&tid, &child_attr, child_starter, (void *)slot))) { + errno = rc; + ap_log_error(APLOG_MARK, APLOG_ERR, s, "pthread_create: unable to create new thread"); + + /* 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; + } + + ap_scoreboard_image->parent[slot].last_rtime = now; + ap_scoreboard_image->parent[slot].pid = tid; + + return 0; +} + + + +/* start up a bunch of children */ +static void startup_children(int number_to_start) +{ + int i; + time_t now = time(0); + + 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; + +static void perform_idle_server_maintenance(void) +{ + int i; + int to_kill; + int idle_count; + short_score *ss; + time_t now = time(0); + int free_length; + int free_slots[MAX_SPAWN_RATE]; + int last_non_dead; + int total_non_dead; + + /* initialize the free_list */ + free_length = 0; + + to_kill = -1; + idle_count = 0; + last_non_dead = -1; + total_non_dead = 0; + +#ifdef SHARED_TIME + ap_scoreboard_image->global.right_now = now; +#endif + + 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; + kill(ps->pid, SIGALRM); + } + } +#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 + */ + kill(ap_scoreboard_image->parent[to_kill].pid, SIGUSR1); + idle_spawn_rate = 1; + } + 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) { + make_child(server_conf, free_slots[i], now); + } + /* the next time around we want to spawn twice as many if this + * wasn't good enough, but not if we've just done a graceful + */ + if (hold_off_on_exponential_spawning) { + --hold_off_on_exponential_spawning; + } + else if (idle_spawn_rate < MAX_SPAWN_RATE) { + idle_spawn_rate *= 2; + } + } + } + else { + idle_spawn_rate = 1; + } +} + + +/***************************************************************** + * Executive routines. + */ + +#ifndef STANDALONE_MAIN +#define STANDALONE_MAIN standalone_main + +static void standalone_main(int argc, char **argv) +{ + int remaining_children_to_start; + + ap_standalone = 1; + + is_graceful = 0; + ++generation; + + if (!one_process) { +#if 0 + detach(); +#endif + } + else { + MONCONTROL(1); + } + + tls()->my_pid = getpid(); + + if ((errno = pthread_attr_init(&child_attr))) { + ap_log_error(APLOG_MARK, APLOG_CRIT, NULL, "pthread_attr_init"); + exit(1); + } + + do { + copy_listeners(pconf); + if (!is_graceful) { + ap_restart_time = time(NULL); + } + ap_clear_pool(pconf); + ptemp = ap_make_sub_pool(pconf); + + server_conf = ap_read_config(pconf, ptemp, ap_server_confname); + setup_listeners(pconf); + ap_open_logs(server_conf, pconf); + ap_log_pid(pconf, ap_pid_fname); + ap_init_modules(pconf, server_conf); + SAFE_ACCEPT(accept_mutex_init(pconf)); + if (!is_graceful) { + reinit_scoreboard(pconf); + } +#ifdef SHARED_TIME + ap_scoreboard_image->global.right_now = time(0); +#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()); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "Server built: %s", ap_get_server_built()); + restart_pending = shutdown_pending = 0; + + while (!restart_pending && !shutdown_pending) { + int child_slot; + int 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 + */ + if (pid >= 0) { + /* Child died... 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(0)); + --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 */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "httpd: 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); + } + + if (is_graceful) { + int i; + + /* USE WITH CAUTION: Graceful restarts are not known to work + * in various configurations on the architectures we support. */ + ap_scoreboard_image->global.exit_generation = generation; + update_scoreboard_global(); + + 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"