summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorArno Töll <debian@toell.net>2012-01-08 22:53:17 +0100
committerArno Töll <debian@toell.net>2012-01-08 22:53:17 +0100
commite072a2dd866b7cb9f14319b80326a4e7fd16fcdf (patch)
treea49dfc56d94a26011fe157835ff6cbe14edbd8a9 /server
parent0890390c00801651d08d3794e13b31a5dabbf5ef (diff)
downloadapache2-e072a2dd866b7cb9f14319b80326a4e7fd16fcdf.tar.gz
Imported Upstream version 2.3.16-beta
Diffstat (limited to 'server')
-rw-r--r--server/Makefile.in49
-rw-r--r--server/NWGNUmakefile16
-rw-r--r--server/config.c809
-rw-r--r--server/config.m44
-rw-r--r--server/connection.c40
-rw-r--r--server/core.c2248
-rw-r--r--server/core_filters.c1048
-rw-r--r--server/eor_bucket.c102
-rw-r--r--server/gen_test_char.c9
-rw-r--r--server/gen_test_char.dep5
-rw-r--r--server/gen_test_char.mak234
-rw-r--r--server/listen.c76
-rw-r--r--server/log.c1116
-rw-r--r--server/main.c298
-rw-r--r--server/mpm/MPM.NAMING7
-rw-r--r--server/mpm/Makefile.in2
-rw-r--r--server/mpm/beos/Makefile.in5
-rw-r--r--server/mpm/beos/beos.c1207
-rw-r--r--server/mpm/beos/beos.h34
-rw-r--r--server/mpm/beos/config5.m47
-rw-r--r--server/mpm/beos/mpm.h50
-rw-r--r--server/mpm/beos/mpm_default.h84
-rw-r--r--server/mpm/config.m4164
-rw-r--r--server/mpm/config2.m489
-rw-r--r--server/mpm/event/Makefile.in1
-rw-r--r--server/mpm/event/config.m413
-rw-r--r--server/mpm/event/config3.m47
-rw-r--r--server/mpm/event/event.c (renamed from server/mpm/experimental/event/event.c)2192
-rw-r--r--server/mpm/event/fdqueue.c (renamed from server/mpm/experimental/event/fdqueue.c)158
-rw-r--r--server/mpm/event/fdqueue.h (renamed from server/mpm/experimental/event/fdqueue.h)40
-rw-r--r--server/mpm/event/mpm_default.h (renamed from server/mpm/experimental/event/mpm_default.h)24
-rw-r--r--server/mpm/event/pod.c (renamed from server/mpm/experimental/event/pod.c)18
-rw-r--r--server/mpm/event/pod.h (renamed from server/mpm/experimental/event/pod.h)15
-rw-r--r--server/mpm/experimental/event/Makefile.in5
-rw-r--r--server/mpm/experimental/event/config5.m46
-rw-r--r--server/mpm/experimental/event/mpm.h62
-rw-r--r--server/mpm/mpmt_os2/Makefile.in6
-rw-r--r--server/mpm/mpmt_os2/config.m410
-rw-r--r--server/mpm/mpmt_os2/config5.m46
-rw-r--r--server/mpm/mpmt_os2/mpm.h43
-rw-r--r--server/mpm/mpmt_os2/mpm_default.h14
-rw-r--r--server/mpm/mpmt_os2/mpmt_os2.c163
-rw-r--r--server/mpm/mpmt_os2/mpmt_os2_child.c36
-rw-r--r--server/mpm/netware/mpm.h58
-rw-r--r--server/mpm/netware/mpm_default.h45
-rw-r--r--server/mpm/netware/mpm_netware.c270
-rw-r--r--server/mpm/prefork/Makefile.in6
-rw-r--r--server/mpm/prefork/config.m48
-rw-r--r--server/mpm/prefork/config3.m41
-rw-r--r--server/mpm/prefork/mpm.h62
-rw-r--r--server/mpm/prefork/mpm_default.h24
-rw-r--r--server/mpm/prefork/prefork.c785
-rw-r--r--server/mpm/winnt/Makefile.in6
-rw-r--r--server/mpm/winnt/Win9xConHook.c697
-rw-r--r--server/mpm/winnt/Win9xConHook.def10
-rw-r--r--server/mpm/winnt/Win9xConHook.dsp115
-rw-r--r--server/mpm/winnt/Win9xConHook.h66
-rw-r--r--server/mpm/winnt/child.c1247
-rw-r--r--server/mpm/winnt/config.m413
-rw-r--r--server/mpm/winnt/config3.m42
-rw-r--r--server/mpm/winnt/mpm.h49
-rw-r--r--server/mpm/winnt/mpm_default.h30
-rw-r--r--server/mpm/winnt/mpm_winnt.c682
-rw-r--r--server/mpm/winnt/mpm_winnt.h69
-rw-r--r--server/mpm/winnt/nt_eventlog.c8
-rw-r--r--server/mpm/winnt/service.c994
-rw-r--r--server/mpm/worker/Makefile.in5
-rw-r--r--server/mpm/worker/config.m411
-rw-r--r--server/mpm/worker/config3.m4 (renamed from server/mpm/worker/config5.m4)5
-rw-r--r--server/mpm/worker/fdqueue.c19
-rw-r--r--server/mpm/worker/fdqueue.h6
-rw-r--r--server/mpm/worker/mpm.h62
-rw-r--r--server/mpm/worker/mpm_default.h24
-rw-r--r--server/mpm/worker/pod.c17
-rw-r--r--server/mpm/worker/pod.h15
-rw-r--r--server/mpm/worker/worker.c1191
-rw-r--r--server/mpm_common.c1231
-rw-r--r--server/mpm_unix.c942
-rw-r--r--server/protocol.c319
-rw-r--r--server/request.c675
-rw-r--r--server/scoreboard.c198
-rw-r--r--server/util.c1163
-rw-r--r--server/util_cfgtree.c1
-rw-r--r--server/util_cookies.c290
-rw-r--r--server/util_debug.c99
-rw-r--r--server/util_ebcdic.c15
-rw-r--r--server/util_expr_eval.c1723
-rw-r--r--server/util_expr_parse.c2130
-rw-r--r--server/util_expr_parse.h104
-rw-r--r--server/util_expr_parse.y217
-rw-r--r--server/util_expr_private.h141
-rw-r--r--server/util_expr_scan.c2669
-rw-r--r--server/util_expr_scan.l400
-rw-r--r--server/util_filter.c64
-rw-r--r--server/util_mutex.c560
-rw-r--r--server/util_pcre.c266
-rw-r--r--server/util_regex.c210
-rw-r--r--server/util_script.c319
-rw-r--r--server/util_time.c102
-rw-r--r--server/util_xml.c10
-rw-r--r--server/vhost.c321
101 files changed, 20523 insertions, 10770 deletions
diff --git a/server/Makefile.in b/server/Makefile.in
index db2caa0d..42d1fe5c 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -7,14 +7,15 @@ SUBDIRS = mpm
LTLIBRARY_NAME = libmain.la
LTLIBRARY_SOURCES = \
- test_char.h \
config.c log.c main.c vhost.c util.c \
util_script.c util_md5.c util_cfgtree.c util_ebcdic.c util_time.c \
- connection.c listen.c \
- mpm_common.c util_charset.c util_debug.c util_xml.c \
- util_filter.c util_pcre.c exports.c \
+ connection.c listen.c util_mutex.c mpm_common.c mpm_unix.c \
+ util_charset.c util_cookies.c util_debug.c util_xml.c \
+ util_filter.c util_pcre.c util_regex.c exports.c \
scoreboard.c error_bucket.c protocol.c core.c request.c provider.c \
- eoc_bucket.c core_filters.c
+ eoc_bucket.c eor_bucket.c core_filters.c \
+ util_expr_parse.c util_expr_scan.c util_expr_eval.c
+LTLIBRARY_DEPENDENCIES = test_char.h
TARGETS = delete-exports $(LTLIBRARY_NAME) $(CORE_IMPLIB_FILE) export_vars.h httpd.exp
@@ -30,7 +31,7 @@ test_char.h: gen_test_char
util.lo: test_char.h
-EXPORT_DIRS = $(top_srcdir)/include $(top_srcdir)/os/$(OS_DIR) $(top_srcdir)/modules/http
+EXPORT_DIRS = $(top_srcdir)/include $(top_srcdir)/os/$(OS_DIR)
EXPORT_DIRS_APR = $(APR_INCLUDEDIR) $(APU_INCLUDEDIR)
# If export_files is a dependency here, but we remove it during this stage,
@@ -53,16 +54,13 @@ delete-exports:
fi
export_files:
- tmp=export_files_unsorted.txt; \
- rm -f $$tmp && touch $$tmp; \
- for dir in $(EXPORT_DIRS); do \
- ls $$dir/*.h >> $$tmp; \
- done; \
- for dir in $(EXPORT_DIRS_APR); do \
- (ls $$dir/ap[ru].h $$dir/ap[ru]_*.h >> $$tmp 2>/dev/null); \
- done; \
- sort -u $$tmp > $@; \
- rm -f $$tmp
+ ( for dir in $(EXPORT_DIRS); do \
+ ls $$dir/*.h ; \
+ done; \
+ for dir in $(EXPORT_DIRS_APR); do \
+ ls $$dir/ap[ru].h $$dir/ap[ru]_*.h 2>/dev/null; \
+ done; \
+ ) | sort -u > $@
exports.c: export_files
$(AWK) -f $(top_srcdir)/build/make_exports.awk `cat $?` > $@
@@ -83,3 +81,22 @@ httpd.exp: exports.c export_vars.h
@echo "* Please do not edit by hand." >> $@
$(CPP) $(ALL_CPPFLAGS) $(ALL_INCLUDES) exports.c | grep "ap_hack_" | grep -v apr_ | sed -e 's/^.*[)]\(.*\);$$/\1/' >> $@
$(CPP) $(ALL_CPPFLAGS) $(ALL_INCLUDES) export_vars.h | grep -v apr_ | sed -e 's/^\#[^!]*//' | sed -e '/^$$/d' >> $@
+
+
+# developer stuff
+# (we really don't expect end users to use these targets!)
+#
+util_expr_scan.c util_expr_parse.c util_expr_parse.h: util_expr_scan.l util_expr_parse.y
+ bison -pap_expr_yy --defines=$(builddir)/util_expr_parse.h \
+ -o $(builddir)/util_expr_parse.c $(srcdir)/util_expr_parse.y
+ flex -Pap_expr_yy -o $(builddir)/util_expr_scan.c $(srcdir)/util_expr_scan.l
+ set -e ; \
+ for f in util_expr_scan.c util_expr_parse.c util_expr_parse.h ; do \
+ sed -e "s|\"$(builddir)/|\"|g" < $(builddir)/$$f > \
+ $(builddir)/$$f.$$$$ && \
+ mv $(builddir)/$$f.$$$$ $(builddir)/$$f ; \
+ done
+ # work around flex bug
+ # http://sourceforge.net/tracker/?func=detail&aid=3029024&group_id=97492&atid=618177
+ perl -0777 -p -i -e 's,\n(void|int) ap_expr_yy[gs]et_column[^\n]+\)\n.*?\n\},,gs' \
+ $(builddir)/util_expr_scan.c
diff --git a/server/NWGNUmakefile b/server/NWGNUmakefile
index 16ce9734..7f96e81e 100644
--- a/server/NWGNUmakefile
+++ b/server/NWGNUmakefile
@@ -102,7 +102,7 @@ NLM_NAME = genchars
# This is used by the link '-desc ' directive.
# If left blank, NLM_NAME will be used.
#
-NLM_DESCRIPTION = Apache $(VERSION_STR) Generate Test Characters
+NLM_DESCRIPTION = Generate Test Characters
#
# This is used by the '-threadname' directive. If left blank,
@@ -112,9 +112,9 @@ NLM_THREAD_NAME = genchars
#
# If this is specified, it will override VERSION value in
-# $(AP_WORK)/build/NWGNUenvironment.inc
+# $(AP_WORK)\NWGNUNetWare.rul
#
-NLM_VERSION =
+NLM_VERSION = 1,0,0
#
# If this is specified, it will override the default of 64K
@@ -242,6 +242,16 @@ install :: nlms FORCE
# Any specialized rules here
#
+# Make sure that the build doesn't attempt to regenerate the shipping files.
+# This requires a 'touch' utility. Can be downloaded from 'coreutils' at
+# http://sourceforge.net/projects/gnuwin32/
+util_expr_parse.h : util_expr_parse.y
+ touch util_expr_parse.h
+util_expr_parse.c : util_expr_parse.y
+ touch util_expr_parse.c
+util_expr_scan.c : util_expr_scan.l
+ touch util_expr_scan.c
+
#
# Include the 'tail' makefile that has targets that depend on variables defined
# in this makefile
diff --git a/server/config.c b/server/config.c
index 8a742bfb..8c56308b 100644
--- a/server/config.c
+++ b/server/config.c
@@ -39,8 +39,6 @@
#define APR_WANT_STRFUNC
#include "apr_want.h"
-#define CORE_PRIVATE
-
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
@@ -51,12 +49,18 @@
#include "http_main.h"
#include "http_vhost.h"
#include "util_cfgtree.h"
-#include "mpm.h"
+#include "util_varbuf.h"
+#include "mpm_common.h"
+#define APLOG_UNSET (APLOG_NO_MODULE - 1)
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
AP_DECLARE_DATA const char *ap_server_argv0 = NULL;
-
AP_DECLARE_DATA const char *ap_server_root = NULL;
+AP_DECLARE_DATA server_rec *ap_server_conf = NULL;
+AP_DECLARE_DATA apr_pool_t *ap_pglobal = NULL;
AP_DECLARE_DATA apr_array_header_t *ap_server_pre_read_config = NULL;
AP_DECLARE_DATA apr_array_header_t *ap_server_post_read_config = NULL;
@@ -67,6 +71,7 @@ AP_DECLARE_DATA ap_directive_t *ap_conftree = NULL;
APR_HOOK_STRUCT(
APR_HOOK_LINK(header_parser)
APR_HOOK_LINK(pre_config)
+ APR_HOOK_LINK(check_config)
APR_HOOK_LINK(post_config)
APR_HOOK_LINK(open_logs)
APR_HOOK_LINK(child_init)
@@ -84,6 +89,11 @@ AP_IMPLEMENT_HOOK_RUN_ALL(int, pre_config,
apr_pool_t *ptemp),
(pconf, plog, ptemp), OK, DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int, check_config,
+ (apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s),
+ (pconf, plog, ptemp, s), OK, DECLINED)
+
AP_IMPLEMENT_HOOK_VOID(test_config,
(apr_pool_t *pconf, server_rec *s),
(pconf, s))
@@ -160,6 +170,20 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(int, handler, (request_rec *r),
AP_IMPLEMENT_HOOK_RUN_FIRST(int, quick_handler, (request_rec *r, int lookup),
(r, lookup), DECLINED)
+/* hooks with no args are implemented last, after disabling APR hook probes */
+#if defined(APR_HOOK_PROBES_ENABLED)
+#undef APR_HOOK_PROBES_ENABLED
+#undef APR_HOOK_PROBE_ENTRY
+#define APR_HOOK_PROBE_ENTRY(ud,ns,name,args)
+#undef APR_HOOK_PROBE_RETURN
+#define APR_HOOK_PROBE_RETURN(ud,ns,name,rv,args)
+#undef APR_HOOK_PROBE_INVOKE
+#define APR_HOOK_PROBE_INVOKE(ud,ns,name,src,args)
+#undef APR_HOOK_PROBE_COMPLETE
+#define APR_HOOK_PROBE_COMPLETE(ud,ns,name,src,rv,args)
+#undef APR_HOOK_INT_DCL_UD
+#define APR_HOOK_INT_DCL_UD
+#endif
AP_IMPLEMENT_HOOK_VOID(optional_fn_retrieve, (void), ())
/****************************************************************
@@ -179,15 +203,40 @@ static int total_modules = 0;
*/
static int dynamic_modules = 0;
+/* The maximum possible value for total_modules, i.e. number of static
+ * modules plus DYNAMIC_MODULE_LIMIT.
+ */
+static int max_modules = 0;
+
+/* The number of elements we need to alloc for config vectors. Before loading
+ * of dynamic modules, we must be liberal and set this to max_modules. After
+ * loading of dynamic modules, we can trim it down to total_modules. On
+ * restart, reset to max_modules.
+ */
+static int conf_vector_length = 0;
+
+static int reserved_module_slots = 0;
+
AP_DECLARE_DATA module *ap_top_module = NULL;
AP_DECLARE_DATA module **ap_loaded_modules=NULL;
static apr_hash_t *ap_config_hash = NULL;
+/* a list of the module symbol names with the trailing "_module"removed */
+static char **ap_module_short_names = NULL;
+
typedef int (*handler_func)(request_rec *);
typedef void *(*dir_maker_func)(apr_pool_t *, char *);
typedef void *(*merger_func)(apr_pool_t *, void *, void *);
+/* A list of the merge_dir_config functions of all loaded modules, sorted
+ * by module_index.
+ * Using this list in ap_merge_per_dir_configs() is faster than following
+ * the module->next linked list because of better memory locality (resulting
+ * in better cache usage).
+ */
+static merger_func *merger_func_cache;
+
/* maximum nesting level for config directories */
#ifndef AP_MAX_INCLUDE_DIR_DEPTH
#define AP_MAX_INCLUDE_DIR_DEPTH (128)
@@ -206,15 +255,13 @@ typedef void *(*merger_func)(apr_pool_t *, void *, void *);
static ap_conf_vector_t *create_empty_config(apr_pool_t *p)
{
- void *conf_vector = apr_pcalloc(p, sizeof(void *) *
- (total_modules + DYNAMIC_MODULE_LIMIT));
+ void *conf_vector = apr_pcalloc(p, sizeof(void *) * conf_vector_length);
return conf_vector;
}
static ap_conf_vector_t *create_default_per_dir_config(apr_pool_t *p)
{
- void **conf_vector = apr_pcalloc(p, sizeof(void *) *
- (total_modules + DYNAMIC_MODULE_LIMIT));
+ void **conf_vector = apr_pcalloc(p, sizeof(void *) * conf_vector_length);
module *modp;
for (modp = ap_top_module; modp; modp = modp->next) {
@@ -231,19 +278,17 @@ AP_CORE_DECLARE(ap_conf_vector_t *) ap_merge_per_dir_configs(apr_pool_t *p,
ap_conf_vector_t *base,
ap_conf_vector_t *new_conf)
{
- void **conf_vector = apr_palloc(p, sizeof(void *) * total_modules);
+ void **conf_vector = apr_palloc(p, sizeof(void *) * conf_vector_length);
void **base_vector = (void **)base;
void **new_vector = (void **)new_conf;
- module *modp;
-
- for (modp = ap_top_module; modp; modp = modp->next) {
- int i = modp->module_index;
+ int i;
+ for (i = 0; i < total_modules; i++) {
if (!new_vector[i]) {
conf_vector[i] = base_vector[i];
}
else {
- merger_func df = modp->merge_dir_config;
+ const merger_func df = merger_func_cache[i];
if (df && base_vector[i]) {
conf_vector[i] = (*df)(p, base_vector[i], new_vector[i]);
}
@@ -257,8 +302,7 @@ AP_CORE_DECLARE(ap_conf_vector_t *) ap_merge_per_dir_configs(apr_pool_t *p,
static ap_conf_vector_t *create_server_config(apr_pool_t *p, server_rec *s)
{
- void **conf_vector = apr_pcalloc(p, sizeof(void *) *
- (total_modules + DYNAMIC_MODULE_LIMIT));
+ void **conf_vector = apr_pcalloc(p, sizeof(void *) * conf_vector_length);
module *modp;
for (modp = ap_top_module; modp; modp = modp->next) {
@@ -324,6 +368,12 @@ static int invoke_filter_init(request_rec *r, ap_filter_t *filters)
return OK;
}
+/*
+ * TODO: Move this to an appropriate include file and possibly prefix it
+ * with AP_.
+ */
+#define DEFAULT_HANDLER_NAME ""
+
AP_CORE_DECLARE(int) ap_invoke_handler(request_rec *r)
{
const char *handler;
@@ -356,18 +406,23 @@ AP_CORE_DECLARE(int) ap_invoke_handler(request_rec *r)
}
if (!r->handler) {
- handler = r->content_type ? r->content_type : ap_default_type(r);
- if ((p=ap_strchr_c(handler, ';')) != NULL) {
- char *new_handler = (char *)apr_pmemdup(r->pool, handler,
- p - handler + 1);
- char *p2 = new_handler + (p - handler);
- handler = new_handler;
-
- /* MIME type arguments */
- while (p2 > handler && p2[-1] == ' ')
- --p2; /* strip trailing spaces */
-
- *p2='\0';
+ if (r->content_type) {
+ handler = r->content_type;
+ if ((p=ap_strchr_c(handler, ';')) != NULL) {
+ char *new_handler = (char *)apr_pmemdup(r->pool, handler,
+ p - handler + 1);
+ char *p2 = new_handler + (p - handler);
+ handler = new_handler;
+
+ /* exclude media type arguments */
+ while (p2 > handler && p2[-1] == ' ')
+ --p2; /* strip trailing spaces */
+
+ *p2='\0';
+ }
+ }
+ else {
+ handler = DEFAULT_HANDLER_NAME;
}
r->handler = handler;
@@ -378,11 +433,11 @@ AP_CORE_DECLARE(int) ap_invoke_handler(request_rec *r)
r->handler = old_handler;
if (result == DECLINED && r->handler && r->filename) {
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(00523)
"handler \"%s\" not found for: %s", r->handler, r->filename);
}
- if ((result != OK) && (result != DONE) && (result != DECLINED)
- && (result != AP_FILTER_ERROR)
+ if ((result != OK) && (result != DONE) && (result != DECLINED) && (result != SUSPENDED)
+ && (result != AP_FILTER_ERROR) /* ap_die() knows about this specifically */
&& !ap_is_HTTP_VALID_RESPONSE(result)) {
/* If a module is deliberately returning something else
* (request_rec in non-HTTP or proprietary extension?)
@@ -392,7 +447,7 @@ AP_CORE_DECLARE(int) ap_invoke_handler(request_rec *r)
*/
ignore = apr_table_get(r->notes, "HTTP_IGNORE_RANGE");
if (!ignore) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00524)
"Handler for %s returned invalid result code %d",
r->handler, result);
result = HTTP_INTERNAL_SERVER_ERROR;
@@ -441,19 +496,13 @@ struct ap_mod_list_struct {
const command_rec *cmd;
};
-static apr_status_t reload_conf_hash(void *baton)
-{
- ap_config_hash = NULL;
- return APR_SUCCESS;
-}
-
static void rebuild_conf_hash(apr_pool_t *p, int add_prelinked)
{
module **m;
ap_config_hash = apr_hash_make(p);
- apr_pool_cleanup_register(p, NULL, reload_conf_hash,
+ apr_pool_cleanup_register(p, &ap_config_hash, ap_pool_cleanup_set_null,
apr_pool_cleanup_null);
if (add_prelinked) {
for (m = ap_prelinked_modules; *m != NULL; m++) {
@@ -494,8 +543,11 @@ static void ap_add_module_commands(module *m, apr_pool_t *p)
/* One-time setup for precompiled modules --- NOT to be done on restart */
-AP_DECLARE(const char *) ap_add_module(module *m, apr_pool_t *p)
+AP_DECLARE(const char *) ap_add_module(module *m, apr_pool_t *p,
+ const char *sym_name)
{
+ ap_module_symbol_t *sym = ap_prelinked_module_symbols;
+
/* This could be called from a LoadModule httpd.conf command,
* after the file has been linked and the module structure within it
* teased out...
@@ -508,23 +560,50 @@ AP_DECLARE(const char *) ap_add_module(module *m, apr_pool_t *p)
m->name, m->version, MODULE_MAGIC_NUMBER_MAJOR);
}
- if (m->next == NULL) {
- m->next = ap_top_module;
- ap_top_module = m;
- }
-
if (m->module_index == -1) {
- m->module_index = total_modules++;
- dynamic_modules++;
-
- if (dynamic_modules > DYNAMIC_MODULE_LIMIT) {
+ if (dynamic_modules >= DYNAMIC_MODULE_LIMIT) {
return apr_psprintf(p, "Module \"%s\" could not be loaded, "
"because the dynamic module limit was "
"reached. Please increase "
"DYNAMIC_MODULE_LIMIT and recompile.", m->name);
}
+ /*
+ * If this fails some module forgot to call ap_reserve_module_slots*.
+ */
+ ap_assert(total_modules < conf_vector_length);
+
+ m->module_index = total_modules++;
+ dynamic_modules++;
+
+ }
+ else if (!sym_name) {
+ while (sym->modp != NULL) {
+ if (sym->modp == m) {
+ sym_name = sym->name;
+ break;
+ }
+ sym++;
+ }
+ }
+
+ if (m->next == NULL) {
+ m->next = ap_top_module;
+ ap_top_module = m;
+ }
+
+ if (sym_name) {
+ int len = strlen(sym_name);
+ int slen = strlen("_module");
+ if (len > slen && !strcmp(sym_name + len - slen, "_module")) {
+ len -= slen;
+ }
+
+ ap_module_short_names[m->module_index] = strdup(sym_name);
+ ap_module_short_names[m->module_index][len] = '\0';
+ merger_func_cache[m->module_index] = m->merge_dir_config;
}
+
/* Some C compilers put a complete path into __FILE__, but we want
* only the filename (e.g. mod_includes.c). So check for path
* components (Unix and DOS), and remove them.
@@ -587,7 +666,7 @@ AP_DECLARE(void) ap_remove_module(module *m)
if (!modp) {
/* Uh-oh, this module doesn't exist */
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00525)
"Cannot remove module %s: not found in module list",
m->name);
return;
@@ -597,13 +676,18 @@ AP_DECLARE(void) ap_remove_module(module *m)
modp->next = modp->next->next;
}
+ free(ap_module_short_names[m->module_index]);
+ ap_module_short_names[m->module_index] = NULL;
+ merger_func_cache[m->module_index] = NULL;
+
m->module_index = -1; /* simulate being unloaded, should
* be unnecessary */
dynamic_modules--;
total_modules--;
}
-AP_DECLARE(const char *) ap_add_loaded_module(module *mod, apr_pool_t *p)
+AP_DECLARE(const char *) ap_add_loaded_module(module *mod, apr_pool_t *p,
+ const char *short_name)
{
module **m;
const char *error;
@@ -611,7 +695,7 @@ AP_DECLARE(const char *) ap_add_loaded_module(module *mod, apr_pool_t *p)
/*
* Add module pointer to top of chained module list
*/
- error = ap_add_module(mod, p);
+ error = ap_add_module(mod, p, short_name);
if (error) {
return error;
}
@@ -679,15 +763,23 @@ AP_DECLARE(const char *) ap_setup_prelinked_modules(process_rec *process)
for (m = ap_preloaded_modules; *m != NULL; m++)
(*m)->module_index = total_modules++;
+ max_modules = total_modules + DYNAMIC_MODULE_LIMIT + 1;
+ conf_vector_length = max_modules;
+
/*
- * Initialise list of loaded modules
+ * Initialise list of loaded modules and short names
*/
ap_loaded_modules = (module **)apr_palloc(process->pool,
- sizeof(module *) * (total_modules + DYNAMIC_MODULE_LIMIT + 1));
+ sizeof(module *) * conf_vector_length);
+ if (!ap_module_short_names)
+ ap_module_short_names = ap_calloc(sizeof(char *), conf_vector_length);
+
+ if (!merger_func_cache)
+ merger_func_cache = ap_calloc(sizeof(merger_func), conf_vector_length);
- if (ap_loaded_modules == NULL) {
+ if (ap_loaded_modules == NULL || ap_module_short_names == NULL
+ || merger_func_cache == NULL)
return "Ouch! Out of memory in ap_setup_prelinked_modules()!";
- }
for (m = ap_preloaded_modules, m2 = ap_loaded_modules; *m != NULL; )
*m2++ = *m++;
@@ -698,7 +790,7 @@ AP_DECLARE(const char *) ap_setup_prelinked_modules(process_rec *process)
* Initialize chain of linked (=activate) modules
*/
for (m = ap_prelinked_modules; *m != NULL; m++) {
- error = ap_add_module(*m, process->pconf);
+ error = ap_add_module(*m, process->pconf, NULL);
if (error) {
return error;
}
@@ -714,6 +806,13 @@ AP_DECLARE(const char *) ap_find_module_name(module *m)
return m->name;
}
+AP_DECLARE(const char *) ap_find_module_short_name(int module_index)
+{
+ if (module_index < 0 || module_index >= conf_vector_length)
+ return NULL;
+ return ap_module_short_names[module_index];
+}
+
AP_DECLARE(module *) ap_find_linked_module(const char *name)
{
module *modp;
@@ -740,10 +839,16 @@ AP_DECLARE(module *) ap_find_linked_module(const char *name)
static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms,
void *mconfig, const char *args)
{
+ int override_list_ok = 0;
char *w, *w2, *w3;
const char *errmsg = NULL;
- if ((parms->override & cmd->req_override) == 0)
+ /** Have we been provided a list of acceptable directives? */
+ if(parms->override_list != NULL)
+ if(apr_table_get(parms->override_list, cmd->name) != NULL)
+ override_list_ok = 1;
+
+ if ((parms->override & cmd->req_override) == 0 && !override_list_ok)
return apr_pstrcat(parms->pool, cmd->name, " not allowed here", NULL);
parms->info = cmd->cmd_data;
@@ -1082,6 +1187,9 @@ static const char *ap_build_config_sub(apr_pool_t *p, apr_pool_t *temp_pool,
return retval;
}
+#define VARBUF_INIT_LEN 200
+#define VARBUF_MAX_LEN (16*1024*1024)
+
AP_DECLARE(const char *) ap_build_cont_config(apr_pool_t *p,
apr_pool_t *temp_pool,
cmd_parms *parms,
@@ -1089,25 +1197,26 @@ AP_DECLARE(const char *) ap_build_cont_config(apr_pool_t *p,
ap_directive_t **curr_parent,
char *orig_directive)
{
- char *l;
char *bracket;
const char *retval;
ap_directive_t *sub_tree = NULL;
-
- /* Since this function can be called recursively, allocate
- * the temporary 8k string buffer from the temp_pool rather
- * than the stack to avoid over-running a fixed length stack.
- */
- l = apr_palloc(temp_pool, MAX_STRING_LEN);
-
- bracket = apr_pstrcat(p, orig_directive + 1, ">", NULL);
- while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) {
- if (!memcmp(l, "</", 2)
- && (strcasecmp(l + 2, bracket) == 0)
+ apr_status_t rc;
+ struct ap_varbuf vb;
+ apr_size_t max_len = VARBUF_MAX_LEN;
+ if (p == temp_pool)
+ max_len = HUGE_STRING_LEN; /* lower limit for .htaccess */
+
+ bracket = apr_pstrcat(temp_pool, orig_directive + 1, ">", NULL);
+ ap_varbuf_init(temp_pool, &vb, VARBUF_INIT_LEN);
+
+ while ((rc = ap_varbuf_cfg_getline(&vb, parms->config_file, max_len))
+ == APR_SUCCESS) {
+ if (!memcmp(vb.buf, "</", 2)
+ && (strcasecmp(vb.buf + 2, bracket) == 0)
&& (*curr_parent == NULL)) {
break;
}
- retval = ap_build_config_sub(p, temp_pool, l, parms, current,
+ retval = ap_build_config_sub(p, temp_pool, vb.buf, parms, current,
curr_parent, &sub_tree);
if (retval != NULL)
return retval;
@@ -1120,6 +1229,9 @@ AP_DECLARE(const char *) ap_build_cont_config(apr_pool_t *p,
sub_tree = *current;
}
}
+ ap_varbuf_free(&vb);
+ if (rc != APR_EOF && rc != APR_SUCCESS)
+ return ap_pcfg_strerror(temp_pool, parms->config_file, rc);
*current = sub_tree;
return NULL;
@@ -1131,7 +1243,7 @@ static const char *ap_walk_config_sub(const ap_directive_t *current,
{
const command_rec *cmd;
ap_mod_list *ml;
- char *dir = apr_pstrdup(parms->pool, current->directive);
+ char *dir = apr_pstrdup(parms->temp_pool, current->directive);
ap_str_tolower(dir);
@@ -1211,17 +1323,41 @@ AP_DECLARE(const char *) ap_build_config(cmd_parms *parms,
{
ap_directive_t *current = *conftree;
ap_directive_t *curr_parent = NULL;
- char *l = apr_palloc (temp_pool, MAX_STRING_LEN);
const char *errmsg;
+ ap_directive_t **last_ptr = NULL;
+ apr_status_t rc;
+ struct ap_varbuf vb;
+ apr_size_t max_len = VARBUF_MAX_LEN;
+ if (p == temp_pool)
+ max_len = HUGE_STRING_LEN; /* lower limit for .htaccess */
+
+ ap_varbuf_init(temp_pool, &vb, VARBUF_INIT_LEN);
if (current != NULL) {
+ /* If we have to traverse the whole tree again for every included
+ * config file, the required time grows as O(n^2) with the number of
+ * files. This can be a significant delay for large configurations.
+ * Therefore we cache a pointer to the last node.
+ */
+ last_ptr = &(current->last);
+
+ if(last_ptr && *last_ptr) {
+ current = *last_ptr;
+ }
+
while (current->next) {
current = current->next;
}
+
+ if(last_ptr) {
+ /* update cached pointer to last node */
+ *last_ptr = current;
+ }
}
- while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) {
- errmsg = ap_build_config_sub(p, temp_pool, l, parms,
+ while ((rc = ap_varbuf_cfg_getline(&vb, parms->config_file, max_len))
+ == APR_SUCCESS) {
+ errmsg = ap_build_config_sub(p, temp_pool, vb.buf, parms,
&current, &curr_parent, conftree);
if (errmsg != NULL)
return errmsg;
@@ -1234,6 +1370,9 @@ AP_DECLARE(const char *) ap_build_config(cmd_parms *parms,
*conftree = current;
}
}
+ ap_varbuf_free(&vb);
+ if (rc != APR_EOF && rc != APR_SUCCESS)
+ return ap_pcfg_strerror(temp_pool, parms->config_file, rc);
if (curr_parent != NULL) {
errmsg = "";
@@ -1314,6 +1453,18 @@ AP_DECLARE_NONSTD(const char *) ap_set_flag_slot(cmd_parms *cmd,
return NULL;
}
+AP_DECLARE_NONSTD(const char *) ap_set_flag_slot_char(cmd_parms *cmd,
+ void *struct_ptr_v, int arg)
+{
+ int offset = (int)(long)cmd->info;
+ char *struct_ptr = (char *)struct_ptr_v;
+
+ *(struct_ptr + offset) = arg ? 1 : 0;
+
+ return NULL;
+}
+
+
AP_DECLARE_NONSTD(const char *) ap_set_file_slot(cmd_parms *cmd, void *struct_ptr,
const char *arg)
{
@@ -1343,13 +1494,35 @@ AP_DECLARE_NONSTD(const char *) ap_set_deprecated(cmd_parms *cmd,
return cmd->cmd->errmsg;
}
+AP_DECLARE(void) ap_reset_module_loglevels(struct ap_logconf *l, int val)
+{
+ if (l->module_levels)
+ memset(l->module_levels, val, conf_vector_length);
+}
+
+AP_DECLARE(void) ap_set_module_loglevel(apr_pool_t *pool, struct ap_logconf *l,
+ int index, int level)
+{
+ if (!l->module_levels) {
+ l->module_levels = apr_palloc(pool, conf_vector_length);
+ if (l->level == APLOG_UNSET) {
+ ap_reset_module_loglevels(l, APLOG_UNSET);
+ }
+ else {
+ ap_reset_module_loglevels(l, APLOG_NO_MODULE);
+ }
+ }
+
+ l->module_levels[index] = level;
+}
+
/*****************************************************************
*
* Reading whole config files...
*/
static cmd_parms default_parms =
-{NULL, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+{NULL, 0, 0, NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
AP_DECLARE(char *) ap_server_root_relative(apr_pool_t *p, const char *file)
{
@@ -1369,18 +1542,25 @@ AP_DECLARE(char *) ap_server_root_relative(apr_pool_t *p, const char *file)
AP_DECLARE(const char *) ap_soak_end_container(cmd_parms *cmd, char *directive)
{
- char l[MAX_STRING_LEN];
+ struct ap_varbuf vb;
const char *args;
char *cmd_name;
+ apr_status_t rc;
+ apr_size_t max_len = VARBUF_MAX_LEN;
+ if (cmd->pool == cmd->temp_pool)
+ max_len = HUGE_STRING_LEN; /* lower limit for .htaccess */
+
+ ap_varbuf_init(cmd->temp_pool, &vb, VARBUF_INIT_LEN);
- while(!(ap_cfg_getline(l, MAX_STRING_LEN, cmd->config_file))) {
+ while((rc = ap_varbuf_cfg_getline(&vb, cmd->config_file, max_len))
+ == APR_SUCCESS) {
#if RESOLVE_ENV_PER_TOKEN
- args = l;
+ args = vb.buf;
#else
- args = ap_resolve_env(cmd->temp_pool, l);
+ args = ap_resolve_env(cmd->temp_pool, vb.buf);
#endif
- cmd_name = ap_getword_conf(cmd->pool, &args);
+ cmd_name = ap_getword_conf(cmd->temp_pool, &args);
if (cmd_name[0] == '<') {
if (cmd_name[1] == '/') {
cmd_name[strlen(cmd_name) - 1] = '\0';
@@ -1391,6 +1571,7 @@ AP_DECLARE(const char *) ap_soak_end_container(cmd_parms *cmd, char *directive)
cmd_name, ">", NULL);
}
+ ap_varbuf_free(&vb);
return NULL; /* found end of container */
}
else {
@@ -1406,6 +1587,8 @@ AP_DECLARE(const char *) ap_soak_end_container(cmd_parms *cmd, char *directive)
}
}
}
+ if (rc != APR_EOF && rc != APR_SUCCESS)
+ return ap_pcfg_strerror(cmd->temp_pool, cmd->config_file, rc);
return apr_pstrcat(cmd->pool, "Expected </",
directive + 1, "> before end of configuration",
@@ -1420,7 +1603,7 @@ static const char *execute_now(char *cmd_line, const char *args,
{
const command_rec *cmd;
ap_mod_list *ml;
- char *dir = apr_pstrdup(parms->pool, cmd_line);
+ char *dir = apr_pstrdup(parms->temp_pool, cmd_line);
ap_str_tolower(dir);
@@ -1465,31 +1648,31 @@ typedef struct {
/* arr_elts_getstr() returns the next line from the string array. */
-static void *arr_elts_getstr(void *buf, size_t bufsiz, void *param)
+static apr_status_t arr_elts_getstr(void *buf, size_t bufsiz, void *param)
{
arr_elts_param_t *arr_param = (arr_elts_param_t *)param;
+ char *elt;
/* End of array reached? */
if (++arr_param->curr_idx > arr_param->array->nelts)
- return NULL;
+ return APR_EOF;
/* return the line */
- apr_cpystrn(buf,
- ((char **)arr_param->array->elts)[arr_param->curr_idx - 1],
- bufsiz);
-
- return buf;
+ elt = ((char **)arr_param->array->elts)[arr_param->curr_idx - 1];
+ if (apr_cpystrn(buf, elt, bufsiz) - (char *)buf >= bufsiz - 1)
+ return APR_ENOSPC;
+ return APR_SUCCESS;
}
/* arr_elts_close(): dummy close routine (makes sure no more lines can be read) */
-static int arr_elts_close(void *param)
+static apr_status_t arr_elts_close(void *param)
{
arr_elts_param_t *arr_param = (arr_elts_param_t *)param;
arr_param->curr_idx = arr_param->array->nelts;
- return 0;
+ return APR_SUCCESS;
}
static const char *process_command_config(server_rec *s,
@@ -1532,7 +1715,7 @@ static const char *process_command_config(server_rec *s,
}
typedef struct {
- char *fname;
+ const char *fname;
} fnames;
static int fname_alphasort(const void *fn1, const void *fn2)
@@ -1543,25 +1726,65 @@ static int fname_alphasort(const void *fn1, const void *fn2)
return strcmp(f1->fname,f2->fname);
}
+AP_DECLARE(const char *) ap_process_resource_config(server_rec *s,
+ const char *fname,
+ ap_directive_t **conftree,
+ apr_pool_t *p,
+ apr_pool_t *ptemp)
+{
+ ap_configfile_t *cfp;
+ cmd_parms parms;
+ apr_status_t rv;
+ const char *error;
+
+ parms = default_parms;
+ parms.pool = p;
+ parms.temp_pool = ptemp;
+ parms.server = s;
+ parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT);
+ parms.override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI;
+
+ rv = ap_pcfg_openfile(&cfp, p, fname);
+ if (rv != APR_SUCCESS) {
+ char errmsg[120];
+ return apr_psprintf(p, "Could not open configuration file %s: %s",
+ fname, apr_strerror(rv, errmsg, sizeof errmsg));
+ }
+
+ parms.config_file = cfp;
+ error = ap_build_config(&parms, p, ptemp, conftree);
+ ap_cfg_closefile(cfp);
+
+ if (error) {
+ if (parms.err_directive)
+ return apr_psprintf(p, "Syntax error on line %d of %s: %s",
+ parms.err_directive->line_num,
+ parms.err_directive->filename, error);
+ else
+ return error;
+ }
+
+ return NULL;
+}
+
static const char *process_resource_config_nofnmatch(server_rec *s,
const char *fname,
ap_directive_t **conftree,
apr_pool_t *p,
apr_pool_t *ptemp,
- unsigned depth)
+ unsigned depth,
+ int optional)
{
- cmd_parms parms;
- ap_configfile_t *cfp;
const char *error;
apr_status_t rv;
- if (ap_is_directory(p, fname)) {
+ if (ap_is_directory(ptemp, fname)) {
apr_dir_t *dirp;
apr_finfo_t dirent;
int current;
apr_array_header_t *candidates = NULL;
fnames *fnew;
- char *path = apr_pstrdup(p, fname);
+ char *path = apr_pstrdup(ptemp, fname);
if (++depth > AP_MAX_INCLUDE_DIR_DEPTH) {
return apr_psprintf(p, "Directory %s exceeds the maximum include "
@@ -1575,20 +1798,20 @@ static const char *process_resource_config_nofnmatch(server_rec *s,
* entries here and store 'em away. Recall we need full pathnames
* for this.
*/
- rv = apr_dir_open(&dirp, path, p);
+ rv = apr_dir_open(&dirp, path, ptemp);
if (rv != APR_SUCCESS) {
char errmsg[120];
return apr_psprintf(p, "Could not open config directory %s: %s",
path, apr_strerror(rv, errmsg, sizeof errmsg));
}
- candidates = apr_array_make(p, 1, sizeof(fnames));
+ candidates = apr_array_make(ptemp, 1, sizeof(fnames));
while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
/* strip out '.' and '..' */
if (strcmp(dirent.name, ".")
&& strcmp(dirent.name, "..")) {
fnew = (fnames *) apr_array_push(candidates);
- fnew->fname = ap_make_full_path(p, path, dirent.name);
+ fnew->fname = ap_make_full_path(ptemp, path, dirent.name);
}
}
@@ -1605,7 +1828,7 @@ static const char *process_resource_config_nofnmatch(server_rec *s,
fnew = &((fnames *) candidates->elts)[current];
error = process_resource_config_nofnmatch(s, fnew->fname,
conftree, p, ptemp,
- depth);
+ depth, optional);
if (error) {
return error;
}
@@ -1615,39 +1838,124 @@ static const char *process_resource_config_nofnmatch(server_rec *s,
return NULL;
}
- /* GCC's initialization extensions are soooo nice here... */
- parms = default_parms;
- parms.pool = p;
- parms.temp_pool = ptemp;
- parms.server = s;
- parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT);
- parms.override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI;
+ return ap_process_resource_config(s, fname, conftree, p, ptemp);
+}
- rv = ap_pcfg_openfile(&cfp, p, fname);
+static const char *process_resource_config_fnmatch(server_rec *s,
+ const char *path,
+ const char *fname,
+ ap_directive_t **conftree,
+ apr_pool_t *p,
+ apr_pool_t *ptemp,
+ unsigned depth,
+ int optional)
+{
+ const char *rest;
+ apr_status_t rv;
+ apr_dir_t *dirp;
+ apr_finfo_t dirent;
+ apr_array_header_t *candidates = NULL;
+ fnames *fnew;
+ int current;
+
+ /* find the first part of the filename */
+ rest = ap_strchr_c(fname, '/');
+ if (rest) {
+ fname = apr_pstrndup(ptemp, fname, rest - fname);
+ rest++;
+ }
+
+ /* optimisation - if the filename isn't a wildcard, process it directly */
+ if (!apr_fnmatch_test(fname)) {
+ path = ap_make_full_path(ptemp, path, fname);
+ if (!rest) {
+ return process_resource_config_nofnmatch(s, path,
+ conftree, p,
+ ptemp, 0, optional);
+ }
+ else {
+ return process_resource_config_fnmatch(s, path, rest,
+ conftree, p,
+ ptemp, 0, optional);
+ }
+ }
+
+ /*
+ * first course of business is to grok all the directory
+ * entries here and store 'em away. Recall we need full pathnames
+ * for this.
+ */
+ rv = apr_dir_open(&dirp, path, ptemp);
if (rv != APR_SUCCESS) {
char errmsg[120];
- return apr_psprintf(p, "Could not open configuration file %s: %s",
- fname, apr_strerror(rv, errmsg, sizeof errmsg));
+ return apr_psprintf(p, "Could not open config directory %s: %s",
+ path, apr_strerror(rv, errmsg, sizeof errmsg));
+ }
+
+ candidates = apr_array_make(ptemp, 1, sizeof(fnames));
+ while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) {
+ /* strip out '.' and '..' */
+ if (strcmp(dirent.name, ".")
+ && strcmp(dirent.name, "..")
+ && (apr_fnmatch(fname, dirent.name,
+ APR_FNM_PERIOD) == APR_SUCCESS)) {
+ const char *full_path = ap_make_full_path(ptemp, path, dirent.name);
+ /* If matching internal to path, and we happen to match something
+ * other than a directory, skip it
+ */
+ if (rest && (rv == APR_SUCCESS) && (dirent.filetype != APR_DIR)) {
+ continue;
+ }
+ fnew = (fnames *) apr_array_push(candidates);
+ fnew->fname = full_path;
+ }
}
- parms.config_file = cfp;
- error = ap_build_config(&parms, p, ptemp, conftree);
- ap_cfg_closefile(cfp);
+ apr_dir_close(dirp);
+ if (candidates->nelts != 0) {
+ const char *error;
- if (error) {
- return apr_psprintf(p, "Syntax error on line %d of %s: %s",
- parms.err_directive->line_num,
- parms.err_directive->filename, error);
+ qsort((void *) candidates->elts, candidates->nelts,
+ sizeof(fnames), fname_alphasort);
+
+ /*
+ * Now recurse these... we handle errors and subdirectories
+ * via the recursion, which is nice
+ */
+ for (current = 0; current < candidates->nelts; ++current) {
+ fnew = &((fnames *) candidates->elts)[current];
+ if (!rest) {
+ error = process_resource_config_nofnmatch(s, fnew->fname,
+ conftree, p,
+ ptemp, 0, optional);
+ }
+ else {
+ error = process_resource_config_fnmatch(s, fnew->fname, rest,
+ conftree, p,
+ ptemp, 0, optional);
+ }
+ if (error) {
+ return error;
+ }
+ }
+ }
+ else {
+
+ if (!optional) {
+ return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing "
+ "(use IncludeOptional if required)", fname, path);
+ }
}
return NULL;
}
-AP_DECLARE(const char *) ap_process_resource_config(server_rec *s,
+AP_DECLARE(const char *) ap_process_fnmatch_configs(server_rec *s,
const char *fname,
ap_directive_t **conftree,
apr_pool_t *p,
- apr_pool_t *ptemp)
+ apr_pool_t *ptemp,
+ int optional)
{
/* XXX: lstat() won't work on the wildcard pattern...
*/
@@ -1655,95 +1963,35 @@ AP_DECLARE(const char *) ap_process_resource_config(server_rec *s,
/* don't require conf/httpd.conf if we have a -C or -c switch */
if ((ap_server_pre_read_config->nelts
|| ap_server_post_read_config->nelts)
- && !(strcmp(fname, ap_server_root_relative(p, SERVER_CONFIG_FILE)))) {
+ && !(strcmp(fname, ap_server_root_relative(ptemp, SERVER_CONFIG_FILE)))) {
apr_finfo_t finfo;
- if (apr_stat(&finfo, fname, APR_FINFO_LINK | APR_FINFO_TYPE, p) != APR_SUCCESS)
+ if (apr_stat(&finfo, fname, APR_FINFO_LINK | APR_FINFO_TYPE, ptemp) != APR_SUCCESS)
return NULL;
}
if (!apr_fnmatch_test(fname)) {
- return process_resource_config_nofnmatch(s, fname, conftree, p, ptemp,
- 0);
+ return ap_process_resource_config(s, fname, conftree, p, ptemp);
}
else {
- apr_dir_t *dirp;
- apr_finfo_t dirent;
- int current;
- apr_array_header_t *candidates = NULL;
- fnames *fnew;
- apr_status_t rv;
- char *path = apr_pstrdup(p, fname), *pattern = NULL;
-
- pattern = ap_strrchr(path, '/');
+ apr_status_t status;
+ const char *rootpath, *filepath = fname;
- AP_DEBUG_ASSERT(pattern != NULL); /* path must be absolute. */
+ /* locate the start of the directories proper */
+ status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, ptemp);
- *pattern++ = '\0';
-
- if (apr_fnmatch_test(path)) {
- return apr_pstrcat(p, "Wildcard patterns not allowed in Include ",
- fname, NULL);
- }
-
- if (!ap_is_directory(p, path)){
- return apr_pstrcat(p, "Include directory '", path, "' not found",
- NULL);
- }
-
- if (!apr_fnmatch_test(pattern)) {
- return apr_pstrcat(p, "Must include a wildcard pattern for "
- "Include ", fname, NULL);
- }
-
- /*
- * first course of business is to grok all the directory
- * entries here and store 'em away. Recall we need full pathnames
- * for this.
- */
- rv = apr_dir_open(&dirp, path, p);
- if (rv != APR_SUCCESS) {
- char errmsg[120];
- return apr_psprintf(p, "Could not open config directory %s: %s",
- path, apr_strerror(rv, errmsg, sizeof errmsg));
+ /* we allow APR_SUCCESS and APR_EINCOMPLETE */
+ if (APR_ERELATIVE == status) {
+ return apr_pstrcat(p, "Include must have an absolute path, ", fname, NULL);
}
-
- candidates = apr_array_make(p, 1, sizeof(fnames));
- while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
- /* strip out '.' and '..' */
- if (strcmp(dirent.name, ".")
- && strcmp(dirent.name, "..")
- && (apr_fnmatch(pattern, dirent.name,
- APR_FNM_PERIOD) == APR_SUCCESS)) {
- fnew = (fnames *) apr_array_push(candidates);
- fnew->fname = ap_make_full_path(p, path, dirent.name);
- }
+ else if (APR_EBADPATH == status) {
+ return apr_pstrcat(p, "Include has a bad path, ", fname, NULL);
}
- apr_dir_close(dirp);
- if (candidates->nelts != 0) {
- const char *error;
-
- qsort((void *) candidates->elts, candidates->nelts,
- sizeof(fnames), fname_alphasort);
-
- /*
- * Now recurse these... we handle errors and subdirectories
- * via the recursion, which is nice
- */
- for (current = 0; current < candidates->nelts; ++current) {
- fnew = &((fnames *) candidates->elts)[current];
- error = process_resource_config_nofnmatch(s, fnew->fname,
- conftree, p,
- ptemp, 0);
- if (error) {
- return error;
- }
- }
- }
+ /* walk the filepath */
+ return process_resource_config_fnmatch(s, rootpath, filepath, conftree, p, ptemp,
+ 0, optional);
}
-
- return NULL;
}
AP_DECLARE(int) ap_process_config_tree(server_rec *s,
@@ -1764,12 +2012,12 @@ AP_DECLARE(int) ap_process_config_tree(server_rec *s,
errmsg = ap_walk_config(conftree, &parms, s->lookup_defaults);
if (errmsg) {
- ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0, p,
- "Syntax error on line %d of %s:",
- parms.err_directive->line_num,
- parms.err_directive->filename);
- ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0, p,
- "%s", errmsg);
+ if (parms.err_directive)
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0, p, APLOGNO(00526)
+ "Syntax error on line %d of %s:",
+ parms.err_directive->line_num,
+ parms.err_directive->filename);
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0, p, "%s", errmsg);
return HTTP_INTERNAL_SERVER_ERROR;
}
@@ -1778,7 +2026,7 @@ AP_DECLARE(int) ap_process_config_tree(server_rec *s,
AP_CORE_DECLARE(int) ap_parse_htaccess(ap_conf_vector_t **result,
request_rec *r, int override,
- int override_opts,
+ int override_opts, apr_table_t *override_list,
const char *d, const char *access_name)
{
ap_configfile_t *f = NULL;
@@ -1800,6 +2048,7 @@ AP_CORE_DECLARE(int) ap_parse_htaccess(ap_conf_vector_t **result,
parms = default_parms;
parms.override = override;
parms.override_opts = override_opts;
+ parms.override_list = override_list;
parms.pool = r->pool;
parms.temp_pool = r->pool;
parms.server = r->server;
@@ -1840,10 +2089,11 @@ AP_CORE_DECLARE(int) ap_parse_htaccess(ap_conf_vector_t **result,
else {
if (!APR_STATUS_IS_ENOENT(status)
&& !APR_STATUS_IS_ENOTDIR(status)) {
- ap_log_rerror(APLOG_MARK, APLOG_CRIT, status, r,
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, status, r, APLOGNO(00529)
"%s pcfg_openfile: unable to check htaccess file, "
- "ensure it is readable",
- filename);
+ "ensure it is readable and that '%s' "
+ "is executable",
+ filename, d);
apr_table_setn(r->notes, "error-notes",
"Server unable to read htaccess file, denying "
"access to be safe");
@@ -1884,7 +2134,8 @@ AP_CORE_DECLARE(const char *) ap_init_virtual_host(apr_pool_t *p,
s->keep_alive = -1;
s->keep_alive_max = -1;
s->error_log = main_server->error_log;
- s->loglevel = main_server->loglevel;
+ s->log.level = APLOG_UNSET;
+ s->log.module_levels = NULL;
/* useful default, otherwise we get a port of 0 on redirects */
s->port = main_server->port;
s->next = NULL;
@@ -1905,10 +2156,52 @@ AP_CORE_DECLARE(const char *) ap_init_virtual_host(apr_pool_t *p,
return ap_parse_vhost_addrs(p, hostname, s);
}
+AP_DECLARE(struct ap_logconf *) ap_new_log_config(apr_pool_t *p,
+ const struct ap_logconf *old)
+{
+ struct ap_logconf *l = apr_pcalloc(p, sizeof(struct ap_logconf));
+ if (old) {
+ l->level = old->level;
+ if (old->module_levels) {
+ l->module_levels =
+ apr_pmemdup(p, old->module_levels, conf_vector_length);
+ }
+ }
+ else {
+ l->level = APLOG_UNSET;
+ }
+ return l;
+}
+
+AP_DECLARE(void) ap_merge_log_config(const struct ap_logconf *old_conf,
+ struct ap_logconf *new_conf)
+{
+ if (new_conf->level != APLOG_UNSET) {
+ /* Setting the main loglevel resets all per-module log levels.
+ * I.e. if new->level has been set, we must ignore old->module_levels.
+ */
+ return;
+ }
+
+ new_conf->level = old_conf->level;
+ if (new_conf->module_levels == NULL) {
+ new_conf->module_levels = old_conf->module_levels;
+ }
+ else if (old_conf->module_levels != NULL) {
+ int i;
+ for (i = 0; i < conf_vector_length; i++) {
+ if (new_conf->module_levels[i] == APLOG_UNSET)
+ new_conf->module_levels[i] = old_conf->module_levels[i];
+ }
+ }
+}
AP_DECLARE(void) ap_fixup_virtual_hosts(apr_pool_t *p, server_rec *main_server)
{
server_rec *virt;
+ core_dir_config *dconf =
+ ap_get_core_module_config(main_server->lookup_defaults);
+ dconf->log = &main_server->log;
for (virt = main_server->next; virt; virt = virt->next) {
merge_server_configs(p, main_server->module_config,
@@ -1933,6 +2226,11 @@ AP_DECLARE(void) ap_fixup_virtual_hosts(apr_pool_t *p, server_rec *main_server)
if (virt->keep_alive_max == -1)
virt->keep_alive_max = main_server->keep_alive_max;
+ ap_merge_log_config(&main_server->log, &virt->log);
+
+ dconf = ap_get_core_module_config(virt->lookup_defaults);
+ dconf->log = &virt->log;
+
/* XXX: this is really something that should be dealt with by a
* post-config api phase
*/
@@ -1965,7 +2263,8 @@ static server_rec *init_server_config(process_rec *process, apr_pool_t *p)
s->server_hostname = NULL;
s->server_scheme = NULL;
s->error_fname = DEFAULT_ERRORLOG;
- s->loglevel = DEFAULT_LOGLEVEL;
+ s->log.level = DEFAULT_LOGLEVEL;
+ s->log.module_levels = NULL;
s->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE;
s->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE;
s->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS;
@@ -1978,8 +2277,13 @@ static server_rec *init_server_config(process_rec *process, apr_pool_t *p)
/* NOT virtual host; don't match any real network interface */
rv = apr_sockaddr_info_get(&s->addrs->host_addr,
- NULL, APR_INET, 0, 0, p);
- ap_assert(rv == APR_SUCCESS); /* otherwise: bug or no storage */
+ NULL, APR_UNSPEC, 0, 0, p);
+ if (rv != APR_SUCCESS) {
+ /* should we test here for rv being an EAIERR? */
+ ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, rv, NULL, APLOGNO(00530)
+ "initialisation: bug or getaddrinfo fail");
+ return NULL;
+ }
s->addrs->host_port = 0; /* matches any port */
s->addrs->virthost = ""; /* must be non-NULL */
@@ -1992,6 +2296,38 @@ static server_rec *init_server_config(process_rec *process, apr_pool_t *p)
}
+static apr_status_t reset_conf_vector_length(void *dummy)
+{
+ reserved_module_slots = 0;
+ conf_vector_length = max_modules;
+ return APR_SUCCESS;
+}
+
+static int conf_vector_length_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp)
+{
+ /*
+ * We have loaded all modules that are loaded by EXEC_ON_READ directives.
+ * From now on we reduce the size of the config vectors to what we need,
+ * plus what has been reserved (e.g. by mod_perl) for additional modules
+ * loaded later on.
+ * If max_modules is too small, ap_add_module() will abort.
+ */
+ if (total_modules + reserved_module_slots < max_modules) {
+ conf_vector_length = total_modules + reserved_module_slots;
+ }
+ apr_pool_cleanup_register(pconf, NULL, reset_conf_vector_length,
+ apr_pool_cleanup_null);
+ return OK;
+}
+
+
+AP_CORE_DECLARE(void) ap_register_config_hooks(apr_pool_t *p)
+{
+ ap_hook_pre_config(conf_vector_length_pre_config, NULL, NULL,
+ APR_HOOK_REALLY_LAST);
+}
+
AP_DECLARE(server_rec*) ap_read_config(process_rec *process, apr_pool_t *ptemp,
const char *filename,
ap_directive_t **conftree)
@@ -1999,6 +2335,9 @@ AP_DECLARE(server_rec*) ap_read_config(process_rec *process, apr_pool_t *ptemp,
const char *confname, *error;
apr_pool_t *p = process->pconf;
server_rec *s = init_server_config(process, p);
+ if (s == NULL) {
+ return s;
+ }
init_config_globals(p);
@@ -2018,7 +2357,7 @@ AP_DECLARE(server_rec*) ap_read_config(process_rec *process, apr_pool_t *ptemp,
if (!confname) {
ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT,
- APR_EBADPATH, NULL, "Invalid config file path %s",
+ APR_EBADPATH, NULL, APLOGNO(00532) "Invalid config file path %s",
filename);
return NULL;
}
@@ -2030,6 +2369,13 @@ AP_DECLARE(server_rec*) ap_read_config(process_rec *process, apr_pool_t *ptemp,
return NULL;
}
+ error = ap_check_mpm();
+ if (error) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, 0, NULL, APLOGNO(00534)
+ "%s: Configuration error: %s", ap_server_argv0, error);
+ return NULL;
+ }
+
error = process_command_config(s, ap_server_post_read_config, conftree,
p, ptemp);
@@ -2092,10 +2438,10 @@ static void show_overrides(const command_rec *pc, module *pm)
printf("anywhere");
}
else if (pc->req_override & RSRC_CONF) {
- printf("only outside <Directory>, <Files> or <Location>");
+ printf("only outside <Directory>, <Files>, <Location>, or <If>");
}
else {
- printf("only inside <Directory>, <Files> or <Location>");
+ printf("only inside <Directory>, <Files>, <Location>, or <If>");
}
/* Warn if the directive is allowed inside <Directory> or .htaccess
@@ -2184,7 +2530,42 @@ AP_DECLARE(void) ap_show_modules(void)
printf(" %s\n", ap_loaded_modules[n]->name);
}
-AP_DECLARE(const char *) ap_show_mpm(void)
+AP_DECLARE(void *) ap_retained_data_get(const char *key)
+{
+ void *retained;
+
+ apr_pool_userdata_get((void *)&retained, key, ap_pglobal);
+ return retained;
+}
+
+AP_DECLARE(void *) ap_retained_data_create(const char *key, apr_size_t size)
+{
+ void *retained;
+
+ retained = apr_pcalloc(ap_pglobal, size);
+ apr_pool_userdata_set((const void *)retained, key, apr_pool_cleanup_null, ap_pglobal);
+ return retained;
+}
+
+static int count_directives_sub(const char *directive, ap_directive_t *current)
+{
+ int count = 0;
+ while (current != NULL) {
+ if (current->first_child != NULL)
+ count += count_directives_sub(directive, current->first_child);
+ if (strcasecmp(current->directive, directive) == 0)
+ count++;
+ current = current->next;
+ }
+ return count;
+}
+
+AP_DECLARE(void) ap_reserve_module_slots(int count)
+{
+ reserved_module_slots += count;
+}
+
+AP_DECLARE(void) ap_reserve_module_slots_directive(const char *directive)
{
- return MPM_NAME;
+ ap_reserve_module_slots(count_directives_sub(directive, ap_conftree));
}
diff --git a/server/config.m4 b/server/config.m4
index 85fa4d17..dde51ed3 100644
--- a/server/config.m4
+++ b/server/config.m4
@@ -13,3 +13,7 @@ AC_CHECK_FUNCS(syslog)
dnl Obsolete scoreboard code uses this.
AC_CHECK_HEADERS(sys/times.h)
AC_CHECK_FUNCS(times)
+
+dnl Add expr header files to INCLUDES
+# util_expr needs header files in server source dir
+APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/server])
diff --git a/server/connection.c b/server/connection.c
index 3db5a2e6..6e4495f8 100644
--- a/server/connection.c
+++ b/server/connection.c
@@ -17,14 +17,12 @@
#include "apr.h"
#include "apr_strings.h"
-#define CORE_PRIVATE
#include "ap_config.h"
#include "httpd.h"
#include "http_connection.h"
#include "http_request.h"
#include "http_protocol.h"
#include "ap_mpm.h"
-#include "mpm_default.h"
#include "http_config.h"
#include "http_core.h"
#include "http_vhost.h"
@@ -95,23 +93,23 @@ AP_CORE_DECLARE(void) ap_flush_conn(conn_rec *c)
* all the response data has been sent to the client.
*/
#define SECONDS_TO_LINGER 2
-AP_DECLARE(void) ap_lingering_close(conn_rec *c)
+
+AP_DECLARE(int) ap_start_lingering_close(conn_rec *c)
{
- char dummybuf[512];
- apr_size_t nbytes;
- apr_time_t timeup = 0;
- apr_socket_t *csd = ap_get_module_config(c->conn_config, &core_module);
+ apr_socket_t *csd = ap_get_conn_socket(c);
if (!csd) {
- return;
+ return 1;
}
- ap_update_child_status(c->sbh, SERVER_CLOSING, NULL);
+ if (c->sbh) {
+ ap_update_child_status(c->sbh, SERVER_CLOSING, NULL);
+ }
#ifdef NO_LINGCLOSE
ap_flush_conn(c); /* just close it */
apr_socket_close(csd);
- return;
+ return 1;
#endif
/* Close the connection, being careful to send out whatever is still
@@ -124,7 +122,7 @@ AP_DECLARE(void) ap_lingering_close(conn_rec *c)
if (c->aborted) {
apr_socket_close(csd);
- return;
+ return 1;
}
/* Shut down the socket for write, which will send a FIN
@@ -133,6 +131,20 @@ AP_DECLARE(void) ap_lingering_close(conn_rec *c)
if (apr_socket_shutdown(csd, APR_SHUTDOWN_WRITE) != APR_SUCCESS
|| c->aborted) {
apr_socket_close(csd);
+ return 1;
+ }
+
+ return 0;
+}
+
+AP_DECLARE(void) ap_lingering_close(conn_rec *c)
+{
+ char dummybuf[512];
+ apr_size_t nbytes;
+ apr_time_t timeup = 0;
+ apr_socket_t *csd = ap_get_conn_socket(c);
+
+ if (ap_start_lingering_close(c)) {
return;
}
@@ -158,9 +170,9 @@ AP_DECLARE(void) ap_lingering_close(conn_rec *c)
* First time through;
* calculate now + 30 seconds (MAX_SECS_TO_LINGER).
*
- * If some module requested a shortened waiting period, only wait
- * for 2s (SECONDS_TO_LINGER). This is useful for mitigating
- * certain DoS attacks.
+ * If some module requested a shortened waiting period, only wait for
+ * 2s (SECONDS_TO_LINGER). This is useful for mitigating certain
+ * DoS attacks.
*/
if (apr_table_get(c->notes, "short-lingering-close")) {
timeup = apr_time_now() + apr_time_from_sec(SECONDS_TO_LINGER);
diff --git a/server/core.c b/server/core.c
index ca727139..c1f472cf 100644
--- a/server/core.c
+++ b/server/core.c
@@ -20,14 +20,13 @@
#include "apr_fnmatch.h"
#include "apr_hash.h"
#include "apr_thread_proc.h" /* for RLIMIT stuff */
-#include "apr_hooks.h"
+#include "apr_random.h"
#define APR_WANT_IOVEC
#define APR_WANT_STRFUNC
#define APR_WANT_MEMFUNC
#include "apr_want.h"
-#define CORE_PRIVATE
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
@@ -42,7 +41,8 @@
#include "apr_buckets.h"
#include "util_filter.h"
#include "util_ebcdic.h"
-#include "mpm.h"
+#include "util_mutex.h"
+#include "util_time.h"
#include "mpm_common.h"
#include "scoreboard.h"
#include "mod_core.h"
@@ -51,6 +51,13 @@
#include "mod_so.h" /* for ap_find_loaded_module_symbol */
+#if defined(RLIMIT_CPU) || defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS) || defined (RLIMIT_NPROC)
+#include "unixd.h"
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
/* LimitRequestBody handling */
#define AP_LIMIT_REQ_BODY_UNSET ((apr_off_t) -1)
#define AP_DEFAULT_LIMIT_REQ_BODY ((apr_off_t) 0)
@@ -67,7 +74,11 @@
#endif
/* valid in core-conf, but not in runtime r->used_path_info */
-#define AP_ACCEPT_PATHINFO_UNSET 3
+#define AP_ACCEPT_PATHINFO_UNSET 3
+
+#define AP_CONTENT_MD5_OFF 0
+#define AP_CONTENT_MD5_ON 1
+#define AP_CONTENT_MD5_UNSET 2
APR_HOOK_STRUCT(
APR_HOOK_LINK(get_mgmt_items)
@@ -81,66 +92,68 @@ AP_IMPLEMENT_HOOK_RUN_ALL(int, get_mgmt_items,
* server operations, including options and commands which control the
* operation of other modules. Consider this the bureaucracy module.
*
- * The core module also defines handlers, etc., do handle just enough
- * to allow a server with the core module ONLY to actually serve documents
- * (though it slaps DefaultType on all of 'em); this was useful in testing,
- * but may not be worth preserving.
+ * The core module also defines handlers, etc., to handle just enough
+ * to allow a server with the core module ONLY to actually serve documents.
*
* This file could almost be mod_core.c, except for the stuff which affects
* the http_conf_globals.
*/
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
/* Handles for core filters */
AP_DECLARE_DATA ap_filter_rec_t *ap_subreq_core_filter_handle;
AP_DECLARE_DATA ap_filter_rec_t *ap_core_output_filter_handle;
AP_DECLARE_DATA ap_filter_rec_t *ap_content_length_filter_handle;
AP_DECLARE_DATA ap_filter_rec_t *ap_core_input_filter_handle;
+/* Provide ap_document_root_check storage and default value = true */
+AP_DECLARE_DATA int ap_document_root_check = 1;
+
/* magic pointer for ErrorDocument xxx "default" */
static char errordocument_default;
-/* Default ap_document_root_check to default value: true */
-AP_DECLARE_DATA int ap_document_root_check = 1;
+static apr_array_header_t *saved_server_config_defines = NULL;
+static apr_table_t *server_config_defined_vars = NULL;
+
+AP_DECLARE_DATA int ap_main_state = AP_SQ_MS_INITIAL_STARTUP;
+AP_DECLARE_DATA int ap_run_mode = AP_SQ_RM_UNKNOWN;
+AP_DECLARE_DATA int ap_config_generation = 0;
static void *create_core_dir_config(apr_pool_t *a, char *dir)
{
core_dir_config *conf;
- int i;
conf = (core_dir_config *)apr_pcalloc(a, sizeof(core_dir_config));
/* conf->r and conf->d[_*] are initialized by dirsection() or left NULL */
- conf->opts = dir ? OPT_UNSET : OPT_UNSET|OPT_ALL;
+ conf->opts = dir ? OPT_UNSET : OPT_UNSET|OPT_SYM_LINKS;
conf->opts_add = conf->opts_remove = OPT_NONE;
- conf->override = dir ? OR_UNSET : OR_UNSET|OR_ALL;
+ conf->override = OR_UNSET|OR_NONE;
conf->override_opts = OPT_UNSET | OPT_ALL | OPT_SYM_OWNER | OPT_MULTI;
- conf->content_md5 = 2;
+ conf->content_md5 = AP_CONTENT_MD5_UNSET;
conf->accept_path_info = AP_ACCEPT_PATHINFO_UNSET;
conf->use_canonical_name = USE_CANONICAL_NAME_UNSET;
conf->use_canonical_phys_port = USE_CANONICAL_PHYS_PORT_UNSET;
conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET;
- conf->satisfy = apr_palloc(a, sizeof(*conf->satisfy) * METHODS);
- for (i = 0; i < METHODS; ++i) {
- conf->satisfy[i] = SATISFY_NOSPEC;
- }
-#ifdef RLIMIT_CPU
- conf->limit_cpu = NULL;
-#endif
-#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
- conf->limit_mem = NULL;
-#endif
-#ifdef RLIMIT_NPROC
- conf->limit_nproc = NULL;
-#endif
+ /*
+ * left as NULL (we use apr_pcalloc):
+ * conf->limit_cpu = NULL;
+ * conf->limit_mem = NULL;
+ * conf->limit_nproc = NULL;
+ * conf->sec_file = NULL;
+ * conf->sec_if = NULL;
+ */
conf->limit_req_body = AP_LIMIT_REQ_BODY_UNSET;
conf->limit_xml_body = AP_LIMIT_UNSET;
- conf->sec_file = apr_array_make(a, 2, sizeof(ap_conf_vector_t *));
conf->server_signature = srv_sig_unset;
@@ -148,11 +161,12 @@ static void *create_core_dir_config(apr_pool_t *a, char *dir)
conf->add_default_charset_name = DEFAULT_ADD_DEFAULT_CHARSET_NAME;
/* Overriding all negotiation
+ * Set NULL by apr_pcalloc:
+ * conf->mime_type = NULL;
+ * conf->handler = NULL;
+ * conf->output_filters = NULL;
+ * conf->input_filters = NULL;
*/
- conf->mime_type = NULL;
- conf->handler = NULL;
- conf->output_filters = NULL;
- conf->input_filters = NULL;
/*
* Flag for use of inodes in ETags.
@@ -165,66 +179,14 @@ static void *create_core_dir_config(apr_pool_t *a, char *dir)
conf->enable_sendfile = ENABLE_SENDFILE_UNSET;
conf->allow_encoded_slashes = 0;
conf->decode_encoded_slashes = 0;
-
+
conf->max_ranges = AP_MAXRANGES_UNSET;
+ conf->max_overlaps = AP_MAXRANGES_UNSET;
+ conf->max_reversals = AP_MAXRANGES_UNSET;
return (void *)conf;
}
-/*
- * Overlay one hash table of ct_output_filters onto another
- */
-static void *merge_ct_filters(apr_pool_t *p,
- const void *key,
- apr_ssize_t klen,
- const void *overlay_val,
- const void *base_val,
- const void *data)
-{
- ap_filter_rec_t *cur;
- const ap_filter_rec_t *overlay_info = (const ap_filter_rec_t *)overlay_val;
- const ap_filter_rec_t *base_info = (const ap_filter_rec_t *)base_val;
-
- cur = NULL;
-
- while (overlay_info) {
- ap_filter_rec_t *new;
-
- new = apr_pcalloc(p, sizeof(ap_filter_rec_t));
- new->name = apr_pstrdup(p, overlay_info->name);
- new->next = cur;
- cur = new;
- overlay_info = overlay_info->next;
- }
-
- while (base_info) {
- ap_filter_rec_t *f;
- int found = 0;
-
- /* We can't have dups. */
- f = cur;
- while (f) {
- if (!strcasecmp(base_info->name, f->name)) {
- found = 1;
- break;
- }
-
- f = f->next;
- }
-
- if (!found) {
- f = apr_pcalloc(p, sizeof(ap_filter_rec_t));
- f->name = apr_pstrdup(p, base_info->name);
- f->next = cur;
- cur = f;
- }
-
- base_info = base_info->next;
- }
-
- return cur;
-}
-
static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
{
core_dir_config *base = (core_dir_config *)basev;
@@ -241,6 +203,7 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
conf->d_is_fnmatch = new->d_is_fnmatch;
conf->d_components = new->d_components;
conf->r = new->r;
+ conf->condition = new->condition;
if (new->opts & OPT_UNSET) {
/* there was no explicit setting of new->opts, so we merge
@@ -278,27 +241,15 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
conf->override_opts = new->override_opts;
}
- if (new->ap_default_type) {
- conf->ap_default_type = new->ap_default_type;
- }
-
- if (new->ap_auth_type) {
- conf->ap_auth_type = new->ap_auth_type;
- }
-
- if (new->ap_auth_name) {
- conf->ap_auth_name = new->ap_auth_name;
- }
-
- if (new->ap_requires) {
- conf->ap_requires = new->ap_requires;
+ if (conf->override_list == NULL) {
+ conf->override_list = new->override_list;
}
if (conf->response_code_strings == NULL) {
conf->response_code_strings = new->response_code_strings;
}
else if (new->response_code_strings != NULL) {
- /* If we merge, the merge-result must have it's own array
+ /* If we merge, the merge-result must have its own array
*/
conf->response_code_strings = apr_pmemdup(a,
base->response_code_strings,
@@ -317,11 +268,11 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
conf->hostname_lookups = new->hostname_lookups;
}
- if ((new->content_md5 & 2) == 0) {
+ if (new->content_md5 == AP_CONTENT_MD5_UNSET) {
conf->content_md5 = new->content_md5;
}
- if (new->accept_path_info != 3) {
+ if (new->accept_path_info != AP_ACCEPT_PATHINFO_UNSET) {
conf->accept_path_info = new->accept_path_info;
}
@@ -364,22 +315,23 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
conf->sec_file = new->sec_file;
}
else if (new->sec_file) {
- /* If we merge, the merge-result must have it's own array
+ /* If we merge, the merge-result must have its own array
*/
conf->sec_file = apr_array_append(a, base->sec_file, new->sec_file);
}
/* Otherwise we simply use the base->sec_file array
*/
- /* use a separate ->satisfy[] array either way */
- conf->satisfy = apr_palloc(a, sizeof(*conf->satisfy) * METHODS);
- for (i = 0; i < METHODS; ++i) {
- if (new->satisfy[i] != SATISFY_NOSPEC) {
- conf->satisfy[i] = new->satisfy[i];
- } else {
- conf->satisfy[i] = base->satisfy[i];
- }
+ if (!conf->sec_if) {
+ conf->sec_if = new->sec_if;
}
+ else if (new->sec_if) {
+ /* If we merge, the merge-result must have its own array
+ */
+ conf->sec_if = apr_array_append(a, base->sec_if, new->sec_if);
+ }
+ /* Otherwise we simply use the base->sec_if array
+ */
if (new->server_signature != srv_sig_unset) {
conf->server_signature = new->server_signature;
@@ -408,21 +360,6 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
conf->input_filters = new->input_filters;
}
- if (conf->ct_output_filters && new->ct_output_filters) {
- conf->ct_output_filters = apr_hash_merge(a,
- new->ct_output_filters,
- conf->ct_output_filters,
- merge_ct_filters,
- NULL);
- }
- else if (new->ct_output_filters) {
- conf->ct_output_filters = apr_hash_copy(a, new->ct_output_filters);
- }
- else if (conf->ct_output_filters) {
- /* That memcpy above isn't enough. */
- conf->ct_output_filters = apr_hash_copy(a, base->ct_output_filters);
- }
-
/*
* Now merge the setting of the FileETag directive.
*/
@@ -455,11 +392,35 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
conf->allow_encoded_slashes = new->allow_encoded_slashes;
conf->decode_encoded_slashes = new->decode_encoded_slashes;
+ if (new->log) {
+ if (!conf->log) {
+ conf->log = new->log;
+ }
+ else {
+ conf->log = ap_new_log_config(a, new->log);
+ ap_merge_log_config(base->log, conf->log);
+ }
+ }
+
conf->max_ranges = new->max_ranges != AP_MAXRANGES_UNSET ? new->max_ranges : base->max_ranges;
+ conf->max_overlaps = new->max_overlaps != AP_MAXRANGES_UNSET ? new->max_overlaps : base->max_overlaps;
+ conf->max_reversals = new->max_reversals != AP_MAXRANGES_UNSET ? new->max_reversals : base->max_reversals;
return (void*)conf;
}
+#if APR_HAS_SO_ACCEPTFILTER
+#ifndef ACCEPT_FILTER_NAME
+#define ACCEPT_FILTER_NAME "httpready"
+#ifdef __FreeBSD_version
+#if __FreeBSD_version < 411000 /* httpready broken before 4.1.1 */
+#undef ACCEPT_FILTER_NAME
+#define ACCEPT_FILTER_NAME "dataready"
+#endif
+#endif
+#endif
+#endif
+
static void *create_core_server_config(apr_pool_t *a, server_rec *s)
{
core_server_config *conf;
@@ -467,40 +428,44 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
conf = (core_server_config *)apr_pcalloc(a, sizeof(core_server_config));
-#ifdef GPROF
- conf->gprof_dir = NULL;
+ /* global-default / global-only settings */
+
+ if (!is_virtual) {
+ conf->ap_document_root = DOCUMENT_LOCATION;
+ conf->access_name = DEFAULT_ACCESS_FNAME;
+
+ /* A mapping only makes sense in the global context */
+ conf->accf_map = apr_table_make(a, 5);
+#if APR_HAS_SO_ACCEPTFILTER
+ apr_table_setn(conf->accf_map, "http", ACCEPT_FILTER_NAME);
+ apr_table_setn(conf->accf_map, "https", "dataready");
+#else
+ apr_table_setn(conf->accf_map, "http", "data");
+ apr_table_setn(conf->accf_map, "https", "data");
#endif
+ }
+ /* pcalloc'ed - we have NULL's/0's
+ else ** is_virtual ** {
+ conf->ap_document_root = NULL;
+ conf->access_name = NULL;
+ conf->accf_map = NULL;
+ }
+ */
+
+ /* initialization, no special case for global context */
- conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME;
- conf->ap_document_root = is_virtual ? NULL : DOCUMENT_LOCATION;
conf->sec_dir = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
conf->sec_url = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
- /* recursion stopper */
- conf->redirect_limit = 0; /* 0 == unset */
+ /* pcalloc'ed - we have NULL's/0's
+ conf->gprof_dir = NULL;
+
+ ** recursion stopper; 0 == unset
+ conf->redirect_limit = 0;
conf->subreq_limit = 0;
conf->protocol = NULL;
- conf->accf_map = apr_table_make(a, 5);
-
-#ifdef APR_TCP_DEFER_ACCEPT
- apr_table_set(conf->accf_map, "http", "data");
- apr_table_set(conf->accf_map, "https", "data");
-#endif
-
-#if APR_HAS_SO_ACCEPTFILTER
-#ifndef ACCEPT_FILTER_NAME
-#define ACCEPT_FILTER_NAME "httpready"
-#ifdef __FreeBSD_version
-#if __FreeBSD_version < 411000 /* httpready broken before 4.1.1 */
-#undef ACCEPT_FILTER_NAME
-#define ACCEPT_FILTER_NAME "dataready"
-#endif
-#endif
-#endif
- apr_table_set(conf->accf_map, "http", ACCEPT_FILTER_NAME);
- apr_table_set(conf->accf_map, "https", "dataready");
-#endif
+ */
conf->trace_enable = AP_TRACE_UNSET;
@@ -511,36 +476,44 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
{
core_server_config *base = (core_server_config *)basev;
core_server_config *virt = (core_server_config *)virtv;
- core_server_config *conf;
-
- conf = (core_server_config *)apr_pmemdup(p, virt, sizeof(core_server_config));
+ core_server_config *conf = (core_server_config *)
+ apr_pmemdup(p, base, sizeof(core_server_config));
- if (!conf->access_name) {
- conf->access_name = base->access_name;
- }
-
- if (!conf->ap_document_root) {
- conf->ap_document_root = base->ap_document_root;
- }
+ if (virt->ap_document_root)
+ conf->ap_document_root = virt->ap_document_root;
- if (!conf->protocol) {
- conf->protocol = base->protocol;
- }
+ if (virt->access_name)
+ conf->access_name = virt->access_name;
+ /* XXX optimize to keep base->sec_ pointers if virt->sec_ array is empty */
conf->sec_dir = apr_array_append(p, base->sec_dir, virt->sec_dir);
conf->sec_url = apr_array_append(p, base->sec_url, virt->sec_url);
- conf->redirect_limit = virt->redirect_limit
- ? virt->redirect_limit
- : base->redirect_limit;
+ if (virt->redirect_limit)
+ conf->redirect_limit = virt->redirect_limit;
+
+ if (virt->subreq_limit)
+ conf->subreq_limit = virt->subreq_limit;
+
+ if (virt->trace_enable != AP_TRACE_UNSET)
+ conf->trace_enable = virt->trace_enable;
+
+ /* no action for virt->accf_map, not allowed per-vhost */
+
+ if (virt->protocol)
+ conf->protocol = virt->protocol;
+
+ if (virt->gprof_dir)
+ conf->gprof_dir = virt->gprof_dir;
- conf->subreq_limit = virt->subreq_limit
- ? virt->subreq_limit
- : base->subreq_limit;
+ if (virt->error_log_format)
+ conf->error_log_format = virt->error_log_format;
- conf->trace_enable = (virt->trace_enable != AP_TRACE_UNSET)
- ? virt->trace_enable
- : base->trace_enable;
+ if (virt->error_log_conn)
+ conf->error_log_conn = virt->error_log_conn;
+
+ if (virt->error_log_req)
+ conf->error_log_req = virt->error_log_req;
return conf;
}
@@ -551,8 +524,7 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
AP_CORE_DECLARE(void) ap_add_per_dir_conf(server_rec *s, void *dir_config)
{
- core_server_config *sconf = ap_get_module_config(s->module_config,
- &core_module);
+ core_server_config *sconf = ap_get_core_module_config(s->module_config);
void **new_space = (void **)apr_array_push(sconf->sec_dir);
*new_space = dir_config;
@@ -560,20 +532,56 @@ AP_CORE_DECLARE(void) ap_add_per_dir_conf(server_rec *s, void *dir_config)
AP_CORE_DECLARE(void) ap_add_per_url_conf(server_rec *s, void *url_config)
{
- core_server_config *sconf = ap_get_module_config(s->module_config,
- &core_module);
+ core_server_config *sconf = ap_get_core_module_config(s->module_config);
void **new_space = (void **)apr_array_push(sconf->sec_url);
*new_space = url_config;
}
-AP_CORE_DECLARE(void) ap_add_file_conf(core_dir_config *conf, void *url_config)
+AP_CORE_DECLARE(void) ap_add_file_conf(apr_pool_t *p, core_dir_config *conf,
+ void *url_config)
{
- void **new_space = (void **)apr_array_push(conf->sec_file);
+ void **new_space;
+
+ if (!conf->sec_file)
+ conf->sec_file = apr_array_make(p, 2, sizeof(ap_conf_vector_t *));
+ new_space = (void **)apr_array_push(conf->sec_file);
*new_space = url_config;
}
+AP_CORE_DECLARE(const char *) ap_add_if_conf(apr_pool_t *p,
+ core_dir_config *conf,
+ void *if_config)
+{
+ void **new_space;
+ core_dir_config *new = ap_get_module_config(if_config, &core_module);
+
+ if (!conf->sec_if) {
+ conf->sec_if = apr_array_make(p, 2, sizeof(ap_conf_vector_t *));
+ }
+ if (new->condition_ifelse & AP_CONDITION_ELSE) {
+ int have_if = 0;
+ if (conf->sec_if->nelts > 0) {
+ core_dir_config *last;
+ ap_conf_vector_t *lastelt = APR_ARRAY_IDX(conf->sec_if,
+ conf->sec_if->nelts - 1,
+ ap_conf_vector_t *);
+ last = ap_get_module_config(lastelt, &core_module);
+ if (last->condition_ifelse & AP_CONDITION_IF)
+ have_if = 1;
+ }
+ if (!have_if)
+ return "<Else> or <ElseIf> section without previous <If> or "
+ "<ElseIf> section in same scope";
+ }
+
+ new_space = (void **)apr_array_push(conf->sec_if);
+ *new_space = if_config;
+ return NULL;
+}
+
+
/* We need to do a stable sort, qsort isn't stable. So to make it stable
* we'll be maintaining the original index into the list, and using it
* as the minor key during sorting. The major key is the number of
@@ -591,8 +599,8 @@ static int reorder_sorter(const void *va, const void *vb)
core_dir_config *core_a;
core_dir_config *core_b;
- core_a = ap_get_module_config(a->elt, &core_module);
- core_b = ap_get_module_config(b->elt, &core_module);
+ core_a = ap_get_core_module_config(a->elt);
+ core_b = ap_get_core_module_config(b->elt);
/* a regex always sorts after a non-regex
*/
@@ -628,7 +636,7 @@ void ap_core_reorder_directories(apr_pool_t *p, server_rec *s)
int i;
apr_pool_t *tmp;
- sconf = ap_get_module_config(s->module_config, &core_module);
+ sconf = ap_get_core_module_config(s->module_config);
sec_dir = sconf->sec_dir;
nelts = sec_dir->nelts;
elts = (ap_conf_vector_t **)sec_dir->elts;
@@ -671,89 +679,103 @@ void ap_core_reorder_directories(apr_pool_t *p, server_rec *s)
AP_DECLARE(int) ap_allow_options(request_rec *r)
{
core_dir_config *conf =
- (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+ (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
- /* Per comment in http_core.h - the OPT_INC_WITH_EXEC bit is
- * inverted, such that the exposed semantics match that of
- * OPT_INCNOEXEC; i.e., the bit is only enabled if exec= is *not*
- * permitted. */
- if (conf->opts & OPT_INCLUDES) {
- return conf->opts ^ OPT_INC_WITH_EXEC;
- }
- else {
- return conf->opts;
- }
+ return conf->opts;
}
AP_DECLARE(int) ap_allow_overrides(request_rec *r)
{
core_dir_config *conf;
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
+ conf = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
return conf->override;
}
+/*
+ * Optional function coming from mod_authn_core, used for
+ * retrieving the type of autorization
+ */
+static APR_OPTIONAL_FN_TYPE(authn_ap_auth_type) *authn_ap_auth_type;
+
AP_DECLARE(const char *) ap_auth_type(request_rec *r)
{
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- return conf->ap_auth_type;
+ if (authn_ap_auth_type) {
+ return authn_ap_auth_type(r);
+ }
+ return NULL;
}
+/*
+ * Optional function coming from mod_authn_core, used for
+ * retrieving the authorization realm
+ */
+static APR_OPTIONAL_FN_TYPE(authn_ap_auth_name) *authn_ap_auth_name;
+
AP_DECLARE(const char *) ap_auth_name(request_rec *r)
{
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- return conf->ap_auth_name;
+ if (authn_ap_auth_name) {
+ return authn_ap_auth_name(r);
+ }
+ return NULL;
}
-AP_DECLARE(const char *) ap_default_type(request_rec *r)
-{
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
+/*
+ * Optional function coming from mod_access_compat, used to determine how
+ access control interacts with authentication/authorization
+ */
+static APR_OPTIONAL_FN_TYPE(access_compat_ap_satisfies) *access_compat_ap_satisfies;
- return conf->ap_default_type
- ? conf->ap_default_type
- : DEFAULT_CONTENT_TYPE;
+AP_DECLARE(int) ap_satisfies(request_rec *r)
+{
+ if (access_compat_ap_satisfies) {
+ return access_compat_ap_satisfies(r);
+ }
+ return SATISFY_NOSPEC;
}
AP_DECLARE(const char *) ap_document_root(request_rec *r) /* Don't use this! */
{
- core_server_config *conf;
-
- conf = (core_server_config *)ap_get_module_config(r->server->module_config,
- &core_module);
-
- return conf->ap_document_root;
+ core_server_config *sconf;
+ core_request_config *rconf = ap_get_core_module_config(r->request_config);
+ if (rconf->document_root)
+ return rconf->document_root;
+ sconf = ap_get_core_module_config(r->server->module_config);
+ return sconf->ap_document_root;
}
-AP_DECLARE(const apr_array_header_t *) ap_requires(request_rec *r)
+AP_DECLARE(const char *) ap_context_prefix(request_rec *r)
{
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- return conf->ap_requires;
+ core_request_config *conf = ap_get_core_module_config(r->request_config);
+ if (conf->context_prefix)
+ return conf->context_prefix;
+ else
+ return "";
}
-AP_DECLARE(int) ap_satisfies(request_rec *r)
+AP_DECLARE(const char *) ap_context_document_root(request_rec *r)
{
- core_dir_config *conf;
+ core_request_config *conf = ap_get_core_module_config(r->request_config);
+ if (conf->context_document_root)
+ return conf->context_document_root;
+ else
+ return ap_document_root(r);
+}
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
+AP_DECLARE(void) ap_set_document_root(request_rec *r, const char *document_root)
+{
+ core_request_config *conf = ap_get_core_module_config(r->request_config);
+ conf->document_root = document_root;
+}
- return conf->satisfy[r->method_number];
+AP_DECLARE(void) ap_set_context_info(request_rec *r, const char *context_prefix,
+ const char *context_document_root)
+{
+ core_request_config *conf = ap_get_core_module_config(r->request_config);
+ if (context_prefix)
+ conf->context_prefix = context_prefix;
+ if (context_document_root)
+ conf->context_document_root = context_document_root;
}
/* Should probably just get rid of this... the only code that cares is
@@ -764,19 +786,16 @@ AP_DECLARE(int) ap_satisfies(request_rec *r)
char *ap_response_code_string(request_rec *r, int error_index)
{
core_dir_config *dirconf;
- core_request_config *reqconf;
+ core_request_config *reqconf = ap_get_core_module_config(r->request_config);
/* check for string registered via ap_custom_response() first */
- reqconf = (core_request_config *)ap_get_module_config(r->request_config,
- &core_module);
if (reqconf->response_code_strings != NULL &&
reqconf->response_code_strings[error_index] != NULL) {
return reqconf->response_code_strings[error_index];
}
/* check for string specified via ErrorDocument */
- dirconf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
+ dirconf = ap_get_core_module_config(r->per_dir_config);
if (dirconf->response_code_strings == NULL) {
return NULL;
@@ -810,7 +829,7 @@ static APR_INLINE void do_double_reverse (conn_rec *conn)
rv = apr_sockaddr_info_get(&sa, conn->remote_host, APR_UNSPEC, 0, 0, conn->pool);
if (rv == APR_SUCCESS) {
while (sa) {
- if (apr_sockaddr_equal(sa, conn->remote_addr)) {
+ if (apr_sockaddr_equal(sa, conn->client_addr)) {
conn->double_reverse = 1;
return;
}
@@ -835,9 +854,8 @@ AP_DECLARE(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config,
/* If we haven't checked the host name, and we want to */
if (dir_config) {
- hostname_lookups =
- ((core_dir_config *)ap_get_module_config(dir_config, &core_module))
- ->hostname_lookups;
+ hostname_lookups = ((core_dir_config *)ap_get_core_module_config(dir_config))
+ ->hostname_lookups;
if (hostname_lookups == HOSTNAME_LOOKUP_UNSET) {
hostname_lookups = HOSTNAME_LOOKUP_OFF;
@@ -853,7 +871,7 @@ AP_DECLARE(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config,
&& (type == REMOTE_DOUBLE_REV
|| hostname_lookups != HOSTNAME_LOOKUP_OFF)) {
- if (apr_getnameinfo(&conn->remote_host, conn->remote_addr, 0)
+ if (apr_getnameinfo(&conn->remote_host, conn->client_addr, 0)
== APR_SUCCESS) {
ap_str_tolower(conn->remote_host);
@@ -892,7 +910,7 @@ AP_DECLARE(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config,
}
else {
*str_is_ip = 1;
- return conn->remote_ip;
+ return conn->client_ip;
}
}
}
@@ -932,8 +950,7 @@ AP_DECLARE(const char *) ap_get_server_name(request_rec *r)
core_dir_config *d;
const char *retval;
- d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
+ d = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
switch (d->use_canonical_name) {
case USE_CANONICAL_NAME_ON:
@@ -956,7 +973,7 @@ AP_DECLARE(const char *) ap_get_server_name(request_rec *r)
retval = r->hostname ? r->hostname : r->server->server_hostname;
break;
default:
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00109)
"ap_get_server_name: Invalid UCN Option somehow");
retval = "localhost";
break;
@@ -969,7 +986,7 @@ AP_DECLARE(const char *) ap_get_server_name(request_rec *r)
* of using in a URL. If the server name is an IPv6 literal
* address, it will be returned in URL format (e.g., "[fe80::1]").
*/
-static const char *get_server_name_for_url(request_rec *r)
+AP_DECLARE(const char *) ap_get_server_name_for_url(request_rec *r)
{
const char *plain_server_name = ap_get_server_name(r);
@@ -985,7 +1002,7 @@ AP_DECLARE(apr_port_t) ap_get_server_port(const request_rec *r)
{
apr_port_t port;
core_dir_config *d =
- (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+ (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
switch (d->use_canonical_name) {
case USE_CANONICAL_NAME_OFF:
@@ -1019,7 +1036,7 @@ AP_DECLARE(apr_port_t) ap_get_server_port(const request_rec *r)
ap_default_port(r);
break;
default:
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00110)
"ap_get_server_port: Invalid UCN Option somehow");
port = ap_default_port(r);
break;
@@ -1032,7 +1049,7 @@ AP_DECLARE(char *) ap_construct_url(apr_pool_t *p, const char *uri,
request_rec *r)
{
unsigned port = ap_get_server_port(r);
- const char *host = get_server_name_for_url(r);
+ const char *host = ap_get_server_name_for_url(r);
if (ap_is_default_port(port, r)) {
return apr_pstrcat(p, ap_http_scheme(r), "://", host, uri, NULL);
@@ -1044,7 +1061,7 @@ AP_DECLARE(char *) ap_construct_url(apr_pool_t *p, const char *uri,
AP_DECLARE(apr_off_t) ap_get_limit_req_body(const request_rec *r)
{
core_dir_config *d =
- (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+ (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
if (d->limit_req_body == AP_LIMIT_REQ_BODY_UNSET) {
return AP_DEFAULT_LIMIT_REQ_BODY;
@@ -1089,9 +1106,16 @@ AP_DECLARE(const char *) ap_check_cmd_context(cmd_parms *cmd,
" cannot occur within <VirtualHost> section", NULL);
}
- if ((forbidden & NOT_IN_LIMIT) && cmd->limited != -1) {
+ if ((forbidden & (NOT_IN_LIMIT | NOT_IN_DIR_LOC_FILE))
+ && cmd->limited != -1) {
return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
- " cannot occur within <Limit> section", NULL);
+ " cannot occur within <Limit> or <LimitExcept> "
+ "section", NULL);
+ }
+
+ if ((forbidden & NOT_IN_HTACCESS) && (cmd->pool == cmd->temp_pool)) {
+ return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
+ " cannot occur within htaccess files", NULL);
}
if ((forbidden & NOT_IN_DIR_LOC_FILE) == NOT_IN_DIR_LOC_FILE) {
@@ -1129,10 +1153,9 @@ static const char *set_access_name(cmd_parms *cmd, void *dummy,
const char *arg)
{
void *sconf = cmd->server->module_config;
- core_server_config *conf = ap_get_module_config(sconf, &core_module);
+ core_server_config *conf = ap_get_core_module_config(sconf);
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
if (err != NULL) {
return err;
}
@@ -1141,19 +1164,205 @@ static const char *set_access_name(cmd_parms *cmd, void *dummy,
return NULL;
}
+AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word)
+{
+# define SMALL_EXPANSION 5
+ struct sll {
+ struct sll *next;
+ const char *string;
+ apr_size_t len;
+ } *result, *current, sresult[SMALL_EXPANSION];
+ char *res_buf, *cp;
+ const char *s, *e, *ep;
+ unsigned spc;
+ apr_size_t outlen;
+
+ s = ap_strchr_c(word, '$');
+ if (!s) {
+ return word;
+ }
+
+ /* well, actually something to do */
+ ep = word + strlen(word);
+ spc = 0;
+ result = current = &(sresult[spc++]);
+ current->next = NULL;
+ current->string = word;
+ current->len = s - word;
+ outlen = current->len;
+
+ do {
+ /* prepare next entry */
+ if (current->len) {
+ current->next = (spc < SMALL_EXPANSION)
+ ? &(sresult[spc++])
+ : (struct sll *)apr_palloc(p,
+ sizeof(*current->next));
+ current = current->next;
+ current->next = NULL;
+ current->len = 0;
+ }
+
+ if (*s == '$') {
+ if (s[1] == '{' && (e = ap_strchr_c(s, '}'))) {
+ char *name = apr_pstrndup(p, s+2, e-s-2);
+ word = NULL;
+ if (server_config_defined_vars)
+ word = apr_table_get(server_config_defined_vars, name);
+ if (!word)
+ word = getenv(name);
+ if (word) {
+ current->string = word;
+ current->len = strlen(word);
+ outlen += current->len;
+ }
+ else {
+ if (ap_strchr(name, ':') == 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, APLOGNO(00111)
+ "Config variable ${%s} is not defined",
+ name);
+ current->string = s;
+ current->len = e - s + 1;
+ outlen += current->len;
+ }
+ s = e + 1;
+ }
+ else {
+ current->string = s++;
+ current->len = 1;
+ ++outlen;
+ }
+ }
+ else {
+ word = s;
+ s = ap_strchr_c(s, '$');
+ current->string = word;
+ current->len = s ? s - word : ep - word;
+ outlen += current->len;
+ }
+ } while (s && *s);
+
+ /* assemble result */
+ res_buf = cp = apr_palloc(p, outlen + 1);
+ do {
+ if (result->len) {
+ memcpy(cp, result->string, result->len);
+ cp += result->len;
+ }
+ result = result->next;
+ } while (result);
+ res_buf[outlen] = '\0';
+
+ return res_buf;
+}
+
+static int reset_config_defines(void *dummy)
+{
+ ap_server_config_defines = saved_server_config_defines;
+ server_config_defined_vars = NULL;
+ return OK;
+}
+
+/*
+ * Make sure we can revert the effects of Define/UnDefine when restarting.
+ * This function must be called once per loading of the config, before
+ * ap_server_config_defines is changed. This may be during reading of the
+ * config, which is even before the pre_config hook is run (due to
+ * EXEC_ON_READ for Define/UnDefine).
+ */
+static void init_config_defines(apr_pool_t *pconf)
+{
+ saved_server_config_defines = ap_server_config_defines;
+ /* Use apr_array_copy instead of apr_array_copy_hdr because it does not
+ * protect from the way unset_define removes entries.
+ */
+ ap_server_config_defines = apr_array_copy(pconf, ap_server_config_defines);
+}
+
+static const char *set_define(cmd_parms *cmd, void *dummy,
+ const char *name, const char *value)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
+ if (err)
+ return err;
+ if (ap_strchr_c(name, ':') != NULL)
+ return "Variable name must not contain ':'";
+
+ if (!saved_server_config_defines)
+ init_config_defines(cmd->pool);
+ if (!ap_exists_config_define(name)) {
+ char **newv = (char **)apr_array_push(ap_server_config_defines);
+ *newv = apr_pstrdup(cmd->pool, name);
+ }
+ if (value) {
+ if (!server_config_defined_vars)
+ server_config_defined_vars = apr_table_make(cmd->pool, 5);
+ apr_table_setn(server_config_defined_vars, name, value);
+ }
+
+ return NULL;
+}
+
+static const char *unset_define(cmd_parms *cmd, void *dummy,
+ const char *name)
+{
+ int i;
+ char **defines;
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
+ if (err)
+ return err;
+ if (ap_strchr_c(name, ':') != NULL)
+ return "Variable name must not contain ':'";
+
+ if (!saved_server_config_defines)
+ init_config_defines(cmd->pool);
+
+ defines = (char **)ap_server_config_defines->elts;
+ for (i = 0; i < ap_server_config_defines->nelts; i++) {
+ if (strcmp(defines[i], name) == 0) {
+ defines[i] = apr_array_pop(ap_server_config_defines);
+ break;
+ }
+ }
+
+ if (server_config_defined_vars) {
+ apr_table_unset(server_config_defined_vars, name);
+ }
+
+ return NULL;
+}
+
+static const char *generate_error(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ if (!arg || !*arg) {
+ return "The Error directive was used with no message.";
+ }
+
+ if (*arg == '"' || *arg == '\'') { /* strip off quotes */
+ apr_size_t len = strlen(arg);
+ char last = *(arg + len - 1);
+
+ if (*arg == last) {
+ return apr_pstrndup(cmd->pool, arg + 1, len - 2);
+ }
+ }
+
+ return arg;
+}
+
#ifdef GPROF
static const char *set_gprof_dir(cmd_parms *cmd, void *dummy, const char *arg)
{
void *sconf = cmd->server->module_config;
- core_server_config *conf = ap_get_module_config(sconf, &core_module);
+ core_server_config *conf = ap_get_core_module_config(sconf);
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
if (err != NULL) {
return err;
}
- conf->gprof_dir = apr_pstrdup(cmd->pool, arg);
+ conf->gprof_dir = arg;
return NULL;
}
#endif /*GPROF*/
@@ -1163,11 +1372,6 @@ static const char *set_add_default_charset(cmd_parms *cmd,
{
core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
if (!strcasecmp(arg, "Off")) {
d->add_default_charset = ADD_DEFAULT_CHARSET_OFF;
}
@@ -1187,10 +1391,9 @@ static const char *set_document_root(cmd_parms *cmd, void *dummy,
const char *arg)
{
void *sconf = cmd->server->module_config;
- core_server_config *conf = ap_get_module_config(sconf, &core_module);
+ core_server_config *conf = ap_get_core_module_config(sconf);
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
if (err != NULL) {
return err;
}
@@ -1213,7 +1416,7 @@ static const char *set_document_root(cmd_parms *cmd, void *dummy,
|| !ap_is_directory(cmd->pool, arg)) {
if (cmd->server->is_virtual) {
ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0,
- cmd->pool,
+ cmd->pool, APLOGNO(00112)
"Warning: DocumentRoot [%s] does not exist",
arg);
conf->ap_document_root = arg;
@@ -1228,8 +1431,7 @@ static const char *set_document_root(cmd_parms *cmd, void *dummy,
AP_DECLARE(void) ap_custom_response(request_rec *r, int status,
const char *string)
{
- core_request_config *conf =
- ap_get_module_config(r->request_config, &core_module);
+ core_request_config *conf = ap_get_core_module_config(r->request_config);
int idx;
if (conf->response_code_strings == NULL) {
@@ -1252,11 +1454,6 @@ static const char *set_error_document(cmd_parms *cmd, void *conf_,
int error_number, index_number, idx500;
enum { MSG, LOCAL_PATH, REMOTE_PATH } what = MSG;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
/* 1st parameter should be a 3 digit number, which we recognize;
* convert it into an array index
*/
@@ -1284,7 +1481,7 @@ static const char *set_error_document(cmd_parms *cmd, void *conf_,
/* The entry should be ignored if it is a full URL for a 401 error */
if (error_number == 401 && what == REMOTE_PATH) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server, APLOGNO(00113)
"cannot use a full URL in a 401 ErrorDocument "
"directive --- ignoring!");
}
@@ -1382,22 +1579,20 @@ static const char *set_override(cmd_parms *cmd, void *d_, const char *l)
core_dir_config *d = d_;
char *w;
char *k, *v;
-
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
+ const char *err;
/* Throw a warning if we're in <Location> or <Files> */
if (ap_check_cmd_context(cmd, NOT_IN_LOCATION | NOT_IN_FILES)) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00114)
"Useless use of AllowOverride in line %d of %s.",
cmd->directive->line_num, cmd->directive->filename);
}
+ if ((err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS)) != NULL)
+ return err;
d->override = OR_NONE;
while (l[0]) {
- w = ap_getword_conf(cmd->pool, &l);
+ w = ap_getword_conf(cmd->temp_pool, &l);
k = w;
v = strchr(k, '=');
@@ -1413,7 +1608,7 @@ static const char *set_override(cmd_parms *cmd, void *d_, const char *l)
if (v)
set_allow_opts(cmd, &(d->override_opts), v);
else
- d->override_opts = OPT_ALL;
+ d->override_opts = OPT_SYM_LINKS;
}
else if (!strcasecmp(w, "FileInfo")) {
d->override |= OR_FILEINFO;
@@ -1440,23 +1635,68 @@ static const char *set_override(cmd_parms *cmd, void *d_, const char *l)
return NULL;
}
+static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *const argv[])
+{
+ core_dir_config *d = d_;
+ int i;
+ const char *err;
+
+ /* Throw a warning if we're in <Location> or <Files> */
+ if (ap_check_cmd_context(cmd, NOT_IN_LOCATION | NOT_IN_FILES)) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00115)
+ "Useless use of AllowOverrideList in line %d of %s.",
+ cmd->directive->line_num, cmd->directive->filename);
+ }
+ if ((err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS)) != NULL)
+ return err;
+
+ d->override_list = apr_table_make(cmd->pool, 1);
+
+ for (i=0;i<argc;i++){
+ if (!strcasecmp(argv[i], "None")) {
+ return NULL;
+ }
+ else {
+ const command_rec *result = NULL;
+ module *mod = ap_top_module;
+ result = ap_find_command_in_modules(argv[i], &mod);
+ if (result)
+ apr_table_set(d->override_list, argv[i], "1");
+ else
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00116)
+ "Discarding unrecognized directive `%s' in AllowOverrideList.",
+ argv[i]);
+ }
+ }
+
+ return NULL;
+}
+
static const char *set_options(cmd_parms *cmd, void *d_, const char *l)
{
core_dir_config *d = d_;
allow_options_t opt;
int first = 1;
+ int merge = 0;
+ int all_none = 0;
char action;
while (l[0]) {
- char *w = ap_getword_conf(cmd->pool, &l);
+ char *w = ap_getword_conf(cmd->temp_pool, &l);
action = '\0';
if (*w == '+' || *w == '-') {
action = *(w++);
+ if (!merge && !first && !all_none) {
+ return "Either all Options must start with + or -, or no Option may.";
+ }
+ merge = 1;
}
else if (first) {
- d->opts = OPT_NONE;
- first = 0;
+ d->opts = OPT_NONE;
+ }
+ else if (merge) {
+ return "Either all Options must start with + or -, or no Option may.";
}
if (!strcasecmp(w, "Indexes")) {
@@ -1484,10 +1724,24 @@ static const char *set_options(cmd_parms *cmd, void *d_, const char *l)
opt = OPT_MULTI|OPT_EXECCGI;
}
else if (!strcasecmp(w, "None")) {
+ if (!first) {
+ return "'Options None' must be the first Option given.";
+ }
+ else if (merge) { /* Only works since None may not follow any other option. */
+ return "You may not use 'Options +None' or 'Options -None'.";
+ }
opt = OPT_NONE;
+ all_none = 1;
}
else if (!strcasecmp(w, "All")) {
+ if (!first) {
+ return "'Options All' must be the first option given.";
+ }
+ else if (merge) { /* Only works since All may not follow any other option. */
+ return "You may not use 'Options +All' or 'Options -All'.";
+ }
opt = OPT_ALL;
+ all_none = 1;
}
else {
return apr_pstrcat(cmd->pool, "Illegal option ", w, NULL);
@@ -1510,6 +1764,20 @@ static const char *set_options(cmd_parms *cmd, void *d_, const char *l)
else {
d->opts |= opt;
}
+
+ first = 0;
+ }
+
+ return NULL;
+}
+
+static const char *set_default_type(cmd_parms *cmd, void *d_,
+ const char *arg)
+{
+ if ((strcasecmp(arg, "off") != 0) && (strcasecmp(arg, "none") != 0)) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00117)
+ "Ignoring deprecated use of DefaultType in line %d of %s.",
+ cmd->directive->line_num, cmd->directive->filename);
}
return NULL;
@@ -1541,7 +1809,7 @@ static const char *set_etag_bits(cmd_parms *cmd, void *mconfig,
action = '*';
bit = ETAG_UNSET;
valid = 1;
- token = ap_getword_conf(cmd->pool, &args);
+ token = ap_getword_conf(cmd->temp_pool, &args);
if ((*token == '+') || (*token == '-')) {
action = *token;
token++;
@@ -1652,11 +1920,6 @@ static const char *set_enable_mmap(cmd_parms *cmd, void *d_,
const char *arg)
{
core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
if (strcasecmp(arg, "on") == 0) {
d->enable_mmap = ENABLE_MMAP_ON;
@@ -1675,11 +1938,6 @@ static const char *set_enable_sendfile(cmd_parms *cmd, void *d_,
const char *arg)
{
core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
if (strcasecmp(arg, "on") == 0) {
d->enable_sendfile = ENABLE_SENDFILE_ON;
@@ -1694,46 +1952,6 @@ static const char *set_enable_sendfile(cmd_parms *cmd, void *d_,
return NULL;
}
-static const char *satisfy(cmd_parms *cmd, void *c_, const char *arg)
-{
- core_dir_config *c = c_;
- int satisfy = SATISFY_NOSPEC;
- int i;
-
- if (!strcasecmp(arg, "all")) {
- satisfy = SATISFY_ALL;
- }
- else if (!strcasecmp(arg, "any")) {
- satisfy = SATISFY_ANY;
- }
- else {
- return "Satisfy either 'any' or 'all'.";
- }
-
- for (i = 0; i < METHODS; ++i) {
- if (cmd->limited & (AP_METHOD_BIT << i)) {
- c->satisfy[i] = satisfy;
- }
- }
-
- return NULL;
-}
-
-static const char *require(cmd_parms *cmd, void *c_, const char *arg)
-{
- require_line *r;
- core_dir_config *c = c_;
-
- if (!c->ap_requires) {
- c->ap_requires = apr_array_make(cmd->pool, 2, sizeof(require_line));
- }
-
- r = (require_line *)apr_array_push(c->ap_requires);
- r->requirement = apr_pstrdup(cmd->pool, arg);
- r->method_mask = cmd->limited;
-
- return NULL;
-}
/*
* Report a missing-'>' syntax error.
@@ -1761,25 +1979,21 @@ AP_CORE_DECLARE_NONSTD(const char *) ap_limit_section(cmd_parms *cmd,
const char *limited_methods;
void *tog = cmd->cmd->cmd_data;
apr_int64_t limited = 0;
+ apr_int64_t old_limited = cmd->limited;
const char *errmsg;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
if (endp == NULL) {
return unclosed_directive(cmd);
}
- limited_methods = apr_pstrndup(cmd->pool, arg, endp - arg);
+ limited_methods = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
if (!limited_methods[0]) {
return missing_container_arg(cmd);
}
while (limited_methods[0]) {
- char *method = ap_getword_conf(cmd->pool, &limited_methods);
+ char *method = ap_getword_conf(cmd->temp_pool, &limited_methods);
int methnum;
/* check for builtin or module registered method number */
@@ -1792,7 +2006,8 @@ AP_CORE_DECLARE_NONSTD(const char *) ap_limit_section(cmd_parms *cmd,
/* method has not been registered yet, but resorce restriction
* is always checked before method handling, so register it.
*/
- methnum = ap_method_register(cmd->pool, method);
+ methnum = ap_method_register(cmd->pool,
+ apr_pstrdup(cmd->pool, method));
}
limited |= (AP_METHOD_BIT << methnum);
@@ -1801,11 +2016,23 @@ AP_CORE_DECLARE_NONSTD(const char *) ap_limit_section(cmd_parms *cmd,
/* Killing two features with one function,
* if (tog == NULL) <Limit>, else <LimitExcept>
*/
- cmd->limited = tog ? ~limited : limited;
+ limited = tog ? ~limited : limited;
+
+ if (!(old_limited & limited)) {
+ return apr_pstrcat(cmd->pool, cmd->cmd->name,
+ "> directive excludes all methods", NULL);
+ }
+ else if ((old_limited & limited) == old_limited) {
+ return apr_pstrcat(cmd->pool, cmd->cmd->name,
+ "> directive specifies methods already excluded",
+ NULL);
+ }
+
+ cmd->limited &= limited;
errmsg = ap_walk_config(cmd->directive->first_child, cmd, cmd->context);
- cmd->limited = -1;
+ cmd->limited = old_limited;
return errmsg;
}
@@ -1833,8 +2060,7 @@ static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg)
ap_regex_t *r = NULL;
const command_rec *thiscmd = cmd->cmd;
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
if (err != NULL) {
return err;
}
@@ -1843,7 +2069,7 @@ static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg)
return unclosed_directive(cmd);
}
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
+ arg = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
if (!arg[0]) {
return missing_container_arg(cmd);
@@ -1930,8 +2156,7 @@ static const char *urlsection(cmd_parms *cmd, void *mconfig, const char *arg)
ap_regex_t *r = NULL;
const command_rec *thiscmd = cmd->cmd;
ap_conf_vector_t *new_url_conf = ap_create_per_dir_config(cmd->pool);
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
if (err != NULL) {
return err;
}
@@ -1940,7 +2165,7 @@ static const char *urlsection(cmd_parms *cmd, void *mconfig, const char *arg)
return unclosed_directive(cmd);
}
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
+ arg = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
if (!arg[0]) {
return missing_container_arg(cmd);
@@ -1997,9 +2222,9 @@ static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
core_dir_config *conf;
ap_regex_t *r = NULL;
const command_rec *thiscmd = cmd->cmd;
- core_dir_config *c = mconfig;
ap_conf_vector_t *new_file_conf = ap_create_per_dir_config(cmd->pool);
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_LOCATION);
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_LOCATION | NOT_IN_LIMIT);
if (err != NULL) {
return err;
@@ -2009,7 +2234,7 @@ static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
return unclosed_directive(cmd);
}
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
+ arg = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
if (!arg[0]) {
return missing_container_arg(cmd);
@@ -2057,7 +2282,7 @@ static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
conf->d_is_fnmatch = apr_fnmatch_test(conf->d) != 0;
conf->r = r;
- ap_add_file_conf(c, new_file_conf);
+ ap_add_file_conf(cmd->pool, (core_dir_config *)mconfig, new_file_conf);
if (*arg != '\0') {
return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
@@ -2070,34 +2295,99 @@ static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
return NULL;
}
-static const char *start_ifmod(cmd_parms *cmd, void *mconfig, const char *arg)
+#define COND_IF ((void *)1)
+#define COND_ELSE ((void *)2)
+#define COND_ELSEIF ((void *)3)
+
+static const char *ifsection(cmd_parms *cmd, void *mconfig, const char *arg)
{
+ const char *errmsg;
const char *endp = ap_strrchr_c(arg, '>');
- int not = (arg[0] == '!');
- module *found;
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ const command_rec *thiscmd = cmd->cmd;
+ ap_conf_vector_t *new_if_conf = ap_create_per_dir_config(cmd->pool);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ const char *condition;
+ const char *expr_err;
+
+ if (err != NULL) {
+ return err;
+ }
if (endp == NULL) {
return unclosed_directive(cmd);
}
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
+ arg = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
- if (not) {
- arg++;
+
+ /* Only if not an .htaccess file */
+ if (!old_path) {
+ cmd->override = OR_ALL|ACCESS_CONF;
}
- if (!arg[0]) {
- return missing_container_arg(cmd);
+ /* initialize our config and fetch it */
+ conf = ap_set_config_vectors(cmd->server, new_if_conf, cmd->path,
+ &core_module, cmd->pool);
+
+ if (cmd->cmd->cmd_data == COND_IF)
+ conf->condition_ifelse = AP_CONDITION_IF;
+ else if (cmd->cmd->cmd_data == COND_ELSEIF)
+ conf->condition_ifelse = AP_CONDITION_ELSEIF;
+ else if (cmd->cmd->cmd_data == COND_ELSE)
+ conf->condition_ifelse = AP_CONDITION_ELSE;
+ else
+ ap_assert(0);
+
+ if (conf->condition_ifelse == AP_CONDITION_ELSE) {
+ if (arg[0])
+ return "<Else> does not take an argument";
+ }
+ else {
+ if (!arg[0])
+ return missing_container_arg(cmd);
+ condition = ap_getword_conf(cmd->pool, &arg);
+ conf->condition = ap_expr_parse_cmd(cmd, condition, 0, &expr_err, NULL);
+ if (expr_err)
+ return apr_psprintf(cmd->pool, "Cannot parse condition clause: %s",
+ expr_err);
}
- found = ap_find_linked_module(arg);
+ errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_if_conf);
+ if (errmsg != NULL)
+ return errmsg;
+
+ conf->d = cmd->path;
+ conf->d_is_fnmatch = 0;
+ conf->r = NULL;
+
+ errmsg = ap_add_if_conf(cmd->pool, (core_dir_config *)mconfig, new_if_conf);
+ if (errmsg != NULL)
+ return errmsg;
+
+ if (*arg != '\0') {
+ return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+ "> arguments not supported.", NULL);
+ }
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+static module *find_module(server_rec *s, const char *name)
+{
+ module *found = ap_find_linked_module(name);
/* search prelinked stuff */
if (!found) {
ap_module_symbol_t *current = ap_prelinked_module_symbols;
for (; current->name; ++current) {
- if (!strcmp(current->name, arg)) {
+ if (!strcmp(current->name, name)) {
found = current->modp;
break;
}
@@ -2110,10 +2400,52 @@ static const char *start_ifmod(cmd_parms *cmd, void *mconfig, const char *arg)
APR_RETRIEVE_OPTIONAL_FN(ap_find_loaded_module_symbol);
if (check_symbol) {
- found = check_symbol(cmd->server, arg);
+ /*
+ * There are two phases where calling ap_find_loaded_module_symbol
+ * is problematic:
+ *
+ * During reading of the config, ap_server_conf is invalid but s
+ * points to the main server config, if passed from cmd->server
+ * of an EXEC_ON_READ directive.
+ *
+ * During config parsing, s may be a virtual host that would cause
+ * a segfault in mod_so if passed to ap_find_loaded_module_symbol,
+ * because mod_so's server config for vhosts is initialized later.
+ * But ap_server_conf is already set at this time.
+ *
+ * Therefore we use s if it is not virtual and ap_server_conf if
+ * s is virtual.
+ */
+ found = check_symbol(s->is_virtual ? ap_server_conf : s, name);
}
}
+ return found;
+}
+
+
+static const char *start_ifmod(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+ const char *endp = ap_strrchr_c(arg, '>');
+ int not = (arg[0] == '!');
+ module *found;
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ arg = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
+
+ if (not) {
+ arg++;
+ }
+
+ if (!arg[0]) {
+ return missing_container_arg(cmd);
+ }
+
+ found = find_module(cmd->server, arg);
+
if ((!not && found) || (not && !found)) {
ap_directive_t *parent = NULL;
ap_directive_t *current = NULL;
@@ -2156,7 +2488,7 @@ static const char *start_ifdefine(cmd_parms *cmd, void *dummy, const char *arg)
return unclosed_directive(cmd);
}
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
+ arg = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
if (arg[0] == '!') {
not = 1;
@@ -2203,7 +2535,7 @@ static const char *virtualhost_section(cmd_parms *cmd, void *dummy,
return unclosed_directive(cmd);
}
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
+ arg = apr_pstrndup(cmd->temp_pool, arg, endp - arg);
if (!arg[0]) {
return missing_container_arg(cmd);
@@ -2266,8 +2598,8 @@ static const char *set_accf_map(cmd_parms *cmd, void *dummy,
const char *iproto, const char* iaccf)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- core_server_config *conf = ap_get_module_config(cmd->server->module_config,
- &core_module);
+ core_server_config *conf =
+ ap_get_core_module_config(cmd->server->module_config);
char* proto;
char* accf;
if (err != NULL) {
@@ -2278,31 +2610,29 @@ static const char *set_accf_map(cmd_parms *cmd, void *dummy,
ap_str_tolower(proto);
accf = apr_pstrdup(cmd->pool, iaccf);
ap_str_tolower(accf);
- apr_table_set(conf->accf_map, proto, accf);
+ apr_table_setn(conf->accf_map, proto, accf);
return NULL;
}
AP_DECLARE(const char*) ap_get_server_protocol(server_rec* s)
{
- core_server_config *conf = ap_get_module_config(s->module_config,
- &core_module);
+ core_server_config *conf = ap_get_core_module_config(s->module_config);
return conf->protocol;
}
AP_DECLARE(void) ap_set_server_protocol(server_rec* s, const char* proto)
{
- core_server_config *conf = ap_get_module_config(s->module_config,
- &core_module);
+ core_server_config *conf = ap_get_core_module_config(s->module_config);
conf->protocol = proto;
}
static const char *set_protocol(cmd_parms *cmd, void *dummy,
const char *arg)
{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- core_server_config *conf = ap_get_module_config(cmd->server->module_config,
- &core_module);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
+ core_server_config *conf =
+ ap_get_core_module_config(cmd->server->module_config);
char* proto;
if (err != NULL) {
@@ -2325,7 +2655,7 @@ static const char *set_server_string_slot(cmd_parms *cmd, void *dummy,
char *struct_ptr = (char *)cmd->server;
const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ NOT_IN_DIR_LOC_FILE);
if (err != NULL) {
return err;
}
@@ -2344,7 +2674,7 @@ static const char *set_server_string_slot(cmd_parms *cmd, void *dummy,
static const char *server_hostname_port(cmd_parms *cmd, void *dummy, const char *arg)
{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
const char *portstr, *part;
char *scheme;
int port;
@@ -2353,12 +2683,16 @@ static const char *server_hostname_port(cmd_parms *cmd, void *dummy, const char
return err;
}
+ if (apr_fnmatch_test(arg))
+ return apr_pstrcat(cmd->temp_pool, "Invalid ServerName \"", arg,
+ "\" use ServerAlias to set multiple server names.", NULL);
+
part = ap_strstr_c(arg, "://");
if (part) {
- scheme = apr_pstrmemdup(cmd->pool, arg, part - arg);
+ scheme = apr_pstrndup(cmd->pool, arg, part - arg);
ap_str_tolower(scheme);
- cmd->server->server_scheme = scheme;
+ cmd->server->server_scheme = (const char *)scheme;
part += 3;
} else {
part = arg;
@@ -2366,8 +2700,8 @@ static const char *server_hostname_port(cmd_parms *cmd, void *dummy, const char
portstr = ap_strchr_c(part, ':');
if (portstr) {
- cmd->server->server_hostname = apr_pstrmemdup(cmd->pool, part,
- portstr - part);
+ cmd->server->server_hostname = apr_pstrndup(cmd->pool, part,
+ portstr - part);
portstr++;
port = atoi(portstr);
if (port <= 0 || port >= 65536) { /* 65536 == 1<<16 */
@@ -2389,11 +2723,6 @@ static const char *set_signature_flag(cmd_parms *cmd, void *d_,
const char *arg)
{
core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
if (strcasecmp(arg, "On") == 0) {
d->server_signature = srv_sig_on;
@@ -2422,7 +2751,7 @@ static const char *set_server_root(cmd_parms *cmd, void *dummy,
if ((apr_filepath_merge((char**)&ap_server_root, NULL, arg,
APR_FILEPATH_TRUENAME, cmd->pool) != APR_SUCCESS)
- || !ap_is_directory(cmd->pool, ap_server_root)) {
+ || !ap_is_directory(cmd->temp_pool, ap_server_root)) {
return "ServerRoot must be a valid directory";
}
@@ -2431,7 +2760,7 @@ static const char *set_server_root(cmd_parms *cmd, void *dummy,
static const char *set_timeout(cmd_parms *cmd, void *dummy, const char *arg)
{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
if (err != NULL) {
return err;
@@ -2444,15 +2773,10 @@ static const char *set_timeout(cmd_parms *cmd, void *dummy, const char *arg)
static const char *set_allow2f(cmd_parms *cmd, void *d_, const char *arg)
{
core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
if (0 == strcasecmp(arg, "on")) {
d->allow_encoded_slashes = 1;
- d->decode_encoded_slashes = 1;
+ d->decode_encoded_slashes = 1; /* for compatibility with 2.0 & 2.2 */
} else if (0 == strcasecmp(arg, "off")) {
d->allow_encoded_slashes = 0;
d->decode_encoded_slashes = 0;
@@ -2471,11 +2795,6 @@ static const char *set_hostname_lookups(cmd_parms *cmd, void *d_,
const char *arg)
{
core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
if (!strcasecmp(arg, "on")) {
d->hostname_lookups = HOSTNAME_LOOKUP_ON;
@@ -2496,7 +2815,7 @@ static const char *set_hostname_lookups(cmd_parms *cmd, void *d_,
static const char *set_serverpath(cmd_parms *cmd, void *dummy,
const char *arg)
{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
if (err != NULL) {
return err;
@@ -2510,13 +2829,8 @@ static const char *set_serverpath(cmd_parms *cmd, void *dummy,
static const char *set_content_md5(cmd_parms *cmd, void *d_, int arg)
{
core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
- d->content_md5 = arg != 0;
+ d->content_md5 = arg ? AP_CONTENT_MD5_ON : AP_CONTENT_MD5_OFF;
return NULL;
}
@@ -2544,11 +2858,6 @@ static const char *set_use_canonical_name(cmd_parms *cmd, void *d_,
const char *arg)
{
core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
if (strcasecmp(arg, "on") == 0) {
d->use_canonical_name = USE_CANONICAL_NAME_ON;
@@ -2570,11 +2879,6 @@ static const char *set_use_canonical_phys_port(cmd_parms *cmd, void *d_,
const char *arg)
{
core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
if (strcasecmp(arg, "on") == 0) {
d->use_canonical_phys_port = USE_CANONICAL_PHYS_PORT_ON;
@@ -2589,13 +2893,13 @@ static const char *set_use_canonical_phys_port(cmd_parms *cmd, void *d_,
return NULL;
}
-
static const char *include_config (cmd_parms *cmd, void *dummy,
const char *name)
{
ap_directive_t *conftree = NULL;
- const char* conffile, *error;
+ const char *conffile, *error;
unsigned *recursion;
+ int optional = cmd->cmd->cmd_data ? 1 : 0;
void *data;
apr_pool_userdata_get(&data, "ap_include_sentinel", cmd->pool);
@@ -2610,8 +2914,8 @@ static const char *include_config (cmd_parms *cmd, void *dummy,
if (++*recursion > AP_MAX_INCLUDE_DEPTH) {
*recursion = 0;
- return apr_psprintf(cmd->pool, "Exceeded maximum include depth of %u. "
- "You have probably a recursion somewhere.",
+ return apr_psprintf(cmd->pool, "Exceeded maximum include depth of %u, "
+ "There appears to be a recursion.",
AP_MAX_INCLUDE_DEPTH);
}
@@ -2622,8 +2926,9 @@ static const char *include_config (cmd_parms *cmd, void *dummy,
name, NULL);
}
- error = ap_process_resource_config(cmd->server, conffile,
- &conftree, cmd->pool, cmd->temp_pool);
+ error = ap_process_fnmatch_configs(cmd->server, conffile, &conftree,
+ cmd->pool, cmd->temp_pool,
+ optional);
if (error) {
*recursion = 0;
return error;
@@ -2639,50 +2944,67 @@ static const char *include_config (cmd_parms *cmd, void *dummy,
return NULL;
}
-static const char *set_loglevel(cmd_parms *cmd, void *dummy, const char *arg)
+static const char *set_loglevel(cmd_parms *cmd, void *config_, const char *arg_)
{
- char *str;
+ char *level_str;
+ int level;
+ module *module;
+ char *arg = apr_pstrdup(cmd->temp_pool, arg_);
+ struct ap_logconf *log;
+ const char *err;
+
+ if (cmd->path) {
+ core_dir_config *dconf = config_;
+ if (!dconf->log) {
+ dconf->log = ap_new_log_config(cmd->pool, NULL);
+ }
+ log = dconf->log;
+ }
+ else {
+ log = &cmd->server->log;
+ }
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
+ if (arg == NULL)
+ return "LogLevel requires level keyword or module loglevel specifier";
+
+ level_str = ap_strrchr(arg, ':');
+
+ if (level_str == NULL) {
+ err = ap_parse_log_level(arg, &log->level);
+ if (err != NULL)
+ return err;
+ ap_reset_module_loglevels(log, APLOG_NO_MODULE);
+ ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, cmd->server,
+ "Setting LogLevel for all modules to %s", arg);
+ return NULL;
}
- if ((str = ap_getword_conf(cmd->pool, &arg))) {
- if (!strcasecmp(str, "emerg")) {
- cmd->server->loglevel = APLOG_EMERG;
- }
- else if (!strcasecmp(str, "alert")) {
- cmd->server->loglevel = APLOG_ALERT;
- }
- else if (!strcasecmp(str, "crit")) {
- cmd->server->loglevel = APLOG_CRIT;
- }
- else if (!strcasecmp(str, "error")) {
- cmd->server->loglevel = APLOG_ERR;
- }
- else if (!strcasecmp(str, "warn")) {
- cmd->server->loglevel = APLOG_WARNING;
- }
- else if (!strcasecmp(str, "notice")) {
- cmd->server->loglevel = APLOG_NOTICE;
- }
- else if (!strcasecmp(str, "info")) {
- cmd->server->loglevel = APLOG_INFO;
- }
- else if (!strcasecmp(str, "debug")) {
- cmd->server->loglevel = APLOG_DEBUG;
- }
- else {
- return "LogLevel requires level keyword: one of "
- "emerg/alert/crit/error/warn/notice/info/debug";
- }
+ *level_str++ = '\0';
+ if (!*level_str) {
+ return apr_psprintf(cmd->temp_pool, "Module specifier '%s' must be "
+ "followed by a log level keyword", arg);
}
- else {
- return "LogLevel requires level keyword";
+
+ err = ap_parse_log_level(level_str, &level);
+ if (err != NULL)
+ return apr_psprintf(cmd->temp_pool, "%s:%s: %s", arg, level_str, err);
+
+ if ((module = find_module(cmd->server, arg)) == NULL) {
+ char *name = apr_psprintf(cmd->temp_pool, "%s_module", arg);
+ ap_log_error(APLOG_MARK, APLOG_TRACE6, 0, cmd->server,
+ "Cannot find module '%s', trying '%s'", arg, name);
+ module = find_module(cmd->server, name);
+ }
+
+ if (module == NULL) {
+ return apr_psprintf(cmd->temp_pool, "Cannot find module %s", arg);
}
+ ap_set_module_loglevel(cmd->pool, log, module->module_index, level);
+ ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, cmd->server,
+ "Setting LogLevel for module %s to %s", module->name,
+ level_str);
+
return NULL;
}
@@ -2691,8 +3013,7 @@ AP_DECLARE(const char *) ap_psignature(const char *prefix, request_rec *r)
char sport[20];
core_dir_config *conf;
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
+ conf = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
if ((conf->server_signature == srv_sig_off)
|| (conf->server_signature == srv_sig_unset)) {
return "";
@@ -2720,19 +3041,6 @@ AP_DECLARE(const char *) ap_psignature(const char *prefix, request_rec *r)
}
/*
- * Load an authorisation realm into our location configuration, applying the
- * usual rules that apply to realms.
- */
-static const char *set_authname(cmd_parms *cmd, void *mconfig,
- const char *word1)
-{
- core_dir_config *aconfig = (core_dir_config *)mconfig;
-
- aconfig->ap_auth_name = ap_escape_quotes(cmd->pool, word1);
- return NULL;
-}
-
-/*
* Handle a request to include the server's OS platform in the Server
* response header field (the ServerTokens directive). Unfortunately
* this requires a new global in order to communicate the setting back to
@@ -2742,14 +3050,14 @@ static const char *set_authname(cmd_parms *cmd, void *mconfig,
static char *server_banner = NULL;
static int banner_locked = 0;
-static char *server_description = NULL;
+static const char *server_description = NULL;
enum server_token_type {
- SrvTk_MAJOR, /* eg: Apache/2 */
- SrvTk_MINOR, /* eg. Apache/2.0 */
- SrvTk_MINIMAL, /* eg: Apache/2.0.41 */
- SrvTk_OS, /* eg: Apache/2.0.41 (UNIX) */
- SrvTk_FULL, /* eg: Apache/2.0.41 (UNIX) PHP/4.2.2 FooBar/1.2b */
+ SrvTk_MAJOR, /* eg: Apache/2 */
+ SrvTk_MINOR, /* eg. Apache/2.0 */
+ SrvTk_MINIMAL, /* eg: Apache/2.0.41 */
+ SrvTk_OS, /* eg: Apache/2.0.41 (UNIX) */
+ SrvTk_FULL, /* eg: Apache/2.0.41 (UNIX) PHP/4.2.2 FooBar/1.2b */
SrvTk_PRODUCT_ONLY /* eg: Apache */
};
static enum server_token_type ap_server_tokens = SrvTk_FULL;
@@ -2782,14 +3090,6 @@ AP_DECLARE(const char *) ap_get_server_banner(void)
return server_banner ? server_banner : AP_SERVER_BASEVERSION;
}
-/* ap_get_server_version() is deprecated. ap_get_server_banner()
- * provides the same semantics.
- */
-AP_DECLARE(const char *) ap_get_server_version(void)
-{
- return ap_get_server_banner();
-}
-
AP_DECLARE(void) ap_add_version_component(apr_pool_t *pconf, const char *component)
{
if (! banner_locked) {
@@ -2849,7 +3149,7 @@ static void set_banner(apr_pool_t *pconf)
}
static const char *set_serv_tokens(cmd_parms *cmd, void *dummy,
- const char *arg)
+ const char *arg1, const char *arg2)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
@@ -2857,19 +3157,19 @@ static const char *set_serv_tokens(cmd_parms *cmd, void *dummy,
return err;
}
- if (!strcasecmp(arg, "OS")) {
+ if (!strcasecmp(arg1, "OS")) {
ap_server_tokens = SrvTk_OS;
}
- else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) {
+ else if (!strcasecmp(arg1, "Min") || !strcasecmp(arg1, "Minimal")) {
ap_server_tokens = SrvTk_MINIMAL;
}
- else if (!strcasecmp(arg, "Major")) {
+ else if (!strcasecmp(arg1, "Major")) {
ap_server_tokens = SrvTk_MAJOR;
}
- else if (!strcasecmp(arg, "Minor") ) {
+ else if (!strcasecmp(arg1, "Minor") ) {
ap_server_tokens = SrvTk_MINOR;
}
- else if (!strcasecmp(arg, "Prod") || !strcasecmp(arg, "ProductOnly")) {
+ else if (!strcasecmp(arg1, "Prod") || !strcasecmp(arg1, "ProductOnly")) {
ap_server_tokens = SrvTk_PRODUCT_ONLY;
}
else {
@@ -2882,8 +3182,7 @@ static const char *set_serv_tokens(cmd_parms *cmd, void *dummy,
static const char *set_limit_req_line(cmd_parms *cmd, void *dummy,
const char *arg)
{
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
int lim;
if (err != NULL) {
@@ -2903,8 +3202,7 @@ static const char *set_limit_req_line(cmd_parms *cmd, void *dummy,
static const char *set_limit_req_fieldsize(cmd_parms *cmd, void *dummy,
const char *arg)
{
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
int lim;
if (err != NULL) {
@@ -2925,8 +3223,7 @@ static const char *set_limit_req_fieldsize(cmd_parms *cmd, void *dummy,
static const char *set_limit_req_fields(cmd_parms *cmd, void *dummy,
const char *arg)
{
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
int lim;
if (err != NULL) {
@@ -2948,13 +3245,8 @@ static const char *set_limit_req_body(cmd_parms *cmd, void *conf_,
const char *arg)
{
core_dir_config *conf = conf_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
char *errp;
- if (err != NULL) {
- return err;
- }
-
if (APR_SUCCESS != apr_strtoff(&conf->limit_req_body, arg, &errp, 10)) {
return "LimitRequestBody argument is not parsable.";
}
@@ -2969,11 +3261,6 @@ static const char *set_limit_xml_req_body(cmd_parms *cmd, void *conf_,
const char *arg)
{
core_dir_config *conf = conf_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
conf->limit_xml_body = atol(arg);
if (conf->limit_xml_body < 0)
@@ -2987,31 +3274,84 @@ static const char *set_max_ranges(cmd_parms *cmd, void *conf_, const char *arg)
core_dir_config *conf = conf_;
int val = 0;
- if (!strcasecmp(arg, "none")) {
+ if (!strcasecmp(arg, "none")) {
val = AP_MAXRANGES_NORANGES;
}
- else if (!strcasecmp(arg, "default")) {
+ else if (!strcasecmp(arg, "default")) {
val = AP_MAXRANGES_DEFAULT;
}
- else if (!strcasecmp(arg, "unlimited")) {
+ else if (!strcasecmp(arg, "unlimited")) {
val = AP_MAXRANGES_UNLIMITED;
}
- else {
+ else {
val = atoi(arg);
if (val <= 0)
- return "MaxRanges requires 'none', 'default', 'unlimited' or "
+ return "MaxRanges requires 'none', 'default', 'unlimited' or "
"a positive integer";
}
conf->max_ranges = val;
-
+
return NULL;
}
+
+static const char *set_max_overlaps(cmd_parms *cmd, void *conf_, const char *arg)
+{
+ core_dir_config *conf = conf_;
+ int val = 0;
+
+ if (!strcasecmp(arg, "none")) {
+ val = AP_MAXRANGES_NORANGES;
+ }
+ else if (!strcasecmp(arg, "default")) {
+ val = AP_MAXRANGES_DEFAULT;
+ }
+ else if (!strcasecmp(arg, "unlimited")) {
+ val = AP_MAXRANGES_UNLIMITED;
+ }
+ else {
+ val = atoi(arg);
+ if (val <= 0)
+ return "MaxRangeOverlaps requires 'none', 'default', 'unlimited' or "
+ "a positive integer";
+ }
+
+ conf->max_overlaps = val;
+
+ return NULL;
+}
+
+static const char *set_max_reversals(cmd_parms *cmd, void *conf_, const char *arg)
+{
+ core_dir_config *conf = conf_;
+ int val = 0;
+
+ if (!strcasecmp(arg, "none")) {
+ val = AP_MAXRANGES_NORANGES;
+ }
+ else if (!strcasecmp(arg, "default")) {
+ val = AP_MAXRANGES_DEFAULT;
+ }
+ else if (!strcasecmp(arg, "unlimited")) {
+ val = AP_MAXRANGES_UNLIMITED;
+ }
+ else {
+ val = atoi(arg);
+ if (val <= 0)
+ return "MaxRangeReversals requires 'none', 'default', 'unlimited' or "
+ "a positive integer";
+ }
+
+ conf->max_reversals = val;
+
+ return NULL;
+}
+
AP_DECLARE(size_t) ap_get_limit_xml_body(const request_rec *r)
{
core_dir_config *conf;
- conf = ap_get_module_config(r->per_dir_config, &core_module);
+ conf = ap_get_core_module_config(r->per_dir_config);
if (conf->limit_xml_body == AP_LIMIT_UNSET)
return AP_DEFAULT_LIMIT_XML_BODY;
@@ -3022,7 +3362,7 @@ AP_DECLARE(size_t) ap_get_limit_xml_body(const request_rec *r)
static const char *no_set_limit(cmd_parms *cmd, void *conf_,
const char *arg, const char *arg2)
{
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server, APLOGNO(00118)
"%s not supported on this platform", cmd->cmd->name);
return NULL;
@@ -3035,7 +3375,7 @@ static const char *set_limit_cpu(cmd_parms *cmd, void *conf_,
{
core_dir_config *conf = conf_;
- unixd_set_rlimit(cmd, &conf->limit_cpu, arg, arg2, RLIMIT_CPU);
+ ap_unixd_set_rlimit(cmd, &conf->limit_cpu, arg, arg2, RLIMIT_CPU);
return NULL;
}
#endif
@@ -3047,11 +3387,11 @@ static const char *set_limit_mem(cmd_parms *cmd, void *conf_,
core_dir_config *conf = conf_;
#if defined(RLIMIT_AS)
- unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2 ,RLIMIT_AS);
+ ap_unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2 ,RLIMIT_AS);
#elif defined(RLIMIT_DATA)
- unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_DATA);
+ ap_unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_DATA);
#elif defined(RLIMIT_VMEM)
- unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_VMEM);
+ ap_unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_VMEM);
#endif
return NULL;
@@ -3064,7 +3404,7 @@ static const char *set_limit_nproc(cmd_parms *cmd, void *conf_,
{
core_dir_config *conf = conf_;
- unixd_set_rlimit(cmd, &conf->limit_nproc, arg, arg2, RLIMIT_NPROC);
+ ap_unixd_set_rlimit(cmd, &conf->limit_nproc, arg, arg2, RLIMIT_NPROC);
return NULL;
}
#endif
@@ -3072,15 +3412,15 @@ static const char *set_limit_nproc(cmd_parms *cmd, void *conf_,
static const char *set_recursion_limit(cmd_parms *cmd, void *dummy,
const char *arg1, const char *arg2)
{
- core_server_config *conf = ap_get_module_config(cmd->server->module_config,
- &core_module);
+ core_server_config *conf =
+ ap_get_core_module_config(cmd->server->module_config);
int limit = atoi(arg1);
if (limit <= 0) {
return "The recursion limit must be greater than zero.";
}
if (limit < 4) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00119)
"Limiting internal redirects to very low numbers may "
"cause normal requests to fail.");
}
@@ -3094,7 +3434,7 @@ static const char *set_recursion_limit(cmd_parms *cmd, void *dummy,
return "The recursion limit must be greater than zero.";
}
if (limit < 4) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00120)
"Limiting the subrequest depth to a very low level may"
" cause normal requests to fail.");
}
@@ -3109,20 +3449,20 @@ static void log_backtrace(const request_rec *r)
{
const request_rec *top = r;
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00121)
"r->uri = %s", r->uri ? r->uri : "(unexpectedly NULL)");
while (top && (top->prev || top->main)) {
if (top->prev) {
top = top->prev;
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00122)
"redirected from r->uri = %s",
top->uri ? top->uri : "(unexpectedly NULL)");
}
if (!top->prev && top->main) {
top = top->main;
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00123)
"subrequested from r->uri = %s",
top->uri ? top->uri : "(unexpectedly NULL)");
}
@@ -3134,8 +3474,8 @@ static void log_backtrace(const request_rec *r)
*/
AP_DECLARE(int) ap_is_recursion_limit_exceeded(const request_rec *r)
{
- core_server_config *conf = ap_get_module_config(r->server->module_config,
- &core_module);
+ core_server_config *conf =
+ ap_get_core_module_config(r->server->module_config);
const request_rec *top = r;
int redirects = 0, subreqs = 0;
int rlimit = conf->redirect_limit
@@ -3150,7 +3490,7 @@ AP_DECLARE(int) ap_is_recursion_limit_exceeded(const request_rec *r)
if (top->prev) {
if (++redirects >= rlimit) {
/* uuh, too much. */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00124)
"Request exceeded the limit of %d internal "
"redirects due to probable configuration error. "
"Use 'LimitInternalRecursion' to increase the "
@@ -3170,9 +3510,9 @@ AP_DECLARE(int) ap_is_recursion_limit_exceeded(const request_rec *r)
if (!top->prev && top->main) {
if (++subreqs >= slimit) {
/* uuh, too much. */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00125)
"Request exceeded the limit of %d subrequest "
- "nesting levels due to probable confguration "
+ "nesting levels due to probable configuration "
"error. Use 'LimitInternalRecursion' to increase "
"the limit if necessary. Use 'LogLevel debug' to "
"get a backtrace.", slimit);
@@ -3192,109 +3532,280 @@ AP_DECLARE(int) ap_is_recursion_limit_exceeded(const request_rec *r)
return 0;
}
-static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
- const char *arg, const char *arg2)
+static const char *set_trace_enable(cmd_parms *cmd, void *dummy,
+ const char *arg1)
{
- core_dir_config *conf = conf_;
- ap_filter_rec_t *old, *new = NULL;
- const char *filter_name;
+ core_server_config *conf =
+ ap_get_core_module_config(cmd->server->module_config);
- if (!conf->ct_output_filters) {
- conf->ct_output_filters = apr_hash_make(cmd->pool);
- old = NULL;
+ if (strcasecmp(arg1, "on") == 0) {
+ conf->trace_enable = AP_TRACE_ENABLE;
+ }
+ else if (strcasecmp(arg1, "off") == 0) {
+ conf->trace_enable = AP_TRACE_DISABLE;
+ }
+ else if (strcasecmp(arg1, "extended") == 0) {
+ conf->trace_enable = AP_TRACE_EXTENDED;
}
else {
- old = (ap_filter_rec_t*) apr_hash_get(conf->ct_output_filters, arg2,
- APR_HASH_KEY_STRING);
- /* find last entry */
- if (old) {
- while (old->next) {
- old = old->next;
- }
- }
+ return "TraceEnable must be one of 'on', 'off', or 'extended'";
}
- while (*arg &&
- (filter_name = ap_getword(cmd->pool, &arg, ';')) &&
- strcmp(filter_name, "")) {
- new = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
- new->name = filter_name;
+ return NULL;
+}
+
+static apr_hash_t *errorlog_hash;
+
+static int log_constant_item(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ char *end = apr_cpystrn(buf, arg, buflen);
+ return end - buf;
+}
+
+static char *parse_errorlog_misc_string(apr_pool_t *p,
+ ap_errorlog_format_item *it,
+ const char **sa)
+{
+ const char *s;
+ char scratch[MAX_STRING_LEN];
+ char *d = scratch;
+ /*
+ * non-leading white space terminates this string to allow the next field
+ * separator to be inserted
+ */
+ int at_start = 1;
- /* We found something, so let's append it. */
- if (old) {
- old->next = new;
+ it->func = log_constant_item;
+ s = *sa;
+
+ while (*s && *s != '%' && (*s != ' ' || at_start) && d < scratch + MAX_STRING_LEN) {
+ if (*s != '\\') {
+ if (*s != ' ') {
+ at_start = 0;
+ }
+ *d++ = *s++;
}
else {
- apr_hash_set(conf->ct_output_filters, arg2,
- APR_HASH_KEY_STRING, new);
+ s++;
+ switch (*s) {
+ case 'r':
+ *d++ = '\r';
+ s++;
+ break;
+ case 'n':
+ *d++ = '\n';
+ s++;
+ break;
+ case 't':
+ *d++ = '\t';
+ s++;
+ break;
+ case '\0':
+ /* handle end of string */
+ *d++ = '\\';
+ break;
+ default:
+ /* copy next char verbatim */
+ *d++ = *s++;
+ break;
+ }
}
- old = new;
- }
-
- if (!new) {
- return "invalid filter name";
}
+ *d = '\0';
+ it->arg = apr_pstrdup(p, scratch);
+ *sa = s;
return NULL;
}
-/*
- * Insert filters requested by the AddOutputFilterByType
- * configuration directive. We cannot add filters based
- * on content-type until after the handler has started
- * to run. Only then do we reliably know the content-type.
- */
-void ap_add_output_filters_by_type(request_rec *r)
+
+static char *parse_errorlog_item(apr_pool_t *p, ap_errorlog_format_item *it,
+ const char **sa)
{
- core_dir_config *conf;
- const char *ctype;
+ const char *s = *sa;
+ ap_errorlog_handler *handler;
+ int i;
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
+ if (*s != '%') {
+ if (*s == ' ') {
+ it->flags |= AP_ERRORLOG_FLAG_FIELD_SEP;
+ }
+ return parse_errorlog_misc_string(p, it, sa);
+ }
- /* We can't do anything with no content-type or if we don't have a
- * filter configured.
- */
- if (!r->content_type || !conf->ct_output_filters) {
- return;
+ ++s;
+
+ if (*s == ' ') {
+ /* percent-space (% ) is a field separator */
+ it->flags |= AP_ERRORLOG_FLAG_FIELD_SEP;
+ *sa = ++s;
+ /* recurse */
+ return parse_errorlog_item(p, it, sa);
+ }
+ else if (*s == '%') {
+ it->arg = "%";
+ it->func = log_constant_item;
+ *sa = ++s;
+ return NULL;
}
- /* remove c-t decoration */
- ctype = ap_field_noparam(r->pool, r->content_type);
- if (ctype) {
- ap_filter_rec_t *ct_filter;
- ct_filter = apr_hash_get(conf->ct_output_filters, ctype,
- APR_HASH_KEY_STRING);
- while (ct_filter) {
- ap_add_output_filter(ct_filter->name, NULL, r, r->connection);
- ct_filter = ct_filter->next;
+ while (*s) {
+ switch (*s) {
+ case '{':
+ ++s;
+ it->arg = ap_getword(p, &s, '}');
+ break;
+ case '+':
+ ++s;
+ it->flags |= AP_ERRORLOG_FLAG_REQUIRED;
+ break;
+ case '-':
+ ++s;
+ it->flags |= AP_ERRORLOG_FLAG_NULL_AS_HYPHEN;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ i = *s - '0';
+ while (apr_isdigit(*++s))
+ i = i * 10 + (*s) - '0';
+ it->min_loglevel = i;
+ break;
+ case 'M':
+ it->func = NULL;
+ it->flags |= AP_ERRORLOG_FLAG_MESSAGE;
+ *sa = ++s;
+ return NULL;
+ default:
+ handler = (ap_errorlog_handler *)apr_hash_get(errorlog_hash, s, 1);
+ if (!handler) {
+ char dummy[2];
+
+ dummy[0] = *s;
+ dummy[1] = '\0';
+ return apr_pstrcat(p, "Unrecognized error log format directive %",
+ dummy, NULL);
+ }
+ it->func = handler->func;
+ *sa = ++s;
+ return NULL;
}
}
- return;
+ return "Ran off end of error log format parsing args to some directive";
}
-static const char *set_trace_enable(cmd_parms *cmd, void *dummy,
- const char *arg1)
+static apr_array_header_t *parse_errorlog_string(apr_pool_t *p,
+ const char *s,
+ const char **err,
+ int is_main_fmt)
{
- core_server_config *conf = ap_get_module_config(cmd->server->module_config,
- &core_module);
+ apr_array_header_t *a = apr_array_make(p, 30,
+ sizeof(ap_errorlog_format_item));
+ char *res;
+ int seen_msg_fmt = 0;
+
+ while (s && *s) {
+ ap_errorlog_format_item *item =
+ (ap_errorlog_format_item *)apr_array_push(a);
+ memset(item, 0, sizeof(*item));
+ res = parse_errorlog_item(p, item, &s);
+ if (res) {
+ *err = res;
+ return NULL;
+ }
+ if (item->flags & AP_ERRORLOG_FLAG_MESSAGE) {
+ if (!is_main_fmt) {
+ *err = "%M cannot be used in once-per-request or "
+ "once-per-connection formats";
+ return NULL;
+ }
+ seen_msg_fmt = 1;
+ }
+ if (is_main_fmt && item->flags & AP_ERRORLOG_FLAG_REQUIRED) {
+ *err = "The '+' flag cannot be used in the main error log format";
+ return NULL;
+ }
+ if (!is_main_fmt && item->min_loglevel) {
+ *err = "The loglevel cannot be used as a condition in "
+ "once-per-request or once-per-connection formats";
+ return NULL;
+ }
+ if (item->min_loglevel > APLOG_TRACE8) {
+ *err = "The specified loglevel modifier is out of range";
+ return NULL;
+ }
+ }
- if (strcasecmp(arg1, "on") == 0) {
- conf->trace_enable = AP_TRACE_ENABLE;
+ if (is_main_fmt && !seen_msg_fmt) {
+ *err = "main ErrorLogFormat must contain message format string '%M'";
+ return NULL;
}
- else if (strcasecmp(arg1, "off") == 0) {
- conf->trace_enable = AP_TRACE_DISABLE;
+
+ return a;
+}
+
+static const char *set_errorlog_format(cmd_parms *cmd, void *dummy,
+ const char *arg1, const char *arg2)
+{
+ const char *err_string = NULL;
+ core_server_config *conf =
+ ap_get_core_module_config(cmd->server->module_config);
+
+ if (!arg2) {
+ conf->error_log_format = parse_errorlog_string(cmd->pool, arg1,
+ &err_string, 1);
+ }
+ else if (!strcasecmp(arg1, "connection")) {
+ if (!conf->error_log_conn) {
+ conf->error_log_conn = apr_array_make(cmd->pool, 5,
+ sizeof(apr_array_header_t *));
+ }
+
+ if (*arg2) {
+ apr_array_header_t **e;
+ e = (apr_array_header_t **) apr_array_push(conf->error_log_conn);
+ *e = parse_errorlog_string(cmd->pool, arg2, &err_string, 0);
+ }
}
- else if (strcasecmp(arg1, "extended") == 0) {
- conf->trace_enable = AP_TRACE_EXTENDED;
+ else if (!strcasecmp(arg1, "request")) {
+ if (!conf->error_log_req) {
+ conf->error_log_req = apr_array_make(cmd->pool, 5,
+ sizeof(apr_array_header_t *));
+ }
+
+ if (*arg2) {
+ apr_array_header_t **e;
+ e = (apr_array_header_t **) apr_array_push(conf->error_log_req);
+ *e = parse_errorlog_string(cmd->pool, arg2, &err_string, 0);
+ }
}
else {
- return "TraceEnable must be one of 'on', 'off', or 'extended'";
+ err_string = "ErrorLogFormat type must be one of request, connection";
}
- return NULL;
+ return err_string;
+}
+
+AP_DECLARE(void) ap_register_errorlog_handler(apr_pool_t *p, char *tag,
+ ap_errorlog_handler_fn_t *handler,
+ int flags)
+{
+ ap_errorlog_handler *log_struct = apr_palloc(p, sizeof(*log_struct));
+ log_struct->func = handler;
+ log_struct->flags = flags;
+
+ apr_hash_set(errorlog_hash, tag, 1, (const void *)log_struct);
}
+
/* Note --- ErrorDocument will now work from .htaccess files.
* The AllowOverride of Fileinfo allows webmasters to turn it off
*/
@@ -3314,16 +3825,17 @@ AP_INIT_RAW_ARGS("<VirtualHost", virtualhost_section, NULL, RSRC_CONF,
"more host addresses"),
AP_INIT_RAW_ARGS("<Files", filesection, NULL, OR_ALL,
"Container for directives affecting files matching specified patterns"),
-AP_INIT_RAW_ARGS("<Limit", ap_limit_section, NULL, OR_ALL,
+AP_INIT_RAW_ARGS("<Limit", ap_limit_section, NULL, OR_LIMIT | OR_AUTHCFG,
"Container for authentication directives when accessed using specified HTTP "
"methods"),
-AP_INIT_RAW_ARGS("<LimitExcept", ap_limit_section, (void*)1, OR_ALL,
+AP_INIT_RAW_ARGS("<LimitExcept", ap_limit_section, (void*)1,
+ OR_LIMIT | OR_AUTHCFG,
"Container for authentication directives to be applied when any HTTP "
"method other than those specified is used to access the resource"),
AP_INIT_TAKE1("<IfModule", start_ifmod, NULL, EXEC_ON_READ | OR_ALL,
- "Container for directives based on existance of specified modules"),
+ "Container for directives based on existence of specified modules"),
AP_INIT_TAKE1("<IfDefine", start_ifdefine, NULL, EXEC_ON_READ | OR_ALL,
- "Container for directives based on existance of command line defines"),
+ "Container for directives based on existence of command line defines"),
AP_INIT_RAW_ARGS("<DirectoryMatch", dirsection, (void*)1, RSRC_CONF,
"Container for directives affecting resources located in the "
"specified directories"),
@@ -3332,15 +3844,6 @@ AP_INIT_RAW_ARGS("<LocationMatch", urlsection, (void*)1, RSRC_CONF,
"specified URL paths"),
AP_INIT_RAW_ARGS("<FilesMatch", filesection, (void*)1, OR_ALL,
"Container for directives affecting files matching specified patterns"),
-AP_INIT_TAKE1("AuthType", ap_set_string_slot,
- (void*)APR_OFFSETOF(core_dir_config, ap_auth_type), OR_AUTHCFG,
- "An HTTP authorization type (e.g., \"Basic\")"),
-AP_INIT_TAKE1("AuthName", set_authname, NULL, OR_AUTHCFG,
- "The authentication realm (e.g. \"Members Only\")"),
-AP_INIT_RAW_ARGS("Require", require, NULL, OR_AUTHCFG,
- "Selects which authenticated users or groups may access a protected space"),
-AP_INIT_TAKE1("Satisfy", satisfy, NULL, OR_AUTHCFG,
- "access policy if both allow and require used ('all' or 'any')"),
#ifdef GPROF
AP_INIT_TAKE1("GprofDir", set_gprof_dir, NULL, RSRC_CONF,
"Directory to plop gmon.out files"),
@@ -3349,6 +3852,18 @@ AP_INIT_TAKE1("AddDefaultCharset", set_add_default_charset, NULL, OR_FILEINFO,
"The name of the default charset to add to any Content-Type without one or 'Off' to disable"),
AP_INIT_TAKE1("AcceptPathInfo", set_accept_path_info, NULL, OR_FILEINFO,
"Set to on or off for PATH_INFO to be accepted by handlers, or default for the per-handler preference"),
+AP_INIT_TAKE12("Define", set_define, NULL, EXEC_ON_READ|ACCESS_CONF|RSRC_CONF,
+ "Define a variable, optionally to a value. Same as passing -D to the command line."),
+AP_INIT_TAKE1("UnDefine", unset_define, NULL, EXEC_ON_READ|ACCESS_CONF|RSRC_CONF,
+ "Undefine the existence of a variable. Undo a Define."),
+AP_INIT_RAW_ARGS("Error", generate_error, NULL, OR_ALL,
+ "Generate error message from within configuration"),
+AP_INIT_RAW_ARGS("<If", ifsection, COND_IF, OR_ALL,
+ "Container for directives to be conditionally applied"),
+AP_INIT_RAW_ARGS("<ElseIf", ifsection, COND_ELSEIF, OR_ALL,
+ "Container for directives to be conditionally applied"),
+AP_INIT_RAW_ARGS("<Else", ifsection, COND_ELSE, OR_ALL,
+ "Container for directives to be conditionally applied"),
/* Old resource config file commands */
@@ -3361,11 +3876,13 @@ AP_INIT_TAKE2("ErrorDocument", set_error_document, NULL, OR_FILEINFO,
AP_INIT_RAW_ARGS("AllowOverride", set_override, NULL, ACCESS_CONF,
"Controls what groups of directives can be configured by per-directory "
"config files"),
+AP_INIT_TAKE_ARGV("AllowOverrideList", set_override_list, NULL, ACCESS_CONF,
+ "Controls what individual directives can be configured by per-directory "
+ "config files"),
AP_INIT_RAW_ARGS("Options", set_options, NULL, OR_OPTIONS,
"Set a number of attributes for a given directory"),
-AP_INIT_TAKE1("DefaultType", ap_set_string_slot,
- (void*)APR_OFFSETOF(core_dir_config, ap_default_type),
- OR_FILEINFO, "the default MIME type for untypable files"),
+AP_INIT_TAKE1("DefaultType", set_default_type, NULL, OR_FILEINFO,
+ "the default media type for otherwise untyped files (DEPRECATED)"),
AP_INIT_RAW_ARGS("FileETag", set_etag_bits, NULL, OR_FILEINFO,
"Specify components used to construct a file's ETag"),
AP_INIT_TAKE1("EnableMMAP", set_enable_mmap, NULL, OR_FILEINFO,
@@ -3397,6 +3914,8 @@ AP_INIT_TAKE1("ServerRoot", set_server_root, NULL, RSRC_CONF | EXEC_ON_READ,
AP_INIT_TAKE1("ErrorLog", set_server_string_slot,
(void *)APR_OFFSETOF(server_rec, error_fname), RSRC_CONF,
"The filename of the error log"),
+AP_INIT_TAKE12("ErrorLogFormat", set_errorlog_format, NULL, RSRC_CONF,
+ "Format string for the ErrorLog"),
AP_INIT_RAW_ARGS("ServerAlias", set_server_alias, NULL, RSRC_CONF,
"A name or names alternately used to access the server"),
AP_INIT_TAKE1("ServerPath", set_serverpath, NULL, RSRC_CONF,
@@ -3415,13 +3934,19 @@ AP_INIT_TAKE1("UseCanonicalPhysicalPort", set_use_canonical_phys_port, NULL,
/* TODO: ListenBacklog in MPM */
AP_INIT_TAKE1("Include", include_config, NULL,
(RSRC_CONF | ACCESS_CONF | EXEC_ON_READ),
- "Name of the config file to be included"),
-AP_INIT_TAKE1("LogLevel", set_loglevel, NULL, RSRC_CONF,
+ "Name(s) of the config file(s) to be included; fails if the wildcard does "
+ "not match at least one file"),
+AP_INIT_TAKE1("IncludeOptional", include_config, (void*)1,
+ (RSRC_CONF | ACCESS_CONF | EXEC_ON_READ),
+ "Name or pattern of the config file(s) to be included; ignored if the file "
+ "does not exist or the pattern does not match any files"),
+AP_INIT_ITERATE("LogLevel", set_loglevel, NULL, RSRC_CONF|ACCESS_CONF,
"Level of verbosity in error logging"),
AP_INIT_TAKE1("NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF,
"A numeric IP address:port, or the name of a host"),
-AP_INIT_TAKE1("ServerTokens", set_serv_tokens, NULL, RSRC_CONF,
- "Determine tokens displayed in the Server: header - Min(imal), OS or Full"),
+AP_INIT_TAKE12("ServerTokens", set_serv_tokens, NULL, RSRC_CONF,
+ "Determine tokens displayed in the Server: header - Min(imal), "
+ "Major, Minor, Prod, OS or Full"),
AP_INIT_TAKE1("LimitRequestLine", set_limit_req_line, NULL, RSRC_CONF,
"Limit on maximum size of an HTTP request line"),
AP_INIT_TAKE1("LimitRequestFieldsize", set_limit_req_fieldsize, NULL,
@@ -3435,10 +3960,18 @@ AP_INIT_TAKE1("LimitRequestBody", set_limit_req_body,
AP_INIT_TAKE1("LimitXMLRequestBody", set_limit_xml_req_body, NULL, OR_ALL,
"Limit (in bytes) on maximum size of an XML-based request "
"body"),
+AP_INIT_RAW_ARGS("Mutex", ap_set_mutex, NULL, RSRC_CONF,
+ "mutex (or \"default\") and mechanism"),
AP_INIT_TAKE1("MaxRanges", set_max_ranges, NULL, RSRC_CONF|ACCESS_CONF,
"Maximum number of Ranges in a request before returning the entire "
"resource, or 0 for unlimited"),
+AP_INIT_TAKE1("MaxRangeOverlaps", set_max_overlaps, NULL, RSRC_CONF|ACCESS_CONF,
+ "Maximum number of overlaps in Ranges in a request before returning the entire "
+ "resource, or 0 for unlimited"),
+AP_INIT_TAKE1("MaxRangeReversals", set_max_reversals, NULL, RSRC_CONF|ACCESS_CONF,
+ "Maximum number of reversals in Ranges in a request before returning the entire "
+ "resource, or 0 for unlimited"),
/* System Resource Controls */
#ifdef RLIMIT_CPU
AP_INIT_TAKE12("RLimitCPU", set_limit_cpu,
@@ -3481,59 +4014,43 @@ AP_INIT_TAKE1("SetOutputFilter", ap_set_string_slot,
AP_INIT_TAKE1("SetInputFilter", ap_set_string_slot,
(void *)APR_OFFSETOF(core_dir_config, input_filters), OR_FILEINFO,
"filter (or ; delimited list of filters) to be run on the request body"),
-AP_INIT_ITERATE2("AddOutputFilterByType", add_ct_output_filters,
- (void *)APR_OFFSETOF(core_dir_config, ct_output_filters), OR_FILEINFO,
- "output filter name followed by one or more content-types"),
AP_INIT_TAKE1("AllowEncodedSlashes", set_allow2f, NULL, RSRC_CONF,
"Allow URLs containing '/' encoded as '%2F'"),
+/* scoreboard.c directives */
+AP_INIT_TAKE1("ScoreBoardFile", ap_set_scoreboard, NULL, RSRC_CONF,
+ "A file for Apache to maintain runtime process management information"),
+AP_INIT_FLAG("ExtendedStatus", ap_set_extended_status, NULL, RSRC_CONF,
+ "\"On\" to track extended status information, \"Off\" to disable"),
+AP_INIT_FLAG("SeeRequestTail", ap_set_reqtail, NULL, RSRC_CONF,
+ "For extended status, \"On\" to see the last 63 chars of "
+ "the request line, \"Off\" (default) to see the first 63"),
+
/*
* These are default configuration directives that mpms can/should
- * pay attention to. If an mpm wishes to use these, they should
- * #defined them in mpm.h.
+ * pay attention to.
+ * XXX These are not for all platforms, and even some Unix MPMs might not want
+ * some directives.
*/
-#ifdef AP_MPM_WANT_SET_PIDFILE
AP_INIT_TAKE1("PidFile", ap_mpm_set_pidfile, NULL, RSRC_CONF,
"A file for logging the server process ID"),
-#endif
-#ifdef AP_MPM_WANT_SET_SCOREBOARD
-AP_INIT_TAKE1("ScoreBoardFile", ap_mpm_set_scoreboard, NULL, RSRC_CONF,
- "A file for Apache to maintain runtime process management information"),
-#endif
-#ifdef AP_MPM_WANT_SET_LOCKFILE
-AP_INIT_TAKE1("LockFile", ap_mpm_set_lockfile, NULL, RSRC_CONF,
- "The lockfile used when Apache needs to lock the accept() call"),
-#endif
-#ifdef AP_MPM_WANT_SET_MAX_REQUESTS
AP_INIT_TAKE1("MaxRequestsPerChild", ap_mpm_set_max_requests, NULL, RSRC_CONF,
- "Maximum number of requests a particular child serves before dying."),
-#endif
-#ifdef AP_MPM_WANT_SET_COREDUMPDIR
+ "Maximum number of connections a particular child serves before "
+ "dying. (DEPRECATED, use MaxConnectionsPerChild)"),
+AP_INIT_TAKE1("MaxConnectionsPerChild", ap_mpm_set_max_requests, NULL, RSRC_CONF,
+ "Maximum number of connections a particular child serves before dying."),
AP_INIT_TAKE1("CoreDumpDirectory", ap_mpm_set_coredumpdir, NULL, RSRC_CONF,
"The location of the directory Apache changes to before dumping core"),
-#endif
-#ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
-AP_INIT_TAKE1("AcceptMutex", ap_mpm_set_accept_lock_mech, NULL, RSRC_CONF,
- ap_valid_accept_mutex_string),
-#endif
-#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
AP_INIT_TAKE1("MaxMemFree", ap_mpm_set_max_mem_free, NULL, RSRC_CONF,
"Maximum number of 1k blocks a particular childs allocator may hold."),
-#endif
-#ifdef AP_MPM_WANT_SET_STACKSIZE
AP_INIT_TAKE1("ThreadStackSize", ap_mpm_set_thread_stacksize, NULL, RSRC_CONF,
"Size in bytes of stack used by threads handling client connections"),
-#endif
#if AP_ENABLE_EXCEPTION_HOOK
AP_INIT_TAKE1("EnableExceptionHook", ap_mpm_set_exception_hook, NULL, RSRC_CONF,
"Controls whether exception hook may be called after a crash"),
#endif
AP_INIT_TAKE1("TraceEnable", set_trace_enable, NULL, RSRC_CONF,
"'on' (default), 'off' or 'extended' to trace request body content"),
-#ifdef SUEXEC_BIN
-AP_INIT_FLAG("Suexec", unixd_set_suexec, NULL, RSRC_CONF,
- "Enable or disable suEXEC support"),
-#endif
{ NULL }
};
@@ -3544,9 +4061,8 @@ AP_INIT_FLAG("Suexec", unixd_set_suexec, NULL, RSRC_CONF,
AP_DECLARE_NONSTD(int) ap_core_translate(request_rec *r)
{
- void *sconf = r->server->module_config;
- core_server_config *conf = ap_get_module_config(sconf, &core_module);
apr_status_t rv;
+ char *path;
/* XXX this seems too specific, this should probably become
* some general-case test
@@ -3555,7 +4071,7 @@ AP_DECLARE_NONSTD(int) ap_core_translate(request_rec *r)
return HTTP_FORBIDDEN;
}
if (!r->uri || ((r->uri[0] != '/') && strcmp(r->uri, "*"))) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00126)
"Invalid URI in request %s", r->the_request);
return HTTP_BAD_REQUEST;
}
@@ -3566,46 +4082,31 @@ AP_DECLARE_NONSTD(int) ap_core_translate(request_rec *r)
|| r->uri[r->server->pathlen] == '/'
|| r->uri[r->server->pathlen] == '\0'))
{
- /* skip all leading /'s (e.g. http://localhost///foo)
- * so we are looking at only the relative path.
- */
- char *path = r->uri + r->server->pathlen;
- while (*path == '/') {
- ++path;
- }
- if ((rv = apr_filepath_merge(&r->filename, conf->ap_document_root, path,
- APR_FILEPATH_TRUENAME
- | APR_FILEPATH_SECUREROOT, r->pool))
- != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- "Cannot map %s to file", r->the_request);
- return HTTP_FORBIDDEN;
- }
- r->canonical_filename = r->filename;
+ path = r->uri + r->server->pathlen;
}
else {
- /*
- * Make sure that we do not mess up the translation by adding two
- * /'s in a row. This happens under windows when the document
- * root ends with a /
- */
- /* skip all leading /'s (e.g. http://localhost///foo)
- * so we are looking at only the relative path.
- */
- char *path = r->uri;
- while (*path == '/') {
- ++path;
- }
- if ((rv = apr_filepath_merge(&r->filename, conf->ap_document_root, path,
- APR_FILEPATH_TRUENAME
- | APR_FILEPATH_SECUREROOT, r->pool))
- != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- "Cannot map %s to file", r->the_request);
- return HTTP_FORBIDDEN;
- }
- r->canonical_filename = r->filename;
+ path = r->uri;
}
+ /*
+ * Make sure that we do not mess up the translation by adding two
+ * /'s in a row. This happens under windows when the document
+ * root ends with a /
+ */
+ /* skip all leading /'s (e.g. http://localhost///foo)
+ * so we are looking at only the relative path.
+ */
+ while (*path == '/') {
+ ++path;
+ }
+ if ((rv = apr_filepath_merge(&r->filename, ap_document_root(r), path,
+ APR_FILEPATH_TRUENAME
+ | APR_FILEPATH_SECUREROOT, r->pool))
+ != APR_SUCCESS) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00127)
+ "Cannot map %s to file", r->the_request);
+ return HTTP_FORBIDDEN;
+ }
+ r->canonical_filename = r->filename;
return OK;
}
@@ -3636,8 +4137,7 @@ static int do_nothing(request_rec *r) { return OK; }
static int core_override_type(request_rec *r)
{
core_dir_config *conf =
- (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
+ (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
/* Check for overrides with ForceType / SetHandler
*/
@@ -3653,9 +4153,9 @@ static int core_override_type(request_rec *r)
* beginning of the fixup phase (here!), so modules should override the user's
* discretion in their own module fixup phase. It is tristate, if
* the user doesn't specify, the result is AP_REQ_DEFAULT_PATH_INFO.
- * (which the module may interpret to its own customary behavior.)
+ * (which the module may interpret to its own customary behavior.)
* It won't be touched if the value is no longer AP_ACCEPT_PATHINFO_UNSET,
- * so any module changing the value prior to the fixup phase
+ * so any module changing the value prior to the fixup phase
* OVERRIDES the user's choice.
*/
if ((r->used_path_info == AP_REQ_DEFAULT_PATH_INFO)
@@ -3685,9 +4185,8 @@ static int default_handler(request_rec *r)
*/
int bld_content_md5;
- d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
- bld_content_md5 = (d->content_md5 & 1)
+ d = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
+ bld_content_md5 = (d->content_md5 == AP_CONTENT_MD5_ON)
&& r->output_filters->frec->ftype != AP_FTYPE_RESOURCE;
ap_allow_standard_methods(r, MERGE_ALLOW, M_GET, M_OPTIONS, M_POST, -1);
@@ -3702,8 +4201,8 @@ static int default_handler(request_rec *r)
}
if (r->method_number == M_GET || r->method_number == M_POST) {
- if (r->finfo.filetype == 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ if (r->finfo.filetype == APR_NOFILE) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00128)
"File does not exist: %s", r->filename);
return HTTP_NOT_FOUND;
}
@@ -3712,7 +4211,7 @@ static int default_handler(request_rec *r)
* raw I/O on a dir.
*/
if (r->finfo.filetype == APR_DIR) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00129)
"Attempt to serve directory: %s", r->filename);
return HTTP_NOT_FOUND;
}
@@ -3721,7 +4220,7 @@ static int default_handler(request_rec *r)
r->path_info && *r->path_info)
{
/* default to reject */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00130)
"File does not exist: %s",
apr_pstrcat(r->pool, r->filename, r->path_info, NULL));
return HTTP_NOT_FOUND;
@@ -3738,10 +4237,10 @@ static int default_handler(request_rec *r)
if (r->method_number != M_GET) {
core_request_config *req_cfg;
- req_cfg = ap_get_module_config(r->request_config, &core_module);
+ req_cfg = ap_get_core_module_config(r->request_config);
if (!req_cfg->deliver_script) {
/* The flag hasn't been set for this request. Punt. */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00131)
"This resource does not accept the %s method.",
r->method);
return HTTP_METHOD_NOT_ALLOWED;
@@ -3751,11 +4250,10 @@ static int default_handler(request_rec *r)
if ((status = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY
#if APR_HAS_SENDFILE
- | ((d->enable_sendfile == ENABLE_SENDFILE_OFF)
- ? 0 : APR_SENDFILE_ENABLED)
+ | AP_SENDFILE_ENABLED(d->enable_sendfile)
#endif
, 0, r->pool)) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(00132)
"file permissions deny server access: %s", r->filename);
return HTTP_FORBIDDEN;
}
@@ -3765,6 +4263,10 @@ static int default_handler(request_rec *r)
ap_set_etag(r);
ap_set_accept_ranges(r);
ap_set_content_length(r, r->finfo.size);
+ if (bld_content_md5) {
+ apr_table_setn(r->headers_out, "Content-MD5",
+ ap_md5digest(r->pool, fd));
+ }
bb = apr_brigade_create(r->pool, c->bucket_alloc);
@@ -3773,40 +4275,13 @@ static int default_handler(request_rec *r)
r->status = errstatus;
}
else {
- if (bld_content_md5) {
- apr_table_setn(r->headers_out, "Content-MD5",
- ap_md5digest(r->pool, fd));
- }
-
- /* For platforms where the size of the file may be larger than
- * that which can be stored in a single bucket (where the
- * length field is an apr_size_t), split it into several
- * buckets: */
- if (sizeof(apr_off_t) > sizeof(apr_size_t)
- && r->finfo.size > AP_MAX_SENDFILE) {
- apr_off_t fsize = r->finfo.size;
- e = apr_bucket_file_create(fd, 0, AP_MAX_SENDFILE, r->pool,
- c->bucket_alloc);
- while (fsize > AP_MAX_SENDFILE) {
- apr_bucket *ce;
- apr_bucket_copy(e, &ce);
- APR_BRIGADE_INSERT_TAIL(bb, ce);
- e->start += AP_MAX_SENDFILE;
- fsize -= AP_MAX_SENDFILE;
- }
- e->length = (apr_size_t)fsize; /* Resize just the last bucket */
- }
- else {
- e = apr_bucket_file_create(fd, 0, (apr_size_t)r->finfo.size,
- r->pool, c->bucket_alloc);
- }
+ e = apr_brigade_insert_file(bb, fd, 0, r->finfo.size, r->pool);
#if APR_HAS_MMAP
if (d->enable_mmap == ENABLE_MMAP_OFF) {
(void)apr_bucket_file_enable_mmap(e, 0);
}
#endif
- APR_BRIGADE_INSERT_TAIL(bb, e);
}
e = apr_bucket_eos_create(c->bucket_alloc);
@@ -3820,7 +4295,7 @@ static int default_handler(request_rec *r)
}
else {
/* no way to know what type of error occurred */
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00133)
"default_handler: ap_pass_brigade returned %i",
status);
return HTTP_INTERNAL_SERVER_ERROR;
@@ -3828,8 +4303,19 @@ static int default_handler(request_rec *r)
}
else { /* unusual method (not GET or POST) */
if (r->method_number == M_INVALID) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Invalid method in request %s", r->the_request);
+ /* See if this looks like an undecrypted SSL handshake attempt.
+ * It's safe to look a couple bytes into the_request if it exists, as it's
+ * always allocated at least MIN_LINE_ALLOC (80) bytes.
+ */
+ if (r->the_request
+ && r->the_request[0] == 0x16
+ && (r->the_request[1] == 0x2 || r->the_request[1] == 0x3)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00134)
+ "Invalid method in request %s - possible attempt to establish SSL connection on non-SSL port", r->the_request);
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00135)
+ "Invalid method in request %s", r->the_request);
+ }
return HTTP_NOT_IMPLEMENTED;
}
@@ -3843,23 +4329,61 @@ static int default_handler(request_rec *r)
/* Optional function coming from mod_logio, used for logging of output
* traffic
*/
-APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *logio_add_bytes_out;
+APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *ap__logio_add_bytes_out;
+APR_OPTIONAL_FN_TYPE(authz_some_auth_required) *ap__authz_ap_some_auth_required;
+
+/* Insist that at least one module will undertake to provide system
+ * security by dropping startup privileges.
+ */
+static int sys_privileges = 0;
+AP_DECLARE(int) ap_sys_privileges_handlers(int inc)
+{
+ sys_privileges += inc;
+ return sys_privileges;
+}
+
+static int core_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
+{
+ ap_mutex_init(pconf);
+
+ if (!saved_server_config_defines)
+ init_config_defines(pconf);
+ apr_pool_cleanup_register(pconf, NULL, reset_config_defines,
+ apr_pool_cleanup_null);
+
+ mpm_common_pre_config(pconf);
+
+ return OK;
+}
static int core_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
- logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out);
+ ap__logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out);
ident_lookup = APR_RETRIEVE_OPTIONAL_FN(ap_ident_lookup);
+ ap__authz_ap_some_auth_required = APR_RETRIEVE_OPTIONAL_FN(authz_some_auth_required);
+ authn_ap_auth_type = APR_RETRIEVE_OPTIONAL_FN(authn_ap_auth_type);
+ authn_ap_auth_name = APR_RETRIEVE_OPTIONAL_FN(authn_ap_auth_name);
+ access_compat_ap_satisfies = APR_RETRIEVE_OPTIONAL_FN(access_compat_ap_satisfies);
set_banner(pconf);
ap_setup_make_content_type(pconf);
+ ap_setup_auth_internal(ptemp);
+ if (!sys_privileges) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, APLOGNO(00136)
+ "Server MUST relinquish startup privileges before "
+ "accepting connections. Please ensure mod_unixd "
+ "or other system security module is loaded.");
+ return !OK;
+ }
+ apr_pool_cleanup_register(pconf, NULL, ap_mpm_end_gen_helper,
+ apr_pool_cleanup_null);
return OK;
}
static void core_insert_filter(request_rec *r)
{
core_dir_config *conf = (core_dir_config *)
- ap_get_module_config(r->per_dir_config,
- &core_module);
+ ap_get_core_module_config(r->per_dir_config);
const char *filter, *filters = conf->output_filters;
if (filters) {
@@ -3900,7 +4424,7 @@ AP_DECLARE(void **) ap_get_request_note(request_rec *r, apr_size_t note_num)
}
req_cfg = (core_request_config *)
- ap_get_module_config(r->request_config, &core_module);
+ ap_get_core_module_config(r->request_config);
if (!req_cfg) {
return NULL;
@@ -3909,6 +4433,11 @@ AP_DECLARE(void **) ap_get_request_note(request_rec *r, apr_size_t note_num)
return &(req_cfg->notes[note_num]);
}
+AP_DECLARE(apr_socket_t *) ap_get_conn_socket(conn_rec *c)
+{
+ return ap_get_core_module_config(c->conn_config);
+}
+
static int core_create_req(request_rec *r)
{
/* Alloc the config struct and the array of request notes in
@@ -3925,14 +4454,14 @@ static int core_create_req(request_rec *r)
if (r->main) {
core_request_config *main_req_cfg = (core_request_config *)
- ap_get_module_config(r->main->request_config, &core_module);
+ ap_get_core_module_config(r->main->request_config);
req_cfg->bb = main_req_cfg->bb;
}
else {
req_cfg->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
}
- ap_set_module_config(r->request_config, &core_module, req_cfg);
+ ap_set_core_module_config(r->request_config, req_cfg);
return OK;
}
@@ -3961,26 +4490,27 @@ static conn_rec *core_create_conn(apr_pool_t *ptrans, server_rec *server,
c->pool = ptrans;
if ((rv = apr_socket_addr_get(&c->local_addr, APR_LOCAL, csd))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
+ ap_log_error(APLOG_MARK, APLOG_INFO, rv, server, APLOGNO(00137)
"apr_socket_addr_get(APR_LOCAL)");
apr_socket_close(csd);
return NULL;
}
apr_sockaddr_ip_get(&c->local_ip, c->local_addr);
- if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, csd))
+ if ((rv = apr_socket_addr_get(&c->client_addr, APR_REMOTE, csd))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
+ ap_log_error(APLOG_MARK, APLOG_INFO, rv, server, APLOGNO(00138)
"apr_socket_addr_get(APR_REMOTE)");
apr_socket_close(csd);
return NULL;
}
- apr_sockaddr_ip_get(&c->remote_ip, c->remote_addr);
+ apr_sockaddr_ip_get(&c->client_ip, c->client_addr);
c->base_server = server;
c->id = id;
c->bucket_alloc = alloc;
+
c->clogging_input_filters = 0;
return c;
@@ -3991,7 +4521,6 @@ static int core_pre_connection(conn_rec *c, void *csd)
core_net_rec *net = apr_palloc(c->pool, sizeof(*net));
apr_status_t rv;
-#ifdef AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
/* 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
@@ -4004,10 +4533,9 @@ static int core_pre_connection(conn_rec *c, void *csd)
/* expected cause is that the client disconnected already,
* hence the debug level
*/
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c,
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(00139)
"apr_socket_opt_set(APR_TCP_NODELAY)");
}
-#endif
/* The core filter requires the timeout mode to be set, which
* incidentally sets the socket to be nonblocking. If this
@@ -4018,7 +4546,7 @@ static int core_pre_connection(conn_rec *c, void *csd)
rv = apr_socket_timeout_set(csd, c->base_server->timeout);
if (rv != APR_SUCCESS) {
/* expected cause is that the client disconnected already */
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c,
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(00140)
"apr_socket_timeout_set");
}
@@ -4027,38 +4555,201 @@ static int core_pre_connection(conn_rec *c, void *csd)
net->out_ctx = NULL;
net->client_socket = csd;
- ap_set_module_config(net->c->conn_config, &core_module, csd);
+ ap_set_core_module_config(net->c->conn_config, csd);
ap_add_input_filter_handle(ap_core_input_filter_handle, net, NULL, net->c);
ap_add_output_filter_handle(ap_core_output_filter_handle, net, NULL, net->c);
return DONE;
}
+AP_DECLARE(int) ap_state_query(int query)
+{
+ switch (query) {
+ case AP_SQ_MAIN_STATE:
+ return ap_main_state;
+ case AP_SQ_RUN_MODE:
+ return ap_run_mode;
+ case AP_SQ_CONFIG_GEN:
+ return ap_config_generation;
+ default:
+ return AP_SQ_NOT_SUPPORTED;
+ }
+}
+
+static apr_random_t *rng = NULL;
+#if APR_HAS_THREADS
+static apr_thread_mutex_t *rng_mutex = NULL;
+#endif
+
+static void core_child_init(apr_pool_t *pchild, server_rec *s)
+{
+ apr_proc_t proc;
+#if APR_HAS_THREADS
+ int threaded_mpm;
+ if (ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm) == APR_SUCCESS
+ && threaded_mpm)
+ {
+ apr_thread_mutex_create(&rng_mutex, APR_THREAD_MUTEX_DEFAULT, pchild);
+ }
+#endif
+ /* The MPMs use plain fork() and not apr_proc_fork(), so we have to call
+ * apr_random_after_fork() manually in the child
+ */
+ proc.pid = getpid();
+ apr_random_after_fork(&proc);
+}
+
+AP_CORE_DECLARE(void) ap_random_parent_after_fork(void)
+{
+ /*
+ * To ensure that the RNG state in the parent changes after the fork, we
+ * pull some data from the RNG and discard it. This ensures that the RNG
+ * states in the children are different even after the pid wraps around.
+ * As we only use apr_random for insecure random bytes, pulling 2 bytes
+ * should be enough.
+ * XXX: APR should probably have some dedicated API to do this, but it
+ * XXX: currently doesn't.
+ */
+ apr_uint16_t data;
+ apr_random_insecure_bytes(rng, &data, sizeof(data));
+}
+
+AP_CORE_DECLARE(void) ap_init_rng(apr_pool_t *p)
+{
+ unsigned char seed[8];
+ apr_status_t rv;
+ rng = apr_random_standard_new(p);
+ do {
+ rv = apr_generate_random_bytes(seed, sizeof(seed));
+ if (rv != APR_SUCCESS)
+ goto error;
+ apr_random_add_entropy(rng, seed, sizeof(seed));
+ rv = apr_random_insecure_ready(rng);
+ } while (rv == APR_ENOTENOUGHENTROPY);
+ if (rv == APR_SUCCESS)
+ return;
+error:
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, APLOGNO(00141)
+ "Could not initialize random number generator");
+ exit(1);
+}
+
+AP_DECLARE(void) ap_random_insecure_bytes(void *buf, apr_size_t size)
+{
+#if APR_HAS_THREADS
+ if (rng_mutex)
+ apr_thread_mutex_lock(rng_mutex);
+#endif
+ /* apr_random_insecure_bytes can only fail with APR_ENOTENOUGHENTROPY,
+ * and we have ruled that out during initialization. Therefore we don't
+ * need to check the return code.
+ */
+ apr_random_insecure_bytes(rng, buf, size);
+#if APR_HAS_THREADS
+ if (rng_mutex)
+ apr_thread_mutex_unlock(rng_mutex);
+#endif
+}
+
+/*
+ * Finding a random number in a range.
+ * n' = a + n(b-a+1)/(M+1)
+ * where:
+ * n' = random number in range
+ * a = low end of range
+ * b = high end of range
+ * n = random number of 0..M
+ * M = maxint
+ * Algorithm 'borrowed' from PHP's rand() function.
+ */
+#define RAND_RANGE(__n, __min, __max, __tmax) \
+(__n) = (__min) + (long) ((double) ((__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
+AP_DECLARE(apr_uint32_t) ap_random_pick(apr_uint32_t min, apr_uint32_t max)
+{
+ apr_uint32_t number;
+ if (max < 16384) {
+ apr_uint16_t num16;
+ ap_random_insecure_bytes(&num16, sizeof(num16));
+ RAND_RANGE(num16, min, max, APR_UINT16_MAX);
+ number = num16;
+ }
+ else {
+ ap_random_insecure_bytes(&number, sizeof(number));
+ RAND_RANGE(number, min, max, APR_UINT32_MAX);
+ }
+ return number;
+}
+
+static void core_dump_config(apr_pool_t *p, server_rec *s)
+{
+ core_server_config *sconf = ap_get_core_module_config(s->module_config);
+ apr_file_t *out = NULL;
+ char *tmp;
+ const char **defines;
+ int i;
+ if (!ap_exists_config_define("DUMP_RUN_CFG"))
+ return;
+
+ apr_file_open_stdout(&out, p);
+ apr_file_printf(out, "ServerRoot: \"%s\"\n", ap_server_root);
+ tmp = ap_server_root_relative(p, sconf->ap_document_root);
+ apr_file_printf(out, "Main DocumentRoot: \"%s\"\n", tmp);
+ tmp = ap_server_root_relative(p, s->error_fname);
+ apr_file_printf(out, "Main ErrorLog: \"%s\"\n", tmp);
+ if (ap_scoreboard_fname) {
+ tmp = ap_server_root_relative(p, ap_scoreboard_fname);
+ apr_file_printf(out, "ScoreBoardFile: \"%s\"\n", tmp);
+ }
+ ap_dump_mutexes(p, s, out);
+ ap_mpm_dump_pidfile(p, out);
+
+ defines = (const char **)ap_server_config_defines->elts;
+ for (i = 0; i < ap_server_config_defines->nelts; i++) {
+ const char *name = defines[i];
+ const char *val = NULL;
+ if (server_config_defined_vars)
+ val = apr_table_get(server_config_defined_vars, name);
+ if (val)
+ apr_file_printf(out, "Define: %s=%s\n", name, val);
+ else
+ apr_file_printf(out, "Define: %s\n", name);
+ }
+
+}
+
static void register_hooks(apr_pool_t *p)
{
- /* create_connection and install_transport_filters are
- * hooks that should always be APR_HOOK_REALLY_LAST to give other
- * modules the opportunity to install alternate network transports
- * and stop other functions from being run.
+ errorlog_hash = apr_hash_make(p);
+ ap_register_log_hooks(p);
+ ap_register_config_hooks(p);
+ ap_expr_init(p);
+
+ /* create_connection and pre_connection should always be hooked
+ * APR_HOOK_REALLY_LAST by core to give other modules the opportunity
+ * to install alternate network transports and stop other functions
+ * from being run.
*/
ap_hook_create_connection(core_create_conn, NULL, NULL,
APR_HOOK_REALLY_LAST);
ap_hook_pre_connection(core_pre_connection, NULL, NULL,
APR_HOOK_REALLY_LAST);
+ ap_hook_pre_config(core_pre_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
ap_hook_post_config(core_post_config,NULL,NULL,APR_HOOK_REALLY_FIRST);
+ ap_hook_test_config(core_dump_config,NULL,NULL,APR_HOOK_FIRST);
ap_hook_translate_name(ap_core_translate,NULL,NULL,APR_HOOK_REALLY_LAST);
ap_hook_map_to_storage(core_map_to_storage,NULL,NULL,APR_HOOK_REALLY_LAST);
ap_hook_open_logs(ap_open_logs,NULL,NULL,APR_HOOK_REALLY_FIRST);
+ ap_hook_child_init(core_child_init,NULL,NULL,APR_HOOK_REALLY_FIRST);
ap_hook_child_init(ap_logs_child_init,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_handler(default_handler,NULL,NULL,APR_HOOK_REALLY_LAST);
/* FIXME: I suspect we can eliminate the need for these do_nothings - Ben */
ap_hook_type_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);
ap_hook_fixups(core_override_type,NULL,NULL,APR_HOOK_REALLY_FIRST);
- ap_hook_access_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);
ap_hook_create_request(core_create_req, NULL, NULL, APR_HOOK_MIDDLE);
APR_OPTIONAL_HOOK(proxy, create_req, core_create_proxy_req, NULL, NULL,
APR_HOOK_MIDDLE);
ap_hook_pre_mpm(ap_create_scoreboard, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_status(ap_core_child_status, NULL, NULL, APR_HOOK_MIDDLE);
/* register the core's insert_filter hook and register core-provided
* filters
@@ -4082,8 +4773,9 @@ static void register_hooks(apr_pool_t *p)
NULL, AP_FTYPE_RESOURCE - 10);
}
-AP_DECLARE_DATA module core_module = {
- STANDARD20_MODULE_STUFF,
+AP_DECLARE_MODULE(core) = {
+ MPM20_MODULE_STUFF,
+ AP_PLATFORM_REWRITE_ARGS_HOOK, /* hook to run before apache parses args */
create_core_dir_config, /* create per-directory config structure */
merge_core_dir_configs, /* merge per-directory config structures */
create_core_server_config, /* create per-server config structure */
diff --git a/server/core_filters.c b/server/core_filters.c
index 7feb157e..686513fe 100644
--- a/server/core_filters.c
+++ b/server/core_filters.c
@@ -25,14 +25,12 @@
#include "apr_fnmatch.h"
#include "apr_hash.h"
#include "apr_thread_proc.h" /* for RLIMIT stuff */
-#include "apr_hooks.h"
#define APR_WANT_IOVEC
#define APR_WANT_STRFUNC
#define APR_WANT_MEMFUNC
#include "apr_want.h"
-#define CORE_PRIVATE
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
@@ -47,7 +45,6 @@
#include "apr_buckets.h"
#include "util_filter.h"
#include "util_ebcdic.h"
-#include "mpm.h"
#include "mpm_common.h"
#include "scoreboard.h"
#include "mod_core.h"
@@ -77,26 +74,9 @@ do { \
} while (!APR_BRIGADE_EMPTY(b) && (e != APR_BRIGADE_SENTINEL(b))); \
} while (0)
-
-/**
- * Split the contents of a brigade after bucket 'e' to an existing brigade
- *
- * XXXX: Should this function be added to APR-Util?
- */
-static void brigade_move(apr_bucket_brigade *b, apr_bucket_brigade *a,
- apr_bucket *e)
-{
- apr_bucket *f;
-
- if (e != APR_BRIGADE_SENTINEL(b)) {
- f = APR_RING_LAST(&b->list);
- APR_RING_UNSPLICE(e, f, link);
- APR_RING_SPLICE_HEAD(&a->list, e, f, apr_bucket, link);
- }
-
- APR_BRIGADE_CHECK_CONSISTENCY(a);
- APR_BRIGADE_CHECK_CONSISTENCY(b);
-}
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
int ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
ap_input_mode_t mode, apr_read_type_e block,
@@ -214,10 +194,9 @@ int ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
* the brigade that was passed down, and send that brigade back.
*
* NOTE: This is VERY dangerous to use, and should only be done with
- * extreme caution. However, the Perchild MPM needs this feature
- * if it is ever going to work correctly again. With this, the Perchild
- * MPM can easily request the socket and all data that has been read,
- * which means that it can pass it to the correct child process.
+ * extreme caution. FWLIW, this would be needed by an MPM like Perchild;
+ * such an MPM can easily request the socket and all data that has been
+ * read, which means that it can pass it to the correct child process.
*/
if (mode == AP_MODE_EXHAUSTIVE) {
apr_bucket *e;
@@ -268,6 +247,37 @@ int ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
return APR_SUCCESS;
}
+ /* Have we read as much data as we wanted (be greedy)? */
+ if (len < readbytes) {
+ apr_size_t bucket_len;
+
+ rv = APR_SUCCESS;
+ /* We already registered the data in e in len */
+ e = APR_BUCKET_NEXT(e);
+ while ((len < readbytes) && (rv == APR_SUCCESS)
+ && (e != APR_BRIGADE_SENTINEL(ctx->b))) {
+ /* Check for the availability of buckets with known length */
+ if (e->length != -1) {
+ len += e->length;
+ e = APR_BUCKET_NEXT(e);
+ }
+ else {
+ /*
+ * Read from bucket, but non blocking. If there isn't any
+ * more data, well than this is fine as well, we will
+ * not wait for more since we already got some and we are
+ * only checking if there isn't more.
+ */
+ rv = apr_bucket_read(e, &str, &bucket_len,
+ APR_NONBLOCK_READ);
+ if (rv == APR_SUCCESS) {
+ len += bucket_len;
+ e = APR_BUCKET_NEXT(e);
+ }
+ }
+ }
+ }
+
/* We can only return at most what we read. */
if (len < readbytes) {
readbytes = len;
@@ -279,7 +289,7 @@ int ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
}
/* Must do move before CONCAT */
- brigade_move(ctx->b, ctx->tmpbb, e);
+ ctx->tmpbb = apr_brigade_split_ex(ctx->b, e, ctx->tmpbb);
if (mode == AP_MODE_READBYTES) {
APR_BRIGADE_CONCAT(b, ctx->b);
@@ -305,216 +315,264 @@ int ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
return APR_SUCCESS;
}
-static apr_status_t writev_it_all(apr_socket_t *s,
- struct iovec *vec, int nvec,
- apr_size_t len, apr_size_t *nbytes)
-{
- apr_size_t bytes_written = 0;
- apr_status_t rv;
- apr_size_t n = len;
- int i = 0;
+static void setaside_remaining_output(ap_filter_t *f,
+ core_output_filter_ctx_t *ctx,
+ apr_bucket_brigade *bb,
+ conn_rec *c);
- *nbytes = 0;
+static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
+ apr_bucket_brigade *bb,
+ apr_size_t *bytes_written,
+ conn_rec *c);
- /* XXX handle checking for non-blocking socket */
- while (bytes_written != len) {
- rv = apr_socket_sendv(s, vec + i, nvec - i, &n);
- *nbytes += n;
- bytes_written += n;
- if (rv != APR_SUCCESS)
- return rv;
+static void remove_empty_buckets(apr_bucket_brigade *bb);
- /* If the write did not complete, adjust the iovecs and issue
- * apr_socket_sendv again
- */
- if (bytes_written < len) {
- /* Skip over the vectors that have already been written */
- apr_size_t cnt = vec[i].iov_len;
- while (n >= cnt && i + 1 < nvec) {
- i++;
- cnt += vec[i].iov_len;
- }
+static apr_status_t send_brigade_blocking(apr_socket_t *s,
+ apr_bucket_brigade *bb,
+ apr_size_t *bytes_written,
+ conn_rec *c);
- if (n < cnt) {
- /* Handle partial write of vec i */
- vec[i].iov_base = (char *) vec[i].iov_base +
- (vec[i].iov_len - (cnt - n));
- vec[i].iov_len = cnt -n;
- }
- }
+static apr_status_t writev_nonblocking(apr_socket_t *s,
+ struct iovec *vec, apr_size_t nvec,
+ apr_bucket_brigade *bb,
+ apr_size_t *cumulative_bytes_written,
+ conn_rec *c);
- n = len - bytes_written;
- }
+#if APR_HAS_SENDFILE
+static apr_status_t sendfile_nonblocking(apr_socket_t *s,
+ apr_bucket *bucket,
+ apr_size_t *cumulative_bytes_written,
+ conn_rec *c);
+#endif
- return APR_SUCCESS;
-}
+/* XXX: Should these be configurable parameters? */
+#define THRESHOLD_MIN_WRITE 4096
+#define THRESHOLD_MAX_BUFFER 65536
+#define MAX_REQUESTS_IN_PIPELINE 5
-/* sendfile_it_all()
- * send the entire file using sendfile()
- * handle partial writes
- * return only when all bytes have been sent or an error is encountered.
+/* Optional function coming from mod_logio, used for logging of output
+ * traffic
*/
+extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *ap__logio_add_bytes_out;
-#if APR_HAS_SENDFILE
-static apr_status_t sendfile_it_all(core_net_rec *c,
- apr_file_t *fd,
- apr_hdtr_t *hdtr,
- apr_off_t file_offset,
- apr_size_t file_bytes_left,
- apr_size_t total_bytes_left,
- apr_size_t *bytes_sent,
- apr_int32_t flags)
+apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
{
+ conn_rec *c = f->c;
+ core_net_rec *net = f->ctx;
+ core_output_filter_ctx_t *ctx = net->out_ctx;
+ apr_bucket_brigade *bb = NULL;
+ apr_bucket *bucket, *next, *flush_upto = NULL;
+ apr_size_t bytes_in_brigade, non_file_bytes_in_brigade;
+ int eor_buckets_in_brigade;
apr_status_t rv;
-#ifdef AP_DEBUG
- apr_interval_time_t timeout = 0;
-#endif
- AP_DEBUG_ASSERT((apr_socket_timeout_get(c->client_socket, &timeout)
- == APR_SUCCESS)
- && timeout > 0); /* socket must be in timeout mode */
-
- /* Reset the bytes_sent field */
- *bytes_sent = 0;
-
- do {
- apr_size_t tmplen = file_bytes_left;
-
- rv = apr_socket_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
- flags);
- *bytes_sent += tmplen;
- total_bytes_left -= tmplen;
- if (!total_bytes_left || rv != APR_SUCCESS) {
- return rv; /* normal case & error exit */
+ /* Fail quickly if the connection has already been aborted. */
+ if (c->aborted) {
+ if (new_bb != NULL) {
+ apr_brigade_cleanup(new_bb);
}
+ return APR_ECONNABORTED;
+ }
- AP_DEBUG_ASSERT(total_bytes_left > 0 && tmplen > 0);
-
- /* partial write, oooh noooo...
- * Skip over any header data which was written
+ if (ctx == NULL) {
+ ctx = apr_pcalloc(c->pool, sizeof(*ctx));
+ net->out_ctx = (core_output_filter_ctx_t *)ctx;
+ rv = apr_socket_opt_set(net->client_socket, APR_SO_NONBLOCK, 1);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ /*
+ * Need to create tmp brigade with correct lifetime. Passing
+ * NULL to apr_brigade_split_ex would result in a brigade
+ * allocated from bb->pool which might be wrong.
*/
- while (tmplen && hdtr->numheaders) {
- if (tmplen >= hdtr->headers[0].iov_len) {
- tmplen -= hdtr->headers[0].iov_len;
- --hdtr->numheaders;
- ++hdtr->headers;
- }
- else {
- char *iov_base = (char *)hdtr->headers[0].iov_base;
+ ctx->tmp_flush_bb = apr_brigade_create(c->pool, c->bucket_alloc);
+ /* same for buffered_bb and ap_save_brigade */
+ ctx->buffered_bb = apr_brigade_create(c->pool, c->bucket_alloc);
+ }
- hdtr->headers[0].iov_len -= tmplen;
- iov_base += tmplen;
- hdtr->headers[0].iov_base = iov_base;
- tmplen = 0;
+ if (new_bb != NULL) {
+ for (bucket = APR_BRIGADE_FIRST(new_bb); bucket != APR_BRIGADE_SENTINEL(new_bb); bucket = APR_BUCKET_NEXT(bucket)) {
+ if (bucket->length > 0) {
+ ctx->bytes_in += bucket->length;
}
}
+ bb = new_bb;
+ }
- /* Skip over any file data which was written */
-
- if (tmplen <= file_bytes_left) {
- file_offset += tmplen;
- file_bytes_left -= tmplen;
- continue;
+ if ((ctx->buffered_bb != NULL) &&
+ !APR_BRIGADE_EMPTY(ctx->buffered_bb)) {
+ if (new_bb != NULL) {
+ APR_BRIGADE_PREPEND(bb, ctx->buffered_bb);
}
+ else {
+ bb = ctx->buffered_bb;
+ }
+ c->data_in_output_filters = 0;
+ }
+ else if (new_bb == NULL) {
+ return APR_SUCCESS;
+ }
- tmplen -= file_bytes_left;
- file_bytes_left = 0;
- file_offset = 0;
+ /* Scan through the brigade and decide whether to attempt a write,
+ * based on the following rules:
+ *
+ * 1) The new_bb is null: Do a nonblocking write of as much as
+ * possible: do a nonblocking write of as much data as possible,
+ * then save the rest in ctx->buffered_bb. (If new_bb == NULL,
+ * it probably means that the MPM is doing asynchronous write
+ * completion and has just determined that this connection
+ * is writable.)
+ *
+ * 2) The brigade contains a flush bucket: Do a blocking write
+ * of everything up that point.
+ *
+ * 3) The request is in CONN_STATE_HANDLER state, and the brigade
+ * contains at least THRESHOLD_MAX_BUFFER bytes in non-file
+ * buckets: Do blocking writes until the amount of data in the
+ * buffer is less than THRESHOLD_MAX_BUFFER. (The point of this
+ * rule is to provide flow control, in case a handler is
+ * streaming out lots of data faster than the data can be
+ * sent to the client.)
+ *
+ * 4) The request is in CONN_STATE_HANDLER state, and the brigade
+ * contains at least MAX_REQUESTS_IN_PIPELINE EOR buckets:
+ * Do blocking writes until less than MAX_REQUESTS_IN_PIPELINE EOR
+ * buckets are left. (The point of this rule is to prevent too many
+ * FDs being kept open by pipelined requests, possibly allowing a
+ * DoS).
+ *
+ * 5) The brigade contains at least THRESHOLD_MIN_WRITE
+ * bytes: Do a nonblocking write of as much data as possible,
+ * then save the rest in ctx->buffered_bb.
+ */
- /* Skip over any trailer data which was written */
+ if (new_bb == NULL) {
+ rv = send_brigade_nonblocking(net->client_socket, bb,
+ &(ctx->bytes_written), c);
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ rv = APR_SUCCESS;
+ }
+ else if (rv != APR_SUCCESS) {
+ /* The client has aborted the connection */
+ c->aborted = 1;
+ }
+ setaside_remaining_output(f, ctx, bb, c);
+ return rv;
+ }
- while (tmplen && hdtr->numtrailers) {
- if (tmplen >= hdtr->trailers[0].iov_len) {
- tmplen -= hdtr->trailers[0].iov_len;
- --hdtr->numtrailers;
- ++hdtr->trailers;
+ bytes_in_brigade = 0;
+ non_file_bytes_in_brigade = 0;
+ eor_buckets_in_brigade = 0;
+ for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb);
+ bucket = next) {
+ next = APR_BUCKET_NEXT(bucket);
+
+ if (!APR_BUCKET_IS_METADATA(bucket)) {
+ if (bucket->length == (apr_size_t)-1) {
+ const char *data;
+ apr_size_t length;
+ /* XXX support nonblocking read here? */
+ rv = apr_bucket_read(bucket, &data, &length, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ /* reading may have split the bucket, so recompute next: */
+ next = APR_BUCKET_NEXT(bucket);
}
- else {
- char *iov_base = (char *)hdtr->trailers[0].iov_base;
+ bytes_in_brigade += bucket->length;
+ if (!APR_BUCKET_IS_FILE(bucket)) {
+ non_file_bytes_in_brigade += bucket->length;
+ }
+ }
+ else if (AP_BUCKET_IS_EOR(bucket)) {
+ eor_buckets_in_brigade++;
+ }
- hdtr->trailers[0].iov_len -= tmplen;
- iov_base += tmplen;
- hdtr->trailers[0].iov_base = iov_base;
- tmplen = 0;
+ if (APR_BUCKET_IS_FLUSH(bucket) ||
+ (non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) ||
+ (eor_buckets_in_brigade > MAX_REQUESTS_IN_PIPELINE) )
+ {
+ if (APLOGctrace6(c)) {
+ char *reason = APR_BUCKET_IS_FLUSH(bucket) ?
+ "FLUSH bucket" :
+ (non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) ?
+ "THRESHOLD_MAX_BUFFER" :
+ "MAX_REQUESTS_IN_PIPELINE";
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
+ "core_output_filter: flushing because of %s",
+ reason);
}
+ /*
+ * Defer the actual blocking write to avoid doing many writes.
+ */
+ flush_upto = next;
+
+ bytes_in_brigade = 0;
+ non_file_bytes_in_brigade = 0;
+ eor_buckets_in_brigade = 0;
+ }
+ }
+
+ if (flush_upto != NULL) {
+ ctx->tmp_flush_bb = apr_brigade_split_ex(bb, flush_upto,
+ ctx->tmp_flush_bb);
+ rv = send_brigade_blocking(net->client_socket, bb,
+ &(ctx->bytes_written), c);
+ if (rv != APR_SUCCESS) {
+ /* The client has aborted the connection */
+ c->aborted = 1;
+ return rv;
+ }
+ APR_BRIGADE_CONCAT(bb, ctx->tmp_flush_bb);
+ }
+
+ if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) {
+ rv = send_brigade_nonblocking(net->client_socket, bb,
+ &(ctx->bytes_written), c);
+ if ((rv != APR_SUCCESS) && (!APR_STATUS_IS_EAGAIN(rv))) {
+ /* The client has aborted the connection */
+ c->aborted = 1;
+ return rv;
}
- } while (1);
+ }
+
+ setaside_remaining_output(f, ctx, bb, c);
+ return APR_SUCCESS;
}
-#endif
/*
- * emulate_sendfile()
- * Sends the contents of file fd along with header/trailer bytes, if any,
- * to the network. emulate_sendfile will return only when all the bytes have been
- * sent (i.e., it handles partial writes) or on a network error condition.
+ * This function assumes that either ctx->buffered_bb == NULL, or
+ * ctx->buffered_bb is empty, or ctx->buffered_bb == bb
*/
-static apr_status_t emulate_sendfile(core_net_rec *c, apr_file_t *fd,
- apr_hdtr_t *hdtr, apr_off_t offset,
- apr_size_t length, apr_size_t *nbytes)
+static void setaside_remaining_output(ap_filter_t *f,
+ core_output_filter_ctx_t *ctx,
+ apr_bucket_brigade *bb,
+ conn_rec *c)
{
- apr_status_t rv = APR_SUCCESS;
- apr_size_t togo; /* Remaining number of bytes in the file to send */
- apr_size_t sendlen = 0;
- apr_size_t bytes_sent;
- apr_int32_t i;
- apr_off_t o; /* Track the file offset for partial writes */
- char buffer[8192];
-
- *nbytes = 0;
-
- /* Send the headers
- * writev_it_all handles partial writes.
- * XXX: optimization... if headers are less than MIN_WRITE_SIZE, copy
- * them into buffer
- */
- if (hdtr && hdtr->numheaders > 0 ) {
- for (i = 0; i < hdtr->numheaders; i++) {
- sendlen += hdtr->headers[i].iov_len;
- }
-
- rv = writev_it_all(c->client_socket, hdtr->headers, hdtr->numheaders,
- sendlen, &bytes_sent);
- *nbytes += bytes_sent; /* track total bytes sent */
- }
-
- /* Seek the file to 'offset' */
- if (offset >= 0 && rv == APR_SUCCESS) {
- rv = apr_file_seek(fd, APR_SET, &offset);
- }
-
- /* Send the file, making sure to handle partial writes */
- togo = length;
- while (rv == APR_SUCCESS && togo) {
- sendlen = togo > sizeof(buffer) ? sizeof(buffer) : togo;
- o = 0;
- rv = apr_file_read(fd, buffer, &sendlen);
- while (rv == APR_SUCCESS && sendlen) {
- bytes_sent = sendlen;
- rv = apr_socket_send(c->client_socket, &buffer[o], &bytes_sent);
- *nbytes += bytes_sent;
- if (rv == APR_SUCCESS) {
- sendlen -= bytes_sent; /* sendlen != bytes_sent ==> partial write */
- o += bytes_sent; /* o is where we are in the buffer */
- togo -= bytes_sent; /* track how much of the file we've sent */
+ if (bb == NULL) {
+ return;
+ }
+ remove_empty_buckets(bb);
+ if (!APR_BRIGADE_EMPTY(bb)) {
+ c->data_in_output_filters = 1;
+ if (bb != ctx->buffered_bb) {
+ if (!ctx->deferred_write_pool) {
+ apr_pool_create(&ctx->deferred_write_pool, c->pool);
+ apr_pool_tag(ctx->deferred_write_pool, "deferred_write");
}
+ ap_save_brigade(f, &(ctx->buffered_bb), &bb,
+ ctx->deferred_write_pool);
+ apr_brigade_cleanup(bb);
}
}
-
- /* Send the trailers
- * XXX: optimization... if it will fit, send this on the last send in the
- * loop above
- */
- sendlen = 0;
- if ( rv == APR_SUCCESS && hdtr && hdtr->numtrailers > 0 ) {
- for (i = 0; i < hdtr->numtrailers; i++) {
- sendlen += hdtr->trailers[i].iov_len;
- }
- rv = writev_it_all(c->client_socket, hdtr->trailers, hdtr->numtrailers,
- sendlen, &bytes_sent);
- *nbytes += bytes_sent;
+ else if (ctx->deferred_write_pool) {
+ /*
+ * There are no more requests in the pipeline. We can just clear the
+ * pool.
+ */
+ apr_pool_clear(ctx->deferred_write_pool);
}
-
- return rv;
}
#ifndef APR_MAX_IOVEC_SIZE
@@ -527,398 +585,266 @@ static apr_status_t emulate_sendfile(core_net_rec *c, apr_file_t *fd,
#endif
#endif
-/* Optional function coming from mod_logio, used for logging of output
- * traffic
- */
-extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *logio_add_bytes_out;
-
-apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
+static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
+ apr_bucket_brigade *bb,
+ apr_size_t *bytes_written,
+ conn_rec *c)
{
+ apr_bucket *bucket, *next;
apr_status_t rv;
- apr_bucket_brigade *more;
- conn_rec *c = f->c;
- core_net_rec *net = f->ctx;
- core_output_filter_ctx_t *ctx = net->out_ctx;
- apr_read_type_e eblock = APR_NONBLOCK_READ;
- apr_pool_t *input_pool = b->p;
+ struct iovec vec[MAX_IOVEC_TO_WRITE];
+ apr_size_t nvec = 0;
- /* Fail quickly if the connection has already been aborted. */
- if (c->aborted) {
- apr_brigade_cleanup(b);
- return APR_ECONNABORTED;
- }
+ remove_empty_buckets(bb);
- if (ctx == NULL) {
- ctx = apr_pcalloc(c->pool, sizeof(*ctx));
- net->out_ctx = ctx;
- }
-
- /* If we have a saved brigade, concatenate the new brigade to it */
- if (ctx->b) {
- APR_BRIGADE_CONCAT(ctx->b, b);
- b = ctx->b;
- ctx->b = NULL;
- }
-
- /* Perform multiple passes over the brigade, sending batches of output
- to the connection. */
- while (b && !APR_BRIGADE_EMPTY(b)) {
- apr_size_t nbytes = 0;
- apr_bucket *last_e = NULL; /* initialized for debugging */
- apr_bucket *e;
-
- /* one group of iovecs per pass over the brigade */
- apr_size_t nvec = 0;
- apr_size_t nvec_trailers = 0;
- struct iovec vec[MAX_IOVEC_TO_WRITE];
- struct iovec vec_trailers[MAX_IOVEC_TO_WRITE];
-
- /* one file per pass over the brigade */
- apr_file_t *fd = NULL;
- apr_size_t flen = 0;
- apr_off_t foffset = 0;
-
- /* keep track of buckets that we've concatenated
- * to avoid small writes
- */
- apr_bucket *last_merged_bucket = NULL;
-
- /* tail of brigade if we need another pass */
- more = NULL;
-
- /* Iterate over the brigade: collect iovecs and/or a file */
- for (e = APR_BRIGADE_FIRST(b);
- e != APR_BRIGADE_SENTINEL(b);
- e = APR_BUCKET_NEXT(e))
- {
- /* keep track of the last bucket processed */
- last_e = e;
- if (APR_BUCKET_IS_EOS(e) || AP_BUCKET_IS_EOC(e)) {
- break;
- }
- else if (APR_BUCKET_IS_FLUSH(e)) {
- if (e != APR_BRIGADE_LAST(b)) {
- more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
- }
- break;
- }
-
- /* It doesn't make any sense to use sendfile for a file bucket
- * that represents 10 bytes.
+ for (bucket = APR_BRIGADE_FIRST(bb);
+ bucket != APR_BRIGADE_SENTINEL(bb);
+ bucket = next) {
+ next = APR_BUCKET_NEXT(bucket);
+#if APR_HAS_SENDFILE
+ if (APR_BUCKET_IS_FILE(bucket)) {
+ apr_bucket_file *file_bucket = (apr_bucket_file *)(bucket->data);
+ apr_file_t *fd = file_bucket->fd;
+ /* Use sendfile to send this file unless:
+ * - the platform doesn't support sendfile,
+ * - the file is too small for sendfile to be useful, or
+ * - sendfile is disabled in the httpd config via "EnableSendfile off"
*/
- else if (APR_BUCKET_IS_FILE(e)
- && (e->length >= AP_MIN_SENDFILE_BYTES)) {
- apr_bucket_file *a = e->data;
-
- /* We can't handle more than one file bucket at a time
- * so we split here and send the file we have already
- * found.
- */
- if (fd) {
- more = apr_brigade_split(b, e);
- break;
- }
- fd = a->fd;
- flen = e->length;
- foffset = e->start;
- }
- else {
- const char *str;
- apr_size_t n;
-
- rv = apr_bucket_read(e, &str, &n, eblock);
- if (APR_STATUS_IS_EAGAIN(rv)) {
- /* send what we have so far since we shouldn't expect more
- * output for a while... next time we read, block
- */
- more = apr_brigade_split(b, e);
- eblock = APR_BLOCK_READ;
- break;
- }
- eblock = APR_NONBLOCK_READ;
- if (n) {
- if (!fd) {
- if (nvec == MAX_IOVEC_TO_WRITE) {
- /* woah! too many. buffer them up, for use later. */
- apr_bucket *temp, *next;
- apr_bucket_brigade *temp_brig;
-
- if (nbytes >= AP_MIN_BYTES_TO_WRITE) {
- /* We have enough data in the iovec
- * to justify doing a writev
- */
- more = apr_brigade_split(b, e);
- break;
- }
-
- /* Create a temporary brigade as a means
- * of concatenating a bunch of buckets together
- */
- temp_brig = apr_brigade_create(f->c->pool,
- f->c->bucket_alloc);
- if (last_merged_bucket) {
- /* If we've concatenated together small
- * buckets already in a previous pass,
- * the initial buckets in this brigade
- * are heap buckets that may have extra
- * space left in them (because they
- * were created by apr_brigade_write()).
- * We can take advantage of this by
- * building the new temp brigade out of
- * these buckets, so that the content
- * in them doesn't have to be copied again.
- */
- APR_BRIGADE_PREPEND(b, temp_brig);
- brigade_move(temp_brig, b, APR_BUCKET_NEXT(last_merged_bucket));
- }
-
- temp = APR_BRIGADE_FIRST(b);
- while (temp != e) {
- apr_bucket *d;
- rv = apr_bucket_read(temp, &str, &n, APR_BLOCK_READ);
- apr_brigade_write(temp_brig, NULL, NULL, str, n);
- d = temp;
- temp = APR_BUCKET_NEXT(temp);
- apr_bucket_delete(d);
- }
-
- nvec = 0;
- nbytes = 0;
- temp = APR_BRIGADE_FIRST(temp_brig);
- APR_BUCKET_REMOVE(temp);
- APR_BRIGADE_INSERT_HEAD(b, temp);
- apr_bucket_read(temp, &str, &n, APR_BLOCK_READ);
- vec[nvec].iov_base = (char*) str;
- vec[nvec].iov_len = n;
- nvec++;
-
- /* Just in case the temporary brigade has
- * multiple buckets, recover the rest of
- * them and put them in the brigade that
- * we're sending.
- */
- for (next = APR_BRIGADE_FIRST(temp_brig);
- next != APR_BRIGADE_SENTINEL(temp_brig);
- next = APR_BRIGADE_FIRST(temp_brig)) {
- APR_BUCKET_REMOVE(next);
- APR_BUCKET_INSERT_AFTER(temp, next);
- temp = next;
- apr_bucket_read(next, &str, &n,
- APR_BLOCK_READ);
- vec[nvec].iov_base = (char*) str;
- vec[nvec].iov_len = n;
- nvec++;
- }
-
- apr_brigade_destroy(temp_brig);
-
- last_merged_bucket = temp;
- e = temp;
- last_e = e;
- }
- else {
- vec[nvec].iov_base = (char*) str;
- vec[nvec].iov_len = n;
- nvec++;
- }
+ if ((apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) &&
+ (bucket->length >= AP_MIN_SENDFILE_BYTES)) {
+ if (nvec > 0) {
+ (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 1);
+ rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
+ nvec = 0;
+ if (rv != APR_SUCCESS) {
+ (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
+ return rv;
}
- else {
- /* The bucket is a trailer to a file bucket */
-
- if (nvec_trailers == MAX_IOVEC_TO_WRITE) {
- /* woah! too many. stop now. */
- more = apr_brigade_split(b, e);
- break;
- }
-
- vec_trailers[nvec_trailers].iov_base = (char*) str;
- vec_trailers[nvec_trailers].iov_len = n;
- nvec_trailers++;
- }
-
- nbytes += n;
}
+ rv = sendfile_nonblocking(s, bucket, bytes_written, c);
+ if (nvec > 0) {
+ (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
+ }
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ break;
}
}
-
-
- /* Completed iterating over the brigade, now determine if we want
- * to buffer the brigade or send the brigade out on the network.
- *
- * Save if we haven't accumulated enough bytes to send, the connection
- * is not about to be closed, and:
- *
- * 1) we didn't see a file, we don't have more passes over the
- * brigade to perform, AND we didn't stop at a FLUSH bucket.
- * (IOW, we will save plain old bytes such as HTTP headers)
- * or
- * 2) we hit the EOS and have a keep-alive connection
- * (IOW, this response is a bit more complex, but we save it
- * with the hope of concatenating with another response)
- */
- if (nbytes + flen < AP_MIN_BYTES_TO_WRITE
- && !AP_BUCKET_IS_EOC(last_e)
- && ((!fd && !more && !APR_BUCKET_IS_FLUSH(last_e))
- || (APR_BUCKET_IS_EOS(last_e)
- && c->keepalive == AP_CONN_KEEPALIVE))) {
-
- /* NEVER save an EOS in here. If we are saving a brigade with
- * an EOS bucket, then we are doing keepalive connections, and
- * we want to process to second request fully.
- */
- if (APR_BUCKET_IS_EOS(last_e)) {
- apr_bucket *bucket;
- int file_bucket_saved = 0;
- apr_bucket_delete(last_e);
- for (bucket = APR_BRIGADE_FIRST(b);
- bucket != APR_BRIGADE_SENTINEL(b);
- bucket = APR_BUCKET_NEXT(bucket)) {
-
- /* Do a read on each bucket to pull in the
- * data from pipe and socket buckets, so
- * that we don't leave their file descriptors
- * open indefinitely. Do the same for file
- * buckets, with one exception: allow the
- * first file bucket in the brigade to remain
- * a file bucket, so that we don't end up
- * doing an mmap+memcpy every time a client
- * requests a <8KB file over a keepalive
- * connection.
- */
- if (APR_BUCKET_IS_FILE(bucket) && !file_bucket_saved) {
- file_bucket_saved = 1;
- }
- else {
- const char *buf;
- apr_size_t len = 0;
- rv = apr_bucket_read(bucket, &buf, &len,
- APR_BLOCK_READ);
- if (rv != APR_SUCCESS) {
- ap_log_cerror(APLOG_MARK, APLOG_ERR, rv,
- c, "core_output_filter:"
- " Error reading from bucket.");
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- }
- }
+#endif /* APR_HAS_SENDFILE */
+ /* didn't sendfile */
+ if (!APR_BUCKET_IS_METADATA(bucket)) {
+ const char *data;
+ apr_size_t length;
+ rv = apr_bucket_read(bucket, &data, &length, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ return rv;
}
- if (!ctx->deferred_write_pool) {
- apr_pool_create(&ctx->deferred_write_pool, c->pool);
- apr_pool_tag(ctx->deferred_write_pool, "deferred_write");
+ /* reading may have split the bucket, so recompute next: */
+ next = APR_BUCKET_NEXT(bucket);
+ vec[nvec].iov_base = (char *)data;
+ vec[nvec].iov_len = length;
+ nvec++;
+ if (nvec == MAX_IOVEC_TO_WRITE) {
+ rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
+ nvec = 0;
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ break;
}
- ap_save_brigade(f, &ctx->b, &b, ctx->deferred_write_pool);
-
- return APR_SUCCESS;
}
+ }
- if (fd) {
- apr_hdtr_t hdtr;
- apr_size_t bytes_sent;
+ if (nvec > 0) {
+ rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
-#if APR_HAS_SENDFILE
- apr_int32_t flags = 0;
-#endif
+ remove_empty_buckets(bb);
- memset(&hdtr, '\0', sizeof(hdtr));
- if (nvec) {
- hdtr.numheaders = nvec;
- hdtr.headers = vec;
- }
+ return APR_SUCCESS;
+}
- if (nvec_trailers) {
- hdtr.numtrailers = nvec_trailers;
- hdtr.trailers = vec_trailers;
- }
+static void remove_empty_buckets(apr_bucket_brigade *bb)
+{
+ apr_bucket *bucket;
+ while (((bucket = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) &&
+ (APR_BUCKET_IS_METADATA(bucket) || (bucket->length == 0))) {
+ APR_BUCKET_REMOVE(bucket);
+ apr_bucket_destroy(bucket);
+ }
+}
-#if APR_HAS_SENDFILE
- if (apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) {
+static apr_status_t send_brigade_blocking(apr_socket_t *s,
+ apr_bucket_brigade *bb,
+ apr_size_t *bytes_written,
+ conn_rec *c)
+{
+ apr_status_t rv;
- if (c->keepalive == AP_CONN_CLOSE && APR_BUCKET_IS_EOS(last_e)) {
- /* Prepare the socket to be reused */
- flags |= APR_SENDFILE_DISCONNECT_SOCKET;
+ rv = APR_SUCCESS;
+ while (!APR_BRIGADE_EMPTY(bb)) {
+ rv = send_brigade_nonblocking(s, bb, bytes_written, c);
+ if (rv != APR_SUCCESS) {
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ /* Wait until we can send more data */
+ apr_int32_t nsds;
+ apr_interval_time_t timeout;
+ apr_pollfd_t pollset;
+
+ pollset.p = c->pool;
+ pollset.desc_type = APR_POLL_SOCKET;
+ pollset.reqevents = APR_POLLOUT;
+ pollset.desc.s = s;
+ apr_socket_timeout_get(s, &timeout);
+ rv = apr_poll(&pollset, 1, &nsds, timeout);
+ if (rv != APR_SUCCESS) {
+ break;
}
-
- rv = sendfile_it_all(net, /* the network information */
- fd, /* the file to send */
- &hdtr, /* header and trailer iovecs */
- foffset, /* offset in the file to begin
- sending from */
- flen, /* length of file */
- nbytes + flen, /* total length including
- headers */
- &bytes_sent, /* how many bytes were
- sent */
- flags); /* apr_sendfile flags */
}
- else
-#endif
- {
- rv = emulate_sendfile(net, fd, &hdtr, foffset, flen,
- &bytes_sent);
+ else {
+ break;
}
-
- if (logio_add_bytes_out && bytes_sent > 0)
- logio_add_bytes_out(c, bytes_sent);
-
- fd = NULL;
- }
- else {
- apr_size_t bytes_sent;
-
- rv = writev_it_all(net->client_socket,
- vec, nvec,
- nbytes, &bytes_sent);
-
- if (logio_add_bytes_out && bytes_sent > 0)
- logio_add_bytes_out(c, bytes_sent);
}
+ }
+ return rv;
+}
- apr_brigade_cleanup(b);
+static apr_status_t writev_nonblocking(apr_socket_t *s,
+ struct iovec *vec, apr_size_t nvec,
+ apr_bucket_brigade *bb,
+ apr_size_t *cumulative_bytes_written,
+ conn_rec *c)
+{
+ apr_status_t rv = APR_SUCCESS, arv;
+ apr_size_t bytes_written = 0, bytes_to_write = 0;
+ apr_size_t i, offset;
+ apr_interval_time_t old_timeout;
+
+ arv = apr_socket_timeout_get(s, &old_timeout);
+ if (arv != APR_SUCCESS) {
+ return arv;
+ }
+ arv = apr_socket_timeout_set(s, 0);
+ if (arv != APR_SUCCESS) {
+ return arv;
+ }
- /* drive cleanups for resources which were set aside
- * this may occur before or after termination of the request which
- * created the resource
- */
- if (ctx->deferred_write_pool) {
- if (more && more->p == ctx->deferred_write_pool) {
- /* "more" belongs to the deferred_write_pool,
- * which is about to be cleared.
- */
- if (APR_BRIGADE_EMPTY(more)) {
- more = NULL;
+ for (i = 0; i < nvec; i++) {
+ bytes_to_write += vec[i].iov_len;
+ }
+ offset = 0;
+ while (bytes_written < bytes_to_write) {
+ apr_size_t n = 0;
+ rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
+ if (n > 0) {
+ bytes_written += n;
+ for (i = offset; i < nvec; ) {
+ apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
+ if (APR_BUCKET_IS_METADATA(bucket)) {
+ APR_BUCKET_REMOVE(bucket);
+ apr_bucket_destroy(bucket);
+ }
+ else if (n >= vec[i].iov_len) {
+ APR_BUCKET_REMOVE(bucket);
+ apr_bucket_destroy(bucket);
+ offset++;
+ n -= vec[i++].iov_len;
}
else {
- /* uh oh... change more's lifetime
- * to the input brigade's lifetime
- */
- apr_bucket_brigade *tmp_more = more;
- more = NULL;
- ap_save_brigade(f, &more, &tmp_more, input_pool);
+ apr_bucket_split(bucket, n);
+ APR_BUCKET_REMOVE(bucket);
+ apr_bucket_destroy(bucket);
+ vec[i].iov_len -= n;
+ vec[i].iov_base = (char *) vec[i].iov_base + n;
+ break;
}
}
- apr_pool_clear(ctx->deferred_write_pool);
}
-
if (rv != APR_SUCCESS) {
- ap_log_cerror(APLOG_MARK, APLOG_INFO, rv, c,
- "core_output_filter: writing data to the network");
-
- if (more)
- apr_brigade_cleanup(more);
+ break;
+ }
+ }
+ if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {
+ ap__logio_add_bytes_out(c, bytes_written);
+ }
+ *cumulative_bytes_written += bytes_written;
- /* No need to check for SUCCESS, we did that above. */
- if (!APR_STATUS_IS_EAGAIN(rv)) {
- c->aborted = 1;
- return APR_ECONNABORTED;
- }
+ arv = apr_socket_timeout_set(s, old_timeout);
+ if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
+ return arv;
+ }
+ else {
+ return rv;
+ }
+}
- return APR_SUCCESS;
- }
+#if APR_HAS_SENDFILE
- b = more;
- more = NULL;
- } /* end while () */
+static apr_status_t sendfile_nonblocking(apr_socket_t *s,
+ apr_bucket *bucket,
+ apr_size_t *cumulative_bytes_written,
+ conn_rec *c)
+{
+ apr_status_t rv = APR_SUCCESS;
+ apr_bucket_file *file_bucket;
+ apr_file_t *fd;
+ apr_size_t file_length;
+ apr_off_t file_offset;
+ apr_size_t bytes_written = 0;
- return APR_SUCCESS;
+ if (!APR_BUCKET_IS_FILE(bucket)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server, APLOGNO(00006)
+ "core_filter: sendfile_nonblocking: "
+ "this should never happen");
+ return APR_EGENERAL;
+ }
+ file_bucket = (apr_bucket_file *)(bucket->data);
+ fd = file_bucket->fd;
+ file_length = bucket->length;
+ file_offset = bucket->start;
+
+ if (bytes_written < file_length) {
+ apr_size_t n = file_length - bytes_written;
+ apr_status_t arv;
+ apr_interval_time_t old_timeout;
+
+ arv = apr_socket_timeout_get(s, &old_timeout);
+ if (arv != APR_SUCCESS) {
+ return arv;
+ }
+ arv = apr_socket_timeout_set(s, 0);
+ if (arv != APR_SUCCESS) {
+ return arv;
+ }
+ rv = apr_socket_sendfile(s, fd, NULL, &file_offset, &n, 0);
+ if (rv == APR_SUCCESS) {
+ bytes_written += n;
+ file_offset += n;
+ }
+ arv = apr_socket_timeout_set(s, old_timeout);
+ if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
+ rv = arv;
+ }
+ }
+ if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {
+ ap__logio_add_bytes_out(c, bytes_written);
+ }
+ *cumulative_bytes_written += bytes_written;
+ if ((bytes_written < file_length) && (bytes_written > 0)) {
+ apr_bucket_split(bucket, bytes_written);
+ APR_BUCKET_REMOVE(bucket);
+ apr_bucket_destroy(bucket);
+ }
+ else if (bytes_written == file_length) {
+ APR_BUCKET_REMOVE(bucket);
+ apr_bucket_destroy(bucket);
+ }
+ return rv;
}
+
+#endif
diff --git a/server/eor_bucket.c b/server/eor_bucket.c
new file mode 100644
index 00000000..4d3e1ecf
--- /dev/null
+++ b/server/eor_bucket.c
@@ -0,0 +1,102 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "httpd.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "scoreboard.h"
+
+static apr_status_t eor_bucket_cleanup(void *data)
+{
+ apr_bucket *b = (apr_bucket *)data;
+ request_rec *r = (request_rec *)b->data;
+
+ if (r != NULL) {
+ /*
+ * If eor_bucket_destroy is called after us, this prevents
+ * eor_bucket_destroy from trying to destroy the pool again.
+ */
+ b->data = NULL;
+ /* Update child status and log the transaction */
+ ap_update_child_status(r->connection->sbh, SERVER_BUSY_LOG, r);
+ ap_run_log_transaction(r);
+ if (ap_extended_status) {
+ ap_increment_counts(r->connection->sbh, r);
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t eor_bucket_read(apr_bucket *b, const char **str,
+ apr_size_t *len, apr_read_type_e block)
+{
+ *str = NULL;
+ *len = 0;
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(apr_bucket *) ap_bucket_eor_make(apr_bucket *b, request_rec *r)
+{
+ b->length = 0;
+ b->start = 0;
+ b->data = r;
+ b->type = &ap_bucket_type_eor;
+
+ return b;
+}
+
+AP_DECLARE(apr_bucket *) ap_bucket_eor_create(apr_bucket_alloc_t *list,
+ request_rec *r)
+{
+ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+ APR_BUCKET_INIT(b);
+ b->free = apr_bucket_free;
+ b->list = list;
+ if (r) {
+ /*
+ * Register a cleanup for the request pool as the eor bucket could
+ * have been allocated from a different pool then the request pool
+ * e.g. the parent pool of the request pool. In this case
+ * eor_bucket_destroy might be called at a point of time when the
+ * request pool had been already destroyed.
+ * We need to use a pre-cleanup here because a module may create a
+ * sub-pool which is still needed during the log_transaction hook.
+ */
+ apr_pool_pre_cleanup_register(r->pool, (void *)b, eor_bucket_cleanup);
+ }
+ return ap_bucket_eor_make(b, r);
+}
+
+static void eor_bucket_destroy(void *data)
+{
+ request_rec *r = (request_rec *)data;
+
+ if (r) {
+ /* eor_bucket_cleanup will be called when the pool gets destroyed */
+ apr_pool_destroy(r->pool);
+ }
+}
+
+AP_DECLARE_DATA const apr_bucket_type_t ap_bucket_type_eor = {
+ "EOR", 5, APR_BUCKET_METADATA,
+ eor_bucket_destroy,
+ eor_bucket_read,
+ apr_bucket_setaside_noop,
+ apr_bucket_split_notimpl,
+ apr_bucket_simple_copy
+};
+
diff --git a/server/gen_test_char.c b/server/gen_test_char.c
index a0b55100..1c40bde9 100644
--- a/server/gen_test_char.c
+++ b/server/gen_test_char.c
@@ -51,6 +51,7 @@
#define T_HTTP_TOKEN_STOP (0x08)
#define T_ESCAPE_LOGITEM (0x10)
#define T_ESCAPE_FORENSIC (0x20)
+#define T_ESCAPE_URLENCODED (0x40)
int main(int argc, char *argv[])
{
@@ -65,6 +66,7 @@ int main(int argc, char *argv[])
"#define T_HTTP_TOKEN_STOP (%u)\n"
"#define T_ESCAPE_LOGITEM (%u)\n"
"#define T_ESCAPE_FORENSIC (%u)\n"
+ "#define T_ESCAPE_URLENCODED (%u)\n"
"\n"
"static const unsigned char test_char_table[256] = {",
T_ESCAPE_SHELL_CMD,
@@ -72,7 +74,8 @@ int main(int argc, char *argv[])
T_OS_ESCAPE_PATH,
T_HTTP_TOKEN_STOP,
T_ESCAPE_LOGITEM,
- T_ESCAPE_FORENSIC);
+ T_ESCAPE_FORENSIC,
+ T_ESCAPE_URLENCODED);
for (c = 0; c < 256; ++c) {
flags = 0;
@@ -108,6 +111,10 @@ int main(int argc, char *argv[])
flags |= T_OS_ESCAPE_PATH;
}
+ if (!apr_isalnum(c) && !strchr(".-*_ ", c)) {
+ flags |= T_ESCAPE_URLENCODED;
+ }
+
/* these are the "tspecials" (RFC2068) or "separators" (RFC2616) */
if (c && (apr_iscntrl(c) || strchr(" \t()<>@,;:\\\"/[]?={}", c))) {
flags |= T_HTTP_TOKEN_STOP;
diff --git a/server/gen_test_char.dep b/server/gen_test_char.dep
deleted file mode 100644
index c749241e..00000000
--- a/server/gen_test_char.dep
+++ /dev/null
@@ -1,5 +0,0 @@
-# Microsoft Developer Studio Generated Dependency File, included by gen_test_char.mak
-
-.\gen_test_char.c : \
- "..\srclib\apr\include\apr_lib.h"\
-
diff --git a/server/gen_test_char.mak b/server/gen_test_char.mak
deleted file mode 100644
index d6e7bfb1..00000000
--- a/server/gen_test_char.mak
+++ /dev/null
@@ -1,234 +0,0 @@
-# Microsoft Developer Studio Generated NMAKE File, Based on gen_test_char.dsp
-!IF "$(CFG)" == ""
-CFG=gen_test_char - Win32 Debug
-!MESSAGE No configuration specified. Defaulting to gen_test_char - Win32 Debug.
-!ENDIF
-
-!IF "$(CFG)" != "gen_test_char - Win32 Release" && "$(CFG)" != "gen_test_char - Win32 Debug"
-!MESSAGE Invalid configuration "$(CFG)" specified.
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "gen_test_char.mak" CFG="gen_test_char - Win32 Debug"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "gen_test_char - Win32 Release" (based on "Win32 (x86) Console Application")
-!MESSAGE "gen_test_char - Win32 Debug" (based on "Win32 (x86) Console Application")
-!MESSAGE
-!ERROR An invalid configuration is specified.
-!ENDIF
-
-!IF "$(OS)" == "Windows_NT"
-NULL=
-!ELSE
-NULL=nul
-!ENDIF
-
-!IF "$(CFG)" == "gen_test_char - Win32 Release"
-
-OUTDIR=.
-INTDIR=.\Release
-# Begin Custom Macros
-OutDir=.
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\gen_test_char.exe"
-
-!ELSE
-
-ALL : "libapr - Win32 Release" "$(OUTDIR)\gen_test_char.exe"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 ReleaseCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\gen_test_char.idb"
- -@erase "$(INTDIR)\gen_test_char.obj"
- -@erase "$(OUTDIR)\gen_test_char.exe"
-
-"$(INTDIR)" :
- if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MD /W3 /O2 /I "..\include" /I "..\srclib\apr\include" /I "..\srclib\apr-util\include" /I "..\os\win32" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\gen_test_char" /FD /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-RSC=rc.exe
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\gen_test_char.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\Release\gen_test_char.pdb" /out:"$(OUTDIR)\gen_test_char.exe" /opt:ref
-LINK32_OBJS= \
- "$(INTDIR)\gen_test_char.obj" \
- "..\srclib\apr\Release\libapr-1.lib"
-
-"$(OUTDIR)\gen_test_char.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-!ELSEIF "$(CFG)" == "gen_test_char - Win32 Debug"
-
-OUTDIR=.
-INTDIR=.\Debug
-# Begin Custom Macros
-OutDir=.
-# End Custom Macros
-
-!IF "$(RECURSE)" == "0"
-
-ALL : "$(OUTDIR)\gen_test_char.exe"
-
-!ELSE
-
-ALL : "libapr - Win32 Debug" "$(OUTDIR)\gen_test_char.exe"
-
-!ENDIF
-
-!IF "$(RECURSE)" == "1"
-CLEAN :"libapr - Win32 DebugCLEAN"
-!ELSE
-CLEAN :
-!ENDIF
- -@erase "$(INTDIR)\gen_test_char.idb"
- -@erase "$(INTDIR)\gen_test_char.obj"
- -@erase "$(OUTDIR)\Debug\gen_test_char.pdb"
- -@erase "$(OUTDIR)\gen_test_char.exe"
-
-"$(INTDIR)" :
- if not exist "$(INTDIR)/$(NULL)" mkdir "$(INTDIR)"
-
-CPP=cl.exe
-CPP_PROJ=/nologo /MDd /W3 /Zi /Od /I "..\include" /I "..\srclib\apr\include" /I "..\srclib\apr-util\include" /I "..\os\win32" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\gen_test_char" /FD /EHsc /c
-
-.c{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.obj::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.c{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cpp{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-.cxx{$(INTDIR)}.sbr::
- $(CPP) @<<
- $(CPP_PROJ) $<
-<<
-
-RSC=rc.exe
-BSC32=bscmake.exe
-BSC32_FLAGS=/nologo /o"$(OUTDIR)\gen_test_char.bsc"
-BSC32_SBRS= \
-
-LINK32=link.exe
-LINK32_FLAGS=kernel32.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\Debug\gen_test_char.pdb" /debug /out:"$(OUTDIR)\gen_test_char.exe"
-LINK32_OBJS= \
- "$(INTDIR)\gen_test_char.obj" \
- "..\srclib\apr\Debug\libapr-1.lib"
-
-"$(OUTDIR)\gen_test_char.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
- $(LINK32) @<<
- $(LINK32_FLAGS) $(LINK32_OBJS)
-<<
-
-!ENDIF
-
-
-!IF "$(NO_EXTERNAL_DEPS)" != "1"
-!IF EXISTS("gen_test_char.dep")
-!INCLUDE "gen_test_char.dep"
-!ELSE
-!MESSAGE Warning: cannot find "gen_test_char.dep"
-!ENDIF
-!ENDIF
-
-
-!IF "$(CFG)" == "gen_test_char - Win32 Release" || "$(CFG)" == "gen_test_char - Win32 Debug"
-
-!IF "$(CFG)" == "gen_test_char - Win32 Release"
-
-"libapr - Win32 Release" :
- cd ".\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release"
- cd "..\..\server"
-
-"libapr - Win32 ReleaseCLEAN" :
- cd ".\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Release" RECURSE=1 CLEAN
- cd "..\..\server"
-
-!ELSEIF "$(CFG)" == "gen_test_char - Win32 Debug"
-
-"libapr - Win32 Debug" :
- cd ".\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug"
- cd "..\..\server"
-
-"libapr - Win32 DebugCLEAN" :
- cd ".\..\srclib\apr"
- $(MAKE) /$(MAKEFLAGS) /F ".\libapr.mak" CFG="libapr - Win32 Debug" RECURSE=1 CLEAN
- cd "..\..\server"
-
-!ENDIF
-
-SOURCE=.\gen_test_char.c
-
-"$(INTDIR)\gen_test_char.obj" : $(SOURCE) "$(INTDIR)"
-
-
-
-!ENDIF
-
diff --git a/server/listen.c b/server/listen.c
index b284d83b..a4935aaa 100644
--- a/server/listen.c
+++ b/server/listen.c
@@ -20,16 +20,18 @@
#define APR_WANT_STRFUNC
#include "apr_want.h"
-#define CORE_PRIVATE
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "ap_listen.h"
#include "http_log.h"
-#include "mpm.h"
#include "mpm_common.h"
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
AP_DECLARE_DATA ap_listen_rec *ap_listeners = NULL;
static ap_listen_rec *old_listeners;
@@ -54,7 +56,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
#ifndef WIN32
stat = apr_socket_opt_set(s, APR_SO_REUSEADDR, one);
if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
- ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
+ ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00067)
"make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)",
server->bind_addr);
apr_socket_close(s);
@@ -64,7 +66,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
stat = apr_socket_opt_set(s, APR_SO_KEEPALIVE, one);
if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
- ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
+ ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00068)
"make_sock: for address %pI, apr_socket_opt_set: (SO_KEEPALIVE)",
server->bind_addr);
apr_socket_close(s);
@@ -75,7 +77,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
if (server->bind_addr->family == APR_INET6) {
stat = apr_socket_opt_set(s, APR_IPV6_V6ONLY, v6only_setting);
if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
- ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
+ ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00069)
"make_sock: for address %pI, apr_socket_opt_set: "
"(IPV6_V6ONLY)",
server->bind_addr);
@@ -107,7 +109,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
if (send_buffer_size) {
stat = apr_socket_opt_set(s, APR_SO_SNDBUF, send_buffer_size);
if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
- ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p,
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p, APLOGNO(00070)
"make_sock: failed to set SendBufferSize for "
"address %pI, using default",
server->bind_addr);
@@ -117,7 +119,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
if (receive_buffer_size) {
stat = apr_socket_opt_set(s, APR_SO_RCVBUF, receive_buffer_size);
if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
- ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p,
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, stat, p, APLOGNO(00071)
"make_sock: failed to set ReceiveBufferSize for "
"address %pI, using default",
server->bind_addr);
@@ -130,7 +132,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
#endif
if ((stat = apr_socket_bind(s, server->bind_addr)) != APR_SUCCESS) {
- ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p,
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, stat, p, APLOGNO(00072)
"make_sock: could not bind to address %pI",
server->bind_addr);
apr_socket_close(s);
@@ -138,7 +140,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
}
if ((stat = apr_socket_listen(s, ap_listenbacklog)) != APR_SUCCESS) {
- ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, stat, p,
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, stat, p, APLOGNO(00073)
"make_sock: unable to listen for connections "
"on address %pI",
server->bind_addr);
@@ -159,7 +161,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
*/
stat = apr_socket_opt_set(s, APR_SO_REUSEADDR, one);
if (stat != APR_SUCCESS && stat != APR_ENOTIMPL) {
- ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p,
+ ap_log_perror(APLOG_MARK, APLOG_CRIT, stat, p, APLOGNO(00074)
"make_sock: for address %pI, apr_socket_opt_set: (SO_REUSEADDR)",
server->bind_addr);
apr_socket_close(s);
@@ -170,11 +172,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
server->sd = s;
server->active = 1;
-#ifdef MPM_ACCEPT_FUNC
- server->accept_func = MPM_ACCEPT_FUNC;
-#else
server->accept_func = NULL;
-#endif
return APR_SUCCESS;
}
@@ -182,8 +180,7 @@ static apr_status_t make_sock(apr_pool_t *p, ap_listen_rec *server)
static const char* find_accf_name(server_rec *s, const char *proto)
{
const char* accf;
- core_server_config *conf = ap_get_module_config(s->module_config,
- &core_module);
+ core_server_config *conf = ap_get_core_module_config(s->module_config);
if (!proto) {
return NULL;
}
@@ -219,19 +216,17 @@ static void ap_apply_accept_filter(apr_pool_t *p, ap_listen_rec *lis,
rv = apr_socket_accept_filter(s, apr_pstrdup(p, accf),
apr_pstrdup(p,""));
if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
- ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p, APLOGNO(00075)
"Failed to enable the '%s' Accept Filter",
accf);
}
#else
-#ifdef APR_TCP_DEFER_ACCEPT
- rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 1);
+ rv = apr_socket_opt_set(s, APR_TCP_DEFER_ACCEPT, 30);
if (rv != APR_SUCCESS && !APR_STATUS_IS_ENOTIMPL(rv)) {
- ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p,
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, rv, p, APLOGNO(00076)
"Failed to enable APR_TCP_DEFER_ACCEPT");
}
#endif
-#endif
}
}
@@ -242,7 +237,8 @@ static apr_status_t close_listeners_on_exec(void *v)
}
static const char *alloc_listener(process_rec *process, char *addr,
- apr_port_t port, const char* proto)
+ apr_port_t port, const char* proto,
+ void *dummy)
{
ap_listen_rec **walk, *last;
apr_status_t status;
@@ -277,13 +273,16 @@ static const char *alloc_listener(process_rec *process, char *addr,
}
if (found_listener) {
+ if (ap_listeners->slave != dummy) {
+ return "Cannot define a slave on the same IP:port as a Listener";
+ }
return NULL;
}
if ((status = apr_sockaddr_info_get(&sa, addr, APR_UNSPEC, port, 0,
process->pool))
!= APR_SUCCESS) {
- ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
+ ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool, APLOGNO(00077)
"alloc_listener: failed to set up sockaddr for %s",
addr);
return "Listen setup failed";
@@ -321,7 +320,7 @@ static const char *alloc_listener(process_rec *process, char *addr,
}
#endif
if (status != APR_SUCCESS) {
- ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool,
+ ap_log_perror(APLOG_MARK, APLOG_CRIT, status, process->pool, APLOGNO(00078)
"alloc_listener: failed to get a socket for %s",
addr);
return "Listen setup failed";
@@ -334,6 +333,7 @@ static const char *alloc_listener(process_rec *process, char *addr,
last->next = new;
last = new;
}
+ new->slave = dummy;
}
return NULL;
@@ -416,7 +416,7 @@ static int open_listeners(apr_pool_t *pool)
* listen (which would generate an error). IPv4 will be handled
* on the established IPv6 socket.
*/
- if (IS_INADDR_ANY(lr->bind_addr)) {
+ if (IS_INADDR_ANY(lr->bind_addr) && previous) {
for (cur = ap_listeners; cur != lr; cur = cur->next) {
if (lr->bind_addr->port == cur->bind_addr->port
&& IS_IN6ADDR_ANY(cur->bind_addr)
@@ -440,7 +440,6 @@ static int open_listeners(apr_pool_t *pool)
#endif
if (make_sock(pool, lr) == APR_SUCCESS) {
++num_open;
- lr->active = 1;
}
else {
#if APR_HAVE_IPV6
@@ -499,7 +498,7 @@ static int open_listeners(apr_pool_t *pool)
status = apr_socket_opt_set(lr->sd, APR_SO_NONBLOCK, use_nonblock);
if (status != APR_SUCCESS) {
- ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool,
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, pool, APLOGNO(00079)
"unable to control socket non-blocking status");
return -1;
}
@@ -589,6 +588,22 @@ AP_DECLARE_NONSTD(void) ap_close_listeners(void)
lr->active = 0;
}
}
+AP_DECLARE_NONSTD(int) ap_close_selected_listeners(ap_slave_t *slave)
+{
+ ap_listen_rec *lr;
+ int n = 0;
+
+ for (lr = ap_listeners; lr; lr = lr->next) {
+ if (lr->slave != slave) {
+ apr_socket_close(lr->sd);
+ lr->active = 0;
+ }
+ else {
+ ++n;
+ }
+ }
+ return n;
+}
AP_DECLARE(void) ap_listen_pre_config(void)
{
@@ -597,7 +612,10 @@ AP_DECLARE(void) ap_listen_pre_config(void)
ap_listenbacklog = DEFAULT_LISTENBACKLOG;
}
-
+/* Hack: populate an extra field
+ * When this gets called from a Listen directive, dummy is null.
+ * So we can use non-null dummy to pass a data pointer without conflict
+ */
AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
int argc, char *const argv[])
{
@@ -644,7 +662,7 @@ AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
ap_str_tolower(proto);
}
- return alloc_listener(cmd->server->process, host, port, proto);
+ return alloc_listener(cmd->server->process, host, port, proto, dummy);
}
AP_DECLARE_NONSTD(const char *) ap_set_listenbacklog(cmd_parms *cmd,
diff --git a/server/log.c b/server/log.c
index 42a77290..de560efc 100644
--- a/server/log.c
+++ b/server/log.c
@@ -28,6 +28,8 @@
#include "apr_thread_proc.h"
#include "apr_lib.h"
#include "apr_signal.h"
+#include "apr_portable.h"
+#include "apr_base64.h"
#define APR_WANT_STDIO
#define APR_WANT_STRFUNC
@@ -39,8 +41,9 @@
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
-
-#define CORE_PRIVATE
+#if APR_HAVE_PROCESS_H
+#include <process.h> /* for getpid() on Win32 */
+#endif
#include "ap_config.h"
#include "httpd.h"
@@ -51,13 +54,23 @@
#include "util_time.h"
#include "ap_mpm.h"
+#if HAVE_GETTID
+#include <sys/syscall.h>
+#include <sys/types.h>
+#endif
+
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
typedef struct {
- char *t_name;
- int t_val;
+ const char *t_name;
+ int t_val;
} TRANS;
APR_HOOK_STRUCT(
APR_HOOK_LINK(error_log)
+ APR_HOOK_LINK(generate_log_id)
)
int AP_DECLARE_DATA ap_default_loglevel = DEFAULT_LOGLEVEL;
@@ -136,6 +149,14 @@ static const TRANS priorities[] = {
{"notice", APLOG_NOTICE},
{"info", APLOG_INFO},
{"debug", APLOG_DEBUG},
+ {"trace1", APLOG_TRACE1},
+ {"trace2", APLOG_TRACE2},
+ {"trace3", APLOG_TRACE3},
+ {"trace4", APLOG_TRACE4},
+ {"trace5", APLOG_TRACE5},
+ {"trace6", APLOG_TRACE6},
+ {"trace7", APLOG_TRACE7},
+ {"trace8", APLOG_TRACE8},
{NULL, -1},
};
@@ -151,14 +172,35 @@ typedef struct read_handle_t {
static read_handle_t *read_handles;
-/* clear_handle_list() is called when plog is cleared; at that
- * point we need to forget about our old list of pipe read
- * handles. We let the plog cleanups close the actual pipes.
+/**
+ * @brief The piped logging structure.
+ *
+ * Piped logs are used to move functionality out of the main server.
+ * For example, log rotation is done with piped logs.
*/
-static apr_status_t clear_handle_list(void *v)
+struct piped_log {
+ /** The pool to use for the piped log */
+ apr_pool_t *p;
+ /** The pipe between the server and the logging process */
+ apr_file_t *read_fd, *write_fd;
+#ifdef AP_HAVE_RELIABLE_PIPED_LOGS
+ /** The name of the program the logging process is running */
+ char *program;
+ /** The pid of the logging process */
+ apr_proc_t *pid;
+ /** How to reinvoke program when it must be replaced */
+ apr_cmdtype_e cmdtype;
+#endif
+};
+
+AP_DECLARE(apr_file_t *) ap_piped_log_read_fd(piped_log *pl)
{
- read_handles = NULL;
- return APR_SUCCESS;
+ return pl->read_fd;
+}
+
+AP_DECLARE(apr_file_t *) ap_piped_log_write_fd(piped_log *pl)
+{
+ return pl->write_fd;
}
/* remember to close this handle in the child process
@@ -167,7 +209,7 @@ static apr_status_t clear_handle_list(void *v)
* take the parent process's child procs.
* If the win32 parent instead passed each and every
* logger write handle from itself down to the child,
- * and the parent manages all aspects of keeping the
+ * and the parent manages all aspects of keeping the
* reliable pipe log children alive, this would still
* make no sense :) Cripple it on Win32.
*/
@@ -206,14 +248,14 @@ AP_DECLARE(apr_status_t) ap_replace_stderr_log(apr_pool_t *p,
char *filename = ap_server_root_relative(p, fname);
if (!filename) {
ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT,
- APR_EBADPATH, NULL, "Invalid -E error log file %s",
+ APR_EBADPATH, NULL, APLOGNO(00085) "Invalid -E error log file %s",
fname);
return APR_EBADPATH;
}
if ((rc = apr_file_open(&stderr_file, filename,
APR_APPEND | APR_WRITE | APR_CREATE | APR_LARGEFILE,
APR_OS_DEFAULT, p)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL, APLOGNO(00086)
"%s: could not open error log file %s.",
ap_server_argv0, fname);
return rc;
@@ -224,18 +266,18 @@ AP_DECLARE(apr_status_t) ap_replace_stderr_log(apr_pool_t *p,
*/
stderr_pool = p;
}
- if ((rc = apr_file_open_stderr(&stderr_log, stderr_pool))
+ if ((rc = apr_file_open_stderr(&stderr_log, stderr_pool))
== APR_SUCCESS) {
apr_file_flush(stderr_log);
- if ((rc = apr_file_dup2(stderr_log, stderr_file, stderr_pool))
+ if ((rc = apr_file_dup2(stderr_log, stderr_file, stderr_pool))
== APR_SUCCESS) {
apr_file_close(stderr_file);
/*
* You might ponder why stderr_pool should survive?
* The trouble is, stderr_pool may have s_main->error_log,
* so we aren't in a position to destory stderr_pool until
- * the next recycle. There's also an apparent bug which
- * is not; if some folk decided to call this function before
+ * the next recycle. There's also an apparent bug which
+ * is not; if some folk decided to call this function before
* the core open error logs hook, this pool won't survive.
* Neither does the stderr logger, so this isn't a problem.
*/
@@ -246,7 +288,7 @@ AP_DECLARE(apr_status_t) ap_replace_stderr_log(apr_pool_t *p,
stderr_pool = NULL;
if (rc != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rc, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rc, NULL, APLOGNO(00087)
"unable to replace stderr with error log file");
}
return rc;
@@ -255,7 +297,7 @@ AP_DECLARE(apr_status_t) ap_replace_stderr_log(apr_pool_t *p,
static void log_child_errfn(apr_pool_t *pool, apr_status_t err,
const char *description)
{
- ap_log_error(APLOG_MARK, APLOG_ERR, err, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, err, NULL, APLOGNO(00088)
"%s", description);
}
@@ -275,16 +317,18 @@ static int log_child(apr_pool_t *p, const char *progname,
apr_status_t rc;
apr_procattr_t *procattr;
apr_proc_t *procnew;
- apr_file_t *outfile, *errfile;
+ apr_file_t *errfile;
if (((rc = apr_procattr_create(&procattr, p)) == APR_SUCCESS)
+ && ((rc = apr_procattr_dir_set(procattr,
+ ap_server_root)) == APR_SUCCESS)
&& ((rc = apr_procattr_cmdtype_set(procattr, cmdtype)) == APR_SUCCESS)
&& ((rc = apr_procattr_io_set(procattr,
APR_FULL_BLOCK,
APR_NO_PIPE,
APR_NO_PIPE)) == APR_SUCCESS)
&& ((rc = apr_procattr_error_check_set(procattr, 1)) == APR_SUCCESS)
- && ((rc = apr_procattr_child_errfn_set(procattr, log_child_errfn))
+ && ((rc = apr_procattr_child_errfn_set(procattr, log_child_errfn))
== APR_SUCCESS)) {
char **args;
const char *pname;
@@ -293,11 +337,8 @@ static int log_child(apr_pool_t *p, const char *progname,
pname = apr_pstrdup(p, args[0]);
procnew = (apr_proc_t *)apr_pcalloc(p, sizeof(*procnew));
- if ((rc = apr_file_open_stdout(&outfile, p)) == APR_SUCCESS) {
- rc = apr_procattr_child_out_set(procattr, outfile, NULL);
- if (dummy_stderr)
- rc = apr_procattr_child_err_set(procattr, outfile, NULL);
- else if ((rc = apr_file_open_stderr(&errfile, p)) == APR_SUCCESS)
+ if (dummy_stderr) {
+ if ((rc = apr_file_open_stdout(&errfile, p)) == APR_SUCCESS)
rc = apr_procattr_child_err_set(procattr, errfile, NULL);
}
@@ -325,29 +366,30 @@ static int open_error_log(server_rec *s, int is_main, apr_pool_t *p)
if (*s->error_fname == '|') {
apr_file_t *dummy = NULL;
- apr_cmdtype_e cmdtype = APR_SHELLCMD_ENV;
+ apr_cmdtype_e cmdtype = APR_PROGRAM_ENV;
fname = s->error_fname + 1;
/* In 2.4 favor PROGRAM_ENV, accept "||prog" syntax for compatibility
* and "|$cmd" to override the default.
- * Any 2.2 backport would continue to favor SHELLCMD_ENV so there
+ * Any 2.2 backport would continue to favor SHELLCMD_ENV so there
* accept "||prog" to override, and "|$cmd" to ease conversion.
*/
- if (*fname == '|') {
- cmdtype = APR_PROGRAM_ENV;
+ if (*fname == '|')
++fname;
- }
- if (*fname == '$')
+ if (*fname == '$') {
+ cmdtype = APR_SHELLCMD_ENV;
++fname;
-
+ }
+
/* Spawn a new child logger. If this is the main server_rec,
* the new child must use a dummy stderr since the current
* stderr might be a pipe to the old logger. Otherwise, the
* child inherits the parents stderr. */
rc = log_child(p, fname, &dummy, cmdtype, is_main);
if (rc != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL,
- "Couldn't start ErrorLog process");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL, APLOGNO(00089)
+ "Couldn't start ErrorLog process '%s'.",
+ s->error_fname + 1);
return DONE;
}
@@ -379,7 +421,7 @@ static int open_error_log(server_rec *s, int is_main, apr_pool_t *p)
else {
fname = ap_server_root_relative(p, s->error_fname);
if (!fname) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EBADPATH, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EBADPATH, NULL, APLOGNO(00090)
"%s: Invalid error log path %s.",
ap_server_argv0, s->error_fname);
return DONE;
@@ -387,7 +429,7 @@ static int open_error_log(server_rec *s, int is_main, apr_pool_t *p)
if ((rc = apr_file_open(&s->error_log, fname,
APR_APPEND | APR_WRITE | APR_CREATE | APR_LARGEFILE,
APR_OS_DEFAULT, p)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL, APLOGNO(00091)
"%s: could not open error log file %s.",
ap_server_argv0, fname);
return DONE;
@@ -408,24 +450,24 @@ int ap_open_logs(apr_pool_t *pconf, apr_pool_t *p /* plog */,
/* Register to throw away the read_handles list when we
* cleanup plog. Upon fork() for the apache children,
* this read_handles list is closed so only the parent
- * can relaunch a lost log child. These read handles
+ * can relaunch a lost log child. These read handles
* are always closed on exec.
- * We won't care what happens to our stderr log child
- * between log phases, so we don't mind losing stderr's
+ * We won't care what happens to our stderr log child
+ * between log phases, so we don't mind losing stderr's
* read_handle a little bit early.
*/
- apr_pool_cleanup_register(p, NULL, clear_handle_list,
+ apr_pool_cleanup_register(p, &read_handles, ap_pool_cleanup_set_null,
apr_pool_cleanup_null);
/* HERE we need a stdout log that outlives plog.
- * We *presume* the parent of plog is a process
+ * We *presume* the parent of plog is a process
* or global pool which spans server restarts.
* Create our stderr_pool as a child of the plog's
* parent pool.
*/
apr_pool_create(&stderr_p, apr_pool_parent_get(p));
apr_pool_tag(stderr_p, "stderr_pool");
-
+
if (open_error_log(s_main, 1, stderr_p) != OK) {
return DONE;
}
@@ -433,12 +475,12 @@ int ap_open_logs(apr_pool_t *pconf, apr_pool_t *p /* plog */,
replace_stderr = 1;
if (s_main->error_log) {
apr_status_t rv;
-
+
/* Replace existing stderr with new log. */
apr_file_flush(s_main->error_log);
rv = apr_file_dup2(stderr_log, s_main->error_log, stderr_p);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s_main,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s_main, APLOGNO(00092)
"unable to replace stderr with error_log");
}
else {
@@ -465,9 +507,10 @@ int ap_open_logs(apr_pool_t *pconf, apr_pool_t *p /* plog */,
* because it points to the old error log, or back to the tty
* of the submitter.
* XXX: This is BS - /dev/null is non-portable
+ * errno-as-apr_status_t is also non-portable
*/
if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, errno, s_main,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, errno, s_main, APLOGNO(00093)
"unable to replace stderr with /dev/null");
}
@@ -505,91 +548,144 @@ AP_DECLARE(void) ap_error_log2stderr(server_rec *s) {
}
}
-static void log_error_core(const char *file, int line, int level,
- apr_status_t status, const server_rec *s,
- const conn_rec *c,
- const request_rec *r, apr_pool_t *pool,
- const char *fmt, va_list args)
+static int cpystrn(char *buf, const char *arg, int buflen)
{
- char errstr[MAX_STRING_LEN];
-#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
- char scratch[MAX_STRING_LEN];
-#endif
- apr_size_t len, errstrlen;
- apr_file_t *logf = NULL;
- const char *referer;
- int level_and_mask = level & APLOG_LEVELMASK;
+ char *end;
+ if (!arg)
+ return 0;
+ end = apr_cpystrn(buf, arg, buflen);
+ return end - buf;
+}
- if (r && r->connection) {
- c = r->connection;
- }
- if (s == NULL) {
- /*
- * If we are doing stderr logging (startup), don't log messages that are
- * above the default server log level unless it is a startup/shutdown
- * notice
- */
- if ((level_and_mask != APLOG_NOTICE)
- && (level_and_mask > ap_default_loglevel)) {
- return;
- }
+static int log_remote_address(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->r && !(arg && *arg == 'c'))
+ return apr_snprintf(buf, buflen, "%s:%d", info->r->useragent_ip,
+ info->r->useragent_addr->port);
+ else if (info->c)
+ return apr_snprintf(buf, buflen, "%s:%d", info->c->client_ip,
+ info->c->client_addr->port);
+ else
+ return 0;
+}
- logf = stderr_log;
- }
- else if (s->error_log) {
- /*
- * If we are doing normal logging, don't log messages that are
- * above the server log level unless it is a startup/shutdown notice
- */
- if ((level_and_mask != APLOG_NOTICE)
- && (level_and_mask > s->loglevel)) {
- return;
- }
+static int log_local_address(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->c)
+ return apr_snprintf(buf, buflen, "%s:%d", info->c->local_ip,
+ info->c->local_addr->port);
+ else
+ return 0;
+}
- logf = s->error_log;
- }
-#ifdef TPF
- else if (tpf_child) {
- /*
- * If we are doing normal logging, don't log messages that are
- * above the server log level unless it is a startup/shutdown notice
- */
- if ((level_and_mask != APLOG_NOTICE)
- && (level_and_mask > s->loglevel)) {
- return;
- }
+static int log_pid(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ pid_t pid = getpid();
+ return apr_snprintf(buf, buflen, "%" APR_PID_T_FMT, pid);
+}
- logf = stderr;
+static int log_tid(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+#if APR_HAS_THREADS
+ int result;
+#endif
+#if HAVE_GETTID
+ if (arg && *arg == 'g') {
+ pid_t tid = syscall(SYS_gettid);
+ if (tid == -1)
+ return 0;
+ return apr_snprintf(buf, buflen, "%"APR_PID_T_FMT, tid);
}
-#endif /* TPF */
- else {
- /*
- * If we are doing syslog logging, don't log messages that are
- * above the server log level (including a startup/shutdown notice)
- */
- if (level_and_mask > s->loglevel) {
- return;
- }
+#endif
+#if APR_HAS_THREADS
+ if (ap_mpm_query(AP_MPMQ_IS_THREADED, &result) == APR_SUCCESS
+ && result != AP_MPMQ_NOT_SUPPORTED)
+ {
+ apr_os_thread_t tid = apr_os_thread_current();
+ return apr_snprintf(buf, buflen, "%pT", &tid);
}
+#endif
+ return 0;
+}
- if (logf && ((level & APLOG_STARTUP) != APLOG_STARTUP)) {
- errstr[0] = '[';
- ap_recent_ctime(errstr + 1, apr_time_now());
- errstr[1 + APR_CTIME_LEN - 1] = ']';
- errstr[1 + APR_CTIME_LEN ] = ' ';
- len = 1 + APR_CTIME_LEN + 1;
- } else {
- len = 0;
+static int log_ctime(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ int time_len = buflen;
+ int option = AP_CTIME_OPTION_NONE;
+
+ while(arg && *arg) {
+ switch (*arg) {
+ case 'u': option |= AP_CTIME_OPTION_USEC;
+ break;
+ case 'c': option |= AP_CTIME_OPTION_COMPACT;
+ break;
+ }
+ arg++;
}
- if ((level & APLOG_STARTUP) != APLOG_STARTUP) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "[%s] ", priorities[level_and_mask].t_name);
+ ap_recent_ctime_ex(buf, apr_time_now(), option, &time_len);
+
+ /* ap_recent_ctime_ex includes the trailing \0 in time_len */
+ return time_len - 1;
+}
+
+static int log_loglevel(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->level < 0)
+ return 0;
+ else
+ return cpystrn(buf, priorities[info->level].t_name, buflen);
+}
+
+static int log_log_id(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ /*
+ * C: log conn log_id if available,
+ * c: log conn log id if available and not a once-per-request log line
+ * else: log request log id if available
+ */
+ if (arg && !strcasecmp(arg, "c")) {
+ if (info->c && (*arg != 'C' || !info->r)) {
+ return cpystrn(buf, info->c->log_id, buflen);
+ }
+ }
+ else if (info->rmain) {
+ return cpystrn(buf, info->rmain->log_id, buflen);
}
+ return 0;
+}
+
+static int log_keepalives(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (!info->c)
+ return 0;
+
+ return apr_snprintf(buf, buflen, "%d", info->c->keepalives);
+}
+
+static int log_module_name(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ return cpystrn(buf, ap_find_module_short_name(info->module_index), buflen);
+}
-#ifndef TPF
- if (file && level_and_mask == APLOG_DEBUG) {
+static int log_file_line(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->file == NULL) {
+ return 0;
+ }
+ else {
+ const char *file = info->file;
#if defined(_OSD_POSIX) || defined(WIN32) || defined(__MVS__)
char tmp[256];
char *e = strrchr(file, '/');
@@ -621,71 +717,362 @@ static void log_error_core(const char *file, int line, int level,
file = p + 1;
}
#endif /*_OSD_POSIX || WIN32 */
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "%s(%d): ", file, line);
+ return apr_snprintf(buf, buflen, "%s(%d)", file, info->line);
}
-#endif /* TPF */
+}
- if (c) {
- /* XXX: TODO: add a method of selecting whether logged client
- * addresses are in dotted quad or resolved form... dotted
- * quad is the most secure, which is why I'm implementing it
- * first. -djg
- */
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "[client %s] ", c->remote_ip);
+static int log_apr_status(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ apr_status_t status = info->status;
+ int len;
+ if (!status)
+ return 0;
+
+ if (status < APR_OS_START_EAIERR) {
+ len = apr_snprintf(buf, buflen, "(%d)", status);
}
- if (status != 0) {
- if (status < APR_OS_START_EAIERR) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "(%d)", status);
- }
- else if (status < APR_OS_START_SYSERR) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "(EAI %d)", status - APR_OS_START_EAIERR);
- }
- else if (status < 100000 + APR_OS_START_SYSERR) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "(OS %d)", status - APR_OS_START_SYSERR);
- }
- else {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- "(os 0x%08x)", status - APR_OS_START_SYSERR);
+ else if (status < APR_OS_START_SYSERR) {
+ len = apr_snprintf(buf, buflen, "(EAI %d)",
+ status - APR_OS_START_EAIERR);
+ }
+ else if (status < 100000 + APR_OS_START_SYSERR) {
+ len = apr_snprintf(buf, buflen, "(OS %d)",
+ status - APR_OS_START_SYSERR);
+ }
+ else {
+ len = apr_snprintf(buf, buflen, "(os 0x%08x)",
+ status - APR_OS_START_SYSERR);
+ }
+ apr_strerror(status, buf + len, buflen - len);
+ len += strlen(buf + len);
+ return len;
+}
+
+static int log_server_name(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->r)
+ return cpystrn(buf, ap_get_server_name((request_rec *)info->r), buflen);
+
+ return 0;
+}
+
+static int log_virtual_host(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->s)
+ return cpystrn(buf, info->s->server_hostname, buflen);
+
+ return 0;
+}
+
+
+static int log_table_entry(const apr_table_t *table, const char *name,
+ char *buf, int buflen)
+{
+#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
+ const char *value;
+ char scratch[MAX_STRING_LEN];
+
+ if ((value = apr_table_get(table, name)) != NULL) {
+ ap_escape_errorlog_item(scratch, value, MAX_STRING_LEN);
+ return cpystrn(buf, scratch, buflen);
+ }
+
+ return 0;
+#else
+ return cpystrn(buf, apr_table_get(table, name), buflen);
+#endif
+}
+
+static int log_header(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->r)
+ return log_table_entry(info->r->headers_in, arg, buf, buflen);
+
+ return 0;
+}
+
+static int log_note(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ /* XXX: maybe escaping the entry is not necessary for notes? */
+ if (info->r)
+ return log_table_entry(info->r->notes, arg, buf, buflen);
+
+ return 0;
+}
+
+static int log_env_var(const ap_errorlog_info *info, const char *arg,
+ char *buf, int buflen)
+{
+ if (info->r)
+ return log_table_entry(info->r->subprocess_env, arg, buf, buflen);
+
+ return 0;
+}
+
+static int core_generate_log_id(const conn_rec *c, const request_rec *r,
+ const char **idstring)
+{
+ apr_uint64_t id, tmp;
+ pid_t pid;
+ int len;
+ char *encoded;
+
+ if (r && r->request_time) {
+ id = (apr_uint64_t)r->request_time;
+ }
+ else {
+ id = (apr_uint64_t)apr_time_now();
+ }
+
+ pid = getpid();
+ if (sizeof(pid_t) > 2) {
+ tmp = pid;
+ tmp = tmp << 40;
+ id ^= tmp;
+ pid = pid >> 24;
+ tmp = pid;
+ tmp = tmp << 56;
+ id ^= tmp;
+ }
+ else {
+ tmp = pid;
+ tmp = tmp << 40;
+ id ^= tmp;
+ }
+#if APR_HAS_THREADS
+ {
+ apr_uintptr_t tmp2 = (apr_uintptr_t)c->current_thread;
+ tmp = tmp2;
+ tmp = tmp << 32;
+ id ^= tmp;
+ }
+#endif
+
+ len = apr_base64_encode_len(sizeof(id)); /* includes trailing \0 */
+ encoded = apr_palloc(r ? r->pool : c->pool, len);
+ apr_base64_encode(encoded, (char *)&id, sizeof(id));
+
+ /* Skip the last char, it is always '=' */
+ encoded[len - 2] = '\0';
+
+ *idstring = encoded;
+
+ return OK;
+}
+
+static void add_log_id(const conn_rec *c, const request_rec *r)
+{
+ const char **id;
+ /* need to cast const away */
+ if (r) {
+ id = &((request_rec *)r)->log_id;
+ }
+ else {
+ id = &((conn_rec *)c)->log_id;
+ }
+
+ ap_run_generate_log_id(c, r, id);
+}
+
+AP_DECLARE(void) ap_register_log_hooks(apr_pool_t *p)
+{
+ ap_hook_generate_log_id(core_generate_log_id, NULL, NULL,
+ APR_HOOK_REALLY_LAST);
+
+ ap_register_errorlog_handler(p, "a", log_remote_address, 0);
+ ap_register_errorlog_handler(p, "A", log_local_address, 0);
+ ap_register_errorlog_handler(p, "e", log_env_var, 0);
+ ap_register_errorlog_handler(p, "E", log_apr_status, 0);
+ ap_register_errorlog_handler(p, "F", log_file_line, 0);
+ ap_register_errorlog_handler(p, "i", log_header, 0);
+ ap_register_errorlog_handler(p, "k", log_keepalives, 0);
+ ap_register_errorlog_handler(p, "l", log_loglevel, 0);
+ ap_register_errorlog_handler(p, "L", log_log_id, 0);
+ ap_register_errorlog_handler(p, "m", log_module_name, 0);
+ ap_register_errorlog_handler(p, "n", log_note, 0);
+ ap_register_errorlog_handler(p, "P", log_pid, 0);
+ ap_register_errorlog_handler(p, "t", log_ctime, 0);
+ ap_register_errorlog_handler(p, "T", log_tid, 0);
+ ap_register_errorlog_handler(p, "v", log_virtual_host, 0);
+ ap_register_errorlog_handler(p, "V", log_server_name, 0);
+}
+
+/*
+ * This is used if no error log format is defined and during startup.
+ * It automatically omits the timestamp if logging to syslog.
+ */
+static int do_errorlog_default(const ap_errorlog_info *info, char *buf,
+ int buflen, int *errstr_start, int *errstr_end,
+ const char *errstr_fmt, va_list args)
+{
+ int len = 0;
+ int field_start = 0;
+ int item_len;
+#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
+ char scratch[MAX_STRING_LEN];
+#endif
+
+ if (!info->using_syslog && !info->startup) {
+ buf[len++] = '[';
+ len += log_ctime(info, "u", buf + len, buflen - len);
+ buf[len++] = ']';
+ buf[len++] = ' ';
+ }
+
+ if (!info->startup) {
+ buf[len++] = '[';
+ len += log_module_name(info, NULL, buf + len, buflen - len);
+ buf[len++] = ':';
+ len += log_loglevel(info, NULL, buf + len, buflen - len);
+ len += cpystrn(buf + len, "] [pid ", buflen - len);
+
+ len += log_pid(info, NULL, buf + len, buflen - len);
+#if APR_HAS_THREADS
+ field_start = len;
+ len += cpystrn(buf + len, ":tid ", buflen - len);
+ item_len = log_tid(info, NULL, buf + len, buflen - len);
+ if (!item_len)
+ len = field_start;
+ else
+ len += item_len;
+#endif
+ buf[len++] = ']';
+ buf[len++] = ' ';
+ }
+
+ if (info->level >= APLOG_DEBUG) {
+ item_len = log_file_line(info, NULL, buf + len, buflen - len);
+ if (item_len) {
+ len += item_len;
+ len += cpystrn(buf + len, ": ", buflen - len);
}
- apr_strerror(status, errstr + len, MAX_STRING_LEN - len);
- len += strlen(errstr + len);
- if (MAX_STRING_LEN - len > 2) {
- errstr[len++] = ':';
- errstr[len++] = ' ';
- errstr[len] = '\0';
+ }
+
+ if (info->status) {
+ item_len = log_apr_status(info, NULL, buf + len, buflen - len);
+ if (item_len) {
+ len += item_len;
+ len += cpystrn(buf + len, ": ", buflen - len);
}
}
- errstrlen = len;
+ /*
+ * useragent_ip/client_ip can be client or backend server. If we have
+ * a scoreboard handle, it is likely a client.
+ */
+ if (info->r) {
+ len += apr_snprintf(buf + len, buflen - len,
+ info->r->connection->sbh ? "[client %s:%d] " : "[remote %s:%d] ",
+ info->r->useragent_ip, info->r->useragent_addr->port);
+ }
+ else if (info->c) {
+ len += apr_snprintf(buf + len, buflen - len,
+ info->c->sbh ? "[client %s:%d] " : "[remote %s:%d] ",
+ info->c->client_ip, info->c->client_addr->port);
+ }
+
+ /* the actual error message */
+ *errstr_start = len;
#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
- if (apr_vsnprintf(scratch, MAX_STRING_LEN - len, fmt, args)) {
- len += ap_escape_errorlog_item(errstr + len, scratch,
- MAX_STRING_LEN - len);
+ if (apr_vsnprintf(scratch, MAX_STRING_LEN, errstr_fmt, args)) {
+ len += ap_escape_errorlog_item(buf + len, scratch,
+ buflen - len);
+
}
#else
- len += apr_vsnprintf(errstr + len, MAX_STRING_LEN - len, fmt, args);
+ len += apr_vsnprintf(buf + len, buflen - len, errstr_fmt, args);
#endif
+ *errstr_end = len;
+
+ field_start = len;
+ len += cpystrn(buf + len, ", referer: ", buflen - len);
+ item_len = log_header(info, "Referer", buf + len, buflen - len);
+ if (item_len)
+ len += item_len;
+ else
+ len = field_start;
- if ( r && (referer = apr_table_get(r->headers_in, "Referer"))
+ return len;
+}
+
+static int do_errorlog_format(apr_array_header_t *fmt, ap_errorlog_info *info,
+ char *buf, int buflen, int *errstr_start,
+ int *errstr_end, const char *err_fmt, va_list args)
+{
#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
- && ap_escape_errorlog_item(scratch, referer, MAX_STRING_LEN - len)
+ char scratch[MAX_STRING_LEN];
#endif
- ) {
- len += apr_snprintf(errstr + len, MAX_STRING_LEN - len,
- ", referer: %s",
+ int i;
+ int len = 0;
+ int field_start = 0;
+ int skipping = 0;
+ ap_errorlog_format_item *items = (ap_errorlog_format_item *)fmt->elts;
+
+ for (i = 0; i < fmt->nelts; ++i) {
+ ap_errorlog_format_item *item = &items[i];
+ if (item->flags & AP_ERRORLOG_FLAG_FIELD_SEP) {
+ if (skipping) {
+ skipping = 0;
+ }
+ else {
+ field_start = len;
+ }
+ }
+
+ if (item->flags & AP_ERRORLOG_FLAG_MESSAGE) {
+ /* the actual error message */
+ *errstr_start = len;
#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED
- scratch
+ if (apr_vsnprintf(scratch, MAX_STRING_LEN, err_fmt, args)) {
+ len += ap_escape_errorlog_item(buf + len, scratch,
+ buflen - len);
+
+ }
#else
- referer
+ len += apr_vsnprintf(buf + len, buflen - len, err_fmt, args);
#endif
- );
+ *errstr_end = len;
+ }
+ else if (skipping) {
+ continue;
+ }
+ else if (info->level != -1 && (int)item->min_loglevel > info->level) {
+ len = field_start;
+ skipping = 1;
+ }
+ else {
+ int item_len = (*item->func)(info, item->arg, buf + len,
+ buflen - len);
+ if (!item_len) {
+ if (item->flags & AP_ERRORLOG_FLAG_REQUIRED) {
+ /* required item is empty. skip whole line */
+ buf[0] = '\0';
+ return 0;
+ }
+ else if (item->flags & AP_ERRORLOG_FLAG_NULL_AS_HYPHEN) {
+ buf[len++] = '-';
+ }
+ else {
+ len = field_start;
+ skipping = 1;
+ }
+ }
+ else {
+ len += item_len;
+ }
+ }
}
+ return len;
+}
+static void write_logline(char *errstr, apr_size_t len, apr_file_t *logf,
+ int level)
+{
/* NULL if we are logging to syslog */
if (logf) {
/* Truncate for the terminator (as apr_snprintf does) */
@@ -698,44 +1085,238 @@ static void log_error_core(const char *file, int line, int level,
}
#ifdef HAVE_SYSLOG
else {
- syslog(level_and_mask, "%s", errstr);
+ syslog(level < LOG_PRIMASK ? level : APLOG_DEBUG, "%s", errstr);
}
#endif
+}
+
+static void log_error_core(const char *file, int line, int module_index,
+ int level,
+ apr_status_t status, const server_rec *s,
+ const conn_rec *c,
+ const request_rec *r, apr_pool_t *pool,
+ const char *fmt, va_list args)
+{
+ char errstr[MAX_STRING_LEN];
+ apr_file_t *logf = NULL;
+ int level_and_mask = level & APLOG_LEVELMASK;
+ const request_rec *rmain = NULL;
+ core_server_config *sconf = NULL;
+ ap_errorlog_info info;
+
+ /* do we need to log once-per-req or once-per-conn info? */
+ int log_conn_info = 0, log_req_info = 0;
+ apr_array_header_t **lines = NULL;
+ int done = 0;
+ int line_number = 0;
+
+ if (r && r->connection) {
+ c = r->connection;
+ }
+
+ if (s == NULL) {
+ /*
+ * If we are doing stderr logging (startup), don't log messages that are
+ * above the default server log level unless it is a startup/shutdown
+ * notice
+ */
+#ifndef DEBUG
+ if ((level_and_mask != APLOG_NOTICE)
+ && (level_and_mask > ap_default_loglevel)) {
+ return;
+ }
+#endif
+
+ logf = stderr_log;
+ }
+ else {
+ int configured_level = r ? ap_get_request_module_loglevel(r, module_index) :
+ c ? ap_get_conn_server_module_loglevel(c, s, module_index) :
+ ap_get_server_module_loglevel(s, module_index);
+ if (s->error_log) {
+ /*
+ * If we are doing normal logging, don't log messages that are
+ * above the module's log level unless it is a startup/shutdown notice
+ */
+ if ((level_and_mask != APLOG_NOTICE)
+ && (level_and_mask > configured_level)) {
+ return;
+ }
+
+ logf = s->error_log;
+ }
+ else {
+ /*
+ * If we are doing syslog logging, don't log messages that are
+ * above the module's log level (including a startup/shutdown notice)
+ */
+ if (level_and_mask > configured_level) {
+ return;
+ }
+ }
+
+ /* the faked server_rec from mod_cgid does not have s->module_config */
+ if (s->module_config) {
+ sconf = ap_get_core_module_config(s->module_config);
+ if (c && !c->log_id) {
+ add_log_id(c, NULL);
+ if (sconf->error_log_conn && sconf->error_log_conn->nelts > 0)
+ log_conn_info = 1;
+ }
+ if (r) {
+ if (r->main)
+ rmain = r->main;
+ else
+ rmain = r;
+
+ if (!rmain->log_id) {
+ /* XXX: do we need separate log ids for subrequests? */
+ if (sconf->error_log_req && sconf->error_log_req->nelts > 0)
+ log_req_info = 1;
+ /*
+ * XXX: potential optimization: only create log id if %L is
+ * XXX: actually used
+ */
+ add_log_id(c, rmain);
+ }
+ }
+ }
+ }
+
+ info.s = s;
+ info.c = c;
+ info.pool = pool;
+ info.file = NULL;
+ info.line = 0;
+ info.status = 0;
+ info.using_syslog = (logf == NULL);
+ info.startup = ((level & APLOG_STARTUP) == APLOG_STARTUP);
+ info.format = fmt;
+
+ while (!done) {
+ apr_array_header_t *log_format;
+ int len = 0, errstr_start = 0, errstr_end = 0;
+ /* XXX: potential optimization: format common prefixes only once */
+ if (log_conn_info) {
+ /* once-per-connection info */
+ if (line_number == 0) {
+ lines = (apr_array_header_t **)sconf->error_log_conn->elts;
+ info.r = NULL;
+ info.rmain = NULL;
+ info.level = -1;
+ info.module_index = APLOG_NO_MODULE;
+ }
+
+ log_format = lines[line_number++];
+
+ if (line_number == sconf->error_log_conn->nelts) {
+ /* this is the last line of once-per-connection info */
+ line_number = 0;
+ log_conn_info = 0;
+ }
+ }
+ else if (log_req_info) {
+ /* once-per-request info */
+ if (line_number == 0) {
+ lines = (apr_array_header_t **)sconf->error_log_req->elts;
+ info.r = rmain;
+ info.rmain = rmain;
+ info.level = -1;
+ info.module_index = APLOG_NO_MODULE;
+ }
+
+ log_format = lines[line_number++];
+
+ if (line_number == sconf->error_log_req->nelts) {
+ /* this is the last line of once-per-request info */
+ line_number = 0;
+ log_req_info = 0;
+ }
+ }
+ else {
+ /* the actual error message */
+ info.r = r;
+ info.rmain = rmain;
+ info.level = level_and_mask;
+ info.module_index = module_index;
+ info.file = file;
+ info.line = line;
+ info.status = status;
+ log_format = sconf ? sconf->error_log_format : NULL;
+ done = 1;
+ }
+
+ /*
+ * prepare and log one line
+ */
+
+ if (log_format) {
+ len += do_errorlog_format(log_format, &info, errstr + len,
+ MAX_STRING_LEN - len,
+ &errstr_start, &errstr_end, fmt, args);
+ }
+ else {
+ len += do_errorlog_default(&info, errstr + len, MAX_STRING_LEN - len,
+ &errstr_start, &errstr_end, fmt, args);
+ }
+
+ if (!*errstr)
+ {
+ /*
+ * Don't log empty lines. This can happen with once-per-conn/req
+ * info if an item with AP_ERRORLOG_FLAG_REQUIRED is NULL.
+ */
+ continue;
+ }
+ write_logline(errstr, len, logf, level_and_mask);
+
+ if (done) {
+ /*
+ * We don't call the error_log hook for per-request/per-conn
+ * lines, and we only pass the actual log message, not the
+ * prefix and suffix.
+ */
+ errstr[errstr_end] = '\0';
+ ap_run_error_log(&info, errstr + errstr_start);
+ }
- ap_run_error_log(file, line, level, status, s, r, pool, errstr + errstrlen);
+ *errstr = '\0';
+ }
}
-AP_DECLARE(void) ap_log_error(const char *file, int line, int level,
- apr_status_t status, const server_rec *s,
- const char *fmt, ...)
+AP_DECLARE(void) ap_log_error_(const char *file, int line, int module_index,
+ int level, apr_status_t status,
+ const server_rec *s, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- log_error_core(file, line, level, status, s, NULL, NULL, NULL, fmt, args);
+ log_error_core(file, line, module_index, level, status, s, NULL, NULL,
+ NULL, fmt, args);
va_end(args);
}
-AP_DECLARE(void) ap_log_perror(const char *file, int line, int level,
- apr_status_t status, apr_pool_t *p,
- const char *fmt, ...)
+AP_DECLARE(void) ap_log_perror_(const char *file, int line, int module_index,
+ int level, apr_status_t status, apr_pool_t *p,
+ const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- log_error_core(file, line, level, status, NULL, NULL, NULL, p, fmt, args);
+ log_error_core(file, line, module_index, level, status, NULL, NULL, NULL,
+ p, fmt, args);
va_end(args);
}
-AP_DECLARE(void) ap_log_rerror(const char *file, int line, int level,
- apr_status_t status, const request_rec *r,
- const char *fmt, ...)
+AP_DECLARE(void) ap_log_rerror_(const char *file, int line, int module_index,
+ int level, apr_status_t status,
+ const request_rec *r, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- log_error_core(file, line, level, status, r->server, NULL, r, NULL, fmt,
- args);
+ log_error_core(file, line, module_index, level, status, r->server, NULL, r,
+ NULL, fmt, args);
/*
* IF APLOG_TOCLIENT is set,
@@ -756,18 +1337,77 @@ AP_DECLARE(void) ap_log_rerror(const char *file, int line, int level,
va_end(args);
}
-AP_DECLARE(void) ap_log_cerror(const char *file, int line, int level,
- apr_status_t status, const conn_rec *c,
- const char *fmt, ...)
+AP_DECLARE(void) ap_log_cserror_(const char *file, int line, int module_index,
+ int level, apr_status_t status,
+ const conn_rec *c, const server_rec *s,
+ const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- log_error_core(file, line, level, status, c->base_server, c, NULL, NULL,
- fmt, args);
+ log_error_core(file, line, module_index, level, status, s, c,
+ NULL, NULL, fmt, args);
va_end(args);
}
+AP_DECLARE(void) ap_log_cerror_(const char *file, int line, int module_index,
+ int level, apr_status_t status,
+ const conn_rec *c, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ log_error_core(file, line, module_index, level, status, c->base_server, c,
+ NULL, NULL, fmt, args);
+ va_end(args);
+}
+
+AP_DECLARE(void) ap_log_command_line(apr_pool_t *plog, server_rec *s)
+{
+ int i;
+ process_rec *process = s->process;
+ char *result;
+ int len_needed = 0;
+
+ /* Piece together the command line from the pieces
+ * in process->argv, with spaces in between.
+ */
+ for (i = 0; i < process->argc; i++) {
+ len_needed += strlen(process->argv[i]) + 1;
+ }
+
+ result = (char *) apr_palloc(plog, len_needed);
+ *result = '\0';
+
+ for (i = 0; i < process->argc; i++) {
+ strcat(result, process->argv[i]);
+ if ((i+1)< process->argc) {
+ strcat(result, " ");
+ }
+ }
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(00094)
+ "Command line: '%s'", result);
+}
+
+AP_DECLARE(void) ap_remove_pid(apr_pool_t *p, const char *rel_fname)
+{
+ apr_status_t rv;
+ const char *fname = ap_server_root_relative(p, rel_fname);
+
+ if (fname != NULL) {
+ rv = apr_file_remove(fname, p);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00095)
+ "failed to remove PID file %s", fname);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00096)
+ "removed PID file %s (pid=%" APR_PID_T_FMT ")",
+ fname, getpid());
+ }
+ }
+}
+
AP_DECLARE(void) ap_log_pid(apr_pool_t *p, const char *filename)
{
apr_file_t *pid_file = NULL;
@@ -784,7 +1424,7 @@ AP_DECLARE(void) ap_log_pid(apr_pool_t *p, const char *filename)
fname = ap_server_root_relative(p, filename);
if (!fname) {
ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, APR_EBADPATH,
- NULL, "Invalid PID file path %s, ignoring.", filename);
+ NULL, APLOGNO(00097) "Invalid PID file path %s, ignoring.", filename);
return;
}
@@ -798,7 +1438,7 @@ AP_DECLARE(void) ap_log_pid(apr_pool_t *p, const char *filename)
* that may screw up scripts written to do something
* based on the last modification time of the pid file.
*/
- ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, p,
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, p, APLOGNO(00098)
"pid file %s overwritten -- Unclean "
"shutdown of previous Apache run?",
fname);
@@ -808,14 +1448,14 @@ AP_DECLARE(void) ap_log_pid(apr_pool_t *p, const char *filename)
APR_WRITE | APR_CREATE | APR_TRUNCATE,
APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD, p))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00099)
"could not create %s", fname);
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00100)
"%s: could not log pid to file %s",
ap_server_argv0, fname);
exit(1);
}
- apr_file_printf(pid_file, "%ld" APR_EOL_STR, (long)mypid);
+ apr_file_printf(pid_file, "%" APR_PID_T_FMT APR_EOL_STR, mypid);
apr_file_close(pid_file);
saved_pid = mypid;
}
@@ -837,7 +1477,7 @@ AP_DECLARE(apr_status_t) ap_read_pid(apr_pool_t *p, const char *filename,
fname = ap_server_root_relative(p, filename);
if (!fname) {
ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, APR_EBADPATH,
- NULL, "Invalid PID file path %s, ignoring.", filename);
+ NULL, APLOGNO(00101) "Invalid PID file path %s, ignoring.", filename);
return APR_EGENERAL;
}
@@ -872,7 +1512,7 @@ AP_DECLARE(void) ap_log_assert(const char *szExp, const char *szFile,
char time_str[APR_CTIME_LEN];
apr_ctime(time_str, apr_time_now());
- ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, APLOGNO(00102)
"[%s] file %s, line %d, assertion \"%s\" failed",
time_str, szFile, nLine, szExp);
#if defined(WIN32)
@@ -897,30 +1537,26 @@ static apr_status_t piped_log_spawn(piped_log *pl)
apr_status_t status;
if (((status = apr_procattr_create(&procattr, pl->p)) != APR_SUCCESS) ||
+ ((status = apr_procattr_dir_set(procattr, ap_server_root))
+ != APR_SUCCESS) ||
((status = apr_procattr_cmdtype_set(procattr, pl->cmdtype))
!= APR_SUCCESS) ||
((status = apr_procattr_child_in_set(procattr,
- ap_piped_log_read_fd(pl),
- ap_piped_log_write_fd(pl)))
+ pl->read_fd,
+ pl->write_fd))
!= APR_SUCCESS) ||
((status = apr_procattr_child_errfn_set(procattr, log_child_errfn))
!= APR_SUCCESS) ||
((status = apr_procattr_error_check_set(procattr, 1)) != APR_SUCCESS)) {
char buf[120];
/* Something bad happened, give up and go away. */
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00103)
"piped_log_spawn: unable to setup child process '%s': %s",
pl->program, apr_strerror(status, buf, sizeof(buf)));
}
else {
char **args;
const char *pname;
- apr_file_t *outfile, *errfile;
-
- if ((status = apr_file_open_stdout(&outfile, pl->p)) == APR_SUCCESS)
- status = apr_procattr_child_out_set(procattr, outfile, NULL);
- if ((status = apr_file_open_stderr(&errfile, pl->p)) == APR_SUCCESS)
- status = apr_procattr_child_err_set(procattr, errfile, NULL);
apr_tokenize_to_argv(pl->program, &args, pl->p);
pname = apr_pstrdup(pl->p, args[0]);
@@ -930,19 +1566,19 @@ static apr_status_t piped_log_spawn(piped_log *pl)
if (status == APR_SUCCESS) {
pl->pid = procnew;
- /* procnew->in was dup2'd from ap_piped_log_write_fd(pl);
+ /* procnew->in was dup2'd from pl->write_fd;
* since the original fd is still valid, close the copy to
* avoid a leak. */
apr_file_close(procnew->in);
procnew->in = NULL;
apr_proc_other_child_register(procnew, piped_log_maintenance, pl,
- ap_piped_log_write_fd(pl), pl->p);
- close_handle_in_child(pl->p, ap_piped_log_read_fd(pl));
+ pl->write_fd, pl->p);
+ close_handle_in_child(pl->p, pl->read_fd);
}
else {
char buf[120];
/* Something bad happened, give up and go away. */
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00104)
"unable to start piped log program '%s': %s",
pl->program, apr_strerror(status, buf, sizeof(buf)));
}
@@ -967,20 +1603,20 @@ static void piped_log_maintenance(int reason, void *data, apr_wait_t status)
apr_proc_other_child_unregister(pl);
stats = ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state);
if (stats != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00105)
"can't query MPM state; not restarting "
"piped log program '%s'",
pl->program);
}
else if (mpm_state != AP_MPMQ_STOPPING) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00106)
"piped log program '%s' failed unexpectedly",
pl->program);
if ((stats = piped_log_spawn(pl)) != APR_SUCCESS) {
/* what can we do? This could be the error log we're having
* problems opening up... */
char buf[120];
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00107)
"piped_log_maintenance: unable to respawn '%s': %s",
pl->program, apr_strerror(stats, buf, sizeof(buf)));
}
@@ -1009,8 +1645,8 @@ static apr_status_t piped_log_cleanup_for_exec(void *data)
{
piped_log *pl = data;
- apr_file_close(ap_piped_log_read_fd(pl));
- apr_file_close(ap_piped_log_write_fd(pl));
+ apr_file_close(pl->read_fd);
+ apr_file_close(pl->write_fd);
return APR_SUCCESS;
}
@@ -1037,16 +1673,17 @@ AP_DECLARE(piped_log *) ap_open_piped_log_ex(apr_pool_t *p,
pl->program = apr_pstrdup(p, program);
pl->pid = NULL;
pl->cmdtype = cmdtype;
- if (apr_file_pipe_create(&ap_piped_log_read_fd(pl),
- &ap_piped_log_write_fd(pl), p) != APR_SUCCESS) {
+ if (apr_file_pipe_create_ex(&pl->read_fd,
+ &pl->write_fd,
+ APR_FULL_BLOCK, p) != APR_SUCCESS) {
return NULL;
}
apr_pool_cleanup_register(p, pl, piped_log_cleanup,
piped_log_cleanup_for_exec);
if (piped_log_spawn(pl) != APR_SUCCESS) {
apr_pool_cleanup_kill(p, pl, piped_log_cleanup);
- apr_file_close(ap_piped_log_read_fd(pl));
- apr_file_close(ap_piped_log_write_fd(pl));
+ apr_file_close(pl->read_fd);
+ apr_file_close(pl->write_fd);
return NULL;
}
return pl;
@@ -1058,7 +1695,7 @@ static apr_status_t piped_log_cleanup(void *data)
{
piped_log *pl = data;
- apr_file_close(ap_piped_log_write_fd(pl));
+ apr_file_close(pl->write_fd);
return APR_SUCCESS;
}
@@ -1072,15 +1709,16 @@ AP_DECLARE(piped_log *) ap_open_piped_log_ex(apr_pool_t *p,
rc = log_child(p, program, &dummy, cmdtype, 0);
if (rc != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL,
- "Couldn't start piped log process");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, NULL, APLOGNO(00108)
+ "Couldn't start piped log process '%s'.",
+ (program == NULL) ? "NULL" : program);
return NULL;
}
pl = apr_palloc(p, sizeof (*pl));
pl->p = p;
- ap_piped_log_read_fd(pl) = NULL;
- ap_piped_log_write_fd(pl) = dummy;
+ pl->read_fd = NULL;
+ pl->write_fd = dummy;
apr_pool_cleanup_register(p, pl, piped_log_cleanup, piped_log_cleanup);
return pl;
@@ -1091,19 +1729,19 @@ AP_DECLARE(piped_log *) ap_open_piped_log_ex(apr_pool_t *p,
AP_DECLARE(piped_log *) ap_open_piped_log(apr_pool_t *p,
const char *program)
{
- apr_cmdtype_e cmdtype = APR_SHELLCMD_ENV;
+ apr_cmdtype_e cmdtype = APR_PROGRAM_ENV;
/* In 2.4 favor PROGRAM_ENV, accept "||prog" syntax for compatibility
* and "|$cmd" to override the default.
- * Any 2.2 backport would continue to favor SHELLCMD_ENV so there
+ * Any 2.2 backport would continue to favor SHELLCMD_ENV so there
* accept "||prog" to override, and "|$cmd" to ease conversion.
*/
- if (*program == '|') {
- cmdtype = APR_PROGRAM_ENV;
+ if (*program == '|')
++program;
- }
- if (*program == '$')
+ if (*program == '$') {
+ cmdtype = APR_SHELLCMD_ENV;
++program;
+ }
return ap_open_piped_log_ex(p, program, cmdtype);
}
@@ -1113,10 +1751,30 @@ AP_DECLARE(void) ap_close_piped_log(piped_log *pl)
apr_pool_cleanup_run(pl->p, pl, piped_log_cleanup);
}
+AP_DECLARE(const char *) ap_parse_log_level(const char *str, int *val)
+{
+ char *err = "Log level keyword must be one of emerg/alert/crit/error/warn/"
+ "notice/info/debug/trace1/.../trace8";
+ int i = 0;
+
+ if (str == NULL)
+ return err;
+
+ while (priorities[i].t_name != NULL) {
+ if (!strcasecmp(str, priorities[i].t_name)) {
+ *val = priorities[i].t_val;
+ return NULL;
+ }
+ i++;
+ }
+ return err;
+}
+
AP_IMPLEMENT_HOOK_VOID(error_log,
- (const char *file, int line, int level,
- apr_status_t status, const server_rec *s,
- const request_rec *r, apr_pool_t *pool,
- const char *errstr), (file, line, level,
- status, s, r, pool, errstr))
+ (const ap_errorlog_info *info, const char *errstr),
+ (info, errstr))
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, generate_log_id,
+ (const conn_rec *c, const request_rec *r,
+ const char **id),
+ (c, r, id), DECLINED)
diff --git a/server/main.c b/server/main.c
index 04ed6c78..204d66c7 100644
--- a/server/main.c
+++ b/server/main.c
@@ -28,18 +28,22 @@
#define APR_WANT_STRFUNC
#include "apr_want.h"
-#define CORE_PRIVATE
#include "ap_config.h"
#include "httpd.h"
#include "http_main.h"
#include "http_log.h"
#include "http_config.h"
#include "http_core.h"
+#include "mod_core.h"
+#include "http_request.h"
#include "http_vhost.h"
#include "apr_uri.h"
#include "util_ebcdic.h"
#include "ap_mpm.h"
-#include "mpm_common.h"
+
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
/* WARNING: Win32 binds http_main.c dynamically to the server. Please place
* extern functions and global data in another appropriate module.
@@ -93,10 +97,15 @@ static void show_compile_settings(void)
printf("Server built: %s\n", ap_get_server_built());
printf("Server's Module Magic Number: %u:%u\n",
MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);
- printf("Server loaded: APR %s, APR-Util %s\n",
+#if APR_MAJOR_VERSION >= 2
+ printf("Server loaded: APR %s\n", apr_version_string());
+ printf("Compiled using: APR %s\n", APR_VERSION_STRING);
+#else
+ printf("Server loaded: APR %s, APR-UTIL %s\n",
apr_version_string(), apu_version_string());
- printf("Compiled using: APR %s, APR-Util %s\n",
+ printf("Compiled using: APR %s, APR-UTIL %s\n",
APR_VERSION_STRING, APU_VERSION_STRING);
+#endif
/* sizeof(foo) is long on some platforms so we might as well
* make it long everywhere to keep the printf format
* consistent
@@ -118,10 +127,6 @@ static void show_compile_settings(void)
printf(" -D OS=\"" OS "\"\n");
#endif
-#ifdef APACHE_MPM_DIR
- printf(" -D APACHE_MPM_DIR=\"" APACHE_MPM_DIR "\"\n");
-#endif
-
#ifdef HAVE_SHMGET
printf(" -D HAVE_SHMGET\n");
#endif
@@ -212,10 +217,6 @@ static void show_compile_settings(void)
printf(" -D NEED_HASHBANG_EMUL\n");
#endif
-#ifdef SHARED_CORE
- printf(" -D SHARED_CORE\n");
-#endif
-
/* This list displays the compiled in default paths: */
#ifdef HTTPD_ROOT
printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n");
@@ -225,10 +226,6 @@ static void show_compile_settings(void)
printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n");
#endif
-#if defined(SHARED_CORE) && defined(SHARED_CORE_DIR)
- printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n");
-#endif
-
#ifdef DEFAULT_PIDLOG
printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n");
#endif
@@ -237,10 +234,6 @@ static void show_compile_settings(void)
printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n");
#endif
-#ifdef DEFAULT_LOCKFILE
- printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n");
-#endif
-
#ifdef DEFAULT_ERRORLOG
printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n");
#endif
@@ -267,11 +260,19 @@ static void destroy_and_exit_process(process_rec *process,
* might get lost.
*/
apr_sleep(TASK_SWITCH_SLEEP);
+ ap_main_state = AP_SQ_MS_EXITING;
apr_pool_destroy(process->pool); /* and destroy all descendent pools */
apr_terminate();
exit(process_exit_value);
}
+/* APR callback invoked if allocation fails. */
+static int abort_on_oom(int retcode)
+{
+ ap_abort_on_oom();
+ return retcode; /* unreachable, hopefully. */
+}
+
static process_rec *init_process(int *argc, const char * const * *argv)
{
process_rec *process;
@@ -289,16 +290,22 @@ static process_rec *init_process(int *argc, const char * const * *argv)
/* For all intents and purposes, this is impossibly unlikely,
* but APR doesn't exist yet, we can't use it for reporting
* these earliest two failures;
+ *
+ * XXX: Note the apr_ctime() and apr_time_now() calls. These
+ * work, today, against an uninitialized APR, but in the future
+ * (if they relied on global pools or mutexes, for example) then
+ * the datestamp logic will need to be replaced.
*/
char ctimebuff[APR_CTIME_LEN];
apr_ctime(ctimebuff, apr_time_now());
fprintf(stderr, "[%s] [crit] (%d) %s: %s failed "
- "to initial context, exiting\n",
+ "to initial context, exiting\n",
ctimebuff, stat, (*argv)[0], failed);
apr_terminate();
exit(1);
}
+ apr_pool_abort_set(abort_on_oom, cntx);
apr_pool_tag(cntx, "process");
ap_open_stderr_log(cntx);
@@ -323,47 +330,33 @@ static void usage(process_rec *process)
const char *bin = process->argv[0];
int pad_len = strlen(bin);
-#ifdef SHARED_CORE
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL ,
- "Usage: %s [-R directory] [-D name] [-d directory] [-f file]",
- bin);
-#else
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"Usage: %s [-D name] [-d directory] [-f file]", bin);
-#endif
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" %*s [-C \"directive\"] [-c \"directive\"]", pad_len, " ");
#ifdef WIN32
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " %*s [-w] [-k start|restart|stop|shutdown]", pad_len, " ");
+ " %*s [-w] [-k start|restart|stop|shutdown] [-n service_name]",
+ pad_len, " ");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" %*s [-k install|config|uninstall] [-n service_name]",
pad_len, " ");
-#endif
-#ifdef AP_MPM_WANT_SIGNAL_SERVER
-#ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN
+#else
+/* XXX not all MPMs support signalling the server in general or graceful-stop
+ * in particular
+ */
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" %*s [-k start|restart|graceful|graceful-stop|stop]",
pad_len, " ");
-#else
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " %*s [-k start|restart|graceful|stop]", pad_len, " ");
-#endif /* AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN */
#endif
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " %*s [-v] [-V] [-h] [-l] [-L] [-t] [-T] [-S]",
+ " %*s [-v] [-V] [-h] [-l] [-L] [-t] [-T] [-S] [-X]",
pad_len, " ");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"Options:");
-#ifdef SHARED_CORE
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " -R directory : specify an alternate location for "
- "shared object files");
-#endif
-
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" -D name : define a name for use in "
"<IfDefine name> directives");
@@ -386,7 +379,7 @@ static void usage(process_rec *process)
#ifdef WIN32
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" -n name : set service name and use its "
- "ServerConfigFile");
+ "ServerConfigFile and ServerRoot");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" -k start : tell Apache to start");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
@@ -423,10 +416,11 @@ static void usage(process_rec *process)
" -L : list available configuration "
"directives");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " -t -D DUMP_VHOSTS : show parsed settings (currently only "
- "vhost settings)");
+ " -t -D DUMP_VHOSTS : show parsed vhost settings");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " -S : a synonym for -t -D DUMP_VHOSTS");
+ " -t -D DUMP_RUN_CFG : show parsed run settings");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ " -S : a synonym for -t -D DUMP_VHOSTS -D DUMP_RUN_CFG");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" -t -D DUMP_MODULES : show all loaded modules ");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
@@ -434,7 +428,9 @@ static void usage(process_rec *process)
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" -t : run syntax check for config files");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " -T : start without DocumentRoot(s) check");
+ " -T : start without DocumentRoot(s) check");
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ " -X : debug mode (only one worker, do not detach)");
destroy_and_exit_process(process, 1);
}
@@ -442,14 +438,12 @@ static void usage(process_rec *process)
int main(int argc, const char * const argv[])
{
char c;
- int configtestonly = 0;
+ int showcompile = 0, showdirectives = 0;
const char *confname = SERVER_CONFIG_FILE;
const char *def_server_root = HTTPD_ROOT;
const char *temp_error_log = NULL;
const char *error;
process_rec *process;
- server_rec *server_conf;
- apr_pool_t *pglobal;
apr_pool_t *pconf;
apr_pool_t *plog; /* Pool of log streams, reset _after_ each read of conf */
apr_pool_t *ptemp; /* Pool for temporary config stuff, reset often */
@@ -457,23 +451,28 @@ int main(int argc, const char * const argv[])
apr_getopt_t *opt;
apr_status_t rv;
module **mod;
- const char *optarg;
+ const char *opt_arg;
APR_OPTIONAL_FN_TYPE(ap_signal_server) *signal_server;
AP_MONCONTROL(0); /* turn off profiling of startup */
process = init_process(&argc, &argv);
- pglobal = process->pool;
+ ap_pglobal = process->pool;
pconf = process->pconf;
ap_server_argv0 = process->short_name;
+ ap_init_rng(ap_pglobal);
+
+ /* Set up the OOM callback in the global pool, so all pools should
+ * by default inherit it. */
+ apr_pool_abort_set(abort_on_oom, apr_pool_parent_get(process->pool));
#if APR_CHARSET_EBCDIC
- if (ap_init_ebcdic(pglobal) != APR_SUCCESS) {
+ if (ap_init_ebcdic(ap_pglobal) != APR_SUCCESS) {
destroy_and_exit_process(process, 1);
}
#endif
- apr_pool_create(&pcommands, pglobal);
+ apr_pool_create(&pcommands, ap_pglobal);
apr_pool_tag(pcommands, "pcommands");
ap_server_pre_read_config = apr_array_make(pcommands, 1, sizeof(char *));
ap_server_post_read_config = apr_array_make(pcommands, 1, sizeof(char *));
@@ -481,8 +480,8 @@ int main(int argc, const char * const argv[])
error = ap_setup_prelinked_modules(process);
if (error) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_EMERG, 0, NULL, "%s: %s",
- ap_server_argv0, error);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_EMERG, 0, NULL, APLOGNO(00012)
+ "%s: %s", ap_server_argv0, error);
destroy_and_exit_process(process, 1);
}
@@ -493,68 +492,46 @@ int main(int argc, const char * const argv[])
*/
apr_getopt_init(&opt, pcommands, process->argc, process->argv);
- while ((rv = apr_getopt(opt, AP_SERVER_BASEARGS, &c, &optarg))
+ while ((rv = apr_getopt(opt, AP_SERVER_BASEARGS, &c, &opt_arg))
== APR_SUCCESS) {
char **new;
switch (c) {
case 'c':
new = (char **)apr_array_push(ap_server_post_read_config);
- *new = apr_pstrdup(pcommands, optarg);
+ *new = apr_pstrdup(pcommands, opt_arg);
break;
case 'C':
new = (char **)apr_array_push(ap_server_pre_read_config);
- *new = apr_pstrdup(pcommands, optarg);
+ *new = apr_pstrdup(pcommands, opt_arg);
break;
case 'd':
- def_server_root = optarg;
+ def_server_root = opt_arg;
break;
case 'D':
new = (char **)apr_array_push(ap_server_config_defines);
- *new = apr_pstrdup(pcommands, optarg);
- /* Setting -D DUMP_VHOSTS is equivalent to setting -S */
- if (strcmp(optarg, "DUMP_VHOSTS") == 0)
- configtestonly = 1;
+ *new = apr_pstrdup(pcommands, opt_arg);
+ /* Setting -D DUMP_VHOSTS should work like setting -S */
+ if (strcmp(opt_arg, "DUMP_VHOSTS") == 0)
+ ap_run_mode = AP_SQ_RM_CONFIG_DUMP;
+ /* Setting -D DUMP_RUN_CFG should work like setting -S */
+ else if (strcmp(opt_arg, "DUMP_RUN_CFG") == 0)
+ ap_run_mode = AP_SQ_RM_CONFIG_DUMP;
/* Setting -D DUMP_MODULES is equivalent to setting -M */
- if (strcmp(optarg, "DUMP_MODULES") == 0)
- configtestonly = 1;
+ else if (strcmp(opt_arg, "DUMP_MODULES") == 0)
+ ap_run_mode = AP_SQ_RM_CONFIG_DUMP;
break;
case 'e':
- if (strcasecmp(optarg, "emerg") == 0) {
- ap_default_loglevel = APLOG_EMERG;
- }
- else if (strcasecmp(optarg, "alert") == 0) {
- ap_default_loglevel = APLOG_ALERT;
- }
- else if (strcasecmp(optarg, "crit") == 0) {
- ap_default_loglevel = APLOG_CRIT;
- }
- else if (strncasecmp(optarg, "err", 3) == 0) {
- ap_default_loglevel = APLOG_ERR;
- }
- else if (strncasecmp(optarg, "warn", 4) == 0) {
- ap_default_loglevel = APLOG_WARNING;
- }
- else if (strcasecmp(optarg, "notice") == 0) {
- ap_default_loglevel = APLOG_NOTICE;
- }
- else if (strcasecmp(optarg, "info") == 0) {
- ap_default_loglevel = APLOG_INFO;
- }
- else if (strcasecmp(optarg, "debug") == 0) {
- ap_default_loglevel = APLOG_DEBUG;
- }
- else {
+ if (ap_parse_log_level(opt_arg, &ap_default_loglevel) != NULL)
usage(process);
- }
break;
case 'E':
- temp_error_log = apr_pstrdup(process->pool, optarg);
+ temp_error_log = apr_pstrdup(process->pool, opt_arg);
break;
case 'X':
@@ -563,7 +540,7 @@ int main(int argc, const char * const argv[])
break;
case 'f':
- confname = optarg;
+ confname = opt_arg;
break;
case 'v':
@@ -571,20 +548,18 @@ int main(int argc, const char * const argv[])
printf("Server built: %s\n", ap_get_server_built());
destroy_and_exit_process(process, 0);
- case 'V':
- show_compile_settings();
- destroy_and_exit_process(process, 0);
-
case 'l':
ap_show_modules();
destroy_and_exit_process(process, 0);
case 'L':
- ap_show_directives();
- destroy_and_exit_process(process, 0);
+ ap_run_mode = AP_SQ_RM_CONFIG_DUMP;
+ showdirectives = 1;
+ break;
case 't':
- configtestonly = 1;
+ if (ap_run_mode == AP_SQ_RM_UNKNOWN)
+ ap_run_mode = AP_SQ_RM_CONFIG_TEST;
break;
case 'T':
@@ -592,29 +567,46 @@ int main(int argc, const char * const argv[])
break;
case 'S':
- configtestonly = 1;
+ ap_run_mode = AP_SQ_RM_CONFIG_DUMP;
new = (char **)apr_array_push(ap_server_config_defines);
*new = "DUMP_VHOSTS";
+ new = (char **)apr_array_push(ap_server_config_defines);
+ *new = "DUMP_RUN_CFG";
break;
case 'M':
- configtestonly = 1;
+ ap_run_mode = AP_SQ_RM_CONFIG_DUMP;
new = (char **)apr_array_push(ap_server_config_defines);
*new = "DUMP_MODULES";
break;
+ case 'V':
+ if (strcmp(ap_show_mpm(), "")) { /* MPM built-in? */
+ show_compile_settings();
+ destroy_and_exit_process(process, 0);
+ }
+ else {
+ showcompile = 1;
+ ap_run_mode = AP_SQ_RM_CONFIG_DUMP;
+ }
+ break;
+
case 'h':
case '?':
usage(process);
}
}
+ if (ap_run_mode == AP_SQ_RM_UNKNOWN)
+ ap_run_mode = AP_SQ_RM_NORMAL;
+
/* bad cmdline option? then we die */
if (rv != APR_EOF || opt->ind < opt->argc) {
usage(process);
}
- apr_pool_create(&plog, pglobal);
+ ap_main_state = AP_SQ_MS_CREATE_PRE_CONFIG;
+ apr_pool_create(&plog, ap_pglobal);
apr_pool_tag(plog, "plog");
apr_pool_create(&ptemp, pconf);
apr_pool_tag(ptemp, "ptemp");
@@ -629,27 +621,52 @@ int main(int argc, const char * const argv[])
if (temp_error_log) {
ap_replace_stderr_log(process->pool, temp_error_log);
}
- server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
- if (!server_conf) {
+ ap_server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
+ if (!ap_server_conf) {
destroy_and_exit_process(process, 1);
}
+ apr_pool_cleanup_register(pconf, &ap_server_conf, ap_pool_cleanup_set_null,
+ apr_pool_cleanup_null);
+ /* sort hooks here to make sure pre_config hooks are sorted properly */
apr_hook_sort_all();
if (ap_run_pre_config(pconf, plog, ptemp) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,
- NULL, "Pre-configuration failed");
+ NULL, APLOGNO(00013) "Pre-configuration failed");
destroy_and_exit_process(process, 1);
}
- rv = ap_process_config_tree(server_conf, ap_conftree,
+ rv = ap_process_config_tree(ap_server_conf, ap_conftree,
process->pconf, ptemp);
if (rv == OK) {
- ap_fixup_virtual_hosts(pconf, server_conf);
- ap_fini_vhost_config(pconf, server_conf);
+ ap_fixup_virtual_hosts(pconf, ap_server_conf);
+ ap_fini_vhost_config(pconf, ap_server_conf);
+ /*
+ * Sort hooks again because ap_process_config_tree may have add modules
+ * and hence hooks. This happens with mod_perl and modules written in
+ * perl.
+ */
+ apr_hook_sort_all();
+
+ if (ap_run_check_config(pconf, plog, ptemp, ap_server_conf) != OK) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,
+ NULL, APLOGNO(00014) "Configuration check failed");
+ destroy_and_exit_process(process, 1);
+ }
- if (configtestonly) {
- ap_run_test_config(pconf, server_conf);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Syntax OK");
+ if (ap_run_mode != AP_SQ_RM_NORMAL) {
+ if (showcompile) { /* deferred due to dynamically loaded MPM */
+ show_compile_settings();
+ }
+ else if (showdirectives) { /* deferred in case of DSOs */
+ ap_show_directives();
+ destroy_and_exit_process(process, 0);
+ }
+ else {
+ ap_run_test_config(pconf, ap_server_conf);
+ if (ap_run_mode == AP_SQ_RM_CONFIG_TEST)
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Syntax OK");
+ }
destroy_and_exit_process(process, 0);
}
}
@@ -670,24 +687,28 @@ int main(int argc, const char * const argv[])
apr_pool_clear(plog);
- if ( ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {
+ if ( ap_run_open_logs(pconf, plog, ptemp, ap_server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
- 0, NULL, "Unable to open logs");
+ 0, NULL, APLOGNO(00015) "Unable to open logs");
destroy_and_exit_process(process, 1);
}
- if ( ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {
+ if ( ap_run_post_config(pconf, plog, ptemp, ap_server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,
- NULL, "Configuration Failed");
+ NULL, APLOGNO(00016) "Configuration Failed");
destroy_and_exit_process(process, 1);
}
apr_pool_destroy(ptemp);
for (;;) {
+ ap_main_state = AP_SQ_MS_DESTROY_CONFIG;
apr_hook_deregister_all();
apr_pool_clear(pconf);
+ ap_clear_auth_internal();
+ ap_main_state = AP_SQ_MS_CREATE_CONFIG;
+ ap_config_generation++;
for (mod = ap_prelinked_modules; *mod != NULL; mod++) {
ap_register_hooks(*mod, pconf);
}
@@ -700,34 +721,50 @@ int main(int argc, const char * const argv[])
apr_pool_create(&ptemp, pconf);
apr_pool_tag(ptemp, "ptemp");
ap_server_root = def_server_root;
- server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
- if (!server_conf) {
+ ap_server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
+ if (!ap_server_conf) {
destroy_and_exit_process(process, 1);
}
+ apr_pool_cleanup_register(pconf, &ap_server_conf,
+ ap_pool_cleanup_set_null, apr_pool_cleanup_null);
+ /* sort hooks here to make sure pre_config hooks are sorted properly */
apr_hook_sort_all();
if (ap_run_pre_config(pconf, plog, ptemp) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
- 0, NULL, "Pre-configuration failed");
+ 0, NULL, APLOGNO(00017) "Pre-configuration failed");
destroy_and_exit_process(process, 1);
}
- if (ap_process_config_tree(server_conf, ap_conftree, process->pconf,
+ if (ap_process_config_tree(ap_server_conf, ap_conftree, process->pconf,
ptemp) != OK) {
destroy_and_exit_process(process, 1);
}
- ap_fixup_virtual_hosts(pconf, server_conf);
- ap_fini_vhost_config(pconf, server_conf);
+ ap_fixup_virtual_hosts(pconf, ap_server_conf);
+ ap_fini_vhost_config(pconf, ap_server_conf);
+ /*
+ * Sort hooks again because ap_process_config_tree may have add modules
+ * and hence hooks. This happens with mod_perl and modules written in
+ * perl.
+ */
+ apr_hook_sort_all();
+
+ if (ap_run_check_config(pconf, plog, ptemp, ap_server_conf) != OK) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR, 0,
+ NULL, APLOGNO(00018) "Configuration check failed");
+ destroy_and_exit_process(process, 1);
+ }
+
apr_pool_clear(plog);
- if (ap_run_open_logs(pconf, plog, ptemp, server_conf) != OK) {
+ if (ap_run_open_logs(pconf, plog, ptemp, ap_server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
- 0, NULL, "Unable to open logs");
+ 0, NULL, APLOGNO(00019) "Unable to open logs");
destroy_and_exit_process(process, 1);
}
- if (ap_run_post_config(pconf, plog, ptemp, server_conf) != OK) {
+ if (ap_run_post_config(pconf, plog, ptemp, ap_server_conf) != OK) {
ap_log_error(APLOG_MARK, APLOG_STARTUP |APLOG_ERR,
- 0, NULL, "Configuration Failed");
+ 0, NULL, APLOGNO(00020) "Configuration Failed");
destroy_and_exit_process(process, 1);
}
@@ -736,7 +773,8 @@ int main(int argc, const char * const argv[])
ap_run_optional_fn_retrieve();
- if (ap_mpm_run(pconf, plog, server_conf))
+ ap_main_state = AP_SQ_MS_RUN_MPM;
+ if (ap_run_mpm(pconf, plog, ap_server_conf) != OK)
break;
apr_pool_lock(pconf, 0);
@@ -754,8 +792,8 @@ int main(int argc, const char * const argv[])
* has all of the APR functions specified by the apr/apr.exports and
* apr-util/aprutil.exports files.
*/
-const void *suck_in_APR(void);
-const void *suck_in_APR(void)
+const void *ap_suck_in_APR(void);
+const void *ap_suck_in_APR(void)
{
extern const void *ap_ugly_hack;
diff --git a/server/mpm/MPM.NAMING b/server/mpm/MPM.NAMING
index 83c0694d..c07884d4 100644
--- a/server/mpm/MPM.NAMING
+++ b/server/mpm/MPM.NAMING
@@ -2,14 +2,13 @@
The following MPMs currently exist:
prefork ....... Multi Process Model with Preforking (Apache 1.3)
- perchild ...... Multi Process Model with Threading.
- Constant number of processes, variable number of threads
- each child process can have a different uid/gid.
mpmt_os2 ...... Multi Process Model with Threading on OS/2
Constant number of processes, variable number of threads.
One acceptor thread per process, multiple workers threads.
winnt ......... Single Process Model with Threading on Windows NT
+ event ......... Multi Process model with threads. One acceptor thread,
+ multiple worker threads, separate poller threads for idle
+ connections and asynchoneous write completion.
worker ........ Multi Process model with threads. One acceptor thread,
multiple worker threads.
netware ....... Multi-threaded MPM for Netware
- beos .......... Single Process Model with Threading on BeOS
diff --git a/server/mpm/Makefile.in b/server/mpm/Makefile.in
index 2decbde6..a158f8b0 100644
--- a/server/mpm/Makefile.in
+++ b/server/mpm/Makefile.in
@@ -1,4 +1,4 @@
-SUBDIRS = $(MPM_SUBDIR_NAME)
+SUBDIRS = $(MPM_SUBDIRS)
include $(top_builddir)/build/rules.mk
diff --git a/server/mpm/beos/Makefile.in b/server/mpm/beos/Makefile.in
deleted file mode 100644
index 3f88b041..00000000
--- a/server/mpm/beos/Makefile.in
+++ /dev/null
@@ -1,5 +0,0 @@
-
-LTLIBRARY_NAME = libbeos.la
-LTLIBRARY_SOURCES = beos.c
-
-include $(top_srcdir)/build/ltlib.mk
diff --git a/server/mpm/beos/beos.c b/server/mpm/beos/beos.c
deleted file mode 100644
index 70515ee0..00000000
--- a/server/mpm/beos/beos.c
+++ /dev/null
@@ -1,1207 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* The BeOS MPM!
- *
- * This is a single process, with multiple worker threads.
- *
- * Under testing I found that given the inability of BeOS to handle threads
- * and forks it didn't make sense to try and have a set of "children" threads
- * that spawned the "worker" threads, so just missed out the middle mand and
- * somehow arrived here.
- *
- * For 2.1 this has been rewritten to have simpler logic, though there is still
- * some simplification that can be done. It's still a work in progress!
- *
- * TODO Items
- *
- * - on exit most worker threads segfault trying to access a kernel page.
- */
-
-#define CORE_PRIVATE
-
-#include <kernel/OS.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <signal.h>
-
-#include "apr_strings.h"
-#include "apr_portable.h"
-#include "httpd.h"
-#include "http_main.h"
-#include "http_log.h"
-#include "http_config.h" /* for read_config */
-#include "http_core.h" /* for get_remote_host */
-#include "http_connection.h"
-#include "ap_mpm.h"
-#include "beosd.h"
-#include "ap_listen.h"
-#include "scoreboard.h"
-#include "mpm_common.h"
-#include "mpm.h"
-#include "mpm_default.h"
-#include "apr_thread_mutex.h"
-#include "apr_poll.h"
-
-extern int _kset_fd_limit_(int num);
-
-/* Limit on the total --- clients will be locked out if more servers than
- * this are needed. It is intended solely to keep the server from crashing
- * when things get out of hand.
- *
- * We keep a hard maximum number of servers, for two reasons:
- * 1) in case something goes seriously wrong, we want to stop the server starting
- * threads ad infinitum and crashing the server (remember that BeOS has a 192
- * thread per team limit).
- * 2) it keeps the size of the scoreboard file small
- * enough that we can read the whole thing without worrying too much about
- * the overhead.
- */
-
-/* we only ever have 1 main process running... */
-#define HARD_SERVER_LIMIT 1
-
-/* Limit on the threads per process. Clients will be locked out if more than
- * this * HARD_SERVER_LIMIT are needed.
- *
- * We keep this for one reason it keeps the size of the scoreboard file small
- * enough that we can read the whole thing without worrying too much about
- * the overhead.
- */
-#ifdef NO_THREADS
-#define HARD_THREAD_LIMIT 1
-#endif
-#ifndef HARD_THREAD_LIMIT
-#define HARD_THREAD_LIMIT 50
-#endif
-
-/*
- * Actual definitions of config globals
- */
-
-static int ap_threads_to_start=0;
-static int ap_max_requests_per_thread = 0;
-static int min_spare_threads=0;
-static int max_spare_threads=0;
-static int ap_thread_limit=0;
-static int num_listening_sockets = 0;
-static int mpm_state = AP_MPMQ_STARTING;
-apr_thread_mutex_t *accept_mutex = NULL;
-
-static apr_pool_t *pconf; /* Pool for config stuff */
-
-static int server_pid;
-
-
-/*
- * The max child slot ever assigned, preserved across restarts. Necessary
- * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We use
- * this value to optimize routines that have to scan the entire scoreboard.
- */
-int ap_max_child_assigned = -1;
-int ap_max_threads_limit = -1;
-
-static apr_socket_t *udp_sock;
-static apr_sockaddr_t *udp_sa;
-
-server_rec *ap_server_conf;
-
-/* one_process */
-static int one_process = 0;
-
-#ifdef DEBUG_SIGSTOP
-int raise_sigstop_flags;
-#endif
-
-static void check_restart(void *data);
-
-/* When a worker thread gets to the end of it's life it dies with an
- * exit value of the code supplied to this function. The thread has
- * already had check_restart() registered to be called when dying, so
- * we don't concern ourselves with restarting at all here. We do however
- * mark the scoreboard slot as belonging to a dead server and zero out
- * it's thread_id.
- *
- * TODO - use the status we set to determine if we need to restart the
- * thread.
- */
-static void clean_child_exit(int code, int slot)
-{
- (void) ap_update_child_status_from_indexes(0, slot, SERVER_DEAD,
- (request_rec*)NULL);
- ap_scoreboard_image->servers[0][slot].tid = 0;
- exit_thread(code);
-}
-
-
-/*****************************************************************
- * Connection structures and accounting...
- */
-
-/* volatile just in case */
-static int volatile shutdown_pending;
-static int volatile restart_pending;
-static int volatile is_graceful;
-static int volatile child_fatal;
-ap_generation_t volatile ap_my_generation = 0;
-
-/*
- * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
- * functions to initiate shutdown or restart without relying on signals.
- * Previously this was initiated in sig_term() and restart() signal handlers,
- * but we want to be able to start a shutdown/restart from other sources --
- * e.g. on Win32, from the service manager. Now the service manager can
- * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that
- * these functions can also be called by the child processes, since global
- * variables are no longer used to pass on the required action to the parent.
- *
- * These should only be called from the parent process itself, since the
- * parent process will use the shutdown_pending and restart_pending variables
- * to determine whether to shutdown or restart. The child process should
- * call signal_parent() directly to tell the parent to die -- this will
- * cause neither of those variable to be set, which the parent will
- * assume means something serious is wrong (which it will be, for the
- * child to force an exit) and so do an exit anyway.
- */
-
-static void ap_start_shutdown(void)
-{
- /* If the user tries to shut us down twice in quick succession then we
- * may well get triggered while we are working through previous attempt
- * to shutdown. We won't worry about even reporting it as it seems a little
- * pointless.
- */
- if (shutdown_pending == 1)
- return;
-
- shutdown_pending = 1;
-}
-
-/* do a graceful restart if graceful == 1 */
-static 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;
-}
-
-/* sig_coredump attempts to handle all the potential signals we
- * may get that should result in a core dump. This is called from
- * the signal handler routine, so when we enter we are essentially blocked
- * on the signal. Once we exit we will allow the signal to be processed by
- * system, which may or may not produce a .core file. All this function does
- * is try and respect the users wishes about where that file should be
- * located (chdir) and then signal the parent with the signal.
- *
- * If we called abort() the parent would only see SIGABRT which doesn't provide
- * as much information.
- */
-static void sig_coredump(int sig)
-{
- chdir(ap_coredump_dir);
- signal(sig, SIG_DFL);
- kill(server_pid, sig);
-}
-
-static void sig_term(int sig)
-{
- ap_start_shutdown();
-}
-
-static void restart(int sig)
-{
- ap_start_restart(sig == AP_SIG_GRACEFUL);
-}
-
-/* Handle queries about our inner workings... */
-AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
-{
- switch(query_code){
- case AP_MPMQ_MAX_DAEMON_USED:
- *result = ap_max_child_assigned;
- return APR_SUCCESS;
- case AP_MPMQ_IS_THREADED:
- *result = AP_MPMQ_DYNAMIC;
- return APR_SUCCESS;
- case AP_MPMQ_IS_FORKED:
- *result = AP_MPMQ_NOT_SUPPORTED;
- return APR_SUCCESS;
- case AP_MPMQ_HARD_LIMIT_DAEMONS:
- *result = HARD_SERVER_LIMIT;
- return APR_SUCCESS;
- case AP_MPMQ_HARD_LIMIT_THREADS:
- *result = HARD_THREAD_LIMIT;
- return APR_SUCCESS;
- case AP_MPMQ_MAX_THREADS:
- *result = HARD_THREAD_LIMIT;
- return APR_SUCCESS;
- case AP_MPMQ_MIN_SPARE_DAEMONS:
- *result = 0;
- return APR_SUCCESS;
- case AP_MPMQ_MIN_SPARE_THREADS:
- *result = max_spare_threads;
- return APR_SUCCESS;
- case AP_MPMQ_MAX_SPARE_DAEMONS:
- *result = 0;
- return APR_SUCCESS;
- case AP_MPMQ_MAX_SPARE_THREADS:
- *result = min_spare_threads;
- return APR_SUCCESS;
- case AP_MPMQ_MAX_REQUESTS_DAEMON:
- *result = ap_max_requests_per_thread;
- return APR_SUCCESS;
- case AP_MPMQ_MAX_DAEMONS:
- *result = HARD_SERVER_LIMIT;
- return APR_SUCCESS;
- case AP_MPMQ_MPM_STATE:
- *result = mpm_state;
- return APR_SUCCESS;
- }
- return APR_ENOTIMPL;
-}
-
-/* This accepts a connection and allows us to handle the error codes better than
- * the previous code, while also making it more obvious.
- */
-static apr_status_t beos_accept(void **accepted, ap_listen_rec *lr, apr_pool_t *ptrans)
-{
- apr_socket_t *csd;
- apr_status_t status;
- int sockdes;
-
- *accepted = NULL;
- status = apr_socket_accept(&csd, lr->sd, ptrans);
- if (status == APR_SUCCESS) {
- *accepted = csd;
- apr_os_sock_get(&sockdes, csd);
- return status;
- }
-
- if (APR_STATUS_IS_EINTR(status)) {
- return status;
- }
- /* This switch statement provides us with better error details. */
- switch (status) {
-#ifdef ECONNABORTED
- case ECONNABORTED:
-#endif
-#ifdef ETIMEDOUT
- case ETIMEDOUT:
-#endif
-#ifdef EHOSTUNREACH
- case EHOSTUNREACH:
-#endif
-#ifdef ENETUNREACH
- case ENETUNREACH:
-#endif
- break;
-#ifdef ENETDOWN
- case ENETDOWN:
- /*
- * When the network layer has been shut down, there
- * is not much use in simply exiting: the parent
- * would simply re-create us (and we'd fail again).
- * Use the CHILDFATAL code to tear the server down.
- * @@@ Martin's idea for possible improvement:
- * A different approach would be to define
- * a new APEXIT_NETDOWN exit code, the reception
- * of which would make the parent shutdown all
- * children, then idle-loop until it detected that
- * the network is up again, and restart the children.
- * Ben Hyde noted that temporary ENETDOWN situations
- * occur in mobile IP.
- */
- ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf,
- "apr_socket_accept: giving up.");
- return APR_EGENERAL;
-#endif /*ENETDOWN*/
-
- default:
- ap_log_error(APLOG_MARK, APLOG_ERR, status, ap_server_conf,
- "apr_socket_accept: (client socket)");
- return APR_EGENERAL;
- }
- return status;
-}
-
-static void tell_workers_to_exit(void)
-{
- apr_size_t len;
- int i = 0;
- for (i = 0 ; i < ap_max_child_assigned; i++){
- len = 4;
- if (apr_socket_sendto(udp_sock, udp_sa, 0, "die!", &len) != APR_SUCCESS)
- break;
- }
-}
-
-static void set_signals(void)
-{
- struct sigaction sa;
-
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
-
- /* The first batch get handled by sig_coredump */
- if (!one_process) {
- sa.sa_handler = sig_coredump;
-
- if (sigaction(SIGSEGV, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGSEGV)");
- if (sigaction(SIGBUS, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGBUS)");
- if (sigaction(SIGABRT, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABRT)");
- if (sigaction(SIGILL, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGILL)");
- sa.sa_flags = 0;
- }
-
- /* These next two are handled by sig_term */
- sa.sa_handler = sig_term;
- if (sigaction(SIGTERM, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
- if (sigaction(SIGINT, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
-
- /* We ignore SIGPIPE */
- sa.sa_handler = SIG_IGN;
- if (sigaction(SIGPIPE, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGPIPE)");
-
- /* we want to ignore HUPs and AP_SIG_GRACEFUL while we're busy
- * processing one */
- sigaddset(&sa.sa_mask, SIGHUP);
- sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL);
- sa.sa_handler = restart;
- if (sigaction(SIGHUP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
- if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(" AP_SIG_GRACEFUL_STRING ")");
-}
-
-/*****************************************************************
- * Here follows a long bunch of generic server bookkeeping stuff...
- */
-
-int ap_graceful_stop_signalled(void)
-{
- return is_graceful;
-}
-
-/* This is the thread that actually does all the work. */
-static int32 worker_thread(void *dummy)
-{
- int worker_slot = (int)dummy;
- apr_allocator_t *allocator;
- apr_bucket_alloc_t *bucket_alloc;
- apr_status_t rv = APR_EINIT;
- int last_poll_idx = 0;
- sigset_t sig_mask;
- int requests_this_child = 0;
- apr_pollset_t *pollset = NULL;
- ap_listen_rec *lr = NULL;
- ap_sb_handle_t *sbh = NULL;
- int i;
- /* each worker thread is in control of its own destiny...*/
- int this_worker_should_exit = 0;
- /* We have 2 pools that we create/use throughout the lifetime of this
- * worker. The first and longest lived is the pworker pool. From
- * this we create the ptrans pool, the lifetime of which is the same
- * as each connection and is reset prior to each attempt to
- * process a connection.
- */
- apr_pool_t *ptrans = NULL;
- apr_pool_t *pworker = NULL;
-
- mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
- * child initializes
- */
-
- on_exit_thread(check_restart, (void*)worker_slot);
-
- /* block the signals for this thread only if we're not running as a
- * single process.
- */
- if (!one_process) {
- sigfillset(&sig_mask);
- sigprocmask(SIG_BLOCK, &sig_mask, NULL);
- }
-
- /* Each worker thread is fully in control of it's destinay and so
- * to allow each thread to handle the lifetime of it's own resources
- * we create and use a subcontext for every thread.
- * The subcontext is a child of the pconf pool.
- */
- apr_allocator_create(&allocator);
- apr_allocator_max_free_set(allocator, ap_max_mem_free);
- apr_pool_create_ex(&pworker, pconf, NULL, allocator);
- apr_allocator_owner_set(allocator, pworker);
-
- apr_pool_create(&ptrans, pworker);
- apr_pool_tag(ptrans, "transaction");
-
- ap_create_sb_handle(&sbh, pworker, 0, worker_slot);
- (void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL);
-
- /* We add an extra socket here as we add the udp_sock we use for signalling
- * death. This gets added after the others.
- */
- apr_pollset_create(&pollset, num_listening_sockets + 1, pworker, 0);
-
- for (lr = ap_listeners, i = num_listening_sockets; i--; lr = lr->next) {
- apr_pollfd_t pfd = {0};
-
- pfd.desc_type = APR_POLL_SOCKET;
- pfd.desc.s = lr->sd;
- pfd.reqevents = APR_POLLIN;
- pfd.client_data = lr;
-
- apr_pollset_add(pollset, &pfd);
- }
- {
- apr_pollfd_t pfd = {0};
-
- pfd.desc_type = APR_POLL_SOCKET;
- pfd.desc.s = udp_sock;
- pfd.reqevents = APR_POLLIN;
-
- apr_pollset_add(pollset, &pfd);
- }
-
- bucket_alloc = apr_bucket_alloc_create(pworker);
-
- mpm_state = AP_MPMQ_RUNNING;
-
- while (!this_worker_should_exit) {
- conn_rec *current_conn;
- void *csd;
-
- /* (Re)initialize this child to a pre-connection state. */
- apr_pool_clear(ptrans);
-
- if ((ap_max_requests_per_thread > 0
- && requests_this_child++ >= ap_max_requests_per_thread))
- clean_child_exit(0, worker_slot);
-
- (void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL);
-
- apr_thread_mutex_lock(accept_mutex);
-
- /* We always (presently) have at least 2 sockets we listen on, so
- * we don't have the ability for a fast path for a single socket
- * as some MPM's allow :(
- */
- for (;;) {
- apr_int32_t numdesc = 0;
- const apr_pollfd_t *pdesc = NULL;
-
- rv = apr_pollset_poll(pollset, -1, &numdesc, &pdesc);
- if (rv != APR_SUCCESS) {
- if (APR_STATUS_IS_EINTR(rv)) {
- if (one_process && shutdown_pending)
- return;
- continue;
- }
- ap_log_error(APLOG_MARK, APLOG_ERR, rv,
- ap_server_conf, "apr_pollset_poll: (listen)");
- clean_child_exit(1, worker_slot);
- }
- /* We can always use pdesc[0], but sockets at position N
- * could end up completely starved of attention in a very
- * busy server. Therefore, we round-robin across the
- * returned set of descriptors. While it is possible that
- * the returned set of descriptors might flip around and
- * continue to starve some sockets, we happen to know the
- * internal pollset implementation retains ordering
- * stability of the sockets. Thus, the round-robin should
- * ensure that a socket will eventually be serviced.
- */
- if (last_poll_idx >= numdesc)
- last_poll_idx = 0;
-
- /* Grab a listener record from the client_data of the poll
- * descriptor, and advance our saved index to round-robin
- * the next fetch.
- *
- * ### hmm... this descriptor might have POLLERR rather
- * ### than POLLIN
- */
-
- lr = pdesc[last_poll_idx++].client_data;
-
- /* The only socket we add without client_data is the first, the UDP socket
- * we listen on for restart signals. If we've therefore gotten a hit on that
- * listener lr will be NULL here and we know we've been told to die.
- * Before we jump to the end of the while loop with this_worker_should_exit
- * set to 1 (causing us to exit normally we hope) we release the accept_mutex
- * as we want every thread to go through this same routine :)
- * Bit of a hack, but compared to what I had before...
- */
- if (lr == NULL) {
- this_worker_should_exit = 1;
- apr_thread_mutex_unlock(accept_mutex);
- goto got_a_black_spot;
- }
- goto got_fd;
- }
-got_fd:
- /* Run beos_accept to accept the connection and set things up to
- * allow us to process it. We always release the accept_lock here,
- * even if we failt o accept as otherwise we'll starve other workers
- * which would be bad.
- */
- rv = beos_accept(&csd, lr, ptrans);
- apr_thread_mutex_unlock(accept_mutex);
-
- if (rv == APR_EGENERAL) {
- /* resource shortage or should-not-occur occured */
- clean_child_exit(1, worker_slot);
- } else if (rv != APR_SUCCESS)
- continue;
-
- current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd, worker_slot, sbh, bucket_alloc);
- if (current_conn) {
- ap_process_connection(current_conn, csd);
- ap_lingering_close(current_conn);
- }
-
- if (ap_my_generation !=
- ap_scoreboard_image->global->running_generation) { /* restart? */
- /* yeah, this could be non-graceful restart, in which case the
- * parent will kill us soon enough, but why bother checking?
- */
- this_worker_should_exit = 1;
- }
-got_a_black_spot:
- }
-
- apr_pool_destroy(ptrans);
- apr_pool_destroy(pworker);
-
- clean_child_exit(0, worker_slot);
-}
-
-static int make_worker(int slot)
-{
- thread_id tid;
-
- if (slot + 1 > ap_max_child_assigned)
- ap_max_child_assigned = slot + 1;
-
- (void) ap_update_child_status_from_indexes(0, slot, SERVER_STARTING, (request_rec*)NULL);
-
- if (one_process) {
- set_signals();
- ap_scoreboard_image->parent[0].pid = getpid();
- ap_scoreboard_image->servers[0][slot].tid = find_thread(NULL);
- return 0;
- }
-
- tid = spawn_thread(worker_thread, "apache_worker", B_NORMAL_PRIORITY,
- (void *)slot);
- if (tid < B_NO_ERROR) {
- ap_log_error(APLOG_MARK, APLOG_ERR, errno, NULL,
- "spawn_thread: Unable to start a new thread");
- /* In case system resources are maxed out, we don't want
- * Apache running away with the CPU trying to fork over and
- * over and over again.
- */
- (void) ap_update_child_status_from_indexes(0, slot, SERVER_DEAD,
- (request_rec*)NULL);
-
- sleep(10);
- return -1;
- }
- resume_thread(tid);
-
- ap_scoreboard_image->servers[0][slot].tid = tid;
- return 0;
-}
-
-/* When a worker thread exits, this function is called. If we are not in
- * a shutdown situation then we restart the worker in the slot that was
- * just vacated.
- */
-static void check_restart(void *data)
-{
- if (!restart_pending && !shutdown_pending) {
- int slot = (int)data;
- make_worker(slot);
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL,
- "spawning a new worker thread in slot %d", slot);
- }
-}
-
-/* Start number_to_start children. This is used to start both the
- * initial 'pool' of workers but also to replace existing workers who
- * have reached the end of their time. It walks through the scoreboard to find
- * an empty slot and starts the worker thread in that slot.
- */
-static void startup_threads(int number_to_start)
-{
- int i;
-
- for (i = 0; number_to_start && i < ap_thread_limit; ++i) {
- if (ap_scoreboard_image->servers[0][i].tid)
- continue;
-
- if (make_worker(i) < 0)
- break;
-
- --number_to_start;
- }
-}
-
-
-/*
- * 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 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 free_length;
- int free_slots[MAX_SPAWN_RATE];
- int last_non_dead = -1;
-
- /* initialize the free_list */
- free_length = 0;
-
- for (i = 0; i < ap_thread_limit; ++i) {
- if (ap_scoreboard_image->servers[0][i].tid == 0) {
- if (free_length < spawn_rate) {
- free_slots[free_length] = i;
- ++free_length;
- }
- }
- else {
- last_non_dead = i;
- }
-
- if (i >= ap_max_child_assigned && free_length >= spawn_rate) {
- break;
- }
- }
- ap_max_child_assigned = last_non_dead + 1;
-
- if (free_length > 0) {
- for (i = 0; i < free_length; ++i) {
- make_worker(free_slots[i]);
- }
- /* the next time around we want to spawn twice as many if this
- * wasn't good enough, but not if we've just done a graceful
- */
- if (hold_off_on_exponential_spawning) {
- --hold_off_on_exponential_spawning;
- } else if (spawn_rate < MAX_SPAWN_RATE) {
- spawn_rate *= 2;
- }
- } else {
- spawn_rate = 1;
- }
-}
-
-static void server_main_loop(int remaining_threads_to_start)
-{
- int child_slot;
- apr_exit_why_e exitwhy;
- int status;
- apr_proc_t pid;
- int i;
-
- while (!restart_pending && !shutdown_pending) {
-
- ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);
-
- if (pid.pid >= 0) {
- if (ap_process_child_status(&pid, exitwhy, status) == APEXIT_CHILDFATAL) {
- shutdown_pending = 1;
- child_fatal = 1;
- return;
- }
- /* non-fatal death... note that it's gone in the scoreboard. */
- child_slot = -1;
- for (i = 0; i < ap_max_child_assigned; ++i) {
- if (ap_scoreboard_image->servers[0][i].tid == pid.pid) {
- child_slot = i;
- break;
- }
- }
- if (child_slot >= 0) {
- ap_scoreboard_image->servers[0][child_slot].tid = 0;
- (void) ap_update_child_status_from_indexes(0, child_slot,
- SERVER_DEAD,
- (request_rec*)NULL);
-
- if (remaining_threads_to_start
- && child_slot < ap_thread_limit) {
- /* we're still doing a 1-for-1 replacement of dead
- * children with new children
- */
- make_worker(child_slot);
- --remaining_threads_to_start;
- }
-/* TODO
-#if APR_HAS_OTHER_CHILD
- }
- else if (apr_proc_other_child_refresh(&pid, status) == 0) {
-#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_WARNING, 0, ap_server_conf,
- "long lost child came home! (pid %ld)", pid.pid);
- }
-
- /* Don't perform idle maintenance when a child dies,
- * only do it when there's a timeout. Remember only a
- * finite number of children can die, and it's pretty
- * pathological for a lot to die suddenly.
- */
- continue;
- }
- else if (remaining_threads_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_threads(remaining_threads_to_start);
- remaining_threads_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();
- }
-}
-
-/* This is called to not only setup and run for the initial time, but also
- * when we've asked for a restart. This means it must be able to handle both
- * situations. It also means that when we exit here we should have tidied
- * up after ourselves fully.
- */
-int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
-{
- int remaining_threads_to_start, i,j;
- apr_status_t rv;
- ap_listen_rec *lr;
- pconf = _pconf;
- ap_server_conf = s;
-
- /* Increase the available pool of fd's. This code from
- * Joe Kloss <joek@be.com>
- */
- if( FD_SETSIZE > 128 && (i = _kset_fd_limit_( 128 )) < 0 ){
- ap_log_error(APLOG_MARK, APLOG_ERR, i, s,
- "could not set FD_SETSIZE (_kset_fd_limit_ failed)");
- }
-
- /* BeOS R5 doesn't support pipes on select() calls, so we use a
- * UDP socket as these are supported in both R5 and BONE. If we only cared
- * about BONE we'd use a pipe, but there it is.
- * As we have UDP support in APR, now use the APR functions and check all the
- * return values...
- */
- if (apr_sockaddr_info_get(&udp_sa, "127.0.0.1", APR_UNSPEC, 7772, 0, _pconf)
- != APR_SUCCESS){
- ap_log_error(APLOG_MARK, APLOG_ALERT, errno, s,
- "couldn't create control socket information, shutting down");
- return 1;
- }
- if (apr_socket_create(&udp_sock, udp_sa->family, SOCK_DGRAM, 0,
- _pconf) != APR_SUCCESS){
- ap_log_error(APLOG_MARK, APLOG_ALERT, errno, s,
- "couldn't create control socket, shutting down");
- return 1;
- }
- if (apr_socket_bind(udp_sock, udp_sa) != APR_SUCCESS){
- ap_log_error(APLOG_MARK, APLOG_ALERT, errno, s,
- "couldn't bind UDP socket!");
- return 1;
- }
-
- if ((num_listening_sockets = ap_setup_listeners(ap_server_conf)) < 1) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
- "no listening sockets available, shutting down");
- return 1;
- }
-
- ap_log_pid(pconf, ap_pid_fname);
-
- /*
- * Create our locks...
- */
-
- /* accept_mutex
- * used to lock around select so we only have one thread
- * in select at a time
- */
- rv = apr_thread_mutex_create(&accept_mutex, 0, pconf);
- if (rv != APR_SUCCESS) {
- /* tsch tsch, can't have more than one thread in the accept loop
- at a time so we need to fall on our sword... */
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
- "Couldn't create accept lock");
- return 1;
- }
-
- /*
- * Startup/shutdown...
- */
-
- if (!is_graceful) {
- /* setup the scoreboard shared memory */
- if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
- return 1;
- }
-
- for (i = 0; i < HARD_SERVER_LIMIT; i++) {
- ap_scoreboard_image->parent[i].pid = 0;
- for (j = 0;j < HARD_THREAD_LIMIT; j++)
- ap_scoreboard_image->servers[i][j].tid = 0;
- }
- }
-
- if (HARD_SERVER_LIMIT == 1)
- ap_scoreboard_image->parent[0].pid = getpid();
-
- set_signals();
-
- /* Sanity checks to avoid thrashing... */
- if (max_spare_threads < min_spare_threads )
- max_spare_threads = min_spare_threads;
-
- /* If we're doing a graceful_restart then we're going to see a lot
- * of threads exiting immediately when we get into the main loop
- * below (because we just sent them AP_SIG_GRACEFUL). This happens
- * pretty rapidly... and for each one that exits we'll start a new one
- * until we reach at least threads_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_threads_to_start = ap_threads_to_start;
- /* sanity check on the number to start... */
- if (remaining_threads_to_start > ap_thread_limit) {
- remaining_threads_to_start = ap_thread_limit;
- }
-
- /* If we're doing the single process thing or we're in a graceful_restart
- * then we don't start threads here.
- * if we're in one_process mode we don't want to start threads
- * do we??
- */
- if (!is_graceful && !one_process) {
- startup_threads(remaining_threads_to_start);
- remaining_threads_to_start = 0;
- } else {
- /* give the system some time to recover before kicking into
- * exponential mode */
- hold_off_on_exponential_spawning = 10;
- }
-
- /*
- * record that we've entered the world !
- */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
- "%s configured -- resuming normal operations",
- ap_get_server_description());
-
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
- "Server built: %s", ap_get_server_built());
-
- restart_pending = shutdown_pending = 0;
-
- mpm_state = AP_MPMQ_RUNNING;
-
- /* We sit in the server_main_loop() until we somehow manage to exit. When
- * we do, we need to kill the workers we have, so we start by using the
- * tell_workers_to_exit() function, but as it sometimes takes a short while
- * to accomplish this we have a pause builtin to allow them the chance to
- * gracefully exit.
- */
- if (!one_process) {
- server_main_loop(remaining_threads_to_start);
- tell_workers_to_exit();
- snooze(1000000);
- } else {
- worker_thread((void*)0);
- }
- mpm_state = AP_MPMQ_STOPPING;
-
- /* close the UDP socket we've been using... */
- apr_socket_close(udp_sock);
-
- if ((one_process || shutdown_pending) && !child_fatal) {
- const char *pidfile = NULL;
- pidfile = ap_server_root_relative (pconf, ap_pid_fname);
- if ( pidfile != NULL && unlink(pidfile) == 0)
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
- "removed PID file %s (pid=%ld)", pidfile,
- (long)getpid());
- }
-
- if (one_process) {
- return 1;
- }
-
- /*
- * If we get here we're shutting down...
- */
- if (shutdown_pending) {
- /* Time to gracefully shut down:
- * Kill child processes, tell them to call child_exit, etc...
- */
- if (beosd_killpg(getpgrp(), SIGTERM) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
- "killpg SIGTERM");
-
- /* use ap_reclaim_child_processes starting with SIGTERM */
- ap_reclaim_child_processes(1);
-
- if (!child_fatal) { /* already recorded */
- /* record the shutdown in the log */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
- "caught SIGTERM, shutting down");
- }
-
- return 1;
- }
-
- /* we've been told to restart */
- signal(SIGHUP, SIG_IGN);
-
- if (is_graceful) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
- AP_SIG_GRACEFUL_STRING " received. Doing graceful restart");
- } else {
- /* Kill 'em all. Since the child acts the same on the parents SIGTERM
- * and a SIGHUP, we may as well use the same signal, because some user
- * pthreads are stealing signals from us left and right.
- */
-
- ap_reclaim_child_processes(1); /* Start with SIGTERM */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
- "SIGHUP received. Attempting to restart");
- }
-
- /* just before we go, tidy up the lock we created to prevent a
- * potential leak of semaphores...
- */
- apr_thread_mutex_destroy(accept_mutex);
-
- return 0;
-}
-
-static int beos_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
-{
- static int restart_num = 0;
- int no_detach, debug, foreground;
- apr_status_t rv;
-
- mpm_state = AP_MPMQ_STARTING;
-
- debug = ap_exists_config_define("DEBUG");
-
- if (debug) {
- foreground = one_process = 1;
- no_detach = 0;
- }
- else
- {
- one_process = ap_exists_config_define("ONE_PROCESS");
- no_detach = ap_exists_config_define("NO_DETACH");
- foreground = ap_exists_config_define("FOREGROUND");
- }
-
- /* sigh, want this only the second time around */
- if (restart_num++ == 1) {
- is_graceful = 0;
-
- if (!one_process && !foreground) {
- rv = apr_proc_detach(no_detach ? APR_PROC_DETACH_FOREGROUND
- : APR_PROC_DETACH_DAEMONIZE);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
- "apr_proc_detach failed");
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- }
-
- server_pid = getpid();
- }
-
- beosd_pre_config();
- ap_listen_pre_config();
- ap_threads_to_start = DEFAULT_START_THREADS;
- min_spare_threads = DEFAULT_MIN_FREE_THREADS;
- max_spare_threads = DEFAULT_MAX_FREE_THREADS;
- ap_thread_limit = HARD_THREAD_LIMIT;
- ap_pid_fname = DEFAULT_PIDLOG;
- ap_max_requests_per_thread = DEFAULT_MAX_REQUESTS_PER_THREAD;
-#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
- ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-#endif
-
- apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
-
- return OK;
-}
-
-static void beos_hooks(apr_pool_t *p)
-{
- one_process = 0;
-
- ap_hook_pre_config(beos_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
-}
-
-static const char *set_threads_to_start(cmd_parms *cmd, void *dummy, const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- ap_threads_to_start = atoi(arg);
- if (ap_threads_to_start < 0) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "StartThreads set to a value less than 0, reset to 1");
- ap_threads_to_start = 1;
- }
- return NULL;
-}
-
-static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy, const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- min_spare_threads = atoi(arg);
- if (min_spare_threads <= 0) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: detected MinSpareThreads set to non-positive.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Resetting to 1 to avoid almost certain Apache failure.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Please read the documentation.");
- min_spare_threads = 1;
- }
-
- return NULL;
-}
-
-static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy, const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- max_spare_threads = atoi(arg);
- return NULL;
-}
-
-static const char *set_threads_limit (cmd_parms *cmd, void *dummy, const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- ap_thread_limit = atoi(arg);
- if (ap_thread_limit > HARD_THREAD_LIMIT) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxClients of %d exceeds compile time limit "
- "of %d servers,", ap_thread_limit, HARD_THREAD_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering MaxClients to %d. To increase, please "
- "see the", HARD_THREAD_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " HARD_THREAD_LIMIT define in server/mpm/beos/mpm_default.h.");
- ap_thread_limit = HARD_THREAD_LIMIT;
- }
- else if (ap_thread_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require MaxClients > 0, setting to %d", HARD_THREAD_LIMIT);
- ap_thread_limit = HARD_THREAD_LIMIT;
- }
- return NULL;
-}
-
-static const char *set_max_requests_per_thread (cmd_parms *cmd, void *dummy, const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- ap_max_requests_per_thread = atoi(arg);
- if (ap_max_requests_per_thread < 0) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxRequestsPerThread was set below 0"
- "reset to 0, but this may not be what you want.");
- ap_max_requests_per_thread = 0;
- }
-
- return NULL;
-}
-
-static const command_rec beos_cmds[] = {
-BEOS_DAEMON_COMMANDS,
-LISTEN_COMMANDS,
-AP_INIT_TAKE1( "StartThreads", set_threads_to_start, NULL, RSRC_CONF,
- "Number of threads to launch at server startup"),
-AP_INIT_TAKE1( "MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
- "Minimum number of idle children, to handle request spikes"),
-AP_INIT_TAKE1( "MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
- "Maximum number of idle children" ),
-AP_INIT_TAKE1( "MaxClients", set_threads_limit, NULL, RSRC_CONF,
- "Maximum number of children alive at the same time (max threads)" ),
-AP_INIT_TAKE1( "MaxRequestsPerThread", set_max_requests_per_thread, NULL, RSRC_CONF,
- "Maximum number of requests served by a thread" ),
-{ NULL }
-};
-
-module AP_MODULE_DECLARE_DATA mpm_beos_module = {
- MPM20_MODULE_STUFF,
- NULL, /* hook to run before apache parses args */
- NULL, /* create per-directory config structure */
- NULL, /* merge per-directory config structures */
- NULL, /* create per-server config structure */
- NULL, /* merge per-server config structures */
- beos_cmds, /* command apr_table_t */
- beos_hooks /* register_hooks */
-};
-
diff --git a/server/mpm/beos/beos.h b/server/mpm/beos/beos.h
deleted file mode 100644
index 8b7aed7d..00000000
--- a/server/mpm/beos/beos.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file beos/beos.h
- * @brief Extern functions/values for BEOS MPM
- *
- * @addtogroup APACHE_MPM_BEOS
- * @{
- */
-#ifndef APACHE_MPM_BEOS_H
-#define APACHE_MPM_BEOS_H
-
-extern int ap_threads_per_child;
-extern int ap_pipe_of_death[2];
-extern int ap_extended_status;
-extern void clean_child_exit(int);
-extern int max_daemons_limit;
-
-#endif /* APACHE_MPM_BEOS_H */
-/** @} */
diff --git a/server/mpm/beos/config5.m4 b/server/mpm/beos/config5.m4
deleted file mode 100644
index 4f201408..00000000
--- a/server/mpm/beos/config5.m4
+++ /dev/null
@@ -1,7 +0,0 @@
-dnl ## XXX - Need a more thorough check of the proper flags to use
-
-if test "$MPM_NAME" = "beos" ; then
- apache_apr_flags="--enable-threads"
-
- APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
-fi
diff --git a/server/mpm/beos/mpm.h b/server/mpm/beos/mpm.h
deleted file mode 100644
index d61594b0..00000000
--- a/server/mpm/beos/mpm.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file beos/mpm.h
- * @brief BEOS MPM
- *
- * @defgroup APACHE_MPM_BEOS BEOS MPM
- * @ingroup APACHE_MPM APACHE_OS_BEOS
- * @{
- */
-
-#ifndef APACHE_MPM_BEOS_H
-#define APACHE_MPM_BEOS_H
-
-#define BEOS_MPM
-#include "scoreboard.h"
-
-#define MPM_NAME "Beos"
-#define MPM_CHILD_PID(i) (ap_scoreboard_image->servers[0][i].tid)
-#define MPM_NOTE_CHILD_KILLED(i) (MPM_CHILD_PID(i) = 0)
-
-#define AP_MPM_WANT_RECLAIM_CHILD_PROCESSES
-#define AP_MPM_WANT_WAIT_OR_TIMEOUT
-#define AP_MPM_WANT_PROCESS_CHILD_STATUS
-#define AP_MPM_WANT_SET_PIDFILE
-#define AP_MPM_WANT_SET_SCOREBOARD
-#define AP_MPM_WANT_SET_MAX_REQUESTS
-#define AP_MPM_WANT_SET_COREDUMPDIR
-#define AP_MPM_WANT_SET_MAX_MEM_FREE
-
-extern int ap_max_child_assigned;
-extern server_rec *ap_server_conf;
-extern int ap_threads_per_child;
-
-#endif /* APACHE_MPM_BEOS_H */
-/** @} */
diff --git a/server/mpm/beos/mpm_default.h b/server/mpm/beos/mpm_default.h
deleted file mode 100644
index 316a4c09..00000000
--- a/server/mpm/beos/mpm_default.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file beos/mpm_default.h
- * @brief beos MPM defaults
- *
- * @addtogroup APACHE_MPM_BEOS
- * @{
- */
-#ifndef APACHE_MPM_DEFAULT_H
-#define APACHE_MPM_DEFAULT_H
-
-/* we use the child (c) as zero in our code... */
-#define AP_ID_FROM_CHILD_THREAD(c, t) t
-/* as the child is always zero, just return the id... */
-#define AP_CHILD_THREAD_FROM_ID(i) 0 , i
-
-/* Number of threads to spawn off by default --- also, if fewer than
- * this free when the caretaker checks, it will spawn more.
- */
-#ifndef DEFAULT_START_THREADS
-#define DEFAULT_START_THREADS 10
-#endif
-
-#ifdef NO_THREADS
-#define DEFAULT_THREADS 1
-#endif
-#ifndef DEFAULT_THREADS
-#define DEFAULT_THREADS 10
-#endif
-
-/* The following 2 settings are used to control the number of threads
- * we have available. Normally the DEFAULT_MAX_FREE_THREADS is set
- * to the same as the HARD_THREAD_LIMIT to avoid churning of starting
- * new threads to replace threads killed off...
- */
-
-/* Maximum number of *free* threads --- more than this, and
- * they will die off.
- */
-#ifndef DEFAULT_MAX_FREE_THREADS
-#define DEFAULT_MAX_FREE_THREADS HARD_THREAD_LIMIT
-#endif
-
-/* Minimum --- fewer than this, and more will be created */
-#ifndef DEFAULT_MIN_FREE_THREADS
-#define DEFAULT_MIN_FREE_THREADS 1
-#endif
-
-/* Where the main/parent process's pid is logged */
-#ifndef DEFAULT_PIDLOG
-#define DEFAULT_PIDLOG DEFAULT_REL_RUNTIMEDIR "/httpd.pid"
-#endif
-
-/*
- * Interval, in microseconds, between scoreboard maintenance.
- */
-#ifndef SCOREBOARD_MAINTENANCE_INTERVAL
-#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
-#endif
-
-/* Number of requests to try to handle in a single process. If == 0,
- * the children don't die off.
- */
-#ifndef DEFAULT_MAX_REQUESTS_PER_THREAD
-#define DEFAULT_MAX_REQUESTS_PER_THREAD 0
-#endif
-
-#endif /* AP_MPM_DEFAULT_H */
-/** @} */
diff --git a/server/mpm/config.m4 b/server/mpm/config.m4
index b0907769..0b263cbc 100644
--- a/server/mpm/config.m4
+++ b/server/mpm/config.m4
@@ -1,79 +1,117 @@
-AC_MSG_CHECKING(which MPM to use)
-AC_ARG_WITH(mpm,
-APACHE_HELP_STRING(--with-mpm=MPM,Choose the process model for Apache to use.
- MPM={beos|event|worker|prefork|mpmt_os2|winnt}),[
- APACHE_MPM=$withval
-],[
- if test "x$APACHE_MPM" = "x"; then
- APACHE_MPM=prefork
- fi
+dnl common platform checks needed by MPMs, methods for MPMs to state
+dnl their support for the platform, functions to query MPM properties
+
+APR_CHECK_APR_DEFINE(APR_HAS_THREADS)
+
+have_threaded_sig_graceful=yes
+case $host in
+ *-linux-*)
+ case `uname -r` in
+ 2.0* )
+ dnl Threaded MPM's are not supported on Linux 2.0
+ dnl as on 2.0 the linuxthreads library uses SIGUSR1
+ dnl and SIGUSR2 internally
+ have_threaded_sig_graceful=no
+ ;;
+ esac
+ ;;
+esac
+
+dnl See if APR supports APR_POLLSET_THREADSAFE.
+dnl XXX This hack tests for the underlying functions used by APR when it supports
+dnl XXX APR_POLLSET_THREADSAFE, and duplicates APR's Darwin version check.
+dnl A run-time check for
+dnl apr_pollset_create(,,APR_POLLSET_THREADSAFE) == APR_SUCCESS
+dnl would be great but an in-tree apr (srclib/apr) hasn't been built yet.
+
+AC_CACHE_CHECK([whether APR supports thread-safe pollsets], [ac_cv_have_threadsafe_pollset], [
+ case $host in
+ *-apple-darwin[[1-9]].*)
+ APR_SETIFNULL(ac_cv_func_kqueue, [no])
+ ;;
+ esac
+ AC_CHECK_FUNCS(kqueue port_create epoll_create)
+ if test "$ac_cv_func_kqueue$ac_cv_func_port_create$ac_cv_func_epoll_create" != "nonono"; then
+ ac_cv_have_threadsafe_pollset=yes
+ else
+ ac_cv_have_threadsafe_pollset=no
+ fi
])
-AC_MSG_RESULT($APACHE_MPM)
-apache_cv_mpm=$APACHE_MPM
-
-ap_mpm_is_threaded ()
-{
- if test "$apache_cv_mpm" = "worker" -o "$apache_cv_mpm" = "event" -o "$apache_cv_mpm" = "winnt" ; then
- return 0
+dnl See if this is a forking platform w.r.t. MPMs
+case $host in
+ *mingw32* | *os2-emx*)
+ forking_mpms_supported=no
+ ;;
+ *)
+ forking_mpms_supported=yes
+ ;;
+esac
+
+dnl APACHE_MPM_SUPPORTED(name, supports-shared, is_threaded)
+AC_DEFUN(APACHE_MPM_SUPPORTED,[
+ if test "$2" = "yes"; then
+ eval "ap_supported_mpm_$1=shared"
+ ap_supported_shared_mpms="$ap_supported_shared_mpms $1 "
else
+ eval "ap_supported_mpm_$1=static"
+ fi
+ if test "$3" = "yes"; then
+ eval "ap_threaded_mpm_$1=yes"
+ fi
+])dnl
+
+dnl APACHE_MPM_ENABLED(name)
+AC_DEFUN(APACHE_MPM_ENABLED,[
+ if ap_mpm_is_enabled $1; then
+ :
+ else
+ eval "ap_enabled_mpm_$1=yes"
+ ap_enabled_mpms="$ap_enabled_mpms $1 "
+ fi
+])dnl
+
+ap_mpm_is_supported ()
+{
+ eval "tmp=\$ap_supported_mpm_$1"
+ if test -z "$tmp"; then
return 1
+ else
+ return 0
fi
}
-ap_mpm_is_experimental ()
+ap_mpm_supports_shared ()
{
- if test "$apache_cv_mpm" = "event" ; then
+ eval "tmp=\$ap_supported_mpm_$1"
+ if test "$tmp" = "shared"; then
return 0
else
return 1
fi
}
-if ap_mpm_is_threaded; then
- APR_CHECK_APR_DEFINE(APR_HAS_THREADS)
-
- if test "x$ac_cv_define_APR_HAS_THREADS" = "xno"; then
- AC_MSG_RESULT(The currently selected MPM requires threads which your system seems to lack)
- AC_MSG_CHECKING(checking for replacement)
- AC_MSG_RESULT(prefork selected)
- apache_cv_mpm=prefork
- else
- case $host in
- *-linux-*)
- case `uname -r` in
- 2.0* )
- dnl Threaded MPM's are not supported on Linux 2.0
- dnl as on 2.0 the linuxthreads library uses SIGUSR1
- dnl and SIGUSR2 internally
- echo "Threaded MPM's are not supported on this platform"
- AC_MSG_CHECKING(checking for replacement)
- AC_MSG_RESULT(prefork selected)
- apache_cv_mpm=prefork
- ;;
- esac
- ;;
- esac
- fi
-fi
-
-APACHE_FAST_OUTPUT(server/mpm/Makefile)
-
-MPM_NAME=$apache_cv_mpm
-if ap_mpm_is_experimental; then
- AC_MSG_WARN(You have selected an EXPERIMENTAL MPM. Be warned!)
- MPM_SUBDIR_NAME=experimental/$MPM_NAME
-else
- MPM_SUBDIR_NAME=$MPM_NAME
-fi
-MPM_DIR=server/mpm/$MPM_SUBDIR_NAME
-MPM_LIB=$MPM_DIR/lib${MPM_NAME}.la
-
-if test ! -f "$abs_srcdir/$MPM_DIR/mpm.h"; then
- AC_MSG_ERROR(the selected mpm -- $apache_cv_mpm -- is not supported)
-fi
+ap_mpm_is_threaded ()
+{
+ if test "$mpm_build" = "shared" -a ac_cv_define_APR_HAS_THREADS = "yes"; then
+ return 0
+ fi
-APACHE_SUBST(MPM_NAME)
-APACHE_SUBST(MPM_SUBDIR_NAME)
-MODLIST="$MODLIST mpm_${MPM_NAME}"
+ for mpm in $ap_enabled_mpms; do
+ eval "tmp=\$ap_threaded_mpm_$mpm"
+ if test "$tmp" = "yes"; then
+ return 0
+ fi
+ done
+ return 1
+}
+ap_mpm_is_enabled ()
+{
+ eval "tmp=\$ap_enabled_mpm_$1"
+ if test "$tmp" = "yes"; then
+ return 0
+ else
+ return 1
+ fi
+}
diff --git a/server/mpm/config2.m4 b/server/mpm/config2.m4
new file mode 100644
index 00000000..d7e73ec0
--- /dev/null
+++ b/server/mpm/config2.m4
@@ -0,0 +1,89 @@
+AC_MSG_CHECKING(which MPM to use by default)
+AC_ARG_WITH(mpm,
+APACHE_HELP_STRING(--with-mpm=MPM,Choose the process model for Apache to use by default.
+ MPM={event|worker|prefork|winnt}
+ This will be statically linked as the only available MPM unless
+ --enable-mpms-shared is also specified.
+),[
+ default_mpm=$withval
+ AC_MSG_RESULT($withval);
+],[
+ dnl Order of preference for default MPM:
+ dnl The Windows and OS/2 MPMs are used on those platforms.
+ dnl Everywhere else: event, worker, prefork
+ if ap_mpm_is_supported "winnt"; then
+ default_mpm=winnt
+ AC_MSG_RESULT(winnt)
+ elif ap_mpm_is_supported "mpmt_os2"; then
+ default_mpm=mpmt_os2
+ AC_MSG_RESULT(mpmt_os2)
+ elif ap_mpm_is_supported "event"; then
+ default_mpm=event
+ AC_MSG_RESULT(event)
+ elif ap_mpm_is_supported "worker"; then
+ default_mpm=worker
+ AC_MSG_RESULT(worker - event is not supported)
+ else
+ default_mpm=prefork
+ AC_MSG_RESULT(prefork - event and worker are not supported)
+ fi
+])
+
+APACHE_MPM_ENABLED($default_mpm)
+
+AC_ARG_ENABLE(mpms-shared,
+APACHE_HELP_STRING(--enable-mpms-shared=MPM-LIST,Space-separated list of MPM modules to enable for dynamic loading. MPM-LIST=list | "all"),[
+ if test "$enableval" = "no"; then
+ mpm_build=static
+ else
+ mpm_build=shared
+dnl Build just the default MPM if --enable-mpms-shared has no argument.
+ if test "$enableval" = "yes"; then
+ enableval=$default_mpm
+ fi
+ for i in $enableval; do
+ if test "$i" = "all"; then
+ for j in $ap_supported_shared_mpms; do
+ eval "enable_mpm_$j=shared"
+ APACHE_MPM_ENABLED($j)
+ done
+ else
+ i=`echo $i | sed 's/-/_/g'`
+ if ap_mpm_supports_shared $i; then
+ eval "enable_mpm_$i=shared"
+ APACHE_MPM_ENABLED($i)
+ else
+ AC_MSG_ERROR([MPM $i does not support dynamic loading.])
+ fi
+ fi
+ done
+ fi
+], [mpm_build=static])
+
+for i in $ap_enabled_mpms; do
+ if ap_mpm_is_supported $i; then
+ :
+ else
+ AC_MSG_ERROR([MPM $i is not supported on this platform.])
+ fi
+done
+
+if test $mpm_build = "shared"; then
+ eval "tmp=\$enable_mpm_$default_mpm"
+ if test "$tmp" != "shared"; then
+ AC_MSG_ERROR([The default MPM ($default_mpm) must be included in --enable-mpms-shared. Use --with-mpm to change the default MPM.])
+ fi
+fi
+
+APACHE_FAST_OUTPUT(server/mpm/Makefile)
+
+if test $mpm_build = "shared"; then
+ MPM_LIB=""
+else
+ MPM_LIB=server/mpm/$default_mpm/lib${default_mpm}.la
+ MODLIST="$MODLIST mpm_${default_mpm}"
+fi
+
+MPM_SUBDIRS=$ap_enabled_mpms
+APACHE_SUBST(MPM_SUBDIRS)
+APACHE_SUBST(MPM_LIB)
diff --git a/server/mpm/event/Makefile.in b/server/mpm/event/Makefile.in
new file mode 100644
index 00000000..f34af9cb
--- /dev/null
+++ b/server/mpm/event/Makefile.in
@@ -0,0 +1 @@
+include $(top_srcdir)/build/special.mk
diff --git a/server/mpm/event/config.m4 b/server/mpm/event/config.m4
new file mode 100644
index 00000000..351f1acf
--- /dev/null
+++ b/server/mpm/event/config.m4
@@ -0,0 +1,13 @@
+AC_MSG_CHECKING(if event MPM supports this platform)
+if test $forking_mpms_supported != yes; then
+ AC_MSG_RESULT(no - This is not a forking platform)
+elif test $ac_cv_define_APR_HAS_THREADS != yes; then
+ AC_MSG_RESULT(no - APR does not support threads)
+elif test $have_threaded_sig_graceful != yes; then
+ AC_MSG_RESULT(no - SIG_GRACEFUL cannot be used with a threaded MPM)
+elif test $ac_cv_have_threadsafe_pollset != yes; then
+ AC_MSG_RESULT(no - APR_POLLSET_THREADSAFE is not supported)
+else
+ AC_MSG_RESULT(yes)
+ APACHE_MPM_SUPPORTED(event, yes, yes)
+fi
diff --git a/server/mpm/event/config3.m4 b/server/mpm/event/config3.m4
new file mode 100644
index 00000000..e7ec6320
--- /dev/null
+++ b/server/mpm/event/config3.m4
@@ -0,0 +1,7 @@
+dnl ## XXX - Need a more thorough check of the proper flags to use
+
+APACHE_SUBST(MOD_MPM_EVENT_LDADD)
+
+APACHE_MPM_MODULE(event, $enable_mpm_event, event.lo fdqueue.lo pod.lo,[
+ AC_CHECK_FUNCS(pthread_kill)
+], , [\$(MOD_MPM_EVENT_LDADD)])
diff --git a/server/mpm/experimental/event/event.c b/server/mpm/event/event.c
index bbee899e..b7f1d874 100644
--- a/server/mpm/experimental/event/event.c
+++ b/server/mpm/event/event.c
@@ -25,7 +25,7 @@
* this MPM has a dedicated thread for handling both the Listenting sockets,
* and all sockets that are in a Keep Alive status.
*
- * The MPM assumes the underlying apr_pollset implmentation is somewhat
+ * The MPM assumes the underlying apr_pollset implementation is somewhat
* threadsafe. This currently is only compatible with KQueue and EPoll. This
* enables the MPM to avoid extra high level locking or having to wake up the
* listener thread when a keep-alive socket needs to be sent to it.
@@ -51,12 +51,13 @@
#include "apr_thread_proc.h"
#include "apr_signal.h"
#include "apr_thread_mutex.h"
-#include "apr_proc_mutex.h"
#include "apr_poll.h"
#include "apr_ring.h"
#include "apr_queue.h"
+#include "apr_atomic.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
+#include "apr_version.h"
#if APR_HAVE_UNISTD_H
#include <unistd.h>
@@ -75,8 +76,6 @@
#error The Event MPM requires APR threads, but they are unavailable.
#endif
-#define CORE_PRIVATE
-
#include "ap_config.h"
#include "httpd.h"
#include "http_main.h"
@@ -92,10 +91,12 @@
#include "fdqueue.h"
#include "mpm_default.h"
#include "http_vhost.h"
+#include "unixd.h"
#include <signal.h>
#include <limits.h> /* for INT_MAX */
+
/* Limit on the total --- clients will be locked out if more servers than
* this are needed. It is intended solely to keep the server from crashing
* when things get out of hand.
@@ -136,36 +137,117 @@
#define MAX_THREAD_LIMIT 100000
#endif
+#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
+
+#if !APR_VERSION_AT_LEAST(1,4,0)
+#define apr_time_from_msec(x) (x * 1000)
+#endif
+
+#ifndef MAX_SECS_TO_LINGER
+#define MAX_SECS_TO_LINGER 30
+#endif
+#define SECONDS_TO_LINGER 2
+
/*
* Actual definitions of config globals
*/
-int ap_threads_per_child = 0; /* Worker threads per child */
+#ifndef DEFAULT_WORKER_FACTOR
+#define DEFAULT_WORKER_FACTOR 2
+#endif
+#define WORKER_FACTOR_SCALE 16 /* scale factor to allow fractional values */
+static unsigned int worker_factor = DEFAULT_WORKER_FACTOR * WORKER_FACTOR_SCALE;
+
+static int threads_per_child = 0; /* Worker threads per child */
static int ap_daemons_to_start = 0;
static int min_spare_threads = 0;
static int max_spare_threads = 0;
static int ap_daemons_limit = 0;
-static int server_limit = DEFAULT_SERVER_LIMIT;
-static int first_server_limit = 0;
-static int thread_limit = DEFAULT_THREAD_LIMIT;
-static int first_thread_limit = 0;
-static int changed_limit_at_restart;
+static int max_workers = 0;
+static int server_limit = 0;
+static int thread_limit = 0;
static int dying = 0;
static int workers_may_exit = 0;
static int start_thread_may_exit = 0;
static int listener_may_exit = 0;
static int requests_this_child;
static int num_listensocks = 0;
+static apr_uint32_t connection_count = 0;
static int resource_shortage = 0;
static fd_queue_t *worker_queue;
static fd_queue_info_t *worker_queue_info;
static int mpm_state = AP_MPMQ_STARTING;
-static int sick_child_detected;
-apr_thread_mutex_t *timeout_mutex;
-APR_RING_HEAD(timeout_head_t, conn_state_t);
-static struct timeout_head_t timeout_head;
+static apr_thread_mutex_t *timeout_mutex;
+struct event_conn_state_t {
+ /** APR_RING of expiration timeouts */
+ APR_RING_ENTRY(event_conn_state_t) timeout_list;
+ /** the expiration time of the next keepalive timeout */
+ apr_time_t expiration_time;
+ /** connection record this struct refers to */
+ conn_rec *c;
+ /** memory pool to allocate from */
+ apr_pool_t *p;
+ /** bucket allocator */
+ apr_bucket_alloc_t *bucket_alloc;
+ /** poll file descriptor information */
+ apr_pollfd_t pfd;
+ /** public parts of the connection state */
+ conn_state_t pub;
+};
+APR_RING_HEAD(timeout_head_t, event_conn_state_t);
+
+struct timeout_queue {
+ struct timeout_head_t head;
+ int count;
+ const char *tag;
+};
+/*
+ * Several timeout queues that use different timeouts, so that we always can
+ * simply append to the end.
+ * write_completion_q uses TimeOut
+ * keepalive_q uses KeepAliveTimeOut
+ * linger_q uses MAX_SECS_TO_LINGER
+ * short_linger_q uses SECONDS_TO_LINGER
+ */
+static struct timeout_queue write_completion_q, keepalive_q, linger_q,
+ short_linger_q;
+static apr_pollfd_t *listener_pollfd;
+
+/*
+ * Macros for accessing struct timeout_queue.
+ * For TO_QUEUE_APPEND and TO_QUEUE_REMOVE, timeout_mutex must be held.
+ */
+#define TO_QUEUE_APPEND(q, el) \
+ do { \
+ APR_RING_INSERT_TAIL(&(q).head, el, event_conn_state_t, timeout_list); \
+ (q).count++; \
+ } while (0)
+
+#define TO_QUEUE_REMOVE(q, el) \
+ do { \
+ APR_RING_REMOVE(el, timeout_list); \
+ (q).count--; \
+ } while (0)
+
+#define TO_QUEUE_INIT(q) \
+ do { \
+ APR_RING_INIT(&(q).head, event_conn_state_t, timeout_list); \
+ (q).tag = #q; \
+ } while (0)
+
+#define TO_QUEUE_ELEM_INIT(el) APR_RING_ELEM_INIT(el, timeout_list)
+
+/*
+ * The pollset for sockets that are in any of the timeout queues. Currently
+ * we use the timeout_mutex to make sure that connections are added/removed
+ * atomically to/from both event_pollset and a timeout queue. Otherwise
+ * some confusion can happen under high load if timeout queues and pollset
+ * get out of sync.
+ * XXX: It should be possible to make the lock unnecessary in many or even all
+ * XXX: cases.
+ */
static apr_pollset_t *event_pollset;
/* The structure used to pass unique initialization info to each thread */
@@ -196,27 +278,47 @@ typedef enum
typedef struct
{
poll_type_e type;
- int status; /*XXX what is this for? 0 and 1 don't make it clear */
void *baton;
} listener_poll_type;
-#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
-
-/*
- * The max child slot ever assigned, preserved across restarts. Necessary
- * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We
- * use this value to optimize routines that have to scan the entire
- * scoreboard.
+/* data retained by event across load/unload of the module
+ * allocated on first call to pre-config hook; located on
+ * subsequent calls to pre-config hook
*/
-int ap_max_daemons_limit = -1;
-
-static ap_pod_t *pod;
+typedef struct event_retained_data {
+ int first_server_limit;
+ int first_thread_limit;
+ int module_loads;
+ int sick_child_detected;
+ ap_generation_t my_generation;
+ int volatile is_graceful; /* set from signal handler */
+ int maxclients_reported;
+ /*
+ * The max child slot ever assigned, preserved across restarts. Necessary
+ * to deal with MaxRequestWorkers changes across AP_SIG_GRACEFUL restarts.
+ * We use this value to optimize routines that have to scan the entire
+ * scoreboard.
+ */
+ int max_daemons_limit;
+ /*
+ * 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.
+ */
+ int idle_spawn_rate;
+#ifndef MAX_SPAWN_RATE
+#define MAX_SPAWN_RATE (32)
+#endif
+ int hold_off_on_exponential_spawning;
+} event_retained_data;
+static event_retained_data *retained;
-/* *Non*-shared http_main globals... */
+#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
-server_rec *ap_server_conf;
+static ap_event_pod_t *pod;
-/* The worker MPM respects a couple of runtime flags that can aid
+/* The event MPM respects a couple of runtime flags that can aid
* in debugging. Setting the -DNO_DETACH flag will prevent the root process
* from detaching from its controlling terminal. Additionally, setting
* the -DONE_PROCESS flag (which implies -DNO_DETACH) will get you the
@@ -256,10 +358,36 @@ static apr_os_thread_t *listener_os_thread;
*/
static apr_socket_t **worker_sockets;
+static void disable_listensocks(int process_slot)
+{
+ int i;
+ for (i = 0; i < num_listensocks; i++) {
+ apr_pollset_remove(event_pollset, &listener_pollfd[i]);
+ }
+ ap_scoreboard_image->parent[process_slot].not_accepting = 1;
+}
+
+static void enable_listensocks(int process_slot)
+{
+ int i;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00457)
+ "Accepting new connections again: "
+ "%u active conns, %u idle workers",
+ apr_atomic_read32(&connection_count),
+ ap_queue_info_get_idlers(worker_queue_info));
+ for (i = 0; i < num_listensocks; i++)
+ apr_pollset_add(event_pollset, &listener_pollfd[i]);
+ /*
+ * XXX: This is not yet optimal. If many workers suddenly become available,
+ * XXX: the parent may kill some processes off too soon.
+ */
+ ap_scoreboard_image->parent[process_slot].not_accepting = 0;
+}
+
static void close_worker_sockets(void)
{
int i;
- for (i = 0; i < ap_threads_per_child; i++) {
+ for (i = 0; i < threads_per_child; i++) {
if (worker_sockets[i]) {
apr_socket_close(worker_sockets[i]);
worker_sockets[i] = NULL;
@@ -278,6 +406,10 @@ static void wakeup_listener(void)
*/
return;
}
+
+ /* unblock the listener if it's waiting for a worker */
+ ap_queue_info_term(worker_queue_info);
+
/*
* we should just be able to "kill(ap_my_pid, LISTENER_SIGNAL)" on all
* platforms and wake up the listener thread since it is the only thread
@@ -316,58 +448,112 @@ static void signal_threads(int mode)
if (mode == ST_UNGRACEFUL) {
workers_may_exit = 1;
ap_queue_interrupt_all(worker_queue);
- ap_queue_info_term(worker_queue_info);
close_worker_sockets(); /* forcefully kill all current connections */
}
}
-AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+static int event_query(int query_code, int *result, apr_status_t *rv)
{
+ *rv = APR_SUCCESS;
switch (query_code) {
case AP_MPMQ_MAX_DAEMON_USED:
- *result = ap_max_daemons_limit;
- return APR_SUCCESS;
+ *result = retained->max_daemons_limit;
+ break;
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_STATIC;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_IS_FORKED:
*result = AP_MPMQ_DYNAMIC;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_IS_ASYNC:
*result = 1;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_DAEMONS:
*result = server_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_THREADS:
*result = thread_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_THREADS:
- *result = ap_threads_per_child;
- return APR_SUCCESS;
+ *result = threads_per_child;
+ break;
case AP_MPMQ_MIN_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MIN_SPARE_THREADS:
*result = min_spare_threads;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_THREADS:
*result = max_spare_threads;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_REQUESTS_DAEMON:
*result = ap_max_requests_per_child;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_DAEMONS:
*result = ap_daemons_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MPM_STATE:
*result = mpm_state;
- return APR_SUCCESS;
+ break;
+ case AP_MPMQ_GENERATION:
+ *result = retained->my_generation;
+ break;
+ default:
+ *rv = APR_ENOTIMPL;
+ break;
}
- return APR_ENOTIMPL;
+ return OK;
+}
+
+static void event_note_child_killed(int childnum, pid_t pid, ap_generation_t gen)
+{
+ if (childnum != -1) { /* child had a scoreboard slot? */
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[childnum].pid,
+ ap_scoreboard_image->parent[childnum].generation,
+ childnum, MPM_CHILD_EXITED);
+ ap_scoreboard_image->parent[childnum].pid = 0;
+ }
+ else {
+ ap_run_child_status(ap_server_conf, pid, gen, -1, MPM_CHILD_EXITED);
+ }
+}
+
+static void event_note_child_started(int slot, pid_t pid)
+{
+ ap_scoreboard_image->parent[slot].pid = pid;
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[slot].pid,
+ retained->my_generation, slot, MPM_CHILD_STARTED);
+}
+
+static void event_note_child_lost_slot(int slot, pid_t newpid)
+{
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00458)
+ "pid %" APR_PID_T_FMT " taking over scoreboard slot from "
+ "%" APR_PID_T_FMT "%s",
+ newpid,
+ ap_scoreboard_image->parent[slot].pid,
+ ap_scoreboard_image->parent[slot].quiescing ?
+ " (quiescing)" : "");
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[slot].pid,
+ ap_scoreboard_image->parent[slot].generation,
+ slot, MPM_CHILD_LOST_SLOT);
+ /* Don't forget about this exiting child process, or we
+ * won't be able to kill it if it doesn't exit by the
+ * time the server is shut down.
+ */
+ ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid,
+ ap_scoreboard_image->parent[slot].generation);
+}
+
+static const char *event_get_name(void)
+{
+ return "event";
}
/* a clean exit from a child with proper cleanup */
@@ -378,6 +564,11 @@ static void clean_child_exit(int code)
if (pchild) {
apr_pool_destroy(pchild);
}
+
+ if (one_process) {
+ event_note_child_killed(/* slot */ 0, 0, 0);
+ }
+
exit(code);
}
@@ -390,12 +581,16 @@ static void just_die(int sig)
* Connection structures and accounting...
*/
-/* volatile just in case */
+static int child_fatal;
+
+/* volatile because they're updated from a signal handler */
static int volatile shutdown_pending;
static int volatile restart_pending;
-static int volatile is_graceful;
-static volatile int child_fatal;
-ap_generation_t volatile ap_my_generation;
+
+static apr_status_t decrement_connection_count(void *dummy) {
+ apr_atomic_dec32(&connection_count);
+ return APR_SUCCESS;
+}
/*
* ap_start_shutdown() and ap_start_restart(), below, are a first stab at
@@ -427,7 +622,7 @@ static void ap_start_shutdown(int graceful)
return;
}
shutdown_pending = 1;
- is_graceful = graceful;
+ retained->is_graceful = graceful;
}
/* do a graceful restart if graceful == 1 */
@@ -439,7 +634,7 @@ static void ap_start_restart(int graceful)
return;
}
restart_pending = 1;
- is_graceful = graceful;
+ retained->is_graceful = graceful;
}
static void sig_term(int sig)
@@ -468,34 +663,37 @@ static void set_signals(void)
sa.sa_handler = sig_term;
if (sigaction(SIGTERM, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00459)
"sigaction(SIGTERM)");
#ifdef AP_SIG_GRACEFUL_STOP
if (sigaction(AP_SIG_GRACEFUL_STOP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00460)
"sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")");
#endif
#ifdef SIGINT
if (sigaction(SIGINT, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00461)
"sigaction(SIGINT)");
#endif
#ifdef SIGXCPU
sa.sa_handler = SIG_DFL;
if (sigaction(SIGXCPU, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00462)
"sigaction(SIGXCPU)");
#endif
#ifdef SIGXFSZ
- sa.sa_handler = SIG_DFL;
+ /* For systems following the LFS standard, ignoring SIGXFSZ allows
+ * a write() beyond the 2GB limit to fail gracefully with E2BIG
+ * rather than terminate the process. */
+ sa.sa_handler = SIG_IGN;
if (sigaction(SIGXFSZ, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00463)
"sigaction(SIGXFSZ)");
#endif
#ifdef SIGPIPE
sa.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00464)
"sigaction(SIGPIPE)");
#endif
@@ -505,10 +703,10 @@ static void set_signals(void)
sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL);
sa.sa_handler = restart;
if (sigaction(SIGHUP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00465)
"sigaction(SIGHUP)");
if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00466)
"sigaction(" AP_SIG_GRACEFUL_STRING ")");
#else
if (!one_process) {
@@ -516,7 +714,7 @@ static void set_signals(void)
apr_signal(SIGXCPU, SIG_DFL);
#endif /* SIGXCPU */
#ifdef SIGXFSZ
- apr_signal(SIGXFSZ, SIG_DFL);
+ apr_signal(SIGXFSZ, SIG_IGN);
#endif /* SIGXFSZ */
}
@@ -537,68 +735,140 @@ static void set_signals(void)
#endif
}
-/*****************************************************************
- * Here follows a long bunch of generic server bookkeeping stuff...
+/*
+ * close our side of the connection
+ * Pre-condition: cs is not in any timeout queue and not in the pollset,
+ * timeout_mutex is not locked
+ * return: 0 if connection is fully closed,
+ * 1 if connection is lingering
+ * may be called by listener or by worker thread
*/
-
-int ap_graceful_stop_signalled(void)
- /* XXX this is really a bad confusing obsolete name
- * maybe it should be ap_mpm_process_exiting?
- */
+static int start_lingering_close(event_conn_state_t *cs)
{
- /* note: for a graceful termination, listener_may_exit will be set before
- * workers_may_exit, so check listener_may_exit
- */
- return listener_may_exit;
+ apr_status_t rv;
+
+ cs->c->sbh = NULL; /* prevent scoreboard updates from the listener
+ * worker will loop around and set SERVER_READY soon
+ */
+
+ if (ap_start_lingering_close(cs->c)) {
+ apr_pool_clear(cs->p);
+ ap_push_pool(worker_queue_info, cs->p);
+ return 0;
+ }
+ else {
+ apr_socket_t *csd = ap_get_conn_socket(cs->c);
+ struct timeout_queue *q;
+
+ rv = apr_socket_timeout_set(csd, 0);
+ AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+ /*
+ * If some module requested a shortened waiting period, only wait for
+ * 2s (SECONDS_TO_LINGER). This is useful for mitigating certain
+ * DoS attacks.
+ */
+ if (apr_table_get(cs->c->notes, "short-lingering-close")) {
+ cs->expiration_time =
+ apr_time_now() + apr_time_from_sec(SECONDS_TO_LINGER);
+ q = &short_linger_q;
+ cs->pub.state = CONN_STATE_LINGER_SHORT;
+ }
+ else {
+ cs->expiration_time =
+ apr_time_now() + apr_time_from_sec(MAX_SECS_TO_LINGER);
+ q = &linger_q;
+ cs->pub.state = CONN_STATE_LINGER_NORMAL;
+ }
+ apr_thread_mutex_lock(timeout_mutex);
+ TO_QUEUE_APPEND(*q, cs);
+ cs->pfd.reqevents = APR_POLLIN | APR_POLLHUP | APR_POLLERR;
+ rv = apr_pollset_add(event_pollset, &cs->pfd);
+ apr_thread_mutex_unlock(timeout_mutex);
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
+ "start_lingering_close: apr_pollset_add failure");
+ apr_thread_mutex_lock(timeout_mutex);
+ TO_QUEUE_REMOVE(*q, cs);
+ apr_thread_mutex_unlock(timeout_mutex);
+ apr_socket_close(cs->pfd.desc.s);
+ apr_pool_clear(cs->p);
+ ap_push_pool(worker_queue_info, cs->p);
+ return 0;
+ }
+ }
+ return 1;
}
-/*****************************************************************
- * Child process main loop.
+/*
+ * forcibly close a lingering connection after the lingering period has
+ * expired
+ * Pre-condition: cs is not in any timeout queue and not in the pollset
+ * return: irrelevant (need same prototype as start_lingering_close)
*/
+static int stop_lingering_close(event_conn_state_t *cs)
+{
+ apr_status_t rv;
+ apr_socket_t *csd = ap_get_conn_socket(cs->c);
+ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf,
+ "socket reached timeout in lingering-close state");
+ rv = apr_socket_close(csd);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00468) "error closing socket");
+ AP_DEBUG_ASSERT(0);
+ }
+ apr_pool_clear(cs->p);
+ ap_push_pool(worker_queue_info, cs->p);
+ return 0;
+}
-static int process_socket(apr_pool_t * p, apr_socket_t * sock,
- conn_state_t * cs, int my_child_num,
+/*
+ * process one connection in the worker
+ * return: 1 if the connection has been completed,
+ * 0 if it is still open and waiting for some event
+ */
+static int process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock,
+ event_conn_state_t * cs, int my_child_num,
int my_thread_num)
{
conn_rec *c;
- listener_poll_type *pt;
long conn_id = ID_FROM_CHILD_THREAD(my_child_num, my_thread_num);
- int csd;
int rc;
- apr_time_t time_now = 0;
ap_sb_handle_t *sbh;
ap_create_sb_handle(&sbh, p, my_child_num, my_thread_num);
- apr_os_sock_get(&csd, sock);
-
- time_now = apr_time_now();
if (cs == NULL) { /* This is a new connection */
-
- cs = apr_pcalloc(p, sizeof(conn_state_t));
-
- pt = apr_pcalloc(p, sizeof(*pt));
-
+ listener_poll_type *pt = apr_pcalloc(p, sizeof(*pt));
+ cs = apr_pcalloc(p, sizeof(event_conn_state_t));
cs->bucket_alloc = apr_bucket_alloc_create(p);
c = ap_run_create_connection(p, ap_server_conf, sock,
conn_id, sbh, cs->bucket_alloc);
+ if (!c) {
+ apr_bucket_alloc_destroy(cs->bucket_alloc);
+ apr_pool_clear(p);
+ ap_push_pool(worker_queue_info, p);
+ return 1;
+ }
+ apr_atomic_inc32(&connection_count);
+ apr_pool_cleanup_register(c->pool, NULL, decrement_connection_count, apr_pool_cleanup_null);
+ c->current_thread = thd;
cs->c = c;
- c->cs = cs;
+ c->cs = &(cs->pub);
cs->p = p;
cs->pfd.desc_type = APR_POLL_SOCKET;
cs->pfd.reqevents = APR_POLLIN;
cs->pfd.desc.s = sock;
pt->type = PT_CSD;
- pt->status = 1;
pt->baton = cs;
cs->pfd.client_data = pt;
+ TO_QUEUE_ELEM_INIT(cs);
ap_update_vhost_given_ip(c);
rc = ap_run_pre_connection(c, sock);
if (rc != OK && rc != DONE) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
- "process_socket: connection aborted");
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(00469)
+ "process_socket: connection aborted");
c->aborted = 1;
}
@@ -615,12 +885,13 @@ static int process_socket(apr_pool_t * p, apr_socket_t * sock,
* When the accept filter is active, sockets are kept in the
* kernel until a HTTP request is received.
*/
- cs->state = CONN_STATE_READ_REQUEST_LINE;
+ cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
}
else {
c = cs->c;
c->sbh = sbh;
+ c->current_thread = thd;
}
if (c->clogging_input_filters && !c->aborted) {
@@ -629,10 +900,13 @@ static int process_socket(apr_pool_t * p, apr_socket_t * sock,
* like the Worker MPM does.
*/
ap_run_process_connection(c);
- cs->state = CONN_STATE_LINGER;
+ if (cs->pub.state != CONN_STATE_SUSPENDED) {
+ cs->pub.state = CONN_STATE_LINGER;
+ }
}
- if (cs->state == CONN_STATE_READ_REQUEST_LINE) {
+read_request:
+ if (cs->pub.state == CONN_STATE_READ_REQUEST_LINE) {
if (!c->aborted) {
ap_run_process_connection(c);
@@ -642,19 +916,55 @@ static int process_socket(apr_pool_t * p, apr_socket_t * sock,
*/
}
else {
- cs->state = CONN_STATE_LINGER;
+ cs->pub.state = CONN_STATE_LINGER;
}
}
- if (cs->state == CONN_STATE_LINGER) {
- ap_lingering_close(c);
- apr_pool_clear(p);
- ap_push_pool(worker_queue_info, p);
- return 0;
+ if (cs->pub.state == CONN_STATE_WRITE_COMPLETION) {
+ ap_filter_t *output_filter = c->output_filters;
+ apr_status_t rv;
+ ap_update_child_status_from_conn(sbh, SERVER_BUSY_WRITE, c);
+ while (output_filter->next != NULL) {
+ output_filter = output_filter->next;
+ }
+ rv = output_filter->frec->filter_func.out_func(output_filter, NULL);
+ if (rv != APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c, APLOGNO(00470)
+ "network write failure in core output filter");
+ cs->pub.state = CONN_STATE_LINGER;
+ }
+ else if (c->data_in_output_filters) {
+ /* Still in WRITE_COMPLETION_STATE:
+ * Set a write timeout for this connection, and let the
+ * event thread poll for writeability.
+ */
+ cs->expiration_time = ap_server_conf->timeout + apr_time_now();
+ apr_thread_mutex_lock(timeout_mutex);
+ TO_QUEUE_APPEND(write_completion_q, cs);
+ cs->pfd.reqevents = APR_POLLOUT | APR_POLLHUP | APR_POLLERR;
+ rc = apr_pollset_add(event_pollset, &cs->pfd);
+ apr_thread_mutex_unlock(timeout_mutex);
+ return 1;
+ }
+ else if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted ||
+ listener_may_exit) {
+ cs->pub.state = CONN_STATE_LINGER;
+ }
+ else if (c->data_in_input_filters) {
+ cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
+ goto read_request;
+ }
+ else {
+ cs->pub.state = CONN_STATE_CHECK_REQUEST_LINE_READABLE;
+ }
}
- else if (cs->state == CONN_STATE_CHECK_REQUEST_LINE_READABLE) {
+
+ if (cs->pub.state == CONN_STATE_LINGER) {
+ if (!start_lingering_close(cs))
+ return 0;
+ }
+ else if (cs->pub.state == CONN_STATE_CHECK_REQUEST_LINE_READABLE) {
apr_status_t rc;
- listener_poll_type *pt = (listener_poll_type *) cs->pfd.client_data;
/* It greatly simplifies the logic to use a single timeout value here
* because the new element can just be added to the end of the list and
@@ -664,14 +974,14 @@ static int process_socket(apr_pool_t * p, apr_socket_t * sock,
* timeout today. With a normal client, the socket will be readable in
* a few milliseconds anyway.
*/
- cs->expiration_time = ap_server_conf->keep_alive_timeout + time_now;
+ cs->expiration_time = ap_server_conf->keep_alive_timeout +
+ apr_time_now();
apr_thread_mutex_lock(timeout_mutex);
- APR_RING_INSERT_TAIL(&timeout_head, cs, conn_state_t, timeout_list);
+ TO_QUEUE_APPEND(keepalive_q, cs);
- pt->status = 0;
- /* Add work to pollset. These are always read events */
+ /* Add work to pollset. */
+ cs->pfd.reqevents = APR_POLLIN;
rc = apr_pollset_add(event_pollset, &cs->pfd);
-
apr_thread_mutex_unlock(timeout_mutex);
if (rc != APR_SUCCESS) {
@@ -684,7 +994,7 @@ static int process_socket(apr_pool_t * p, apr_socket_t * sock,
}
/* requests_this_child has gone to zero or below. See if the admin coded
- "MaxRequestsPerChild 0", and keep going in that case. Doing it this way
+ "MaxConnectionsPerChild 0", and keep going in that case. Doing it this way
simplifies the hot path in worker_thread */
static void check_infinite_requests(void)
{
@@ -692,21 +1002,27 @@ static void check_infinite_requests(void)
signal_threads(ST_GRACEFUL);
}
else {
- /* wow! if you're executing this code, you may have set a record.
- * either this child process has served over 2 billion requests, or
- * you're running a threaded 2.0 on a 16 bit machine.
- *
- * I'll buy pizza and beers at Apachecon for the first person to do
- * the former without cheating (dorking with INT_MAX, or running with
- * uncommitted performance patches, for example).
- *
- * for the latter case, you probably deserve a beer too. Greg Ames
- */
-
requests_this_child = INT_MAX; /* keep going */
}
}
+static void close_listeners(int process_slot, int *closed) {
+ if (!*closed) {
+ int i;
+ disable_listensocks(process_slot);
+ ap_close_listeners();
+ *closed = 1;
+ dying = 1;
+ ap_scoreboard_image->parent[process_slot].quiescing = 1;
+ for (i = 0; i < threads_per_child; ++i) {
+ ap_update_child_status_from_indexes(process_slot, i,
+ SERVER_GRACEFUL, NULL);
+ }
+ /* wake up the main thread */
+ kill(ap_my_pid, SIGTERM);
+ }
+}
+
static void unblock_signal(int sig)
{
sigset_t sig_mask;
@@ -727,31 +1043,58 @@ static void dummy_signal_handler(int sig)
*/
}
-static apr_status_t push2worker(const apr_pollfd_t * pfd,
- apr_pollset_t * pollset)
+
+static apr_status_t init_pollset(apr_pool_t *p)
{
- listener_poll_type *pt = (listener_poll_type *) pfd->client_data;
- conn_state_t *cs = (conn_state_t *) pt->baton;
- apr_status_t rc;
+ ap_listen_rec *lr;
+ listener_poll_type *pt;
+ int i = 0;
+
+ TO_QUEUE_INIT(write_completion_q);
+ TO_QUEUE_INIT(keepalive_q);
+ TO_QUEUE_INIT(linger_q);
+ TO_QUEUE_INIT(short_linger_q);
+
+ listener_pollfd = apr_palloc(p, sizeof(apr_pollfd_t) * num_listensocks);
+ for (lr = ap_listeners; lr != NULL; lr = lr->next, i++) {
+ apr_pollfd_t *pfd;
+ AP_DEBUG_ASSERT(i < num_listensocks);
+ pfd = &listener_pollfd[i];
+ pt = apr_pcalloc(p, sizeof(*pt));
+ pfd->desc_type = APR_POLL_SOCKET;
+ pfd->desc.s = lr->sd;
+ pfd->reqevents = APR_POLLIN;
- if (pt->status == 1) {
- return 0;
- }
+ pt->type = PT_ACCEPT;
+ pt->baton = lr;
- pt->status = 1;
+ pfd->client_data = pt;
- rc = apr_pollset_remove(pollset, pfd);
+ apr_socket_opt_set(pfd->desc.s, APR_SO_NONBLOCK, 1);
+ apr_pollset_add(event_pollset, pfd);
- /*
- * Some of the pollset backends, like KQueue or Epoll
- * automagically remove the FD if the socket is closed,
- * therefore, we can accept _SUCCESS or _NOTFOUND,
- * and we still want to keep going
- */
- if (rc != APR_SUCCESS && rc != APR_NOTFOUND) {
- cs->state = CONN_STATE_LINGER;
+ lr->accept_func = ap_unixd_accept;
}
+ return APR_SUCCESS;
+}
+
+static apr_status_t push_timer2worker(timer_event_t* te)
+{
+ return ap_queue_push_timer(worker_queue, te);
+}
+
+/*
+ * Pre-condition: pfd->cs is neither in pollset nor timeout queue
+ * this function may only be called by the listener
+ */
+static apr_status_t push2worker(const apr_pollfd_t * pfd,
+ apr_pollset_t * pollset)
+{
+ listener_poll_type *pt = (listener_poll_type *) pfd->client_data;
+ event_conn_state_t *cs = (event_conn_state_t *) pt->baton;
+ apr_status_t rc;
+
rc = ap_queue_push(worker_queue, cs->pfd.desc.s, cs, cs->p);
if (rc != APR_SUCCESS) {
/* trash the connection; we couldn't queue the connected
@@ -760,50 +1103,200 @@ static apr_status_t push2worker(const apr_pollfd_t * pfd,
apr_bucket_alloc_destroy(cs->bucket_alloc);
apr_socket_close(cs->pfd.desc.s);
ap_log_error(APLOG_MARK, APLOG_CRIT, rc,
- ap_server_conf, "push2worker: ap_queue_push failed");
+ ap_server_conf, APLOGNO(00471) "push2worker: ap_queue_push failed");
apr_pool_clear(cs->p);
ap_push_pool(worker_queue_info, cs->p);
}
- return APR_SUCCESS;
+ return rc;
}
/* get_worker:
- * reserve a worker thread, block if all are currently busy.
- * this prevents the worker queue from overflowing and lets
- * other processes accept new connections in the mean time.
+ * If *have_idle_worker_p == 0, reserve a worker thread, and set
+ * *have_idle_worker_p = 1.
+ * If *have_idle_worker_p is already 1, will do nothing.
+ * If blocking == 1, block if all workers are currently busy.
+ * If no worker was available immediately, will set *all_busy to 1.
+ * XXX: If there are no workers, we should not block immediately but
+ * XXX: close all keep-alive connections first.
*/
-static int get_worker(int *have_idle_worker_p)
+static void get_worker(int *have_idle_worker_p, int blocking, int *all_busy)
{
apr_status_t rc;
- if (!*have_idle_worker_p) {
- rc = ap_queue_info_wait_for_idler(worker_queue_info);
-
- if (rc == APR_SUCCESS) {
- *have_idle_worker_p = 1;
- return 1;
- }
- else {
- if (!APR_STATUS_IS_EOF(rc)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf,
- "ap_queue_info_wait_for_idler failed. "
- "Attempting to shutdown process gracefully");
- signal_threads(ST_GRACEFUL);
- }
- return 0;
- }
- }
- else {
+ if (*have_idle_worker_p) {
/* already reserved a worker thread - must have hit a
* transient error on a previous pass
*/
- return 1;
+ return;
+ }
+
+ if (blocking)
+ rc = ap_queue_info_wait_for_idler(worker_queue_info, all_busy);
+ else
+ rc = ap_queue_info_try_get_idler(worker_queue_info);
+
+ if (rc == APR_SUCCESS) {
+ *have_idle_worker_p = 1;
+ }
+ else if (!blocking && rc == APR_EAGAIN) {
+ *all_busy = 1;
+ }
+ else if (!APR_STATUS_IS_EOF(rc)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf, APLOGNO(00472)
+ "ap_queue_info_wait_for_idler failed. "
+ "Attempting to shutdown process gracefully");
+ signal_threads(ST_GRACEFUL);
+ }
+}
+
+/* XXXXXX: Convert to skiplist or other better data structure
+ * (yes, this is VERY VERY VERY VERY BAD)
+ */
+
+/* Structures to reuse */
+static APR_RING_HEAD(timer_free_ring_t, timer_event_t) timer_free_ring;
+/* Active timers */
+static APR_RING_HEAD(timer_ring_t, timer_event_t) timer_ring;
+
+static apr_thread_mutex_t *g_timer_ring_mtx;
+
+static apr_status_t event_register_timed_callback(apr_time_t t,
+ ap_mpm_callback_fn_t *cbfn,
+ void *baton)
+{
+ int inserted = 0;
+ timer_event_t *ep;
+ timer_event_t *te;
+ /* oh yeah, and make locking smarter/fine grained. */
+ apr_thread_mutex_lock(g_timer_ring_mtx);
+
+ if (!APR_RING_EMPTY(&timer_free_ring, timer_event_t, link)) {
+ te = APR_RING_FIRST(&timer_free_ring);
+ APR_RING_REMOVE(te, link);
}
+ else {
+ /* XXXXX: lol, pool allocation without a context from any thread.Yeah. Right. MPMs Suck. */
+ te = ap_malloc(sizeof(timer_event_t));
+ APR_RING_ELEM_INIT(te, link);
+ }
+
+ te->cbfunc = cbfn;
+ te->baton = baton;
+ /* XXXXX: optimize */
+ te->when = t + apr_time_now();
+
+ /* Okay, insert sorted by when.. */
+ for (ep = APR_RING_FIRST(&timer_ring);
+ ep != APR_RING_SENTINEL(&timer_ring,
+ timer_event_t, link);
+ ep = APR_RING_NEXT(ep, link))
+ {
+ if (ep->when > te->when) {
+ inserted = 1;
+ APR_RING_INSERT_BEFORE(ep, te, link);
+ break;
+ }
+ }
+
+ if (!inserted) {
+ APR_RING_INSERT_TAIL(&timer_ring, te, timer_event_t, link);
+ }
+
+ apr_thread_mutex_unlock(g_timer_ring_mtx);
+
+ return APR_SUCCESS;
}
-static void *listener_thread(apr_thread_t * thd, void *dummy)
+/*
+ * Close socket and clean up if remote closed its end while we were in
+ * lingering close.
+ * Only to be called in the listener thread;
+ * Pre-condition: cs is in one of the linger queues and in the pollset
+ */
+static void process_lingering_close(event_conn_state_t *cs, const apr_pollfd_t *pfd)
+{
+ apr_socket_t *csd = ap_get_conn_socket(cs->c);
+ char dummybuf[2048];
+ apr_size_t nbytes;
+ apr_status_t rv;
+ struct timeout_queue *q;
+ q = (cs->pub.state == CONN_STATE_LINGER_SHORT) ? &short_linger_q : &linger_q;
+
+ /* socket is already in non-blocking state */
+ do {
+ nbytes = sizeof(dummybuf);
+ rv = apr_socket_recv(csd, dummybuf, &nbytes);
+ } while (rv == APR_SUCCESS);
+
+ if (!APR_STATUS_IS_EOF(rv)) {
+ return;
+ }
+
+ apr_thread_mutex_lock(timeout_mutex);
+ rv = apr_pollset_remove(event_pollset, pfd);
+ AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+
+ rv = apr_socket_close(csd);
+ AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+
+ TO_QUEUE_REMOVE(*q, cs);
+ apr_thread_mutex_unlock(timeout_mutex);
+ TO_QUEUE_ELEM_INIT(cs);
+
+ apr_pool_clear(cs->p);
+ ap_push_pool(worker_queue_info, cs->p);
+}
+
+/* call 'func' for all elements of 'q' with timeout less than 'timeout_time'.
+ * Pre-condition: timeout_mutex must already be locked
+ * Post-condition: timeout_mutex will be locked again
+ */
+static void process_timeout_queue(struct timeout_queue *q,
+ apr_time_t timeout_time,
+ int (*func)(event_conn_state_t *))
{
+ int count = 0;
+ event_conn_state_t *first, *cs, *last;
+ apr_status_t rv;
+ if (!q->count) {
+ return;
+ }
+ AP_DEBUG_ASSERT(!APR_RING_EMPTY(&q->head, event_conn_state_t, timeout_list));
+
+ cs = first = APR_RING_FIRST(&q->head);
+ while (cs != APR_RING_SENTINEL(&q->head, event_conn_state_t, timeout_list)
+ && cs->expiration_time < timeout_time) {
+ last = cs;
+ rv = apr_pollset_remove(event_pollset, &cs->pfd);
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_NOTFOUND(rv)) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, cs->c, APLOGNO(00473)
+ "apr_pollset_remove failed");
+ }
+ cs = APR_RING_NEXT(cs, timeout_list);
+ count++;
+ }
+ if (!count)
+ return;
+
+ APR_RING_UNSPLICE(first, last, timeout_list);
+ AP_DEBUG_ASSERT(q->count >= count);
+ q->count -= count;
+ apr_thread_mutex_unlock(timeout_mutex);
+ while (count) {
+ cs = APR_RING_NEXT(first, timeout_list);
+ TO_QUEUE_ELEM_INIT(first);
+ func(first);
+ first = cs;
+ count--;
+ }
+ apr_thread_mutex_lock(timeout_mutex);
+}
+
+static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
+{
+ timer_event_t *ep;
+ timer_event_t *te;
apr_status_t rc;
proc_info *ti = dummy;
int process_slot = ti->pid;
@@ -812,206 +1305,275 @@ static void *listener_thread(apr_thread_t * thd, void *dummy)
apr_pool_t *ptrans; /* Pool for per-transaction stuff */
ap_listen_rec *lr;
int have_idle_worker = 0;
- conn_state_t *cs;
+ event_conn_state_t *cs;
const apr_pollfd_t *out_pfd;
apr_int32_t num = 0;
- apr_time_t time_now = 0;
apr_interval_time_t timeout_interval;
- apr_time_t timeout_time;
+ apr_time_t timeout_time = 0, now, last_log;
listener_poll_type *pt;
+ int closed = 0, listeners_disabled = 0;
+ last_log = apr_time_now();
free(ti);
- /* We set this to force apr_pollset to wakeup if there hasn't been any IO
- * on any of its sockets. This allows sockets to have been added
- * when no other keepalive operations where going on.
- *
- * current value is 1 second
- */
- timeout_interval = 1000000;
-
/* the following times out events that are really close in the future
* to prevent extra poll calls
*
* current value is .1 second
*/
#define TIMEOUT_FUDGE_FACTOR 100000
+#define EVENT_FUDGE_FACTOR 10000
- /* POLLSET_SCALE_FACTOR * ap_threads_per_child sets the size of
- * the pollset. I've seen 15 connections per active worker thread
- * running SPECweb99.
- *
- * However, with the newer apr_pollset, this is the number of sockets that
- * we will return to any *one* call to poll(). Therefore, there is no
- * reason to make it more than ap_threads_per_child.
- */
-#define POLLSET_SCALE_FACTOR 1
-
- rc = apr_thread_mutex_create(&timeout_mutex, APR_THREAD_MUTEX_DEFAULT,
- tpool);
- if (rc != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf,
- "creation of the timeout mutex failed. Attempting to "
- "shutdown process gracefully");
- signal_threads(ST_GRACEFUL);
- return NULL;
- }
-
- APR_RING_INIT(&timeout_head, conn_state_t, timeout_list);
-
- /* Create the main pollset */
- rc = apr_pollset_create(&event_pollset,
- ap_threads_per_child * POLLSET_SCALE_FACTOR,
- tpool, APR_POLLSET_THREADSAFE);
+ rc = init_pollset(tpool);
if (rc != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf,
- "apr_pollset_create with Thread Safety failed. "
- "Attempting to shutdown process gracefully");
+ "failed to initialize pollset, "
+ "attempting to shutdown process gracefully");
signal_threads(ST_GRACEFUL);
return NULL;
}
- for (lr = ap_listeners; lr != NULL; lr = lr->next) {
- apr_pollfd_t pfd = { 0 };
- pt = apr_pcalloc(tpool, sizeof(*pt));
- pfd.desc_type = APR_POLL_SOCKET;
- pfd.desc.s = lr->sd;
- pfd.reqevents = APR_POLLIN;
-
- pt->type = PT_ACCEPT;
- pt->baton = lr;
-
- pfd.client_data = pt;
-
- apr_socket_opt_set(pfd.desc.s, APR_SO_NONBLOCK, 1);
- apr_pollset_add(event_pollset, &pfd);
- }
-
/* Unblock the signal used to wake this thread up, and set a handler for
* it.
*/
unblock_signal(LISTENER_SIGNAL);
apr_signal(LISTENER_SIGNAL, dummy_signal_handler);
- while (!listener_may_exit) {
+ for (;;) {
+ int workers_were_busy = 0;
+ if (listener_may_exit) {
+ close_listeners(process_slot, &closed);
+ if (terminate_mode == ST_UNGRACEFUL
+ || apr_atomic_read32(&connection_count) == 0)
+ break;
+ }
if (requests_this_child <= 0) {
check_infinite_requests();
}
- rc = apr_pollset_poll(event_pollset, timeout_interval, &num,
- &out_pfd);
+ now = apr_time_now();
+ if (APLOGtrace6(ap_server_conf)) {
+ /* trace log status every second */
+ if (now - last_log > apr_time_from_msec(1000)) {
+ last_log = now;
+ apr_thread_mutex_lock(timeout_mutex);
+ ap_log_error(APLOG_MARK, APLOG_TRACE6, 0, ap_server_conf,
+ "connections: %d (write-completion: %d "
+ "keep-alive: %d lingering: %d)",
+ connection_count, write_completion_q.count,
+ keepalive_q.count,
+ linger_q.count + short_linger_q.count);
+ apr_thread_mutex_unlock(timeout_mutex);
+ }
+ }
+ apr_thread_mutex_lock(g_timer_ring_mtx);
+ if (!APR_RING_EMPTY(&timer_ring, timer_event_t, link)) {
+ te = APR_RING_FIRST(&timer_ring);
+ if (te->when > now) {
+ timeout_interval = te->when - now;
+ }
+ else {
+ timeout_interval = 1;
+ }
+ }
+ else {
+ timeout_interval = apr_time_from_msec(100);
+ }
+ apr_thread_mutex_unlock(g_timer_ring_mtx);
+
+ rc = apr_pollset_poll(event_pollset, timeout_interval, &num, &out_pfd);
if (rc != APR_SUCCESS) {
if (APR_STATUS_IS_EINTR(rc)) {
continue;
}
if (!APR_STATUS_IS_TIMEUP(rc)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rc, ap_server_conf,
"apr_pollset_poll failed. Attempting to "
"shutdown process gracefully");
signal_threads(ST_GRACEFUL);
}
}
- if (listener_may_exit)
- break;
+ if (listener_may_exit) {
+ close_listeners(process_slot, &closed);
+ if (terminate_mode == ST_UNGRACEFUL
+ || apr_atomic_read32(&connection_count) == 0)
+ break;
+ }
- while (num && get_worker(&have_idle_worker)) {
+ now = apr_time_now();
+ apr_thread_mutex_lock(g_timer_ring_mtx);
+ for (ep = APR_RING_FIRST(&timer_ring);
+ ep != APR_RING_SENTINEL(&timer_ring,
+ timer_event_t, link);
+ ep = APR_RING_FIRST(&timer_ring))
+ {
+ if (ep->when < now + EVENT_FUDGE_FACTOR) {
+ APR_RING_REMOVE(ep, link);
+ push_timer2worker(ep);
+ }
+ else {
+ break;
+ }
+ }
+ apr_thread_mutex_unlock(g_timer_ring_mtx);
+
+ while (num) {
pt = (listener_poll_type *) out_pfd->client_data;
if (pt->type == PT_CSD) {
/* one of the sockets is readable */
- cs = (conn_state_t *) pt->baton;
- switch (cs->state) {
+ struct timeout_queue *remove_from_q = &write_completion_q;
+ int blocking = 1;
+ cs = (event_conn_state_t *) pt->baton;
+ switch (cs->pub.state) {
case CONN_STATE_CHECK_REQUEST_LINE_READABLE:
- cs->state = CONN_STATE_READ_REQUEST_LINE;
+ cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
+ remove_from_q = &keepalive_q;
+ /* don't wait for a worker for a keepalive request */
+ blocking = 0;
+ /* FALL THROUGH */
+ case CONN_STATE_WRITE_COMPLETION:
+ get_worker(&have_idle_worker, blocking,
+ &workers_were_busy);
+ apr_thread_mutex_lock(timeout_mutex);
+ TO_QUEUE_REMOVE(*remove_from_q, cs);
+ rc = apr_pollset_remove(event_pollset, &cs->pfd);
+
+ /*
+ * Some of the pollset backends, like KQueue or Epoll
+ * automagically remove the FD if the socket is closed,
+ * therefore, we can accept _SUCCESS or _NOTFOUND,
+ * and we still want to keep going
+ */
+ if (rc != APR_SUCCESS && !APR_STATUS_IS_NOTFOUND(rc)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf,
+ "pollset remove failed");
+ apr_thread_mutex_unlock(timeout_mutex);
+ start_lingering_close(cs);
+ break;
+ }
+
+ apr_thread_mutex_unlock(timeout_mutex);
+ TO_QUEUE_ELEM_INIT(cs);
+ /* If we didn't get a worker immediately for a keep-alive
+ * request, we close the connection, so that the client can
+ * re-connect to a different process.
+ */
+ if (!have_idle_worker) {
+ start_lingering_close(cs);
+ break;
+ }
+ rc = push2worker(out_pfd, event_pollset);
+ if (rc != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rc,
+ ap_server_conf, "push2worker failed");
+ }
+ else {
+ have_idle_worker = 0;
+ }
+ break;
+ case CONN_STATE_LINGER_NORMAL:
+ case CONN_STATE_LINGER_SHORT:
+ process_lingering_close(cs, out_pfd);
break;
default:
- ap_log_error(APLOG_MARK, APLOG_ERR, rc,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rc,
ap_server_conf,
"event_loop: unexpected state %d",
- cs->state);
- AP_DEBUG_ASSERT(0);
- }
-
- apr_thread_mutex_lock(timeout_mutex);
- APR_RING_REMOVE(cs, timeout_list);
- apr_thread_mutex_unlock(timeout_mutex);
-
- rc = push2worker(out_pfd, event_pollset);
- if (rc != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rc,
- ap_server_conf, "push2worker failed");
- }
- else {
- have_idle_worker = 0;
+ cs->pub.state);
+ ap_assert(0);
}
}
- else {
+ else if (pt->type == PT_ACCEPT) {
/* A Listener Socket is ready for an accept() */
- apr_pool_t *recycled_pool = NULL;
-
- lr = (ap_listen_rec *) pt->baton;
-
- ap_pop_pool(&recycled_pool, worker_queue_info);
-
- if (recycled_pool == NULL) {
- /* create a new transaction pool for each accepted socket */
- apr_allocator_t *allocator;
-
- apr_allocator_create(&allocator);
- apr_allocator_max_free_set(allocator,
- ap_max_mem_free);
- apr_pool_create_ex(&ptrans, pconf, NULL, allocator);
- apr_allocator_owner_set(allocator, ptrans);
- if (ptrans == NULL) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rc,
- ap_server_conf,
- "Failed to create transaction pool");
- signal_threads(ST_GRACEFUL);
- return NULL;
- }
+ if (workers_were_busy) {
+ if (!listeners_disabled)
+ disable_listensocks(process_slot);
+ listeners_disabled = 1;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+ "All workers busy, not accepting new conns"
+ "in this process");
}
- else {
- ptrans = recycled_pool;
+ else if (apr_atomic_read32(&connection_count) > threads_per_child
+ + ap_queue_info_get_idlers(worker_queue_info) *
+ worker_factor / WORKER_FACTOR_SCALE)
+ {
+ if (!listeners_disabled)
+ disable_listensocks(process_slot);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+ "Too many open connections (%u), "
+ "not accepting new conns in this process",
+ apr_atomic_read32(&connection_count));
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
+ "Idle workers: %u",
+ ap_queue_info_get_idlers(worker_queue_info));
+ listeners_disabled = 1;
+ }
+ else if (listeners_disabled) {
+ listeners_disabled = 0;
+ enable_listensocks(process_slot);
}
+ if (!listeners_disabled) {
+ lr = (ap_listen_rec *) pt->baton;
+ ap_pop_pool(&ptrans, worker_queue_info);
- apr_pool_tag(ptrans, "transaction");
+ if (ptrans == NULL) {
+ /* create a new transaction pool for each accepted socket */
+ apr_allocator_t *allocator;
+
+ apr_allocator_create(&allocator);
+ apr_allocator_max_free_set(allocator,
+ ap_max_mem_free);
+ apr_pool_create_ex(&ptrans, pconf, NULL, allocator);
+ apr_allocator_owner_set(allocator, ptrans);
+ if (ptrans == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rc,
+ ap_server_conf,
+ "Failed to create transaction pool");
+ signal_threads(ST_GRACEFUL);
+ return NULL;
+ }
+ }
+ apr_pool_tag(ptrans, "transaction");
- rc = lr->accept_func(&csd, lr, ptrans);
+ get_worker(&have_idle_worker, 1, &workers_were_busy);
+ rc = lr->accept_func(&csd, lr, ptrans);
- /* later we trash rv and rely on csd to indicate
- * success/failure
- */
- AP_DEBUG_ASSERT(rc == APR_SUCCESS || !csd);
+ /* later we trash rv and rely on csd to indicate
+ * success/failure
+ */
+ AP_DEBUG_ASSERT(rc == APR_SUCCESS || !csd);
- if (rc == APR_EGENERAL) {
- /* E[NM]FILE, ENOMEM, etc */
- resource_shortage = 1;
- signal_threads(ST_GRACEFUL);
- }
+ if (rc == APR_EGENERAL) {
+ /* E[NM]FILE, ENOMEM, etc */
+ resource_shortage = 1;
+ signal_threads(ST_GRACEFUL);
+ }
- if (csd != NULL) {
- rc = ap_queue_push(worker_queue, csd, NULL, ptrans);
- if (rc != APR_SUCCESS) {
- /* trash the connection; we couldn't queue the connected
- * socket to a worker
- */
- apr_socket_close(csd);
- ap_log_error(APLOG_MARK, APLOG_CRIT, rc,
- ap_server_conf,
- "ap_queue_push failed");
- apr_pool_clear(ptrans);
- ap_push_pool(worker_queue_info, ptrans);
+ if (csd != NULL) {
+ rc = ap_queue_push(worker_queue, csd, NULL, ptrans);
+ if (rc != APR_SUCCESS) {
+ /* trash the connection; we couldn't queue the connected
+ * socket to a worker
+ */
+ apr_socket_close(csd);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rc,
+ ap_server_conf,
+ "ap_queue_push failed");
+ apr_pool_clear(ptrans);
+ ap_push_pool(worker_queue_info, ptrans);
+ }
+ else {
+ have_idle_worker = 0;
+ }
}
else {
- have_idle_worker = 0;
+ apr_pool_clear(ptrans);
+ ap_push_pool(worker_queue_info, ptrans);
}
}
- else {
- apr_pool_clear(ptrans);
- ap_push_pool(worker_queue_info, ptrans);
- }
} /* if:else on pt->type */
out_pfd++;
num--;
@@ -1020,53 +1582,64 @@ static void *listener_thread(apr_thread_t * thd, void *dummy)
/* XXX possible optimization: stash the current time for use as
* r->request_time for new requests
*/
- time_now = apr_time_now();
-
- /* handle timed out sockets */
- apr_thread_mutex_lock(timeout_mutex);
-
- cs = APR_RING_FIRST(&timeout_head);
- timeout_time = time_now + TIMEOUT_FUDGE_FACTOR;
- while (!APR_RING_EMPTY(&timeout_head, conn_state_t, timeout_list)
- && cs->expiration_time < timeout_time) {
-
- cs->state = CONN_STATE_LINGER;
+ now = apr_time_now();
+ /* we only do this once per 0.1s (TIMEOUT_FUDGE_FACTOR) */
+ if (now > timeout_time) {
+ struct process_score *ps;
+ timeout_time = now + TIMEOUT_FUDGE_FACTOR;
- APR_RING_REMOVE(cs, timeout_list);
- apr_thread_mutex_unlock(timeout_mutex);
+ /* handle timed out sockets */
+ apr_thread_mutex_lock(timeout_mutex);
- if (!get_worker(&have_idle_worker)) {
- apr_thread_mutex_lock(timeout_mutex);
- APR_RING_INSERT_HEAD(&timeout_head, cs,
- conn_state_t, timeout_list);
- break;
+ /* Step 1: keepalive timeouts */
+ /* If all workers are busy, we kill older keep-alive connections so that they
+ * may connect to another process.
+ */
+ if (workers_were_busy && keepalive_q.count) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
+ "All workers are busy, will close %d keep-alive "
+ "connections",
+ keepalive_q.count);
+ process_timeout_queue(&keepalive_q,
+ timeout_time + ap_server_conf->keep_alive_timeout,
+ start_lingering_close);
}
-
- rc = push2worker(&cs->pfd, event_pollset);
-
- if (rc != APR_SUCCESS) {
- return NULL;
- /* XXX return NULL looks wrong - not an init failure
- * that bypasses all the cleanup outside the main loop
- * break seems more like it
- * need to evaluate seriousness of push2worker failures
- */
+ else {
+ process_timeout_queue(&keepalive_q, timeout_time,
+ start_lingering_close);
}
- have_idle_worker = 0;
- apr_thread_mutex_lock(timeout_mutex);
- cs = APR_RING_FIRST(&timeout_head);
- }
- apr_thread_mutex_unlock(timeout_mutex);
+ /* Step 2: write completion timeouts */
+ process_timeout_queue(&write_completion_q, timeout_time, start_lingering_close);
+ /* Step 3: (normal) lingering close completion timeouts */
+ process_timeout_queue(&linger_q, timeout_time, stop_lingering_close);
+ /* Step 4: (short) lingering close completion timeouts */
+ process_timeout_queue(&short_linger_q, timeout_time, stop_lingering_close);
+
+ ps = ap_get_scoreboard_process(process_slot);
+ ps->write_completion = write_completion_q.count;
+ ps->lingering_close = linger_q.count + short_linger_q.count;
+ ps->keep_alive = keepalive_q.count;
+ apr_thread_mutex_unlock(timeout_mutex);
+ ps->connections = apr_atomic_read32(&connection_count);
+ /* XXX: should count CONN_STATE_SUSPENDED and set ps->suspended */
+ }
+ if (listeners_disabled && !workers_were_busy &&
+ (int)apr_atomic_read32(&connection_count) <
+ ((int)ap_queue_info_get_idlers(worker_queue_info) - 1) *
+ worker_factor / WORKER_FACTOR_SCALE + threads_per_child)
+ {
+ listeners_disabled = 0;
+ enable_listensocks(process_slot);
+ }
+ /*
+ * XXX: do we need to set some timeout that re-enables the listensocks
+ * XXX: in case no other event occurs?
+ */
} /* listener main loop */
- ap_close_listeners();
+ close_listeners(process_slot, &closed);
ap_queue_term(worker_queue);
- dying = 1;
- ap_scoreboard_image->parent[process_slot].quiescing = 1;
-
- /* wake up the main thread */
- kill(ap_my_pid, SIGTERM);
apr_thread_exit(thd, APR_SUCCESS);
return NULL;
@@ -1085,15 +1658,17 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy)
int process_slot = ti->pid;
int thread_slot = ti->tid;
apr_socket_t *csd = NULL;
- conn_state_t *cs;
+ event_conn_state_t *cs;
apr_pool_t *ptrans; /* Pool for per-transaction stuff */
apr_status_t rv;
int is_idle = 0;
+ timer_event_t *te = NULL;
free(ti);
ap_scoreboard_image->servers[process_slot][thread_slot].pid = ap_my_pid;
- ap_scoreboard_image->servers[process_slot][thread_slot].generation = ap_my_generation;
+ ap_scoreboard_image->servers[process_slot][thread_slot].tid = apr_os_thread_current();
+ ap_scoreboard_image->servers[process_slot][thread_slot].generation = retained->my_generation;
ap_update_child_status_from_indexes(process_slot, thread_slot,
SERVER_STARTING, NULL);
@@ -1111,12 +1686,14 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy)
}
ap_update_child_status_from_indexes(process_slot, thread_slot,
- SERVER_READY, NULL);
+ dying ? SERVER_GRACEFUL : SERVER_READY, NULL);
worker_pop:
if (workers_may_exit) {
break;
}
- rv = ap_queue_pop(worker_queue, &csd, &cs, &ptrans);
+
+ te = NULL;
+ rv = ap_queue_pop_something(worker_queue, &csd, &cs, &ptrans, &te);
if (rv != APR_SUCCESS) {
/* We get APR_EOF during a graceful shutdown once all the
@@ -1146,17 +1723,28 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy)
}
continue;
}
- is_idle = 0;
- worker_sockets[thread_slot] = csd;
- rv = process_socket(ptrans, csd, cs, process_slot, thread_slot);
- if (!rv) {
- requests_this_child--;
+ if (te != NULL) {
+ te->cbfunc(te->baton);
+
+ {
+ apr_thread_mutex_lock(g_timer_ring_mtx);
+ APR_RING_INSERT_TAIL(&timer_free_ring, te, timer_event_t, link);
+ apr_thread_mutex_unlock(g_timer_ring_mtx);
+ }
+ }
+ else {
+ is_idle = 0;
+ worker_sockets[thread_slot] = csd;
+ rv = process_socket(thd, ptrans, csd, cs, process_slot, thread_slot);
+ if (!rv) {
+ requests_this_child--;
+ }
+ worker_sockets[thread_slot] = NULL;
}
- worker_sockets[thread_slot] = NULL;
}
ap_update_child_status_from_indexes(process_slot, thread_slot,
- (dying) ? SERVER_DEAD :
+ dying ? SERVER_DEAD :
SERVER_GRACEFUL,
(request_rec *) NULL);
@@ -1183,14 +1771,14 @@ static void create_listener_thread(thread_starter * ts)
proc_info *my_info;
apr_status_t rv;
- my_info = (proc_info *) malloc(sizeof(proc_info));
+ my_info = (proc_info *) ap_malloc(sizeof(proc_info));
my_info->pid = my_child_num;
my_info->tid = -1; /* listener thread doesn't have a thread slot */
my_info->sd = 0;
rv = apr_thread_create(&ts->listener, thread_attr, listener_thread,
my_info, pchild);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00474)
"apr_thread_create: unable to create listener thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
@@ -1217,32 +1805,64 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
int listener_started = 0;
int loops;
int prev_threads_created;
+ int max_recycled_pools = -1;
/* We must create the fd queues before we start up the listener
* and worker threads. */
worker_queue = apr_pcalloc(pchild, sizeof(*worker_queue));
- rv = ap_queue_init(worker_queue, ap_threads_per_child, pchild);
+ rv = ap_queue_init(worker_queue, threads_per_child, pchild);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
"ap_queue_init() failed");
clean_child_exit(APEXIT_CHILDFATAL);
}
+ if (ap_max_mem_free != APR_ALLOCATOR_MAX_FREE_UNLIMITED) {
+ /* If we want to conserve memory, let's not keep an unlimited number of
+ * pools & allocators.
+ * XXX: This should probably be a separate config directive
+ */
+ max_recycled_pools = threads_per_child * 3 / 4 ;
+ }
rv = ap_queue_info_create(&worker_queue_info, pchild,
- ap_threads_per_child);
+ threads_per_child, max_recycled_pools);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
"ap_queue_info_create() failed");
clean_child_exit(APEXIT_CHILDFATAL);
}
- worker_sockets = apr_pcalloc(pchild, ap_threads_per_child
+ /* Create the timeout mutex and main pollset before the listener
+ * thread starts.
+ */
+ rv = apr_thread_mutex_create(&timeout_mutex, APR_THREAD_MUTEX_DEFAULT,
+ pchild);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
+ "creation of the timeout mutex failed.");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ /* Create the main pollset */
+ rv = apr_pollset_create(&event_pollset,
+ threads_per_child, /* XXX don't we need more, to handle
+ * connections in K-A or lingering
+ * close?
+ */
+ pchild, APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
+ "apr_pollset_create with Thread Safety failed.");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ worker_sockets = apr_pcalloc(pchild, threads_per_child
* sizeof(apr_socket_t *));
loops = prev_threads_created = 0;
while (1) {
- /* ap_threads_per_child does not include the listener thread */
- for (i = 0; i < ap_threads_per_child; i++) {
+ /* threads_per_child does not include the listener thread */
+ for (i = 0; i < threads_per_child; i++) {
int status =
ap_scoreboard_image->servers[child_num_arg][i].status;
@@ -1250,12 +1870,7 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
continue;
}
- my_info = (proc_info *) malloc(sizeof(proc_info));
- if (my_info == NULL) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
- "malloc: out of memory");
- clean_child_exit(APEXIT_CHILDFATAL);
- }
+ my_info = (proc_info *) ap_malloc(sizeof(proc_info));
my_info->pid = my_child_num;
my_info->tid = i;
my_info->sd = 0;
@@ -1284,7 +1899,7 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
}
- if (start_thread_may_exit || threads_created == ap_threads_per_child) {
+ if (start_thread_may_exit || threads_created == threads_per_child) {
break;
}
/* wait for previous generation to clean up an entry */
@@ -1296,7 +1911,7 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
"child %" APR_PID_T_FMT " isn't taking over "
"slots very quickly (%d of %d)",
ap_my_pid, threads_created,
- ap_threads_per_child);
+ threads_per_child);
}
prev_threads_created = threads_created;
}
@@ -1333,36 +1948,30 @@ static void join_workers(apr_thread_t * listener, apr_thread_t ** threads)
*/
iter = 0;
- while (iter < 10 &&
-#ifdef HAVE_PTHREAD_KILL
- pthread_kill(*listener_os_thread, 0)
-#else
- kill(ap_my_pid, 0)
-#endif
- == 0) {
- /* listener not dead yet */
+ while (iter < 10 && !dying) {
+ /* listener has not stopped accepting yet */
apr_sleep(apr_time_make(0, 500000));
wakeup_listener();
++iter;
}
if (iter >= 10) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
- "the listener thread didn't exit");
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00475)
+ "the listener thread didn't stop accepting");
}
else {
rv = apr_thread_join(&thread_rv, listener);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00476)
"apr_thread_join: unable to join listener thread");
}
}
}
- for (i = 0; i < ap_threads_per_child; i++) {
+ for (i = 0; i < threads_per_child; i++) {
if (threads[i]) { /* if we ever created this thread */
rv = apr_thread_join(&thread_rv, threads[i]);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00477)
"apr_thread_join: unable to join worker "
"thread %d", i);
}
@@ -1380,7 +1989,7 @@ static void join_start_thread(apr_thread_t * start_thread_id)
*/
rv = apr_thread_join(&thread_rv, start_thread_id);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00478)
"apr_thread_join: unable to join the start " "thread");
}
}
@@ -1403,10 +2012,13 @@ static void child_main(int child_num_arg)
/*stuff to do before we switch id's, so we have permissions. */
ap_reopen_scoreboard(pchild, NULL, 0);
- if (unixd_setup_child()) {
+ if (ap_run_drop_privileges(pchild, ap_server_conf)) {
clean_child_exit(APEXIT_CHILDFATAL);
}
+ apr_thread_mutex_create(&g_timer_ring_mtx, APR_THREAD_MUTEX_DEFAULT, pchild);
+ APR_RING_INIT(&timer_free_ring, timer_event_t, link);
+ APR_RING_INIT(&timer_ring, timer_event_t, link);
ap_run_child_init(pchild, ap_server_conf);
/* done with init critical section */
@@ -1417,7 +2029,7 @@ static void child_main(int child_num_arg)
*/
rv = apr_setup_signal_thread();
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(00479)
"Couldn't initialize signal thread");
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -1435,16 +2047,8 @@ static void child_main(int child_num_arg)
/* clear the storage; we may not create all our threads immediately,
* and we want a 0 entry to indicate a thread which was not created
*/
- threads = (apr_thread_t **) calloc(1,
- sizeof(apr_thread_t *) *
- ap_threads_per_child);
- if (threads == NULL) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
- "malloc: out of memory");
- clean_child_exit(APEXIT_CHILDFATAL);
- }
-
- ts = (thread_starter *) apr_palloc(pchild, sizeof(*ts));
+ threads = ap_calloc(threads_per_child, sizeof(apr_thread_t *));
+ ts = apr_palloc(pchild, sizeof(*ts));
apr_threadattr_create(&thread_attr, pchild);
/* 0 means PTHREAD_CREATE_JOINABLE */
@@ -1462,7 +2066,7 @@ static void child_main(int child_num_arg)
rv = apr_thread_create(&start_thread_id, thread_attr, start_threads,
ts, pchild);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00480)
"apr_thread_create: unable to create worker thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
@@ -1501,13 +2105,13 @@ static void child_main(int child_num_arg)
else { /* !one_process */
/* remove SIGTERM from the set of blocked signals... if one of
* the other threads in the process needs to take us down
- * (e.g., for MaxRequestsPerChild) it will send us SIGTERM
+ * (e.g., for MaxConnectionsPerChild) it will send us SIGTERM
*/
unblock_signal(SIGTERM);
apr_signal(SIGTERM, dummy_signal_handler);
/* Watch for any messages from the parent over the POD */
while (1) {
- rv = ap_mpm_pod_check(pod);
+ rv = ap_event_pod_check(pod);
if (rv == AP_NORESTART) {
/* see if termination was triggered while we slept */
switch (terminate_mode) {
@@ -1549,24 +2153,27 @@ static int make_child(server_rec * s, int slot)
{
int pid;
- if (slot + 1 > ap_max_daemons_limit) {
- ap_max_daemons_limit = slot + 1;
+ if (slot + 1 > retained->max_daemons_limit) {
+ retained->max_daemons_limit = slot + 1;
}
if (one_process) {
set_signals();
- ap_scoreboard_image->parent[slot].pid = getpid();
+ event_note_child_started(slot, getpid());
child_main(slot);
+ /* NOTREACHED */
}
if ((pid = fork()) == -1) {
- ap_log_error(APLOG_MARK, APLOG_ERR, errno, s,
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, APLOGNO(00481)
"fork: Unable to fork new process");
- /* fork didn't succeed. Fix the scoreboard or else
- * it will say SERVER_STARTING forever and ever
+ /* fork didn't succeed. There's no need to touch the scoreboard;
+ * if we were trying to replace a failed child process, then
+ * server_main_loop() marked its workers SERVER_DEAD, and if
+ * we were trying to replace a child process that exited normally,
+ * its worker_thread()s left SERVER_DEAD or SERVER_GRACEFUL behind.
*/
- ap_update_child_status_from_indexes(slot, 0, SERVER_DEAD, NULL);
/* In case system resources are maxxed out, we don't want
Apache running away with the CPU trying to fork over and
@@ -1584,20 +2191,27 @@ static int make_child(server_rec * s, int slot)
int status = bindprocessor(BINDPROCESS, (int) getpid(),
PROCESSOR_CLASS_ANY);
if (status != OK)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno,
- ap_server_conf,
- "processor unbind failed %d", status);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, errno,
+ ap_server_conf, APLOGNO(00482)
+ "processor unbind failed");
#endif
RAISE_SIGSTOP(MAKE_CHILD);
apr_signal(SIGTERM, just_die);
child_main(slot);
-
- clean_child_exit(0);
+ /* NOTREACHED */
}
/* else */
+ if (ap_scoreboard_image->parent[slot].pid != 0) {
+ /* This new child process is squatting on the scoreboard
+ * entry owned by an exiting child process, which cannot
+ * exit until all active requests complete.
+ */
+ event_note_child_lost_slot(slot, pid);
+ }
ap_scoreboard_image->parent[slot].quiescing = 0;
- ap_scoreboard_image->parent[slot].pid = pid;
+ ap_scoreboard_image->parent[slot].not_accepting = 0;
+ event_note_child_started(slot, pid);
return 0;
}
@@ -1617,19 +2231,6 @@ static void startup_children(int number_to_start)
}
}
-
-/*
- * idle_spawn_rate is the number of children that will be spawned on the
- * next maintenance cycle if there aren't enough idle servers. It is
- * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
- * without the need to spawn.
- */
-static int idle_spawn_rate = 1;
-#ifndef MAX_SPAWN_RATE
-#define MAX_SPAWN_RATE (32)
-#endif
-static int hold_off_on_exponential_spawning;
-
static void perform_idle_server_maintenance(void)
{
int i, j;
@@ -1652,17 +2253,21 @@ static void perform_idle_server_maintenance(void)
for (i = 0; i < ap_daemons_limit; ++i) {
/* Initialization to satisfy the compiler. It doesn't know
- * that ap_threads_per_child is always > 0 */
+ * that threads_per_child is always > 0 */
int status = SERVER_DEAD;
int any_dying_threads = 0;
int any_dead_threads = 0;
int all_dead_threads = 1;
- if (i >= ap_max_daemons_limit
- && totally_free_length == idle_spawn_rate)
+ if (i >= retained->max_daemons_limit
+ && totally_free_length == retained->idle_spawn_rate)
+ /* short cut if all active processes have been examined and
+ * enough empty scoreboard slots have been found
+ */
+
break;
ps = &ap_scoreboard_image->parent[i];
- for (j = 0; j < ap_threads_per_child; j++) {
+ for (j = 0; j < threads_per_child; j++) {
ws = &ap_scoreboard_image->servers[i][j];
status = ws->status;
@@ -1681,8 +2286,9 @@ static void perform_idle_server_maintenance(void)
*/
if (ps->pid != 0) { /* XXX just set all_dead_threads in outer
for loop if no pid? not much else matters */
- if (status <= SERVER_READY &&
- !ps->quiescing && ps->generation == ap_my_generation) {
+ if (status <= SERVER_READY && !ps->quiescing && !ps->not_accepting
+ && ps->generation == retained->my_generation)
+ {
++idle_thread_count;
}
if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
@@ -1691,7 +2297,7 @@ static void perform_idle_server_maintenance(void)
}
}
if (any_dead_threads
- && totally_free_length < idle_spawn_rate
+ && totally_free_length < retained->idle_spawn_rate
&& free_length < MAX_SPAWN_RATE
&& (!ps->pid /* no process in the slot */
|| ps->quiescing)) { /* or at least one is going away */
@@ -1720,12 +2326,12 @@ static void perform_idle_server_maintenance(void)
}
}
- if (sick_child_detected) {
+ if (retained->sick_child_detected) {
if (active_thread_count > 0) {
/* some child processes appear to be working. don't kill the
* whole server.
*/
- sick_child_detected = 0;
+ retained->sick_child_detected = 0;
}
else {
/* looks like a basket case. give up.
@@ -1733,7 +2339,7 @@ static void perform_idle_server_maintenance(void)
shutdown_pending = 1;
child_fatal = 1;
ap_log_error(APLOG_MARK, APLOG_ALERT, 0,
- ap_server_conf,
+ ap_server_conf, APLOGNO(00483)
"No active workers found..."
" Apache is exiting!");
/* the child already logged the failure details */
@@ -1741,35 +2347,39 @@ static void perform_idle_server_maintenance(void)
}
}
- ap_max_daemons_limit = last_non_dead + 1;
+ retained->max_daemons_limit = last_non_dead + 1;
if (idle_thread_count > max_spare_threads) {
/* Kill off one child */
- ap_mpm_pod_signal(pod, TRUE);
- idle_spawn_rate = 1;
+ ap_event_pod_signal(pod, TRUE);
+ retained->idle_spawn_rate = 1;
}
else if (idle_thread_count < min_spare_threads) {
/* terminate the free list */
- if (free_length == 0) {
- /* only report this condition once */
- static int reported = 0;
-
- if (!reported) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0,
- ap_server_conf,
- "server reached MaxClients setting, consider"
- " raising the MaxClients setting");
- reported = 1;
+ if (free_length == 0) { /* scoreboard is full, can't fork */
+
+ if (active_thread_count >= ap_daemons_limit * threads_per_child) {
+ if (!retained->maxclients_reported) {
+ /* only report this condition once */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00484)
+ "server reached MaxRequestWorkers setting, "
+ "consider raising the MaxRequestWorkers "
+ "setting");
+ retained->maxclients_reported = 1;
+ }
}
- idle_spawn_rate = 1;
+ else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00485)
+ "scoreboard is full, not at MaxRequestWorkers");
+ }
+ retained->idle_spawn_rate = 1;
}
else {
- if (free_length > idle_spawn_rate) {
- free_length = idle_spawn_rate;
+ if (free_length > retained->idle_spawn_rate) {
+ free_length = retained->idle_spawn_rate;
}
- if (idle_spawn_rate >= 8) {
- ap_log_error(APLOG_MARK, APLOG_INFO, 0,
- ap_server_conf,
+ if (retained->idle_spawn_rate >= 8) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00486)
"server seems busy, (you may need "
"to increase StartServers, ThreadsPerChild "
"or Min/MaxSpareThreads), "
@@ -1783,21 +2393,22 @@ static void perform_idle_server_maintenance(void)
/* 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;
+ if (retained->hold_off_on_exponential_spawning) {
+ --retained->hold_off_on_exponential_spawning;
}
- else if (idle_spawn_rate < MAX_SPAWN_RATE) {
- idle_spawn_rate *= 2;
+ else if (retained->idle_spawn_rate < MAX_SPAWN_RATE) {
+ retained->idle_spawn_rate *= 2;
}
}
}
else {
- idle_spawn_rate = 1;
+ retained->idle_spawn_rate = 1;
}
}
static void server_main_loop(int remaining_children_to_start)
{
+ ap_generation_t old_gen;
int child_slot;
apr_exit_why_e exitwhy;
int status, processed_status;
@@ -1805,34 +2416,50 @@ static void server_main_loop(int remaining_children_to_start)
int i;
while (!restart_pending && !shutdown_pending) {
- ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);
+ ap_wait_or_timeout(&exitwhy, &status, &pid, pconf, ap_server_conf);
if (pid.pid != -1) {
processed_status = ap_process_child_status(&pid, exitwhy, status);
+ child_slot = ap_find_child_by_pid(&pid);
if (processed_status == APEXIT_CHILDFATAL) {
- shutdown_pending = 1;
- child_fatal = 1;
- return;
+ /* fix race condition found in PR 39311
+ * A child created at the same time as a graceful happens
+ * can find the lock missing and create a fatal error.
+ * It is not fatal for the last generation to be in this state.
+ */
+ if (child_slot < 0
+ || ap_get_scoreboard_process(child_slot)->generation
+ == retained->my_generation) {
+ shutdown_pending = 1;
+ child_fatal = 1;
+ return;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, APLOGNO(00487)
+ "Ignoring fatal error in child of previous "
+ "generation (pid %ld).",
+ (long)pid.pid);
+ retained->sick_child_detected = 1;
+ }
}
else if (processed_status == APEXIT_CHILDSICK) {
/* tell perform_idle_server_maintenance to check into this
* on the next timer pop
*/
- sick_child_detected = 1;
+ retained->sick_child_detected = 1;
}
/* non-fatal death... note that it's gone in the scoreboard. */
- child_slot = find_child_by_pid(&pid);
if (child_slot >= 0) {
- for (i = 0; i < ap_threads_per_child; i++)
+ for (i = 0; i < threads_per_child; i++)
ap_update_child_status_from_indexes(child_slot, i,
SERVER_DEAD,
(request_rec *) NULL);
- ap_scoreboard_image->parent[child_slot].pid = 0;
+ event_note_child_killed(child_slot, 0, 0);
ap_scoreboard_image->parent[child_slot].quiescing = 0;
if (processed_status == APEXIT_CHILDSICK) {
/* resource shortage, minimize the fork rate */
- idle_spawn_rate = 1;
+ retained->idle_spawn_rate = 1;
}
else if (remaining_children_to_start
&& child_slot < ap_daemons_limit) {
@@ -1842,6 +2469,11 @@ static void server_main_loop(int remaining_children_to_start)
make_child(ap_server_conf, child_slot);
--remaining_children_to_start;
}
+ }
+ else if (ap_unregister_extra_mpm_process(pid.pid, &old_gen) == 1) {
+
+ event_note_child_killed(-1, /* already out of the scoreboard */
+ pid.pid, old_gen);
#if APR_HAS_OTHER_CHILD
}
else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH,
@@ -1849,12 +2481,12 @@ static void server_main_loop(int remaining_children_to_start)
/* handled */
#endif
}
- else if (is_graceful) {
+ else if (retained->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_WARNING, 0,
- ap_server_conf,
+ ap_server_conf, APLOGNO(00488)
"long lost child came home! (pid %ld)",
(long) pid.pid);
}
@@ -1883,93 +2515,79 @@ static void server_main_loop(int remaining_children_to_start)
}
}
-int ap_mpm_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
+static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
{
int remaining_children_to_start;
ap_log_pid(pconf, ap_pid_fname);
- first_server_limit = server_limit;
- first_thread_limit = thread_limit;
-
- if (changed_limit_at_restart) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
- "WARNING: Attempt to change ServerLimit or ThreadLimit "
- "ignored during restart");
- changed_limit_at_restart = 0;
- }
-
- if (!is_graceful) {
+ if (!retained->is_graceful) {
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
mpm_state = AP_MPMQ_STOPPING;
- return 1;
+ return DONE;
}
/* fix the generation number in the global score; we just got a new,
* cleared scoreboard
*/
- ap_scoreboard_image->global->running_generation = ap_my_generation;
+ ap_scoreboard_image->global->running_generation = retained->my_generation;
}
+ restart_pending = shutdown_pending = 0;
set_signals();
/* Don't thrash... */
- if (max_spare_threads < min_spare_threads + ap_threads_per_child)
- max_spare_threads = min_spare_threads + ap_threads_per_child;
+ if (max_spare_threads < min_spare_threads + threads_per_child)
+ max_spare_threads = min_spare_threads + threads_per_child;
/* If we're doing a graceful_restart then we're going to see a lot
* of children exiting immediately when we get into the main loop
* below (because we just sent them AP_SIG_GRACEFUL). 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
+ * rapidly... and for each one that exits we may start a new one, until
+ * there are at least min_spare_threads idle threads, counting across
+ * all children. But we may be permitted to start more children 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) {
+ if (!retained->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;
+ retained->hold_off_on_exponential_spawning = 10;
}
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00489)
"%s configured -- resuming normal operations",
ap_get_server_description());
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00490)
"Server built: %s", ap_get_server_built());
+ ap_log_command_line(plog, s);
- restart_pending = shutdown_pending = 0;
mpm_state = AP_MPMQ_RUNNING;
server_main_loop(remaining_children_to_start);
mpm_state = AP_MPMQ_STOPPING;
- if (shutdown_pending && !is_graceful) {
+ if (shutdown_pending && !retained->is_graceful) {
/* Time to shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
- ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
- ap_reclaim_child_processes(1); /* Start with SIGTERM */
+ ap_event_pod_killpg(pod, ap_daemons_limit, FALSE);
+ ap_reclaim_child_processes(1, /* Start with SIGTERM */
+ event_note_child_killed);
if (!child_fatal) {
/* cleanup pid file on normal shutdown */
- const char *pidfile = NULL;
- pidfile = ap_server_root_relative(pconf, ap_pid_fname);
- if (pidfile != NULL && unlink(pidfile) == 0)
- ap_log_error(APLOG_MARK, APLOG_INFO, 0,
- ap_server_conf,
- "removed PID file %s (pid=%ld)",
- pidfile, (long) getpid());
-
+ ap_remove_pid(pconf, ap_pid_fname);
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0,
- ap_server_conf, "caught SIGTERM, shutting down");
+ ap_server_conf, APLOGNO(00491) "caught SIGTERM, shutting down");
}
- return 1;
+ return DONE;
} else if (shutdown_pending) {
/* Time to gracefully shut down:
* Kill child processes, tell them to call child_exit, etc...
@@ -1980,20 +2598,13 @@ int ap_mpm_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
/* Close our listeners, and then ask our children to do same */
ap_close_listeners();
- ap_mpm_pod_killpg(pod, ap_daemons_limit, TRUE);
- ap_relieve_child_processes();
+ ap_event_pod_killpg(pod, ap_daemons_limit, TRUE);
+ ap_relieve_child_processes(event_note_child_killed);
if (!child_fatal) {
/* cleanup pid file on normal shutdown */
- const char *pidfile = NULL;
- pidfile = ap_server_root_relative (pconf, ap_pid_fname);
- if ( pidfile != NULL && unlink(pidfile) == 0)
- ap_log_error(APLOG_MARK, APLOG_INFO, 0,
- ap_server_conf,
- "removed PID file %s (pid=%ld)",
- pidfile, (long)getpid());
-
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_remove_pid(pconf, ap_pid_fname);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00492)
"caught " AP_SIG_GRACEFUL_STOP_STRING
", shutting down gracefully");
}
@@ -2010,7 +2621,7 @@ int ap_mpm_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
apr_sleep(apr_time_from_sec(1));
/* Relieve any children which have now exited */
- ap_relieve_child_processes();
+ ap_relieve_child_processes(event_note_child_killed);
active_children = 0;
for (index = 0; index < ap_daemons_limit; ++index) {
@@ -2027,10 +2638,10 @@ int ap_mpm_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
* way, try and make sure that all of our processes are
* really dead.
*/
- ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
- ap_reclaim_child_processes(1);
+ ap_event_pod_killpg(pod, ap_daemons_limit, FALSE);
+ ap_reclaim_child_processes(1, event_note_child_killed);
- return 1;
+ return DONE;
}
/* we've been told to restart */
@@ -2038,22 +2649,22 @@ int ap_mpm_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
if (one_process) {
/* not worth thinking about */
- return 1;
+ return DONE;
}
/* advance to the next generation */
/* XXX: we really need to make sure this new generation number isn't in
* use by any of the children.
*/
- ++ap_my_generation;
- ap_scoreboard_image->global->running_generation = ap_my_generation;
+ ++retained->my_generation;
+ ap_scoreboard_image->global->running_generation = retained->my_generation;
- if (is_graceful) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ if (retained->is_graceful) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00493)
AP_SIG_GRACEFUL_STRING
" received. Doing graceful restart");
/* wake up the children...time to die. But we'll have more soon */
- ap_mpm_pod_killpg(pod, ap_daemons_limit, TRUE);
+ ap_event_pod_killpg(pod, ap_daemons_limit, TRUE);
/* This is mostly for debugging... so that we know what is still
@@ -2066,91 +2677,62 @@ int ap_mpm_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
* and a SIGHUP, we may as well use the same signal, because some user
* pthreads are stealing signals from us left and right.
*/
- ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
+ ap_event_pod_killpg(pod, ap_daemons_limit, FALSE);
- ap_reclaim_child_processes(1); /* Start with SIGTERM */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_reclaim_child_processes(1, /* Start with SIGTERM */
+ event_note_child_killed);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00494)
"SIGHUP received. Attempting to restart");
}
- return 0;
+ return OK;
}
/* This really should be a post_config hook, but the error log is already
* redirected by that point, so we need to do this in the open_logs phase.
*/
-static int worker_open_logs(apr_pool_t * p, apr_pool_t * plog,
- apr_pool_t * ptemp, server_rec * s)
+static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
+ apr_pool_t * ptemp, server_rec * s)
{
+ int startup = 0;
+ int level_flags = 0;
apr_status_t rv;
pconf = p;
- ap_server_conf = s;
+
+ /* the reverse of pre_config, we want this only the first time around */
+ if (retained->module_loads == 1) {
+ startup = 1;
+ level_flags |= APLOG_STARTUP;
+ }
if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
- ap_log_error(APLOG_MARK, APLOG_ALERT | APLOG_STARTUP, 0,
- NULL, "no listening sockets available, shutting down");
+ ap_log_error(APLOG_MARK, APLOG_ALERT | level_flags, 0,
+ (startup ? NULL : s),
+ "no listening sockets available, shutting down");
return DONE;
}
if (!one_process) {
- if ((rv = ap_mpm_pod_open(pconf, &pod))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_STARTUP, rv, NULL,
- "Could not open pipe-of-death.");
+ if ((rv = ap_event_pod_open(pconf, &pod))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not open pipe-of-death");
return DONE;
}
}
return OK;
}
-static int worker_pre_config(apr_pool_t * pconf, apr_pool_t * plog,
- apr_pool_t * ptemp)
+static int event_pre_config(apr_pool_t * pconf, apr_pool_t * plog,
+ apr_pool_t * ptemp)
{
- static int restart_num = 0;
int no_detach, debug, foreground;
- ap_directive_t *pdir;
- ap_directive_t *max_clients = NULL;
apr_status_t rv;
+ const char *userdata_key = "mpm_event_module";
mpm_state = AP_MPMQ_STARTING;
- /* make sure that "ThreadsPerChild" gets set before "MaxClients" */
- for (pdir = ap_conftree; pdir != NULL; pdir = pdir->next) {
- if (strncasecmp(pdir->directive, "ThreadsPerChild", 15) == 0) {
- if (!max_clients) {
- /* we're in the clear, got ThreadsPerChild first */
- break;
- }
- else {
- /* now to swap the data */
- ap_directive_t temp;
-
- temp.directive = pdir->directive;
- temp.args = pdir->args;
- /* Make sure you don't change 'next', or you may get loops! */
- /* XXX: first_child, parent, and data can never be set
- * for these directives, right? -aaron */
- temp.filename = pdir->filename;
- temp.line_num = pdir->line_num;
-
- pdir->directive = max_clients->directive;
- pdir->args = max_clients->args;
- pdir->filename = max_clients->filename;
- pdir->line_num = max_clients->line_num;
-
- max_clients->directive = temp.directive;
- max_clients->args = temp.args;
- max_clients->filename = temp.filename;
- max_clients->line_num = temp.line_num;
- break;
- }
- }
- else if (!max_clients
- && strncasecmp(pdir->directive, "MaxClients", 10) == 0) {
- max_clients = pdir;
- }
- }
-
debug = ap_exists_config_define("DEBUG");
if (debug) {
@@ -2164,64 +2746,309 @@ static int worker_pre_config(apr_pool_t * pconf, apr_pool_t * plog,
}
/* sigh, want this only the second time around */
- if (restart_num++ == 1) {
- is_graceful = 0;
+ retained = ap_retained_data_get(userdata_key);
+ if (!retained) {
+ retained = ap_retained_data_create(userdata_key, sizeof(*retained));
+ retained->max_daemons_limit = -1;
+ retained->idle_spawn_rate = 1;
+ }
+ ++retained->module_loads;
+ if (retained->module_loads == 2) {
rv = apr_pollset_create(&event_pollset, 1, plog,
- APR_POLLSET_THREADSAFE);
+ APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, APLOGNO(00495)
"Couldn't create a Thread Safe Pollset. "
- "Is it supported on your platform?");
+ "Is it supported on your platform?"
+ "Also check system or user limits!");
return HTTP_INTERNAL_SERVER_ERROR;
}
apr_pollset_destroy(event_pollset);
if (!one_process && !foreground) {
+ /* before we detach, setup crash handlers to log to errorlog */
+ ap_fatal_signal_setup(ap_server_conf, pconf);
rv = apr_proc_detach(no_detach ? APR_PROC_DETACH_FOREGROUND
: APR_PROC_DETACH_DAEMONIZE);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, APLOGNO(00496)
"apr_proc_detach failed");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
- parent_pid = ap_my_pid = getpid();
}
- unixd_pre_config(ptemp);
+ parent_pid = ap_my_pid = getpid();
+
ap_listen_pre_config();
ap_daemons_to_start = DEFAULT_START_DAEMON;
min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
max_spare_threads = DEFAULT_MAX_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
+ server_limit = DEFAULT_SERVER_LIMIT;
+ thread_limit = DEFAULT_THREAD_LIMIT;
ap_daemons_limit = server_limit;
- ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
- ap_pid_fname = DEFAULT_PIDLOG;
- ap_lock_fname = DEFAULT_LOCKFILE;
- ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
+ threads_per_child = DEFAULT_THREADS_PER_CHILD;
+ max_workers = ap_daemons_limit * threads_per_child;
ap_extended_status = 0;
-#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
- ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-#endif
- apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+ return OK;
+}
+
+static int event_check_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ int startup = 0;
+
+ /* the reverse of pre_config, we want this only the first time around */
+ if (retained->module_loads == 1) {
+ startup = 1;
+ }
+
+ if (server_limit > MAX_SERVER_LIMIT) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00497)
+ "WARNING: ServerLimit of %d exceeds compile-time "
+ "limit of", server_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d servers, decreasing to %d.",
+ MAX_SERVER_LIMIT, MAX_SERVER_LIMIT);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00498)
+ "ServerLimit of %d exceeds compile-time limit "
+ "of %d, decreasing to match",
+ server_limit, MAX_SERVER_LIMIT);
+ }
+ server_limit = MAX_SERVER_LIMIT;
+ }
+ else if (server_limit < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00499)
+ "WARNING: ServerLimit of %d not allowed, "
+ "increasing to 1.", server_limit);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00500)
+ "ServerLimit of %d not allowed, increasing to 1",
+ server_limit);
+ }
+ server_limit = 1;
+ }
+
+ /* you cannot change ServerLimit across a restart; ignore
+ * any such attempts
+ */
+ if (!retained->first_server_limit) {
+ retained->first_server_limit = server_limit;
+ }
+ else if (server_limit != retained->first_server_limit) {
+ /* don't need a startup console version here */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00501)
+ "changing ServerLimit to %d from original value of %d "
+ "not allowed during restart",
+ server_limit, retained->first_server_limit);
+ server_limit = retained->first_server_limit;
+ }
+
+ if (thread_limit > MAX_THREAD_LIMIT) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00502)
+ "WARNING: ThreadLimit of %d exceeds compile-time "
+ "limit of", thread_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d threads, decreasing to %d.",
+ MAX_THREAD_LIMIT, MAX_THREAD_LIMIT);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00503)
+ "ThreadLimit of %d exceeds compile-time limit "
+ "of %d, decreasing to match",
+ thread_limit, MAX_THREAD_LIMIT);
+ }
+ thread_limit = MAX_THREAD_LIMIT;
+ }
+ else if (thread_limit < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00504)
+ "WARNING: ThreadLimit of %d not allowed, "
+ "increasing to 1.", thread_limit);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00505)
+ "ThreadLimit of %d not allowed, increasing to 1",
+ thread_limit);
+ }
+ thread_limit = 1;
+ }
+
+ /* you cannot change ThreadLimit across a restart; ignore
+ * any such attempts
+ */
+ if (!retained->first_thread_limit) {
+ retained->first_thread_limit = thread_limit;
+ }
+ else if (thread_limit != retained->first_thread_limit) {
+ /* don't need a startup console version here */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00506)
+ "changing ThreadLimit to %d from original value of %d "
+ "not allowed during restart",
+ thread_limit, retained->first_thread_limit);
+ thread_limit = retained->first_thread_limit;
+ }
+
+ if (threads_per_child > thread_limit) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00507)
+ "WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
+ "of", threads_per_child);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d threads, decreasing to %d.",
+ thread_limit, thread_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " To increase, please see the ThreadLimit "
+ "directive.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00508)
+ "ThreadsPerChild of %d exceeds ThreadLimit "
+ "of %d, decreasing to match",
+ threads_per_child, thread_limit);
+ }
+ threads_per_child = thread_limit;
+ }
+ else if (threads_per_child < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00509)
+ "WARNING: ThreadsPerChild of %d not allowed, "
+ "increasing to 1.", threads_per_child);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00510)
+ "ThreadsPerChild of %d not allowed, increasing to 1",
+ threads_per_child);
+ }
+ threads_per_child = 1;
+ }
+
+ if (max_workers < threads_per_child) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00511)
+ "WARNING: MaxRequestWorkers of %d is less than "
+ "ThreadsPerChild of", max_workers);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d, increasing to %d. MaxRequestWorkers must be at "
+ "least as large",
+ threads_per_child, threads_per_child);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " as the number of threads in a single server.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00512)
+ "MaxRequestWorkers of %d is less than ThreadsPerChild "
+ "of %d, increasing to match",
+ max_workers, threads_per_child);
+ }
+ max_workers = threads_per_child;
+ }
+
+ ap_daemons_limit = max_workers / threads_per_child;
+
+ if (max_workers % threads_per_child) {
+ int tmp_max_workers = ap_daemons_limit * threads_per_child;
+
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00513)
+ "WARNING: MaxRequestWorkers of %d is not an integer "
+ "multiple of", max_workers);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " ThreadsPerChild of %d, decreasing to nearest "
+ "multiple %d,", threads_per_child,
+ tmp_max_workers);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " for a maximum of %d servers.",
+ ap_daemons_limit);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00514)
+ "MaxRequestWorkers of %d is not an integer multiple "
+ "of ThreadsPerChild of %d, decreasing to nearest "
+ "multiple %d", max_workers, threads_per_child,
+ tmp_max_workers);
+ }
+ max_workers = tmp_max_workers;
+ }
+
+ if (ap_daemons_limit > server_limit) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00515)
+ "WARNING: MaxRequestWorkers of %d would require %d "
+ "servers and ", max_workers, ap_daemons_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " would exceed ServerLimit of %d, decreasing to %d.",
+ server_limit, server_limit * threads_per_child);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " To increase, please see the ServerLimit "
+ "directive.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00516)
+ "MaxRequestWorkers of %d would require %d servers and "
+ "exceed ServerLimit of %d, decreasing to %d",
+ max_workers, ap_daemons_limit, server_limit,
+ server_limit * threads_per_child);
+ }
+ ap_daemons_limit = server_limit;
+ }
+
+ /* ap_daemons_to_start > ap_daemons_limit checked in ap_mpm_run() */
+ if (ap_daemons_to_start < 0) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00517)
+ "WARNING: StartServers of %d not allowed, "
+ "increasing to 1.", ap_daemons_to_start);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00518)
+ "StartServers of %d not allowed, increasing to 1",
+ ap_daemons_to_start);
+ }
+ ap_daemons_to_start = 1;
+ }
+
+ if (min_spare_threads < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00519)
+ "WARNING: MinSpareThreads of %d not allowed, "
+ "increasing to 1", min_spare_threads);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " to avoid almost certain server failure.");
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " Please read the documentation.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00520)
+ "MinSpareThreads of %d not allowed, increasing to 1",
+ min_spare_threads);
+ }
+ min_spare_threads = 1;
+ }
+
+ /* max_spare_threads < min_spare_threads + threads_per_child
+ * checked in ap_mpm_run()
+ */
return OK;
}
static void event_hooks(apr_pool_t * p)
{
- /* The worker open_logs phase must run before the core's, or stderr
+ /* Our open_logs hook function must run before the core's, or stderr
* will be redirected to a file, and the messages won't print to the
* console.
*/
static const char *const aszSucc[] = { "core.c", NULL };
one_process = 0;
- ap_hook_open_logs(worker_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
+ ap_hook_open_logs(event_open_logs, NULL, aszSucc, APR_HOOK_REALLY_FIRST);
/* we need to set the MPM state before other pre-config hooks use MPM query
* to retrieve it, so register as REALLY_FIRST
*/
- ap_hook_pre_config(worker_pre_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
+ ap_hook_pre_config(event_pre_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
+ ap_hook_check_config(event_check_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm(event_run, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_query(event_query, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_register_timed_callback(event_register_timed_callback, NULL, NULL,
+ APR_HOOK_MIDDLE);
+ ap_hook_mpm_get_name(event_get_name, NULL, NULL, APR_HOOK_MIDDLE);
}
static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy,
@@ -2245,16 +3072,6 @@ static const char *set_min_spare_threads(cmd_parms * cmd, void *dummy,
}
min_spare_threads = atoi(arg);
- if (min_spare_threads <= 0) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: detected MinSpareThreads set to non-positive.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Resetting to 1 to avoid almost certain Apache failure.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Please read the documentation.");
- min_spare_threads = 1;
- }
-
return NULL;
}
@@ -2270,62 +3087,19 @@ static const char *set_max_spare_threads(cmd_parms * cmd, void *dummy,
return NULL;
}
-static const char *set_max_clients(cmd_parms * cmd, void *dummy,
+static const char *set_max_workers(cmd_parms * cmd, void *dummy,
const char *arg)
{
- int max_clients;
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
-
- /* It is ok to use ap_threads_per_child here because we are
- * sure that it gets set before MaxClients in the pre_config stage. */
- max_clients = atoi(arg);
- if (max_clients < ap_threads_per_child) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxClients (%d) must be at least as large",
- max_clients);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " as ThreadsPerChild (%d). Automatically",
- ap_threads_per_child);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " increasing MaxClients to %d.", ap_threads_per_child);
- max_clients = ap_threads_per_child;
- }
- ap_daemons_limit = max_clients / ap_threads_per_child;
- if ((max_clients > 0) && (max_clients % ap_threads_per_child)) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxClients (%d) is not an integer multiple",
- max_clients);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " of ThreadsPerChild (%d), lowering MaxClients to %d",
- ap_threads_per_child,
- ap_daemons_limit * ap_threads_per_child);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " for a maximum of %d child processes,",
- ap_daemons_limit);
- max_clients = ap_daemons_limit * ap_threads_per_child;
- }
- if (ap_daemons_limit > server_limit) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxClients of %d would require %d servers,",
- max_clients, ap_daemons_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " and would exceed the ServerLimit value of %d.",
- server_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " Automatically lowering MaxClients to %d. To increase,",
- server_limit * ap_threads_per_child);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " please see the ServerLimit directive.");
- ap_daemons_limit = server_limit;
- }
- else if (ap_daemons_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require MaxClients > 0, setting to 1");
- ap_daemons_limit = 1;
+ if (!strcasecmp(cmd->cmd->name, "MaxClients")) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(00521)
+ "MaxClients is deprecated, use MaxRequestWorkers "
+ "instead.");
}
+ max_workers = atoi(arg);
return NULL;
}
@@ -2337,107 +3111,54 @@ static const char *set_threads_per_child(cmd_parms * cmd, void *dummy,
return err;
}
- ap_threads_per_child = atoi(arg);
- if (ap_threads_per_child > thread_limit) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
- "value of %d", ap_threads_per_child, thread_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "threads, lowering ThreadsPerChild to %d. To increase, "
- "please see the", thread_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " ThreadLimit directive.");
- ap_threads_per_child = thread_limit;
- }
- else if (ap_threads_per_child < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require ThreadsPerChild > 0, setting to 1");
- ap_threads_per_child = 1;
- }
+ threads_per_child = atoi(arg);
return NULL;
}
static const char *set_server_limit (cmd_parms *cmd, void *dummy, const char *arg)
{
- int tmp_server_limit;
-
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
- tmp_server_limit = atoi(arg);
- /* you cannot change ServerLimit across a restart; ignore
- * any such attempts
- */
- if (first_server_limit &&
- tmp_server_limit != server_limit) {
- /* how do we log a message? the error log is a bit bucket at this
- * point; we'll just have to set a flag so that ap_mpm_run()
- * logs a warning later
- */
- changed_limit_at_restart = 1;
- return NULL;
- }
- server_limit = tmp_server_limit;
-
- if (server_limit > MAX_SERVER_LIMIT) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: ServerLimit of %d exceeds compile time limit "
- "of %d servers,", server_limit, MAX_SERVER_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering ServerLimit to %d.", MAX_SERVER_LIMIT);
- server_limit = MAX_SERVER_LIMIT;
- }
- else if (server_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require ServerLimit > 0, setting to 1");
- server_limit = 1;
- }
+ server_limit = atoi(arg);
return NULL;
}
static const char *set_thread_limit(cmd_parms * cmd, void *dummy,
const char *arg)
{
- int tmp_thread_limit;
-
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
- tmp_thread_limit = atoi(arg);
- /* you cannot change ThreadLimit across a restart; ignore
- * any such attempts
- */
- if (first_thread_limit && tmp_thread_limit != thread_limit) {
- /* how do we log a message? the error log is a bit bucket at this
- * point; we'll just have to set a flag so that ap_mpm_run()
- * logs a warning later
- */
- changed_limit_at_restart = 1;
- return NULL;
- }
- thread_limit = tmp_thread_limit;
+ thread_limit = atoi(arg);
+ return NULL;
+}
- if (thread_limit > MAX_THREAD_LIMIT) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: ThreadLimit of %d exceeds compile time limit "
- "of %d servers,", thread_limit, MAX_THREAD_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering ThreadLimit to %d.", MAX_THREAD_LIMIT);
- thread_limit = MAX_THREAD_LIMIT;
- }
- else if (thread_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require ThreadLimit > 0, setting to 1");
- thread_limit = 1;
+static const char *set_worker_factor(cmd_parms * cmd, void *dummy,
+ const char *arg)
+{
+ double val;
+ char *endptr;
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
}
+
+ val = strtod(arg, &endptr);
+ if (*endptr)
+ return "error parsing value";
+
+ worker_factor = val * WORKER_FACTOR_SCALE;
+ if (worker_factor == 0)
+ worker_factor = 1;
return NULL;
}
+
static const command_rec event_cmds[] = {
- UNIX_DAEMON_COMMANDS,
LISTEN_COMMANDS,
AP_INIT_TAKE1("StartServers", set_daemons_to_start, NULL, RSRC_CONF,
"Number of child processes launched at server startup"),
@@ -2447,20 +3168,25 @@ static const command_rec event_cmds[] = {
"Minimum number of idle threads, to handle request spikes"),
AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
"Maximum number of idle threads"),
- AP_INIT_TAKE1("MaxClients", set_max_clients, NULL, RSRC_CONF,
+ AP_INIT_TAKE1("MaxClients", set_max_workers, NULL, RSRC_CONF,
+ "Deprecated name of MaxRequestWorkers"),
+ AP_INIT_TAKE1("MaxRequestWorkers", set_max_workers, NULL, RSRC_CONF,
"Maximum number of threads alive at the same time"),
AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
"Number of threads each child creates"),
AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
"Maximum number of worker threads per child process for this "
"run of Apache - Upper limit for ThreadsPerChild"),
+ AP_INIT_TAKE1("AsyncRequestWorkerFactor", set_worker_factor, NULL, RSRC_CONF,
+ "How many additional connects will be accepted per idle "
+ "worker thread"),
AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND,
{NULL}
};
-module AP_MODULE_DECLARE_DATA mpm_event_module = {
+AP_DECLARE_MODULE(mpm_event) = {
MPM20_MODULE_STUFF,
- ap_mpm_rewrite_args, /* hook to run before apache parses args */
+ NULL, /* hook to run before apache parses args */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
diff --git a/server/mpm/experimental/event/fdqueue.c b/server/mpm/event/fdqueue.c
index 5a1baa94..32911757 100644
--- a/server/mpm/experimental/event/fdqueue.c
+++ b/server/mpm/event/fdqueue.c
@@ -34,6 +34,8 @@ struct fd_queue_info_t
apr_thread_cond_t *wait_for_idler;
int terminated;
int max_idlers;
+ int max_recycled_pools;
+ apr_uint32_t recycled_pools_count;
recycled_pool *recycled_pools;
};
@@ -50,7 +52,7 @@ static apr_status_t queue_info_cleanup(void *data_)
break;
}
if (apr_atomic_casptr
- ((volatile void **) &(qi->recycled_pools), first_pool->next,
+ ((void*) &(qi->recycled_pools), first_pool->next,
first_pool) == first_pool) {
apr_pool_destroy(first_pool->pool);
}
@@ -60,7 +62,8 @@ static apr_status_t queue_info_cleanup(void *data_)
}
apr_status_t ap_queue_info_create(fd_queue_info_t ** queue_info,
- apr_pool_t * pool, int max_idlers)
+ apr_pool_t * pool, int max_idlers,
+ int max_recycled_pools)
{
apr_status_t rv;
fd_queue_info_t *qi;
@@ -77,6 +80,7 @@ apr_status_t ap_queue_info_create(fd_queue_info_t ** queue_info,
return rv;
}
qi->recycled_pools = NULL;
+ qi->max_recycled_pools = max_recycled_pools;
qi->max_idlers = max_idlers;
apr_pool_cleanup_register(pool, qi, queue_info_cleanup,
apr_pool_cleanup_null);
@@ -95,7 +99,12 @@ apr_status_t ap_queue_info_set_idle(fd_queue_info_t * queue_info,
ap_push_pool(queue_info, pool_to_recycle);
/* Atomically increment the count of idle workers */
- prev_idlers = apr_atomic_inc32(&(queue_info->idlers));
+ /*
+ * TODO: The atomics expect unsigned whereas we're using signed.
+ * Need to double check that they work as expected or else
+ * rework how we determine blocked.
+ */
+ prev_idlers = apr_atomic_inc32((apr_uint32_t *)&(queue_info->idlers));
/* If other threads are waiting on a worker, wake one up */
if (prev_idlers < 0) {
@@ -118,20 +127,34 @@ apr_status_t ap_queue_info_set_idle(fd_queue_info_t * queue_info,
return APR_SUCCESS;
}
-apr_status_t ap_queue_info_wait_for_idler(fd_queue_info_t * queue_info)
+apr_status_t ap_queue_info_try_get_idler(fd_queue_info_t * queue_info)
+{
+ int prev_idlers;
+ prev_idlers = apr_atomic_dec32((apr_uint32_t *)&(queue_info->idlers));
+ if (prev_idlers <= 0) {
+ apr_atomic_inc32((apr_uint32_t *)&(queue_info->idlers)); /* back out dec */
+ return APR_EAGAIN;
+ }
+ return APR_SUCCESS;
+}
+
+apr_status_t ap_queue_info_wait_for_idler(fd_queue_info_t * queue_info,
+ int *had_to_block)
{
apr_status_t rv;
int prev_idlers;
/* Atomically decrement the idle worker count, saving the old value */
- prev_idlers = apr_atomic_add32(&(queue_info->idlers), -1);
+ /* See TODO in ap_queue_info_set_idle() */
+ prev_idlers = apr_atomic_add32((apr_uint32_t *)&(queue_info->idlers), -1);
/* Block if there weren't any idle workers */
if (prev_idlers <= 0) {
rv = apr_thread_mutex_lock(queue_info->idlers_mutex);
if (rv != APR_SUCCESS) {
AP_DEBUG_ASSERT(0);
- apr_atomic_inc32(&(queue_info->idlers)); /* back out dec */
+ /* See TODO in ap_queue_info_set_idle() */
+ apr_atomic_inc32((apr_uint32_t *)&(queue_info->idlers)); /* back out dec */
return rv;
}
/* Re-check the idle worker count to guard against a
@@ -154,6 +177,7 @@ apr_status_t ap_queue_info_wait_for_idler(fd_queue_info_t * queue_info)
* threads are waiting on an idle worker.
*/
if (queue_info->idlers < 0) {
+ *had_to_block = 1;
rv = apr_thread_cond_wait(queue_info->wait_for_idler,
queue_info->idlers_mutex);
if (rv != APR_SUCCESS) {
@@ -180,33 +204,48 @@ apr_status_t ap_queue_info_wait_for_idler(fd_queue_info_t * queue_info)
}
}
+apr_uint32_t ap_queue_info_get_idlers(fd_queue_info_t * queue_info)
+{
+ apr_int32_t val;
+ val = (apr_int32_t)apr_atomic_read32((apr_uint32_t *)&queue_info->idlers);
+ if (val < 0)
+ return 0;
+ return val;
+}
void ap_push_pool(fd_queue_info_t * queue_info,
apr_pool_t * pool_to_recycle)
{
+ struct recycled_pool *new_recycle;
/* If we have been given a pool to recycle, atomically link
* it into the queue_info's list of recycled pools
*/
- if (pool_to_recycle) {
- struct recycled_pool *new_recycle;
- new_recycle = (struct recycled_pool *) apr_palloc(pool_to_recycle,
- sizeof
- (*new_recycle));
- new_recycle->pool = pool_to_recycle;
- for (;;) {
- /*
- * Save queue_info->recycled_pool in local variable next because
- * new_recycle->next can be changed after apr_atomic_casptr
- * function call. For gory details see PR 44402.
- */
- struct recycled_pool *next = queue_info->recycled_pools;
- new_recycle->next = next;
- if (apr_atomic_casptr
- ((volatile void **) &(queue_info->recycled_pools),
- new_recycle, next) == next) {
- break;
- }
+ if (!pool_to_recycle)
+ return;
+
+ if (queue_info->max_recycled_pools >= 0) {
+ apr_uint32_t cnt = apr_atomic_read32(&queue_info->recycled_pools_count);
+ if (cnt >= queue_info->max_recycled_pools) {
+ apr_pool_destroy(pool_to_recycle);
+ return;
}
+ apr_atomic_inc32(&queue_info->recycled_pools_count);
+ }
+
+ new_recycle = (struct recycled_pool *) apr_palloc(pool_to_recycle,
+ sizeof (*new_recycle));
+ new_recycle->pool = pool_to_recycle;
+ for (;;) {
+ /*
+ * Save queue_info->recycled_pool in local variable next because
+ * new_recycle->next can be changed after apr_atomic_casptr
+ * function call. For gory details see PR 44402.
+ */
+ struct recycled_pool *next = queue_info->recycled_pools;
+ new_recycle->next = next;
+ if (apr_atomic_casptr((void*) &(queue_info->recycled_pools),
+ new_recycle, next) == next)
+ break;
}
}
@@ -231,9 +270,11 @@ void ap_pop_pool(apr_pool_t ** recycled_pool, fd_queue_info_t * queue_info)
break;
}
if (apr_atomic_casptr
- ((volatile void **) &(queue_info->recycled_pools),
+ ((void*) &(queue_info->recycled_pools),
first_pool->next, first_pool) == first_pool) {
*recycled_pool = first_pool->pool;
+ if (queue_info->max_recycled_pools >= 0)
+ apr_atomic_dec32(&queue_info->recycled_pools_count);
break;
}
}
@@ -261,7 +302,7 @@ apr_status_t ap_queue_info_term(fd_queue_info_t * queue_info)
* Detects when the fd_queue_t is empty. This utility function is expected
* to be called from within critical sections, and is not threadsafe.
*/
-#define ap_queue_empty(queue) ((queue)->nelts == 0)
+#define ap_queue_empty(queue) ((queue)->nelts == 0 && APR_RING_EMPTY(&queue->timers ,timer_event_t, link))
/**
* Callback routine that is called to destroy this
@@ -298,9 +339,13 @@ apr_status_t ap_queue_init(fd_queue_t * queue, int queue_capacity,
return rv;
}
+ APR_RING_INIT(&queue->timers, timer_event_t, link);
+
queue->data = apr_palloc(a, queue_capacity * sizeof(fd_queue_elem_t));
queue->bounds = queue_capacity;
queue->nelts = 0;
+ queue->in = 0;
+ queue->out = 0;
/* Set all the sockets in the queue to NULL */
for (i = 0; i < queue_capacity; ++i)
@@ -319,7 +364,7 @@ apr_status_t ap_queue_init(fd_queue_t * queue, int queue_capacity,
* to reserve an idle worker thread
*/
apr_status_t ap_queue_push(fd_queue_t * queue, apr_socket_t * sd,
- conn_state_t * cs, apr_pool_t * p)
+ event_conn_state_t * ecs, apr_pool_t * p)
{
fd_queue_elem_t *elem;
apr_status_t rv;
@@ -331,9 +376,12 @@ apr_status_t ap_queue_push(fd_queue_t * queue, apr_socket_t * sd,
AP_DEBUG_ASSERT(!queue->terminated);
AP_DEBUG_ASSERT(!ap_queue_full(queue));
- elem = &queue->data[queue->nelts];
+ elem = &queue->data[queue->in];
+ queue->in++;
+ if (queue->in >= queue->bounds)
+ queue->in -= queue->bounds;
elem->sd = sd;
- elem->cs = cs;
+ elem->ecs = ecs;
elem->p = p;
queue->nelts++;
@@ -346,14 +394,36 @@ apr_status_t ap_queue_push(fd_queue_t * queue, apr_socket_t * sd,
return APR_SUCCESS;
}
+apr_status_t ap_queue_push_timer(fd_queue_t * queue, timer_event_t *te)
+{
+ apr_status_t rv;
+
+ if ((rv = apr_thread_mutex_lock(queue->one_big_mutex)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ AP_DEBUG_ASSERT(!queue->terminated);
+
+ APR_RING_INSERT_TAIL(&queue->timers, te, timer_event_t, link);
+
+ apr_thread_cond_signal(queue->not_empty);
+
+ if ((rv = apr_thread_mutex_unlock(queue->one_big_mutex)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ return APR_SUCCESS;
+}
+
/**
* Retrieves the next available socket from the queue. If there are no
* sockets available, it will block until one becomes available.
* Once retrieved, the socket is placed into the address specified by
* 'sd'.
*/
-apr_status_t ap_queue_pop(fd_queue_t * queue, apr_socket_t ** sd,
- conn_state_t ** cs, apr_pool_t ** p)
+apr_status_t ap_queue_pop_something(fd_queue_t * queue, apr_socket_t ** sd,
+ event_conn_state_t ** ecs, apr_pool_t ** p,
+ timer_event_t ** te_out)
{
fd_queue_elem_t *elem;
apr_status_t rv;
@@ -382,14 +452,26 @@ apr_status_t ap_queue_pop(fd_queue_t * queue, apr_socket_t ** sd,
}
}
- elem = &queue->data[--queue->nelts];
- *sd = elem->sd;
- *cs = elem->cs;
- *p = elem->p;
+ *te_out = NULL;
+
+ if (!APR_RING_EMPTY(&queue->timers, timer_event_t, link)) {
+ *te_out = APR_RING_FIRST(&queue->timers);
+ APR_RING_REMOVE(*te_out, link);
+ }
+ else {
+ elem = &queue->data[queue->out];
+ queue->out++;
+ if (queue->out >= queue->bounds)
+ queue->out -= queue->bounds;
+ queue->nelts--;
+ *sd = elem->sd;
+ *ecs = elem->ecs;
+ *p = elem->p;
#ifdef AP_DEBUG
- elem->sd = NULL;
- elem->p = NULL;
+ elem->sd = NULL;
+ elem->p = NULL;
#endif /* AP_DEBUG */
+ }
rv = apr_thread_mutex_unlock(queue->one_big_mutex);
return rv;
diff --git a/server/mpm/experimental/event/fdqueue.h b/server/mpm/event/fdqueue.h
index bd18adfd..955816b7 100644
--- a/server/mpm/experimental/event/fdqueue.h
+++ b/server/mpm/event/fdqueue.h
@@ -37,28 +37,48 @@
#endif
#include <apr_errno.h>
+#include "ap_mpm.h"
+
typedef struct fd_queue_info_t fd_queue_info_t;
+typedef struct event_conn_state_t event_conn_state_t;
apr_status_t ap_queue_info_create(fd_queue_info_t ** queue_info,
- apr_pool_t * pool, int max_idlers);
+ apr_pool_t * pool, int max_idlers,
+ int max_recycled_pools);
apr_status_t ap_queue_info_set_idle(fd_queue_info_t * queue_info,
apr_pool_t * pool_to_recycle);
-apr_status_t ap_queue_info_wait_for_idler(fd_queue_info_t * queue_info);
+apr_status_t ap_queue_info_try_get_idler(fd_queue_info_t * queue_info);
+apr_status_t ap_queue_info_wait_for_idler(fd_queue_info_t * queue_info,
+ int *had_to_block);
apr_status_t ap_queue_info_term(fd_queue_info_t * queue_info);
+apr_uint32_t ap_queue_info_get_idlers(fd_queue_info_t * queue_info);
struct fd_queue_elem_t
{
apr_socket_t *sd;
apr_pool_t *p;
- conn_state_t *cs;
+ event_conn_state_t *ecs;
};
typedef struct fd_queue_elem_t fd_queue_elem_t;
+typedef struct timer_event_t timer_event_t;
+
+struct timer_event_t {
+ APR_RING_ENTRY(timer_event_t) link;
+ apr_time_t when;
+ ap_mpm_callback_fn_t *cbfunc;
+ void *baton;
+};
+
+
struct fd_queue_t
{
+ APR_RING_HEAD(timers_t, timer_event_t) timers;
fd_queue_elem_t *data;
- int nelts;
- int bounds;
+ unsigned int nelts;
+ unsigned int bounds;
+ unsigned int in;
+ unsigned int out;
apr_thread_mutex_t *one_big_mutex;
apr_thread_cond_t *not_empty;
int terminated;
@@ -66,15 +86,17 @@ struct fd_queue_t
typedef struct fd_queue_t fd_queue_t;
void ap_pop_pool(apr_pool_t ** recycled_pool, fd_queue_info_t * queue_info);
-void ap_push_pool(fd_queue_info_t * queue_info,
+void ap_push_pool(fd_queue_info_t * queue_info,
apr_pool_t * pool_to_recycle);
apr_status_t ap_queue_init(fd_queue_t * queue, int queue_capacity,
apr_pool_t * a);
apr_status_t ap_queue_push(fd_queue_t * queue, apr_socket_t * sd,
- conn_state_t * cs, apr_pool_t * p);
-apr_status_t ap_queue_pop(fd_queue_t * queue, apr_socket_t ** sd,
- conn_state_t ** cs, apr_pool_t ** p);
+ event_conn_state_t * ecs, apr_pool_t * p);
+apr_status_t ap_queue_push_timer(fd_queue_t *queue, timer_event_t *te);
+apr_status_t ap_queue_pop_something(fd_queue_t * queue, apr_socket_t ** sd,
+ event_conn_state_t ** ecs, apr_pool_t ** p,
+ timer_event_t ** te);
apr_status_t ap_queue_interrupt_all(fd_queue_t * queue);
apr_status_t ap_queue_term(fd_queue_t * queue);
diff --git a/server/mpm/experimental/event/mpm_default.h b/server/mpm/event/mpm_default.h
index c26ee714..079e71fe 100644
--- a/server/mpm/experimental/event/mpm_default.h
+++ b/server/mpm/event/mpm_default.h
@@ -51,29 +51,5 @@
#define DEFAULT_THREADS_PER_CHILD 25
#endif
-/* File used for accept locking, when we use a file */
-#ifndef DEFAULT_LOCKFILE
-#define DEFAULT_LOCKFILE DEFAULT_REL_RUNTIMEDIR "/accept.lock"
-#endif
-
-/* Where the main/parent process's pid is logged */
-#ifndef DEFAULT_PIDLOG
-#define DEFAULT_PIDLOG DEFAULT_REL_RUNTIMEDIR "/httpd.pid"
-#endif
-
-/*
- * Interval, in microseconds, between scoreboard maintenance.
- */
-#ifndef SCOREBOARD_MAINTENANCE_INTERVAL
-#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
-#endif
-
-/* Number of requests to try to handle in a single process. If <= 0,
- * the children don't die off.
- */
-#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
-#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
-#endif
-
#endif /* AP_MPM_DEFAULT_H */
/** @} */
diff --git a/server/mpm/experimental/event/pod.c b/server/mpm/event/pod.c
index 5a9999f7..5deed8ba 100644
--- a/server/mpm/experimental/event/pod.c
+++ b/server/mpm/event/pod.c
@@ -16,11 +16,15 @@
#include "pod.h"
+#include "apr_portable.h"
+
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
-AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t * p, ap_pod_t ** pod)
+APLOG_USE_MODULE(mpm_event);
+
+AP_DECLARE(apr_status_t) ap_event_pod_open(apr_pool_t * p, ap_event_pod_t ** pod)
{
apr_status_t rv;
@@ -41,7 +45,7 @@ AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t * p, ap_pod_t ** pod)
return APR_SUCCESS;
}
-AP_DECLARE(int) ap_mpm_pod_check(ap_pod_t * pod)
+AP_DECLARE(int) ap_event_pod_check(ap_event_pod_t * pod)
{
char c;
apr_os_file_t fd;
@@ -63,7 +67,7 @@ AP_DECLARE(int) ap_mpm_pod_check(ap_pod_t * pod)
return AP_NORESTART;
}
-AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t * pod)
+AP_DECLARE(apr_status_t) ap_event_pod_close(ap_event_pod_t * pod)
{
apr_status_t rv;
@@ -79,7 +83,7 @@ AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t * pod)
return rv;
}
-static apr_status_t pod_signal_internal(ap_pod_t * pod, int graceful)
+static apr_status_t pod_signal_internal(ap_event_pod_t * pod, int graceful)
{
apr_status_t rv;
char char_of_death = graceful ? GRACEFUL_CHAR : RESTART_CHAR;
@@ -87,18 +91,18 @@ static apr_status_t pod_signal_internal(ap_pod_t * pod, int graceful)
rv = apr_file_write(pod->pod_out, &char_of_death, &one);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00522)
"write pipe_of_death");
}
return rv;
}
-AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t * pod, int graceful)
+AP_DECLARE(apr_status_t) ap_event_pod_signal(ap_event_pod_t * pod, int graceful)
{
return pod_signal_internal(pod, graceful);
}
-AP_DECLARE(void) ap_mpm_pod_killpg(ap_pod_t * pod, int num, int graceful)
+AP_DECLARE(void) ap_event_pod_killpg(ap_event_pod_t * pod, int num, int graceful)
{
int i;
apr_status_t rv = APR_SUCCESS;
diff --git a/server/mpm/experimental/event/pod.h b/server/mpm/event/pod.h
index 78e2b9d7..861e4d99 100644
--- a/server/mpm/experimental/event/pod.h
+++ b/server/mpm/event/pod.h
@@ -31,7 +31,6 @@
#include "http_config.h"
#include "http_log.h"
#include "http_main.h"
-#include "mpm.h"
#include "mpm_common.h"
#include "ap_mpm.h"
#include "ap_listen.h"
@@ -43,18 +42,18 @@
#define AP_RESTART 0
#define AP_GRACEFUL 1
-typedef struct ap_pod_t ap_pod_t;
+typedef struct ap_event_pod_t ap_event_pod_t;
-struct ap_pod_t
+struct ap_event_pod_t
{
apr_file_t *pod_in;
apr_file_t *pod_out;
apr_pool_t *p;
};
-AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t * p, ap_pod_t ** pod);
-AP_DECLARE(int) ap_mpm_pod_check(ap_pod_t * pod);
-AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t * pod);
-AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t * pod, int graceful);
-AP_DECLARE(void) ap_mpm_pod_killpg(ap_pod_t * pod, int num, int graceful);
+AP_DECLARE(apr_status_t) ap_event_pod_open(apr_pool_t * p, ap_event_pod_t ** pod);
+AP_DECLARE(int) ap_event_pod_check(ap_event_pod_t * pod);
+AP_DECLARE(apr_status_t) ap_event_pod_close(ap_event_pod_t * pod);
+AP_DECLARE(apr_status_t) ap_event_pod_signal(ap_event_pod_t * pod, int graceful);
+AP_DECLARE(void) ap_event_pod_killpg(ap_event_pod_t * pod, int num, int graceful);
/** @} */
diff --git a/server/mpm/experimental/event/Makefile.in b/server/mpm/experimental/event/Makefile.in
deleted file mode 100644
index 7c2a1a7a..00000000
--- a/server/mpm/experimental/event/Makefile.in
+++ /dev/null
@@ -1,5 +0,0 @@
-
-LTLIBRARY_NAME = libevent.la
-LTLIBRARY_SOURCES = event.c fdqueue.c pod.c
-
-include $(top_srcdir)/build/ltlib.mk
diff --git a/server/mpm/experimental/event/config5.m4 b/server/mpm/experimental/event/config5.m4
deleted file mode 100644
index 5e1db539..00000000
--- a/server/mpm/experimental/event/config5.m4
+++ /dev/null
@@ -1,6 +0,0 @@
-dnl ## XXX - Need a more thorough check of the proper flags to use
-
-if test "$MPM_NAME" = "event" ; then
- AC_CHECK_FUNCS(pthread_kill)
- APACHE_FAST_OUTPUT(server/mpm/$MPM_SUBDIR_NAME/Makefile)
-fi
diff --git a/server/mpm/experimental/event/mpm.h b/server/mpm/experimental/event/mpm.h
deleted file mode 100644
index cf4e61d9..00000000
--- a/server/mpm/experimental/event/mpm.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file event/mpm.h
- * @brief Unix Exent driven MPM
- *
- * @defgroup APACHE_MPM_EVENT Unix Event MPM
- * @ingroup APACHE_OS_UNIX APACHE_MPM
- * @{
- */
-
-#include "scoreboard.h"
-#include "unixd.h"
-
-#ifndef APACHE_MPM_EVENT_H
-#define APACHE_MPM_EVENT_H
-
-#define EVENT_MPM
-
-#define MPM_NAME "Event"
-
-#define AP_MPM_WANT_RECLAIM_CHILD_PROCESSES
-#define AP_MPM_WANT_WAIT_OR_TIMEOUT
-#define AP_MPM_WANT_PROCESS_CHILD_STATUS
-#define AP_MPM_WANT_SET_PIDFILE
-#define AP_MPM_WANT_SET_SCOREBOARD
-#define AP_MPM_WANT_SET_LOCKFILE
-#define AP_MPM_WANT_SET_MAX_REQUESTS
-#define AP_MPM_WANT_SET_COREDUMPDIR
-#define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
-#define AP_MPM_WANT_SIGNAL_SERVER
-#define AP_MPM_WANT_SET_MAX_MEM_FREE
-#define AP_MPM_WANT_SET_STACKSIZE
-#define AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN
-#define AP_MPM_WANT_FATAL_SIGNAL_HANDLER
-#define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
-
-#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
-#define MPM_NOTE_CHILD_KILLED(i) (MPM_CHILD_PID(i) = 0)
-#define MPM_ACCEPT_FUNC unixd_accept
-
-extern int ap_threads_per_child;
-extern int ap_max_daemons_limit;
-extern server_rec *ap_server_conf;
-extern char ap_coredump_dir[MAX_STRING_LEN];
-
-#endif /* APACHE_MPM_EVENT_H */
-/** @} */
diff --git a/server/mpm/mpmt_os2/Makefile.in b/server/mpm/mpmt_os2/Makefile.in
index 38e598ed..f34af9cb 100644
--- a/server/mpm/mpmt_os2/Makefile.in
+++ b/server/mpm/mpmt_os2/Makefile.in
@@ -1,5 +1 @@
-
-LTLIBRARY_NAME = libmpmt_os2.la
-LTLIBRARY_SOURCES = mpmt_os2.c mpmt_os2_child.c
-
-include $(top_srcdir)/build/ltlib.mk
+include $(top_srcdir)/build/special.mk
diff --git a/server/mpm/mpmt_os2/config.m4 b/server/mpm/mpmt_os2/config.m4
new file mode 100644
index 00000000..9a29903c
--- /dev/null
+++ b/server/mpm/mpmt_os2/config.m4
@@ -0,0 +1,10 @@
+AC_MSG_CHECKING(if mpmt_os2 MPM supports this platform)
+case $host in
+ *os2-emx*)
+ AC_MSG_RESULT(yes)
+ APACHE_MPM_SUPPORTED(mpmt_os2, no, yes)
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+esac
diff --git a/server/mpm/mpmt_os2/config5.m4 b/server/mpm/mpmt_os2/config5.m4
index b27c296d..c74f1452 100644
--- a/server/mpm/mpmt_os2/config5.m4
+++ b/server/mpm/mpmt_os2/config5.m4
@@ -1,5 +1,3 @@
-if test "$MPM_NAME" = "mpmt_os2" ; then
- AC_CACHE_SAVE
- APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
+APACHE_MPM_MODULE(mpmt_os2, $enable_mpm_mpmt_os2, mpmt_os2.lo mpmt_os2_child.lo,[
APR_ADDTO(CFLAGS,-Zmt)
-fi
+])
diff --git a/server/mpm/mpmt_os2/mpm.h b/server/mpm/mpmt_os2/mpm.h
deleted file mode 100644
index f8bbc30e..00000000
--- a/server/mpm/mpmt_os2/mpm.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file mpmt_os2/mpm.h
- * @brief MPM for os2
- *
- * @defgroup APACHE_MPM_OS2 os2 MPM
- * @ingroup APACHE_OS_OS2 APACHE_MPM
- */
-
-#ifndef APACHE_MPM_MPMT_OS2_H
-#define APACHE_MPM_MPMT_OS2_H
-
-#define MPMT_OS2_MPM
-
-#include "httpd.h"
-#include "mpm_default.h"
-#include "scoreboard.h"
-
-#define MPM_NAME "MPMT_OS2"
-
-extern server_rec *ap_server_conf;
-#define AP_MPM_WANT_SET_PIDFILE
-#define AP_MPM_WANT_SET_MAX_REQUESTS
-#define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
-#define AP_MPM_WANT_SET_MAX_MEM_FREE
-
-#endif /* APACHE_MPM_MPMT_OS2_H */
-/** @} */
diff --git a/server/mpm/mpmt_os2/mpm_default.h b/server/mpm/mpmt_os2/mpm_default.h
index 2a21cd50..02b4a74d 100644
--- a/server/mpm/mpmt_os2/mpm_default.h
+++ b/server/mpm/mpmt_os2/mpm_default.h
@@ -17,7 +17,7 @@
/**
* @file mpmt_os2/mpm_default.h
* @brief os2 MPM defaults
- *
+ *
* @addtogroup APACHE_MPM_OS2
* @{
*/
@@ -45,11 +45,6 @@
#define DEFAULT_MIN_SPARE_THREAD 5
#endif
-/* Where the main/parent process's pid is logged */
-#ifndef DEFAULT_PIDLOG
-#define DEFAULT_PIDLOG DEFAULT_REL_RUNTIMEDIR "/httpd.pid"
-#endif
-
/*
* Interval, in microseconds, between scoreboard maintenance.
*/
@@ -57,12 +52,5 @@
#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
#endif
-/* Number of requests to try to handle in a single process. If <= 0,
- * the children don't die off.
- */
-#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
-#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
-#endif
-
#endif /* AP_MPM_DEFAULT_H */
/** @} */
diff --git a/server/mpm/mpmt_os2/mpmt_os2.c b/server/mpm/mpmt_os2/mpmt_os2.c
index b3a83c43..3bd360d0 100644
--- a/server/mpm/mpmt_os2/mpmt_os2.c
+++ b/server/mpm/mpmt_os2/mpmt_os2.c
@@ -34,9 +34,8 @@
/*
Todo list
- - Enforce MaxClients somehow
+ - Enforce MaxRequestWorkers somehow
*/
-#define CORE_PRIVATE
#define INCL_NOPMAPI
#define INCL_DOS
#define INCL_DOSERRORS
@@ -49,11 +48,11 @@
#include "http_config.h"
#include "http_core.h" /* for get_remote_host */
#include "http_connection.h"
-#include "mpm.h"
#include "ap_mpm.h"
#include "ap_listen.h"
#include "apr_portable.h"
#include "mpm_common.h"
+#include "scoreboard.h"
#include "apr_strings.h"
#include <os2.h>
#include <process.h>
@@ -71,13 +70,11 @@
server_rec *ap_server_conf;
static apr_pool_t *pconf = NULL; /* Pool for config stuff */
-static const char *ap_pid_fname=NULL;
/* Config globals */
static int one_process = 0;
static int ap_daemons_to_start = 0;
static int ap_thread_limit = 0;
-static int ap_max_requests_per_child = 0;
int ap_min_spare_threads = 0;
int ap_max_spare_threads = 0;
@@ -111,7 +108,7 @@ void ap_mpm_child_main(apr_pool_t *pconf);
static void set_signals();
-int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
+static int mpmt_os2_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
{
char *listener_shm_name;
parent_info_t *parent_info;
@@ -164,7 +161,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
is_parent_process = TRUE;
if (ap_setup_listeners(ap_server_conf) < 1) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s, APLOGNO(00200)
"no listening sockets available, shutting down");
return 1;
}
@@ -176,15 +173,8 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
ap_scoreboard_image->global->running_generation = ap_my_generation;
if (!restart) {
- const char *pidfile = ap_server_root_relative(pconf, ap_pid_fname);
-
- if (pidfile != NULL && remove(pidfile) == 0) {
- ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS,
- ap_server_conf, "removed PID file %s (pid=%d)",
- pidfile, getpid());
- }
-
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_remove_pid(pconf, ap_pid_fname);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00201)
"caught SIGTERM, shutting down");
return 1;
}
@@ -211,7 +201,7 @@ static char master_main()
set_signals();
if (ap_setup_listeners(ap_server_conf) < 1) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s, APLOGNO(00202)
"no listening sockets available, shutting down");
return FALSE;
}
@@ -227,7 +217,7 @@ static char master_main()
PAG_READ|PAG_WRITE|PAG_COMMIT);
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s, APLOGNO(00203)
"failure allocating shared memory, shutting down");
return FALSE;
}
@@ -244,7 +234,7 @@ static char master_main()
rc = DosCreateMutexSem(NULL, &ap_mpm_accept_mutex, DC_SEM_SHARED, FALSE);
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s, APLOGNO(00204)
"failure creating accept mutex, shutting down");
return FALSE;
}
@@ -259,7 +249,7 @@ static char master_main()
PAG_COMMIT|PAG_READ|PAG_WRITE);
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00205)
"unable to allocate shared memory for scoreboard , exiting");
return FALSE;
}
@@ -268,17 +258,11 @@ static char master_main()
}
ap_scoreboard_image->global->restart_time = apr_time_now();
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00206)
"%s configured -- resuming normal operations",
ap_get_server_description());
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00207)
"Server built: %s", ap_get_server_built());
-#ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
- "AcceptMutex: %s (default: %s)",
- apr_proc_mutex_name(accept_mutex),
- apr_proc_mutex_defname());
-#endif
if (one_process) {
ap_scoreboard_image->parent[0].pid = getpid();
ap_mpm_child_main(pconf);
@@ -355,7 +339,7 @@ static void spawn_child(int slot)
ppib->pib_pchcmd, NULL, &proc_rc, progname);
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00208)
"error spawning child, slot %d", slot);
}
@@ -398,63 +382,83 @@ static void set_signals()
sa.sa_handler = sig_term;
if (sigaction(SIGTERM, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00209) "sigaction(SIGTERM)");
if (sigaction(SIGINT, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00210) "sigaction(SIGINT)");
sa.sa_handler = sig_restart;
if (sigaction(SIGHUP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00211) "sigaction(SIGHUP)");
if (sigaction(SIGUSR1, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGUSR1)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00212) "sigaction(SIGUSR1)");
}
/* Enquiry functions used get MPM status info */
-AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+static apr_status_t mpmt_os2_query(int query_code, int *result, apr_status_t *rv)
{
+ *rv = APR_SUCCESS;
+
switch (query_code) {
case AP_MPMQ_MAX_DAEMON_USED:
*result = ap_max_daemons_limit;
- return APR_SUCCESS;
+ break;
+
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_DYNAMIC;
- return APR_SUCCESS;
+ break;
+
case AP_MPMQ_IS_FORKED:
*result = AP_MPMQ_NOT_SUPPORTED;
- return APR_SUCCESS;
+ break;
+
case AP_MPMQ_HARD_LIMIT_DAEMONS:
*result = HARD_SERVER_LIMIT;
- return APR_SUCCESS;
+ break;
+
case AP_MPMQ_HARD_LIMIT_THREADS:
*result = HARD_THREAD_LIMIT;
- return APR_SUCCESS;
+ break;
+
case AP_MPMQ_MIN_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
+
case AP_MPMQ_MAX_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
+
case AP_MPMQ_MAX_REQUESTS_DAEMON:
*result = ap_max_requests_per_child;
- return APR_SUCCESS;
+ break;
+
+ case AP_MPMQ_GENERATION:
+ *result = ap_my_generation;
+ break;
+
+ default:
+ *rv = APR_ENOTIMPL;
+ break;
}
- return APR_ENOTIMPL;
+
+ return OK;
}
-int ap_graceful_stop_signalled(void)
+
+static const char *mpmt_os2_get_name(void)
{
- return is_graceful;
+ return "mpmt_os2";
}
+
/* Configuration handling stuff */
static int mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
@@ -465,14 +469,56 @@ static int mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *
ap_listen_pre_config();
ap_daemons_to_start = DEFAULT_START_DAEMON;
ap_thread_limit = HARD_THREAD_LIMIT;
- ap_pid_fname = DEFAULT_PIDLOG;
- ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
ap_extended_status = 0;
ap_min_spare_threads = DEFAULT_MIN_SPARE_THREAD;
ap_max_spare_threads = DEFAULT_MAX_SPARE_THREAD;
-#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
- ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-#endif
+ ap_sys_privileges_handlers(1);
+
+ return OK;
+}
+
+
+
+static int mpmt_os2_check_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ static int restart_num = 0;
+ int startup = 0;
+
+ /* we want this only the first time around */
+ if (restart_num++ == 0) {
+ startup = 1;
+ }
+
+ if (ap_daemons_to_start < 0) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00213)
+ "WARNING: StartServers of %d not allowed, "
+ "increasing to 1.", ap_daemons_to_start);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00214)
+ "StartServers of %d not allowed, increasing to 1",
+ ap_daemons_to_start);
+ }
+ ap_daemons_to_start = 1;
+ }
+
+ if (ap_min_spare_threads < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00215)
+ "WARNING: MinSpareThreads of %d not allowed, "
+ "increasing to 1", ap_min_spare_threads);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " to avoid almost certain server failure.");
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " Please read the documentation.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00216)
+ "MinSpareThreads of %d not allowed, increasing to 1",
+ ap_min_spare_threads);
+ }
+ ap_min_spare_threads = 1;
+ }
return OK;
}
@@ -482,6 +528,10 @@ static int mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *
static void mpmt_os2_hooks(apr_pool_t *p)
{
ap_hook_pre_config(mpmt_os2_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_check_config(mpmt_os2_check_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm(mpmt_os2_run, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_query(mpmt_os2_query, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_get_name(mpmt_os2_get_name, NULL, NULL, APR_HOOK_MIDDLE);
}
@@ -510,17 +560,6 @@ static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy,
}
ap_min_spare_threads = atoi(arg);
-
- if (ap_min_spare_threads <= 0) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: detected MinSpareThreads set to non-positive.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Resetting to 1 to avoid almost certain Apache failure.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Please read the documentation.");
- ap_min_spare_threads = 1;
- }
-
return NULL;
}
@@ -565,7 +604,7 @@ AP_INIT_TAKE1("ScoreBoardFile", ignore_cmd, NULL, RSRC_CONF, \
{ NULL }
};
-module AP_MODULE_DECLARE_DATA mpm_mpmt_os2_module = {
+AP_DECLARE_MODULE(mpm_mpmt_os2) = {
MPM20_MODULE_STUFF,
NULL, /* hook to run before apache parses args */
NULL, /* create per-directory config structure */
diff --git a/server/mpm/mpmt_os2/mpmt_os2_child.c b/server/mpm/mpmt_os2/mpmt_os2_child.c
index bc313315..ca9f5947 100644
--- a/server/mpm/mpmt_os2/mpmt_os2_child.c
+++ b/server/mpm/mpmt_os2/mpmt_os2_child.c
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#define CORE_PRIVATE
#define INCL_NOPMAPI
#define INCL_DOS
#define INCL_DOSERRORS
@@ -27,7 +26,7 @@
#include "http_config.h"
#include "http_core.h" /* for get_remote_host */
#include "http_connection.h"
-#include "mpm.h"
+#include "scoreboard.h"
#include "ap_mpm.h"
#include "ap_listen.h"
#include "apr_portable.h"
@@ -37,6 +36,8 @@
#include <os2.h>
#include <process.h>
+APLOG_USE_MODULE(mpm_mpmt_os2);
+
/* XXXXXX move these to header file private to this MPM */
/* We don't need many processes,
@@ -116,7 +117,7 @@ void ap_mpm_child_main(apr_pool_t *pconf)
rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE);
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00189)
"unable to create shutdown semaphore, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -126,7 +127,7 @@ void ap_mpm_child_main(apr_pool_t *pconf)
PAG_READ|PAG_WRITE);
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00190)
"scoreboard not readable in child, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -138,7 +139,7 @@ void ap_mpm_child_main(apr_pool_t *pconf)
rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex);
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00191)
"accept mutex couldn't be accessed in child, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -147,7 +148,7 @@ void ap_mpm_child_main(apr_pool_t *pconf)
for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++);
if (child_slot == HARD_SERVER_LIMIT) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00192)
"child pid not found in scoreboard, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -161,7 +162,7 @@ void ap_mpm_child_main(apr_pool_t *pconf)
rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid));
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00193)
"unable to create work queue, exiting");
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -234,7 +235,7 @@ void ap_mpm_child_main(apr_pool_t *pconf)
if (rv != APR_SUCCESS) {
if (!APR_STATUS_IS_EINTR(rv)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00194)
"apr_socket_accept");
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -290,13 +291,14 @@ void ap_mpm_child_main(apr_pool_t *pconf)
void add_worker()
{
int thread_slot;
+ int stacksize = ap_thread_stacksize == 0 ? 128*1024 : ap_thread_stacksize;
/* Find a free thread slot */
for (thread_slot=0; thread_slot < HARD_THREAD_LIMIT; thread_slot++) {
if (ap_scoreboard_image->servers[child_slot][thread_slot].status == SERVER_DEAD) {
ap_scoreboard_image->servers[child_slot][thread_slot].status = SERVER_STARTING;
ap_scoreboard_image->servers[child_slot][thread_slot].tid =
- _beginthread(worker_main, NULL, 128*1024, (void *)thread_slot);
+ _beginthread(worker_main, NULL, stacksize, (void *)thread_slot);
break;
}
}
@@ -317,7 +319,7 @@ ULONG APIENTRY thread_exception_handler(EXCEPTIONREPORTRECORD *pReportRec,
if (pReportRec->ExceptionNum == XCPT_ACCESS_VIOLATION ||
pReportRec->ExceptionNum == XCPT_INTEGER_DIVIDE_BY_ZERO) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00195)
"caught exception in worker thread, initiating child shutdown pid=%d", getpid());
for (c=0; c<HARD_THREAD_LIMIT; c++) {
if (ap_scoreboard_image->servers[child_slot][c].tid == _gettid()) {
@@ -340,6 +342,8 @@ ULONG APIENTRY thread_exception_handler(EXCEPTIONREPORTRECORD *pReportRec,
static void worker_main(void *vpArg)
{
+ apr_thread_t *thd = NULL;
+ apr_os_thread_t osthd;
long conn_id;
conn_rec *current_conn;
apr_pool_t *pconn;
@@ -359,11 +363,14 @@ static void worker_main(void *vpArg)
/* Trap exceptions in this thread so we don't take down the whole process */
DosSetExceptionHandler( &reg_rec );
+ osthd = apr_os_thread_current();
+ apr_os_thread_put(&thd, &osthd, pchild);
+
rc = DosOpenQueue(&owner, &workq,
apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00196)
"unable to open work queue, exiting");
ap_scoreboard_image->servers[child_slot][thread_slot].tid = 0;
}
@@ -385,6 +392,7 @@ static void worker_main(void *vpArg)
sbh, bucket_alloc);
if (current_conn) {
+ current_conn->current_thread = thd;
ap_process_connection(current_conn, worker_args->conn_sd);
ap_lingering_close(current_conn);
}
@@ -416,7 +424,7 @@ static void server_maintenance(void *vpArg)
apr_psprintf(pchild, "/queues/httpd/work.%d", getpid()));
if (rc) {
- ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, APLOGNO(00197)
"unable to open work queue in maintenance thread");
return;
}
@@ -471,10 +479,10 @@ static void set_signals()
sa.sa_handler = sig_term;
if (sigaction(SIGTERM, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00198) "sigaction(SIGTERM)");
sa.sa_handler = sig_hup;
if (sigaction(SIGHUP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00199) "sigaction(SIGHUP)");
}
diff --git a/server/mpm/netware/mpm.h b/server/mpm/netware/mpm.h
deleted file mode 100644
index 106d62a5..00000000
--- a/server/mpm/netware/mpm.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file netware/mpm.h
- * @brief Netware MPM
- *
- * @defgroup APACHE_MPM_NETWARE Netware MPM
- * @ingroup APACHE_OS_NETWARE APACHE_MPM
- * @{
- */
-
-#include "scoreboard.h"
-
-#ifndef APACHE_MPM_THREADED_H
-#define APACHE_MPM_THREADED_H
-
-#define THREADED_MPM
-
-#define MPM_NAME "NetWare_Threaded"
-
-/*#define AP_MPM_WANT_RECLAIM_CHILD_PROCESSES
- #define AP_MPM_WANT_WAIT_OR_TIMEOUT
- #define AP_MPM_WANT_PROCESS_CHILD_STATUS
- #define AP_MPM_WANT_SET_PIDFILE
- #define AP_MPM_WANT_SET_SCOREBOARD
- #define AP_MPM_WANT_SET_LOCKFILE
-*/
-#define AP_MPM_WANT_SET_MAX_REQUESTS
-#define AP_MPM_WANT_SET_MAX_MEM_FREE
-#define AP_MPM_WANT_SET_STACKSIZE
-#define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
-/*#define AP_MPM_WANT_SET_COREDUMPDIR
- #define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
-*/
-
-#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
-#define MPM_NOTE_CHILD_KILLED(i) (MPM_CHILD_PID(i) = 0)
-
-extern int ap_threads_per_child;
-extern int ap_max_workers_limit;
-extern server_rec *ap_server_conf;
-
-#endif /* APACHE_MPM_THREADED_H */
-/** @} */
diff --git a/server/mpm/netware/mpm_default.h b/server/mpm/netware/mpm_default.h
index 96c5aa09..b8c3ad6f 100644
--- a/server/mpm/netware/mpm_default.h
+++ b/server/mpm/netware/mpm_default.h
@@ -24,27 +24,6 @@
#ifndef APACHE_MPM_DEFAULT_H
#define APACHE_MPM_DEFAULT_H
-/* Number of servers to spawn off by default --- also, if fewer than
- * this free when the caretaker checks, it will spawn more.
- */
-#ifndef DEFAULT_START_DAEMON
-#define DEFAULT_START_DAEMON 1
-#endif
-
-/* Maximum number of *free* server processes --- more than this, and
- * they will die off.
- */
-
-#ifndef DEFAULT_MAX_FREE_DAEMON
-#define DEFAULT_MAX_FREE_DAEMON 1
-#endif
-
-/* Minimum --- fewer than this, and more will be created */
-
-#ifndef DEFAULT_MIN_FREE_DAEMON
-#define DEFAULT_MIN_FREE_DAEMON 1
-#endif
-
/* Limit on the threads per process. Clients will be locked out if more than
* this * HARD_SERVER_LIMIT are needed.
*
@@ -81,23 +60,6 @@
#define DEFAULT_MIN_FREE_THREADS 10
#endif
-/* Check for definition of DEFAULT_REL_RUNTIMEDIR */
-#ifndef DEFAULT_REL_RUNTIMEDIR
-#define DEFAULT_REL_RUNTIMEDIR "logs"
-#endif
-
-/* File used for accept locking, when we use a file */
-/*#ifndef DEFAULT_LOCKFILE
- #define DEFAULT_LOCKFILE DEFAULT_REL_RUNTIMEDIR "/accept.lock"
- #endif
-*/
-
-/* Where the main/parent process's pid is logged */
-/*#ifndef DEFAULT_PIDLOG
- #define DEFAULT_PIDLOG DEFAULT_REL_RUNTIMEDIR "/httpd.pid"
- #endif
-*/
-
/*
* Interval, in microseconds, between scoreboard maintenance.
*/
@@ -105,13 +67,6 @@
#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
#endif
-/* Number of requests to try to handle in a single process. If <= 0,
- * the children don't die off.
- */
-#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
-#define DEFAULT_MAX_REQUESTS_PER_CHILD 0
-#endif
-
/* Default stack size allocated for each worker thread.
*/
#ifndef DEFAULT_THREAD_STACKSIZE
diff --git a/server/mpm/netware/mpm_netware.c b/server/mpm/netware/mpm_netware.c
index a8d0952f..77840492 100644
--- a/server/mpm/netware/mpm_netware.c
+++ b/server/mpm/netware/mpm_netware.c
@@ -61,8 +61,6 @@
#include <sys/select.h>
#endif
-#define CORE_PRIVATE
-
#include "ap_config.h"
#include "httpd.h"
#include "mpm_default.h"
@@ -88,6 +86,8 @@
#include <library.h>
#include <screen.h>
+int nlmUnloadSignaled(int wait);
+
/* Limit on the total --- clients will be locked out if more servers than
* this are needed. It is intended solely to keep the server from crashing
* when things get out of hand.
@@ -108,9 +108,11 @@
#define WORKER_READY SERVER_READY
#define WORKER_IDLE_KILL SERVER_IDLE_KILL
-/* config globals */
+#define MPM_HARD_LIMITS_FILE "/mpm_default.h"
-int ap_threads_per_child=0; /* Worker threads per child */
+/* *Non*-shared http_main globals... */
+
+static int ap_threads_per_child=0; /* Worker threads per child */
static int ap_threads_to_start=0;
static int ap_threads_min_free=0;
static int ap_threads_max_free=0;
@@ -119,13 +121,10 @@ static int mpm_state = AP_MPMQ_STARTING;
/*
* The max child slot ever assigned, preserved across restarts. Necessary
- * to deal with MaxClients changes across SIGWINCH restarts. We use this
+ * to deal with MaxRequestWorkers changes across SIGWINCH restarts. We use this
* value to optimize routines that have to scan the entire scoreboard.
*/
-int ap_max_workers_limit = -1;
-server_rec *ap_server_conf;
-
-/* *Non*-shared http_main globals... */
+static int ap_max_workers_limit = -1;
int hold_screen_on_exit = 0; /* Indicates whether the screen should be held open */
@@ -171,7 +170,7 @@ static int volatile shutdown_pending;
static int volatile restart_pending;
static int volatile is_graceful;
static int volatile wait_to_finish=1;
-ap_generation_t volatile ap_my_generation=0;
+static ap_generation_t volatile ap_my_generation=0;
/* a clean exit from a child with proper cleanup */
static void clean_child_exit(int code, int worker_num, apr_pool_t *ptrans,
@@ -199,52 +198,63 @@ static void mpm_main_cleanup(void)
}
}
-AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+static int netware_query(int query_code, int *result, apr_status_t *rv)
{
+ *rv = APR_SUCCESS;
switch(query_code){
case AP_MPMQ_MAX_DAEMON_USED:
*result = 1;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_DYNAMIC;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_IS_FORKED:
*result = AP_MPMQ_NOT_SUPPORTED;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_DAEMONS:
*result = HARD_SERVER_LIMIT;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_THREADS:
*result = HARD_THREAD_LIMIT;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_THREADS:
*result = ap_threads_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MIN_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MIN_SPARE_THREADS:
*result = ap_threads_min_free;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_THREADS:
*result = ap_threads_max_free;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_REQUESTS_DAEMON:
*result = ap_max_requests_per_child;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_DAEMONS:
*result = 1;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MPM_STATE:
*result = mpm_state;
- return APR_SUCCESS;
+ break;
+ case AP_MPMQ_GENERATION:
+ *result = ap_my_generation;
+ break;
+ default:
+ *rv = APR_ENOTIMPL;
+ break;
}
- return APR_ENOTIMPL;
+ return OK;
}
+static const char *netware_get_name(void)
+{
+ return "NetWare";
+}
/*****************************************************************
* Connection structures and accounting...
@@ -314,12 +324,6 @@ int nlmUnloadSignaled(int wait)
*/
-int ap_graceful_stop_signalled(void)
-{
- /* not ever called anymore... */
- return 0;
-}
-
#define MAX_WB_RETRIES 3
#ifdef DBINFO_ON
static int would_block = 0;
@@ -333,12 +337,13 @@ void worker_main(void *arg)
{
ap_listen_rec *lr, *first_lr, *last_lr = NULL;
apr_pool_t *ptrans;
- apr_pool_t *pbucket;
apr_allocator_t *allocator;
apr_bucket_alloc_t *bucket_alloc;
conn_rec *current_conn;
apr_status_t stat = APR_EINIT;
ap_sb_handle_t *sbh;
+ apr_thread_t *thd = NULL;
+ apr_os_thread_t osthd;
int my_worker_num = (int)arg;
apr_socket_t *csd = NULL;
@@ -351,6 +356,9 @@ void worker_main(void *arg)
struct timeval tv;
int wouldblock_retry;
+ osthd = apr_os_thread_current();
+ apr_os_thread_put(&thd, &osthd, pmain);
+
tv.tv_sec = 1;
tv.tv_usec = 0;
@@ -397,7 +405,7 @@ void worker_main(void *arg)
if (srv <= 0) {
if (srv < 0) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00217)
"select() failed on listen socket");
apr_thread_yield();
}
@@ -496,14 +504,14 @@ void worker_main(void *arg)
* Ben Hyde noted that temporary ENETDOWN situations
* occur in mobile IP.
*/
- ap_log_error(APLOG_MARK, APLOG_EMERG, stat, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_EMERG, stat, ap_server_conf, APLOGNO(00218)
"apr_socket_accept: giving up.");
clean_child_exit(APEXIT_CHILDFATAL, my_worker_num, ptrans,
bucket_alloc);
}
#endif
else {
- ap_log_error(APLOG_MARK, APLOG_ERR, stat, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, stat, ap_server_conf, APLOGNO(00219)
"apr_socket_accept: (client socket)");
clean_child_exit(1, my_worker_num, ptrans, bucket_alloc);
}
@@ -519,6 +527,7 @@ void worker_main(void *arg)
my_worker_num, sbh,
bucket_alloc);
if (current_conn) {
+ current_conn->current_thread = thd;
ap_process_connection(current_conn, csd);
ap_lingering_close(current_conn);
}
@@ -605,7 +614,6 @@ static int hold_off_on_exponential_spawning;
static void perform_idle_server_maintenance(apr_pool_t *p)
{
int i;
- int to_kill;
int idle_count;
worker_score *ws;
int free_length;
@@ -616,7 +624,6 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
/* initialize the free_list */
free_length = 0;
- to_kill = -1;
idle_count = 0;
last_non_dead = -1;
total_non_dead = 0;
@@ -648,13 +655,6 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
*/
if (status <= WORKER_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;
@@ -680,16 +680,16 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
static int reported = 0;
if (!reported) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
- "server reached MaxClients setting, consider"
- " raising the MaxClients setting");
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00220)
+ "server reached MaxRequestWorkers setting, consider"
+ " raising the MaxRequestWorkers setting");
reported = 1;
}
idle_spawn_rate = 1;
}
else {
if (idle_spawn_rate >= 8) {
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00221)
"server seems busy, (you may need "
"to increase StartServers, or Min/MaxSpareServers), "
"spawning %d children, there are %d idle, and "
@@ -717,7 +717,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
}
}
-static void display_settings ()
+static void display_settings()
{
int status_array[SERVER_NUM_STATUS];
int i, status, total=0;
@@ -830,7 +830,7 @@ static int setup_listeners(server_rec *s)
int sockdes;
if (ap_setup_listeners(s) < 1 ) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s, APLOGNO(00222)
"no listening sockets available, shutting down");
return -1;
}
@@ -862,7 +862,7 @@ static int shutdown_listeners()
* Executive routines.
*/
-int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
+static int netware_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
apr_status_t status=0;
@@ -870,7 +870,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
ap_server_conf = s;
if (setup_listeners(s)) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, status, s,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, status, s, APLOGNO(00223)
"no listening sockets available, shutting down");
return -1;
}
@@ -886,6 +886,11 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
/* Only set slot 0 since that is all NetWare will ever have. */
ap_scoreboard_image->parent[0].pid = getpid();
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[0].pid,
+ ap_my_generation,
+ 0,
+ MPM_CHILD_STARTED);
set_signals();
@@ -905,17 +910,12 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
hold_screen_on_exit = 0;
}
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00224)
"%s configured -- resuming normal operations",
ap_get_server_description());
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00225)
"Server built: %s", ap_get_server_built());
-#ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
- "AcceptMutex: %s (default: %s)",
- apr_proc_mutex_name(accept_mutex),
- apr_proc_mutex_defname());
-#endif
+ ap_log_command_line(plog, s);
show_server_data();
mpm_state = AP_MPMQ_RUNNING;
@@ -928,12 +928,17 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
}
mpm_state = AP_MPMQ_STOPPING;
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[0].pid,
+ ap_my_generation,
+ 0,
+ MPM_CHILD_EXITED);
/* Shutdown the listen sockets so that we don't get stuck in a blocking call.
shutdown_listeners();*/
if (shutdown_pending) { /* Got an unload from the console */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00226)
"caught SIGTERM, shutting down");
while (worker_thread_count > 0) {
@@ -953,7 +958,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
++ap_my_generation;
ap_scoreboard_image->global->running_generation = ap_my_generation;
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00227)
"Graceful restart requested, doing restart");
/* Wait for all of the threads to terminate before initiating the restart */
@@ -971,13 +976,10 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
static int netware_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
{
- int debug;
char *addrname = NULL;
mpm_state = AP_MPMQ_STARTING;
- debug = ap_exists_config_define("DEBUG");
-
is_graceful = 0;
ap_my_pid = getpid();
addrname = getaddressspacename (NULL, NULL);
@@ -995,22 +997,115 @@ static int netware_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp
ap_threads_min_free = DEFAULT_MIN_FREE_THREADS;
ap_threads_max_free = DEFAULT_MAX_FREE_THREADS;
ap_threads_limit = HARD_THREAD_LIMIT;
- ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
ap_extended_status = 0;
+
+ /* override core's default thread stacksize */
ap_thread_stacksize = DEFAULT_THREAD_STACKSIZE;
-#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
- ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-#endif
+
+ return OK;
+}
+
+static int netware_check_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ static int restart_num = 0;
+ int startup = 0;
+
+ /* we want this only the first time around */
+ if (restart_num++ == 0) {
+ startup = 1;
+ }
+
+ if (ap_threads_limit > HARD_THREAD_LIMIT) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00228)
+ "WARNING: MaxThreads of %d exceeds compile-time "
+ "limit of", ap_threads_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d threads, decreasing to %d.",
+ HARD_THREAD_LIMIT, HARD_THREAD_LIMIT);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " To increase, please see the HARD_THREAD_LIMIT"
+ "define in");
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " server/mpm/netware%s.", MPM_HARD_LIMITS_FILE);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00229)
+ "MaxThreads of %d exceeds compile-time limit "
+ "of %d, decreasing to match",
+ ap_threads_limit, HARD_THREAD_LIMIT);
+ }
+ ap_threads_limit = HARD_THREAD_LIMIT;
+ }
+ else if (ap_threads_limit < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ APLOGNO(00230) "WARNING: MaxThreads of %d not allowed, "
+ "increasing to 1.", ap_threads_limit);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
+ "MaxThreads of %d not allowed, increasing to 1",
+ ap_threads_limit);
+ }
+ ap_threads_limit = 1;
+ }
+
+ /* ap_threads_to_start > ap_threads_limit effectively checked in
+ * call to startup_workers(ap_threads_to_start) in ap_mpm_run()
+ */
+ if (ap_threads_to_start < 0) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00231)
+ "WARNING: StartThreads of %d not allowed, "
+ "increasing to 1.", ap_threads_to_start);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00232)
+ "StartThreads of %d not allowed, increasing to 1",
+ ap_threads_to_start);
+ }
+ ap_threads_to_start = 1;
+ }
+
+ if (ap_threads_min_free < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00233)
+ "WARNING: MinSpareThreads of %d not allowed, "
+ "increasing to 1", ap_threads_min_free);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " to avoid almost certain server failure.");
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " Please read the documentation.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00234)
+ "MinSpareThreads of %d not allowed, increasing to 1",
+ ap_threads_min_free);
+ }
+ ap_threads_min_free = 1;
+ }
+
+ /* ap_threads_max_free < ap_threads_min_free + 1 checked in ap_mpm_run() */
return OK;
}
static void netware_mpm_hooks(apr_pool_t *p)
{
- ap_hook_pre_config(netware_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+ /* Run the pre-config hook after core's so that it can override the
+ * default setting of ThreadStackSize for NetWare.
+ */
+ static const char * const predecessors[] = {"core.c", NULL};
+
+ ap_hook_pre_config(netware_pre_config, predecessors, NULL, APR_HOOK_MIDDLE);
+ ap_hook_check_config(netware_check_config, NULL, NULL, APR_HOOK_MIDDLE);
+ //ap_hook_post_config(netware_post_config, NULL, NULL, 0);
+ //ap_hook_child_init(netware_child_init, NULL, NULL, APR_HOOK_MIDDLE);
+ //ap_hook_open_logs(netware_open_logs, NULL, aszSucc, APR_HOOK_REALLY_FIRST);
+ ap_hook_mpm(netware_run, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_query(netware_query, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_get_name(netware_get_name, NULL, NULL, APR_HOOK_MIDDLE);
}
-void netware_rewrite_args(process_rec *process)
+static void netware_rewrite_args(process_rec *process)
{
char *def_server_root;
char optbuf[3];
@@ -1058,7 +1153,7 @@ void netware_rewrite_args(process_rec *process)
optbuf[0] = '-';
optbuf[2] = '\0';
- apr_getopt_init(&opt, process->pool, process->argc, (char**) process->argv);
+ apr_getopt_init(&opt, process->pool, process->argc, process->argv);
while (apr_getopt(opt, AP_SERVER_BASEARGS"n:", optbuf + 1, &opt_arg) == APR_SUCCESS) {
switch (optbuf[1]) {
case 'n':
@@ -1099,7 +1194,7 @@ static int CommandLineInterpreter(scr_t screenID, const char *commandLine)
if (strlen(commandLine) <= strlen(szCommand))
return NOTMYCOMMAND;
- strncpy (szcommandLine, commandLine, sizeof(szcommandLine)-1);
+ apr_cpystrn(szcommandLine, commandLine, sizeof(szcommandLine));
/* All added commands begin with "APACHE2 " */
@@ -1224,16 +1319,6 @@ static const char *set_min_free_threads(cmd_parms *cmd, void *dummy, const char
}
ap_threads_min_free = atoi(arg);
- if (ap_threads_min_free <= 0) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: detected MinSpareServers set to non-positive.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Resetting to 1 to avoid almost certain Apache failure.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Please read the documentation.");
- ap_threads_min_free = 1;
- }
-
return NULL;
}
@@ -1256,23 +1341,6 @@ static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *ar
}
ap_threads_limit = atoi(arg);
- if (ap_threads_limit > HARD_THREAD_LIMIT) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxThreads of %d exceeds compile time limit "
- "of %d threads,", ap_threads_limit, HARD_THREAD_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering MaxThreads to %d. To increase, please "
- "see the", HARD_THREAD_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " HARD_THREAD_LIMIT define in %s.",
- AP_MPM_HARD_LIMITS_FILE);
- ap_threads_limit = HARD_THREAD_LIMIT;
- }
- else if (ap_threads_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require MaxThreads > 0, setting to 1");
- ap_threads_limit = 1;
- }
return NULL;
}
@@ -1289,7 +1357,7 @@ AP_INIT_TAKE1("MaxThreads", set_thread_limit, NULL, RSRC_CONF,
{ NULL }
};
-module AP_MODULE_DECLARE_DATA mpm_netware_module = {
+AP_DECLARE_MODULE(mpm_netware) = {
MPM20_MODULE_STUFF,
netware_rewrite_args, /* hook to run before apache parses args */
NULL, /* create per-directory config structure */
diff --git a/server/mpm/prefork/Makefile.in b/server/mpm/prefork/Makefile.in
index 034bf5ce..f34af9cb 100644
--- a/server/mpm/prefork/Makefile.in
+++ b/server/mpm/prefork/Makefile.in
@@ -1,5 +1 @@
-
-LTLIBRARY_NAME = libprefork.la
-LTLIBRARY_SOURCES = prefork.c
-
-include $(top_srcdir)/build/ltlib.mk
+include $(top_srcdir)/build/special.mk
diff --git a/server/mpm/prefork/config.m4 b/server/mpm/prefork/config.m4
index 9c189a86..296f834a 100644
--- a/server/mpm/prefork/config.m4
+++ b/server/mpm/prefork/config.m4
@@ -1,3 +1,7 @@
-if test "$MPM_NAME" = "prefork" ; then
- APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
+AC_MSG_CHECKING(if prefork MPM supports this platform)
+if test $forking_mpms_supported != yes; then
+ AC_MSG_RESULT(no - This is not a forking platform)
+else
+ AC_MSG_RESULT(yes)
+ APACHE_MPM_SUPPORTED(prefork, yes, no)
fi
diff --git a/server/mpm/prefork/config3.m4 b/server/mpm/prefork/config3.m4
new file mode 100644
index 00000000..25fd8df3
--- /dev/null
+++ b/server/mpm/prefork/config3.m4
@@ -0,0 +1 @@
+APACHE_MPM_MODULE(prefork, $enable_mpm_prefork)
diff --git a/server/mpm/prefork/mpm.h b/server/mpm/prefork/mpm.h
deleted file mode 100644
index bf1fb949..00000000
--- a/server/mpm/prefork/mpm.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file prefork/mpm.h
- * @brief Unix Prefork MPM (default for Uinx systems)
- *
- * @defgroup APACHE_MPM_PREFORK Unix Prefork
- * @ingroup APACHE_MPM APACHE_OS_UNIX
- * @{
- */
-
-#include "httpd.h"
-#include "mpm_default.h"
-#include "scoreboard.h"
-#include "unixd.h"
-
-#ifndef APACHE_MPM_PREFORK_H
-#define APACHE_MPM_PREFORK_H
-
-#define PREFORK_MPM
-
-#define MPM_NAME "Prefork"
-
-#define AP_MPM_WANT_RECLAIM_CHILD_PROCESSES
-#define AP_MPM_WANT_WAIT_OR_TIMEOUT
-#define AP_MPM_WANT_PROCESS_CHILD_STATUS
-#define AP_MPM_WANT_SET_PIDFILE
-#define AP_MPM_WANT_SET_SCOREBOARD
-#define AP_MPM_WANT_SET_LOCKFILE
-#define AP_MPM_WANT_SET_MAX_REQUESTS
-#define AP_MPM_WANT_SET_COREDUMPDIR
-#define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
-#define AP_MPM_WANT_SIGNAL_SERVER
-#define AP_MPM_WANT_SET_MAX_MEM_FREE
-#define AP_MPM_WANT_FATAL_SIGNAL_HANDLER
-#define AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN
-#define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
-
-#define AP_MPM_USES_POD 1
-#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
-#define MPM_NOTE_CHILD_KILLED(i) (MPM_CHILD_PID(i) = 0)
-#define MPM_ACCEPT_FUNC unixd_accept
-
-extern int ap_threads_per_child;
-extern int ap_max_daemons_limit;
-extern server_rec *ap_server_conf;
-#endif /* APACHE_MPM_PREFORK_H */
-/** @} */
diff --git a/server/mpm/prefork/mpm_default.h b/server/mpm/prefork/mpm_default.h
index d548a7ec..5f131c5a 100644
--- a/server/mpm/prefork/mpm_default.h
+++ b/server/mpm/prefork/mpm_default.h
@@ -46,29 +46,5 @@
#define DEFAULT_MIN_FREE_DAEMON 5
#endif
-/* File used for accept locking, when we use a file */
-#ifndef DEFAULT_LOCKFILE
-#define DEFAULT_LOCKFILE DEFAULT_REL_RUNTIMEDIR "/accept.lock"
-#endif
-
-/* Where the main/parent process's pid is logged */
-#ifndef DEFAULT_PIDLOG
-#define DEFAULT_PIDLOG DEFAULT_REL_RUNTIMEDIR "/httpd.pid"
-#endif
-
-/*
- * Interval, in microseconds, between scoreboard maintenance.
- */
-#ifndef SCOREBOARD_MAINTENANCE_INTERVAL
-#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
-#endif
-
-/* Number of requests to try to handle in a single process. If <= 0,
- * the children don't die off.
- */
-#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
-#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
-#endif
-
#endif /* AP_MPM_DEFAULT_H */
/** @} */
diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c
index 108f961a..5de88e32 100644
--- a/server/mpm/prefork/prefork.c
+++ b/server/mpm/prefork/prefork.c
@@ -31,8 +31,6 @@
#include <sys/types.h>
#endif
-#define CORE_PRIVATE
-
#include "ap_config.h"
#include "httpd.h"
#include "mpm_default.h"
@@ -43,15 +41,13 @@
#include "http_connection.h"
#include "scoreboard.h"
#include "ap_mpm.h"
+#include "util_mutex.h"
#include "unixd.h"
#include "mpm_common.h"
#include "ap_listen.h"
#include "ap_mmn.h"
#include "apr_poll.h"
-#ifdef HAVE_BSTRING_H
-#include <bstring.h> /* for IRIX, FD_SET calls bzero() */
-#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
@@ -90,25 +86,46 @@
/* config globals */
-int ap_threads_per_child=0; /* Worker threads per child */
static apr_proc_mutex_t *accept_mutex;
static int ap_daemons_to_start=0;
static int ap_daemons_min_free=0;
static int ap_daemons_max_free=0;
-static int ap_daemons_limit=0; /* MaxClients */
-static int server_limit = DEFAULT_SERVER_LIMIT;
-static int first_server_limit = 0;
-static int changed_limit_at_restart;
+static int ap_daemons_limit=0; /* MaxRequestWorkers */
+static int server_limit = 0;
static int mpm_state = AP_MPMQ_STARTING;
static ap_pod_t *pod;
-/*
- * The max child slot ever assigned, preserved across restarts. Necessary
- * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We
- * use this value to optimize routines that have to scan the entire scoreboard.
+/* data retained by prefork across load/unload of the module
+ * allocated on first call to pre-config hook; located on
+ * subsequent calls to pre-config hook
*/
-int ap_max_daemons_limit = -1;
-server_rec *ap_server_conf;
+typedef struct prefork_retained_data {
+ int first_server_limit;
+ int module_loads;
+ ap_generation_t my_generation;
+ int volatile is_graceful; /* set from signal handler */
+ int maxclients_reported;
+ /*
+ * The max child slot ever assigned, preserved across restarts. Necessary
+ * to deal with MaxRequestWorkers changes across AP_SIG_GRACEFUL restarts. We
+ * use this value to optimize routines that have to scan the entire scoreboard.
+ */
+ int max_daemons_limit;
+ /*
+ * 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.
+ */
+ int idle_spawn_rate;
+#ifndef MAX_SPAWN_RATE
+#define MAX_SPAWN_RATE (32)
+#endif
+ int hold_off_on_exponential_spawning;
+} prefork_retained_data;
+static prefork_retained_data *retained;
+
+#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
/* 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
@@ -128,17 +145,7 @@ static apr_pool_t *pchild; /* Pool for httpd child stuff */
static pid_t ap_my_pid; /* it seems silly to call getpid all the time */
static pid_t parent_pid;
-#ifndef MULTITHREAD
static int my_child_num;
-#endif
-ap_generation_t volatile ap_my_generation=0;
-
-#ifdef TPF
-int tpf_child = 0;
-char tpf_server_name[INETD_SERVNAME_LENGTH+1];
-#endif /* TPF */
-
-static volatile int die_now = 0;
#ifdef GPROF
/*
@@ -150,7 +157,7 @@ static volatile int die_now = 0;
static void chdir_for_gprof(void)
{
core_server_config *sconf =
- ap_get_module_config(ap_server_conf->module_config, &core_module);
+ ap_get_core_module_config(ap_server_conf->module_config);
char *dir = sconf->gprof_dir;
const char *use_dir;
@@ -168,7 +175,7 @@ static void chdir_for_gprof(void)
APR_GREAD | APR_GEXECUTE |
APR_WREAD | APR_WEXECUTE, pconf);
if(res != APR_SUCCESS && !APR_STATUS_IS_EEXIST(res)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, res, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, res, ap_server_conf, APLOGNO(00142)
"gprof: error creating directory %s", dir);
}
}
@@ -182,9 +189,24 @@ static void chdir_for_gprof(void)
#define chdir_for_gprof()
#endif
-/* XXX - I don't know if TPF will ever use this module or not, so leave
- * the ap_check_signals calls in but disable them - manoj */
-#define ap_check_signals()
+static void prefork_note_child_killed(int childnum, pid_t pid,
+ ap_generation_t gen)
+{
+ AP_DEBUG_ASSERT(childnum != -1); /* no scoreboard squatting with this MPM */
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[childnum].pid,
+ ap_scoreboard_image->parent[childnum].generation,
+ childnum, MPM_CHILD_EXITED);
+ ap_scoreboard_image->parent[childnum].pid = 0;
+}
+
+static void prefork_note_child_started(int slot, pid_t pid)
+{
+ ap_scoreboard_image->parent[slot].pid = pid;
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[slot].pid,
+ retained->my_generation, slot, MPM_CHILD_STARTED);
+}
/* a clean exit from a child with proper cleanup */
static void clean_child_exit(int code) __attribute__ ((noreturn));
@@ -195,6 +217,11 @@ static void clean_child_exit(int code)
if (pchild) {
apr_pool_destroy(pchild);
}
+
+ if (one_process) {
+ prefork_note_child_killed(/* slot */ 0, 0, 0);
+ }
+
ap_mpm_pod_close(pod);
chdir_for_gprof();
exit(code);
@@ -206,13 +233,13 @@ static void accept_mutex_on(void)
if (rv != APR_SUCCESS) {
const char *msg = "couldn't grab the accept mutex";
- if (ap_my_generation !=
+ if (retained->my_generation !=
ap_scoreboard_image->global->running_generation) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, NULL, "%s", msg);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf, APLOGNO(00143) "%s", msg);
clean_child_exit(0);
}
else {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, NULL, "%s", msg);
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(00144) "%s", msg);
exit(APEXIT_CHILDFATAL);
}
}
@@ -224,16 +251,16 @@ static void accept_mutex_off(void)
if (rv != APR_SUCCESS) {
const char *msg = "couldn't release the accept mutex";
- if (ap_my_generation !=
+ if (retained->my_generation !=
ap_scoreboard_image->global->running_generation) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, NULL, "%s", msg);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf, APLOGNO(00145) "%s", msg);
/* don't exit here... we have a connection to
* process, after which point we'll see that the
* generation changed and we'll exit cleanly
*/
}
else {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, NULL, "%s", msg);
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(00146) "%s", msg);
exit(APEXIT_CHILDFATAL);
}
}
@@ -250,74 +277,63 @@ static void accept_mutex_off(void)
#define SAFE_ACCEPT(stmt) do {stmt;} while(0)
#endif
-AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+static int prefork_query(int query_code, int *result, apr_status_t *rv)
{
+ *rv = APR_SUCCESS;
switch(query_code){
case AP_MPMQ_MAX_DAEMON_USED:
*result = ap_daemons_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_NOT_SUPPORTED;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_IS_FORKED:
*result = AP_MPMQ_DYNAMIC;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_DAEMONS:
*result = server_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_THREADS:
*result = HARD_THREAD_LIMIT;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_THREADS:
- *result = 0;
- return APR_SUCCESS;
+ *result = 1;
+ break;
case AP_MPMQ_MIN_SPARE_DAEMONS:
*result = ap_daemons_min_free;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MIN_SPARE_THREADS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_DAEMONS:
*result = ap_daemons_max_free;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_THREADS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_REQUESTS_DAEMON:
*result = ap_max_requests_per_child;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_DAEMONS:
- *result = server_limit;
- return APR_SUCCESS;
+ *result = ap_daemons_limit;
+ break;
case AP_MPMQ_MPM_STATE:
*result = mpm_state;
- return APR_SUCCESS;
+ break;
+ case AP_MPMQ_GENERATION:
+ *result = retained->my_generation;
+ break;
+ default:
+ *rv = APR_ENOTIMPL;
+ break;
}
- return APR_ENOTIMPL;
+ return OK;
}
-#if defined(NEED_WAITPID)
-/*
- Systems without a real waitpid sometimes lose a child's exit while waiting
- for another. Search through the scoreboard for missing children.
- */
-int reap_children(int *exitcode, apr_exit_why_e *status)
+static const char *prefork_get_name(void)
{
- int n, pid;
-
- for (n = 0; n < ap_max_daemons_limit; ++n) {
- if (ap_scoreboard_image->servers[n][0].status != SERVER_DEAD &&
- kill((pid = ap_scoreboard_image->parent[n].pid), 0) == -1) {
- ap_update_child_status_from_indexes(n, 0, SERVER_DEAD, NULL);
- /* just mark it as having a successful exit status */
- *status = APR_PROC_EXIT;
- *exitcode = 0;
- return(pid);
- }
- }
- return 0;
+ return "prefork";
}
-#endif
/*****************************************************************
* Connection structures and accounting...
@@ -328,6 +344,11 @@ static void just_die(int sig)
clean_child_exit(0);
}
+/* volatile because they're updated from a signal handler */
+static int volatile shutdown_pending;
+static int volatile restart_pending;
+static int volatile die_now = 0;
+
static void stop_listening(int sig)
{
mpm_state = AP_MPMQ_STOPPING;
@@ -337,11 +358,6 @@ static void stop_listening(int sig)
die_now = 1;
}
-/* volatile just in case */
-static int volatile shutdown_pending;
-static int volatile restart_pending;
-static int volatile is_graceful;
-
static void sig_term(int sig)
{
if (shutdown_pending == 1) {
@@ -353,7 +369,7 @@ static void sig_term(int sig)
}
mpm_state = AP_MPMQ_STOPPING;
shutdown_pending = 1;
- is_graceful = (sig == AP_SIG_GRACEFUL_STOP);
+ retained->is_graceful = (sig == AP_SIG_GRACEFUL_STOP);
}
/* restart() is the signal handler for SIGHUP and AP_SIG_GRACEFUL
@@ -367,7 +383,7 @@ static void restart(int sig)
}
mpm_state = AP_MPMQ_STOPPING;
restart_pending = 1;
- is_graceful = (sig == AP_SIG_GRACEFUL);
+ retained->is_graceful = (sig == AP_SIG_GRACEFUL);
}
static void set_signals(void)
@@ -386,30 +402,33 @@ static void set_signals(void)
sa.sa_handler = sig_term;
if (sigaction(SIGTERM, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00147) "sigaction(SIGTERM)");
#ifdef AP_SIG_GRACEFUL_STOP
if (sigaction(AP_SIG_GRACEFUL_STOP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00148)
"sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")");
#endif
#ifdef SIGINT
if (sigaction(SIGINT, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00149) "sigaction(SIGINT)");
#endif
#ifdef SIGXCPU
sa.sa_handler = SIG_DFL;
if (sigaction(SIGXCPU, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXCPU)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00150) "sigaction(SIGXCPU)");
#endif
#ifdef SIGXFSZ
- sa.sa_handler = SIG_DFL;
+ /* For systems following the LFS standard, ignoring SIGXFSZ allows
+ * a write() beyond the 2GB limit to fail gracefully with E2BIG
+ * rather than terminate the process. */
+ sa.sa_handler = SIG_IGN;
if (sigaction(SIGXFSZ, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXFSZ)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00151) "sigaction(SIGXFSZ)");
#endif
#ifdef SIGPIPE
sa.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGPIPE)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00152) "sigaction(SIGPIPE)");
#endif
/* we want to ignore HUPs and AP_SIG_GRACEFUL while we're busy
@@ -419,16 +438,16 @@ static void set_signals(void)
sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL);
sa.sa_handler = restart;
if (sigaction(SIGHUP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00153) "sigaction(SIGHUP)");
if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(" AP_SIG_GRACEFUL_STRING ")");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00154) "sigaction(" AP_SIG_GRACEFUL_STRING ")");
#else
if (!one_process) {
#ifdef SIGXCPU
apr_signal(SIGXCPU, SIG_DFL);
#endif /* SIGXCPU */
#ifdef SIGXFSZ
- apr_signal(SIGXFSZ, SIG_DFL);
+ apr_signal(SIGXFSZ, SIG_IGN);
#endif /* SIGXFSZ */
}
@@ -458,18 +477,12 @@ static void set_signals(void)
static int requests_this_child;
static int num_listensocks = 0;
-
-int ap_graceful_stop_signalled(void)
-{
- /* Return true if the server is stopping for whatever reason; the
- * function is used to initiate a fast exit from the connection
- * processing loop. */
- return mpm_state == AP_MPMQ_STOPPING;
-}
-
-
static void child_main(int child_num_arg)
{
+#if APR_HAS_THREADS
+ apr_thread_t *thd = NULL;
+ apr_os_thread_t osthd;
+#endif
apr_pool_t *ptrans;
apr_allocator_t *allocator;
apr_status_t status;
@@ -479,6 +492,7 @@ static void child_main(int child_num_arg)
ap_sb_handle_t *sbh;
apr_bucket_alloc_t *bucket_alloc;
int last_poll_idx = 0;
+ const char *lockfile;
mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this
* child initializes
@@ -497,21 +511,32 @@ static void child_main(int child_num_arg)
apr_allocator_max_free_set(allocator, ap_max_mem_free);
apr_pool_create_ex(&pchild, pconf, NULL, allocator);
apr_allocator_owner_set(allocator, pchild);
+ apr_pool_tag(pchild, "pchild");
+
+#if APR_HAS_THREADS
+ osthd = apr_os_thread_current();
+ apr_os_thread_put(&thd, &osthd, pchild);
+#endif
apr_pool_create(&ptrans, pchild);
apr_pool_tag(ptrans, "transaction");
/* needs to be done before we switch UIDs so we have permissions */
ap_reopen_scoreboard(pchild, NULL, 0);
- status = apr_proc_mutex_child_init(&accept_mutex, ap_lock_fname, pchild);
+ lockfile = apr_proc_mutex_lockfile(accept_mutex);
+ status = apr_proc_mutex_child_init(&accept_mutex,
+ lockfile,
+ pchild);
if (status != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf, APLOGNO(00155)
"Couldn't initialize cross-process lock in child "
- "(%s) (%d)", ap_lock_fname, ap_accept_lock_mech);
+ "(%s) (%s)",
+ lockfile ? lockfile : "none",
+ apr_proc_mutex_name(accept_mutex));
clean_child_exit(APEXIT_CHILDFATAL);
}
- if (unixd_setup_child()) {
+ if (ap_run_drop_privileges(pchild, ap_server_conf)) {
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -524,7 +549,7 @@ static void child_main(int child_num_arg)
/* Set up the pollfd array */
status = apr_pollset_create(&pollset, num_listensocks, pchild, 0);
if (status != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf, APLOGNO(00156)
"Couldn't create pollset in child; check system or user limits");
clean_child_exit(APEXIT_CHILDSICK); /* assume temporary resource issue */
}
@@ -537,8 +562,14 @@ static void child_main(int child_num_arg)
pfd.reqevents = APR_POLLIN;
pfd.client_data = lr;
- /* ### check the status */
- (void) apr_pollset_add(pollset, &pfd);
+ status = apr_pollset_add(pollset, &pfd);
+ if (status != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf, APLOGNO(00157)
+ "Couldn't add listener to pollset; check system or user limits");
+ clean_child_exit(APEXIT_CHILDSICK);
+ }
+
+ lr->accept_func = ap_unixd_accept;
}
mpm_state = AP_MPMQ_RUNNING;
@@ -585,20 +616,16 @@ static void child_main(int child_num_arg)
/* check for termination first so we don't sleep for a while in
* poll if already signalled
*/
- if (one_process && shutdown_pending) {
- SAFE_ACCEPT(accept_mutex_off());
- return;
- }
- else if (die_now) {
- /* In graceful stop/restart; drop the mutex
- * and terminate the child. */
+ if (die_now /* in graceful stop/restart */
+ || (one_process && shutdown_pending)) {
SAFE_ACCEPT(accept_mutex_off());
clean_child_exit(0);
}
+
/* timeout == 10 seconds to avoid a hang at graceful restart/stop
* caused by the closing of sockets by the signal handler
*/
- status = apr_pollset_poll(pollset, apr_time_from_sec(10),
+ status = apr_pollset_poll(pollset, apr_time_from_sec(10),
&numdesc, &pdesc);
if (status != APR_SUCCESS) {
if (APR_STATUS_IS_TIMEUP(status) ||
@@ -612,7 +639,7 @@ static void child_main(int child_num_arg)
* occasionally, and we'd loop forever due to it.
*/
ap_log_error(APLOG_MARK, APLOG_ERR, status,
- ap_server_conf, "apr_pollset_poll: (listen)");
+ ap_server_conf, APLOGNO(00158) "apr_pollset_poll: (listen)");
SAFE_ACCEPT(accept_mutex_off());
clean_child_exit(1);
}
@@ -664,6 +691,9 @@ static void child_main(int child_num_arg)
current_conn = ap_run_create_connection(ptrans, ap_server_conf, csd, my_child_num, sbh, bucket_alloc);
if (current_conn) {
+#if APR_HAS_THREADS
+ current_conn->current_thread = thd;
+#endif
ap_process_connection(current_conn, csd);
ap_lingering_close(current_conn);
}
@@ -676,7 +706,7 @@ static void child_main(int child_num_arg)
if (ap_mpm_pod_check(pod) == APR_SUCCESS) { /* selected as idle? */
die_now = 1;
}
- else if (ap_my_generation !=
+ else if (retained->my_generation !=
ap_scoreboard_image->global->running_generation) { /* restart? */
/* yeah, this could be non-graceful restart, in which case the
* parent will kill us soon enough, but why bother checking?
@@ -684,11 +714,7 @@ static void child_main(int child_num_arg)
die_now = 1;
}
}
- /* This apr_pool_clear call is redundant, should be redundant, but compensates
- * a flaw in the apr reslist code. This should be removed once that flaw has
- * been addressed.
- */
- apr_pool_clear(ptrans);
+ apr_pool_clear(ptrans); /* kludge to avoid crash in APR reslist cleanup code */
clean_child_exit(0);
}
@@ -697,8 +723,8 @@ static int make_child(server_rec *s, int slot)
{
int pid;
- if (slot + 1 > ap_max_daemons_limit) {
- ap_max_daemons_limit = slot + 1;
+ if (slot + 1 > retained->max_daemons_limit) {
+ retained->max_daemons_limit = slot + 1;
}
if (one_process) {
@@ -709,8 +735,9 @@ static int make_child(server_rec *s, int slot)
apr_signal(SIGQUIT, SIG_DFL);
#endif
apr_signal(SIGTERM, sig_term);
+ prefork_note_child_started(slot, getpid());
child_main(slot);
- return 0;
+ /* NOTREACHED */
}
(void) ap_update_child_status_from_indexes(slot, 0, SERVER_STARTING,
@@ -719,13 +746,11 @@ static int make_child(server_rec *s, int slot)
#ifdef _OSD_POSIX
/* BS2000 requires a "special" version of fork() before a setuid() call */
- if ((pid = os_fork(unixd_config.user_name)) == -1) {
-#elif defined(TPF)
- if ((pid = os_fork(s, slot)) == -1) {
+ if ((pid = os_fork(ap_unixd_config.user_name)) == -1) {
#else
if ((pid = fork()) == -1) {
#endif
- ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "fork: Unable to fork new process");
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, APLOGNO(00159) "fork: Unable to fork new process");
/* fork didn't succeed. Fix the scoreboard or else
* it will say SERVER_STARTING forever and ever
@@ -750,8 +775,8 @@ static int make_child(server_rec *s, int slot)
int status = bindprocessor(BINDPROCESS, (int)getpid(),
PROCESSOR_CLASS_ANY);
if (status != OK) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno,
- ap_server_conf, "processor unbind failed %d", status);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, errno,
+ ap_server_conf, APLOGNO(00160) "processor unbind failed");
}
#endif
RAISE_SIGSTOP(MAKE_CHILD);
@@ -768,7 +793,7 @@ static int make_child(server_rec *s, int slot)
child_main(slot);
}
- ap_scoreboard_image->parent[slot].pid = pid;
+ prefork_note_child_started(slot, pid);
return 0;
}
@@ -790,23 +815,9 @@ static void startup_children(int number_to_start)
}
}
-
-/*
- * idle_spawn_rate is the number of children that will be spawned on the
- * next maintenance cycle if there aren't enough idle servers. It is
- * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
- * without the need to spawn.
- */
-static int idle_spawn_rate = 1;
-#ifndef MAX_SPAWN_RATE
-#define MAX_SPAWN_RATE (32)
-#endif
-static int hold_off_on_exponential_spawning;
-
static void perform_idle_server_maintenance(apr_pool_t *p)
{
int i;
- int to_kill;
int idle_count;
worker_score *ws;
int free_length;
@@ -817,7 +828,6 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
/* initialize the free_list */
free_length = 0;
- to_kill = -1;
idle_count = 0;
last_non_dead = -1;
total_non_dead = 0;
@@ -825,13 +835,13 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
for (i = 0; i < ap_daemons_limit; ++i) {
int status;
- if (i >= ap_max_daemons_limit && free_length == idle_spawn_rate)
+ if (i >= retained->max_daemons_limit && free_length == retained->idle_spawn_rate)
break;
ws = &ap_scoreboard_image->servers[i][0];
status = ws->status;
if (status == SERVER_DEAD) {
/* try to keep children numbers as low as possible */
- if (free_length < idle_spawn_rate) {
+ if (free_length < retained->idle_spawn_rate) {
free_slots[free_length] = i;
++free_length;
}
@@ -845,77 +855,58 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
*/
if (status <= SERVER_READY) {
++ idle_count;
- /* always kill the highest numbered child if we have to...
- * no really well thought out reason ... other than observing
- * the server behaviour under linux where lower numbered children
- * tend to service more hits (and hence are more likely to have
- * their data in cpu caches).
- */
- to_kill = i;
}
++total_non_dead;
last_non_dead = i;
}
}
- ap_max_daemons_limit = last_non_dead + 1;
+ retained->max_daemons_limit = last_non_dead + 1;
if (idle_count > ap_daemons_max_free) {
/* kill off one child... we use the pod because that'll cause it to
* shut down gracefully, in case it happened to pick up a request
* while we were counting
*/
ap_mpm_pod_signal(pod);
- idle_spawn_rate = 1;
+ retained->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_ERR, 0, ap_server_conf,
- "server reached MaxClients setting, consider"
- " raising the MaxClients setting");
- reported = 1;
+ if (!retained->maxclients_reported) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00161)
+ "server reached MaxRequestWorkers setting, consider"
+ " raising the MaxRequestWorkers setting");
+ retained->maxclients_reported = 1;
}
- idle_spawn_rate = 1;
+ retained->idle_spawn_rate = 1;
}
else {
- if (idle_spawn_rate >= 8) {
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
+ if (retained->idle_spawn_rate >= 8) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00162)
"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,
+ "%d total children", retained->idle_spawn_rate,
idle_count, total_non_dead);
}
for (i = 0; i < free_length; ++i) {
-#ifdef TPF
- if (make_child(ap_server_conf, free_slots[i]) == -1) {
- if(free_length == 1) {
- shutdown_pending = 1;
- ap_log_error(APLOG_MARK, APLOG_EMERG, 0, ap_server_conf,
- "No active child processes: shutting down");
- }
- }
-#else
make_child(ap_server_conf, free_slots[i]);
-#endif /* TPF */
}
/* the next time around we want to spawn twice as many if this
* wasn't good enough, but not if we've just done a graceful
*/
- if (hold_off_on_exponential_spawning) {
- --hold_off_on_exponential_spawning;
+ if (retained->hold_off_on_exponential_spawning) {
+ --retained->hold_off_on_exponential_spawning;
}
- else if (idle_spawn_rate < MAX_SPAWN_RATE) {
- idle_spawn_rate *= 2;
+ else if (retained->idle_spawn_rate < MAX_SPAWN_RATE) {
+ retained->idle_spawn_rate *= 2;
}
}
}
else {
- idle_spawn_rate = 1;
+ retained->idle_spawn_rate = 1;
}
}
@@ -923,7 +914,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
* Executive routines.
*/
-int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
+static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
int index;
int remaining_children_to_start;
@@ -931,61 +922,32 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
ap_log_pid(pconf, ap_pid_fname);
- first_server_limit = server_limit;
- if (changed_limit_at_restart) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
- "WARNING: Attempt to change ServerLimit "
- "ignored during restart");
- changed_limit_at_restart = 0;
- }
-
/* Initialize cross-process accept lock */
- ap_lock_fname = apr_psprintf(_pconf, "%s.%" APR_PID_T_FMT,
- ap_server_root_relative(_pconf, ap_lock_fname),
- ap_my_pid);
-
- rv = apr_proc_mutex_create(&accept_mutex, ap_lock_fname,
- ap_accept_lock_mech, _pconf);
+ rv = ap_proc_mutex_create(&accept_mutex, NULL, AP_ACCEPT_MUTEX_TYPE, NULL,
+ s, _pconf, 0);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
- "Couldn't create accept lock (%s) (%d)",
- ap_lock_fname, ap_accept_lock_mech);
mpm_state = AP_MPMQ_STOPPING;
- return 1;
- }
-
-#if APR_USE_SYSVSEM_SERIALIZE
- if (ap_accept_lock_mech == APR_LOCK_DEFAULT ||
- ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
-#else
- if (ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
-#endif
- rv = unixd_set_proc_mutex_perms(accept_mutex);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
- "Couldn't set permissions on cross-process lock; "
- "check User and Group directives");
- mpm_state = AP_MPMQ_STOPPING;
- return 1;
- }
+ return DONE;
}
- if (!is_graceful) {
+ if (!retained->is_graceful) {
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
mpm_state = AP_MPMQ_STOPPING;
- return 1;
+ return DONE;
}
/* fix the generation number in the global score; we just got a new,
* cleared scoreboard
*/
- ap_scoreboard_image->global->running_generation = ap_my_generation;
+ ap_scoreboard_image->global->running_generation = retained->my_generation;
}
+ restart_pending = shutdown_pending = 0;
set_signals();
if (one_process) {
AP_MONCONTROL(1);
make_child(ap_server_conf, 0);
+ /* NOTREACHED */
}
else {
if (ap_daemons_max_free < ap_daemons_min_free + 1) /* Don't thrash... */
@@ -1003,7 +965,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
if (remaining_children_to_start > ap_daemons_limit) {
remaining_children_to_start = ap_daemons_limit;
}
- if (!is_graceful) {
+ if (!retained->is_graceful) {
startup_children(remaining_children_to_start);
remaining_children_to_start = 0;
}
@@ -1011,21 +973,19 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
/* give the system some time to recover before kicking into
* exponential mode
*/
- hold_off_on_exponential_spawning = 10;
+ retained->hold_off_on_exponential_spawning = 10;
}
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00163)
"%s configured -- resuming normal operations",
ap_get_server_description());
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00164)
"Server built: %s", ap_get_server_built());
-#ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
- "AcceptMutex: %s (default: %s)",
+ ap_log_command_line(plog, s);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00165)
+ "Accept mutex: %s (default: %s)",
apr_proc_mutex_name(accept_mutex),
apr_proc_mutex_defname());
-#endif
- restart_pending = shutdown_pending = 0;
mpm_state = AP_MPMQ_RUNNING;
@@ -1036,7 +996,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
/* this is a memory leak, but I'll fix it later. */
apr_proc_t pid;
- ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);
+ ap_wait_or_timeout(&exitwhy, &status, &pid, pconf, ap_server_conf);
/* 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
@@ -1044,21 +1004,37 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
*/
if (pid.pid != -1) {
processed_status = ap_process_child_status(&pid, exitwhy, status);
+ child_slot = ap_find_child_by_pid(&pid);
if (processed_status == APEXIT_CHILDFATAL) {
- mpm_state = AP_MPMQ_STOPPING;
- return 1;
+ /* fix race condition found in PR 39311
+ * A child created at the same time as a graceful happens
+ * can find the lock missing and create a fatal error.
+ * It is not fatal for the last generation to be in this state.
+ */
+ if (child_slot < 0
+ || ap_get_scoreboard_process(child_slot)->generation
+ == retained->my_generation) {
+ mpm_state = AP_MPMQ_STOPPING;
+ return DONE;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, APLOGNO(00166)
+ "Ignoring fatal error in child of previous "
+ "generation (pid %ld).",
+ (long)pid.pid);
+ }
}
/* non-fatal death... note that it's gone in the scoreboard. */
- child_slot = find_child_by_pid(&pid);
if (child_slot >= 0) {
(void) ap_update_child_status_from_indexes(child_slot, 0, SERVER_DEAD,
(request_rec *) NULL);
+ prefork_note_child_killed(child_slot, 0, 0);
if (processed_status == APEXIT_CHILDSICK) {
/* child detected a resource shortage (E[NM]FILE, ENOBUFS, etc)
* cut the fork rate to the minimum
*/
- idle_spawn_rate = 1;
+ retained->idle_spawn_rate = 1;
}
else if (remaining_children_to_start
&& child_slot < ap_daemons_limit) {
@@ -1074,13 +1050,13 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
/* handled */
#endif
}
- else if (is_graceful) {
+ else if (retained->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_WARNING,
- 0, ap_server_conf,
+ 0, ap_server_conf, APLOGNO(00167)
"long lost child came home! (pid %ld)", (long)pid.pid);
}
/* Don't perform idle maintenance when a child dies,
@@ -1105,40 +1081,27 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
}
perform_idle_server_maintenance(pconf);
-#ifdef TPF
- shutdown_pending = os_check_server(tpf_server_name);
- ap_check_signals();
- sleep(1);
-#endif /*TPF */
}
} /* one_process */
mpm_state = AP_MPMQ_STOPPING;
- if (shutdown_pending && !is_graceful) {
+ if (shutdown_pending && !retained->is_graceful) {
/* Time to shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
- if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGTERM");
+ if (ap_unixd_killpg(getpgrp(), SIGTERM) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00168) "killpg SIGTERM");
}
- ap_reclaim_child_processes(1); /* Start with SIGTERM */
+ ap_reclaim_child_processes(1, /* Start with SIGTERM */
+ prefork_note_child_killed);
/* cleanup pid file on normal shutdown */
- {
- const char *pidfile = NULL;
- pidfile = ap_server_root_relative (pconf, ap_pid_fname);
- if ( pidfile != NULL && unlink(pidfile) == 0)
- ap_log_error(APLOG_MARK, APLOG_INFO,
- 0, ap_server_conf,
- "removed PID file %s (pid=%ld)",
- pidfile, (long)getpid());
- }
-
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_remove_pid(pconf, ap_pid_fname);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00169)
"caught SIGTERM, shutting down");
- return 1;
+ return DONE;
} else if (shutdown_pending) {
/* Time to perform a graceful shut down:
* Reap the inactive children, and ask the active ones
@@ -1152,7 +1115,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
ap_close_listeners();
/* kill off the idle ones */
- ap_mpm_pod_killpg(pod, ap_max_daemons_limit);
+ ap_mpm_pod_killpg(pod, retained->max_daemons_limit);
/* Send SIGUSR1 to the active children */
active_children = 0;
@@ -1165,20 +1128,11 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
}
/* Allow each child which actually finished to exit */
- ap_relieve_child_processes();
+ ap_relieve_child_processes(prefork_note_child_killed);
/* cleanup pid file */
- {
- const char *pidfile = NULL;
- pidfile = ap_server_root_relative (pconf, ap_pid_fname);
- if ( pidfile != NULL && unlink(pidfile) == 0)
- ap_log_error(APLOG_MARK, APLOG_INFO,
- 0, ap_server_conf,
- "removed PID file %s (pid=%ld)",
- pidfile, (long)getpid());
- }
-
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_remove_pid(pconf, ap_pid_fname);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00170)
"caught " AP_SIG_GRACEFUL_STOP_STRING ", shutting down gracefully");
if (ap_graceful_shutdown_timeout) {
@@ -1193,7 +1147,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
sleep(1);
/* Relieve any children which have now exited */
- ap_relieve_child_processes();
+ ap_relieve_child_processes(prefork_note_child_killed);
active_children = 0;
for (index = 0; index < ap_daemons_limit; ++index) {
@@ -1210,9 +1164,9 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
* way, try and make sure that all of our processes are
* really dead.
*/
- unixd_killpg(getpgrp(), SIGTERM);
+ ap_unixd_killpg(getpgrp(), SIGTERM);
- return 1;
+ return DONE;
}
/* we've been told to restart */
@@ -1220,22 +1174,22 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
apr_signal(AP_SIG_GRACEFUL, SIG_IGN);
if (one_process) {
/* not worth thinking about */
- return 1;
+ return DONE;
}
/* advance to the next generation */
/* XXX: we really need to make sure this new generation number isn't in
* use by any of the children.
*/
- ++ap_my_generation;
- ap_scoreboard_image->global->running_generation = ap_my_generation;
+ ++retained->my_generation;
+ ap_scoreboard_image->global->running_generation = retained->my_generation;
- if (is_graceful) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ if (retained->is_graceful) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00171)
"Graceful restart requested, doing restart");
/* kill off the idle ones */
- ap_mpm_pod_killpg(pod, ap_max_daemons_limit);
+ ap_mpm_pod_killpg(pod, retained->max_daemons_limit);
/* This is mostly for debugging... so that we know what is still
* gracefully dealing with existing request. This will break
@@ -1258,15 +1212,16 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
}
else {
/* Kill 'em off */
- if (unixd_killpg(getpgrp(), SIGHUP) < 0) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGHUP");
+ if (ap_unixd_killpg(getpgrp(), SIGHUP) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00172) "killpg SIGHUP");
}
- ap_reclaim_child_processes(0); /* Not when just starting up */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_reclaim_child_processes(0, /* Not when just starting up */
+ prefork_note_child_killed);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00173)
"SIGHUP received. Attempting to restart");
}
- return 0;
+ return OK;
}
/* This really should be a post_config hook, but the error log is already
@@ -1274,20 +1229,29 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
*/
static int prefork_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
+ int startup = 0;
+ int level_flags = 0;
apr_status_t rv;
pconf = p;
- ap_server_conf = s;
+
+ /* the reverse of pre_config, we want this only the first time around */
+ if (retained->module_loads == 1) {
+ startup = 1;
+ level_flags |= APLOG_STARTUP;
+ }
if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
- ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0,
- NULL, "no listening sockets available, shutting down");
+ ap_log_error(APLOG_MARK, APLOG_ALERT | level_flags, 0,
+ (startup ? NULL : s),
+ "no listening sockets available, shutting down");
return DONE;
}
if ((rv = ap_mpm_pod_open(pconf, &pod))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_STARTUP, rv, NULL,
- "Could not open pipe-of-death.");
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not open pipe-of-death");
return DONE;
}
return OK;
@@ -1295,9 +1259,9 @@ static int prefork_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
static int prefork_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
{
- static int restart_num = 0;
int no_detach, debug, foreground;
apr_status_t rv;
+ const char *userdata_key = "mpm_prefork_module";
mpm_state = AP_MPMQ_STARTING;
@@ -1314,59 +1278,182 @@ static int prefork_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp
foreground = ap_exists_config_define("FOREGROUND");
}
- /* sigh, want this only the second time around */
- if (restart_num++ == 1) {
- is_graceful = 0;
+ ap_mutex_register(p, AP_ACCEPT_MUTEX_TYPE, NULL, APR_LOCK_DEFAULT, 0);
+ /* sigh, want this only the second time around */
+ retained = ap_retained_data_get(userdata_key);
+ if (!retained) {
+ retained = ap_retained_data_create(userdata_key, sizeof(*retained));
+ retained->max_daemons_limit = -1;
+ retained->idle_spawn_rate = 1;
+ }
+ ++retained->module_loads;
+ if (retained->module_loads == 2) {
if (!one_process && !foreground) {
+ /* before we detach, setup crash handlers to log to errorlog */
+ ap_fatal_signal_setup(ap_server_conf, pconf);
rv = apr_proc_detach(no_detach ? APR_PROC_DETACH_FOREGROUND
: APR_PROC_DETACH_DAEMONIZE);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, APLOGNO(00174)
"apr_proc_detach failed");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
-
- parent_pid = ap_my_pid = getpid();
}
- unixd_pre_config(ptemp);
+ parent_pid = ap_my_pid = getpid();
+
ap_listen_pre_config();
ap_daemons_to_start = DEFAULT_START_DAEMON;
ap_daemons_min_free = DEFAULT_MIN_FREE_DAEMON;
ap_daemons_max_free = DEFAULT_MAX_FREE_DAEMON;
+ server_limit = DEFAULT_SERVER_LIMIT;
ap_daemons_limit = server_limit;
- ap_pid_fname = DEFAULT_PIDLOG;
- ap_lock_fname = DEFAULT_LOCKFILE;
- ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
ap_extended_status = 0;
-#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
- ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-#endif
- apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+ return OK;
+}
+
+static int prefork_check_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ int startup = 0;
+
+ /* the reverse of pre_config, we want this only the first time around */
+ if (retained->module_loads == 1) {
+ startup = 1;
+ }
+
+ if (server_limit > MAX_SERVER_LIMIT) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00175)
+ "WARNING: ServerLimit of %d exceeds compile-time "
+ "limit of", server_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d servers, decreasing to %d.",
+ MAX_SERVER_LIMIT, MAX_SERVER_LIMIT);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00176)
+ "ServerLimit of %d exceeds compile-time limit "
+ "of %d, decreasing to match",
+ server_limit, MAX_SERVER_LIMIT);
+ }
+ server_limit = MAX_SERVER_LIMIT;
+ }
+ else if (server_limit < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00177)
+ "WARNING: ServerLimit of %d not allowed, "
+ "increasing to 1.", server_limit);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00178)
+ "ServerLimit of %d not allowed, increasing to 1",
+ server_limit);
+ }
+ server_limit = 1;
+ }
+
+ /* you cannot change ServerLimit across a restart; ignore
+ * any such attempts
+ */
+ if (!retained->first_server_limit) {
+ retained->first_server_limit = server_limit;
+ }
+ else if (server_limit != retained->first_server_limit) {
+ /* don't need a startup console version here */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00179)
+ "changing ServerLimit to %d from original value of %d "
+ "not allowed during restart",
+ server_limit, retained->first_server_limit);
+ server_limit = retained->first_server_limit;
+ }
+
+ if (ap_daemons_limit > server_limit) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00180)
+ "WARNING: MaxRequestWorkers of %d exceeds ServerLimit "
+ "value of", ap_daemons_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d servers, decreasing MaxRequestWorkers to %d.",
+ server_limit, server_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " To increase, please see the ServerLimit "
+ "directive.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00181)
+ "MaxRequestWorkers of %d exceeds ServerLimit value "
+ "of %d, decreasing to match",
+ ap_daemons_limit, server_limit);
+ }
+ ap_daemons_limit = server_limit;
+ }
+ else if (ap_daemons_limit < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00182)
+ "WARNING: MaxRequestWorkers of %d not allowed, "
+ "increasing to 1.", ap_daemons_limit);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00183)
+ "MaxRequestWorkers of %d not allowed, increasing to 1",
+ ap_daemons_limit);
+ }
+ ap_daemons_limit = 1;
+ }
+
+ /* ap_daemons_to_start > ap_daemons_limit checked in prefork_run() */
+ if (ap_daemons_to_start < 0) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00184)
+ "WARNING: StartServers of %d not allowed, "
+ "increasing to 1.", ap_daemons_to_start);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00185)
+ "StartServers of %d not allowed, increasing to 1",
+ ap_daemons_to_start);
+ }
+ ap_daemons_to_start = 1;
+ }
+
+ if (ap_daemons_min_free < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00186)
+ "WARNING: MinSpareServers of %d not allowed, "
+ "increasing to 1", ap_daemons_min_free);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " to avoid almost certain server failure.");
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " Please read the documentation.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00187)
+ "MinSpareServers of %d not allowed, increasing to 1",
+ ap_daemons_min_free);
+ }
+ ap_daemons_min_free = 1;
+ }
+
+ /* ap_daemons_max_free < ap_daemons_min_free + 1 checked in prefork_run() */
return OK;
}
static void prefork_hooks(apr_pool_t *p)
{
- /* The prefork open_logs phase must run before the core's, or stderr
+ /* Our open_logs hook function must run before the core's, or stderr
* will be redirected to a file, and the messages won't print to the
* console.
*/
static const char *const aszSucc[] = {"core.c", NULL};
-#ifdef AUX3
- (void) set42sig();
-#endif
-
- ap_hook_open_logs(prefork_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
+ ap_hook_open_logs(prefork_open_logs, NULL, aszSucc, APR_HOOK_REALLY_FIRST);
/* we need to set the MPM state before other pre-config hooks use MPM query
* to retrieve it, so register as REALLY_FIRST
*/
ap_hook_pre_config(prefork_pre_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
+ ap_hook_check_config(prefork_check_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm(prefork_run, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_query(prefork_query, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_get_name(prefork_get_name, NULL, NULL, APR_HOOK_MIDDLE);
}
static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const char *arg)
@@ -1388,16 +1475,6 @@ static const char *set_min_free_servers(cmd_parms *cmd, void *dummy, const char
}
ap_daemons_min_free = atoi(arg);
- if (ap_daemons_min_free <= 0) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: detected MinSpareServers set to non-positive.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Resetting to 1 to avoid almost certain Apache failure.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Please read the documentation.");
- ap_daemons_min_free = 1;
- }
-
return NULL;
}
@@ -1418,69 +1495,27 @@ static const char *set_max_clients (cmd_parms *cmd, void *dummy, const char *arg
if (err != NULL) {
return err;
}
-
- ap_daemons_limit = atoi(arg);
- if (ap_daemons_limit > server_limit) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxClients of %d exceeds ServerLimit value "
- "of %d servers,", ap_daemons_limit, server_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering MaxClients to %d. To increase, please "
- "see the ServerLimit", server_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " directive.");
- ap_daemons_limit = server_limit;
- }
- else if (ap_daemons_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require MaxClients > 0, setting to 1");
- ap_daemons_limit = 1;
+ if (!strcasecmp(cmd->cmd->name, "MaxClients")) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(00188)
+ "MaxClients is deprecated, use MaxRequestWorkers "
+ "instead.");
}
+ ap_daemons_limit = atoi(arg);
return NULL;
}
static const char *set_server_limit (cmd_parms *cmd, void *dummy, const char *arg)
{
- int tmp_server_limit;
-
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
- tmp_server_limit = atoi(arg);
- /* you cannot change ServerLimit across a restart; ignore
- * any such attempts
- */
- if (first_server_limit &&
- tmp_server_limit != server_limit) {
- /* how do we log a message? the error log is a bit bucket at this
- * point; we'll just have to set a flag so that ap_mpm_run()
- * logs a warning later
- */
- changed_limit_at_restart = 1;
- return NULL;
- }
- server_limit = tmp_server_limit;
-
- if (server_limit > MAX_SERVER_LIMIT) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: ServerLimit of %d exceeds compile time limit "
- "of %d servers,", server_limit, MAX_SERVER_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering ServerLimit to %d.", MAX_SERVER_LIMIT);
- server_limit = MAX_SERVER_LIMIT;
- }
- else if (server_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require ServerLimit > 0, setting to 1");
- server_limit = 1;
- }
+ server_limit = atoi(arg);
return NULL;
}
static const command_rec prefork_cmds[] = {
-UNIX_DAEMON_COMMANDS,
LISTEN_COMMANDS,
AP_INIT_TAKE1("StartServers", set_daemons_to_start, NULL, RSRC_CONF,
"Number of child processes launched at server startup"),
@@ -1489,16 +1524,18 @@ AP_INIT_TAKE1("MinSpareServers", set_min_free_servers, NULL, RSRC_CONF,
AP_INIT_TAKE1("MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF,
"Maximum number of idle children"),
AP_INIT_TAKE1("MaxClients", set_max_clients, NULL, RSRC_CONF,
+ "Deprecated name of MaxRequestWorkers"),
+AP_INIT_TAKE1("MaxRequestWorkers", set_max_clients, NULL, RSRC_CONF,
"Maximum number of children alive at the same time"),
AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF,
- "Maximum value of MaxClients for this run of Apache"),
+ "Maximum value of MaxRequestWorkers for this run of Apache"),
AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND,
{ NULL }
};
-module AP_MODULE_DECLARE_DATA mpm_prefork_module = {
+AP_DECLARE_MODULE(mpm_prefork) = {
MPM20_MODULE_STUFF,
- ap_mpm_rewrite_args, /* hook to run before apache parses args */
+ NULL, /* hook to run before apache parses args */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
diff --git a/server/mpm/winnt/Makefile.in b/server/mpm/winnt/Makefile.in
index 38957c87..f34af9cb 100644
--- a/server/mpm/winnt/Makefile.in
+++ b/server/mpm/winnt/Makefile.in
@@ -1,5 +1 @@
-
-LTLIBRARY_NAME = libwinnt.la
-LTLIBRARY_SOURCES = mpm_winnt.c child.c nt_eventlog.c service.c
-
-include $(top_srcdir)/build/ltlib.mk
+include $(top_srcdir)/build/special.mk
diff --git a/server/mpm/winnt/Win9xConHook.c b/server/mpm/winnt/Win9xConHook.c
deleted file mode 100644
index 0dbcf80d..00000000
--- a/server/mpm/winnt/Win9xConHook.c
+++ /dev/null
@@ -1,697 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifdef WIN32
-
-/*
- * Win9xConHook.dll - a hook proc to clean up Win95/98 console behavior.
- *
- * It is well(?) documented by Microsoft that the Win9x HandlerRoutine
- * hooked by the SetConsoleCtrlHandler never receives the CTRL_CLOSE_EVENT,
- * CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT signals.
- *
- * It is possible to have a second window to monitor the WM_ENDSESSION
- * message, but the close button still fails..
- *
- * There is a 16bit polling method for the close window option, but this
- * is CPU intensive and requires thunking.
- *
- * Attempts to subclass the 'tty' console fail, since that message thread
- * is actually owned by the 16 bit winoldap.mod process, although the
- * window reports it is owned by the process/thread of the console app.
- *
- * Win9xConHook is thunks the WM_CLOSE and WM_ENDSESSION messages,
- * first through a window hook procedure in the winoldap context, into
- * a subclass WndProc, and on to a second hidden monitor window in the
- * console application's context that dispatches them to the console app's
- * registered HandlerRoutine.
- */
-
-/* This debugging define turns on output to COM1, although you better init
- * the port first (even using hyperterm). It's the only way to catch the
- * goings on within system logoff/shutdown.
- * #define DBG 1
- */
-
-#include <windows.h>
-
-/* Variables used within any process context:
- * hookwndmsg is a shared message to send Win9xConHook signals
- * origwndprop is a wndprop atom to store the orig wndproc of the tty
- * hookwndprop is a wndprop atom to store the hwnd of the hidden child
- * is_service reminds us to unmark this process on the way out
- */
-static UINT hookwndmsg = 0;
-static LPCTSTR origwndprop;
-static LPCTSTR hookwndprop;
-static BOOL is_service = 0;
-//static HMODULE hmodThis = NULL;
-
-/* Variables used within the tty processes' context:
- * is_tty flags this process; -1 == unknown, 1 == if tty, 0 == if not
- * hw_tty is the handle of the top level tty in this process context
- * is_subclassed is toggled to assure DllMain removes the subclass on unload
- * hmodLock is there to try and prevent this dll from being unloaded if the
- * hook is removed while we are subclassed
- */
-static int is_tty = -1;
-static HWND hwtty = NULL;
-static BOOL is_subclassed = 0;
-
-// This simply causes a gpfault the moment it tries to FreeLibrary within
-// the subclass procedure ... not good.
-//static HMODULE hmodLock = NULL;
-
-/* Variables used within the service or console app's context:
- * hmodHook is the instance handle of this module for registering the hooks
- * hhkGetMessage is the hook handle for catching Posted messages
- * hhkGetMessage is the hook handle for catching Sent messages
- * monitor_hwnd is the invisible window that handles our tty messages
- * the tty_info strucure is used to pass args into the hidden window's thread
- */
-static HMODULE hmodHook = NULL;
-static HHOOK hhkGetMessage;
-//static HHOOK hhkCallWndProc;
-static HWND monitor_hwnd = NULL;
-
-typedef struct {
- PHANDLER_ROUTINE phandler;
- HINSTANCE instance;
- HWND parent;
- INT type;
- LPCSTR name;
-} tty_info;
-
-/* These are the GetWindowLong offsets for the hidden window's internal info
- * gwltty_phandler is the address of the app's HandlerRoutine
- * gwltty_ttywnd is the tty this hidden window will handle messages from
- */
-#define gwltty_phandler 0
-#define gwltty_ttywnd 4
-
-/* Forward declaration prototypes for internal functions
- */
-static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd);
-static LRESULT WINAPI RegisterWindows9xService(BOOL set_service);
-static LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam);
-static DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty);
-static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam);
-static int HookProc(int hc, HWND *hwnd, UINT *msg,
- WPARAM *wParam, LPARAM *lParam);
-#ifdef DBG
-static VOID DbgPrintf(LPTSTR fmt, ...);
-#endif
-
-
-/* DllMain is invoked by every process in the entire system that is hooked
- * by our window hooks, notably the tty processes' context, and by the user
- * who wants tty messages (the app). Keep it light and simple.
- */
-BOOL __declspec(dllexport) APIENTRY DllMain(HINSTANCE hModule, ULONG ulReason,
- LPVOID pctx)
-{
- if (ulReason == DLL_PROCESS_ATTACH)
- {
- //hmodThis = hModule;
- if (!hookwndmsg) {
- origwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookOrigProc"));
- hookwndprop = MAKEINTATOM(GlobalAddAtom("Win9xConHookThunkWnd"));
- hookwndmsg = RegisterWindowMessage("Win9xConHookMsg");
- }
-#ifdef DBG
-// DbgPrintf("H ProcessAttach:%8.8x\r\n",
-// GetCurrentProcessId());
-#endif
- }
- else if ( ulReason == DLL_PROCESS_DETACH )
- {
-#ifdef DBG
-// DbgPrintf("H ProcessDetach:%8.8x\r\n", GetCurrentProcessId());
-#endif
- if (monitor_hwnd)
- SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
- if (is_subclassed)
- SendMessage(hwtty, hookwndmsg, 0, (LPARAM)hwtty);
- if (hmodHook)
- {
- if (hhkGetMessage) {
- UnhookWindowsHookEx(hhkGetMessage);
- hhkGetMessage = NULL;
- }
- //if (hhkCallWndProc) {
- // UnhookWindowsHookEx(hhkCallWndProc);
- // hhkCallWndProc = NULL;
- //}
- FreeLibrary(hmodHook);
- hmodHook = NULL;
- }
- if (is_service)
- RegisterWindows9xService(FALSE);
- if (hookwndmsg) {
- GlobalDeleteAtom((ATOM)origwndprop);
- GlobalDeleteAtom((ATOM)hookwndprop);
- hookwndmsg = 0;
- }
- }
- return TRUE;
-}
-
-
-/* This group of functions are provided for the service/console app
- * to register itself a HandlerRoutine to accept tty or service messages
- */
-
-
-/* Exported function that creates a Win9x 'service' via a hidden window,
- * that notifies the process via the HandlerRoutine messages.
- */
-BOOL __declspec(dllexport) WINAPI Windows9xServiceCtrlHandler(
- PHANDLER_ROUTINE phandler,
- LPCSTR name)
-{
- /* If we have not yet done so */
- FreeConsole();
-
- if (name)
- {
- DWORD tid;
- HANDLE hThread;
- /* NOTE: this is static so the module can continue to
- * access these args while we go on to other things
- */
- static tty_info tty;
- tty.instance = GetModuleHandle(NULL);
- tty.phandler = phandler;
- tty.parent = NULL;
- tty.name = name;
- tty.type = 2;
- RegisterWindows9xService(TRUE);
- hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
- (LPVOID)&tty, 0, &tid);
- if (hThread)
- {
- CloseHandle(hThread);
- return TRUE;
- }
- }
- else /* remove */
- {
- if (monitor_hwnd)
- SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
- RegisterWindows9xService(FALSE);
- return TRUE;
- }
- return FALSE;
-}
-
-
-/* Exported function that registers a HandlerRoutine to accept missing
- * Win9x CTRL_EVENTs from the tty window, as NT does without a hassle.
- * If add is 1 or 2, register the handler, if 2 also mark it as a service.
- * If add is 0 deregister the handler, and unmark if a service
- */
-BOOL __declspec(dllexport) WINAPI FixConsoleCtrlHandler(
- PHANDLER_ROUTINE phandler,
- INT add)
-{
- HWND parent;
-
- if (add)
- {
- HANDLE hThread;
- DWORD tid;
- /* NOTE: this is static so the module can continue to
- * access these args while we go on to other things
- */
- static tty_info tty;
- EnumWindows(EnumttyWindow, (LPARAM)&parent);
- if (!parent) {
-#ifdef DBG
- DbgPrintf("A EnumttyWindow failed (%d)\r\n", GetLastError());
-#endif
- return FALSE;
- }
- tty.instance = GetModuleHandle(NULL);
- tty.phandler = phandler;
- tty.parent = parent;
- tty.type = add;
- if (add == 2) {
- tty.name = "ttyService";
- RegisterWindows9xService(TRUE);
- }
- else
- tty.name = "ttyMonitor";
- hThread = CreateThread(NULL, 0, ttyConsoleCtrlThread,
- (LPVOID)&tty, 0, &tid);
- if (!hThread)
- return FALSE;
- CloseHandle(hThread);
- hmodHook = LoadLibrary("Win9xConHook.dll");
- if (hmodHook)
- {
- hhkGetMessage = SetWindowsHookEx(WH_GETMESSAGE,
- (HOOKPROC)GetProcAddress(hmodHook, "GetMsgProc"), hmodHook, 0);
- //hhkCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC,
- // (HOOKPROC)GetProcAddress(hmodHook, "CallWndProc"), hmodHook, 0);
- }
- return TRUE;
- }
- else /* remove */
- {
- if (monitor_hwnd) {
- SendMessage(monitor_hwnd, WM_DESTROY, 0, 0);
- }
- if (hmodHook)
- {
- if (hhkGetMessage) {
- UnhookWindowsHookEx(hhkGetMessage);
- hhkGetMessage = NULL;
- }
- //if (hhkCallWndProc) {
- // UnhookWindowsHookEx(hhkCallWndProc);
- // hhkCallWndProc = NULL;
- //}
- FreeLibrary(hmodHook);
- hmodHook = NULL;
- }
- if (is_service)
- RegisterWindows9xService(FALSE);
- return TRUE;
- }
- return FALSE;
-}
-
-
-/* The following internal helpers are only used within the app's context
- */
-
-/* ttyConsoleCreateThread is the process that runs within the user app's
- * context. It creates and pumps the messages of a hidden monitor window,
- * watching for messages from the system, or the associated subclassed tty
- * window. Things can happen in our context that can't be done from the
- * tty's context, and visa versa, so the subclass procedure and this hidden
- * window work together to make it all happen.
- */
-static DWORD WINAPI ttyConsoleCtrlThread(LPVOID tty)
-{
- WNDCLASS wc;
- MSG msg;
- wc.style = CS_GLOBALCLASS;
- wc.lpfnWndProc = ttyConsoleCtrlWndProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 8;
- wc.hInstance = NULL;
- wc.hIcon = NULL;
- wc.hCursor = NULL;
- wc.hbrBackground = NULL;
- wc.lpszMenuName = NULL;
- if (((tty_info*)tty)->parent)
- wc.lpszClassName = "ttyConHookChild";
- else
- wc.lpszClassName = "ApacheWin95ServiceMonitor";
-
- if (!RegisterClass(&wc)) {
-#ifdef DBG
- DbgPrintf("A proc %8.8x Error creating class %s (%d)\r\n",
- GetCurrentProcessId(), wc.lpszClassName, GetLastError());
-#endif
- return 0;
- }
-
- /* Create an invisible window */
- monitor_hwnd = CreateWindow(wc.lpszClassName, ((tty_info*)tty)->name,
- WS_OVERLAPPED & ~WS_VISIBLE,
- CW_USEDEFAULT, CW_USEDEFAULT,
- CW_USEDEFAULT, CW_USEDEFAULT,
- NULL, NULL,
- ((tty_info*)tty)->instance, tty);
-
- if (!monitor_hwnd) {
-#ifdef DBG
- DbgPrintf("A proc %8.8x Error creating window %s %s (%d)\r\n",
- GetCurrentProcessId(), wc.lpszClassName,
- ((tty_info*)tty)->name, GetLastError());
-#endif
- return 0;
- }
-
- while (GetMessage(&msg, NULL, 0, 0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- /* Tag again as deleted, just in case we missed WM_DESTROY */
- monitor_hwnd = NULL;
- return 0;
-}
-
-
-/* This is the WndProc procedure for our invisible window.
- * When our subclasssed tty window receives the WM_CLOSE, WM_ENDSESSION,
- * or WM_QUERYENDSESSION messages, the message is dispatched to our hidden
- * window (this message process), and we call the installed HandlerRoutine
- * that was registered by the app.
- */
-static LRESULT CALLBACK ttyConsoleCtrlWndProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
-{
- if (msg == WM_CREATE)
- {
- tty_info *tty = (tty_info*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
- SetWindowLong(hwnd, gwltty_phandler, (LONG)tty->phandler);
- SetWindowLong(hwnd, gwltty_ttywnd, (LONG)tty->parent);
-#ifdef DBG
- DbgPrintf("A proc %8.8x created %8.8x %s for tty wnd %8.8x\r\n",
- GetCurrentProcessId(), hwnd,
- tty->name, tty->parent);
-#endif
- if (tty->parent) {
- SetProp(tty->parent, hookwndprop, hwnd);
- PostMessage(tty->parent, hookwndmsg,
- tty->type, (LPARAM)tty->parent);
- }
- return 0;
- }
- else if (msg == WM_DESTROY)
- {
- HWND parent = (HWND)GetWindowLong(hwnd, gwltty_ttywnd);
-#ifdef DBG
- DbgPrintf("A proc %8.8x destroyed %8.8x ttyConHookChild\r\n",
- GetCurrentProcessId(), hwnd);
-#endif
- if (parent) {
- RemoveProp(parent, hookwndprop);
- SendMessage(parent, hookwndmsg, 0, (LPARAM)parent);
- }
- monitor_hwnd = NULL;
- }
- else if (msg == WM_CLOSE)
- {
- PHANDLER_ROUTINE phandler =
- (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
- LRESULT rv = phandler(CTRL_CLOSE_EVENT);
-#ifdef DBG
- DbgPrintf("A proc %8.8x invoked CTRL_CLOSE_EVENT "
- "returning %d\r\n",
- GetCurrentProcessId(), rv);
-#endif
- if (rv)
- return !rv;
- }
- else if ((msg == WM_QUERYENDSESSION) || (msg == WM_ENDSESSION))
- {
- if (lParam & ENDSESSION_LOGOFF)
- {
- PHANDLER_ROUTINE phandler =
- (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
- LRESULT rv = phandler(CTRL_LOGOFF_EVENT);
-#ifdef DBG
- DbgPrintf("A proc %8.8x invoked CTRL_LOGOFF_EVENT "
- "returning %d\r\n",
- GetCurrentProcessId(), rv);
-#endif
- if (rv)
- return ((msg == WM_QUERYENDSESSION) ? rv : !rv);
- }
- else
- {
- PHANDLER_ROUTINE phandler =
- (PHANDLER_ROUTINE)GetWindowLong(hwnd, gwltty_phandler);
- LRESULT rv = phandler(CTRL_SHUTDOWN_EVENT);
-#ifdef DBG
- DbgPrintf("A proc %8.8x invoked CTRL_SHUTDOWN_EVENT "
- "returning %d\r\n", GetCurrentProcessId(), rv);
-#endif
- if (rv)
- return ((msg == WM_QUERYENDSESSION) ? rv : !rv);
- }
- }
- return (DefWindowProc(hwnd, msg, wParam, lParam));
-}
-
-
-/* The following internal helpers are invoked by the hooked tty and our app
- */
-
-
-/* Register or deregister the current process as a Windows9x style service.
- * Experience shows this call is ignored across processes, so the second
- * arg to RegisterServiceProcess (process group id) is effectively useless.
- */
-static LRESULT WINAPI RegisterWindows9xService(BOOL set_service)
-{
- static HINSTANCE hkernel;
- static DWORD (WINAPI *register_service_process)(DWORD, DWORD) = NULL;
- BOOL rv;
-
- if (set_service == is_service)
- return 1;
-
-#ifdef DBG
- DbgPrintf("R %s proc %8.8x as a service\r\n",
- set_service ? "installing" : "removing",
- GetCurrentProcessId());
-#endif
-
- if (!register_service_process)
- {
- /* Obtain a handle to the kernel library */
- hkernel = LoadLibrary("KERNEL32.DLL");
- if (!hkernel)
- return 0;
-
- /* Find the RegisterServiceProcess function */
- register_service_process = (DWORD (WINAPI *)(DWORD, DWORD))
- GetProcAddress(hkernel, "RegisterServiceProcess");
- if (register_service_process == NULL) {
- FreeLibrary(hkernel);
- return 0;
- }
- }
-
- /* Register this process as a service */
- rv = register_service_process(0, set_service != FALSE);
- if (rv)
- is_service = set_service;
-
- if (!is_service)
- {
- /* Unload the kernel library */
- FreeLibrary(hkernel);
- register_service_process = NULL;
- }
- return rv;
-}
-
-
-/*
- * This function only works when this process is the active process
- * (e.g. once it is running a child process, it can no longer determine
- * which console window is its own.)
- */
-static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd)
-{
- char tmp[8];
- if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, "tty"))
- {
- DWORD wndproc, thisproc = GetCurrentProcessId();
- GetWindowThreadProcessId(wnd, &wndproc);
- if (wndproc == thisproc) {
- *((HWND*)retwnd) = wnd;
- return FALSE;
- }
- }
- return TRUE;
-}
-
-
-/* The remaining code all executes --in the tty's own process context--
- *
- * That means special attention must be paid to what it's doing...
- */
-
-/* Subclass message process for the tty window
- *
- * This code -handles- WM_CLOSE, WM_ENDSESSION and WM_QUERYENDSESSION
- * by dispatching them to the window identified by the hookwndprop
- * property atom set against our window. Messages are then dispatched
- * to origwndprop property atom we set against the window when we
- * injected this subclass. This trick did not work with simply a hook.
- */
-static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
-{
- WNDPROC origproc = (WNDPROC) GetProp(hwnd, origwndprop);
- if (!origproc)
- return 0;
-
- if (msg == WM_NCDESTROY
- || (msg == hookwndmsg && !LOWORD(wParam) && (HWND)lParam == hwnd))
- {
- if (is_subclassed) {
-#ifdef DBG
- DbgPrintf("W proc %08x hwnd:%08x Subclass removed\r\n",
- GetCurrentProcessId(), hwnd);
-#endif
- if (is_service)
- RegisterWindows9xService(FALSE);
- SetWindowLong(hwnd, GWL_WNDPROC, (LONG)origproc);
- RemoveProp(hwnd, origwndprop);
- RemoveProp(hwnd, hookwndprop);
- is_subclassed = FALSE;
- //if (hmodLock)
- // FreeLibrary(hmodLock);
- //hmodLock = NULL;
- }
- }
- else if (msg == WM_CLOSE || msg == WM_ENDSESSION
- || msg == WM_QUERYENDSESSION)
- {
- HWND child = (HWND)GetProp(hwnd, hookwndprop);
- if (child) {
-#ifdef DBG
- DbgPrintf("W proc %08x hwnd:%08x forwarded msg:%d\r\n",
- GetCurrentProcessId(), hwnd, msg);
-#endif
- return SendMessage(child, msg, wParam, lParam);
- }
- }
- return CallWindowProc(origproc, hwnd, msg, wParam, lParam);
-}
-
-
-/* HookProc, once installed, is responsible for subclassing the system
- * tty windows. It generally does nothing special itself, since
- * research indicates that it cannot deal well with the messages we are
- * interested in, that is, WM_CLOSE, WM_QUERYSHUTDOWN and WM_SHUTDOWN
- * of the tty process.
- *
- * Respond and subclass only when a WM_NULL is received by the window.
- */
-int HookProc(int hc, HWND *hwnd, UINT *msg, WPARAM *wParam, LPARAM *lParam)
-{
- if (is_tty == -1 && *hwnd)
- {
- char ttybuf[8];
- HWND htty;
- hwtty = *hwnd;
- while (htty = GetParent(hwtty))
- hwtty = htty;
- is_tty = (GetClassName(hwtty, ttybuf, sizeof(ttybuf))
- && !strcmp(ttybuf, "tty"));
-#ifdef DBG
- if (is_tty)
- DbgPrintf("H proc %08x tracking hwnd %08x\r\n",
- GetCurrentProcessId(), hwtty);
-#endif
- }
-
- if (*msg == hookwndmsg && *wParam && *lParam == (LPARAM)hwtty && is_tty)
- {
- WNDPROC origproc = (WNDPROC)GetWindowLong(hwtty, GWL_WNDPROC);
- //char myname[MAX_PATH];
- //if (GetModuleFileName(hmodThis, myname, sizeof(myname)))
- // hmodLock = LoadLibrary(myname);
- SetProp(hwtty, origwndprop, origproc);
- SetWindowLong(hwtty, GWL_WNDPROC, (LONG)WndProc);
- is_subclassed = TRUE;
-#ifdef DBG
- DbgPrintf("H proc %08x hwnd:%08x Subclassed\r\n",
- GetCurrentProcessId(), hwtty);
-#endif
- if (LOWORD(*wParam) == 2)
- RegisterWindows9xService(TRUE);
- }
-
- return -1;
-}
-
-
-/*
- * PostMessage Hook:
- */
-LRESULT __declspec(dllexport) CALLBACK GetMsgProc(INT hc, WPARAM wParam,
- LPARAM lParam)
-{
- PMSG pmsg;
-
- pmsg = (PMSG)lParam;
-
- if (pmsg) {
- int rv = HookProc(hc, &pmsg->hwnd, &pmsg->message,
- &pmsg->wParam, &pmsg->lParam);
- if (rv != -1)
- return rv;
- }
- /*
- * CallNextHookEx apparently ignores the hhook argument, so pass NULL
- */
- return CallNextHookEx(NULL, hc, wParam, lParam);
-}
-
-
-/*
- * SendMessage Hook:
- */
-LRESULT __declspec(dllexport) CALLBACK CallWndProc(INT hc, WPARAM wParam,
- LPARAM lParam)
-{
- PCWPSTRUCT pcwps = (PCWPSTRUCT)lParam;
-
- if (pcwps) {
- int rv = HookProc(hc, &pcwps->hwnd, &pcwps->message,
- &pcwps->wParam, &pcwps->lParam);
- if (rv != -1)
- return rv;
- }
- /*
- * CallNextHookEx apparently ignores the hhook argument, so pass NULL
- */
- return CallNextHookEx(NULL, hc, wParam, lParam);
-}
-
-
-#ifdef DBG
-VOID DbgPrintf(
- LPTSTR fmt,
- ...
- )
-{
- static HANDLE mutex;
- va_list marker;
- TCHAR szBuf[256];
- DWORD t;
- HANDLE gDbgOut;
-
- va_start(marker, fmt);
- wvsprintf(szBuf, fmt, marker);
- va_end(marker);
-
- if (!mutex)
- mutex = CreateMutex(NULL, FALSE, "Win9xConHookDbgOut");
- WaitForSingleObject(mutex, INFINITE);
- gDbgOut = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0,
- NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL);
- WriteFile(gDbgOut, szBuf, strlen(szBuf), &t, NULL);
- CloseHandle(gDbgOut);
- ReleaseMutex(mutex);
-}
-#endif
-
-#endif /* WIN32 */
diff --git a/server/mpm/winnt/Win9xConHook.def b/server/mpm/winnt/Win9xConHook.def
deleted file mode 100644
index 85ec1664..00000000
--- a/server/mpm/winnt/Win9xConHook.def
+++ /dev/null
@@ -1,10 +0,0 @@
-LIBRARY Win9xConHook
-
-EXETYPE WINDOWS
-
-EXPORTS
- DllMain
- GetMsgProc
- CallWndProc
- FixConsoleCtrlHandler
- Windows9xServiceCtrlHandler
diff --git a/server/mpm/winnt/Win9xConHook.dsp b/server/mpm/winnt/Win9xConHook.dsp
deleted file mode 100644
index 55bee020..00000000
--- a/server/mpm/winnt/Win9xConHook.dsp
+++ /dev/null
@@ -1,115 +0,0 @@
-# Microsoft Developer Studio Project File - Name="Win9xConHook" - Package Owner=<4>
-# Microsoft Developer Studio Generated Build File, Format Version 6.00
-# ** DO NOT EDIT **
-
-# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
-
-CFG=Win9xConHook - Win32 Release
-!MESSAGE This is not a valid makefile. To build this project using NMAKE,
-!MESSAGE use the Export Makefile command and run
-!MESSAGE
-!MESSAGE NMAKE /f "Win9xConHook.mak".
-!MESSAGE
-!MESSAGE You can specify a configuration when running NMAKE
-!MESSAGE by defining the macro CFG on the command line. For example:
-!MESSAGE
-!MESSAGE NMAKE /f "Win9xConHook.mak" CFG="Win9xConHook - Win32 Release"
-!MESSAGE
-!MESSAGE Possible choices for configuration are:
-!MESSAGE
-!MESSAGE "Win9xConHook - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE "Win9xConHook - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
-!MESSAGE
-
-# Begin Project
-# PROP AllowPerConfigDependencies 0
-# PROP Scc_ProjName ""
-# PROP Scc_LocalPath ""
-CPP=cl.exe
-MTL=midl.exe
-RSC=rc.exe
-
-!IF "$(CFG)" == "Win9xConHook - Win32 Release"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir ".\Release"
-# PROP BASE Intermediate_Dir ".\Release"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 0
-# PROP Output_Dir "Release"
-# PROP Intermediate_Dir "Release"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
-# ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /Fd"Release\Win9xConHook" /FD /c
-# ADD BASE MTL /nologo /D "NDEBUG" /win32
-# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
-# ADD BASE RSC /l 0x809 /d "NDEBUG"
-# ADD RSC /l 0x809 /d "NDEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib /nologo /subsystem:windows /dll /incremental:no /base:"0x1c0f0000"
-# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /subsystem:windows /dll /incremental:no /base:"0x1c0f0000" /opt:ref
-# Begin Special Build Tool
-TargetPath=.\Release\Win9xConHook.dll
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
-# End Special Build Tool
-
-!ELSEIF "$(CFG)" == "Win9xConHook - Win32 Debug"
-
-# PROP BASE Use_MFC 0
-# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir "Debug"
-# PROP BASE Intermediate_Dir "Debug"
-# PROP BASE Target_Dir ""
-# PROP Use_MFC 0
-# PROP Use_Debug_Libraries 1
-# PROP Output_Dir "Debug"
-# PROP Intermediate_Dir "Debug"
-# PROP Ignore_Export_Lib 0
-# PROP Target_Dir ""
-# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
-# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /Fd"Debug\Win9xConHook" /FD /c
-# ADD BASE MTL /nologo /D "_DEBUG" /win32
-# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
-# ADD BASE RSC /l 0x809 /d "_DEBUG"
-# ADD RSC /l 0x809 /d "_DEBUG"
-BSC32=bscmake.exe
-# ADD BASE BSC32 /nologo
-# ADD BSC32 /nologo
-LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib /nologo /subsystem:windows /dll /incremental:no /debug /base:"0x1c0f0000"
-# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /subsystem:windows /dll /incremental:no /debug /base:"0x1c0f0000"
-# Begin Special Build Tool
-TargetPath=.\Debug\Win9xConHook.dll
-SOURCE="$(InputPath)"
-PostBuild_Desc=Embed .manifest
-PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
-# End Special Build Tool
-
-!ENDIF
-
-# Begin Target
-
-# Name "Win9xConHook - Win32 Release"
-# Name "Win9xConHook - Win32 Debug"
-# Begin Source File
-
-SOURCE=.\Win9xConHook.c
-# End Source File
-# Begin Source File
-
-SOURCE=.\Win9xConHook.def
-# End Source File
-# Begin Source File
-
-SOURCE=.\Win9xConHook.h
-# End Source File
-# End Target
-# End Project
diff --git a/server/mpm/winnt/Win9xConHook.h b/server/mpm/winnt/Win9xConHook.h
deleted file mode 100644
index 8b42da57..00000000
--- a/server/mpm/winnt/Win9xConHook.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file Win9xConHook.h
- * @brief ???
- *
- * @addtogroup APACHE_MPM_WINNT
- * @{
- */
-
-#ifndef AP_WIN9XCONHOOK_H
-#define AP_WIN9XCONHOOK_H
-
-#ifdef WIN32
-
-/* Windows9xServiceCtrlHandler registers a handler routine, frees the
- * console window, and registers this process as a service in Win9x.
- * It creats a hidden window of class "ApacheWin95ServiceMonitor"
- * and titled by the name passed, which passes the WM_SHUTDOWN message
- * through the given HandlerRoutine's CTRL_SHUTDOWN event.
- * Call with name of NULL to remove the Service handler.
- */
-BOOL WINAPI Windows9xServiceCtrlHandler(PHANDLER_ROUTINE phandler, LPCSTR name);
-
-
-/* FixConsoleControlHandler registers a handler routine with the
- * Win9xConHook.dll, creating a hidden window and forwarding the
- * WM_ENDSESSION and WM_CLOSE messages to the given HandlerRoutine
- * as CTRL_SHUTDOWN_EVENT, CTRL_LOGOFF_EVENT and CTRL_CLOSE_EVENT.
- * The application should still use SetConsoleCtrlHandler to grab
- * the CTRL_BREAK_EVENT and CTRL_C_EVENT, if desired.
- */
-BOOL WINAPI FixConsoleCtrlHandler(PHANDLER_ROUTINE phandler, BOOL add);
-
-
-/*
- * Exported PostMessage Hook, never use this directly:
- *
- * LRESULT CALLBACK GetMsgProc(INT hc, WPARAM wParam, LPARAM lParam);
- */
-
-
-/*
- * Exported SendMessage Hook, never use this directly:
- *
- * LRESULT CALLBACK CallWndProc(INT hc, WPARAM wParam, LPARAM lParam);
- */
-
-#endif /* WIN32 */
-
-#endif AP_WIN9XCONHOOK_H
-/** @} */
diff --git a/server/mpm/winnt/child.c b/server/mpm/winnt/child.c
index 82cec95f..ab4fddc3 100644
--- a/server/mpm/winnt/child.c
+++ b/server/mpm/winnt/child.c
@@ -16,13 +16,15 @@
#ifdef WIN32
-#define CORE_PRIVATE
+#include "apr.h"
+#include <process.h>
#include "httpd.h"
#include "http_main.h"
#include "http_log.h"
#include "http_config.h" /* for read_config */
#include "http_core.h" /* for get_remote_host */
#include "http_connection.h"
+#include "http_vhost.h" /* for ap_update_vhost_given_ip */
#include "apr_portable.h"
#include "apr_thread_proc.h"
#include "apr_getopt.h"
@@ -38,27 +40,62 @@
#include "mpm_common.h"
#include <malloc.h>
#include "apr_atomic.h"
-
-#include <process.h>
+#include "apr_buckets.h"
+#include "scoreboard.h"
#ifdef __MINGW32__
#include <mswsock.h>
-#endif
-
-/* shared with mpm_winnt.c */
-extern DWORD my_pid;
+#endif
-/* used by parent to signal the child to start and exit */
-/* shared with mpm_winnt.c, but should be private to child.c */
-apr_proc_mutex_t *start_mutex;
-HANDLE exit_event;
+/*
+ * The Windows MPM uses a queue of completion contexts that it passes
+ * between the accept threads and the worker threads. Declare the
+ * functions to access the queue and the structures passed on the
+ * queue in the header file to enable modules to access them
+ * if necessary. The queue resides in the MPM.
+ */
+#ifdef CONTAINING_RECORD
+#undef CONTAINING_RECORD
+#endif
+#define CONTAINING_RECORD(address, type, field) ((type *)( \
+ (char *)(address) - \
+ (char *)(&((type *)0)->field)))
+#if APR_HAVE_IPV6
+#define PADDED_ADDR_SIZE (sizeof(SOCKADDR_IN6)+16)
+#else
+#define PADDED_ADDR_SIZE (sizeof(SOCKADDR_IN)+16)
+#endif
-/* child_main() should never need to modify is_graceful!?! */
-extern int volatile is_graceful;
+APLOG_USE_MODULE(mpm_winnt);
-/* Queue for managing the passing of COMP_CONTEXTs between
+/* Queue for managing the passing of winnt_conn_ctx_t between
* the accept and worker threads.
*/
+typedef struct winnt_conn_ctx_t_s {
+ struct winnt_conn_ctx_t_s *next;
+ OVERLAPPED overlapped;
+ apr_socket_t *sock;
+ SOCKET accept_socket;
+ char buff[2*PADDED_ADDR_SIZE];
+ struct sockaddr *sa_server;
+ int sa_server_len;
+ struct sockaddr *sa_client;
+ int sa_client_len;
+ apr_pool_t *ptrans;
+ apr_bucket_alloc_t *ba;
+ apr_bucket *data;
+#if APR_HAVE_IPV6
+ short socket_family;
+#endif
+} winnt_conn_ctx_t;
+
+typedef enum {
+ IOCP_CONNECTION_ACCEPTED = 1,
+ IOCP_WAIT_FOR_RECEIVE = 2,
+ IOCP_WAIT_FOR_TRANSMITFILE = 3,
+ IOCP_SHUTDOWN = 4
+} io_state_e;
+
static apr_pool_t *pchild;
static int shutdown_in_progress = 0;
static int workers_may_exit = 0;
@@ -67,15 +104,14 @@ static HANDLE max_requests_per_child_event;
static apr_thread_mutex_t *child_lock;
static apr_thread_mutex_t *qlock;
-static PCOMP_CONTEXT qhead = NULL;
-static PCOMP_CONTEXT qtail = NULL;
+static winnt_conn_ctx_t *qhead = NULL;
+static winnt_conn_ctx_t *qtail = NULL;
static apr_uint32_t num_completion_contexts = 0;
static apr_uint32_t max_num_completion_contexts = 0;
static HANDLE ThreadDispatchIOCP = NULL;
static HANDLE qwait_event = NULL;
-
-void mpm_recycle_completion_context(PCOMP_CONTEXT context)
+static void mpm_recycle_completion_context(winnt_conn_ctx_t *context)
{
/* Recycle the completion context.
* - clear the ptrans pool
@@ -88,7 +124,7 @@ void mpm_recycle_completion_context(PCOMP_CONTEXT context)
apr_pool_clear(context->ptrans);
context->ba = apr_bucket_alloc_create(context->ptrans);
context->next = NULL;
- ResetEvent(context->Overlapped.hEvent);
+ ResetEvent(context->overlapped.hEvent);
apr_thread_mutex_lock(qlock);
if (qtail) {
qtail->next = context;
@@ -101,11 +137,12 @@ void mpm_recycle_completion_context(PCOMP_CONTEXT context)
}
}
-PCOMP_CONTEXT mpm_get_completion_context(void)
+static winnt_conn_ctx_t *mpm_get_completion_context(int *timeout)
{
apr_status_t rv;
- PCOMP_CONTEXT context = NULL;
+ winnt_conn_ctx_t *context = NULL;
+ *timeout = 0;
while (1) {
/* Grab a context off the queue */
apr_thread_mutex_lock(qlock);
@@ -129,51 +166,74 @@ PCOMP_CONTEXT mpm_get_completion_context(void)
/* All workers are busy, need to wait for one */
static int reported = 0;
if (!reported) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
- "Server ran out of threads to serve requests. Consider "
- "raising the ThreadsPerChild setting");
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00326)
+ "Server ran out of threads to serve "
+ "requests. Consider raising the "
+ "ThreadsPerChild setting");
reported = 1;
}
/* Wait for a worker to free a context. Once per second, give
* the caller a chance to check for shutdown. If the wait
- * succeeds, get the context off the queue. It must be available,
- * since there's only one consumer.
+ * succeeds, get the context off the queue. It must be
+ * available, since there's only one consumer.
*/
rv = WaitForSingleObject(qwait_event, 1000);
if (rv == WAIT_OBJECT_0)
continue;
- else /* Hopefully, WAIT_TIMEOUT */
+ else {
+ if (rv == WAIT_TIMEOUT) {
+ /* somewhat-normal condition where threads are busy */
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00327)
+ "mpm_get_completion_context: Failed to get a "
+ "free context within 1 second");
+ *timeout = 1;
+ }
+ else {
+ /* should be the unexpected, generic WAIT_FAILED */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(),
+ ap_server_conf, APLOGNO(00328)
+ "mpm_get_completion_context: "
+ "WaitForSingleObject failed to get free context");
+ }
return NULL;
+ }
} else {
/* Allocate another context.
- * Note:
- * Multiple failures in the next two steps will cause the pchild pool
- * to 'leak' storage. I don't think this is worth fixing...
+ * Note: Multiple failures in the next two steps will cause
+ * the pchild pool to 'leak' storage. I don't think this
+ * is worth fixing...
*/
apr_allocator_t *allocator;
apr_thread_mutex_lock(child_lock);
- context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
+ context = (winnt_conn_ctx_t *)apr_pcalloc(pchild,
+ sizeof(winnt_conn_ctx_t));
- context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (context->Overlapped.hEvent == NULL) {
+
+ context->overlapped.hEvent = CreateEvent(NULL, TRUE,
+ FALSE, NULL);
+ if (context->overlapped.hEvent == NULL) {
/* Hopefully this is a temporary condition ... */
- ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf,
- "mpm_get_completion_context: CreateEvent failed.");
+ ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(),
+ ap_server_conf, APLOGNO(00329)
+ "mpm_get_completion_context: "
+ "CreateEvent failed.");
apr_thread_mutex_unlock(child_lock);
return NULL;
}
- /* Create the tranaction pool */
+ /* Create the transaction pool */
apr_allocator_create(&allocator);
apr_allocator_max_free_set(allocator, ap_max_mem_free);
- rv = apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator);
+ rv = apr_pool_create_ex(&context->ptrans, pchild, NULL,
+ allocator);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
- "mpm_get_completion_context: Failed to create the transaction pool.");
- CloseHandle(context->Overlapped.hEvent);
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00330)
+ "mpm_get_completion_context: Failed "
+ "to create the transaction pool.");
+ CloseHandle(context->overlapped.hEvent);
apr_thread_mutex_unlock(child_lock);
return NULL;
@@ -197,285 +257,6 @@ PCOMP_CONTEXT mpm_get_completion_context(void)
return context;
}
-apr_status_t mpm_post_completion_context(PCOMP_CONTEXT context,
- io_state_e state)
-{
- LPOVERLAPPED pOverlapped;
- if (context)
- pOverlapped = &context->Overlapped;
- else
- pOverlapped = NULL;
-
- PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, state, pOverlapped);
- return APR_SUCCESS;
-}
-
-
-/*
- * find_ready_listener()
- * Only used by Win9* and should go away when the win9*_accept() function is
- * reimplemented using apr_poll().
- */
-static ap_listen_rec *head_listener;
-
-static APR_INLINE ap_listen_rec *find_ready_listener(fd_set * main_fds)
-{
- ap_listen_rec *lr;
- SOCKET nsd;
-
- lr = head_listener;
- do {
- apr_os_sock_get(&nsd, lr->sd);
- if (FD_ISSET(nsd, main_fds)) {
- head_listener = lr->next;
- if (!head_listener) {
- head_listener = ap_listeners;
- }
- return lr;
- }
- lr = lr->next;
- if (!lr) {
- lr = ap_listeners;
- }
- } while (lr != head_listener);
- return NULL;
-}
-
-
-/* Windows 9x specific code...
- * Accept processing for on Windows 95/98 uses a producer/consumer queue
- * model. A single thread accepts connections and queues the accepted socket
- * to the accept queue for consumption by a pool of worker threads.
- *
- * win9x_accept()
- * The accept threads runs this function, which accepts connections off
- * the network and calls add_job() to queue jobs to the accept_queue.
- * add_job()/remove_job()
- * Add or remove an accepted socket from the list of sockets
- * connected to clients. allowed_globals.jobmutex protects
- * against multiple concurrent access to the linked list of jobs.
- * win9x_get_connection()
- * Calls remove_job() to pull a job from the accept queue. All the worker
- * threads block on remove_job.
- */
-
-typedef struct joblist_s {
- struct joblist_s *next;
- SOCKET sock;
-} joblist;
-
-typedef struct globals_s {
- HANDLE jobsemaphore;
- joblist *jobhead;
- joblist *jobtail;
- apr_thread_mutex_t *jobmutex;
- int jobcount;
-} globals;
-
-globals allowed_globals = {NULL, NULL, NULL, NULL, 0};
-
-#define MAX_SELECT_ERRORS 100
-
-
-static void add_job(SOCKET sock)
-{
- joblist *new_job;
-
- new_job = (joblist *) malloc(sizeof(joblist));
- if (new_job == NULL) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Ouch! Out of memory in add_job()!");
- return;
- }
- new_job->next = NULL;
- new_job->sock = sock;
-
- apr_thread_mutex_lock(allowed_globals.jobmutex);
-
- if (allowed_globals.jobtail != NULL)
- allowed_globals.jobtail->next = new_job;
- allowed_globals.jobtail = new_job;
- if (!allowed_globals.jobhead)
- allowed_globals.jobhead = new_job;
- allowed_globals.jobcount++;
- ReleaseSemaphore(allowed_globals.jobsemaphore, 1, NULL);
-
- apr_thread_mutex_unlock(allowed_globals.jobmutex);
-}
-
-
-static SOCKET remove_job(void)
-{
- joblist *job;
- SOCKET sock;
-
- WaitForSingleObject(allowed_globals.jobsemaphore, INFINITE);
- apr_thread_mutex_lock(allowed_globals.jobmutex);
-
- if (shutdown_in_progress && !allowed_globals.jobhead) {
- apr_thread_mutex_unlock(allowed_globals.jobmutex);
- return (INVALID_SOCKET);
- }
- job = allowed_globals.jobhead;
- ap_assert(job);
- allowed_globals.jobhead = job->next;
- if (allowed_globals.jobhead == NULL)
- allowed_globals.jobtail = NULL;
- apr_thread_mutex_unlock(allowed_globals.jobmutex);
- sock = job->sock;
- free(job);
-
- return (sock);
-}
-
-
-static unsigned int __stdcall win9x_accept(void * dummy)
-{
- struct timeval tv;
- fd_set main_fds;
- int wait_time = 1;
- SOCKET csd;
- SOCKET nsd = INVALID_SOCKET;
- int count_select_errors = 0;
- int rc;
- int clen;
- ap_listen_rec *lr;
- struct fd_set listenfds;
-#if APR_HAVE_IPV6
- struct sockaddr_in6 sa_client;
-#else
- struct sockaddr_in sa_client;
-#endif
-
- /* Setup the listeners
- * ToDo: Use apr_poll()
- */
- FD_ZERO(&listenfds);
- for (lr = ap_listeners; lr; lr = lr->next) {
- if (lr->sd != NULL) {
- apr_os_sock_get(&nsd, lr->sd);
- FD_SET(nsd, &listenfds);
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
- "Child %lu: Listening on port %d.", my_pid, lr->bind_addr->port);
- }
- }
-
- head_listener = ap_listeners;
-
- while (!shutdown_in_progress) {
- tv.tv_sec = wait_time;
- tv.tv_usec = 0;
- memcpy(&main_fds, &listenfds, sizeof(fd_set));
-
- /* First parameter of select() is ignored on Windows */
- rc = select(0, &main_fds, NULL, NULL, &tv);
-
- if (rc == 0 || (rc == SOCKET_ERROR && APR_STATUS_IS_EINTR(apr_get_netos_error()))) {
- count_select_errors = 0; /* reset count of errors */
- continue;
- }
- else if (rc == SOCKET_ERROR) {
- /* A "real" error occurred, log it and increment the count of
- * select errors. This count is used to ensure we don't go into
- * a busy loop of continuous errors.
- */
- ap_log_error(APLOG_MARK, APLOG_INFO, apr_get_netos_error(), ap_server_conf,
- "select failed with error %d", apr_get_netos_error());
- count_select_errors++;
- if (count_select_errors > MAX_SELECT_ERRORS) {
- shutdown_in_progress = 1;
- ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
- "Too many errors in select loop. Child process exiting.");
- break;
- }
- } else {
- ap_listen_rec *lr;
-
- lr = find_ready_listener(&main_fds);
- if (lr != NULL) {
- /* fetch the native socket descriptor */
- apr_os_sock_get(&nsd, lr->sd);
- }
- }
-
- do {
- clen = sizeof(sa_client);
- csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);
- } while (csd < 0 && APR_STATUS_IS_EINTR(apr_get_netos_error()));
-
- if (csd < 0) {
- if (APR_STATUS_IS_ECONNABORTED(apr_get_netos_error())) {
- ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
- "accept: (client socket)");
- }
- }
- else {
- add_job(csd);
- }
- }
- SetEvent(exit_event);
- return 0;
-}
-
-
-static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
-{
- apr_os_sock_info_t sockinfo;
- int len, salen;
-#if APR_HAVE_IPV6
- salen = sizeof(struct sockaddr_in6);
-#else
- salen = sizeof(struct sockaddr_in);
-#endif
-
-
- if (context == NULL) {
- /* allocate the completion context and the transaction pool */
- apr_allocator_t *allocator;
- apr_thread_mutex_lock(child_lock);
- context = apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
- apr_allocator_create(&allocator);
- apr_allocator_max_free_set(allocator, ap_max_mem_free);
- apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator);
- apr_allocator_owner_set(allocator, context->ptrans);
- apr_pool_tag(context->ptrans, "transaction");
- apr_thread_mutex_unlock(child_lock);
- }
-
- while (1) {
- apr_pool_clear(context->ptrans);
- context->ba = apr_bucket_alloc_create(context->ptrans);
- context->accept_socket = remove_job();
- if (context->accept_socket == INVALID_SOCKET) {
- return NULL;
- }
- len = salen;
- context->sa_server = apr_palloc(context->ptrans, len);
- if (getsockname(context->accept_socket,
- context->sa_server, &len)== SOCKET_ERROR) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
- "getsockname failed");
- continue;
- }
- len = salen;
- context->sa_client = apr_palloc(context->ptrans, len);
- if ((getpeername(context->accept_socket,
- context->sa_client, &len)) == SOCKET_ERROR) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
- "getpeername failed");
- memset(&context->sa_client, '\0', sizeof(context->sa_client));
- }
- sockinfo.os_sock = &context->accept_socket;
- sockinfo.local = context->sa_server;
- sockinfo.remote = context->sa_client;
- sockinfo.family = context->sa_server->sa_family;
- sockinfo.type = SOCK_STREAM;
- apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
-
- return context;
- }
-}
-
/* Windows NT/2000 specific code...
* Accept processing for on Windows NT uses a producer/consumer queue
@@ -491,203 +272,424 @@ static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
* Worker threads block on the ThreadDispatch IOCompletionPort awaiting
* connections to service.
*/
-#define MAX_ACCEPTEX_ERR_COUNT 100
+#define MAX_ACCEPTEX_ERR_COUNT 10
+
static unsigned int __stdcall winnt_accept(void *lr_)
{
ap_listen_rec *lr = (ap_listen_rec *)lr_;
apr_os_sock_info_t sockinfo;
- PCOMP_CONTEXT context = NULL;
+ winnt_conn_ctx_t *context = NULL;
DWORD BytesRead;
SOCKET nlsd;
- int rv, err_count = 0;
+ core_server_config *core_sconf;
+ const char *accf_name;
+ int rv;
+ int accf;
+ int err_count = 0;
+ HANDLE events[3];
#if APR_HAVE_IPV6
SOCKADDR_STORAGE ss_listen;
int namelen = sizeof(ss_listen);
#endif
+ u_long zero = 0;
+
+ core_sconf = ap_get_core_module_config(ap_server_conf->module_config);
+ accf_name = apr_table_get(core_sconf->accf_map, lr->protocol);
+
+ if (strcmp(accf_name, "data") == 0)
+ accf = 2;
+ else if (strcmp(accf_name, "connect") == 0)
+ accf = 1;
+ else if (strcmp(accf_name, "none") == 0)
+ accf = 0;
+ else {
+ accf = 0;
+ accf_name = "none";
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, APLOGNO(00331)
+ "winnt_accept: unrecognized AcceptFilter '%s', "
+ "only 'data', 'connect' or 'none' are valid. "
+ "Using 'none' instead", accf_name);
+ }
apr_os_sock_get(&nlsd, lr->sd);
#if APR_HAVE_IPV6
if (getsockname(nlsd, (struct sockaddr *)&ss_listen, &namelen) == SOCKET_ERROR) {
- ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_netos_error(), ap_server_conf,
- "winnt_accept: getsockname error on listening socket, is IPv6 available?");
+ ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(),
+ ap_server_conf, APLOGNO(00332)
+ "winnt_accept: getsockname error on listening socket, "
+ "is IPv6 available?");
return 1;
}
#endif
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
- "Child %lu: Starting thread to listen on port %d.", my_pid, lr->bind_addr->port);
+ if (accf > 0) /* 'data' or 'connect' */
+ {
+ /* first, high priority event is an already accepted connection */
+ events[1] = exit_event;
+ events[2] = max_requests_per_child_event;
+ }
+ else /* accf == 0, 'none' */
+ {
+reinit: /* target of data or connect upon too many AcceptEx failures */
+
+ /* last, low priority event is a not yet accepted connection */
+ events[0] = exit_event;
+ events[1] = max_requests_per_child_event;
+ events[2] = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ /* The event needs to be removed from the accepted socket,
+ * if not removed from the listen socket prior to accept(),
+ */
+ rv = WSAEventSelect(nlsd, events[2], FD_ACCEPT);
+ if (rv) {
+ ap_log_error(APLOG_MARK, APLOG_ERR,
+ apr_get_netos_error(), ap_server_conf, APLOGNO(00333)
+ "WSAEventSelect() failed.");
+ CloseHandle(events[2]);
+ return 1;
+ }
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00334)
+ "Child: Accept thread listening on %s:%d using AcceptFilter %s",
+ lr->bind_addr->hostname ? lr->bind_addr->hostname : "*",
+ lr->bind_addr->port, accf_name);
+
while (!shutdown_in_progress) {
if (!context) {
- context = mpm_get_completion_context();
+ int timeout;
+
+ context = mpm_get_completion_context(&timeout);
if (!context) {
- /* Temporary resource constraint? */
- Sleep(0);
+ if (!timeout) {
+ /* Hopefully a temporary condition in the provider? */
+ ++err_count;
+ if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(00335)
+ "winnt_accept: Too many failures grabbing a "
+ "connection ctx. Aborting.");
+ break;
+ }
+ }
+ Sleep(100);
continue;
}
}
- /* Create and initialize the accept socket */
-#if APR_HAVE_IPV6
- if (context->accept_socket == INVALID_SOCKET) {
- context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP);
- context->socket_family = ss_listen.ss_family;
- }
- else if (context->socket_family != ss_listen.ss_family) {
- closesocket(context->accept_socket);
- context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP);
- context->socket_family = ss_listen.ss_family;
- }
+ if (accf > 0) /* Either 'connect' or 'data' */
+ {
+ DWORD len;
+ char *buf;
- if (context->accept_socket == INVALID_SOCKET) {
- ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
- "winnt_accept: Failed to allocate an accept socket. "
- "Temporary resource constraint? Try again.");
- Sleep(100);
- continue;
- }
+ /* Create and initialize the accept socket */
+#if APR_HAVE_IPV6
+ if (context->accept_socket == INVALID_SOCKET) {
+ context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM,
+ IPPROTO_TCP);
+ context->socket_family = ss_listen.ss_family;
+ }
+ else if (context->socket_family != ss_listen.ss_family) {
+ closesocket(context->accept_socket);
+ context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM,
+ IPPROTO_TCP);
+ context->socket_family = ss_listen.ss_family;
+ }
#else
- if (context->accept_socket == INVALID_SOCKET) {
- context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (context->accept_socket == INVALID_SOCKET)
+ context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+#endif
+
if (context->accept_socket == INVALID_SOCKET) {
- /* Another temporary condition? */
- ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(),
+ ap_server_conf, APLOGNO(00336)
"winnt_accept: Failed to allocate an accept socket. "
"Temporary resource constraint? Try again.");
Sleep(100);
continue;
}
- }
-#endif
- /* AcceptEx on the completion context. The completion context will be
- * signaled when a connection is accepted.
- */
- if (!AcceptEx(nlsd, context->accept_socket,
- context->buff,
- 0,
- PADDED_ADDR_SIZE,
- PADDED_ADDR_SIZE,
- &BytesRead,
- &context->Overlapped)) {
- rv = apr_get_netos_error();
- if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) ||
- (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) {
- /* We can get here when:
- * 1) the client disconnects early
- * 2) TransmitFile does not properly recycle the accept socket (typically
- * because the client disconnected)
- * 3) there is VPN or Firewall software installed with buggy AcceptEx implementation
- * 4) the webserver is using a dynamic address that has changed
- */
- ++err_count;
- closesocket(context->accept_socket);
- context->accept_socket = INVALID_SOCKET;
- if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
- "Child %lu: Encountered too many errors accepting client connections. "
- "Possible causes: dynamic address renewal, or incompatible VPN or firewall software. "
- "Try using the Win32DisableAcceptEx directive.", my_pid);
- err_count = 0;
- }
- continue;
+
+ if (accf == 2) { /* 'data' */
+ len = APR_BUCKET_BUFF_SIZE;
+ buf = apr_bucket_alloc(len, context->ba);
+ len -= PADDED_ADDR_SIZE * 2;
}
- else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
- (rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) {
- ++err_count;
- if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
- ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
- "Child %lu: Encountered too many errors accepting client connections. "
- "Possible causes: Unknown. "
- "Try using the Win32DisableAcceptEx directive.", my_pid);
- err_count = 0;
- }
- closesocket(context->accept_socket);
- context->accept_socket = INVALID_SOCKET;
- continue;
+ else /* (accf == 1) 'connect' */ {
+ len = 0;
+ buf = context->buff;
}
- err_count = 0;
- /* Wait for pending i/o.
- * Wake up once per second to check for shutdown .
- * XXX: We should be waiting on exit_event instead of polling
+ /* AcceptEx on the completion context. The completion context will be
+ * signaled when a connection is accepted.
*/
- while (1) {
- rv = WaitForSingleObject(context->Overlapped.hEvent, 1000);
- if (rv == WAIT_OBJECT_0) {
- if (context->accept_socket == INVALID_SOCKET) {
- /* socket already closed */
- break;
+ if (!AcceptEx(nlsd, context->accept_socket, buf, len,
+ PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &BytesRead,
+ &context->overlapped)) {
+ rv = apr_get_netos_error();
+ if ((rv == APR_FROM_OS_ERROR(WSAECONNRESET)) ||
+ (rv == APR_FROM_OS_ERROR(WSAEACCES))) {
+ /* We can get here when:
+ * 1) the client disconnects early
+ * 2) handshake was incomplete
+ */
+ if (accf == 2)
+ apr_bucket_free(buf);
+ closesocket(context->accept_socket);
+ context->accept_socket = INVALID_SOCKET;
+ continue;
+ }
+ else if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) ||
+ (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) {
+ /* We can get here when:
+ * 1) TransmitFile does not properly recycle the accept socket (typically
+ * because the client disconnected)
+ * 2) there is VPN or Firewall software installed with
+ * buggy WSAAccept or WSADuplicateSocket implementation
+ * 3) the dynamic address / adapter has changed
+ * Give five chances, then fall back on AcceptFilter 'none'
+ */
+ if (accf == 2)
+ apr_bucket_free(buf);
+ closesocket(context->accept_socket);
+ context->accept_socket = INVALID_SOCKET;
+ ++err_count;
+ if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00337)
+ "Child: Encountered too many AcceptEx "
+ "faults accepting client connections. "
+ "Possible causes: dynamic address renewal, "
+ "or incompatible VPN or firewall software. ");
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, APLOGNO(00338)
+ "winnt_mpm: falling back to "
+ "'AcceptFilter none'.");
+ err_count = 0;
+ accf = 0;
}
- if (!GetOverlappedResult((HANDLE)context->accept_socket,
- &context->Overlapped,
+ continue;
+ }
+ else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
+ (rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) {
+ if (accf == 2)
+ apr_bucket_free(buf);
+ closesocket(context->accept_socket);
+ context->accept_socket = INVALID_SOCKET;
+ ++err_count;
+ if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00339)
+ "Child: Encountered too many AcceptEx "
+ "faults accepting client connections.");
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, APLOGNO(00340)
+ "winnt_mpm: falling back to "
+ "'AcceptFilter none'.");
+ err_count = 0;
+ accf = 0;
+ goto reinit;
+ }
+ continue;
+ }
+
+ err_count = 0;
+ events[0] = context->overlapped.hEvent;
+
+ do {
+ rv = WaitForMultipleObjectsEx(3, events, FALSE, INFINITE, TRUE);
+ } while (rv == WAIT_IO_COMPLETION);
+
+ if (rv == WAIT_OBJECT_0) {
+ if ((context->accept_socket != INVALID_SOCKET) &&
+ !GetOverlappedResult((HANDLE)context->accept_socket,
+ &context->overlapped,
&BytesRead, FALSE)) {
ap_log_error(APLOG_MARK, APLOG_WARNING,
- apr_get_os_error(), ap_server_conf,
+ apr_get_os_error(), ap_server_conf, APLOGNO(00341)
"winnt_accept: Asynchronous AcceptEx failed.");
closesocket(context->accept_socket);
context->accept_socket = INVALID_SOCKET;
}
- break;
}
- /* WAIT_TIMEOUT */
- if (shutdown_in_progress) {
+ else {
+ /* exit_event triggered or event handle was closed */
closesocket(context->accept_socket);
context->accept_socket = INVALID_SOCKET;
+ if (accf == 2)
+ apr_bucket_free(buf);
break;
}
+
+ if (context->accept_socket == INVALID_SOCKET) {
+ if (accf == 2)
+ apr_bucket_free(buf);
+ continue;
+ }
+ }
+ err_count = 0;
+
+ /* Potential optimization; consider handing off to the worker */
+
+ /* Inherit the listen socket settings. Required for
+ * shutdown() to work
+ */
+ if (setsockopt(context->accept_socket, SOL_SOCKET,
+ SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
+ sizeof(nlsd))) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(),
+ ap_server_conf, APLOGNO(00342)
+ "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
+ /* Not a failure condition. Keep running. */
+ }
+
+ /* Get the local & remote address
+ * TODO; error check
+ */
+ GetAcceptExSockaddrs(buf, len, PADDED_ADDR_SIZE, PADDED_ADDR_SIZE,
+ &context->sa_server, &context->sa_server_len,
+ &context->sa_client, &context->sa_client_len);
+
+ /* For 'data', craft a bucket for our data result
+ * and pass to worker_main as context->overlapped.Pointer
+ */
+ if (accf == 2 && BytesRead)
+ {
+ apr_bucket *b;
+ b = apr_bucket_heap_create(buf, APR_BUCKET_BUFF_SIZE,
+ apr_bucket_free, context->ba);
+ /* Adjust the bucket to refer to the actual bytes read */
+ b->length = BytesRead;
+ context->overlapped.Pointer = b;
}
+ else
+ context->overlapped.Pointer = NULL;
+ }
+ else /* (accf = 0) e.g. 'none' */
+ {
+ /* There is no socket reuse without AcceptEx() */
+ if (context->accept_socket != INVALID_SOCKET)
+ closesocket(context->accept_socket);
+
+ /* This could be a persistent event per-listener rather than
+ * per-accept. However, the event needs to be removed from
+ * the target socket if not removed from the listen socket
+ * prior to accept(), or the event select is inherited.
+ * and must be removed from the accepted socket.
+ */
+
+ do {
+ rv = WaitForMultipleObjectsEx(3, events, FALSE, INFINITE, TRUE);
+ } while (rv == WAIT_IO_COMPLETION);
+
+
+ if (rv != WAIT_OBJECT_0 + 2) {
+ /* not FD_ACCEPT;
+ * exit_event triggered or event handle was closed
+ */
+ break;
+ }
+
+ context->sa_server = (void *) context->buff;
+ context->sa_server_len = sizeof(context->buff) / 2;
+ context->sa_client_len = context->sa_server_len;
+ context->sa_client = (void *) (context->buff
+ + context->sa_server_len);
+
+ context->accept_socket = accept(nlsd, context->sa_server,
+ &context->sa_server_len);
+
if (context->accept_socket == INVALID_SOCKET) {
+
+ rv = apr_get_netos_error();
+ if ( rv == APR_FROM_OS_ERROR(WSAECONNRESET)
+ || rv == APR_FROM_OS_ERROR(WSAEINPROGRESS)
+ || rv == APR_FROM_OS_ERROR(WSAEWOULDBLOCK) ) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG,
+ rv, ap_server_conf, APLOGNO(00343)
+ "accept() failed, retrying.");
+ continue;
+ }
+
+ /* A more serious error than 'retry', log it */
+ ap_log_error(APLOG_MARK, APLOG_WARNING,
+ rv, ap_server_conf, APLOGNO(00344)
+ "accept() failed.");
+
+ if ( rv == APR_FROM_OS_ERROR(WSAEMFILE)
+ || rv == APR_FROM_OS_ERROR(WSAENOBUFS) ) {
+ /* Hopefully a temporary condition in the provider? */
+ Sleep(100);
+ ++err_count;
+ if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00345)
+ "Child: Encountered too many accept() "
+ "resource faults, aborting.");
+ break;
+ }
+ continue;
+ }
+ break;
+ }
+ /* Per MSDN, cancel the inherited association of this socket
+ * to the WSAEventSelect API, and restore the state corresponding
+ * to apr_os_sock_make's default assumptions (really, a flaw within
+ * os_sock_make and os_sock_put that it does not query).
+ */
+ WSAEventSelect(context->accept_socket, 0, 0);
+ context->overlapped.Pointer = NULL;
+ err_count = 0;
+
+ context->sa_server_len = sizeof(context->buff) / 2;
+ if (getsockname(context->accept_socket, context->sa_server,
+ &context->sa_server_len) == SOCKET_ERROR) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, APLOGNO(00346)
+ "getsockname failed");
continue;
}
+ if ((getpeername(context->accept_socket, context->sa_client,
+ &context->sa_client_len)) == SOCKET_ERROR) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, APLOGNO(00347)
+ "getpeername failed");
+ memset(&context->sa_client, '\0', sizeof(context->sa_client));
+ }
}
- err_count = 0;
- /* Inherit the listen socket settings. Required for
- * shutdown() to work
- */
- if (setsockopt(context->accept_socket, SOL_SOCKET,
- SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
- sizeof(nlsd))) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
- "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
- /* Not a failure condition. Keep running. */
- }
-
- /* Get the local & remote address */
- GetAcceptExSockaddrs(context->buff,
- 0,
- PADDED_ADDR_SIZE,
- PADDED_ADDR_SIZE,
- &context->sa_server,
- &context->sa_server_len,
- &context->sa_client,
- &context->sa_client_len);
sockinfo.os_sock = &context->accept_socket;
sockinfo.local = context->sa_server;
sockinfo.remote = context->sa_client;
sockinfo.family = context->sa_server->sa_family;
sockinfo.type = SOCK_STREAM;
+ /* Restore the state corresponding to apr_os_sock_make's default
+ * assumption of timeout -1 (really, a flaw of os_sock_make and
+ * os_sock_put that it does not query to determine ->timeout).
+ * XXX: Upon a fix to APR, these three statements should disappear.
+ */
+ ioctlsocket(context->accept_socket, FIONBIO, &zero);
+ setsockopt(context->accept_socket, SOL_SOCKET, SO_RCVTIMEO,
+ (char *) &zero, sizeof(zero));
+ setsockopt(context->accept_socket, SOL_SOCKET, SO_SNDTIMEO,
+ (char *) &zero, sizeof(zero));
apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
- /* When a connection is received, send an io completion notification to
- * the ThreadDispatchIOCP. This function could be replaced by
- * mpm_post_completion_context(), but why do an extra function call...
+ /* When a connection is received, send an io completion notification
+ * to the ThreadDispatchIOCP.
*/
- PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED,
- &context->Overlapped);
+ PostQueuedCompletionStatus(ThreadDispatchIOCP, BytesRead,
+ IOCP_CONNECTION_ACCEPTED,
+ &context->overlapped);
context = NULL;
}
+ if (!accf)
+ CloseHandle(events[2]);
+
if (!shutdown_in_progress) {
/* Yow, hit an irrecoverable error! Tell the child to die. */
SetEvent(exit_event);
}
- ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
- "Child %lu: Accept thread exiting.", my_pid);
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00348)
+ "Child: Accept thread exiting.");
return 0;
}
-static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
+static winnt_conn_ctx_t *winnt_get_connection(winnt_conn_ctx_t *context)
{
int rc;
DWORD BytesRead;
@@ -706,18 +708,19 @@ static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
apr_atomic_dec32(&g_blocked_threads);
return NULL;
}
- rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey,
- &pol, INFINITE);
+ rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead,
+ &CompKey, &pol, INFINITE);
if (!rc) {
rc = apr_get_os_error();
- ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, ap_server_conf,
- "Child %lu: GetQueuedComplationStatus returned %d", my_pid, rc);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, ap_server_conf, APLOGNO(00349)
+ "Child: GetQueuedComplationStatus returned %d",
+ rc);
continue;
}
switch (CompKey) {
case IOCP_CONNECTION_ACCEPTED:
- context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped);
+ context = CONTAINING_RECORD(pol, winnt_conn_ctx_t, overlapped);
break;
case IOCP_SHUTDOWN:
apr_atomic_dec32(&g_blocked_threads);
@@ -739,33 +742,35 @@ static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
* Main entry point for the worker threads. Worker threads block in
* win*_get_connection() awaiting a connection to service.
*/
-static unsigned int __stdcall worker_main(void *thread_num_val)
+static DWORD __stdcall worker_main(void *thread_num_val)
{
+ apr_thread_t *thd = NULL;
+ apr_os_thread_t osthd;
static int requests_this_child = 0;
- PCOMP_CONTEXT context = NULL;
+ winnt_conn_ctx_t *context = NULL;
int thread_num = (int)thread_num_val;
ap_sb_handle_t *sbh;
+ apr_bucket *e;
+ int rc;
+ conn_rec *c;
+ apr_int32_t disconnected;
+
+ osthd = apr_os_thread_current();
+ apr_os_thread_put(&thd, &osthd, pchild);
while (1) {
- conn_rec *c;
- apr_int32_t disconnected;
ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL);
/* Grab a connection off the network */
- if (use_acceptex) {
- context = winnt_get_connection(context);
- }
- else {
- context = win9x_get_connection(context);
- }
+ context = winnt_get_connection(context);
if (!context) {
/* Time for the thread to exit */
break;
}
- /* Have we hit MaxRequestPerChild connections? */
+ /* Have we hit MaxConnectionsPerChild connections? */
if (ap_max_requests_per_child) {
requests_this_child++;
if (requests_this_child > ap_max_requests_per_child) {
@@ -773,33 +778,81 @@ static unsigned int __stdcall worker_main(void *thread_num_val)
}
}
+ e = context->overlapped.Pointer;
+
ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num);
c = ap_run_create_connection(context->ptrans, ap_server_conf,
context->sock, thread_num, sbh,
context->ba);
- if (c) {
- ap_process_connection(c, context->sock);
+ if (!c)
+ {
+ /* ap_run_create_connection closes the socket on failure */
+ context->accept_socket = INVALID_SOCKET;
+ if (e)
+ apr_bucket_free(e);
+ continue;
+ }
+
+ c->current_thread = thd;
+
+ /* follow ap_process_connection(c, context->sock) logic
+ * as it left us no chance to reinject our first data bucket.
+ */
+ ap_update_vhost_given_ip(c);
+
+ rc = ap_run_pre_connection(c, context->sock);
+ if (rc != OK && rc != DONE) {
+ c->aborted = 1;
+ }
+
+ if (e && c->aborted)
+ {
+ apr_bucket_free(e);
+ }
+ else if (e)
+ {
+ core_ctx_t *ctx;
+ core_net_rec *net;
+ ap_filter_t *filt;
+
+ filt = c->input_filters;
+ while ((strcmp(filt->frec->name, "core_in") != 0) && filt->next)
+ filt = filt->next;
+ net = filt->ctx;
+ ctx = net->in_ctx;
+
+ if (net->in_ctx)
+ ctx = net->in_ctx;
+ else
+ {
+ ctx = apr_pcalloc(c->pool, sizeof(*ctx));
+ ctx->b = apr_brigade_create(c->pool, c->bucket_alloc);
+ ctx->tmpbb = apr_brigade_create(c->pool, c->bucket_alloc);
+
+ /* seed the brigade with AcceptEx read heap bucket */
+ e = context->overlapped.Pointer;
+ APR_BRIGADE_INSERT_HEAD(ctx->b, e);
+
+ /* also seed the brigade with the client socket. */
+ e = apr_bucket_socket_create(net->client_socket,
+ c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(ctx->b, e);
+ net->in_ctx = ctx;
+ }
+ }
+
+ if (!c->aborted)
+ {
+ ap_run_process_connection(c);
+
apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED,
&disconnected);
+
if (!disconnected) {
context->accept_socket = INVALID_SOCKET;
ap_lingering_close(c);
}
- else if (!use_acceptex) {
- /* If the socket is disconnected but we are not using acceptex,
- * we cannot reuse the socket. Disconnected sockets are removed
- * from the apr_socket_t struct by apr_sendfile() to prevent the
- * socket descriptor from being inadvertently closed by a call
- * to apr_socket_close(), so close it directly.
- */
- closesocket(context->accept_socket);
- context->accept_socket = INVALID_SOCKET;
- }
- }
- else {
- /* ap_run_create_connection closes the socket on failure */
- context->accept_socket = INVALID_SOCKET;
}
}
@@ -810,7 +863,8 @@ static unsigned int __stdcall worker_main(void *thread_num_val)
}
-static void cleanup_thread(HANDLE *handles, int *thread_cnt, int thread_to_clean)
+static void cleanup_thread(HANDLE *handles, int *thread_cnt,
+ int thread_to_clean)
{
int i;
@@ -832,33 +886,32 @@ static void create_listener_thread(void)
{
unsigned tid;
int num_listeners = 0;
- if (!use_acceptex) {
- _beginthreadex(NULL, 0, win9x_accept,
- NULL, 0, &tid);
- } else {
- /* Start an accept thread per listener
- * XXX: Why would we have a NULL sd in our listeners?
- */
- ap_listen_rec *lr;
-
- /* Number of completion_contexts allowed in the system is
- * (ap_threads_per_child + num_listeners). We need the additional
- * completion contexts to prevent server hangs when ThreadsPerChild
- * is configured to something less than or equal to the number
- * of listeners. This is not a usual case, but people have
- * encountered it.
- * */
- for (lr = ap_listeners; lr ; lr = lr->next) {
- num_listeners++;
- }
- max_num_completion_contexts = ap_threads_per_child + num_listeners;
+ /* Start an accept thread per listener
+ * XXX: Why would we have a NULL sd in our listeners?
+ */
+ ap_listen_rec *lr;
- /* Now start a thread per listener */
- for (lr = ap_listeners; lr; lr = lr->next) {
- if (lr->sd != NULL) {
- _beginthreadex(NULL, 1000, winnt_accept,
- (void *) lr, 0, &tid);
- }
+ /* Number of completion_contexts allowed in the system is
+ * (ap_threads_per_child + num_listeners). We need the additional
+ * completion contexts to prevent server hangs when ThreadsPerChild
+ * is configured to something less than or equal to the number
+ * of listeners. This is not a usual case, but people have
+ * encountered it.
+ */
+ for (lr = ap_listeners; lr ; lr = lr->next) {
+ num_listeners++;
+ }
+ max_num_completion_contexts = ap_threads_per_child + num_listeners;
+
+ /* Now start a thread per listener */
+ for (lr = ap_listeners; lr; lr = lr->next) {
+ if (lr->sd != NULL) {
+ /* A smaller stack is sufficient.
+ * To convert to CreateThread, the returned handle cannot be
+ * ignored, it must be closed/joined.
+ */
+ _beginthreadex(NULL, 65536, winnt_accept,
+ (void *) lr, stack_res_flag, &tid);
}
}
}
@@ -876,7 +929,7 @@ void child_main(apr_pool_t *pconf)
int watch_thread;
int time_remains;
int cld;
- unsigned tid;
+ DWORD tid;
int rv;
int i;
@@ -889,17 +942,13 @@ void child_main(apr_pool_t *pconf)
/* Initialize the child_events */
max_requests_per_child_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!max_requests_per_child_event) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "Child %lu: Failed to create a max_requests event.", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00350)
+ "Child: Failed to create a max_requests event.");
exit(APEXIT_CHILDINIT);
}
child_events[0] = exit_event;
child_events[1] = max_requests_per_child_event;
- allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0, 1000000, NULL);
- apr_thread_mutex_create(&allowed_globals.jobmutex,
- APR_THREAD_MUTEX_DEFAULT, pchild);
-
/*
* Wait until we have permission to start accepting connections.
* start_mutex is used to ensure that only one child ever
@@ -907,38 +956,36 @@ void child_main(apr_pool_t *pconf)
*/
status = apr_proc_mutex_lock(start_mutex);
if (status != APR_SUCCESS) {
- ap_log_error(APLOG_MARK,APLOG_ERR, status, ap_server_conf,
- "Child %lu: Failed to acquire the start_mutex. Process will exit.", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_ERR, status, ap_server_conf, APLOGNO(00351)
+ "Child: Failed to acquire the start_mutex. "
+ "Process will exit.");
exit(APEXIT_CHILDINIT);
}
- ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
- "Child %lu: Acquired the start mutex.", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00352)
+ "Child: Acquired the start mutex.");
/*
* Create the worker thread dispatch IOCompletionPort
- * on Windows NT/2000
*/
- if (use_acceptex) {
- /* Create the worker thread dispatch IOCP */
- ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
- NULL,
- 0,
- 0); /* CONCURRENT ACTIVE THREADS */
- apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
- qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (!qwait_event) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "Child %lu: Failed to create a qwait event.", my_pid);
- exit(APEXIT_CHILDINIT);
- }
+ /* Create the worker thread dispatch IOCP */
+ ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
+ NULL, 0, 0);
+ apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
+ qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!qwait_event) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(),
+ ap_server_conf, APLOGNO(00353)
+ "Child: Failed to create a qwait event.");
+ exit(APEXIT_CHILDINIT);
}
/*
* Create the pool of worker threads
*/
- ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
- "Child %lu: Starting %d worker threads.", my_pid, ap_threads_per_child);
- child_handles = (HANDLE) apr_pcalloc(pchild, ap_threads_per_child * sizeof(HANDLE));
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00354)
+ "Child: Starting %d worker threads.", ap_threads_per_child);
+ child_handles = (HANDLE) apr_pcalloc(pchild, ap_threads_per_child
+ * sizeof(HANDLE));
apr_thread_mutex_create(&child_lock, APR_THREAD_MUTEX_DEFAULT, pchild);
while (1) {
@@ -949,19 +996,24 @@ void child_main(apr_pool_t *pconf)
continue;
}
ap_update_child_status_from_indexes(0, i, SERVER_STARTING, NULL);
- child_handles[i] = (HANDLE) _beginthreadex(NULL, (unsigned)ap_thread_stacksize,
- worker_main, (void *) i, 0, &tid);
+
+ child_handles[i] = CreateThread(NULL, ap_thread_stacksize,
+ worker_main, (void *) i,
+ stack_res_flag, &tid);
if (child_handles[i] == 0) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "Child %lu: _beginthreadex failed. Unable to create all worker threads. "
- "Created %d of the %d threads requested with the ThreadsPerChild configuration directive.",
- my_pid, threads_created, ap_threads_per_child);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(),
+ ap_server_conf, APLOGNO(00355)
+ "Child: CreateThread failed. Unable to "
+ "create all worker threads. Created %d of the %d "
+ "threads requested with the ThreadsPerChild "
+ "configuration directive.",
+ threads_created, ap_threads_per_child);
ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
goto shutdown;
}
threads_created++;
- /* Save the score board index in ht keyed to the thread handle. We need this
- * when cleaning up threads down below...
+ /* Save the score board index in ht keyed to the thread handle.
+ * We need this when cleaning up threads down below...
*/
apr_thread_mutex_lock(child_lock);
score_idx = apr_pcalloc(pchild, sizeof(int));
@@ -982,7 +1034,8 @@ void child_main(apr_pool_t *pconf)
if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) {
break;
}
- /* wait for previous generation to clean up an entry in the scoreboard */
+ /* wait for previous generation to clean up an entry in the scoreboard
+ */
apr_sleep(1 * APR_USEC_PER_SEC);
}
@@ -993,7 +1046,7 @@ void child_main(apr_pool_t *pconf)
*
* max_requests_per_child_event:
* This event is signaled by the worker threads to indicate that
- * the process has handled MaxRequestsPerChild connections.
+ * the process has handled MaxConnectionsPerChild connections.
*
* TIMEOUT:
* To do periodic maintenance on the server (check for thread exits,
@@ -1012,10 +1065,10 @@ void child_main(apr_pool_t *pconf)
*/
while (1) {
#if !APR_HAS_OTHER_CHILD
- rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, INFINITE);
+ rv = WaitForMultipleObjects(2, (HANDLE *)child_events, FALSE, INFINITE);
cld = rv - WAIT_OBJECT_0;
#else
- rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, 1000);
+ rv = WaitForMultipleObjects(2, (HANDLE *)child_events, FALSE, 1000);
cld = rv - WAIT_OBJECT_0;
if (rv == WAIT_TIMEOUT) {
apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
@@ -1024,24 +1077,26 @@ void child_main(apr_pool_t *pconf)
#endif
if (rv == WAIT_FAILED) {
/* Something serious is wrong */
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "Child %lu: WAIT_FAILED -- shutting down server", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(),
+ ap_server_conf, APLOGNO(00356)
+ "Child: WAIT_FAILED -- shutting down server");
break;
}
else if (cld == 0) {
/* Exit event was signaled */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
- "Child %lu: Exit event signaled. Child process is ending.", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00357)
+ "Child: Exit event signaled. Child process is "
+ "ending.");
break;
}
else {
- /* MaxRequestsPerChild event set by the worker threads.
+ /* MaxConnectionsPerChild event set by the worker threads.
* Signal the parent to restart
*/
- ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
- "Child %lu: Process exiting because it reached "
- "MaxRequestsPerChild. Signaling the parent to "
- "restart a new child process.", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00358)
+ "Child: Process exiting because it reached "
+ "MaxConnectionsPerChild. Signaling the parent to "
+ "restart a new child process.");
ap_signal_parent(SIGNAL_PARENT_RESTART);
break;
}
@@ -1054,10 +1109,6 @@ void child_main(apr_pool_t *pconf)
shutdown:
winnt_mpm_state = AP_MPMQ_STOPPING;
- /* Setting is_graceful will cause threads handling keep-alive connections
- * to close the connection after handling the current request.
- */
- is_graceful = 1;
/* Close the listening sockets. Note, we must close the listeners
* before closing any accept sockets pending in AcceptEx to prevent
@@ -1067,7 +1118,7 @@ void child_main(apr_pool_t *pconf)
apr_socket_close(lr->sd);
}
- /* Shutdown listener threads and pending AcceptEx socksts
+ /* Shutdown listener threads and pending AcceptEx sockets
* but allow the worker threads to continue consuming from
* the queue of accepted connections.
*/
@@ -1083,42 +1134,38 @@ void child_main(apr_pool_t *pconf)
*/
rv = apr_proc_mutex_unlock(start_mutex);
if (rv == APR_SUCCESS) {
- ap_log_error(APLOG_MARK,APLOG_NOTICE, rv, ap_server_conf,
- "Child %lu: Released the start mutex", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf, APLOGNO(00359)
+ "Child: Released the start mutex");
}
else {
- ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
- "Child %lu: Failure releasing the start mutex", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00360)
+ "Child: Failure releasing the start mutex");
}
- /* Shutdown the worker threads */
- if (!use_acceptex) {
- for (i = 0; i < threads_created; i++) {
- add_job(INVALID_SOCKET);
+ /* Shutdown the worker threads
+ * Post worker threads blocked on the ThreadDispatch IOCompletion port
+ */
+ while (g_blocked_threads > 0) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00361)
+ "Child: %d threads blocked on the completion port",
+ g_blocked_threads);
+ for (i=g_blocked_threads; i > 0; i--) {
+ PostQueuedCompletionStatus(ThreadDispatchIOCP, 0,
+ IOCP_SHUTDOWN, NULL);
}
+ Sleep(1000);
}
- else { /* Windows NT/2000 */
- /* Post worker threads blocked on the ThreadDispatch IOCompletion port */
- while (g_blocked_threads > 0) {
- ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, ap_server_conf,
- "Child %lu: %d threads blocked on the completion port", my_pid, g_blocked_threads);
- for (i=g_blocked_threads; i > 0; i--) {
- PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_SHUTDOWN, NULL);
- }
- Sleep(1000);
- }
- /* Empty the accept queue of completion contexts */
- apr_thread_mutex_lock(qlock);
- while (qhead) {
- CloseHandle(qhead->Overlapped.hEvent);
- closesocket(qhead->accept_socket);
- qhead = qhead->next;
- }
- apr_thread_mutex_unlock(qlock);
+ /* Empty the accept queue of completion contexts */
+ apr_thread_mutex_lock(qlock);
+ while (qhead) {
+ CloseHandle(qhead->overlapped.hEvent);
+ closesocket(qhead->accept_socket);
+ qhead = qhead->next;
}
+ apr_thread_mutex_unlock(qlock);
- /* Give busy threads a chance to service their connections,
- * (no more than the global server timeout period which
+ /* Give busy threads a chance to service their connections
+ * (no more than the global server timeout period which
* we track in msec remaining).
*/
watch_thread = 0;
@@ -1139,11 +1186,11 @@ void child_main(apr_pool_t *pconf)
/* Every 30 seconds give an update */
if ((time_remains % 30000) == 0) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS,
- ap_server_conf,
- "Child %lu: Waiting %d more seconds "
- "for %d worker threads to finish.",
- my_pid, time_remains / 1000, threads_created);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS,
+ ap_server_conf, APLOGNO(00362)
+ "Child: Waiting %d more seconds "
+ "for %d worker threads to finish.",
+ time_remains / 1000, threads_created);
}
/* We'll poll from the top, 10 times per second */
Sleep(100);
@@ -1181,12 +1228,12 @@ void child_main(apr_pool_t *pconf)
cleanup_thread(child_handles, &threads_created, watch_thread);
}
}
-
+
/* Kill remaining threads off the hard way */
if (threads_created) {
- ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
- "Child %lu: Terminating %d threads that failed to exit.",
- my_pid, threads_created);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00363)
+ "Child: Terminating %d threads that failed to exit.",
+ threads_created);
}
for (i = 0; i < threads_created; i++) {
int *score_idx;
@@ -1195,21 +1242,15 @@ void child_main(apr_pool_t *pconf)
/* Reset the scoreboard entry for the thread we just whacked */
score_idx = apr_hash_get(ht, &child_handles[i], sizeof(HANDLE));
if (score_idx) {
- ap_update_child_status_from_indexes(0, *score_idx,
- SERVER_DEAD, NULL);
+ ap_update_child_status_from_indexes(0, *score_idx, SERVER_DEAD, NULL);
}
}
- ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
- "Child %lu: All worker threads have exited.", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00364)
+ "Child: All worker threads have exited.");
- CloseHandle(allowed_globals.jobsemaphore);
- apr_thread_mutex_destroy(allowed_globals.jobmutex);
apr_thread_mutex_destroy(child_lock);
-
- if (use_acceptex) {
- apr_thread_mutex_destroy(qlock);
- CloseHandle(qwait_event);
- }
+ apr_thread_mutex_destroy(qlock);
+ CloseHandle(qwait_event);
apr_pool_destroy(pchild);
CloseHandle(exit_event);
diff --git a/server/mpm/winnt/config.m4 b/server/mpm/winnt/config.m4
index 181322b0..5c1fd42d 100644
--- a/server/mpm/winnt/config.m4
+++ b/server/mpm/winnt/config.m4
@@ -1,3 +1,10 @@
-if test "$MPM_NAME" = "winnt" ; then
- APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
-fi
+AC_MSG_CHECKING(if WinNT MPM supports this platform)
+case $host in
+ *mingw32*)
+ AC_MSG_RESULT(yes)
+ APACHE_MPM_SUPPORTED(winnt, no, yes)
+ ;;
+ *)
+ AC_MSG_RESULT(no)
+ ;;
+esac
diff --git a/server/mpm/winnt/config3.m4 b/server/mpm/winnt/config3.m4
new file mode 100644
index 00000000..f937e404
--- /dev/null
+++ b/server/mpm/winnt/config3.m4
@@ -0,0 +1,2 @@
+winnt_objects="child.lo mpm_winnt.lo nt_eventlog.lo service.lo"
+APACHE_MPM_MODULE(winnt, $enable_mpm_winnt, $winnt_objects)
diff --git a/server/mpm/winnt/mpm.h b/server/mpm/winnt/mpm.h
deleted file mode 100644
index 536ee6aa..00000000
--- a/server/mpm/winnt/mpm.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file winnt/mpm.h
- * @brief MPM for Windows NT
- *
- * this is the place to make declarations that are MPM specific but that must be
- * shared with non-mpm specific code in the server. Hummm, perhaps we can
- * move most of this stuff to mpm_common.h?
- *
- * @defgroup APACHE_MPM_WINNT WinNT MPM
- * @ingroup APACHE_OS_WIN32 APACHE_MPM
- * @{
- */
-
-#ifndef APACHE_MPM_H
-#define APACHE_MPM_H
-
-#include "scoreboard.h"
-
-#define MPM_NAME "WinNT"
-
-#define AP_MPM_WANT_SET_PIDFILE
-#define AP_MPM_WANT_SET_MAX_REQUESTS
-#define AP_MPM_WANT_SET_COREDUMPDIR
-#define AP_MPM_WANT_SET_SCOREBOARD
-#define AP_MPM_WANT_SET_MAX_MEM_FREE
-#define AP_MPM_WANT_SET_STACKSIZE
-
-extern int ap_threads_per_child;
-extern int ap_thread_limit;
-extern server_rec *ap_server_conf;
-
-#endif /* APACHE_MPM_H */
-/** @} */
diff --git a/server/mpm/winnt/mpm_default.h b/server/mpm/winnt/mpm_default.h
index 990eb47a..a0e38efe 100644
--- a/server/mpm/winnt/mpm_default.h
+++ b/server/mpm/winnt/mpm_default.h
@@ -55,35 +55,5 @@
*/
#define HARD_SERVER_LIMIT 1
-/* Number of servers to spawn off by default
- */
-#ifndef DEFAULT_NUM_DAEMON
-#define DEFAULT_NUM_DAEMON 1
-#endif
-
-/* Check for definition of DEFAULT_REL_RUNTIMEDIR */
-#ifndef DEFAULT_REL_RUNTIMEDIR
-#define DEFAULT_REL_RUNTIMEDIR "logs"
-#endif
-
-/* Where the main/parent process's pid is logged */
-#ifndef DEFAULT_PIDLOG
-#define DEFAULT_PIDLOG DEFAULT_REL_RUNTIMEDIR "/httpd.pid"
-#endif
-
-/*
- * Interval, in microseconds, between scoreboard maintenance.
- */
-#ifndef SCOREBOARD_MAINTENANCE_INTERVAL
-#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
-#endif
-
-/* Number of requests to try to handle in a single process. If <= 0,
- * the children don't die off.
- */
-#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
-#define DEFAULT_MAX_REQUESTS_PER_CHILD 0
-#endif
-
#endif /* AP_MPM_DEFAULT_H */
/** @} */
diff --git a/server/mpm/winnt/mpm_winnt.c b/server/mpm/winnt/mpm_winnt.c
index d9513175..cdf7cac6 100644
--- a/server/mpm/winnt/mpm_winnt.c
+++ b/server/mpm/winnt/mpm_winnt.c
@@ -16,7 +16,6 @@
#ifdef WIN32
-#define CORE_PRIVATE
#include "httpd.h"
#include "http_main.h"
#include "http_log.h"
@@ -31,6 +30,7 @@
#include "apr_shm.h"
#include "apr_thread_mutex.h"
#include "ap_mpm.h"
+#include "apr_general.h"
#include "ap_config.h"
#include "ap_listen.h"
#include "mpm_default.h"
@@ -38,6 +38,15 @@
#include "mpm_common.h"
#include <malloc.h>
#include "apr_atomic.h"
+#include "scoreboard.h"
+
+#ifdef __WATCOMC__
+#define _environ environ
+#endif
+
+#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION /* missing on MinGW */
+#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000
+#endif
/* Because ap_setup_listeners() is skipped in the child, any merging
* of [::]:80 and 0.0.0.0:80 for AP_ENABLE_V4_MAPPED in the parent
@@ -53,49 +62,43 @@
* score by moving a handle down the pipe into the child's stdin.
*/
extern apr_shm_t *ap_scoreboard_shm;
-server_rec *ap_server_conf;
+
+/* my_generation is returned to the scoreboard code */
+static volatile ap_generation_t my_generation=0;
/* Definitions of WINNT MPM specific config globals */
static HANDLE shutdown_event; /* used to signal the parent to shutdown */
static HANDLE restart_event; /* used to signal the parent to restart */
-char ap_coredump_dir[MAX_STRING_LEN];
-
static int one_process = 0;
static char const* signal_arg = NULL;
OSVERSIONINFO osver; /* VER_PLATFORM_WIN32_NT */
+/* set by child_main to STACK_SIZE_PARAM_IS_A_RESERVATION for NT >= 5.1 (XP/2003) */
+DWORD stack_res_flag;
+
static DWORD parent_pid;
DWORD my_pid;
+/* used by parent to signal the child to start and exit */
+apr_proc_mutex_t *start_mutex;
+HANDLE exit_event;
+
int ap_threads_per_child = 0;
-int use_acceptex = 1;
-static int thread_limit = DEFAULT_THREAD_LIMIT;
+static int thread_limit = 0;
static int first_thread_limit = 0;
-static int changed_limit_at_restart;
int winnt_mpm_state = AP_MPMQ_STARTING;
-/* ap_my_generation are used by the scoreboard code */
-ap_generation_t volatile ap_my_generation=0;
-
/* shared by service.c as global, although
* perhaps it should be private.
*/
apr_pool_t *pconf;
-
/* definitions from child.c */
void child_main(apr_pool_t *pconf);
-/* used by parent to signal the child to start and exit
- * NOTE: these are not sophisticated enough for multiple children
- * so they ultimately should not be shared with child.c
- */
-extern apr_proc_mutex_t *start_mutex;
-extern HANDLE exit_event;
-
-/* Only one of these, the pipe from our parent, ment only for
+/* Only one of these, the pipe from our parent, meant only for
* one child worker's consumption (not to be inherited!)
* XXX: decorate this name for the trunk branch, was left simplified
* only to make the 2.2 patch trivial to read.
@@ -114,75 +117,16 @@ static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, const cha
}
ap_threads_per_child = atoi(arg);
- if (ap_threads_per_child > thread_limit) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
- "value of %d threads,", ap_threads_per_child,
- thread_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering ThreadsPerChild to %d. To increase, please"
- " see the", thread_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " ThreadLimit directive.");
- ap_threads_per_child = thread_limit;
- }
- else if (ap_threads_per_child < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require ThreadsPerChild > 0, setting to 1");
- ap_threads_per_child = 1;
- }
return NULL;
}
static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg)
{
- int tmp_thread_limit;
-
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
- tmp_thread_limit = atoi(arg);
- /* you cannot change ThreadLimit across a restart; ignore
- * any such attempts
- */
- if (first_thread_limit &&
- tmp_thread_limit != thread_limit) {
- /* how do we log a message? the error log is a bit bucket at this
- * point; we'll just have to set a flag so that ap_mpm_run()
- * logs a warning later
- */
- changed_limit_at_restart = 1;
- return NULL;
- }
- thread_limit = tmp_thread_limit;
-
- if (thread_limit > MAX_THREAD_LIMIT) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: ThreadLimit of %d exceeds compile time limit "
- "of %d threads,", thread_limit, MAX_THREAD_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering ThreadLimit to %d.", MAX_THREAD_LIMIT);
- thread_limit = MAX_THREAD_LIMIT;
- }
- else if (thread_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require ThreadLimit > 0, setting to 1");
- thread_limit = 1;
- }
- return NULL;
-}
-static const char *set_disable_acceptex(cmd_parms *cmd, void *dummy)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
- if (use_acceptex) {
- use_acceptex = 0;
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
- "Disabled use of AcceptEx() WinSock2 API");
- }
+ thread_limit = atoi(arg);
return NULL;
}
@@ -192,12 +136,25 @@ AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
"Number of threads each child creates" ),
AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
"Maximum worker threads in a server for this run of Apache"),
-AP_INIT_NO_ARGS("Win32DisableAcceptEx", set_disable_acceptex, NULL, RSRC_CONF,
- "Disable use of the high performance AcceptEx WinSock2 API to work around buggy VPN or Firewall software"),
-
{ NULL }
};
+static void winnt_note_child_started(int slot, pid_t pid)
+{
+ ap_scoreboard_image->parent[slot].pid = pid;
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[slot].pid,
+ my_generation, slot, MPM_CHILD_STARTED);
+}
+
+static void winnt_note_child_killed(int slot)
+{
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[slot].pid,
+ ap_scoreboard_image->parent[slot].generation,
+ slot, MPM_CHILD_EXITED);
+ ap_scoreboard_image->parent[slot].pid = 0;
+}
/*
* Signalling Apache on NT.
@@ -244,13 +201,6 @@ void setup_signal_names(char *prefix)
"%s_restart", signal_name_prefix);
}
-int volatile is_graceful = 0;
-
-AP_DECLARE(int) ap_graceful_stop_signalled(void)
-{
- return is_graceful;
-}
-
AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type)
{
HANDLE e;
@@ -267,7 +217,6 @@ AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type)
case SIGNAL_PARENT_RESTART:
case SIGNAL_PARENT_RESTART_GRACEFUL:
{
- is_graceful = 1;
SetEvent(restart_event);
break;
}
@@ -286,7 +235,6 @@ AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type)
case SIGNAL_PARENT_RESTART_GRACEFUL:
{
signal_name = signal_restart_name;
- is_graceful = 1;
break;
}
default:
@@ -298,13 +246,13 @@ AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type)
/* Um, problem, can't signal the parent, which means we can't
* signal ourselves to die. Ignore for now...
*/
- ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf, APLOGNO(00382)
"OpenEvent on %s event", signal_name);
return;
}
if (SetEvent(e) == 0) {
/* Same problem as above */
- ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), ap_server_conf, APLOGNO(00383)
"SetEvent on %s event", signal_name);
CloseHandle(e);
return;
@@ -338,8 +286,8 @@ static void get_handles_from_parent(server_rec *s, HANDLE *child_exit_event,
if (!ReadFile(pipe, &ready_event, sizeof(HANDLE),
&BytesRead, (LPOVERLAPPED) NULL)
|| (BytesRead != sizeof(HANDLE))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "Child %lu: Unable to retrieve the ready event from the parent", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00384)
+ "Child: Unable to retrieve the ready event from the parent");
exit(APEXIT_CHILDINIT);
}
@@ -349,45 +297,45 @@ static void get_handles_from_parent(server_rec *s, HANDLE *child_exit_event,
if (!ReadFile(pipe, child_exit_event, sizeof(HANDLE),
&BytesRead, (LPOVERLAPPED) NULL)
|| (BytesRead != sizeof(HANDLE))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "Child %lu: Unable to retrieve the exit event from the parent", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00385)
+ "Child: Unable to retrieve the exit event from the parent");
exit(APEXIT_CHILDINIT);
}
if (!ReadFile(pipe, &os_start, sizeof(os_start),
&BytesRead, (LPOVERLAPPED) NULL)
|| (BytesRead != sizeof(os_start))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "Child %lu: Unable to retrieve the start_mutex from the parent", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00386)
+ "Child: Unable to retrieve the start_mutex from the parent");
exit(APEXIT_CHILDINIT);
}
*child_start_mutex = NULL;
if ((rv = apr_os_proc_mutex_put(child_start_mutex, &os_start, s->process->pool))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
- "Child %lu: Unable to access the start_mutex from the parent", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00387)
+ "Child: Unable to access the start_mutex from the parent");
exit(APEXIT_CHILDINIT);
}
if (!ReadFile(pipe, &hScore, sizeof(hScore),
&BytesRead, (LPOVERLAPPED) NULL)
|| (BytesRead != sizeof(hScore))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "Child %lu: Unable to retrieve the scoreboard from the parent", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00388)
+ "Child: Unable to retrieve the scoreboard from the parent");
exit(APEXIT_CHILDINIT);
}
*scoreboard_shm = NULL;
if ((rv = apr_os_shm_put(scoreboard_shm, &hScore, s->process->pool))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
- "Child %lu: Unable to access the scoreboard from the parent", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00389)
+ "Child: Unable to access the scoreboard from the parent");
exit(APEXIT_CHILDINIT);
}
rv = ap_reopen_scoreboard(s->process->pool, scoreboard_shm, 1);
if (rv || !(sb_shared = apr_shm_baseaddr_get(*scoreboard_shm))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
- "Child %lu: Unable to reopen the scoreboard from the parent", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00390)
+ "Child: Unable to reopen the scoreboard from the parent");
exit(APEXIT_CHILDINIT);
}
/* We must 'initialize' the scoreboard to relink all the
@@ -395,8 +343,8 @@ static void get_handles_from_parent(server_rec *s, HANDLE *child_exit_event,
*/
ap_init_scoreboard(sb_shared);
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
- "Child %lu: Retrieved our scoreboard from the parent.", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00391)
+ "Child: Retrieved our scoreboard from the parent.");
}
@@ -417,64 +365,64 @@ static int send_handles_to_child(apr_pool_t *p,
if (!DuplicateHandle(hCurrentProcess, child_ready_event, hProcess, &hDup,
EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0)) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00392)
"Parent: Unable to duplicate the ready event handle for the child");
return -1;
}
if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00393)
"Parent: Unable to send the exit event handle to the child");
return -1;
}
if (!DuplicateHandle(hCurrentProcess, child_exit_event, hProcess, &hDup,
EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0)) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00394)
"Parent: Unable to duplicate the exit event handle for the child");
return -1;
}
if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00395)
"Parent: Unable to send the exit event handle to the child");
return -1;
}
if ((rv = apr_os_proc_mutex_get(&os_start, child_start_mutex)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00396)
"Parent: Unable to retrieve the start mutex for the child");
return -1;
}
if (!DuplicateHandle(hCurrentProcess, os_start, hProcess, &hDup,
SYNCHRONIZE, FALSE, 0)) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00397)
"Parent: Unable to duplicate the start mutex to the child");
return -1;
}
if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00398)
"Parent: Unable to send the start mutex to the child");
return -1;
}
if ((rv = apr_os_shm_get(&hScore, scoreboard_shm)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00399)
"Parent: Unable to retrieve the scoreboard handle for the child");
return -1;
}
if (!DuplicateHandle(hCurrentProcess, hScore, hProcess, &hDup,
FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0)) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00400)
"Parent: Unable to duplicate the scoreboard handle to the child");
return -1;
}
if ((rv = apr_file_write_full(child_in, &hDup, sizeof(hDup), &BytesWritten))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00401)
"Parent: Unable to send the scoreboard handle to the child");
return -1;
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00402)
"Parent: Sent the scoreboard to the child");
return 0;
}
@@ -510,49 +458,33 @@ static void get_listeners_from_parent(server_rec *s)
* pipe = GetStdHandle(STD_INPUT_HANDLE);
*/
for (lr = ap_listeners; lr; lr = lr->next, ++lcnt) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00403)
+ "Child: Waiting for data for listening socket %pI",
+ lr->bind_addr);
if (!ReadFile(pipe, &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO),
&BytesRead, (LPOVERLAPPED) NULL)) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "setup_inherited_listeners: Unable to read socket data from parent");
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00404)
+ "Child: Unable to read socket data from parent");
exit(APEXIT_CHILDINIT);
}
+
nsd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
&WSAProtocolInfo, 0, 0);
if (nsd == INVALID_SOCKET) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
- "Child %lu: setup_inherited_listeners(), WSASocket failed to open the inherited socket.", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf, APLOGNO(00405)
+ "Child: WSASocket failed to open the inherited socket");
exit(APEXIT_CHILDINIT);
}
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
- HANDLE hProcess = GetCurrentProcess();
- HANDLE dup;
- if (DuplicateHandle(hProcess, (HANDLE) nsd, hProcess, &dup,
- 0, FALSE, DUPLICATE_SAME_ACCESS)) {
- closesocket(nsd);
- nsd = (SOCKET) dup;
- }
- }
- else {
- /* A different approach. Many users report errors such as
- * (32538)An operation was attempted on something that is not
- * a socket. : Parent: WSADuplicateSocket failed...
- *
- * This appears that the duplicated handle is no longer recognized
- * as a socket handle. SetHandleInformation should overcome that
- * problem by not altering the handle identifier. But this won't
- * work on 9x - it's unsupported.
- */
- if (!SetHandleInformation((HANDLE)nsd, HANDLE_FLAG_INHERIT, 0)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf,
- "set_listeners_noninheritable: SetHandleInformation failed.");
- }
+ if (!SetHandleInformation((HANDLE)nsd, HANDLE_FLAG_INHERIT, 0)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), ap_server_conf, APLOGNO(00406)
+ "Child: SetHandleInformation failed");
}
apr_os_sock_put(&lr->sd, &nsd, s->process->pool);
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
- "Child %lu: retrieved %d listeners from parent", my_pid, lcnt);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00407)
+ "Child: retrieved %d listeners from parent", lcnt);
}
@@ -573,12 +505,12 @@ static int send_listeners_to_child(apr_pool_t *p, DWORD dwProcessId,
apr_os_sock_t nsd;
lpWSAProtocolInfo = apr_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
apr_os_sock_get(&nsd, lr->sd);
- ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
- "Parent: Duplicating socket %d and sending it to child process %lu",
- nsd, dwProcessId);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00408)
+ "Parent: Duplicating socket %d (%pI) and sending it to child process %lu",
+ nsd, lr->bind_addr, dwProcessId);
if (WSADuplicateSocket(nsd, dwProcessId,
lpWSAProtocolInfo) == SOCKET_ERROR) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_netos_error(), ap_server_conf, APLOGNO(00409)
"Parent: WSADuplicateSocket failed for socket %d. Check the FAQ.", nsd);
return -1;
}
@@ -586,13 +518,13 @@ static int send_listeners_to_child(apr_pool_t *p, DWORD dwProcessId,
if ((rv = apr_file_write_full(child_in, lpWSAProtocolInfo,
sizeof(WSAPROTOCOL_INFO), &BytesWritten))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00410)
"Parent: Unable to write duplicated socket %d to the child.", nsd);
return -1;
}
}
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00411)
"Parent: Sent %d listeners to child %lu", lcnt, dwProcessId);
return 0;
}
@@ -614,7 +546,6 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
apr_pool_t *ptemp;
apr_procattr_t *attr;
apr_proc_t new_child;
- apr_file_t *child_out, *child_err;
HANDLE hExitEvent;
HANDLE waitlist[2]; /* see waitlist_e */
char *cmd;
@@ -633,7 +564,7 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
apr_procattr_detach_set(attr, 1);
if (((rv = apr_filepath_get(&cwd, 0, ptemp)) != APR_SUCCESS)
|| ((rv = apr_procattr_dir_set(attr, cwd)) != APR_SUCCESS)) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00412)
"Parent: Failed to get the current path");
}
@@ -643,7 +574,7 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
*/
if ((rv = ap_os_proc_filepath(&cmd, ptemp))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, ERROR_BAD_PATHNAME, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, ERROR_BAD_PATHNAME, ap_server_conf, APLOGNO(00413)
"Parent: Failed to get full path of %s",
ap_server_conf->process->argv[0]);
apr_pool_destroy(ptemp);
@@ -664,33 +595,16 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
/* Create a pipe to send handles to the child */
if ((rv = apr_procattr_io_set(attr, APR_FULL_BLOCK,
APR_NO_PIPE, APR_NO_PIPE)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00414)
"Parent: Unable to create child stdin pipe.");
apr_pool_destroy(ptemp);
return -1;
}
- /* httpd-2.0/2.2 specific to work around apr_proc_create bugs */
- /* set "NUL" as sysout for the child */
- if (((rv = apr_file_open(&child_out, "NUL", APR_WRITE | APR_READ, APR_OS_DEFAULT,p))
- != APR_SUCCESS) ||
- ((rv = apr_procattr_child_out_set(attr, child_out, NULL))
- != APR_SUCCESS)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
- "Parent: Could not set child process stdout");
- }
- if (((rv = apr_file_open_stderr(&child_err, p))
- != APR_SUCCESS) ||
- ((rv = apr_procattr_child_err_set(attr, child_err, NULL))
- != APR_SUCCESS)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
- "Parent: Could not set child process stderr");
- }
-
/* Create the child_ready_event */
waitlist[waitlist_ready] = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!waitlist[waitlist_ready]) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00415)
"Parent: Could not create ready event for child process");
apr_pool_destroy (ptemp);
return -1;
@@ -699,7 +613,7 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
/* Create the child_exit_event */
hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hExitEvent) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00416)
"Parent: Could not create exit event for child process");
apr_pool_destroy(ptemp);
CloseHandle(waitlist[waitlist_ready]);
@@ -710,16 +624,16 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
for (envc = 0; _environ[envc]; ++envc) {
;
}
- env = apr_palloc(ptemp, (envc + 2) * sizeof (char*));
+ env = apr_palloc(ptemp, (envc + 2) * sizeof (char*));
memcpy(env, _environ, envc * sizeof (char*));
apr_snprintf(pidbuf, sizeof(pidbuf), "AP_PARENT_PID=%lu", parent_pid);
env[envc] = pidbuf;
env[envc + 1] = NULL;
- rv = apr_proc_create(&new_child, cmd, (const char * const *)args,
+ rv = apr_proc_create(&new_child, cmd, (const char * const *)args,
(const char * const *)env, attr, ptemp);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00417)
"Parent: Failed to create the child process.");
apr_pool_destroy(ptemp);
CloseHandle(hExitEvent);
@@ -727,8 +641,8 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
CloseHandle(new_child.hproc);
return -1;
}
- apr_file_close(child_out);
- ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
+
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00418)
"Parent: Created child process %d", new_child.pid);
if (send_handles_to_child(ptemp, waitlist[waitlist_ready], hExitEvent,
@@ -805,7 +719,7 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
* translated into an ap_signal_parent(SIGNAL_PARENT_RESTART)
* call by code in service.c.
* 3. The child process calling ap_signal_parent(SIGNAL_PARENT_RESTART)
- * as a result of hitting MaxRequestsPerChild.
+ * as a result of hitting MaxConnectionsPerChild.
*
* shutdown_event
* --------------
@@ -853,7 +767,7 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
&child_exit_event, &child_pid);
if (rv < 0)
{
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00419)
"master_main: create child process failed. Exiting.");
shutdown_pending = 1;
goto die_now;
@@ -869,7 +783,7 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
* child at once.
*/
ap_scoreboard_image->parent[0].quiescing = 0;
- ap_scoreboard_image->parent[0].pid = child_pid;
+ winnt_note_child_started(/* slot */ 0, child_pid);
/* Wait for shutdown or restart events or for child death */
winnt_mpm_state = AP_MPMQ_RUNNING;
@@ -877,23 +791,23 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
cld = rv - WAIT_OBJECT_0;
if (rv == WAIT_FAILED) {
/* Something serious is wrong */
- ap_log_error(APLOG_MARK,APLOG_CRIT, apr_get_os_error(), ap_server_conf,
- "master_main: WaitForMultipeObjects WAIT_FAILED -- doing server shutdown");
+ ap_log_error(APLOG_MARK,APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00420)
+ "master_main: WaitForMultipleObjects WAIT_FAILED -- doing server shutdown");
shutdown_pending = 1;
}
else if (rv == WAIT_TIMEOUT) {
/* Hey, this cannot happen */
- ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
- "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT");
+ ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s, APLOGNO(00421)
+ "master_main: WaitForMultipleObjects with INFINITE wait exited with WAIT_TIMEOUT");
shutdown_pending = 1;
}
else if (cld == SHUTDOWN_HANDLE) {
/* shutdown_event signalled */
shutdown_pending = 1;
- ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, s,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, s, APLOGNO(00422)
"Parent: Received shutdown signal -- Shutting down the server.");
if (ResetEvent(shutdown_event) == 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
+ ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s, APLOGNO(00423)
"ResetEvent(shutdown_event)");
}
}
@@ -902,14 +816,14 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
* then signal the child process to exit.
*/
restart_pending = 1;
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(00424)
"Parent: Received restart signal -- Restarting the server.");
if (ResetEvent(restart_event) == 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
+ ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s, APLOGNO(00425)
"Parent: ResetEvent(restart_event) failed.");
}
if (SetEvent(child_exit_event) == 0) {
- ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s,
+ ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_os_error(), s, APLOGNO(00426)
"Parent: SetEvent for child process %pp failed.",
event_handles[CHILD_HANDLE]);
}
@@ -929,14 +843,14 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
if ( exitcode == APEXIT_CHILDFATAL
|| exitcode == APEXIT_CHILDINIT
|| exitcode == APEXIT_INIT) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(00427)
"Parent: child process exited with status %lu -- Aborting.", exitcode);
shutdown_pending = 1;
}
else {
int i;
restart_pending = 1;
- ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00428)
"Parent: child process exited with status %lu -- Restarting.", exitcode);
for (i = 0; i < ap_threads_per_child; i++) {
ap_update_child_status_from_indexes(0, i, SERVER_DEAD, NULL);
@@ -945,9 +859,12 @@ static int master_main(server_rec *s, HANDLE shutdown_event, HANDLE restart_even
CloseHandle(event_handles[CHILD_HANDLE]);
event_handles[CHILD_HANDLE] = NULL;
}
+
+ winnt_note_child_killed(/* slot */ 0);
+
if (restart_pending) {
- ++ap_my_generation;
- ap_scoreboard_image->global->running_generation = ap_my_generation;
+ ++my_generation;
+ ap_scoreboard_image->global->running_generation = my_generation;
}
die_now:
if (shutdown_pending)
@@ -968,20 +885,20 @@ die_now:
}
/* Signal the child processes to exit */
if (SetEvent(child_exit_event) == 0) {
- ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_os_error(), ap_server_conf, APLOGNO(00429)
"Parent: SetEvent for child process %pp failed",
event_handles[CHILD_HANDLE]);
}
if (event_handles[CHILD_HANDLE]) {
rv = WaitForSingleObject(event_handles[CHILD_HANDLE], timeout);
if (rv == WAIT_OBJECT_0) {
- ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
+ ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00430)
"Parent: Child process exited successfully.");
CloseHandle(event_handles[CHILD_HANDLE]);
event_handles[CHILD_HANDLE] = NULL;
}
else {
- ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
+ ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf, APLOGNO(00431)
"Parent: Forcing termination of child process %pp",
event_handles[CHILD_HANDLE]);
TerminateProcess(event_handles[CHILD_HANDLE], 1);
@@ -1007,50 +924,62 @@ apr_array_header_t *mpm_new_argv;
* service after we preflight the config.
*/
-AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+static int winnt_query(int query_code, int *result, apr_status_t *rv)
{
- switch(query_code){
+ *rv = APR_SUCCESS;
+ switch (query_code) {
case AP_MPMQ_MAX_DAEMON_USED:
*result = MAXIMUM_WAIT_OBJECTS;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_STATIC;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_IS_FORKED:
*result = AP_MPMQ_NOT_SUPPORTED;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_DAEMONS:
*result = HARD_SERVER_LIMIT;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_THREADS:
*result = thread_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_THREADS:
*result = ap_threads_per_child;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MIN_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MIN_SPARE_THREADS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_THREADS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_REQUESTS_DAEMON:
*result = ap_max_requests_per_child;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_DAEMONS:
- *result = 0;
- return APR_SUCCESS;
+ *result = 1;
+ break;
case AP_MPMQ_MPM_STATE:
*result = winnt_mpm_state;
- return APR_SUCCESS;
+ break;
+ case AP_MPMQ_GENERATION:
+ *result = my_generation;
+ break;
+ default:
+ *rv = APR_ENOTIMPL;
+ break;
}
- return APR_ENOTIMPL;
+ return OK;
+}
+
+static const char *winnt_get_name(void)
+{
+ return "WinNT";
}
#define SERVICE_UNSET (-1)
@@ -1064,7 +993,7 @@ static void winnt_rewrite_args(process_rec *process)
{
/* Handle the following SCM aspects in this phase:
*
- * -k runservice [transition for WinNT, nothing for Win9x]
+ * -k runservice [transition in service context only]
* -k install
* -k config
* -k uninstall
@@ -1081,18 +1010,28 @@ static void winnt_rewrite_args(process_rec *process)
char *def_server_root;
char *binpath;
char optbuf[3];
- const char *optarg;
+ const char *opt_arg;
int fixed_args;
char *pid;
apr_getopt_t *opt;
int running_as_service = 1;
int errout = 0;
+ apr_file_t *nullfile;
pconf = process->pconf;
osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osver);
+ /* We wish this was *always* a reservation, but sadly it wasn't so and
+ * we couldn't break a hard limit prior to NT Kernel 5.1
+ */
+ if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && ((osver.dwMajorVersion > 5)
+ || ((osver.dwMajorVersion == 5) && (osver.dwMinorVersion > 0)))) {
+ stack_res_flag = STACK_SIZE_PARAM_IS_A_RESERVATION;
+ }
+
/* AP_PARENT_PID is only valid in the child */
pid = getenv("AP_PARENT_PID");
if (pid)
@@ -1133,9 +1072,10 @@ static void winnt_rewrite_args(process_rec *process)
}
/* This child needs the existing stderr opened for logging,
- * already
+ * already
*/
+
/* The parent is responsible for providing the
* COMPLETE ARGUMENTS REQUIRED to the child.
*
@@ -1168,7 +1108,7 @@ static void winnt_rewrite_args(process_rec *process)
*/
if ((rv = ap_os_proc_filepath(&binpath, process->pconf))
!= APR_SUCCESS) {
- ap_log_error(APLOG_MARK,APLOG_CRIT, rv, NULL,
+ ap_log_error(APLOG_MARK,APLOG_CRIT, rv, NULL, APLOGNO(00432)
"Failed to get the full path of %s", process->argv[0]);
exit(APEXIT_INIT);
}
@@ -1203,7 +1143,7 @@ static void winnt_rewrite_args(process_rec *process)
apr_getopt_init(&opt, process->pool, process->argc, process->argv);
opt->errfn = NULL;
while ((rv = apr_getopt(opt, "wn:k:" AP_SERVER_BASEARGS,
- optbuf + 1, &optarg)) == APR_SUCCESS) {
+ optbuf + 1, &opt_arg)) == APR_SUCCESS) {
switch (optbuf[1]) {
/* Shortcuts; include the -w option to hold the window open on error.
@@ -1216,11 +1156,11 @@ static void winnt_rewrite_args(process_rec *process)
case 'n':
service_set = mpm_service_set_name(process->pool, &service_name,
- optarg);
+ opt_arg);
break;
case 'k':
- signal_arg = optarg;
+ signal_arg = opt_arg;
break;
case 'E':
@@ -1230,8 +1170,8 @@ static void winnt_rewrite_args(process_rec *process)
*(const char **)apr_array_push(mpm_new_argv) =
apr_pstrdup(process->pool, optbuf);
- if (optarg) {
- *(const char **)apr_array_push(mpm_new_argv) = optarg;
+ if (opt_arg) {
+ *(const char **)apr_array_push(mpm_new_argv) = opt_arg;
}
break;
}
@@ -1280,33 +1220,28 @@ static void winnt_rewrite_args(process_rec *process)
* We hold the return value so that we can die in pre_config
* after logging begins, and the failure can land in the log.
*/
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- apr_file_t *nullfile;
-
- if (!errout) {
- mpm_nt_eventlog_stderr_open(service_name, process->pool);
- }
- service_to_start_success = mpm_service_to_start(&service_name,
- process->pool);
- if (service_to_start_success == APR_SUCCESS) {
- service_set = APR_SUCCESS;
- }
+ if (!errout) {
+ mpm_nt_eventlog_stderr_open(service_name, process->pool);
+ }
+ service_to_start_success = mpm_service_to_start(&service_name,
+ process->pool);
+ if (service_to_start_success == APR_SUCCESS) {
+ service_set = APR_SUCCESS;
+ }
- /* Open a null handle to soak stdout in this process.
- * Windows service processes are missing any file handle
- * usable for stdin/out/err. This was the cause of later
- * trouble with invocations of apr_file_open_stdout()
- */
- if ((rv = apr_file_open(&nullfile, "NUL",
- APR_READ | APR_WRITE, APR_OS_DEFAULT,
- process->pool)) == APR_SUCCESS) {
- apr_file_t *nullstdout;
- if (apr_file_open_stdout(&nullstdout, process->pool)
- == APR_SUCCESS)
- apr_file_dup2(nullstdout, nullfile, process->pool);
- apr_file_close(nullfile);
- }
+ /* Open a null handle to soak stdout in this process.
+ * Windows service processes are missing any file handle
+ * usable for stdin/out/err. This was the cause of later
+ * trouble with invocations of apr_file_open_stdout()
+ */
+ if ((rv = apr_file_open(&nullfile, "NUL",
+ APR_READ | APR_WRITE, APR_OS_DEFAULT,
+ process->pool)) == APR_SUCCESS) {
+ apr_file_t *nullstdout;
+ if (apr_file_open_stdout(&nullstdout, process->pool)
+ == APR_SUCCESS)
+ apr_file_dup2(nullstdout, nullfile, process->pool);
+ apr_file_close(nullfile);
}
}
@@ -1320,7 +1255,7 @@ static void winnt_rewrite_args(process_rec *process)
{
if (service_set == APR_SUCCESS)
{
- ap_log_error(APLOG_MARK,APLOG_ERR, 0, NULL,
+ ap_log_error(APLOG_MARK,APLOG_ERR, 0, NULL, APLOGNO(00433)
"%s: Service is already installed.", service_name);
exit(APEXIT_INIT);
}
@@ -1346,26 +1281,26 @@ static void winnt_rewrite_args(process_rec *process)
rv = mpm_merge_service_args(process->pool, mpm_new_argv,
fixed_args);
if (rv == APR_SUCCESS) {
- ap_log_error(APLOG_MARK,APLOG_INFO, 0, NULL,
+ ap_log_error(APLOG_MARK,APLOG_INFO, 0, NULL, APLOGNO(00434)
"Using ConfigArgs of the installed service "
"\"%s\".", service_name);
}
else {
- ap_log_error(APLOG_MARK,APLOG_WARNING, rv, NULL,
+ ap_log_error(APLOG_MARK,APLOG_WARNING, rv, NULL, APLOGNO(00435)
"No installed ConfigArgs for the service "
"\"%s\", using Apache defaults.", service_name);
}
}
else
{
- ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL,
+ ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL, APLOGNO(00436)
"No installed service named \"%s\".", service_name);
exit(APEXIT_INIT);
}
}
if (strcasecmp(signal_arg, "install") && service_set && service_set != SERVICE_UNSET)
{
- ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL,
+ ap_log_error(APLOG_MARK,APLOG_ERR, service_set, NULL, APLOGNO(00437)
"No installed service named \"%s\".", service_name);
exit(APEXIT_INIT);
}
@@ -1424,34 +1359,137 @@ static int winnt_pre_config(apr_pool_t *pconf_, apr_pool_t *plog, apr_pool_t *pt
ap_exists_config_define("DEBUG"))
one_process = -1;
+ /* XXX: presume proper privilages; one nice thing would be
+ * a loud emit if running as "LocalSystem"/"SYSTEM" to indicate
+ * they should change to a user with write access to logs/ alone.
+ */
+ ap_sys_privileges_handlers(1);
+
if (!strcasecmp(signal_arg, "runservice")
- && (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
&& (service_to_start_success != APR_SUCCESS)) {
- ap_log_error(APLOG_MARK,APLOG_CRIT, service_to_start_success, NULL,
+ ap_log_error(APLOG_MARK,APLOG_CRIT, service_to_start_success, NULL, APLOGNO(00438)
"%s: Unable to start the service manager.",
service_name);
exit(APEXIT_INIT);
}
-
- /* Win9x: disable AcceptEx */
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
- use_acceptex = 0;
+ else if (!one_process && !my_generation) {
+ /* Open a null handle to soak stdout in this process.
+ * We need to emulate apr_proc_detach, unix performs this
+ * same check in the pre_config hook (although it is
+ * arguably premature). Services already fixed this.
+ */
+ apr_file_t *nullfile;
+ apr_status_t rv;
+ apr_pool_t *pproc = apr_pool_parent_get(pconf);
+
+ if ((rv = apr_file_open(&nullfile, "NUL",
+ APR_READ | APR_WRITE, APR_OS_DEFAULT,
+ pproc)) == APR_SUCCESS) {
+ apr_file_t *nullstdout;
+ if (apr_file_open_stdout(&nullstdout, pproc)
+ == APR_SUCCESS)
+ apr_file_dup2(nullstdout, nullfile, pproc);
+ apr_file_close(nullfile);
+ }
}
ap_listen_pre_config();
+ thread_limit = DEFAULT_THREAD_LIMIT;
ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
- ap_pid_fname = DEFAULT_PIDLOG;
- ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
-#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
- ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-#endif
- /* use_acceptex which is enabled by default is not available on Win9x.
+
+ return OK;
+}
+
+static int winnt_check_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec* s)
+{
+ int is_parent;
+ static int restart_num = 0;
+ int startup = 0;
+
+ /* We want this only in the parent and only the first time around */
+ is_parent = (parent_pid == my_pid);
+ if (is_parent && restart_num++ == 0) {
+ startup = 1;
+ }
+
+ if (thread_limit > MAX_THREAD_LIMIT) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00439)
+ "WARNING: ThreadLimit of %d exceeds compile-time "
+ "limit of", thread_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d threads, decreasing to %d.",
+ MAX_THREAD_LIMIT, MAX_THREAD_LIMIT);
+ } else if (is_parent) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00440)
+ "ThreadLimit of %d exceeds compile-time limit "
+ "of %d, decreasing to match",
+ thread_limit, MAX_THREAD_LIMIT);
+ }
+ thread_limit = MAX_THREAD_LIMIT;
+ }
+ else if (thread_limit < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00441)
+ "WARNING: ThreadLimit of %d not allowed, "
+ "increasing to 1.", thread_limit);
+ } else if (is_parent) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00442)
+ "ThreadLimit of %d not allowed, increasing to 1",
+ thread_limit);
+ }
+ thread_limit = 1;
+ }
+
+ /* You cannot change ThreadLimit across a restart; ignore
+ * any such attempts.
*/
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
- use_acceptex = 0;
+ if (!first_thread_limit) {
+ first_thread_limit = thread_limit;
+ }
+ else if (thread_limit != first_thread_limit) {
+ /* Don't need a startup console version here */
+ if (is_parent) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00443)
+ "changing ThreadLimit to %d from original value "
+ "of %d not allowed during restart",
+ thread_limit, first_thread_limit);
+ }
+ thread_limit = first_thread_limit;
}
- apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+ if (ap_threads_per_child > thread_limit) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00444)
+ "WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
+ "of", ap_threads_per_child);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d threads, decreasing to %d.",
+ thread_limit, thread_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " To increase, please see the ThreadLimit "
+ "directive.");
+ } else if (is_parent) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00445)
+ "ThreadsPerChild of %d exceeds ThreadLimit "
+ "of %d, decreasing to match",
+ ap_threads_per_child, thread_limit);
+ }
+ ap_threads_per_child = thread_limit;
+ }
+ else if (ap_threads_per_child < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00446)
+ "WARNING: ThreadsPerChild of %d not allowed, "
+ "increasing to 1.", ap_threads_per_child);
+ } else if (is_parent) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00447)
+ "ThreadsPerChild of %d not allowed, increasing to 1",
+ ap_threads_per_child);
+ }
+ ap_threads_per_child = 1;
+ }
return OK;
}
@@ -1524,7 +1562,7 @@ static int winnt_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *pt
* across a restart
*/
PSECURITY_ATTRIBUTES sa = GetNullACL(); /* returns NULL if invalid (Win95?) */
- setup_signal_names(apr_psprintf(pconf,"ap%lu", parent_pid));
+ setup_signal_names(apr_psprintf(pconf, "ap%lu", parent_pid));
ap_log_pid(pconf, ap_pid_fname);
@@ -1533,7 +1571,7 @@ static int winnt_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *pt
*/
shutdown_event = CreateEvent(sa, FALSE, FALSE, signal_shutdown_name);
if (!shutdown_event) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00448)
"Parent: Cannot create shutdown event %s", signal_shutdown_name);
CleanNullACL((void *)sa);
return HTTP_INTERNAL_SERVER_ERROR;
@@ -1545,33 +1583,13 @@ static int winnt_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *pt
restart_event = CreateEvent(sa, FALSE, FALSE, signal_restart_name);
if (!restart_event) {
CloseHandle(shutdown_event);
- ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00449)
"Parent: Cannot create restart event %s", signal_restart_name);
CleanNullACL((void *)sa);
return HTTP_INTERNAL_SERVER_ERROR;
}
CleanNullACL((void *)sa);
- /* Now that we are flying at 15000 feet...
- * wipe out the Win95 service console,
- * signal the SCM the WinNT service started, or
- * if not a service, setup console handlers instead.
- */
- if (!strcasecmp(signal_arg, "runservice"))
- {
- if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
- {
- rv = mpm_service_to_start(&service_name,
- s->process->pool);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
- "%s: Unable to start the service manager.",
- service_name);
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- }
- }
-
/* Create the start mutex, as an unnamed object for security.
* Ths start mutex is used during a restart to prevent more than
* one child process from entering the accept loop at once.
@@ -1580,14 +1598,14 @@ static int winnt_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *pt
APR_LOCK_DEFAULT,
ap_server_conf->process->pool);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf, APLOGNO(00450)
"%s: Unable to create the start_mutex.",
service_name);
return HTTP_INTERNAL_SERVER_ERROR;
}
}
/* Always reset our console handler to be the first, even on a restart
- * because some modules (e.g. mod_perl) might have set a console
+ * because some modules (e.g. mod_perl) might have set a console
* handler to terminate the process.
*/
if (strcasecmp(signal_arg, "runservice"))
@@ -1607,8 +1625,6 @@ static int winnt_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, s
{
/* Initialize shared static objects.
*/
- ap_server_conf = s;
-
if (parent_pid != my_pid) {
return OK;
}
@@ -1625,7 +1641,7 @@ static int winnt_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, s
if (ap_setup_listeners(s) < 1) {
ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0,
- NULL, "no listening sockets available, shutting down");
+ NULL, APLOGNO(00451) "no listening sockets available, shutting down");
return DONE;
}
@@ -1636,7 +1652,7 @@ static void winnt_child_init(apr_pool_t *pchild, struct server_rec *s)
{
apr_status_t rv;
- setup_signal_names(apr_psprintf(pchild,"ap%lu", parent_pid));
+ setup_signal_names(apr_psprintf(pchild, "ap%lu", parent_pid));
/* This is a child process, not in single process mode */
if (!one_process) {
@@ -1650,16 +1666,16 @@ static void winnt_child_init(apr_pool_t *pchild, struct server_rec *s)
/* Done reading from the parent, close that channel */
CloseHandle(pipe);
- ap_my_generation = ap_scoreboard_image->global->running_generation;
+ my_generation = ap_scoreboard_image->global->running_generation;
}
else {
/* Single process mode - this lock doesn't even need to exist */
rv = apr_proc_mutex_create(&start_mutex, signal_name_prefix,
APR_LOCK_DEFAULT, s->process->pool);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
- "%s child %lu: Unable to init the start_mutex.",
- service_name, my_pid);
+ ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf, APLOGNO(00452)
+ "%s child: Unable to init the start_mutex.",
+ service_name);
exit(APEXIT_CHILDINIT);
}
@@ -1669,21 +1685,10 @@ static void winnt_child_init(apr_pool_t *pchild, struct server_rec *s)
}
-AP_DECLARE(int) ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
+static int winnt_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
{
static int restart = 0; /* Default is "not a restart" */
- if (!restart) {
- first_thread_limit = thread_limit;
- }
-
- if (changed_limit_at_restart) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, APR_SUCCESS, ap_server_conf,
- "WARNING: Attempt to change ThreadLimit ignored "
- "during restart");
- changed_limit_at_restart = 0;
- }
-
/* ### If non-graceful restarts are ever introduced - we need to rerun
* the pre_mpm hook on subsequent non-graceful restarts. But Win32
* has only graceful style restarts - and we need this hook to act
@@ -1692,7 +1697,7 @@ AP_DECLARE(int) ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
if (!restart && ((parent_pid == my_pid) || one_process)) {
/* Set up the scoreboard. */
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
- return 1;
+ return DONE;
}
}
@@ -1700,63 +1705,62 @@ AP_DECLARE(int) ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s )
{
/* The child process or in one_process (debug) mode
*/
- ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
- "Child %lu: Child process is running", my_pid);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00453)
+ "Child process is running");
child_main(pconf);
- ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
- "Child %lu: Child process is exiting", my_pid);
- return 1;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00454)
+ "Child process is exiting");
+ return DONE;
}
else
{
/* A real-honest to goodness parent */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00455)
"%s configured -- resuming normal operations",
ap_get_server_description());
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00456)
"Server built: %s", ap_get_server_built());
+ ap_log_command_line(plog, s);
restart = master_main(ap_server_conf, shutdown_event, restart_event);
if (!restart)
{
/* Shutting down. Clean up... */
- const char *pidfile = ap_server_root_relative (pconf, ap_pid_fname);
-
- if (pidfile != NULL && unlink(pidfile) == 0) {
- ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS,
- ap_server_conf, "removed PID file %s (pid=%ld)",
- pidfile, GetCurrentProcessId());
- }
+ ap_remove_pid(pconf, ap_pid_fname);
apr_proc_mutex_destroy(start_mutex);
CloseHandle(restart_event);
CloseHandle(shutdown_event);
- return 1;
+ return DONE;
}
}
- return 0; /* Restart */
+ return OK; /* Restart */
}
static void winnt_hooks(apr_pool_t *p)
{
- /* The prefork open_logs phase must run before the core's, or stderr
+ /* Our open_logs hook function must run before the core's, or stderr
* will be redirected to a file, and the messages won't print to the
* console.
*/
static const char *const aszSucc[] = {"core.c", NULL};
ap_hook_pre_config(winnt_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_check_config(winnt_check_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(winnt_post_config, NULL, NULL, 0);
ap_hook_child_init(winnt_child_init, NULL, NULL, APR_HOOK_MIDDLE);
- ap_hook_open_logs(winnt_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
+ ap_hook_open_logs(winnt_open_logs, NULL, aszSucc, APR_HOOK_REALLY_FIRST);
+ ap_hook_mpm(winnt_run, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_query(winnt_query, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_get_name(winnt_get_name, NULL, NULL, APR_HOOK_MIDDLE);
}
-AP_MODULE_DECLARE_DATA module mpm_winnt_module = {
+AP_DECLARE_MODULE(mpm_winnt) = {
MPM20_MODULE_STUFF,
winnt_rewrite_args, /* hook to run before apache parses args */
NULL, /* create per-directory config structure */
diff --git a/server/mpm/winnt/mpm_winnt.h b/server/mpm/winnt/mpm_winnt.h
index e21a6f43..62340d22 100644
--- a/server/mpm/winnt/mpm_winnt.h
+++ b/server/mpm/winnt/mpm_winnt.h
@@ -16,7 +16,7 @@
/**
* @file mpm_winnt.h
- * @brief WinNT MPM specific
+ * @brief WinNT MPM specific
*
* @addtogroup APACHE_MPM_WINNT
* @{
@@ -25,6 +25,7 @@
#ifndef APACHE_MPM_WINNT_H
#define APACHE_MPM_WINNT_H
+#include "apr_proc_mutex.h"
#include "ap_listen.h"
/* From service.c: */
@@ -32,25 +33,24 @@
#define SERVICE_APACHE_RESTART 128
#ifndef AP_DEFAULT_SERVICE_NAME
-#define AP_DEFAULT_SERVICE_NAME "Apache2.2"
+#define AP_DEFAULT_SERVICE_NAME "Apache2.4"
#endif
-#define SERVICECONFIG9X "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"
#define SERVICECONFIG "System\\CurrentControlSet\\Services\\%s"
#define SERVICEPARAMS "System\\CurrentControlSet\\Services\\%s\\Parameters"
-apr_status_t mpm_service_set_name(apr_pool_t *p, const char **display_name,
+apr_status_t mpm_service_set_name(apr_pool_t *p, const char **display_name,
const char *set_name);
-apr_status_t mpm_merge_service_args(apr_pool_t *p, apr_array_header_t *args,
+apr_status_t mpm_merge_service_args(apr_pool_t *p, apr_array_header_t *args,
int fixed_args);
apr_status_t mpm_service_to_start(const char **display_name, apr_pool_t *p);
apr_status_t mpm_service_started(void);
-apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc,
+apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc,
char const* const* argv, int reconfig);
apr_status_t mpm_service_uninstall(void);
-apr_status_t mpm_service_start(apr_pool_t *ptemp, int argc,
+apr_status_t mpm_service_start(apr_pool_t *ptemp, int argc,
char const* const* argv);
void mpm_signal_service(apr_pool_t *ptemp, int signal);
@@ -65,11 +65,18 @@ void mpm_start_child_console_handler(void);
void mpm_nt_eventlog_stderr_open(const char *display_name, apr_pool_t *p);
void mpm_nt_eventlog_stderr_flush(void);
-/* From winnt.c: */
+/* From mpm_winnt.c: */
+
+extern int ap_threads_per_child;
+
+extern DWORD my_pid;
+extern apr_proc_mutex_t *start_mutex;
+extern HANDLE exit_event;
-extern int use_acceptex;
extern int winnt_mpm_state;
extern OSVERSIONINFO osver;
+extern DWORD stack_res_flag;
+
extern void clean_child_exit(int);
void setup_signal_names(char *prefix);
@@ -81,50 +88,6 @@ typedef enum {
} ap_signal_parent_e;
AP_DECLARE(void) ap_signal_parent(ap_signal_parent_e type);
-/*
- * The Windoes MPM uses a queue of completion contexts that it passes
- * between the accept threads and the worker threads. Declare the
- * functions to access the queue and the structures passed on the
- * queue in the header file to enable modules to access them
- * if necessary. The queue resides in the MPM.
- */
-#ifdef CONTAINING_RECORD
-#undef CONTAINING_RECORD
-#endif
-#define CONTAINING_RECORD(address, type, field) ((type *)( \
- (PCHAR)(address) - \
- (PCHAR)(&((type *)0)->field)))
-#if APR_HAVE_IPV6
-#define PADDED_ADDR_SIZE (sizeof(SOCKADDR_IN6)+16)
-#else
-#define PADDED_ADDR_SIZE (sizeof(SOCKADDR_IN)+16)
-#endif
-
-typedef struct CompContext {
- struct CompContext *next;
- OVERLAPPED Overlapped;
- apr_socket_t *sock;
- SOCKET accept_socket;
- char buff[2*PADDED_ADDR_SIZE];
- struct sockaddr *sa_server;
- int sa_server_len;
- struct sockaddr *sa_client;
- int sa_client_len;
- apr_pool_t *ptrans;
- apr_bucket_alloc_t *ba;
- short socket_family;
-} COMP_CONTEXT, *PCOMP_CONTEXT;
-
-typedef enum {
- IOCP_CONNECTION_ACCEPTED = 1,
- IOCP_WAIT_FOR_RECEIVE = 2,
- IOCP_WAIT_FOR_TRANSMITFILE = 3,
- IOCP_SHUTDOWN = 4
-} io_state_e;
-
-PCOMP_CONTEXT mpm_get_completion_context(void);
-void mpm_recycle_completion_context(PCOMP_CONTEXT pCompContext);
-apr_status_t mpm_post_completion_context(PCOMP_CONTEXT pCompContext, io_state_e state);
void hold_console_open_on_error(void);
/* From child.c: */
diff --git a/server/mpm/winnt/nt_eventlog.c b/server/mpm/winnt/nt_eventlog.c
index d8d758bb..e7e80d0d 100644
--- a/server/mpm/winnt/nt_eventlog.c
+++ b/server/mpm/winnt/nt_eventlog.c
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define CORE_PRIVATE
-
#include "httpd.h"
#include "http_log.h"
#include "mpm_winnt.h"
@@ -153,13 +151,13 @@ void mpm_nt_eventlog_stderr_open(const char *argv0, apr_pool_t *p)
ap_assert(hPipeRead && hPipeWrite);
stderr_ready = CreateEvent(NULL, FALSE, FALSE, NULL);
- stderr_thread = CreateThread(NULL, 0, service_stderr_thread,
- (LPVOID) hPipeRead, 0, &threadid);
+ stderr_thread = CreateThread(NULL, 65536, service_stderr_thread,
+ (LPVOID)hPipeRead, stack_res_flag, &threadid);
ap_assert(stderr_ready && stderr_thread);
WaitForSingleObject(stderr_ready, INFINITE);
- if ((apr_file_open_stderr(&stderr_file, p)
+ if ((apr_file_open_stderr(&stderr_file, p)
== APR_SUCCESS)
&& (apr_os_file_put(&eventlog_file, &hPipeWrite, APR_WRITE, p)
== APR_SUCCESS))
diff --git a/server/mpm/winnt/service.c b/server/mpm/winnt/service.c
index aa5291d2..bf5f021a 100644
--- a/server/mpm/winnt/service.c
+++ b/server/mpm/winnt/service.c
@@ -19,7 +19,6 @@
* preload the API symbols now...
*/
-#define CORE_PRIVATE
#define _WINUSER_
#include "httpd.h"
@@ -36,10 +35,13 @@
#include <winuser.h>
#include <time.h>
+APLOG_USE_MODULE(mpm_winnt);
+
+/* Todo; clear up statics */
static char *mpm_service_name = NULL;
static char *mpm_display_name = NULL;
-static struct
+typedef struct nt_service_ctx_t
{
HANDLE mpm_thread; /* primary thread handle of the apache server */
HANDLE service_thread; /* thread service/monitor handle */
@@ -48,24 +50,12 @@ static struct
HANDLE service_term; /* NT service thread kill signal */
SERVICE_STATUS ssStatus;
SERVICE_STATUS_HANDLE hServiceStatus;
-} globdat;
+} nt_service_ctx_t;
-static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint);
-
-
-/* The service configuration's is stored under the following trees:
- *
- * HKLM\System\CurrentControlSet\Services\[service name]
- *
- * \DisplayName
- * \ImagePath
- * \Parameters\ConfigArgs
- *
- * For Win9x, the launch service command is stored under:
- *
- * HKLM\Software\Microsoft\Windows\CurrentVersion\RunServices\[service name]
- */
+static nt_service_ctx_t globdat;
+static int ReportStatusToSCMgr(int currentState, int waitHint,
+ nt_service_ctx_t *ctx);
/* exit() for Win32 is macro mapped (horrible, we agree) that allows us
* to catch the non-zero conditions and inform the console process that
@@ -133,103 +123,6 @@ void hold_console_open_on_error(void)
while ((remains > 0) && WaitForSingleObject(hConIn, 1000) != WAIT_FAILED);
}
-static BOOL die_on_logoff = FALSE;
-
-static LRESULT CALLBACK monitor_service_9x_proc(HWND hWnd, UINT msg,
- WPARAM wParam, LPARAM lParam)
-{
-/* This is the WndProc procedure for our invisible window.
- * When the user shuts down the system, this window is sent
- * a signal WM_ENDSESSION. We clean up by signaling Apache
- * to shut down, and idle until Apache's primary thread quits.
- */
- if ((msg == WM_ENDSESSION)
- && (die_on_logoff || (lParam != ENDSESSION_LOGOFF)))
- {
- ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
- if (wParam)
- /* Don't leave this message until we are dead! */
- WaitForSingleObject(globdat.mpm_thread, 30000);
- return 0;
- }
- return (DefWindowProc(hWnd, msg, wParam, lParam));
-}
-
-static DWORD WINAPI monitor_service_9x_thread(void *service_name)
-{
- /* When running as a service under Windows 9x, there is no console
- * window present, and no ConsoleCtrlHandler to call when the system
- * is shutdown. If the WatchWindow thread is created with a NULL
- * service_name argument, then the ...SystemMonitor window class is
- * used to create the "Apache" window to watch for logoff and shutdown.
- * If the service_name is provided, the ...ServiceMonitor window class
- * is used to create the window named by the service_name argument,
- * and the logoff message is ignored.
- */
- WNDCLASS wc;
- HWND hwndMain;
- MSG msg;
-
- wc.style = CS_GLOBALCLASS;
- wc.lpfnWndProc = monitor_service_9x_proc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- wc.hInstance = NULL;
- wc.hIcon = NULL;
- wc.hCursor = NULL;
- wc.hbrBackground = NULL;
- wc.lpszMenuName = NULL;
- if (service_name)
- wc.lpszClassName = "ApacheWin95ServiceMonitor";
- else
- wc.lpszClassName = "ApacheWin95SystemMonitor";
-
- die_on_logoff = service_name ? FALSE : TRUE;
-
- if (!RegisterClass(&wc))
- {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(),
- NULL, "Could not register window class for WatchWindow");
- globdat.service_thread_id = 0;
- return 0;
- }
-
- /* Create an invisible window */
- hwndMain = CreateWindow(wc.lpszClassName,
- service_name ? (char *) service_name : "Apache",
- WS_OVERLAPPEDWINDOW & ~WS_VISIBLE,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- CW_USEDEFAULT, NULL, NULL, NULL, NULL);
-
- if (!hwndMain)
- {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(),
- NULL, "Could not create WatchWindow");
- globdat.service_thread_id = 0;
- return 0;
- }
-
- /* If we succeed, eliminate the console window.
- * Signal the parent we are all set up, and
- * watch the message queue while the window lives.
- */
- FreeConsole();
- SetEvent(globdat.service_init);
-
- while (GetMessage(&msg, NULL, 0, 0))
- {
- if (msg.message == WM_CLOSE)
- DestroyWindow(hwndMain);
- else {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- globdat.service_thread_id = 0;
- return 0;
-}
-
-
static BOOL CALLBACK console_control_handler(DWORD ctrl_type)
{
switch (ctrl_type)
@@ -254,7 +147,6 @@ static BOOL CALLBACK console_control_handler(DWORD ctrl_type)
* Wait for Apache to terminate, but respond
* after a reasonable time to tell the system
* that we did attempt to shut ourself down.
- * THESE EVENTS WILL NOT OCCUR UNDER WIN9x!
*/
fprintf(stderr, "Apache server shutdown initiated...\n");
ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
@@ -280,55 +172,9 @@ void mpm_start_console_handler(void)
}
-/* Special situation - children of services need to mind their
- * P's & Q's and wait quietly, ignoring the mean OS signaling
- * shutdown and other horrors, to kill them gracefully...
- */
-
-static BOOL CALLBACK child_control_handler(DWORD ctrl_type)
-{
- switch (ctrl_type)
- {
- case CTRL_C_EVENT:
- case CTRL_BREAK_EVENT:
- /* for Interrupt signals, ignore them.
- * The system will also signal the parent process,
- * which will terminate Apache.
- */
- return TRUE;
-
- case CTRL_CLOSE_EVENT:
- case CTRL_LOGOFF_EVENT:
- case CTRL_SHUTDOWN_EVENT:
- /* for Shutdown signals, ignore them, but... .
- * The system will also signal the parent process,
- * which will terminate Apache, so we need to wait.
- */
- Sleep(30000);
- return TRUE;
- }
-
- /* We should never get here, but this is (mostly) harmless */
- return FALSE;
-}
-
-
-static void stop_child_console_handler(void)
-{
- SetConsoleCtrlHandler(child_control_handler, FALSE);
-}
-
-
void mpm_start_child_console_handler(void)
{
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
- FreeConsole();
- }
- else
- {
- SetConsoleCtrlHandler(child_control_handler, TRUE);
- atexit(stop_child_console_handler);
- }
+ FreeConsole();
}
@@ -336,59 +182,51 @@ void mpm_start_child_console_handler(void)
WinNT service control management
**********************************/
-static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint)
+static int ReportStatusToSCMgr(int currentState, int waitHint,
+ nt_service_ctx_t *ctx)
{
- static int checkPoint = 1;
int rv = APR_SUCCESS;
- if (globdat.hServiceStatus)
+ if (ctx->hServiceStatus)
{
if (currentState == SERVICE_RUNNING) {
- globdat.ssStatus.dwWaitHint = 0;
- globdat.ssStatus.dwCheckPoint = 0;
- globdat.ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+ ctx->ssStatus.dwWaitHint = 0;
+ ctx->ssStatus.dwCheckPoint = 0;
+ ctx->ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
+ | SERVICE_ACCEPT_SHUTDOWN;
}
else if (currentState == SERVICE_STOPPED) {
- globdat.ssStatus.dwWaitHint = 0;
- globdat.ssStatus.dwCheckPoint = 0;
- if (!exitCode && globdat.ssStatus.dwCurrentState
- != SERVICE_STOP_PENDING) {
- /* An unexpected exit? Better to error! */
- exitCode = 1;
- }
- if (exitCode) {
- globdat.ssStatus.dwWin32ExitCode =ERROR_SERVICE_SPECIFIC_ERROR;
- globdat.ssStatus.dwServiceSpecificExitCode = exitCode;
- }
+ ctx->ssStatus.dwWaitHint = 0;
+ ctx->ssStatus.dwCheckPoint = 0;
+ /* An unexpected exit? Better to error! */
+ if (ctx->ssStatus.dwCurrentState != SERVICE_STOP_PENDING
+ && !ctx->ssStatus.dwServiceSpecificExitCode)
+ ctx->ssStatus.dwServiceSpecificExitCode = 1;
+ if (ctx->ssStatus.dwServiceSpecificExitCode)
+ ctx->ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
}
else {
- globdat.ssStatus.dwCheckPoint = ++checkPoint;
- globdat.ssStatus.dwControlsAccepted = 0;
+ ++ctx->ssStatus.dwCheckPoint;
+ ctx->ssStatus.dwControlsAccepted = 0;
if(waitHint)
- globdat.ssStatus.dwWaitHint = waitHint;
+ ctx->ssStatus.dwWaitHint = waitHint;
}
- globdat.ssStatus.dwCurrentState = currentState;
+ ctx->ssStatus.dwCurrentState = currentState;
- rv = SetServiceStatus(globdat.hServiceStatus, &globdat.ssStatus);
+ rv = SetServiceStatus(ctx->hServiceStatus, &ctx->ssStatus);
}
return(rv);
}
-/* Set the service description regardless of platform.
- * We revert to set_service_description on NT/9x, the
- * very long way so any Apache management program can grab the
- * description. This would be bad on Win2000, since it wouldn't
- * notify the service control manager of the name change.
+/* Note this works on Win2000 and later due to ChangeServiceConfig2
+ * Continue to test its existence, but at least drop the feature
+ * of revising service description tags prior to Win2000.
*/
/* borrowed from mpm_winnt.c */
extern apr_pool_t *pconf;
-/* Windows 2000 alone supports ChangeServiceConfig2 in order to
- * register our server_version string... so we need some fixups
- * to avoid binding to that function if we are on WinNT/9x.
- */
static void set_service_description(void)
{
const char *full_description;
@@ -403,10 +241,8 @@ static void set_service_description(void)
*/
full_description = ap_get_server_description();
- if ((osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- && (osver.dwMajorVersion > 4)
- && (ChangeServiceConfig2)
- && (schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)))
+ if ((ChangeServiceConfig2) &&
+ (schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)))
{
SC_HANDLE schService = OpenService(schSCManager, mpm_service_name,
SERVICE_CHANGE_CONFIG);
@@ -415,55 +251,42 @@ static void set_service_description(void)
* object types, some volatile, some not.
*/
/* ###: utf-ize */
- if (ChangeServiceConfig2(schService,
- 1 /* SERVICE_CONFIG_DESCRIPTION */,
- (LPVOID) &full_description)) {
- full_description = NULL;
- }
+ ChangeServiceConfig2(schService,
+ 1 /* SERVICE_CONFIG_DESCRIPTION */,
+ (LPVOID) &full_description);
CloseServiceHandle(schService);
}
CloseServiceHandle(schSCManager);
}
-
- if (full_description)
- {
- char szPath[MAX_PATH];
- ap_regkey_t *svckey;
- apr_status_t rv;
-
- /* Find the Service key that Monitor Applications iterate */
- apr_snprintf(szPath, sizeof(szPath),
- "SYSTEM\\CurrentControlSet\\Services\\%s",
- mpm_service_name);
- rv = ap_regkey_open(&svckey, AP_REGKEY_LOCAL_MACHINE, szPath,
- APR_READ | APR_WRITE, pconf);
- if (rv != APR_SUCCESS) {
- return;
- }
- /* Attempt to set the Description value for our service */
- ap_regkey_value_set(svckey, "Description", full_description, 0, pconf);
- ap_regkey_close(svckey);
- }
}
/* handle the SCM's ControlService() callbacks to our service */
-static VOID WINAPI service_nt_ctrl(DWORD dwCtrlCode)
+static DWORD WINAPI service_nt_ctrl(DWORD dwCtrlCode, DWORD dwEventType,
+ LPVOID lpEventData, LPVOID lpContext)
{
- if (dwCtrlCode == SERVICE_CONTROL_STOP)
+ nt_service_ctx_t *ctx = lpContext;
+
+ /* SHUTDOWN is offered before STOP, accept the first opportunity */
+ if ((dwCtrlCode == SERVICE_CONTROL_STOP)
+ || (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
{
ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
- ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 30000);
- return;
+ ReportStatusToSCMgr(SERVICE_STOP_PENDING, 30000, ctx);
+ return (NO_ERROR);
}
if (dwCtrlCode == SERVICE_APACHE_RESTART)
{
ap_signal_parent(SIGNAL_PARENT_RESTART);
- ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 30000);
- return;
+ ReportStatusToSCMgr(SERVICE_START_PENDING, 30000, ctx);
+ return (NO_ERROR);
+ }
+ if (dwCtrlCode == SERVICE_CONTROL_INTERROGATE) {
+ ReportStatusToSCMgr(globdat.ssStatus.dwCurrentState, 0, ctx);
+ return (NO_ERROR);
}
- ReportStatusToSCMgr(globdat.ssStatus.dwCurrentState, NO_ERROR, 0);
+ return (ERROR_CALL_NOT_IMPLEMENTED);
}
@@ -477,25 +300,26 @@ extern apr_array_header_t *mpm_new_argv;
static void __stdcall service_nt_main_fn(DWORD argc, LPTSTR *argv)
{
const char *ignored;
+ nt_service_ctx_t *ctx = &globdat;
/* args and service names live in the same pool */
mpm_service_set_name(mpm_new_argv->pool, &ignored, argv[0]);
- memset(&globdat.ssStatus, 0, sizeof(globdat.ssStatus));
- globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
- globdat.ssStatus.dwCurrentState = SERVICE_START_PENDING;
- globdat.ssStatus.dwCheckPoint = 1;
+ memset(&ctx->ssStatus, 0, sizeof(ctx->ssStatus));
+ ctx->ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ ctx->ssStatus.dwCurrentState = SERVICE_START_PENDING;
+ ctx->ssStatus.dwCheckPoint = 1;
/* ###: utf-ize */
- if (!(globdat.hServiceStatus = RegisterServiceCtrlHandler(argv[0], service_nt_ctrl)))
+ if (!(ctx->hServiceStatus = RegisterServiceCtrlHandlerEx(argv[0], service_nt_ctrl, ctx)))
{
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(),
- NULL, "Failure registering service handler");
+ NULL, APLOGNO(00365) "Failure registering service handler");
return;
}
/* Report status, no errors, and buy 3 more seconds */
- ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 30000);
+ ReportStatusToSCMgr(SERVICE_START_PENDING, 30000, ctx);
/* We need to append all the command arguments passed via StartService()
* to our running service... which just got here via the SCM...
@@ -524,9 +348,9 @@ static void __stdcall service_nt_main_fn(DWORD argc, LPTSTR *argv)
/* Let the main thread continue now... but hang on to the
* signal_monitor event so we can take further action
*/
- SetEvent(globdat.service_init);
+ SetEvent(ctx->service_init);
- WaitForSingleObject(globdat.service_term, INFINITE);
+ WaitForSingleObject(ctx->service_term, INFINITE);
}
@@ -545,7 +369,7 @@ static DWORD WINAPI service_nt_dispatch_thread(LPVOID nada)
{
/* This is a genuine failure of the SCM. */
rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00366)
"Error starting service control dispatcher");
}
@@ -553,6 +377,16 @@ static DWORD WINAPI service_nt_dispatch_thread(LPVOID nada)
}
+/* The service configuration's is stored under the following trees:
+ *
+ * HKLM\System\CurrentControlSet\Services\[service name]
+ *
+ * \DisplayName
+ * \ImagePath
+ * \Parameters\ConfigArgs
+ */
+
+
apr_status_t mpm_service_set_name(apr_pool_t *p, const char **display_name,
const char *set_name)
{
@@ -599,7 +433,7 @@ apr_status_t mpm_merge_service_args(apr_pool_t *p,
}
if (rv != APR_SUCCESS) {
if (rv == ERROR_FILE_NOT_FOUND) {
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(00367)
"No ConfigArgs registered for %s, perhaps "
"this service is not installed?",
mpm_service_name);
@@ -645,23 +479,13 @@ static void service_stopped(void)
/* Still have a thread & window to clean up, so signal now */
if (globdat.service_thread)
{
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- /* Stop logging to the event log */
- mpm_nt_eventlog_stderr_flush();
+ /* Stop logging to the event log */
+ mpm_nt_eventlog_stderr_flush();
- /* Cause the service_nt_main_fn to complete */
- ReleaseMutex(globdat.service_term);
+ /* Cause the service_nt_main_fn to complete */
+ ReleaseMutex(globdat.service_term);
- ReportStatusToSCMgr(SERVICE_STOPPED, // service state
- NO_ERROR, // exit code
- 0); // wait hint
- }
- else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
- {
- RegisterServiceProcess(0, 0);
- PostThreadMessage(globdat.service_thread_id, WM_CLOSE, 0, 0);
- }
+ ReportStatusToSCMgr(SERVICE_STOPPED, 0, &globdat);
WaitForSingleObject(globdat.service_thread, 5000);
CloseHandle(globdat.service_thread);
@@ -686,31 +510,16 @@ apr_status_t mpm_service_to_start(const char **display_name, apr_pool_t *p)
return APR_ENOTHREAD;
}
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- globdat.service_init = CreateEvent(NULL, FALSE, FALSE, NULL);
- globdat.service_term = CreateMutex(NULL, TRUE, NULL);
- if (!globdat.service_init || !globdat.service_term) {
- return APR_EGENERAL;
- }
-
- globdat.service_thread = CreateThread(NULL, 0, service_nt_dispatch_thread,
- NULL, 0, &globdat.service_thread_id);
+ globdat.service_init = CreateEvent(NULL, FALSE, FALSE, NULL);
+ globdat.service_term = CreateMutex(NULL, TRUE, NULL);
+ if (!globdat.service_init || !globdat.service_term) {
+ return APR_EGENERAL;
}
- else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
- {
- if (!RegisterServiceProcess(0, 1))
- return GetLastError();
-
- globdat.service_init = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (!globdat.service_init) {
- return APR_EGENERAL;
- }
- globdat.service_thread = CreateThread(NULL, 0, monitor_service_9x_thread,
- (LPVOID) mpm_service_name, 0,
- &globdat.service_thread_id);
- }
+ globdat.service_thread = CreateThread(NULL, 65536,
+ service_nt_dispatch_thread,
+ NULL, stack_res_flag,
+ &globdat.service_thread_id);
if (!globdat.service_thread) {
return APR_ENOTHREAD;
@@ -733,22 +542,14 @@ apr_status_t mpm_service_to_start(const char **display_name, apr_pool_t *p)
apr_status_t mpm_service_started(void)
{
set_service_description();
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- ReportStatusToSCMgr(SERVICE_RUNNING, // service state
- NO_ERROR, // exit code
- 0); // wait hint
- }
+ ReportStatusToSCMgr(SERVICE_RUNNING, 0, &globdat);
return APR_SUCCESS;
}
void mpm_service_stopping(void)
{
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- ReportStatusToSCMgr(SERVICE_STOP_PENDING, // service state
- NO_ERROR, // exit code
- 30000); // wait hint
+ ReportStatusToSCMgr(SERVICE_STOP_PENDING, 30000, &globdat);
}
@@ -760,6 +561,8 @@ apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc,
char *launch_cmd;
ap_regkey_t *key;
apr_status_t rv;
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
fprintf(stderr,reconfig ? "Reconfiguring the %s service\n"
: "Installing the %s service\n", mpm_display_name);
@@ -768,137 +571,87 @@ apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc,
if (GetModuleFileName(NULL, exe_path, sizeof(exe_path)) == 0)
{
apr_status_t rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00368)
"GetModuleFileName failed");
return rv;
}
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- SC_HANDLE schService;
- SC_HANDLE schSCManager;
-
- schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
- SC_MANAGER_CREATE_SERVICE);
- if (!schSCManager) {
- rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "Failed to open the WinNT service manager");
- return (rv);
- }
+ schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
+ SC_MANAGER_CREATE_SERVICE);
+ if (!schSCManager) {
+ rv = apr_get_os_error();
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00369)
+ "Failed to open the WinNT service manager, perhaps "
+ "you forgot to log in as Adminstrator?");
+ return (rv);
+ }
- launch_cmd = apr_psprintf(ptemp, "\"%s\" -k runservice", exe_path);
+ launch_cmd = apr_psprintf(ptemp, "\"%s\" -k runservice", exe_path);
- if (reconfig) {
- /* ###: utf-ize */
- schService = OpenService(schSCManager, mpm_service_name,
- SERVICE_CHANGE_CONFIG);
- if (!schService) {
- ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR,
- apr_get_os_error(), NULL,
- "OpenService failed");
- }
- /* ###: utf-ize */
- else if (!ChangeServiceConfig(schService,
- SERVICE_WIN32_OWN_PROCESS,
- SERVICE_AUTO_START,
- SERVICE_ERROR_NORMAL,
- launch_cmd, NULL, NULL,
- "Tcpip\0Afd\0", NULL, NULL,
- mpm_display_name)) {
- ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR,
- apr_get_os_error(), NULL,
- "ChangeServiceConfig failed");
- /* !schService aborts configuration below */
- CloseServiceHandle(schService);
- schService = NULL;
- }
+ if (reconfig) {
+ /* ###: utf-ize */
+ schService = OpenService(schSCManager, mpm_service_name,
+ SERVICE_CHANGE_CONFIG);
+ if (!schService) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR,
+ apr_get_os_error(), NULL,
+ "OpenService failed");
}
- else {
- /* RPCSS is the Remote Procedure Call (RPC) Locator required
- * for DCOM communication pipes. I am far from convinced we
- * should add this to the default service dependencies, but
- * be warned that future apache modules or ISAPI dll's may
- * depend on it.
- */
- /* ###: utf-ize */
- schService = CreateService(schSCManager, // SCManager database
- mpm_service_name, // name of service
- mpm_display_name, // name to display
- SERVICE_ALL_ACCESS, // access required
- SERVICE_WIN32_OWN_PROCESS, // service type
- SERVICE_AUTO_START, // start type
- SERVICE_ERROR_NORMAL, // error control type
- launch_cmd, // service's binary
- NULL, // no load svc group
- NULL, // no tag identifier
- "Tcpip\0Afd\0", // dependencies
- NULL, // use SYSTEM account
- NULL); // no password
-
- if (!schService)
- {
- rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "Failed to create WinNT Service Profile");
- CloseServiceHandle(schSCManager);
- return (rv);
+ /* ###: utf-ize */
+ else if (!ChangeServiceConfig(schService,
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL,
+ launch_cmd, NULL, NULL,
+ "Tcpip\0Afd\0", NULL, NULL,
+ mpm_display_name)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR,
+ apr_get_os_error(), NULL,
+ "ChangeServiceConfig failed");
+
+ /* !schService aborts configuration below */
+ CloseServiceHandle(schService);
+ schService = NULL;
}
}
-
- CloseServiceHandle(schService);
- CloseServiceHandle(schSCManager);
- }
- else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
- {
- /* Store the launch command in the registry */
- launch_cmd = apr_psprintf(ptemp, "\"%s\" -n %s -k runservice",
- exe_path, mpm_service_name);
- rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, SERVICECONFIG9X,
- APR_READ | APR_WRITE | APR_CREATE, pconf);
- if (rv == APR_SUCCESS) {
- rv = ap_regkey_value_set(key, mpm_service_name,
- launch_cmd, 0, pconf);
- ap_regkey_close(key);
- }
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "%s: Failed to add the RunServices registry entry.",
- mpm_display_name);
- return (rv);
- }
-
- apr_snprintf(key_name, sizeof(key_name), SERVICECONFIG, mpm_service_name);
- rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name,
- APR_READ | APR_WRITE | APR_CREATE, pconf);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "%s: Failed to create the registry service key.",
- mpm_display_name);
- return (rv);
- }
- rv = ap_regkey_value_set(key, "ImagePath", launch_cmd, 0, pconf);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "%s: Failed to store ImagePath in the registry.",
- mpm_display_name);
- ap_regkey_close(key);
- return (rv);
- }
- rv = ap_regkey_value_set(key, "DisplayName",
- mpm_display_name, 0, pconf);
- ap_regkey_close(key);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "%s: Failed to store DisplayName in the registry.",
- mpm_display_name);
+ else {
+ /* RPCSS is the Remote Procedure Call (RPC) Locator required
+ * for DCOM communication pipes. I am far from convinced we
+ * should add this to the default service dependencies, but
+ * be warned that future apache modules or ISAPI dll's may
+ * depend on it.
+ */
+ /* ###: utf-ize */
+ schService = CreateService(schSCManager, /* SCManager database */
+ mpm_service_name, /* name of service */
+ mpm_display_name, /* name to display */
+ SERVICE_ALL_ACCESS, /* access required */
+ SERVICE_WIN32_OWN_PROCESS, /* service type */
+ SERVICE_AUTO_START, /* start type */
+ SERVICE_ERROR_NORMAL, /* error control type */
+ launch_cmd, /* service's binary */
+ NULL, /* no load svc group */
+ NULL, /* no tag identifier */
+ "Tcpip\0Afd\0", /* dependencies */
+ NULL, /* use SYSTEM account */
+ NULL); /* no password */
+
+ if (!schService)
+ {
+ rv = apr_get_os_error();
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00370)
+ "Failed to create WinNT Service Profile");
+ CloseServiceHandle(schSCManager);
return (rv);
}
}
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
+
set_service_description();
- /* For both WinNT & Win9x store the service ConfigArgs in the registry...
+ /* Store the service ConfigArgs in the registry...
*/
apr_snprintf(key_name, sizeof(key_name), SERVICEPARAMS, mpm_service_name);
rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name,
@@ -908,7 +661,7 @@ apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc,
ap_regkey_close(key);
}
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00371)
"%s: Failed to store the ConfigArgs in the registry.",
mpm_display_name);
return (rv);
@@ -920,90 +673,52 @@ apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc,
apr_status_t mpm_service_uninstall(void)
{
- char key_name[MAX_PATH];
apr_status_t rv;
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- SC_HANDLE schService;
- SC_HANDLE schSCManager;
-
- fprintf(stderr,"Removing the %s service\n", mpm_display_name);
+ fprintf(stderr,"Removing the %s service\n", mpm_display_name);
- schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
- SC_MANAGER_CONNECT);
- if (!schSCManager) {
- rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "Failed to open the WinNT service manager.");
- return (rv);
- }
+ schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
+ SC_MANAGER_CONNECT);
+ if (!schSCManager) {
+ rv = apr_get_os_error();
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00372)
+ "Failed to open the WinNT service manager.");
+ return (rv);
+ }
- /* ###: utf-ize */
- schService = OpenService(schSCManager, mpm_service_name, DELETE);
+ /* ###: utf-ize */
+ schService = OpenService(schSCManager, mpm_service_name, DELETE);
- if (!schService) {
- rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
+ if (!schService) {
+ rv = apr_get_os_error();
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00373)
"%s: OpenService failed", mpm_display_name);
- return (rv);
- }
-
- /* assure the service is stopped before continuing
- *
- * This may be out of order... we might not be able to be
- * granted all access if the service is running anyway.
- *
- * And do we want to make it *this easy* for them
- * to uninstall their service unintentionally?
- */
- // ap_stop_service(schService);
+ return (rv);
+ }
- if (DeleteService(schService) == 0) {
- rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "%s: Failed to delete the service.", mpm_display_name);
- return (rv);
- }
+ /* assure the service is stopped before continuing
+ *
+ * This may be out of order... we might not be able to be
+ * granted all access if the service is running anyway.
+ *
+ * And do we want to make it *this easy* for them
+ * to uninstall their service unintentionally?
+ */
+ /* ap_stop_service(schService);
+ */
- CloseServiceHandle(schService);
- CloseServiceHandle(schSCManager);
+ if (DeleteService(schService) == 0) {
+ rv = apr_get_os_error();
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00374)
+ "%s: Failed to delete the service.", mpm_display_name);
+ return (rv);
}
- else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
- {
- apr_status_t rv2, rv3;
- ap_regkey_t *key;
- fprintf(stderr,"Removing the %s service\n", mpm_display_name);
-
- /* TODO: assure the service is stopped before continuing */
- rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, SERVICECONFIG9X,
- APR_READ | APR_WRITE | APR_CREATE, pconf);
- if (rv == APR_SUCCESS) {
- rv = ap_regkey_value_remove(key, mpm_service_name, pconf);
- ap_regkey_close(key);
- }
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "%s: Failed to remove the RunServices registry "
- "entry.", mpm_display_name);
- }
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
- /* we blast Services/us, not just the Services/us/Parameters branch */
- apr_snprintf(key_name, sizeof(key_name), SERVICEPARAMS, mpm_service_name);
- rv2 = ap_regkey_remove(AP_REGKEY_LOCAL_MACHINE, key_name, pconf);
- apr_snprintf(key_name, sizeof(key_name), SERVICECONFIG, mpm_service_name);
- rv3 = ap_regkey_remove(AP_REGKEY_LOCAL_MACHINE, key_name, pconf);
- rv2 = (rv2 != APR_SUCCESS) ? rv2 : rv3;
- if (rv2 != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv2, NULL,
- "%s: Failed to remove the service config from the "
- "registry.", mpm_display_name);
- }
- rv = (rv != APR_SUCCESS) ? rv : rv2;
- if (rv != APR_SUCCESS)
- return rv;
- }
fprintf(stderr,"The %s service has been removed successfully.\n", mpm_display_name);
return APR_SUCCESS;
}
@@ -1035,137 +750,62 @@ apr_status_t mpm_service_start(apr_pool_t *ptemp, int argc,
const char * const * argv)
{
apr_status_t rv;
+ CHAR **start_argv;
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
fprintf(stderr,"Starting the %s service\n", mpm_display_name);
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- CHAR **start_argv;
- SC_HANDLE schService;
- SC_HANDLE schSCManager;
-
- schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
- SC_MANAGER_CONNECT);
- if (!schSCManager) {
- rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "Failed to open the WinNT service manager");
- return (rv);
- }
-
- /* ###: utf-ize */
- schService = OpenService(schSCManager, mpm_service_name,
- SERVICE_START | SERVICE_QUERY_STATUS);
- if (!schService) {
- rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "%s: Failed to open the service.", mpm_display_name);
- CloseServiceHandle(schSCManager);
- return (rv);
- }
-
- if (QueryServiceStatus(schService, &globdat.ssStatus)
- && (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING)) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL,
- "Service %s is already started!", mpm_display_name);
- CloseServiceHandle(schService);
- CloseServiceHandle(schSCManager);
- return 0;
- }
-
- start_argv = malloc((argc + 1) * sizeof(const char **));
- memcpy(start_argv, argv, argc * sizeof(const char **));
- start_argv[argc] = NULL;
-
- rv = APR_EINIT;
- /* ###: utf-ize */
- if (StartService(schService, argc, start_argv)
- && signal_service_transition(schService, 0, /* test only */
- SERVICE_START_PENDING,
- SERVICE_RUNNING))
- rv = APR_SUCCESS;
+ schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
+ SC_MANAGER_CONNECT);
+ if (!schSCManager) {
+ rv = apr_get_os_error();
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00375)
+ "Failed to open the WinNT service manager");
+ return (rv);
+ }
- if (rv != APR_SUCCESS)
- rv = apr_get_os_error();
+ /* ###: utf-ize */
+ schService = OpenService(schSCManager, mpm_service_name,
+ SERVICE_START | SERVICE_QUERY_STATUS);
+ if (!schService) {
+ rv = apr_get_os_error();
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, APLOGNO(00376)
+ "%s: Failed to open the service.", mpm_display_name);
+ CloseServiceHandle(schSCManager);
+ return (rv);
+ }
+ if (QueryServiceStatus(schService, &globdat.ssStatus)
+ && (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL, APLOGNO(00377)
+ "Service %s is already started!", mpm_display_name);
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
+ return 0;
}
- else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
- {
- STARTUPINFO si; /* Filled in prior to call to CreateProcess */
- PROCESS_INFORMATION pi; /* filled in on call to CreateProcess */
- char exe_path[MAX_PATH];
- char exe_cmd[MAX_PATH * 4];
- char *next_arg;
- int i;
-
- /* Locate the active top level window named service_name
- * provided the class is ApacheWin95ServiceMonitor
- */
- if (FindWindow("ApacheWin95ServiceMonitor", mpm_service_name)) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL,
- "Service %s is already started!", mpm_display_name);
- return 0;
- }
- /* This may not appear intuitive, but Win9x will not allow a process
- * to detach from the console without releasing the entire console.
- * Ergo, we must spawn a new process for the service to get back our
- * console window.
- * The config is pre-flighted, so there should be no danger of failure.
- */
-
- if (GetModuleFileName(NULL, exe_path, sizeof(exe_path)) == 0)
- {
- apr_status_t rv = apr_get_os_error();
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
- "GetModuleFileName failed");
- return rv;
- }
-
- apr_snprintf(exe_cmd, sizeof(exe_cmd),
- "\"%s\" -n %s -k runservice",
- exe_path, mpm_service_name);
- next_arg = strchr(exe_cmd, '\0');
- for (i = 0; i < argc; ++i) {
- apr_snprintf(next_arg, sizeof(exe_cmd) - (next_arg - exe_cmd),
- " \"%s\"", argv[i]);
- next_arg = strchr(exe_cmd, '\0');
- }
-
- memset(&si, 0, sizeof(si));
- memset(&pi, 0, sizeof(pi));
- si.cb = sizeof(si);
- si.dwFlags = STARTF_USESHOWWINDOW;
- si.wShowWindow = SW_HIDE; /* This might be redundant */
+ start_argv = malloc((argc + 1) * sizeof(const char **));
+ memcpy(start_argv, argv, argc * sizeof(const char **));
+ start_argv[argc] = NULL;
- rv = APR_EINIT;
- if (CreateProcess(NULL, exe_cmd, NULL, NULL, FALSE,
- DETACHED_PROCESS, /* Creation flags */
- NULL, NULL, &si, &pi))
- {
- DWORD code;
- while (GetExitCodeProcess(pi.hProcess, &code) == STILL_ACTIVE) {
- if (FindWindow("ApacheWin95ServiceMonitor", mpm_service_name)) {
- rv = APR_SUCCESS;
- break;
- }
- Sleep (1000);
- }
- }
-
- if (rv != APR_SUCCESS)
- rv = apr_get_os_error();
+ rv = APR_EINIT;
+ /* ###: utf-ize */
+ if (StartService(schService, argc, start_argv)
+ && signal_service_transition(schService, 0, /* test only */
+ SERVICE_START_PENDING,
+ SERVICE_RUNNING))
+ rv = APR_SUCCESS;
+ if (rv != APR_SUCCESS)
+ rv = apr_get_os_error();
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- }
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
if (rv == APR_SUCCESS)
fprintf(stderr,"The %s service is running.\n", mpm_display_name);
else
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, APLOGNO(00378)
"%s: Failed to start the service process.",
mpm_display_name);
@@ -1178,129 +818,69 @@ apr_status_t mpm_service_start(apr_pool_t *ptemp, int argc,
void mpm_signal_service(apr_pool_t *ptemp, int signal)
{
int success = FALSE;
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
- if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
- {
- SC_HANDLE schService;
- SC_HANDLE schSCManager;
-
- schSCManager = OpenSCManager(NULL, NULL, // default machine & database
- SC_MANAGER_CONNECT);
+ schSCManager = OpenSCManager(NULL, NULL, /* default machine & database */
+ SC_MANAGER_CONNECT);
- if (!schSCManager) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
- "Failed to open the NT Service Manager");
- return;
- }
-
- /* ###: utf-ize */
- schService = OpenService(schSCManager, mpm_service_name,
- SERVICE_INTERROGATE | SERVICE_QUERY_STATUS |
- SERVICE_USER_DEFINED_CONTROL |
- SERVICE_START | SERVICE_STOP);
-
- if (schService == NULL) {
- /* Could not open the service */
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
- "Failed to open the %s Service", mpm_display_name);
- CloseServiceHandle(schSCManager);
- return;
- }
-
- if (!QueryServiceStatus(schService, &globdat.ssStatus)) {
- ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
- "Query of Service %s failed", mpm_display_name);
- CloseServiceHandle(schService);
- CloseServiceHandle(schSCManager);
- return;
- }
-
- if (!signal && (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED)) {
- fprintf(stderr,"The %s service is not started.\n", mpm_display_name);
- CloseServiceHandle(schService);
- CloseServiceHandle(schSCManager);
- return;
- }
-
- fprintf(stderr,"The %s service is %s.\n", mpm_display_name,
- signal ? "restarting" : "stopping");
+ if (!schSCManager) {
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL, APLOGNO(00379)
+ "Failed to open the NT Service Manager");
+ return;
+ }
- if (!signal)
- success = signal_service_transition(schService,
- SERVICE_CONTROL_STOP,
- SERVICE_STOP_PENDING,
- SERVICE_STOPPED);
- else if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) {
- mpm_service_start(ptemp, 0, NULL);
- CloseServiceHandle(schService);
- CloseServiceHandle(schSCManager);
- return;
- }
- else
- success = signal_service_transition(schService,
- SERVICE_APACHE_RESTART,
- SERVICE_START_PENDING,
- SERVICE_RUNNING);
+ /* ###: utf-ize */
+ schService = OpenService(schSCManager, mpm_service_name,
+ SERVICE_INTERROGATE | SERVICE_QUERY_STATUS |
+ SERVICE_USER_DEFINED_CONTROL |
+ SERVICE_START | SERVICE_STOP);
+
+ if (schService == NULL) {
+ /* Could not open the service */
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL, APLOGNO(00380)
+ "Failed to open the %s Service", mpm_display_name);
+ CloseServiceHandle(schSCManager);
+ return;
+ }
+ if (!QueryServiceStatus(schService, &globdat.ssStatus)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL, APLOGNO(00381)
+ "Query of Service %s failed", mpm_display_name);
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
+ return;
}
- else /* !isWindowsNT() */
- {
- DWORD service_pid;
- HANDLE hwnd;
- char prefix[20];
- /* Locate the active top level window named service_name
- * provided the class is ApacheWin95ServiceMonitor
- */
- hwnd = FindWindow("ApacheWin95ServiceMonitor", mpm_service_name);
- if (hwnd && GetWindowThreadProcessId(hwnd, &service_pid))
- globdat.ssStatus.dwCurrentState = SERVICE_RUNNING;
- else
- {
- globdat.ssStatus.dwCurrentState = SERVICE_STOPPED;
- if (!signal) {
- fprintf(stderr,"The %s service is not started.\n", mpm_display_name);
- return;
- }
- }
- fprintf(stderr,"The %s service is %s.\n", mpm_display_name,
- signal ? "restarting" : "stopping");
+ if (!signal && (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED)) {
+ fprintf(stderr,"The %s service is not started.\n", mpm_display_name);
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
+ return;
+ }
- apr_snprintf(prefix, sizeof(prefix), "ap%ld", (long)service_pid);
- setup_signal_names(prefix);
+ fprintf(stderr,"The %s service is %s.\n", mpm_display_name,
+ signal ? "restarting" : "stopping");
- if (!signal)
- {
- int ticks = 60;
- ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
- while (--ticks)
- {
- if (!IsWindow(hwnd)) {
- success = TRUE;
- break;
- }
- Sleep(1000);
- }
- }
- else /* !stop */
- {
- /* TODO: Aught to add a little test to the restart logic, and
- * store the restart counter in the window's user dword.
- * Then we can hang on and report a successful restart. But
- * that's a project for another day.
- */
- if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) {
- mpm_service_start(ptemp, 0, NULL);
- return;
- }
- else {
- success = TRUE;
- ap_signal_parent(SIGNAL_PARENT_RESTART);
- }
- }
+ if (!signal)
+ success = signal_service_transition(schService,
+ SERVICE_CONTROL_STOP,
+ SERVICE_STOP_PENDING,
+ SERVICE_STOPPED);
+ else if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) {
+ mpm_service_start(ptemp, 0, NULL);
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
+ return;
}
+ else
+ success = signal_service_transition(schService,
+ SERVICE_APACHE_RESTART,
+ SERVICE_START_PENDING,
+ SERVICE_RUNNING);
+
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
if (success)
fprintf(stderr,"The %s service has %s.\n", mpm_display_name,
diff --git a/server/mpm/worker/Makefile.in b/server/mpm/worker/Makefile.in
index b45b8483..e32210f1 100644
--- a/server/mpm/worker/Makefile.in
+++ b/server/mpm/worker/Makefile.in
@@ -1,5 +1,2 @@
-LTLIBRARY_NAME = libworker.la
-LTLIBRARY_SOURCES = worker.c fdqueue.c pod.c
-
-include $(top_srcdir)/build/ltlib.mk
+include $(top_srcdir)/build/special.mk
diff --git a/server/mpm/worker/config.m4 b/server/mpm/worker/config.m4
new file mode 100644
index 00000000..1a500264
--- /dev/null
+++ b/server/mpm/worker/config.m4
@@ -0,0 +1,11 @@
+AC_MSG_CHECKING(if worker MPM supports this platform)
+if test $forking_mpms_supported != yes; then
+ AC_MSG_RESULT(no - This is not a forking platform)
+elif test $ac_cv_define_APR_HAS_THREADS != yes; then
+ AC_MSG_RESULT(no - APR does not support threads)
+elif test $have_threaded_sig_graceful != yes; then
+ AC_MSG_RESULT(no - SIG_GRACEFUL cannot be used with a threaded MPM)
+else
+ AC_MSG_RESULT(yes)
+ APACHE_MPM_SUPPORTED(worker, yes, yes)
+fi
diff --git a/server/mpm/worker/config5.m4 b/server/mpm/worker/config3.m4
index cc131348..dc2bccb5 100644
--- a/server/mpm/worker/config5.m4
+++ b/server/mpm/worker/config3.m4
@@ -1,6 +1,5 @@
dnl ## XXX - Need a more thorough check of the proper flags to use
-if test "$MPM_NAME" = "worker" ; then
+APACHE_MPM_MODULE(worker, $enable_mpm_worker, worker.lo fdqueue.lo pod.lo,[
AC_CHECK_FUNCS(pthread_kill)
- APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile)
-fi
+])
diff --git a/server/mpm/worker/fdqueue.c b/server/mpm/worker/fdqueue.c
index dfbe8c47..fe5881b4 100644
--- a/server/mpm/worker/fdqueue.c
+++ b/server/mpm/worker/fdqueue.c
@@ -43,7 +43,7 @@ static apr_status_t queue_info_cleanup(void *data_)
if (first_pool == NULL) {
break;
}
- if (apr_atomic_casptr((volatile void**)&(qi->recycled_pools), first_pool->next,
+ if (apr_atomic_casptr((void*)&(qi->recycled_pools), first_pool->next,
first_pool) == first_pool) {
apr_pool_destroy(first_pool->pool);
}
@@ -100,7 +100,7 @@ apr_status_t ap_queue_info_set_idle(fd_queue_info_t *queue_info,
*/
struct recycled_pool *next = queue_info->recycled_pools;
new_recycle->next = next;
- if (apr_atomic_casptr((volatile void**)&(queue_info->recycled_pools),
+ if (apr_atomic_casptr((void*)&(queue_info->recycled_pools),
new_recycle, next) == next) {
break;
}
@@ -209,7 +209,7 @@ apr_status_t ap_queue_info_wait_for_idler(fd_queue_info_t *queue_info,
if (first_pool == NULL) {
break;
}
- if (apr_atomic_casptr((volatile void**)&(queue_info->recycled_pools), first_pool->next,
+ if (apr_atomic_casptr((void*)&(queue_info->recycled_pools), first_pool->next,
first_pool) == first_pool) {
*recycled_pool = first_pool->pool;
break;
@@ -284,6 +284,8 @@ apr_status_t ap_queue_init(fd_queue_t *queue, int queue_capacity, apr_pool_t *a)
queue->data = apr_palloc(a, queue_capacity * sizeof(fd_queue_elem_t));
queue->bounds = queue_capacity;
queue->nelts = 0;
+ queue->in = 0;
+ queue->out = 0;
/* Set all the sockets in the queue to NULL */
for (i = 0; i < queue_capacity; ++i)
@@ -312,7 +314,10 @@ apr_status_t ap_queue_push(fd_queue_t *queue, apr_socket_t *sd, apr_pool_t *p)
AP_DEBUG_ASSERT(!queue->terminated);
AP_DEBUG_ASSERT(!ap_queue_full(queue));
- elem = &queue->data[queue->nelts];
+ elem = &queue->data[queue->in];
+ queue->in++;
+ if (queue->in >= queue->bounds)
+ queue->in -= queue->bounds;
elem->sd = sd;
elem->p = p;
queue->nelts++;
@@ -361,7 +366,11 @@ apr_status_t ap_queue_pop(fd_queue_t *queue, apr_socket_t **sd, apr_pool_t **p)
}
}
- elem = &queue->data[--queue->nelts];
+ elem = &queue->data[queue->out];
+ queue->out++;
+ if (queue->out >= queue->bounds)
+ queue->out -= queue->bounds;
+ queue->nelts--;
*sd = elem->sd;
*p = elem->p;
#ifdef AP_DEBUG
diff --git a/server/mpm/worker/fdqueue.h b/server/mpm/worker/fdqueue.h
index 7de082a0..1d48a1a8 100644
--- a/server/mpm/worker/fdqueue.h
+++ b/server/mpm/worker/fdqueue.h
@@ -55,8 +55,10 @@ typedef struct fd_queue_elem_t fd_queue_elem_t;
struct fd_queue_t {
fd_queue_elem_t *data;
- int nelts;
- int bounds;
+ unsigned int nelts;
+ unsigned int bounds;
+ unsigned int in;
+ unsigned int out;
apr_thread_mutex_t *one_big_mutex;
apr_thread_cond_t *not_empty;
int terminated;
diff --git a/server/mpm/worker/mpm.h b/server/mpm/worker/mpm.h
deleted file mode 100644
index 335f4b6a..00000000
--- a/server/mpm/worker/mpm.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @file worker/mpm.h
- * @brief Unix Worker MPM
- *
- * @defgroup APACHE_MPM_WORKER Unix Worker MPM
- * @ingroup APACHE_OS_UNIX APACHE_MPM
- * @{
- */
-
-#include "scoreboard.h"
-#include "unixd.h"
-
-#ifndef APACHE_MPM_WORKER_H
-#define APACHE_MPM_WORKER_H
-
-#define WORKER_MPM
-
-#define MPM_NAME "Worker"
-
-#define AP_MPM_WANT_RECLAIM_CHILD_PROCESSES
-#define AP_MPM_WANT_WAIT_OR_TIMEOUT
-#define AP_MPM_WANT_PROCESS_CHILD_STATUS
-#define AP_MPM_WANT_SET_PIDFILE
-#define AP_MPM_WANT_SET_SCOREBOARD
-#define AP_MPM_WANT_SET_LOCKFILE
-#define AP_MPM_WANT_SET_MAX_REQUESTS
-#define AP_MPM_WANT_SET_COREDUMPDIR
-#define AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
-#define AP_MPM_WANT_SIGNAL_SERVER
-#define AP_MPM_WANT_SET_MAX_MEM_FREE
-#define AP_MPM_WANT_SET_STACKSIZE
-#define AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN
-#define AP_MPM_WANT_FATAL_SIGNAL_HANDLER
-#define AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
-
-#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
-#define MPM_NOTE_CHILD_KILLED(i) (MPM_CHILD_PID(i) = 0)
-#define MPM_ACCEPT_FUNC unixd_accept
-
-extern int ap_threads_per_child;
-extern int ap_max_daemons_limit;
-extern server_rec *ap_server_conf;
-extern char ap_coredump_dir[MAX_STRING_LEN];
-
-#endif /* APACHE_MPM_WORKER_H */
-/** @} */
diff --git a/server/mpm/worker/mpm_default.h b/server/mpm/worker/mpm_default.h
index a48f6725..244a0bb1 100644
--- a/server/mpm/worker/mpm_default.h
+++ b/server/mpm/worker/mpm_default.h
@@ -50,29 +50,5 @@
#define DEFAULT_THREADS_PER_CHILD 25
#endif
-/* File used for accept locking, when we use a file */
-#ifndef DEFAULT_LOCKFILE
-#define DEFAULT_LOCKFILE DEFAULT_REL_RUNTIMEDIR "/accept.lock"
-#endif
-
-/* Where the main/parent process's pid is logged */
-#ifndef DEFAULT_PIDLOG
-#define DEFAULT_PIDLOG DEFAULT_REL_RUNTIMEDIR "/httpd.pid"
-#endif
-
-/*
- * Interval, in microseconds, between scoreboard maintenance.
- */
-#ifndef SCOREBOARD_MAINTENANCE_INTERVAL
-#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000
-#endif
-
-/* Number of requests to try to handle in a single process. If <= 0,
- * the children don't die off.
- */
-#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD
-#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000
-#endif
-
#endif /* AP_MPM_DEFAULT_H */
/** @} */
diff --git a/server/mpm/worker/pod.c b/server/mpm/worker/pod.c
index 77ad1c90..86f7b39d 100644
--- a/server/mpm/worker/pod.c
+++ b/server/mpm/worker/pod.c
@@ -14,13 +14,16 @@
* limitations under the License.
*/
+#include "apr_portable.h"
#include "pod.h"
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
-AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod)
+APLOG_USE_MODULE(mpm_worker);
+
+AP_DECLARE(apr_status_t) ap_worker_pod_open(apr_pool_t *p, ap_worker_pod_t **pod)
{
apr_status_t rv;
@@ -41,7 +44,7 @@ AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod)
return APR_SUCCESS;
}
-AP_DECLARE(int) ap_mpm_pod_check(ap_pod_t *pod)
+AP_DECLARE(int) ap_worker_pod_check(ap_worker_pod_t *pod)
{
char c;
apr_os_file_t fd;
@@ -63,7 +66,7 @@ AP_DECLARE(int) ap_mpm_pod_check(ap_pod_t *pod)
return AP_NORESTART;
}
-AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod)
+AP_DECLARE(apr_status_t) ap_worker_pod_close(ap_worker_pod_t *pod)
{
apr_status_t rv;
@@ -79,7 +82,7 @@ AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod)
return rv;
}
-static apr_status_t pod_signal_internal(ap_pod_t *pod, int graceful)
+static apr_status_t pod_signal_internal(ap_worker_pod_t *pod, int graceful)
{
apr_status_t rv;
char char_of_death = graceful ? GRACEFUL_CHAR : RESTART_CHAR;
@@ -87,18 +90,18 @@ static apr_status_t pod_signal_internal(ap_pod_t *pod, int graceful)
rv = apr_file_write(pod->pod_out, &char_of_death, &one);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00325)
"write pipe_of_death");
}
return rv;
}
-AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod, int graceful)
+AP_DECLARE(apr_status_t) ap_worker_pod_signal(ap_worker_pod_t *pod, int graceful)
{
return pod_signal_internal(pod, graceful);
}
-AP_DECLARE(void) ap_mpm_pod_killpg(ap_pod_t *pod, int num, int graceful)
+AP_DECLARE(void) ap_worker_pod_killpg(ap_worker_pod_t *pod, int num, int graceful)
{
int i;
apr_status_t rv = APR_SUCCESS;
diff --git a/server/mpm/worker/pod.h b/server/mpm/worker/pod.h
index 55cad819..ccb9cf9d 100644
--- a/server/mpm/worker/pod.h
+++ b/server/mpm/worker/pod.h
@@ -31,7 +31,6 @@
#include "http_config.h"
#include "http_log.h"
#include "http_main.h"
-#include "mpm.h"
#include "mpm_common.h"
#include "ap_mpm.h"
#include "ap_listen.h"
@@ -43,17 +42,17 @@
#define AP_RESTART 0
#define AP_GRACEFUL 1
-typedef struct ap_pod_t ap_pod_t;
+typedef struct ap_worker_pod_t ap_worker_pod_t;
-struct ap_pod_t {
+struct ap_worker_pod_t {
apr_file_t *pod_in;
apr_file_t *pod_out;
apr_pool_t *p;
};
-AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod);
-AP_DECLARE(int) ap_mpm_pod_check(ap_pod_t *pod);
-AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod);
-AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod, int graceful);
-AP_DECLARE(void) ap_mpm_pod_killpg(ap_pod_t *pod, int num, int graceful);
+AP_DECLARE(apr_status_t) ap_worker_pod_open(apr_pool_t *p, ap_worker_pod_t **pod);
+AP_DECLARE(int) ap_worker_pod_check(ap_worker_pod_t *pod);
+AP_DECLARE(apr_status_t) ap_worker_pod_close(ap_worker_pod_t *pod);
+AP_DECLARE(apr_status_t) ap_worker_pod_signal(ap_worker_pod_t *pod, int graceful);
+AP_DECLARE(void) ap_worker_pod_killpg(ap_worker_pod_t *pod, int num, int graceful);
/** @} */
diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c
index 11c7d89c..75f15ebd 100644
--- a/server/mpm/worker/worker.c
+++ b/server/mpm/worker/worker.c
@@ -50,8 +50,6 @@
#error The Worker MPM requires APR threads, but they are unavailable.
#endif
-#define CORE_PRIVATE
-
#include "ap_config.h"
#include "httpd.h"
#include "http_main.h"
@@ -66,6 +64,8 @@
#include "scoreboard.h"
#include "fdqueue.h"
#include "mpm_default.h"
+#include "util_mutex.h"
+#include "unixd.h"
#include <signal.h>
#include <limits.h> /* for INT_MAX */
@@ -114,16 +114,14 @@
* Actual definitions of config globals
*/
-int ap_threads_per_child = 0; /* Worker threads per child */
+static int threads_per_child = 0; /* Worker threads per child */
static int ap_daemons_to_start = 0;
static int min_spare_threads = 0;
static int max_spare_threads = 0;
static int ap_daemons_limit = 0;
-static int server_limit = DEFAULT_SERVER_LIMIT;
-static int first_server_limit = 0;
-static int thread_limit = DEFAULT_THREAD_LIMIT;
-static int first_thread_limit = 0;
-static int changed_limit_at_restart;
+static int max_workers = 0;
+static int server_limit = 0;
+static int thread_limit = 0;
static int dying = 0;
static int workers_may_exit = 0;
static int start_thread_may_exit = 0;
@@ -134,7 +132,42 @@ static int resource_shortage = 0;
static fd_queue_t *worker_queue;
static fd_queue_info_t *worker_queue_info;
static int mpm_state = AP_MPMQ_STARTING;
-static int sick_child_detected;
+
+/* data retained by worker across load/unload of the module
+ * allocated on first call to pre-config hook; located on
+ * subsequent calls to pre-config hook
+ */
+typedef struct worker_retained_data {
+ int first_server_limit;
+ int first_thread_limit;
+ int module_loads;
+ int sick_child_detected;
+ ap_generation_t my_generation;
+ int volatile is_graceful; /* set from signal handler */
+ int maxclients_reported;
+ int near_maxclients_reported;
+ /*
+ * The max child slot ever assigned, preserved across restarts. Necessary
+ * to deal with MaxRequestWorkers changes across AP_SIG_GRACEFUL restarts.
+ * We use this value to optimize routines that have to scan the entire
+ * scoreboard.
+ */
+ int max_daemons_limit;
+ /*
+ * 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.
+ */
+ int idle_spawn_rate;
+#ifndef MAX_SPAWN_RATE
+#define MAX_SPAWN_RATE (32)
+#endif
+ int hold_off_on_exponential_spawning;
+} worker_retained_data;
+static worker_retained_data *retained;
+
+#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid)
/* The structure used to pass unique initialization info to each thread */
typedef struct {
@@ -155,19 +188,7 @@ typedef struct {
#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
-/*
- * The max child slot ever assigned, preserved across restarts. Necessary
- * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We
- * use this value to optimize routines that have to scan the entire
- * scoreboard.
- */
-int ap_max_daemons_limit = -1;
-
-static ap_pod_t *pod;
-
-/* *Non*-shared http_main globals... */
-
-server_rec *ap_server_conf;
+static ap_worker_pod_t *pod;
/* The worker MPM respects a couple of runtime flags that can aid
* in debugging. Setting the -DNO_DETACH flag will prevent the root process
@@ -233,7 +254,7 @@ static apr_socket_t **worker_sockets;
static void close_worker_sockets(void)
{
int i;
- for (i = 0; i < ap_threads_per_child; i++) {
+ for (i = 0; i < threads_per_child; i++) {
if (worker_sockets[i]) {
apr_socket_close(worker_sockets[i]);
worker_sockets[i] = NULL;
@@ -252,6 +273,10 @@ static void wakeup_listener(void)
*/
return;
}
+
+ /* unblock the listener if it's waiting for a worker */
+ ap_queue_info_term(worker_queue_info);
+
/*
* we should just be able to "kill(ap_my_pid, LISTENER_SIGNAL)" on all
* platforms and wake up the listener thread since it is the only thread
@@ -290,55 +315,109 @@ static void signal_threads(int mode)
if (mode == ST_UNGRACEFUL) {
workers_may_exit = 1;
ap_queue_interrupt_all(worker_queue);
- ap_queue_info_term(worker_queue_info);
close_worker_sockets(); /* forcefully kill all current connections */
}
}
-AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+static int worker_query(int query_code, int *result, apr_status_t *rv)
{
- switch(query_code){
+ *rv = APR_SUCCESS;
+ switch (query_code) {
case AP_MPMQ_MAX_DAEMON_USED:
- *result = ap_max_daemons_limit;
- return APR_SUCCESS;
+ *result = retained->max_daemons_limit;
+ break;
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_STATIC;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_IS_FORKED:
*result = AP_MPMQ_DYNAMIC;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_DAEMONS:
*result = server_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_HARD_LIMIT_THREADS:
*result = thread_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_THREADS:
- *result = ap_threads_per_child;
- return APR_SUCCESS;
+ *result = threads_per_child;
+ break;
case AP_MPMQ_MIN_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MIN_SPARE_THREADS:
*result = min_spare_threads;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_DAEMONS:
*result = 0;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_SPARE_THREADS:
*result = max_spare_threads;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_REQUESTS_DAEMON:
*result = ap_max_requests_per_child;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MAX_DAEMONS:
*result = ap_daemons_limit;
- return APR_SUCCESS;
+ break;
case AP_MPMQ_MPM_STATE:
*result = mpm_state;
- return APR_SUCCESS;
+ break;
+ case AP_MPMQ_GENERATION:
+ *result = retained->my_generation;
+ break;
+ default:
+ *rv = APR_ENOTIMPL;
+ break;
}
- return APR_ENOTIMPL;
+ return OK;
+}
+
+static void worker_note_child_killed(int childnum, pid_t pid, ap_generation_t gen)
+{
+ if (childnum != -1) { /* child had a scoreboard slot? */
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[childnum].pid,
+ ap_scoreboard_image->parent[childnum].generation,
+ childnum, MPM_CHILD_EXITED);
+ ap_scoreboard_image->parent[childnum].pid = 0;
+ }
+ else {
+ ap_run_child_status(ap_server_conf, pid, gen, -1, MPM_CHILD_EXITED);
+ }
+}
+
+static void worker_note_child_started(int slot, pid_t pid)
+{
+ ap_scoreboard_image->parent[slot].pid = pid;
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[slot].pid,
+ retained->my_generation, slot, MPM_CHILD_STARTED);
+}
+
+static void worker_note_child_lost_slot(int slot, pid_t newpid)
+{
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00263)
+ "pid %" APR_PID_T_FMT " taking over scoreboard slot from "
+ "%" APR_PID_T_FMT "%s",
+ newpid,
+ ap_scoreboard_image->parent[slot].pid,
+ ap_scoreboard_image->parent[slot].quiescing ?
+ " (quiescing)" : "");
+ ap_run_child_status(ap_server_conf,
+ ap_scoreboard_image->parent[slot].pid,
+ ap_scoreboard_image->parent[slot].generation,
+ slot, MPM_CHILD_LOST_SLOT);
+ /* Don't forget about this exiting child process, or we
+ * won't be able to kill it if it doesn't exit by the
+ * time the server is shut down.
+ */
+ ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid,
+ ap_scoreboard_image->parent[slot].generation);
+}
+
+static const char *worker_get_name(void)
+{
+ return "worker";
}
/* a clean exit from a child with proper cleanup */
@@ -349,6 +428,11 @@ static void clean_child_exit(int code)
if (pchild) {
apr_pool_destroy(pchild);
}
+
+ if (one_process) {
+ worker_note_child_killed(/* slot */ 0, 0, 0);
+ }
+
exit(code);
}
@@ -361,12 +445,11 @@ static void just_die(int sig)
* Connection structures and accounting...
*/
-/* volatile just in case */
+static int child_fatal;
+
+/* volatile because they're updated from a signal handler */
static int volatile shutdown_pending;
static int volatile restart_pending;
-static int volatile is_graceful;
-static volatile int child_fatal;
-ap_generation_t volatile ap_my_generation;
/*
* ap_start_shutdown() and ap_start_restart(), below, are a first stab at
@@ -398,7 +481,7 @@ static void ap_start_shutdown(int graceful)
return;
}
shutdown_pending = 1;
- is_graceful = graceful;
+ retained->is_graceful = graceful;
}
/* do a graceful restart if graceful == 1 */
@@ -410,7 +493,7 @@ static void ap_start_restart(int graceful)
return;
}
restart_pending = 1;
- is_graceful = graceful;
+ retained->is_graceful = graceful;
}
static void sig_term(int sig)
@@ -439,34 +522,37 @@ static void set_signals(void)
sa.sa_handler = sig_term;
if (sigaction(SIGTERM, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00264)
"sigaction(SIGTERM)");
#ifdef AP_SIG_GRACEFUL_STOP
if (sigaction(AP_SIG_GRACEFUL_STOP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00265)
"sigaction(" AP_SIG_GRACEFUL_STOP_STRING ")");
#endif
#ifdef SIGINT
if (sigaction(SIGINT, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00266)
"sigaction(SIGINT)");
#endif
#ifdef SIGXCPU
sa.sa_handler = SIG_DFL;
if (sigaction(SIGXCPU, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00267)
"sigaction(SIGXCPU)");
#endif
#ifdef SIGXFSZ
- sa.sa_handler = SIG_DFL;
+ /* For systems following the LFS standard, ignoring SIGXFSZ allows
+ * a write() beyond the 2GB limit to fail gracefully with E2BIG
+ * rather than terminate the process. */
+ sa.sa_handler = SIG_IGN;
if (sigaction(SIGXFSZ, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00268)
"sigaction(SIGXFSZ)");
#endif
#ifdef SIGPIPE
sa.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00269)
"sigaction(SIGPIPE)");
#endif
@@ -476,10 +562,10 @@ static void set_signals(void)
sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL);
sa.sa_handler = restart;
if (sigaction(SIGHUP, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00270)
"sigaction(SIGHUP)");
if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, APLOGNO(00271)
"sigaction(" AP_SIG_GRACEFUL_STRING ")");
#else
if (!one_process) {
@@ -487,7 +573,7 @@ static void set_signals(void)
apr_signal(SIGXCPU, SIG_DFL);
#endif /* SIGXCPU */
#ifdef SIGXFSZ
- apr_signal(SIGXFSZ, SIG_DFL);
+ apr_signal(SIGXFSZ, SIG_IGN);
#endif /* SIGXFSZ */
}
@@ -512,42 +598,31 @@ static void set_signals(void)
* Here follows a long bunch of generic server bookkeeping stuff...
*/
-int ap_graceful_stop_signalled(void)
- /* XXX this is really a bad confusing obsolete name
- * maybe it should be ap_mpm_process_exiting?
- */
-{
- /* note: for a graceful termination, listener_may_exit will be set before
- * workers_may_exit, so check listener_may_exit
- */
- return listener_may_exit;
-}
-
/*****************************************************************
* Child process main loop.
*/
-static void process_socket(apr_pool_t *p, apr_socket_t *sock, int my_child_num,
+static void process_socket(apr_thread_t *thd, apr_pool_t *p, apr_socket_t *sock,
+ int my_child_num,
int my_thread_num, apr_bucket_alloc_t *bucket_alloc)
{
conn_rec *current_conn;
long conn_id = ID_FROM_CHILD_THREAD(my_child_num, my_thread_num);
- int csd;
ap_sb_handle_t *sbh;
ap_create_sb_handle(&sbh, p, my_child_num, my_thread_num);
- apr_os_sock_get(&csd, sock);
current_conn = ap_run_create_connection(p, ap_server_conf, sock,
conn_id, sbh, bucket_alloc);
if (current_conn) {
+ current_conn->current_thread = thd;
ap_process_connection(current_conn, sock);
ap_lingering_close(current_conn);
}
}
/* requests_this_child has gone to zero or below. See if the admin coded
- "MaxRequestsPerChild 0", and keep going in that case. Doing it this way
+ "MaxConnectionsPerChild 0", and keep going in that case. Doing it this way
simplifies the hot path in worker_thread */
static void check_infinite_requests(void)
{
@@ -555,17 +630,6 @@ static void check_infinite_requests(void)
signal_threads(ST_GRACEFUL);
}
else {
- /* wow! if you're executing this code, you may have set a record.
- * either this child process has served over 2 billion requests, or
- * you're running a threaded 2.0 on a 16 bit machine.
- *
- * I'll buy pizza and beers at Apachecon for the first person to do
- * the former without cheating (dorking with INT_MAX, or running with
- * uncommitted performance patches, for example).
- *
- * for the latter case, you probably deserve a beer too. Greg Ames
- */
-
requests_this_child = INT_MAX; /* keep going */
}
}
@@ -590,7 +654,30 @@ static void dummy_signal_handler(int sig)
*/
}
-static void *listener_thread(apr_thread_t *thd, void * dummy)
+static void accept_mutex_error(const char *func, apr_status_t rv, int process_slot)
+{
+ int level = APLOG_EMERG;
+
+ if (ap_scoreboard_image->parent[process_slot].generation !=
+ ap_scoreboard_image->global->running_generation) {
+ level = APLOG_DEBUG; /* common to get these at restart time */
+ }
+ else if (requests_this_child == INT_MAX
+ || ((requests_this_child == ap_max_requests_per_child)
+ && ap_max_requests_per_child)) {
+ ap_log_error(APLOG_MARK, level, rv, ap_server_conf, APLOGNO(00272)
+ "apr_proc_mutex_%s failed "
+ "before this child process served any requests.",
+ func);
+ clean_child_exit(APEXIT_CHILDSICK);
+ }
+ ap_log_error(APLOG_MARK, level, rv, ap_server_conf, APLOGNO(00273)
+ "apr_proc_mutex_%s failed. Attempting to "
+ "shutdown process gracefully.", func);
+ signal_threads(ST_GRACEFUL);
+}
+
+static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
{
proc_info * ti = dummy;
int process_slot = ti->pid;
@@ -605,8 +692,14 @@ static void *listener_thread(apr_thread_t *thd, void * dummy)
free(ti);
- /* ### check the status */
- (void) apr_pollset_create(&pollset, num_listensocks, tpool, 0);
+ rv = apr_pollset_create(&pollset, num_listensocks, tpool, 0);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "Couldn't create pollset in thread;"
+ " check system or user limits");
+ /* let the parent decide how bad this really is */
+ clean_child_exit(APEXIT_CHILDSICK);
+ }
for (lr = ap_listeners; lr != NULL; lr = lr->next) {
apr_pollfd_t pfd = { 0 };
@@ -616,8 +709,16 @@ static void *listener_thread(apr_thread_t *thd, void * dummy)
pfd.reqevents = APR_POLLIN;
pfd.client_data = lr;
- /* ### check the status */
- (void) apr_pollset_add(pollset, &pfd);
+ rv = apr_pollset_add(pollset, &pfd);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ "Couldn't create add listener to pollset;"
+ " check system or user limits");
+ /* let the parent decide how bad this really is */
+ clean_child_exit(APEXIT_CHILDSICK);
+ }
+
+ lr->accept_func = ap_unixd_accept;
}
/* Unblock the signal used to wake this thread up, and set a handler for
@@ -659,19 +760,10 @@ static void *listener_thread(apr_thread_t *thd, void * dummy)
if ((rv = SAFE_ACCEPT(apr_proc_mutex_lock(accept_mutex)))
!= APR_SUCCESS) {
- int level = APLOG_EMERG;
- if (listener_may_exit) {
- break;
- }
- if (ap_scoreboard_image->parent[process_slot].generation !=
- ap_scoreboard_image->global->running_generation) {
- level = APLOG_DEBUG; /* common to get these at restart time */
+ if (!listener_may_exit) {
+ accept_mutex_error("lock", rv, process_slot);
}
- ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
- "apr_proc_mutex_lock failed. Attempting to shutdown "
- "process gracefully.");
- signal_threads(ST_GRACEFUL);
break; /* skip the lock release */
}
@@ -692,8 +784,7 @@ static void *listener_thread(apr_thread_t *thd, void * dummy)
/* apr_pollset_poll() will only return errors in catastrophic
* circumstances. Let's try exiting gracefully, for now. */
- ap_log_error(APLOG_MARK, APLOG_ERR, rv,
- (const server_rec *) ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
"apr_pollset_poll: (listen)");
signal_threads(ST_GRACEFUL);
}
@@ -750,19 +841,11 @@ static void *listener_thread(apr_thread_t *thd, void * dummy)
}
if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex)))
!= APR_SUCCESS) {
- int level = APLOG_EMERG;
if (listener_may_exit) {
break;
}
- if (ap_scoreboard_image->parent[process_slot].generation !=
- ap_scoreboard_image->global->running_generation) {
- level = APLOG_DEBUG; /* common to get these at restart time */
- }
- ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
- "apr_proc_mutex_unlock failed. Attempting to "
- "shutdown process gracefully.");
- signal_threads(ST_GRACEFUL);
+ accept_mutex_error("unlock", rv, process_slot);
}
if (csd != NULL) {
rv = ap_queue_push(worker_queue, csd, ptrans);
@@ -788,7 +871,7 @@ static void *listener_thread(apr_thread_t *thd, void * dummy)
ap_scoreboard_image->global->running_generation) {
level = APLOG_DEBUG; /* common to get these at restart time */
}
- ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, level, rv, ap_server_conf, APLOGNO(00274)
"apr_proc_mutex_unlock failed. Attempting to "
"shutdown process gracefully.");
signal_threads(ST_GRACEFUL);
@@ -831,7 +914,8 @@ static void * APR_THREAD_FUNC worker_thread(apr_thread_t *thd, void * dummy)
free(ti);
ap_scoreboard_image->servers[process_slot][thread_slot].pid = ap_my_pid;
- ap_scoreboard_image->servers[process_slot][thread_slot].generation = ap_my_generation;
+ ap_scoreboard_image->servers[process_slot][thread_slot].tid = apr_os_thread_current();
+ ap_scoreboard_image->servers[process_slot][thread_slot].generation = retained->my_generation;
ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_STARTING, NULL);
#ifdef HAVE_PTHREAD_KILL
@@ -891,9 +975,9 @@ worker_pop:
is_idle = 0;
worker_sockets[thread_slot] = csd;
bucket_alloc = apr_bucket_alloc_create(ptrans);
- process_socket(ptrans, csd, process_slot, thread_slot, bucket_alloc);
+ process_socket(thd, ptrans, csd, process_slot, thread_slot, bucket_alloc);
worker_sockets[thread_slot] = NULL;
- requests_this_child--; /* FIXME: should be synchronized - aaron */
+ requests_this_child--;
apr_pool_clear(ptrans);
last_ptrans = ptrans;
}
@@ -922,14 +1006,14 @@ static void create_listener_thread(thread_starter *ts)
proc_info *my_info;
apr_status_t rv;
- my_info = (proc_info *)malloc(sizeof(proc_info));
+ my_info = (proc_info *)ap_malloc(sizeof(proc_info));
my_info->pid = my_child_num;
my_info->tid = -1; /* listener thread doesn't have a thread slot */
my_info->sd = 0;
rv = apr_thread_create(&ts->listener, thread_attr, listener_thread,
my_info, pchild);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00275)
"apr_thread_create: unable to create listener thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
@@ -960,7 +1044,7 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
/* We must create the fd queues before we start up the listener
* and worker threads. */
worker_queue = apr_pcalloc(pchild, sizeof(*worker_queue));
- rv = ap_queue_init(worker_queue, ap_threads_per_child, pchild);
+ rv = ap_queue_init(worker_queue, threads_per_child, pchild);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
"ap_queue_init() failed");
@@ -968,32 +1052,27 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
}
rv = ap_queue_info_create(&worker_queue_info, pchild,
- ap_threads_per_child);
+ threads_per_child);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
"ap_queue_info_create() failed");
clean_child_exit(APEXIT_CHILDFATAL);
}
- worker_sockets = apr_pcalloc(pchild, ap_threads_per_child
+ worker_sockets = apr_pcalloc(pchild, threads_per_child
* sizeof(apr_socket_t *));
loops = prev_threads_created = 0;
while (1) {
- /* ap_threads_per_child does not include the listener thread */
- for (i = 0; i < ap_threads_per_child; i++) {
+ /* threads_per_child does not include the listener thread */
+ for (i = 0; i < threads_per_child; i++) {
int status = ap_scoreboard_image->servers[child_num_arg][i].status;
if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
continue;
}
- my_info = (proc_info *)malloc(sizeof(proc_info));
- if (my_info == NULL) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
- "malloc: out of memory");
- clean_child_exit(APEXIT_CHILDFATAL);
- }
+ my_info = (proc_info *)ap_malloc(sizeof(proc_info));
my_info->pid = my_child_num;
my_info->tid = i;
my_info->sd = 0;
@@ -1019,7 +1098,7 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
create_listener_thread(ts);
listener_started = 1;
}
- if (start_thread_may_exit || threads_created == ap_threads_per_child) {
+ if (start_thread_may_exit || threads_created == threads_per_child) {
break;
}
/* wait for previous generation to clean up an entry */
@@ -1030,7 +1109,7 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
"child %" APR_PID_T_FMT " isn't taking over "
"slots very quickly (%d of %d)",
- ap_my_pid, threads_created, ap_threads_per_child);
+ ap_my_pid, threads_created, threads_per_child);
}
prev_threads_created = threads_created;
}
@@ -1080,19 +1159,19 @@ static void join_workers(apr_thread_t *listener, apr_thread_t **threads)
++iter;
}
if (iter >= 10) {
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00276)
"the listener thread didn't exit");
}
else {
rv = apr_thread_join(&thread_rv, listener);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00277)
"apr_thread_join: unable to join listener thread");
}
}
}
- for (i = 0; i < ap_threads_per_child; i++) {
+ for (i = 0; i < threads_per_child; i++) {
if (threads[i]) { /* if we ever created this thread */
#ifdef HAVE_PTHREAD_KILL
apr_os_thread_t *worker_os_thread;
@@ -1103,7 +1182,7 @@ static void join_workers(apr_thread_t *listener, apr_thread_t **threads)
rv = apr_thread_join(&thread_rv, threads[i]);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00278)
"apr_thread_join: unable to join worker "
"thread %d",
i);
@@ -1122,7 +1201,7 @@ static void join_start_thread(apr_thread_t *start_thread_id)
*/
rv = apr_thread_join(&thread_rv, start_thread_id);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00279)
"apr_thread_join: unable to join the start "
"thread");
}
@@ -1146,15 +1225,16 @@ static void child_main(int child_num_arg)
/*stuff to do before we switch id's, so we have permissions.*/
ap_reopen_scoreboard(pchild, NULL, 0);
- rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&accept_mutex, ap_lock_fname,
+ rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&accept_mutex,
+ apr_proc_mutex_lockfile(accept_mutex),
pchild));
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(00280)
"Couldn't initialize cross-process lock in child");
clean_child_exit(APEXIT_CHILDFATAL);
}
- if (unixd_setup_child()) {
+ if (ap_run_drop_privileges(pchild, ap_server_conf)) {
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -1168,7 +1248,7 @@ static void child_main(int child_num_arg)
*/
rv = apr_setup_signal_thread();
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(00281)
"Couldn't initialize signal thread");
clean_child_exit(APEXIT_CHILDFATAL);
}
@@ -1186,14 +1266,8 @@ static void child_main(int child_num_arg)
/* clear the storage; we may not create all our threads immediately,
* and we want a 0 entry to indicate a thread which was not created
*/
- threads = (apr_thread_t **)calloc(1,
- sizeof(apr_thread_t *) * ap_threads_per_child);
- if (threads == NULL) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
- "malloc: out of memory");
- clean_child_exit(APEXIT_CHILDFATAL);
- }
-
+ threads = (apr_thread_t **)ap_calloc(1,
+ sizeof(apr_thread_t *) * threads_per_child);
ts = (thread_starter *)apr_palloc(pchild, sizeof(*ts));
apr_threadattr_create(&thread_attr, pchild);
@@ -1212,7 +1286,7 @@ static void child_main(int child_num_arg)
rv = apr_thread_create(&start_thread_id, thread_attr, start_threads,
ts, pchild);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00282)
"apr_thread_create: unable to create worker thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
@@ -1249,13 +1323,13 @@ static void child_main(int child_num_arg)
else { /* !one_process */
/* remove SIGTERM from the set of blocked signals... if one of
* the other threads in the process needs to take us down
- * (e.g., for MaxRequestsPerChild) it will send us SIGTERM
+ * (e.g., for MaxConnectionsPerChild) it will send us SIGTERM
*/
unblock_signal(SIGTERM);
apr_signal(SIGTERM, dummy_signal_handler);
/* Watch for any messages from the parent over the POD */
while (1) {
- rv = ap_mpm_pod_check(pod);
+ rv = ap_worker_pod_check(pod);
if (rv == AP_NORESTART) {
/* see if termination was triggered while we slept */
switch(terminate_mode) {
@@ -1296,24 +1370,26 @@ static int make_child(server_rec *s, int slot)
{
int pid;
- if (slot + 1 > ap_max_daemons_limit) {
- ap_max_daemons_limit = slot + 1;
+ if (slot + 1 > retained->max_daemons_limit) {
+ retained->max_daemons_limit = slot + 1;
}
if (one_process) {
set_signals();
- ap_scoreboard_image->parent[slot].pid = getpid();
+ worker_note_child_started(slot, getpid());
child_main(slot);
+ /* NOTREACHED */
}
if ((pid = fork()) == -1) {
- ap_log_error(APLOG_MARK, APLOG_ERR, errno, s,
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, APLOGNO(00283)
"fork: Unable to fork new process");
-
- /* fork didn't succeed. Fix the scoreboard or else
- * it will say SERVER_STARTING forever and ever
+ /* fork didn't succeed. There's no need to touch the scoreboard;
+ * if we were trying to replace a failed child process, then
+ * server_main_loop() marked its workers SERVER_DEAD, and if
+ * we were trying to replace a child process that exited normally,
+ * its worker_thread()s left SERVER_DEAD or SERVER_GRACEFUL behind.
*/
- ap_update_child_status_from_indexes(slot, 0, SERVER_DEAD, NULL);
/* In case system resources are maxxed out, we don't want
Apache running away with the CPU trying to fork over and
@@ -1331,35 +1407,26 @@ static int make_child(server_rec *s, int slot)
int status = bindprocessor(BINDPROCESS, (int)getpid(),
PROCESSOR_CLASS_ANY);
if (status != OK)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno,
- ap_server_conf,
- "processor unbind failed %d", status);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, errno,
+ ap_server_conf, APLOGNO(00284)
+ "processor unbind failed");
#endif
RAISE_SIGSTOP(MAKE_CHILD);
apr_signal(SIGTERM, just_die);
child_main(slot);
-
- clean_child_exit(0);
+ /* NOTREACHED */
}
/* else */
if (ap_scoreboard_image->parent[slot].pid != 0) {
/* This new child process is squatting on the scoreboard
* entry owned by an exiting child process, which cannot
* exit until all active requests complete.
- * Don't forget about this exiting child process, or we
- * won't be able to kill it if it doesn't exit by the
- * time the server is shut down.
*/
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
- "taking over scoreboard slot from %" APR_PID_T_FMT "%s",
- ap_scoreboard_image->parent[slot].pid,
- ap_scoreboard_image->parent[slot].quiescing ?
- " (quiescing)" : "");
- ap_register_extra_mpm_process(ap_scoreboard_image->parent[slot].pid);
+ worker_note_child_lost_slot(slot, pid);
}
ap_scoreboard_image->parent[slot].quiescing = 0;
- ap_scoreboard_image->parent[slot].pid = pid;
+ worker_note_child_started(slot, pid);
return 0;
}
@@ -1379,19 +1446,6 @@ static void startup_children(int number_to_start)
}
}
-
-/*
- * idle_spawn_rate is the number of children that will be spawned on the
- * next maintenance cycle if there aren't enough idle servers. It is
- * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
- * without the need to spawn.
- */
-static int idle_spawn_rate = 1;
-#ifndef MAX_SPAWN_RATE
-#define MAX_SPAWN_RATE (32)
-#endif
-static int hold_off_on_exponential_spawning;
-
static void perform_idle_server_maintenance(void)
{
int i, j;
@@ -1414,16 +1468,19 @@ static void perform_idle_server_maintenance(void)
for (i = 0; i < ap_daemons_limit; ++i) {
/* Initialization to satisfy the compiler. It doesn't know
- * that ap_threads_per_child is always > 0 */
+ * that threads_per_child is always > 0 */
int status = SERVER_DEAD;
int any_dying_threads = 0;
int any_dead_threads = 0;
int all_dead_threads = 1;
- if (i >= ap_max_daemons_limit && totally_free_length == idle_spawn_rate)
+ if (i >= retained->max_daemons_limit && totally_free_length == retained->idle_spawn_rate)
+ /* short cut if all active processes have been examined and
+ * enough empty scoreboard slots have been found
+ */
break;
ps = &ap_scoreboard_image->parent[i];
- for (j = 0; j < ap_threads_per_child; j++) {
+ for (j = 0; j < threads_per_child; j++) {
ws = &ap_scoreboard_image->servers[i][j];
status = ws->status;
@@ -1443,9 +1500,9 @@ static void perform_idle_server_maintenance(void)
*/
if (ps->pid != 0) { /* XXX just set all_dead_threads in outer for
loop if no pid? not much else matters */
- if (status <= SERVER_READY &&
+ if (status <= SERVER_READY &&
!ps->quiescing &&
- ps->generation == ap_my_generation) {
+ ps->generation == retained->my_generation) {
++idle_thread_count;
}
if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
@@ -1453,7 +1510,7 @@ static void perform_idle_server_maintenance(void)
}
}
}
- if (any_dead_threads && totally_free_length < idle_spawn_rate
+ if (any_dead_threads && totally_free_length < retained->idle_spawn_rate
&& free_length < MAX_SPAWN_RATE
&& (!ps->pid /* no process in the slot */
|| ps->quiescing)) { /* or at least one is going away */
@@ -1482,12 +1539,12 @@ static void perform_idle_server_maintenance(void)
}
}
- if (sick_child_detected) {
+ if (retained->sick_child_detected) {
if (active_thread_count > 0) {
/* some child processes appear to be working. don't kill the
* whole server.
*/
- sick_child_detected = 0;
+ retained->sick_child_detected = 0;
}
else {
/* looks like a basket case. give up.
@@ -1495,7 +1552,7 @@ static void perform_idle_server_maintenance(void)
shutdown_pending = 1;
child_fatal = 1;
ap_log_error(APLOG_MARK, APLOG_ALERT, 0,
- ap_server_conf,
+ ap_server_conf, APLOGNO(00285)
"No active workers found..."
" Apache is exiting!");
/* the child already logged the failure details */
@@ -1503,52 +1560,53 @@ static void perform_idle_server_maintenance(void)
}
}
- ap_max_daemons_limit = last_non_dead + 1;
+ retained->max_daemons_limit = last_non_dead + 1;
if (idle_thread_count > max_spare_threads) {
/* Kill off one child */
- ap_mpm_pod_signal(pod, TRUE);
- idle_spawn_rate = 1;
+ ap_worker_pod_signal(pod, TRUE);
+ retained->idle_spawn_rate = 1;
}
else if (idle_thread_count < min_spare_threads) {
/* terminate the free list */
- if (free_length == 0) {
- /* No room for more children, might warn about configuration */
- if (active_thread_count >= ap_daemons_limit * ap_threads_per_child) {
- /* no threads are "inactive" - starting, stopping, etc. - which would confuse matters */
- /* Are all threads in use? Then we're really at MaxClients */
+ if (free_length == 0) { /* scoreboard is full, can't fork */
+
+ if (active_thread_count >= ap_daemons_limit * threads_per_child) {
+ /* no threads are "inactive" - starting, stopping, etc. */
+ /* have we reached MaxRequestWorkers, or just getting close? */
if (0 == idle_thread_count) {
- /* only report this condition once */
- static int reported = 0;
-
- if (!reported) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0,
- ap_server_conf,
- "server reached MaxClients setting, consider"
- " raising the MaxClients setting");
- reported = 1;
+ if (!retained->maxclients_reported) {
+ /* only report this condition once */
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00286)
+ "server reached MaxRequestWorkers "
+ "setting, consider raising the "
+ "MaxRequestWorkers setting");
+ retained->maxclients_reported = 1;
}
} else {
- static int reported = 0;
-
- if (!reported) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0,
- ap_server_conf,
- "server is within MinSpareThreads of MaxClients, consider"
- " raising the MaxClients setting");
- reported = 1;
+ if (!retained->near_maxclients_reported) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00287)
+ "server is within MinSpareThreads of "
+ "MaxRequestWorkers, consider raising the "
+ "MaxRequestWorkers setting");
+ retained->near_maxclients_reported = 1;
}
}
}
- idle_spawn_rate = 1;
+ else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+ ap_server_conf, APLOGNO(00288)
+ "scoreboard is full, not at MaxRequestWorkers");
+ }
+ retained->idle_spawn_rate = 1;
}
else {
- if (free_length > idle_spawn_rate) {
- free_length = idle_spawn_rate;
+ if (free_length > retained->idle_spawn_rate) {
+ free_length = retained->idle_spawn_rate;
}
- if (idle_spawn_rate >= 8) {
+ if (retained->idle_spawn_rate >= 8) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0,
- ap_server_conf,
+ ap_server_conf, APLOGNO(00289)
"server seems busy, (you may need "
"to increase StartServers, ThreadsPerChild "
"or Min/MaxSpareThreads), "
@@ -1562,21 +1620,22 @@ static void perform_idle_server_maintenance(void)
/* 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;
+ if (retained->hold_off_on_exponential_spawning) {
+ --retained->hold_off_on_exponential_spawning;
}
- else if (idle_spawn_rate < MAX_SPAWN_RATE) {
- idle_spawn_rate *= 2;
+ else if (retained->idle_spawn_rate < MAX_SPAWN_RATE) {
+ retained->idle_spawn_rate *= 2;
}
}
}
else {
- idle_spawn_rate = 1;
+ retained->idle_spawn_rate = 1;
}
}
static void server_main_loop(int remaining_children_to_start)
{
+ ap_generation_t old_gen;
int child_slot;
apr_exit_why_e exitwhy;
int status, processed_status;
@@ -1584,33 +1643,49 @@ static void server_main_loop(int remaining_children_to_start)
int i;
while (!restart_pending && !shutdown_pending) {
- ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);
+ ap_wait_or_timeout(&exitwhy, &status, &pid, pconf, ap_server_conf);
if (pid.pid != -1) {
processed_status = ap_process_child_status(&pid, exitwhy, status);
+ child_slot = ap_find_child_by_pid(&pid);
if (processed_status == APEXIT_CHILDFATAL) {
- shutdown_pending = 1;
- child_fatal = 1;
- return;
+ /* fix race condition found in PR 39311
+ * A child created at the same time as a graceful happens
+ * can find the lock missing and create a fatal error.
+ * It is not fatal for the last generation to be in this state.
+ */
+ if (child_slot < 0
+ || ap_get_scoreboard_process(child_slot)->generation
+ == retained->my_generation) {
+ shutdown_pending = 1;
+ child_fatal = 1;
+ return;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, APLOGNO(00290)
+ "Ignoring fatal error in child of previous "
+ "generation (pid %ld).",
+ (long)pid.pid);
+ retained->sick_child_detected = 1;
+ }
}
else if (processed_status == APEXIT_CHILDSICK) {
/* tell perform_idle_server_maintenance to check into this
* on the next timer pop
*/
- sick_child_detected = 1;
+ retained->sick_child_detected = 1;
}
/* non-fatal death... note that it's gone in the scoreboard. */
- child_slot = find_child_by_pid(&pid);
if (child_slot >= 0) {
- for (i = 0; i < ap_threads_per_child; i++)
+ for (i = 0; i < threads_per_child; i++)
ap_update_child_status_from_indexes(child_slot, i, SERVER_DEAD,
(request_rec *) NULL);
- ap_scoreboard_image->parent[child_slot].pid = 0;
+ worker_note_child_killed(child_slot, 0, 0);
ap_scoreboard_image->parent[child_slot].quiescing = 0;
if (processed_status == APEXIT_CHILDSICK) {
/* resource shortage, minimize the fork rate */
- idle_spawn_rate = 1;
+ retained->idle_spawn_rate = 1;
}
else if (remaining_children_to_start
&& child_slot < ap_daemons_limit) {
@@ -1621,8 +1696,9 @@ static void server_main_loop(int remaining_children_to_start)
--remaining_children_to_start;
}
}
- else if (ap_unregister_extra_mpm_process(pid.pid) == 1) {
- /* handled */
+ else if (ap_unregister_extra_mpm_process(pid.pid, &old_gen) == 1) {
+ worker_note_child_killed(-1, /* already out of the scoreboard */
+ pid.pid, old_gen);
#if APR_HAS_OTHER_CHILD
}
else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH,
@@ -1630,12 +1706,12 @@ static void server_main_loop(int remaining_children_to_start)
/* handled */
#endif
}
- else if (is_graceful) {
+ else if (retained->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_WARNING, 0,
- ap_server_conf,
+ ap_server_conf, APLOGNO(00291)
"long lost child came home! (pid %ld)",
(long)pid.pid);
}
@@ -1664,128 +1740,91 @@ static void server_main_loop(int remaining_children_to_start)
}
}
-int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
+static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
int remaining_children_to_start;
apr_status_t rv;
ap_log_pid(pconf, ap_pid_fname);
- first_server_limit = server_limit;
- first_thread_limit = thread_limit;
- if (changed_limit_at_restart) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
- "WARNING: Attempt to change ServerLimit or ThreadLimit "
- "ignored during restart");
- changed_limit_at_restart = 0;
- }
-
/* Initialize cross-process accept lock */
- ap_lock_fname = apr_psprintf(_pconf, "%s.%" APR_PID_T_FMT,
- ap_server_root_relative(_pconf, ap_lock_fname),
- ap_my_pid);
-
- rv = apr_proc_mutex_create(&accept_mutex, ap_lock_fname,
- ap_accept_lock_mech, _pconf);
+ rv = ap_proc_mutex_create(&accept_mutex, NULL, AP_ACCEPT_MUTEX_TYPE, NULL,
+ s, _pconf, 0);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
- "Couldn't create accept lock");
mpm_state = AP_MPMQ_STOPPING;
- return 1;
- }
-
-#if APR_USE_SYSVSEM_SERIALIZE
- if (ap_accept_lock_mech == APR_LOCK_DEFAULT ||
- ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
-#else
- if (ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
-#endif
- rv = unixd_set_proc_mutex_perms(accept_mutex);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
- "Couldn't set permissions on cross-process lock; "
- "check User and Group directives");
- mpm_state = AP_MPMQ_STOPPING;
- return 1;
- }
+ return DONE;
}
- if (!is_graceful) {
+ if (!retained->is_graceful) {
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
mpm_state = AP_MPMQ_STOPPING;
- return 1;
+ return DONE;
}
/* fix the generation number in the global score; we just got a new,
* cleared scoreboard
*/
- ap_scoreboard_image->global->running_generation = ap_my_generation;
+ ap_scoreboard_image->global->running_generation = retained->my_generation;
}
+ restart_pending = shutdown_pending = 0;
set_signals();
/* Don't thrash... */
- if (max_spare_threads < min_spare_threads + ap_threads_per_child)
- max_spare_threads = min_spare_threads + ap_threads_per_child;
+ if (max_spare_threads < min_spare_threads + threads_per_child)
+ max_spare_threads = min_spare_threads + threads_per_child;
/* If we're doing a graceful_restart then we're going to see a lot
* of children exiting immediately when we get into the main loop
* below (because we just sent them AP_SIG_GRACEFUL). 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
+ * rapidly... and for each one that exits we may start a new one, until
+ * there are at least min_spare_threads idle threads, counting across
+ * all children. But we may be permitted to start more children 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) {
+ if (!retained->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;
+ retained->hold_off_on_exponential_spawning = 10;
}
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00292)
"%s configured -- resuming normal operations",
ap_get_server_description());
- ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00293)
"Server built: %s", ap_get_server_built());
-#ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
- "AcceptMutex: %s (default: %s)",
+ ap_log_command_line(plog, s);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00294)
+ "Accept mutex: %s (default: %s)",
apr_proc_mutex_name(accept_mutex),
apr_proc_mutex_defname());
-#endif
- restart_pending = shutdown_pending = 0;
mpm_state = AP_MPMQ_RUNNING;
server_main_loop(remaining_children_to_start);
mpm_state = AP_MPMQ_STOPPING;
- if (shutdown_pending && !is_graceful) {
+ if (shutdown_pending && !retained->is_graceful) {
/* Time to shut down:
* Kill child processes, tell them to call child_exit, etc...
*/
- ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
- ap_reclaim_child_processes(1); /* Start with SIGTERM */
+ ap_worker_pod_killpg(pod, ap_daemons_limit, FALSE);
+ ap_reclaim_child_processes(1, /* Start with SIGTERM */
+ worker_note_child_killed);
if (!child_fatal) {
/* cleanup pid file on normal shutdown */
- const char *pidfile = NULL;
- pidfile = ap_server_root_relative (pconf, ap_pid_fname);
- if ( pidfile != NULL && unlink(pidfile) == 0)
- ap_log_error(APLOG_MARK, APLOG_INFO, 0,
- ap_server_conf,
- "removed PID file %s (pid=%" APR_PID_T_FMT ")",
- pidfile, getpid());
-
+ ap_remove_pid(pconf, ap_pid_fname);
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0,
- ap_server_conf, "caught SIGTERM, shutting down");
+ ap_server_conf, APLOGNO(00295) "caught SIGTERM, shutting down");
}
- return 1;
+ return DONE;
} else if (shutdown_pending) {
/* Time to gracefully shut down:
* Kill child processes, tell them to call child_exit, etc...
@@ -1796,20 +1835,13 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
/* Close our listeners, and then ask our children to do same */
ap_close_listeners();
- ap_mpm_pod_killpg(pod, ap_daemons_limit, TRUE);
- ap_relieve_child_processes();
+ ap_worker_pod_killpg(pod, ap_daemons_limit, TRUE);
+ ap_relieve_child_processes(worker_note_child_killed);
if (!child_fatal) {
/* cleanup pid file on normal shutdown */
- const char *pidfile = NULL;
- pidfile = ap_server_root_relative (pconf, ap_pid_fname);
- if ( pidfile != NULL && unlink(pidfile) == 0)
- ap_log_error(APLOG_MARK, APLOG_INFO, 0,
- ap_server_conf,
- "removed PID file %s (pid=%" APR_PID_T_FMT ")",
- pidfile, getpid());
-
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_remove_pid(pconf, ap_pid_fname);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00296)
"caught " AP_SIG_GRACEFUL_STOP_STRING
", shutting down gracefully");
}
@@ -1826,7 +1858,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
apr_sleep(apr_time_from_sec(1));
/* Relieve any children which have now exited */
- ap_relieve_child_processes();
+ ap_relieve_child_processes(worker_note_child_killed);
active_children = 0;
for (index = 0; index < ap_daemons_limit; ++index) {
@@ -1843,10 +1875,10 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
* way, try and make sure that all of our processes are
* really dead.
*/
- ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
- ap_reclaim_child_processes(1);
+ ap_worker_pod_killpg(pod, ap_daemons_limit, FALSE);
+ ap_reclaim_child_processes(1, worker_note_child_killed);
- return 1;
+ return DONE;
}
/* we've been told to restart */
@@ -1854,21 +1886,21 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
if (one_process) {
/* not worth thinking about */
- return 1;
+ return DONE;
}
/* advance to the next generation */
/* XXX: we really need to make sure this new generation number isn't in
* use by any of the children.
*/
- ++ap_my_generation;
- ap_scoreboard_image->global->running_generation = ap_my_generation;
+ ++retained->my_generation;
+ ap_scoreboard_image->global->running_generation = retained->my_generation;
- if (is_graceful) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ if (retained->is_graceful) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00297)
AP_SIG_GRACEFUL_STRING " received. Doing graceful restart");
/* wake up the children...time to die. But we'll have more soon */
- ap_mpm_pod_killpg(pod, ap_daemons_limit, TRUE);
+ ap_worker_pod_killpg(pod, ap_daemons_limit, TRUE);
/* This is mostly for debugging... so that we know what is still
@@ -1881,14 +1913,15 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
* and a SIGHUP, we may as well use the same signal, because some user
* pthreads are stealing signals from us left and right.
*/
- ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
+ ap_worker_pod_killpg(pod, ap_daemons_limit, FALSE);
- ap_reclaim_child_processes(1); /* Start with SIGTERM */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
+ ap_reclaim_child_processes(1, /* Start with SIGTERM */
+ worker_note_child_killed);
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00298)
"SIGHUP received. Attempting to restart");
}
- return 0;
+ return OK;
}
/* This really should be a post_config hook, but the error log is already
@@ -1896,21 +1929,30 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
*/
static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
+ int startup = 0;
+ int level_flags = 0;
apr_status_t rv;
pconf = p;
- ap_server_conf = s;
+
+ /* the reverse of pre_config, we want this only the first time around */
+ if (retained->module_loads == 1) {
+ startup = 1;
+ level_flags |= APLOG_STARTUP;
+ }
if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
- ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0,
- NULL, "no listening sockets available, shutting down");
+ ap_log_error(APLOG_MARK, APLOG_ALERT | level_flags, 0,
+ (startup ? NULL : s),
+ "no listening sockets available, shutting down");
return DONE;
}
if (!one_process) {
- if ((rv = ap_mpm_pod_open(pconf, &pod))) {
- ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_STARTUP, rv, NULL,
- "Could not open pipe-of-death.");
+ if ((rv = ap_worker_pod_open(pconf, &pod))) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | level_flags, rv,
+ (startup ? NULL : s),
+ "could not open pipe-of-death");
return DONE;
}
}
@@ -1920,50 +1962,12 @@ static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp)
{
- static int restart_num = 0;
int no_detach, debug, foreground;
- ap_directive_t *pdir;
- ap_directive_t *max_clients = NULL;
apr_status_t rv;
+ const char *userdata_key = "mpm_worker_module";
mpm_state = AP_MPMQ_STARTING;
- /* make sure that "ThreadsPerChild" gets set before "MaxClients" */
- for (pdir = ap_conftree; pdir != NULL; pdir = pdir->next) {
- if (strncasecmp(pdir->directive, "ThreadsPerChild", 15) == 0) {
- if (!max_clients) {
- break; /* we're in the clear, got ThreadsPerChild first */
- }
- else {
- /* now to swap the data */
- ap_directive_t temp;
-
- temp.directive = pdir->directive;
- temp.args = pdir->args;
- /* Make sure you don't change 'next', or you may get loops! */
- /* XXX: first_child, parent, and data can never be set
- * for these directives, right? -aaron */
- temp.filename = pdir->filename;
- temp.line_num = pdir->line_num;
-
- pdir->directive = max_clients->directive;
- pdir->args = max_clients->args;
- pdir->filename = max_clients->filename;
- pdir->line_num = max_clients->line_num;
-
- max_clients->directive = temp.directive;
- max_clients->args = temp.args;
- max_clients->filename = temp.filename;
- max_clients->line_num = temp.line_num;
- break;
- }
- }
- else if (!max_clients
- && strncasecmp(pdir->directive, "MaxClients", 10) == 0) {
- max_clients = pdir;
- }
- }
-
debug = ap_exists_config_define("DEBUG");
if (debug) {
@@ -1976,56 +1980,299 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
foreground = ap_exists_config_define("FOREGROUND");
}
- /* sigh, want this only the second time around */
- if (restart_num++ == 1) {
- is_graceful = 0;
+ ap_mutex_register(pconf, AP_ACCEPT_MUTEX_TYPE, NULL, APR_LOCK_DEFAULT, 0);
+ /* sigh, want this only the second time around */
+ retained = ap_retained_data_get(userdata_key);
+ if (!retained) {
+ retained = ap_retained_data_create(userdata_key, sizeof(*retained));
+ retained->max_daemons_limit = -1;
+ retained->idle_spawn_rate = 1;
+ }
+ ++retained->module_loads;
+ if (retained->module_loads == 2) {
if (!one_process && !foreground) {
+ /* before we detach, setup crash handlers to log to errorlog */
+ ap_fatal_signal_setup(ap_server_conf, pconf);
rv = apr_proc_detach(no_detach ? APR_PROC_DETACH_FOREGROUND
: APR_PROC_DETACH_DAEMONIZE);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, APLOGNO(00299)
"apr_proc_detach failed");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
- parent_pid = ap_my_pid = getpid();
}
- unixd_pre_config(ptemp);
+ parent_pid = ap_my_pid = getpid();
+
ap_listen_pre_config();
ap_daemons_to_start = DEFAULT_START_DAEMON;
min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
max_spare_threads = DEFAULT_MAX_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
+ server_limit = DEFAULT_SERVER_LIMIT;
+ thread_limit = DEFAULT_THREAD_LIMIT;
ap_daemons_limit = server_limit;
- ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
- ap_pid_fname = DEFAULT_PIDLOG;
- ap_lock_fname = DEFAULT_LOCKFILE;
- ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
+ threads_per_child = DEFAULT_THREADS_PER_CHILD;
+ max_workers = ap_daemons_limit * threads_per_child;
ap_extended_status = 0;
-#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
- ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-#endif
- apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+ return OK;
+}
+
+static int worker_check_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ int startup = 0;
+
+ /* the reverse of pre_config, we want this only the first time around */
+ if (retained->module_loads == 1) {
+ startup = 1;
+ }
+
+ if (server_limit > MAX_SERVER_LIMIT) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00300)
+ "WARNING: ServerLimit of %d exceeds compile-time "
+ "limit of", server_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d servers, decreasing to %d.",
+ MAX_SERVER_LIMIT, MAX_SERVER_LIMIT);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00301)
+ "ServerLimit of %d exceeds compile-time limit "
+ "of %d, decreasing to match",
+ server_limit, MAX_SERVER_LIMIT);
+ }
+ server_limit = MAX_SERVER_LIMIT;
+ }
+ else if (server_limit < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00302)
+ "WARNING: ServerLimit of %d not allowed, "
+ "increasing to 1.", server_limit);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00303)
+ "ServerLimit of %d not allowed, increasing to 1",
+ server_limit);
+ }
+ server_limit = 1;
+ }
+
+ /* you cannot change ServerLimit across a restart; ignore
+ * any such attempts
+ */
+ if (!retained->first_server_limit) {
+ retained->first_server_limit = server_limit;
+ }
+ else if (server_limit != retained->first_server_limit) {
+ /* don't need a startup console version here */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00304)
+ "changing ServerLimit to %d from original value of %d "
+ "not allowed during restart",
+ server_limit, retained->first_server_limit);
+ server_limit = retained->first_server_limit;
+ }
+
+ if (thread_limit > MAX_THREAD_LIMIT) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00305)
+ "WARNING: ThreadLimit of %d exceeds compile-time "
+ "limit of", thread_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d threads, decreasing to %d.",
+ MAX_THREAD_LIMIT, MAX_THREAD_LIMIT);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00306)
+ "ThreadLimit of %d exceeds compile-time limit "
+ "of %d, decreasing to match",
+ thread_limit, MAX_THREAD_LIMIT);
+ }
+ thread_limit = MAX_THREAD_LIMIT;
+ }
+ else if (thread_limit < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00307)
+ "WARNING: ThreadLimit of %d not allowed, "
+ "increasing to 1.", thread_limit);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00308)
+ "ThreadLimit of %d not allowed, increasing to 1",
+ thread_limit);
+ }
+ thread_limit = 1;
+ }
+
+ /* you cannot change ThreadLimit across a restart; ignore
+ * any such attempts
+ */
+ if (!retained->first_thread_limit) {
+ retained->first_thread_limit = thread_limit;
+ }
+ else if (thread_limit != retained->first_thread_limit) {
+ /* don't need a startup console version here */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00309)
+ "changing ThreadLimit to %d from original value of %d "
+ "not allowed during restart",
+ thread_limit, retained->first_thread_limit);
+ thread_limit = retained->first_thread_limit;
+ }
+
+ if (threads_per_child > thread_limit) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00310)
+ "WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
+ "of", threads_per_child);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d threads, decreasing to %d.",
+ thread_limit, thread_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " To increase, please see the ThreadLimit "
+ "directive.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00311)
+ "ThreadsPerChild of %d exceeds ThreadLimit "
+ "of %d, decreasing to match",
+ threads_per_child, thread_limit);
+ }
+ threads_per_child = thread_limit;
+ }
+ else if (threads_per_child < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00312)
+ "WARNING: ThreadsPerChild of %d not allowed, "
+ "increasing to 1.", threads_per_child);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00313)
+ "ThreadsPerChild of %d not allowed, increasing to 1",
+ threads_per_child);
+ }
+ threads_per_child = 1;
+ }
+
+ if (max_workers < threads_per_child) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00314)
+ "WARNING: MaxRequestWorkers of %d is less than "
+ "ThreadsPerChild of", max_workers);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " %d, increasing to %d. MaxRequestWorkers must be at "
+ "least as large",
+ threads_per_child, threads_per_child);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " as the number of threads in a single server.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00315)
+ "MaxRequestWorkers of %d is less than ThreadsPerChild "
+ "of %d, increasing to match",
+ max_workers, threads_per_child);
+ }
+ max_workers = threads_per_child;
+ }
+
+ ap_daemons_limit = max_workers / threads_per_child;
+
+ if (max_workers % threads_per_child) {
+ int tmp_max_workers = ap_daemons_limit * threads_per_child;
+
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00316)
+ "WARNING: MaxRequestWorkers of %d is not an integer "
+ "multiple of", max_workers);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " ThreadsPerChild of %d, decreasing to nearest "
+ "multiple %d,", threads_per_child,
+ tmp_max_workers);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " for a maximum of %d servers.",
+ ap_daemons_limit);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00317)
+ "MaxRequestWorkers of %d is not an integer multiple of "
+ "ThreadsPerChild of %d, decreasing to nearest "
+ "multiple %d", max_workers, threads_per_child,
+ tmp_max_workers);
+ }
+ max_workers = tmp_max_workers;
+ }
+
+ if (ap_daemons_limit > server_limit) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00318)
+ "WARNING: MaxRequestWorkers of %d would require %d "
+ "servers and ", max_workers, ap_daemons_limit);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " would exceed ServerLimit of %d, decreasing to %d.",
+ server_limit, server_limit * threads_per_child);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " To increase, please see the ServerLimit "
+ "directive.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00319)
+ "MaxRequestWorkers of %d would require %d servers and "
+ "exceed ServerLimit of %d, decreasing to %d",
+ max_workers, ap_daemons_limit, server_limit,
+ server_limit * threads_per_child);
+ }
+ ap_daemons_limit = server_limit;
+ }
+
+ /* ap_daemons_to_start > ap_daemons_limit checked in worker_run() */
+ if (ap_daemons_to_start < 0) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00320)
+ "WARNING: StartServers of %d not allowed, "
+ "increasing to 1.", ap_daemons_to_start);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00321)
+ "StartServers of %d not allowed, increasing to 1",
+ ap_daemons_to_start);
+ }
+ ap_daemons_to_start = 1;
+ }
+
+ if (min_spare_threads < 1) {
+ if (startup) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL, APLOGNO(00322)
+ "WARNING: MinSpareThreads of %d not allowed, "
+ "increasing to 1", min_spare_threads);
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " to avoid almost certain server failure.");
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP, 0, NULL,
+ " Please read the documentation.");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(00323)
+ "MinSpareThreads of %d not allowed, increasing to 1",
+ min_spare_threads);
+ }
+ min_spare_threads = 1;
+ }
+
+ /* max_spare_threads < min_spare_threads + threads_per_child
+ * checked in worker_run()
+ */
return OK;
}
static void worker_hooks(apr_pool_t *p)
{
- /* The worker open_logs phase must run before the core's, or stderr
+ /* Our open_logs hook function must run before the core's, or stderr
* will be redirected to a file, and the messages won't print to the
* console.
*/
static const char *const aszSucc[] = {"core.c", NULL};
one_process = 0;
- ap_hook_open_logs(worker_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
+ ap_hook_open_logs(worker_open_logs, NULL, aszSucc, APR_HOOK_REALLY_FIRST);
/* we need to set the MPM state before other pre-config hooks use MPM query
* to retrieve it, so register as REALLY_FIRST
*/
ap_hook_pre_config(worker_pre_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
+ ap_hook_check_config(worker_check_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm(worker_run, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_query(worker_query, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_mpm_get_name(worker_get_name, NULL, NULL, APR_HOOK_MIDDLE);
}
static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy,
@@ -2049,16 +2296,6 @@ static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy,
}
min_spare_threads = atoi(arg);
- if (min_spare_threads <= 0) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: detected MinSpareThreads set to non-positive.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Resetting to 1 to avoid almost certain Apache failure.");
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Please read the documentation.");
- min_spare_threads = 1;
- }
-
return NULL;
}
@@ -2074,63 +2311,19 @@ static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy,
return NULL;
}
-static const char *set_max_clients (cmd_parms *cmd, void *dummy,
+static const char *set_max_workers (cmd_parms *cmd, void *dummy,
const char *arg)
{
- int max_clients;
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
-
- /* It is ok to use ap_threads_per_child here because we are
- * sure that it gets set before MaxClients in the pre_config stage. */
- max_clients = atoi(arg);
- if (max_clients < ap_threads_per_child) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxClients (%d) must be at least as large",
- max_clients);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " as ThreadsPerChild (%d). Automatically",
- ap_threads_per_child);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " increasing MaxClients to %d.",
- ap_threads_per_child);
- max_clients = ap_threads_per_child;
- }
- ap_daemons_limit = max_clients / ap_threads_per_child;
- if ((max_clients > 0) && (max_clients % ap_threads_per_child)) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxClients (%d) is not an integer multiple",
- max_clients);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " of ThreadsPerChild (%d), lowering MaxClients to %d",
- ap_threads_per_child,
- ap_daemons_limit * ap_threads_per_child);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " for a maximum of %d child processes,",
- ap_daemons_limit);
- max_clients = ap_daemons_limit * ap_threads_per_child;
- }
- if (ap_daemons_limit > server_limit) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: MaxClients of %d would require %d servers,",
- max_clients, ap_daemons_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " and would exceed the ServerLimit value of %d.",
- server_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " Automatically lowering MaxClients to %d. To increase,",
- server_limit * ap_threads_per_child);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " please see the ServerLimit directive.");
- ap_daemons_limit = server_limit;
- }
- else if (ap_daemons_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require MaxClients > 0, setting to 1");
- ap_daemons_limit = 1;
+ if (!strcasecmp(cmd->cmd->name, "MaxClients")) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(00324)
+ "MaxClients is deprecated, use MaxRequestWorkers "
+ "instead.");
}
+ max_workers = atoi(arg);
return NULL;
}
@@ -2142,109 +2335,33 @@ static const char *set_threads_per_child (cmd_parms *cmd, void *dummy,
return err;
}
- ap_threads_per_child = atoi(arg);
- if (ap_threads_per_child > thread_limit) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
- "value of %d", ap_threads_per_child,
- thread_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "threads, lowering ThreadsPerChild to %d. To increase, please"
- " see the", thread_limit);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " ThreadLimit directive.");
- ap_threads_per_child = thread_limit;
- }
- else if (ap_threads_per_child < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require ThreadsPerChild > 0, setting to 1");
- ap_threads_per_child = 1;
- }
+ threads_per_child = atoi(arg);
return NULL;
}
static const char *set_server_limit (cmd_parms *cmd, void *dummy, const char *arg)
{
- int tmp_server_limit;
-
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
- tmp_server_limit = atoi(arg);
- /* you cannot change ServerLimit across a restart; ignore
- * any such attempts
- */
- if (first_server_limit &&
- tmp_server_limit != server_limit) {
- /* how do we log a message? the error log is a bit bucket at this
- * point; we'll just have to set a flag so that ap_mpm_run()
- * logs a warning later
- */
- changed_limit_at_restart = 1;
- return NULL;
- }
- server_limit = tmp_server_limit;
-
- if (server_limit > MAX_SERVER_LIMIT) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: ServerLimit of %d exceeds compile time limit "
- "of %d servers,", server_limit, MAX_SERVER_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering ServerLimit to %d.", MAX_SERVER_LIMIT);
- server_limit = MAX_SERVER_LIMIT;
- }
- else if (server_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require ServerLimit > 0, setting to 1");
- server_limit = 1;
- }
+ server_limit = atoi(arg);
return NULL;
}
static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg)
{
- int tmp_thread_limit;
-
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
- tmp_thread_limit = atoi(arg);
- /* you cannot change ThreadLimit across a restart; ignore
- * any such attempts
- */
- if (first_thread_limit &&
- tmp_thread_limit != thread_limit) {
- /* how do we log a message? the error log is a bit bucket at this
- * point; we'll just have to set a flag so that ap_mpm_run()
- * logs a warning later
- */
- changed_limit_at_restart = 1;
- return NULL;
- }
- thread_limit = tmp_thread_limit;
-
- if (thread_limit > MAX_THREAD_LIMIT) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: ThreadLimit of %d exceeds compile time limit "
- "of %d servers,", thread_limit, MAX_THREAD_LIMIT);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- " lowering ThreadLimit to %d.", MAX_THREAD_LIMIT);
- thread_limit = MAX_THREAD_LIMIT;
- }
- else if (thread_limit < 1) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "WARNING: Require ThreadLimit > 0, setting to 1");
- thread_limit = 1;
- }
+ thread_limit = atoi(arg);
return NULL;
}
static const command_rec worker_cmds[] = {
-UNIX_DAEMON_COMMANDS,
LISTEN_COMMANDS,
AP_INIT_TAKE1("StartServers", set_daemons_to_start, NULL, RSRC_CONF,
"Number of child processes launched at server startup"),
@@ -2252,8 +2369,10 @@ AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
"Minimum number of idle threads, to handle request spikes"),
AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
"Maximum number of idle threads"),
-AP_INIT_TAKE1("MaxClients", set_max_clients, NULL, RSRC_CONF,
+AP_INIT_TAKE1("MaxRequestWorkers", set_max_workers, NULL, RSRC_CONF,
"Maximum number of threads alive at the same time"),
+AP_INIT_TAKE1("MaxClients", set_max_workers, NULL, RSRC_CONF,
+ "Deprecated name of MaxRequestWorkers"),
AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
"Number of threads each child creates"),
AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF,
@@ -2264,9 +2383,9 @@ AP_GRACEFUL_SHUTDOWN_TIMEOUT_COMMAND,
{ NULL }
};
-module AP_MODULE_DECLARE_DATA mpm_worker_module = {
+AP_DECLARE_MODULE(mpm_worker) = {
MPM20_MODULE_STUFF,
- ap_mpm_rewrite_args, /* hook to run before apache parses args */
+ NULL, /* hook to run before apache parses args */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
diff --git a/server/mpm_common.c b/server/mpm_common.c
index 754fc643..0cd65e5c 100644
--- a/server/mpm_common.c
+++ b/server/mpm_common.c
@@ -37,17 +37,16 @@
#include "httpd.h"
#include "http_config.h"
+#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
-#include "mpm.h"
#include "mpm_common.h"
+#include "mod_core.h"
#include "ap_mpm.h"
#include "ap_listen.h"
-#include "mpm_default.h"
+#include "util_mutex.h"
-#ifdef AP_MPM_WANT_SET_SCOREBOARD
#include "scoreboard.h"
-#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
@@ -59,320 +58,111 @@
#include <unistd.h>
#endif
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
#if AP_ENABLE_EXCEPTION_HOOK
APR_HOOK_STRUCT(
APR_HOOK_LINK(fatal_exception)
APR_HOOK_LINK(monitor)
+ APR_HOOK_LINK(drop_privileges)
+ APR_HOOK_LINK(mpm)
+ APR_HOOK_LINK(mpm_query)
+ APR_HOOK_LINK(mpm_register_timed_callback)
+ APR_HOOK_LINK(mpm_get_name)
+ APR_HOOK_LINK(end_generation)
+ APR_HOOK_LINK(child_status)
)
AP_IMPLEMENT_HOOK_RUN_ALL(int, fatal_exception,
(ap_exception_info_t *ei), (ei), OK, DECLINED)
#else
APR_HOOK_STRUCT(
APR_HOOK_LINK(monitor)
+ APR_HOOK_LINK(drop_privileges)
+ APR_HOOK_LINK(mpm)
+ APR_HOOK_LINK(mpm_query)
+ APR_HOOK_LINK(mpm_register_timed_callback)
+ APR_HOOK_LINK(mpm_get_name)
+ APR_HOOK_LINK(end_generation)
+ APR_HOOK_LINK(child_status)
)
#endif
AP_IMPLEMENT_HOOK_RUN_ALL(int, monitor,
- (apr_pool_t *p), (p), OK, DECLINED)
-
-
-#ifdef AP_MPM_WANT_RECLAIM_CHILD_PROCESSES
-
-typedef enum {DO_NOTHING, SEND_SIGTERM, SEND_SIGKILL, GIVEUP} action_t;
-
-typedef struct extra_process_t {
- struct extra_process_t *next;
- pid_t pid;
-} extra_process_t;
-
-static extra_process_t *extras;
-
-void ap_register_extra_mpm_process(pid_t pid)
-{
- extra_process_t *p = (extra_process_t *)malloc(sizeof(extra_process_t));
-
- p->next = extras;
- p->pid = pid;
- extras = p;
-}
-
-int ap_unregister_extra_mpm_process(pid_t pid)
-{
- extra_process_t *cur = extras;
- extra_process_t *prev = NULL;
-
- while (cur && cur->pid != pid) {
- prev = cur;
- cur = cur->next;
- }
-
- if (cur) {
- if (prev) {
- prev->next = cur->next;
- }
- else {
- extras = cur->next;
- }
- free(cur);
- return 1; /* found */
- }
- else {
- /* we don't know about any such process */
- return 0;
- }
-}
-
-static int reclaim_one_pid(pid_t pid, action_t action)
-{
- apr_proc_t proc;
- apr_status_t waitret;
-
- /* Ensure pid sanity. */
- if (pid < 1) {
- return 1;
- }
-
- proc.pid = pid;
- waitret = apr_proc_wait(&proc, NULL, NULL, APR_NOWAIT);
- if (waitret != APR_CHILD_NOTDONE) {
- return 1;
- }
-
- switch(action) {
- case DO_NOTHING:
- break;
-
- case SEND_SIGTERM:
- /* ok, now it's being annoying */
- ap_log_error(APLOG_MARK, APLOG_WARNING,
- 0, ap_server_conf,
- "child process %" APR_PID_T_FMT
- " still did not exit, "
- "sending a SIGTERM",
- pid);
- kill(pid, SIGTERM);
- break;
-
- case SEND_SIGKILL:
- ap_log_error(APLOG_MARK, APLOG_ERR,
- 0, ap_server_conf,
- "child process %" APR_PID_T_FMT
- " still did not exit, "
- "sending a SIGKILL",
- pid);
-#ifndef BEOS
- kill(pid, SIGKILL);
-#else
- /* sending a SIGKILL kills the entire team on BeOS, and as
- * httpd thread is part of that team it removes any chance
- * of ever doing a restart. To counter this I'm changing to
- * use a kinder, gentler way of killing a specific thread
- * that is just as effective.
- */
- kill_thread(pid);
-#endif
- break;
-
- case GIVEUP:
- /* 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_ERR,
- 0, ap_server_conf,
- "could not make child process %" APR_PID_T_FMT
- " exit, "
- "attempting to continue anyway",
- pid);
- break;
- }
-
- return 0;
-}
-
-void ap_reclaim_child_processes(int terminate)
-{
- apr_time_t waittime = 1024 * 16;
- int i;
- extra_process_t *cur_extra;
- int not_dead_yet;
- int max_daemons;
- apr_time_t starttime = apr_time_now();
- /* this table of actions and elapsed times tells what action is taken
- * at which elapsed time from starting the reclaim
- */
- struct {
- action_t action;
- apr_time_t action_time;
- } action_table[] = {
- {DO_NOTHING, 0}, /* dummy entry for iterations where we reap
- * children but take no action against
- * stragglers
- */
- {SEND_SIGTERM, apr_time_from_sec(3)},
- {SEND_SIGTERM, apr_time_from_sec(5)},
- {SEND_SIGTERM, apr_time_from_sec(7)},
- {SEND_SIGKILL, apr_time_from_sec(9)},
- {GIVEUP, apr_time_from_sec(10)}
- };
- int cur_action; /* index of action we decided to take this
- * iteration
- */
- int next_action = 1; /* index of first real action */
-
- ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons);
-
- do {
- apr_sleep(waittime);
- /* don't let waittime get longer than 1 second; otherwise, we don't
- * react quickly to the last child exiting, and taking action can
- * be delayed
- */
- waittime = waittime * 4;
- if (waittime > apr_time_from_sec(1)) {
- waittime = apr_time_from_sec(1);
- }
-
- /* see what action to take, if any */
- if (action_table[next_action].action_time <= apr_time_now() - starttime) {
- cur_action = next_action;
- ++next_action;
- }
- else {
- cur_action = 0; /* nothing to do */
- }
-
- /* now see who is done */
- not_dead_yet = 0;
- for (i = 0; i < max_daemons; ++i) {
- pid_t pid = MPM_CHILD_PID(i);
-
- if (pid == 0) {
- continue; /* not every scoreboard entry is in use */
- }
-
- if (reclaim_one_pid(pid, action_table[cur_action].action)) {
- MPM_NOTE_CHILD_KILLED(i);
- }
- else {
- ++not_dead_yet;
- }
- }
-
- cur_extra = extras;
- while (cur_extra) {
- extra_process_t *next = cur_extra->next;
-
- if (reclaim_one_pid(cur_extra->pid, action_table[cur_action].action)) {
- AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid));
- }
- else {
- ++not_dead_yet;
- }
- cur_extra = next;
- }
-#if APR_HAS_OTHER_CHILD
- apr_proc_other_child_refresh_all(APR_OC_REASON_RESTART);
-#endif
-
- } while (not_dead_yet > 0 &&
- action_table[cur_action].action != GIVEUP);
-}
-
-void ap_relieve_child_processes(void)
-{
- int i;
- extra_process_t *cur_extra;
- int max_daemons;
-
- ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons);
-
- /* now see who is done */
- for (i = 0; i < max_daemons; ++i) {
- pid_t pid = MPM_CHILD_PID(i);
-
- if (pid == 0) {
- continue; /* not every scoreboard entry is in use */
- }
-
- if (reclaim_one_pid(pid, DO_NOTHING)) {
- MPM_NOTE_CHILD_KILLED(i);
- }
- }
-
- cur_extra = extras;
- while (cur_extra) {
- extra_process_t *next = cur_extra->next;
+ (apr_pool_t *p, server_rec *s), (p, s), OK, DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int, drop_privileges,
+ (apr_pool_t * pchild, server_rec * s),
+ (pchild, s), OK, DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm,
+ (apr_pool_t *pconf, apr_pool_t *plog, server_rec *s),
+ (pconf, plog, s), DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm_query,
+ (int query_code, int *result, apr_status_t *_rv),
+ (query_code, result, _rv), DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_timed_callback,
+ (apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton),
+ (t, cbfn, baton), APR_ENOTIMPL)
+AP_IMPLEMENT_HOOK_VOID(end_generation,
+ (server_rec *s, ap_generation_t gen),
+ (s, gen))
+AP_IMPLEMENT_HOOK_VOID(child_status,
+ (server_rec *s, pid_t pid, ap_generation_t gen, int slot, mpm_child_status status),
+ (s,pid,gen,slot,status))
+
+/* hooks with no args are implemented last, after disabling APR hook probes */
+#if defined(APR_HOOK_PROBES_ENABLED)
+#undef APR_HOOK_PROBES_ENABLED
+#undef APR_HOOK_PROBE_ENTRY
+#define APR_HOOK_PROBE_ENTRY(ud,ns,name,args)
+#undef APR_HOOK_PROBE_RETURN
+#define APR_HOOK_PROBE_RETURN(ud,ns,name,rv,args)
+#undef APR_HOOK_PROBE_INVOKE
+#define APR_HOOK_PROBE_INVOKE(ud,ns,name,src,args)
+#undef APR_HOOK_PROBE_COMPLETE
+#define APR_HOOK_PROBE_COMPLETE(ud,ns,name,src,rv,args)
+#undef APR_HOOK_INT_DCL_UD
+#define APR_HOOK_INT_DCL_UD
+#endif
+AP_IMPLEMENT_HOOK_RUN_FIRST(const char *, mpm_get_name,
+ (void),
+ (), NULL)
+
+typedef struct mpm_gen_info_t {
+ APR_RING_ENTRY(mpm_gen_info_t) link;
+ int gen; /* which gen? */
+ int active; /* number of active processes */
+ int done; /* gen finished? (whether or not active processes) */
+} mpm_gen_info_t;
+
+APR_RING_HEAD(mpm_gen_info_head_t, mpm_gen_info_t);
+static struct mpm_gen_info_head_t *geninfo, *unused_geninfo;
+static int gen_head_init; /* yuck */
+
+/* variables representing config directives implemented here */
+const char *ap_pid_fname;
+int ap_max_requests_per_child;
+char ap_coredump_dir[MAX_STRING_LEN];
+int ap_coredumpdir_configured;
+int ap_graceful_shutdown_timeout;
+AP_DECLARE_DATA apr_uint32_t ap_max_mem_free;
+apr_size_t ap_thread_stacksize;
- if (reclaim_one_pid(cur_extra->pid, DO_NOTHING)) {
- AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid));
- }
- cur_extra = next;
- }
-}
+#define ALLOCATOR_MAX_FREE_DEFAULT (2048*1024)
-/* Before sending the signal to the pid this function verifies that
- * the pid is a member of the current process group; either using
- * apr_proc_wait(), where waitpid() guarantees to fail for non-child
- * processes; or by using getpgid() directly, if available. */
-apr_status_t ap_mpm_safe_kill(pid_t pid, int sig)
+/* Set defaults for config directives implemented here. This is
+ * called from core's pre-config hook, so MPMs which need to override
+ * one of these should run their pre-config hook after that of core.
+ */
+void mpm_common_pre_config(apr_pool_t *pconf)
{
-#ifndef HAVE_GETPGID
- apr_proc_t proc;
- apr_status_t rv;
- apr_exit_why_e why;
- int status;
-
- /* Ensure pid sanity */
- if (pid < 1) {
- return APR_EINVAL;
- }
-
- proc.pid = pid;
- rv = apr_proc_wait(&proc, &status, &why, APR_NOWAIT);
- if (rv == APR_CHILD_DONE) {
-#ifdef AP_MPM_WANT_PROCESS_CHILD_STATUS
- /* The child already died - log the termination status if
- * necessary: */
- ap_process_child_status(&proc, why, status);
-#endif
- return APR_EINVAL;
- }
- else if (rv != APR_CHILD_NOTDONE) {
- /* The child is already dead and reaped, or was a bogus pid -
- * log this either way. */
- ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf,
- "cannot send signal %d to pid %ld (non-child or "
- "already dead)", sig, (long)pid);
- return APR_EINVAL;
- }
-#else
- pid_t pg;
-
- /* Ensure pid sanity. */
- if (pid < 1) {
- return APR_EINVAL;
- }
-
- pg = getpgid(pid);
- if (pg == -1) {
- /* Process already dead... */
- return errno;
- }
-
- if (pg != getpgrp()) {
- ap_log_error(APLOG_MARK, APLOG_ALERT, 0, ap_server_conf,
- "refusing to send signal %d to pid %ld outside "
- "process group", sig, (long)pid);
- return APR_EINVAL;
- }
-#endif
-
- return kill(pid, sig) ? errno : APR_SUCCESS;
+ ap_pid_fname = DEFAULT_PIDLOG;
+ ap_max_requests_per_child = 0; /* unlimited */
+ apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+ ap_coredumpdir_configured = 0;
+ ap_graceful_shutdown_timeout = 0; /* unlimited */
+ ap_max_mem_free = ALLOCATOR_MAX_FREE_DEFAULT;
+ ap_thread_stacksize = 0; /* use system default */
}
-#endif /* AP_MPM_WANT_RECLAIM_CHILD_PROCESSES */
-
-#ifdef AP_MPM_WANT_WAIT_OR_TIMEOUT
/* number of calls to wait_or_timeout between writable probes */
#ifndef INTERVAL_OF_WRITABLE_PROBES
@@ -381,14 +171,14 @@ apr_status_t ap_mpm_safe_kill(pid_t pid, int sig)
static int wait_or_timeout_counter;
void ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode, apr_proc_t *ret,
- apr_pool_t *p)
+ apr_pool_t *p, server_rec *s)
{
apr_status_t rv;
++wait_or_timeout_counter;
if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
wait_or_timeout_counter = 0;
- ap_run_monitor(p);
+ ap_run_monitor(p, s);
}
rv = apr_proc_wait_all_procs(ret, exitcode, status, APR_NOWAIT, p);
@@ -401,79 +191,12 @@ void ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode, apr_proc_t *ret,
return;
}
-#ifdef NEED_WAITPID
- if ((ret = reap_children(exitcode, status)) > 0) {
- return;
- }
-#endif
-
- apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
+ apr_sleep(apr_time_from_sec(1));
ret->pid = -1;
return;
}
-#endif /* AP_MPM_WANT_WAIT_OR_TIMEOUT */
-
-#ifdef AP_MPM_WANT_PROCESS_CHILD_STATUS
-int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status)
-{
- int signum = status;
- const char *sigdesc = apr_signal_description_get(signum);
-
- /* Child died... if it died due to a fatal error,
- * we should simply bail out. The caller needs to
- * check for bad rc from us and exit, running any
- * appropriate cleanups.
- *
- * If the child died due to a resource shortage,
- * the parent should limit the rate of forking
- */
- if (APR_PROC_CHECK_EXIT(why)) {
- if (status == APEXIT_CHILDSICK) {
- return status;
- }
-
- if (status == APEXIT_CHILDFATAL) {
- ap_log_error(APLOG_MARK, APLOG_ALERT,
- 0, ap_server_conf,
- "Child %" APR_PID_T_FMT
- " returned a Fatal error... Apache is exiting!",
- pid->pid);
- return APEXIT_CHILDFATAL;
- }
-
- return 0;
- }
-
- if (APR_PROC_CHECK_SIGNALED(why)) {
- switch (signum) {
- case SIGTERM:
- case SIGHUP:
- case AP_SIG_GRACEFUL:
- case SIGKILL:
- break;
-
- default:
- if (APR_PROC_CHECK_CORE_DUMP(why)) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE,
- 0, ap_server_conf,
- "child pid %ld exit signal %s (%d), "
- "possible coredump in %s",
- (long)pid->pid, sigdesc, signum,
- ap_coredump_dir);
- }
- else {
- ap_log_error(APLOG_MARK, APLOG_NOTICE,
- 0, ap_server_conf,
- "child pid %ld exit signal %s (%d)",
- (long)pid->pid, sigdesc, signum);
- }
- }
- }
- return 0;
-}
-#endif /* AP_MPM_WANT_PROCESS_CHILD_STATUS */
-#if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF)
+#if defined(TCP_NODELAY)
void ap_sock_disable_nagle(apr_socket_t *s)
{
/* The Nagle algorithm says that we should delay sending partial
@@ -488,7 +211,7 @@ void ap_sock_disable_nagle(apr_socket_t *s)
apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1);
if (status != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf, APLOGNO(00542)
"apr_socket_opt_set: (TCP_NODELAY)");
}
}
@@ -503,7 +226,7 @@ AP_DECLARE(uid_t) ap_uname2id(const char *name)
return (atoi(&name[1]));
if (!(ent = getpwnam(name))) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00543)
"%s: bad user name %s", ap_server_argv0, name);
exit(1);
}
@@ -521,7 +244,7 @@ AP_DECLARE(gid_t) ap_gname2id(const char *name)
return (atoi(&name[1]));
if (!(ent = getgrnam(name))) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00544)
"%s: bad group name %s", ap_server_argv0, name);
exit(1);
}
@@ -533,10 +256,9 @@ AP_DECLARE(gid_t) ap_gname2id(const char *name)
#ifndef HAVE_INITGROUPS
int initgroups(const char *name, gid_t basegid)
{
-#if defined(QNX) || defined(MPE) || defined(BEOS) || defined(_OSD_POSIX) || defined(TPF) || defined(__TANDEM) || defined(OS2) || defined(WIN32) || defined(NETWARE)
-/* QNX, MPE and BeOS do not appear to support supplementary groups. */
+#if defined(_OSD_POSIX) || defined(OS2) || defined(WIN32) || defined(NETWARE)
return 0;
-#else /* ndef QNX */
+#else
gid_t groups[NGROUPS_MAX];
struct group *g;
int index = 0;
@@ -559,205 +281,11 @@ int initgroups(const char *name, gid_t basegid)
endgrent();
return setgroups(index, groups);
-#endif /* def QNX */
+#endif
}
#endif /* def HAVE_INITGROUPS */
-#ifdef AP_MPM_USES_POD
-
-AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod)
-{
- apr_status_t rv;
-
- *pod = apr_palloc(p, sizeof(**pod));
- rv = apr_file_pipe_create(&((*pod)->pod_in), &((*pod)->pod_out), p);
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- apr_file_pipe_timeout_set((*pod)->pod_in, 0);
- (*pod)->p = p;
-
- /* close these before exec. */
- apr_file_inherit_unset((*pod)->pod_in);
- apr_file_inherit_unset((*pod)->pod_out);
-
- return APR_SUCCESS;
-}
-
-AP_DECLARE(apr_status_t) ap_mpm_pod_check(ap_pod_t *pod)
-{
- char c;
- apr_size_t len = 1;
- apr_status_t rv;
-
- rv = apr_file_read(pod->pod_in, &c, &len);
-
- if ((rv == APR_SUCCESS) && (len == 1)) {
- return APR_SUCCESS;
- }
-
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- return AP_NORESTART;
-}
-
-AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod)
-{
- apr_status_t rv;
-
- rv = apr_file_close(pod->pod_out);
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- rv = apr_file_close(pod->pod_in);
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- return APR_SUCCESS;
-}
-
-static apr_status_t pod_signal_internal(ap_pod_t *pod)
-{
- apr_status_t rv;
- char char_of_death = '!';
- apr_size_t one = 1;
-
- rv = apr_file_write(pod->pod_out, &char_of_death, &one);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
- "write pipe_of_death");
- }
-
- return rv;
-}
-
-/* This function connects to the server, then immediately closes the connection.
- * This permits the MPM to skip the poll when there is only one listening
- * socket, because it provides a alternate way to unblock an accept() when
- * the pod is used.
- */
-static apr_status_t dummy_connection(ap_pod_t *pod)
-{
- char *srequest;
- apr_status_t rv;
- apr_socket_t *sock;
- apr_pool_t *p;
- apr_size_t len;
-
- /* create a temporary pool for the socket. pconf stays around too long */
- rv = apr_pool_create(&p, pod->p);
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- rv = apr_socket_create(&sock, ap_listeners->bind_addr->family,
- SOCK_STREAM, 0, p);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
- "get socket to connect to listener");
- apr_pool_destroy(p);
- return rv;
- }
-
- /* on some platforms (e.g., FreeBSD), the kernel won't accept many
- * queued connections before it starts blocking local connects...
- * we need to keep from blocking too long and instead return an error,
- * because the MPM won't want to hold up a graceful restart for a
- * long time
- */
- rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
- "set timeout on socket to connect to listener");
- apr_socket_close(sock);
- apr_pool_destroy(p);
- return rv;
- }
-
- rv = apr_socket_connect(sock, ap_listeners->bind_addr);
- if (rv != APR_SUCCESS) {
- int log_level = APLOG_WARNING;
-
- if (APR_STATUS_IS_TIMEUP(rv)) {
- /* probably some server processes bailed out already and there
- * is nobody around to call accept and clear out the kernel
- * connection queue; usually this is not worth logging
- */
- log_level = APLOG_DEBUG;
- }
-
- ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf,
- "connect to listener on %pI", ap_listeners->bind_addr);
- }
-
- /* Create the request string. We include a User-Agent so that
- * adminstrators can track down the cause of the odd-looking
- * requests in their logs.
- */
- srequest = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ",
- ap_get_server_banner(),
- " (internal dummy connection)\r\n\r\n", NULL);
-
- /* Since some operating systems support buffering of data or entire
- * requests in the kernel, we send a simple request, to make sure
- * the server pops out of a blocking accept().
- */
- /* XXX: This is HTTP specific. We should look at the Protocol for each
- * listener, and send the correct type of request to trigger any Accept
- * Filters.
- */
- len = strlen(srequest);
- apr_socket_send(sock, srequest, &len);
- apr_socket_close(sock);
- apr_pool_destroy(p);
-
- return rv;
-}
-
-AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod)
-{
- apr_status_t rv;
-
- rv = pod_signal_internal(pod);
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- return dummy_connection(pod);
-}
-
-void ap_mpm_pod_killpg(ap_pod_t *pod, int num)
-{
- int i;
- apr_status_t rv = APR_SUCCESS;
-
- /* we don't write anything to the pod here... we assume
- * that the would-be reader of the pod has another way to
- * see that it is time to die once we wake it up
- *
- * writing lots of things to the pod at once is very
- * problematic... we can fill the kernel pipe buffer and
- * be blocked until somebody consumes some bytes or
- * we hit a timeout... if we hit a timeout we can't just
- * keep trying because maybe we'll never successfully
- * write again... but then maybe we'll leave would-be
- * readers stranded (a number of them could be tied up for
- * a while serving time-consuming requests)
- */
- for (i = 0; i < num && rv == APR_SUCCESS; i++) {
- rv = dummy_connection(pod);
- }
-}
-#endif /* #ifdef AP_MPM_USES_POD */
-
/* standard mpm configuration handling */
-#ifdef AP_MPM_WANT_SET_PIDFILE
-const char *ap_pid_fname = NULL;
const char *ap_mpm_set_pidfile(cmd_parms *cmd, void *dummy,
const char *arg)
@@ -774,40 +302,12 @@ const char *ap_mpm_set_pidfile(cmd_parms *cmd, void *dummy,
ap_pid_fname = arg;
return NULL;
}
-#endif
-
-#ifdef AP_MPM_WANT_SET_SCOREBOARD
-const char * ap_mpm_set_scoreboard(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- ap_scoreboard_fname = arg;
- return NULL;
-}
-#endif
-#ifdef AP_MPM_WANT_SET_LOCKFILE
-const char *ap_lock_fname = NULL;
-
-const char *ap_mpm_set_lockfile(cmd_parms *cmd, void *dummy,
- const char *arg)
+void ap_mpm_dump_pidfile(apr_pool_t *p, apr_file_t *out)
{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- ap_lock_fname = arg;
- return NULL;
+ apr_file_printf(out, "PidFile: \"%s\"\n",
+ ap_server_root_relative(p, ap_pid_fname));
}
-#endif
-
-#ifdef AP_MPM_WANT_SET_MAX_REQUESTS
-int ap_max_requests_per_child = 0;
const char *ap_mpm_set_max_requests(cmd_parms *cmd, void *dummy,
const char *arg)
@@ -817,20 +317,20 @@ const char *ap_mpm_set_max_requests(cmd_parms *cmd, void *dummy,
return err;
}
+ if (!strcasecmp(cmd->cmd->name, "MaxRequestsPerChild")) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(00545)
+ "MaxRequestsPerChild is deprecated, use "
+ "MaxConnectionsPerChild instead.");
+ }
+
ap_max_requests_per_child = atoi(arg);
return NULL;
}
-#endif
-
-#ifdef AP_MPM_WANT_SET_COREDUMPDIR
-char ap_coredump_dir[MAX_STRING_LEN];
-int ap_coredumpdir_configured;
const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy,
const char *arg)
{
- apr_status_t rv;
apr_finfo_t finfo;
const char *fname;
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
@@ -843,7 +343,7 @@ const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy,
return apr_pstrcat(cmd->pool, "Invalid CoreDumpDirectory path ",
arg, NULL);
}
- if ((rv = apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool)) != APR_SUCCESS) {
+ if (apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool) != APR_SUCCESS) {
return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
" does not exist", NULL);
}
@@ -855,10 +355,6 @@ const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy,
ap_coredumpdir_configured = 1;
return NULL;
}
-#endif
-
-#ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN
-int ap_graceful_shutdown_timeout = 0;
const char * ap_mpm_set_graceful_shutdown(cmd_parms *cmd, void *dummy,
const char *arg)
@@ -870,244 +366,6 @@ const char * ap_mpm_set_graceful_shutdown(cmd_parms *cmd, void *dummy,
ap_graceful_shutdown_timeout = atoi(arg);
return NULL;
}
-#endif
-
-#ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
-apr_lockmech_e ap_accept_lock_mech = APR_LOCK_DEFAULT;
-
-const char ap_valid_accept_mutex_string[] =
- "Valid accept mutexes for this platform and MPM are: default"
-#if APR_HAS_FLOCK_SERIALIZE
- ", flock"
-#endif
-#if APR_HAS_FCNTL_SERIALIZE
- ", fcntl"
-#endif
-#if APR_HAS_SYSVSEM_SERIALIZE && !defined(PERCHILD_MPM)
- ", sysvsem"
-#endif
-#if APR_HAS_POSIXSEM_SERIALIZE
- ", posixsem"
-#endif
-#if APR_HAS_PROC_PTHREAD_SERIALIZE
- ", pthread"
-#endif
- ".";
-
-AP_DECLARE(const char *) ap_mpm_set_accept_lock_mech(cmd_parms *cmd,
- void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- if (!strcasecmp(arg, "default")) {
- ap_accept_lock_mech = APR_LOCK_DEFAULT;
- }
-#if APR_HAS_FLOCK_SERIALIZE
- else if (!strcasecmp(arg, "flock")) {
- ap_accept_lock_mech = APR_LOCK_FLOCK;
- }
-#endif
-#if APR_HAS_FCNTL_SERIALIZE
- else if (!strcasecmp(arg, "fcntl")) {
- ap_accept_lock_mech = APR_LOCK_FCNTL;
- }
-#endif
-
- /* perchild can't use SysV sems because the permissions on the accept
- * mutex can't be set to allow all processes to use the mutex and
- * at the same time keep all users from being able to dink with the
- * mutex
- */
-#if APR_HAS_SYSVSEM_SERIALIZE && !defined(PERCHILD_MPM)
- else if (!strcasecmp(arg, "sysvsem")) {
- ap_accept_lock_mech = APR_LOCK_SYSVSEM;
- }
-#endif
-#if APR_HAS_POSIXSEM_SERIALIZE
- else if (!strcasecmp(arg, "posixsem")) {
- ap_accept_lock_mech = APR_LOCK_POSIXSEM;
- }
-#endif
-#if APR_HAS_PROC_PTHREAD_SERIALIZE
- else if (!strcasecmp(arg, "pthread")) {
- ap_accept_lock_mech = APR_LOCK_PROC_PTHREAD;
- }
-#endif
- else {
- return apr_pstrcat(cmd->pool, arg, " is an invalid mutex mechanism; ",
- ap_valid_accept_mutex_string, NULL);
- }
- return NULL;
-}
-
-#endif
-
-#ifdef AP_MPM_WANT_SIGNAL_SERVER
-
-static const char *dash_k_arg;
-
-static int send_signal(pid_t pid, int sig)
-{
- if (kill(pid, sig) < 0) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, errno, NULL,
- "sending signal to server");
- return 1;
- }
- return 0;
-}
-
-int ap_signal_server(int *exit_status, apr_pool_t *pconf)
-{
- apr_status_t rv;
- pid_t otherpid;
- int running = 0;
- const char *status;
-
- *exit_status = 0;
-
- rv = ap_read_pid(pconf, ap_pid_fname, &otherpid);
- if (rv != APR_SUCCESS) {
- if (rv != APR_ENOENT) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, NULL,
- "Error retrieving pid file %s", ap_pid_fname);
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
- "Remove it before continuing if it is corrupted.");
- *exit_status = 1;
- return 1;
- }
- status = "httpd (no pid file) not running";
- }
- else {
- if (kill(otherpid, 0) == 0) {
- running = 1;
- status = apr_psprintf(pconf,
- "httpd (pid %" APR_PID_T_FMT ") already "
- "running", otherpid);
- }
- else {
- status = apr_psprintf(pconf,
- "httpd (pid %" APR_PID_T_FMT "?) not running",
- otherpid);
- }
- }
-
- if (!strcmp(dash_k_arg, "start")) {
- if (running) {
- printf("%s\n", status);
- return 1;
- }
- }
-
- if (!strcmp(dash_k_arg, "stop")) {
- if (!running) {
- printf("%s\n", status);
- }
- else {
- send_signal(otherpid, SIGTERM);
- }
- return 1;
- }
-
- if (!strcmp(dash_k_arg, "restart")) {
- if (!running) {
- printf("httpd not running, trying to start\n");
- }
- else {
- *exit_status = send_signal(otherpid, SIGHUP);
- return 1;
- }
- }
-
- if (!strcmp(dash_k_arg, "graceful")) {
- if (!running) {
- printf("httpd not running, trying to start\n");
- }
- else {
- *exit_status = send_signal(otherpid, AP_SIG_GRACEFUL);
- return 1;
- }
- }
-
- if (!strcmp(dash_k_arg, "graceful-stop")) {
-#ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN
- if (!running) {
- printf("%s\n", status);
- }
- else {
- *exit_status = send_signal(otherpid, AP_SIG_GRACEFUL_STOP);
- }
-#else
- printf("httpd MPM \"" MPM_NAME "\" does not support graceful-stop\n");
-#endif
- return 1;
- }
-
- return 0;
-}
-
-void ap_mpm_rewrite_args(process_rec *process)
-{
- apr_array_header_t *mpm_new_argv;
- apr_status_t rv;
- apr_getopt_t *opt;
- char optbuf[3];
- const char *optarg;
-
- mpm_new_argv = apr_array_make(process->pool, process->argc,
- sizeof(const char **));
- *(const char **)apr_array_push(mpm_new_argv) = process->argv[0];
- apr_getopt_init(&opt, process->pool, process->argc, process->argv);
- opt->errfn = NULL;
- optbuf[0] = '-';
- /* option char returned by apr_getopt() will be stored in optbuf[1] */
- optbuf[2] = '\0';
- while ((rv = apr_getopt(opt, "k:" AP_SERVER_BASEARGS,
- optbuf + 1, &optarg)) == APR_SUCCESS) {
- switch(optbuf[1]) {
- case 'k':
- if (!dash_k_arg) {
- if (!strcmp(optarg, "start") || !strcmp(optarg, "stop") ||
- !strcmp(optarg, "restart") || !strcmp(optarg, "graceful") ||
- !strcmp(optarg, "graceful-stop")) {
- dash_k_arg = optarg;
- break;
- }
- }
- default:
- *(const char **)apr_array_push(mpm_new_argv) =
- apr_pstrdup(process->pool, optbuf);
- if (optarg) {
- *(const char **)apr_array_push(mpm_new_argv) = optarg;
- }
- }
- }
-
- /* back up to capture the bad argument */
- if (rv == APR_BADCH || rv == APR_BADARG) {
- opt->ind--;
- }
-
- while (opt->ind < opt->argc) {
- *(const char **)apr_array_push(mpm_new_argv) =
- apr_pstrdup(process->pool, opt->argv[opt->ind++]);
- }
-
- process->argc = mpm_new_argv->nelts;
- process->argv = (const char * const *)mpm_new_argv->elts;
-
- if (dash_k_arg) {
- APR_REGISTER_OPTIONAL_FN(ap_signal_server);
- }
-}
-
-#endif /* AP_MPM_WANT_SIGNAL_SERVER */
-
-#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
-apr_uint32_t ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy,
const char *arg)
@@ -1118,7 +376,7 @@ const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy,
return err;
}
- value = strtol(arg, NULL, 0);
+ value = strtol(arg, NULL, 10);
if (value < 0 || errno == ERANGE)
return apr_pstrcat(cmd->pool, "Invalid MaxMemFree value: ",
arg, NULL);
@@ -1128,11 +386,6 @@ const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy,
return NULL;
}
-#endif /* AP_MPM_WANT_SET_MAX_MEM_FREE */
-
-#ifdef AP_MPM_WANT_SET_STACKSIZE
-apr_size_t ap_thread_stacksize = 0; /* use system default */
-
const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy,
const char *arg)
{
@@ -1142,7 +395,7 @@ const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy,
return err;
}
- value = strtol(arg, NULL, 0);
+ value = strtol(arg, NULL, 10);
if (value < 0 || errno == ERANGE)
return apr_pstrcat(cmd->pool, "Invalid ThreadStackSize value: ",
arg, NULL);
@@ -1152,162 +405,160 @@ const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy,
return NULL;
}
-#endif /* AP_MPM_WANT_SET_STACKSIZE */
-
-#ifdef AP_MPM_WANT_FATAL_SIGNAL_HANDLER
+AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+{
+ apr_status_t rv;
-static pid_t parent_pid, my_pid;
-apr_pool_t *pconf;
+ if (ap_run_mpm_query(query_code, result, &rv) == DECLINED) {
+ rv = APR_EGENERAL;
+ }
-#if AP_ENABLE_EXCEPTION_HOOK
+ return rv;
+}
-static int exception_hook_enabled;
+static void end_gen(mpm_gen_info_t *gi)
+{
+ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf,
+ "end of generation %d", gi->gen);
+ ap_run_end_generation(ap_server_conf, gi->gen);
+ APR_RING_REMOVE(gi, link);
+ APR_RING_INSERT_HEAD(unused_geninfo, gi, mpm_gen_info_t, link);
+}
-const char *ap_mpm_set_exception_hook(cmd_parms *cmd, void *dummy,
- const char *arg)
+apr_status_t ap_mpm_end_gen_helper(void *unused) /* cleanup on pconf */
{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
+ int gen = ap_config_generation - 1; /* differs from MPM generation */
+ mpm_gen_info_t *cur;
- if (cmd->server->is_virtual) {
- return "EnableExceptionHook directive not allowed in <VirtualHost>";
+ if (geninfo == NULL) {
+ /* initial pconf teardown, MPM hasn't run */
+ return APR_SUCCESS;
}
- if (strcasecmp(arg, "on") == 0) {
- exception_hook_enabled = 1;
+ cur = APR_RING_FIRST(geninfo);
+ while (cur != APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link) &&
+ cur->gen != gen) {
+ cur = APR_RING_NEXT(cur, link);
}
- else if (strcasecmp(arg, "off") == 0) {
- exception_hook_enabled = 0;
+
+ if (cur == APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link)) {
+ /* last child of generation already exited */
+ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf,
+ "no record of generation %d", gen);
}
else {
- return "parameter must be 'on' or 'off'";
+ cur->done = 1;
+ if (cur->active == 0) {
+ end_gen(cur);
+ }
}
- return NULL;
+ return APR_SUCCESS;
}
-static void run_fatal_exception_hook(int sig)
-{
- ap_exception_info_t ei = {0};
-
- if (exception_hook_enabled &&
- geteuid() != 0 &&
- my_pid != parent_pid) {
- ei.sig = sig;
- ei.pid = my_pid;
- ap_run_fatal_exception(&ei);
+/* core's child-status hook
+ * tracks number of remaining children per generation and
+ * runs the end-generation hook when the last child of
+ * a generation exits
+ */
+void ap_core_child_status(server_rec *s, pid_t pid,
+ ap_generation_t gen, int slot,
+ mpm_child_status status)
+{
+ mpm_gen_info_t *cur;
+ const char *status_msg = "unknown status";
+
+ if (!gen_head_init) { /* where to run this? */
+ gen_head_init = 1;
+ geninfo = apr_pcalloc(s->process->pool, sizeof *geninfo);
+ unused_geninfo = apr_pcalloc(s->process->pool, sizeof *unused_geninfo);
+ APR_RING_INIT(geninfo, mpm_gen_info_t, link);
+ APR_RING_INIT(unused_geninfo, mpm_gen_info_t, link);
+ }
+
+ cur = APR_RING_FIRST(geninfo);
+ while (cur != APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link) &&
+ cur->gen != gen) {
+ cur = APR_RING_NEXT(cur, link);
+ }
+
+ switch(status) {
+ case MPM_CHILD_STARTED:
+ status_msg = "started";
+ if (cur == APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link)) {
+ /* first child for this generation */
+ if (!APR_RING_EMPTY(unused_geninfo, mpm_gen_info_t, link)) {
+ cur = APR_RING_FIRST(unused_geninfo);
+ APR_RING_REMOVE(cur, link);
+ cur->active = cur->done = 0;
+ }
+ else {
+ cur = apr_pcalloc(s->process->pool, sizeof *cur);
+ }
+ cur->gen = gen;
+ APR_RING_ELEM_INIT(cur, link);
+ APR_RING_INSERT_HEAD(geninfo, cur, mpm_gen_info_t, link);
+ }
+ ap_random_parent_after_fork();
+ ++cur->active;
+ break;
+ case MPM_CHILD_EXITED:
+ status_msg = "exited";
+ if (cur == APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00546)
+ "no record of generation %d of exiting child %" APR_PID_T_FMT,
+ gen, pid);
+ }
+ else {
+ --cur->active;
+ if (!cur->active && cur->done) { /* no children, server has stopped/restarted */
+ end_gen(cur);
+ }
+ }
+ break;
+ case MPM_CHILD_LOST_SLOT:
+ status_msg = "lost slot";
+ /* we don't track by slot, so it doesn't matter */
+ break;
}
+ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s,
+ "mpm child %" APR_PID_T_FMT " (gen %d/slot %d) %s",
+ pid, gen, slot, status_msg);
}
-#endif /* AP_ENABLE_EXCEPTION_HOOK */
-/* handle all varieties of core dumping signals */
-static void sig_coredump(int sig)
+AP_DECLARE(apr_status_t) ap_mpm_register_timed_callback(apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton)
{
- apr_filepath_set(ap_coredump_dir, pconf);
- apr_signal(sig, SIG_DFL);
-#if AP_ENABLE_EXCEPTION_HOOK
- run_fatal_exception_hook(sig);
-#endif
- /* linuxthreads issue calling getpid() here:
- * This comparison won't match if the crashing thread is
- * some module's thread that runs in the parent process.
- * The fallout, which is limited to linuxthreads:
- * The special log message won't be written when such a
- * thread in the parent causes the parent to crash.
- */
- if (getpid() == parent_pid) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE,
- 0, ap_server_conf,
- "seg fault or similar nasty error detected "
- "in the parent process");
- /* XXX we can probably add some rudimentary cleanup code here,
- * like getting rid of the pid file. If any additional bad stuff
- * happens, we are protected from recursive errors taking down the
- * system since this function is no longer the signal handler GLA
- */
- }
- kill(getpid(), sig);
- /* At this point we've got sig blocked, because we're still inside
- * the signal handler. When we leave the signal handler it will
- * be unblocked, and we'll take the signal... and coredump or whatever
- * is appropriate for this particular Unix. In addition the parent
- * will see the real signal we received -- whereas if we called
- * abort() here, the parent would only see SIGABRT.
- */
+ return ap_run_mpm_register_timed_callback(t, cbfn, baton);
}
-apr_status_t ap_fatal_signal_child_setup(server_rec *s)
+AP_DECLARE(const char *)ap_show_mpm(void)
{
- my_pid = getpid();
- return APR_SUCCESS;
+ const char *name = ap_run_mpm_get_name();
+
+ if (!name) {
+ name = "";
+ }
+
+ return name;
}
-apr_status_t ap_fatal_signal_setup(server_rec *s, apr_pool_t *in_pconf)
+AP_DECLARE(const char *)ap_check_mpm(void)
{
-#ifndef NO_USE_SIGACTION
- struct sigaction sa;
-
- sigemptyset(&sa.sa_mask);
+ static const char *last_mpm_name = NULL;
-#if defined(SA_ONESHOT)
- sa.sa_flags = SA_ONESHOT;
-#elif defined(SA_RESETHAND)
- sa.sa_flags = SA_RESETHAND;
-#else
- sa.sa_flags = 0;
-#endif
+ if (!_hooks.link_mpm || _hooks.link_mpm->nelts == 0)
+ return "No MPM loaded.";
+ else if (_hooks.link_mpm->nelts > 1)
+ return "More than one MPM loaded.";
- sa.sa_handler = sig_coredump;
- if (sigaction(SIGSEGV, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGSEGV)");
-#ifdef SIGBUS
- if (sigaction(SIGBUS, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGBUS)");
-#endif
-#ifdef SIGABORT
- if (sigaction(SIGABORT, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABORT)");
-#endif
-#ifdef SIGABRT
- if (sigaction(SIGABRT, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABRT)");
-#endif
-#ifdef SIGILL
- if (sigaction(SIGILL, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGILL)");
-#endif
-#ifdef SIGFPE
- if (sigaction(SIGFPE, &sa, NULL) < 0)
- ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGFPE)");
-#endif
-
-#else /* NO_USE_SIGACTION */
-
- apr_signal(SIGSEGV, sig_coredump);
-#ifdef SIGBUS
- apr_signal(SIGBUS, sig_coredump);
-#endif /* SIGBUS */
-#ifdef SIGABORT
- apr_signal(SIGABORT, sig_coredump);
-#endif /* SIGABORT */
-#ifdef SIGABRT
- apr_signal(SIGABRT, sig_coredump);
-#endif /* SIGABRT */
-#ifdef SIGILL
- apr_signal(SIGILL, sig_coredump);
-#endif /* SIGILL */
-#ifdef SIGFPE
- apr_signal(SIGFPE, sig_coredump);
-#endif /* SIGFPE */
-
-#endif /* NO_USE_SIGACTION */
-
- pconf = in_pconf;
- parent_pid = my_pid = getpid();
+ if (last_mpm_name) {
+ if (strcmp(last_mpm_name, ap_show_mpm())) {
+ return "The MPM cannot be changed during restart.";
+ }
+ }
+ else {
+ last_mpm_name = apr_pstrdup(ap_pglobal, ap_show_mpm());
+ }
- return APR_SUCCESS;
+ return NULL;
}
-
-#endif /* AP_MPM_WANT_FATAL_SIGNAL_HANDLER */
diff --git a/server/mpm_unix.c b/server/mpm_unix.c
new file mode 100644
index 00000000..6b1d6ee1
--- /dev/null
+++ b/server/mpm_unix.c
@@ -0,0 +1,942 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* The purpose of this file is to store the code that MOST mpm's will need
+ * this does not mean a function only goes into this file if every MPM needs
+ * it. It means that if a function is needed by more than one MPM, and
+ * future maintenance would be served by making the code common, then the
+ * function belongs here.
+ *
+ * This is going in src/main because it is not platform specific, it is
+ * specific to multi-process servers, but NOT to Unix. Which is why it
+ * does not belong in src/os/unix
+ */
+
+#ifndef WIN32
+
+#include "apr.h"
+#include "apr_thread_proc.h"
+#include "apr_signal.h"
+#include "apr_strings.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+#include "apr_getopt.h"
+#include "apr_optional.h"
+#include "apr_allocator.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "mpm_common.h"
+#include "ap_mpm.h"
+#include "ap_listen.h"
+#include "scoreboard.h"
+#include "util_mutex.h"
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
+typedef enum {DO_NOTHING, SEND_SIGTERM, SEND_SIGKILL, GIVEUP} action_t;
+
+typedef struct extra_process_t {
+ struct extra_process_t *next;
+ pid_t pid;
+ ap_generation_t gen;
+} extra_process_t;
+
+static extra_process_t *extras;
+
+void ap_register_extra_mpm_process(pid_t pid, ap_generation_t gen)
+{
+ extra_process_t *p = (extra_process_t *)ap_malloc(sizeof(extra_process_t));
+
+ p->next = extras;
+ p->pid = pid;
+ p->gen = gen;
+ extras = p;
+}
+
+int ap_unregister_extra_mpm_process(pid_t pid, ap_generation_t *gen)
+{
+ extra_process_t *cur = extras;
+ extra_process_t *prev = NULL;
+
+ while (cur && cur->pid != pid) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur) {
+ if (prev) {
+ prev->next = cur->next;
+ }
+ else {
+ extras = cur->next;
+ }
+ *gen = cur->gen;
+ free(cur);
+ return 1; /* found */
+ }
+ else {
+ /* we don't know about any such process */
+ return 0;
+ }
+}
+
+static int reclaim_one_pid(pid_t pid, action_t action)
+{
+ apr_proc_t proc;
+ apr_status_t waitret;
+ apr_exit_why_e why;
+ int status;
+
+ /* Ensure pid sanity. */
+ if (pid < 1) {
+ return 1;
+ }
+
+ proc.pid = pid;
+ waitret = apr_proc_wait(&proc, &status, &why, APR_NOWAIT);
+ if (waitret != APR_CHILD_NOTDONE) {
+ if (waitret == APR_CHILD_DONE)
+ ap_process_child_status(&proc, why, status);
+ return 1;
+ }
+
+ switch(action) {
+ case DO_NOTHING:
+ break;
+
+ case SEND_SIGTERM:
+ /* ok, now it's being annoying */
+ ap_log_error(APLOG_MARK, APLOG_WARNING,
+ 0, ap_server_conf, APLOGNO(00045)
+ "child process %" APR_PID_T_FMT
+ " still did not exit, "
+ "sending a SIGTERM",
+ pid);
+ kill(pid, SIGTERM);
+ break;
+
+ case SEND_SIGKILL:
+ ap_log_error(APLOG_MARK, APLOG_ERR,
+ 0, ap_server_conf, APLOGNO(00046)
+ "child process %" APR_PID_T_FMT
+ " still did not exit, "
+ "sending a SIGKILL",
+ pid);
+ kill(pid, SIGKILL);
+ break;
+
+ case GIVEUP:
+ /* 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_ERR,
+ 0, ap_server_conf, APLOGNO(00047)
+ "could not make child process %" APR_PID_T_FMT
+ " exit, "
+ "attempting to continue anyway",
+ pid);
+ break;
+ }
+
+ return 0;
+}
+
+void ap_reclaim_child_processes(int terminate,
+ ap_reclaim_callback_fn_t *mpm_callback)
+{
+ apr_time_t waittime = 1024 * 16;
+ int i;
+ extra_process_t *cur_extra;
+ int not_dead_yet;
+ int max_daemons;
+ apr_time_t starttime = apr_time_now();
+ /* this table of actions and elapsed times tells what action is taken
+ * at which elapsed time from starting the reclaim
+ */
+ struct {
+ action_t action;
+ apr_time_t action_time;
+ } action_table[] = {
+ {DO_NOTHING, 0}, /* dummy entry for iterations where we reap
+ * children but take no action against
+ * stragglers
+ */
+ {SEND_SIGTERM, apr_time_from_sec(3)},
+ {SEND_SIGTERM, apr_time_from_sec(5)},
+ {SEND_SIGTERM, apr_time_from_sec(7)},
+ {SEND_SIGKILL, apr_time_from_sec(9)},
+ {GIVEUP, apr_time_from_sec(10)}
+ };
+ int cur_action; /* index of action we decided to take this
+ * iteration
+ */
+ int next_action = 1; /* index of first real action */
+
+ ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons);
+
+ do {
+ apr_sleep(waittime);
+ /* don't let waittime get longer than 1 second; otherwise, we don't
+ * react quickly to the last child exiting, and taking action can
+ * be delayed
+ */
+ waittime = waittime * 4;
+ if (waittime > apr_time_from_sec(1)) {
+ waittime = apr_time_from_sec(1);
+ }
+
+ /* see what action to take, if any */
+ if (action_table[next_action].action_time <= apr_time_now() - starttime) {
+ cur_action = next_action;
+ ++next_action;
+ }
+ else {
+ cur_action = 0; /* nothing to do */
+ }
+
+ /* now see who is done */
+ not_dead_yet = 0;
+ for (i = 0; i < max_daemons; ++i) {
+ process_score *ps = ap_get_scoreboard_process(i);
+ pid_t pid = ps->pid;
+
+ if (pid == 0) {
+ continue; /* not every scoreboard entry is in use */
+ }
+
+ if (reclaim_one_pid(pid, action_table[cur_action].action)) {
+ mpm_callback(i, 0, 0);
+ }
+ else {
+ ++not_dead_yet;
+ }
+ }
+
+ cur_extra = extras;
+ while (cur_extra) {
+ ap_generation_t old_gen;
+ extra_process_t *next = cur_extra->next;
+
+ if (reclaim_one_pid(cur_extra->pid, action_table[cur_action].action)) {
+ if (ap_unregister_extra_mpm_process(cur_extra->pid, &old_gen) == 1) {
+ mpm_callback(-1, cur_extra->pid, old_gen);
+ }
+ else {
+ AP_DEBUG_ASSERT(1 == 0);
+ }
+ }
+ else {
+ ++not_dead_yet;
+ }
+ cur_extra = next;
+ }
+#if APR_HAS_OTHER_CHILD
+ apr_proc_other_child_refresh_all(APR_OC_REASON_RESTART);
+#endif
+
+ } while (not_dead_yet > 0 &&
+ action_table[cur_action].action != GIVEUP);
+}
+
+void ap_relieve_child_processes(ap_reclaim_callback_fn_t *mpm_callback)
+{
+ int i;
+ extra_process_t *cur_extra;
+ int max_daemons;
+
+ ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons);
+
+ /* now see who is done */
+ for (i = 0; i < max_daemons; ++i) {
+ process_score *ps = ap_get_scoreboard_process(i);
+ pid_t pid = ps->pid;
+
+ if (pid == 0) {
+ continue; /* not every scoreboard entry is in use */
+ }
+
+ if (reclaim_one_pid(pid, DO_NOTHING)) {
+ mpm_callback(i, 0, 0);
+ }
+ }
+
+ cur_extra = extras;
+ while (cur_extra) {
+ ap_generation_t old_gen;
+ extra_process_t *next = cur_extra->next;
+
+ if (reclaim_one_pid(cur_extra->pid, DO_NOTHING)) {
+ if (ap_unregister_extra_mpm_process(cur_extra->pid, &old_gen) == 1) {
+ mpm_callback(-1, cur_extra->pid, old_gen);
+ }
+ else {
+ AP_DEBUG_ASSERT(1 == 0);
+ }
+ }
+ cur_extra = next;
+ }
+}
+
+/* Before sending the signal to the pid this function verifies that
+ * the pid is a member of the current process group; either using
+ * apr_proc_wait(), where waitpid() guarantees to fail for non-child
+ * processes; or by using getpgid() directly, if available. */
+apr_status_t ap_mpm_safe_kill(pid_t pid, int sig)
+{
+#ifndef HAVE_GETPGID
+ apr_proc_t proc;
+ apr_status_t rv;
+ apr_exit_why_e why;
+ int status;
+
+ /* Ensure pid sanity */
+ if (pid < 1) {
+ return APR_EINVAL;
+ }
+
+ proc.pid = pid;
+ rv = apr_proc_wait(&proc, &status, &why, APR_NOWAIT);
+ if (rv == APR_CHILD_DONE) {
+ /* The child already died - log the termination status if
+ * necessary: */
+ ap_process_child_status(&proc, why, status);
+ return APR_EINVAL;
+ }
+ else if (rv != APR_CHILD_NOTDONE) {
+ /* The child is already dead and reaped, or was a bogus pid -
+ * log this either way. */
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, APLOGNO(00048)
+ "cannot send signal %d to pid %ld (non-child or "
+ "already dead)", sig, (long)pid);
+ return APR_EINVAL;
+ }
+#else
+ pid_t pg;
+
+ /* Ensure pid sanity. */
+ if (pid < 1) {
+ return APR_EINVAL;
+ }
+
+ pg = getpgid(pid);
+ if (pg == -1) {
+ /* Process already dead... */
+ return errno;
+ }
+
+ if (pg != getpgrp()) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, 0, ap_server_conf, APLOGNO(00049)
+ "refusing to send signal %d to pid %ld outside "
+ "process group", sig, (long)pid);
+ return APR_EINVAL;
+ }
+#endif
+
+ return kill(pid, sig) ? errno : APR_SUCCESS;
+}
+
+
+int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status)
+{
+ int signum = status;
+ const char *sigdesc;
+
+ /* Child died... if it died due to a fatal error,
+ * we should simply bail out. The caller needs to
+ * check for bad rc from us and exit, running any
+ * appropriate cleanups.
+ *
+ * If the child died due to a resource shortage,
+ * the parent should limit the rate of forking
+ */
+ if (APR_PROC_CHECK_EXIT(why)) {
+ if (status == APEXIT_CHILDSICK) {
+ return status;
+ }
+
+ if (status == APEXIT_CHILDFATAL) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT,
+ 0, ap_server_conf, APLOGNO(00050)
+ "Child %" APR_PID_T_FMT
+ " returned a Fatal error... Apache is exiting!",
+ pid->pid);
+ return APEXIT_CHILDFATAL;
+ }
+
+ return 0;
+ }
+
+ if (APR_PROC_CHECK_SIGNALED(why)) {
+ sigdesc = apr_signal_description_get(signum);
+
+ switch (signum) {
+ case SIGTERM:
+ case SIGHUP:
+ case AP_SIG_GRACEFUL:
+ case SIGKILL:
+ break;
+
+ default:
+ if (APR_PROC_CHECK_CORE_DUMP(why)) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE,
+ 0, ap_server_conf, APLOGNO(00051)
+ "child pid %ld exit signal %s (%d), "
+ "possible coredump in %s",
+ (long)pid->pid, sigdesc, signum,
+ ap_coredump_dir);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE,
+ 0, ap_server_conf, APLOGNO(00052)
+ "child pid %ld exit signal %s (%d)",
+ (long)pid->pid, sigdesc, signum);
+ }
+ }
+ }
+ return 0;
+}
+
+AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod)
+{
+ apr_status_t rv;
+
+ *pod = apr_palloc(p, sizeof(**pod));
+ rv = apr_file_pipe_create_ex(&((*pod)->pod_in), &((*pod)->pod_out),
+ APR_WRITE_BLOCK, p);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ apr_file_pipe_timeout_set((*pod)->pod_in, 0);
+ (*pod)->p = p;
+
+ /* close these before exec. */
+ apr_file_inherit_unset((*pod)->pod_in);
+ apr_file_inherit_unset((*pod)->pod_out);
+
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(apr_status_t) ap_mpm_pod_check(ap_pod_t *pod)
+{
+ char c;
+ apr_size_t len = 1;
+ apr_status_t rv;
+
+ rv = apr_file_read(pod->pod_in, &c, &len);
+
+ if ((rv == APR_SUCCESS) && (len == 1)) {
+ return APR_SUCCESS;
+ }
+
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ return AP_NORESTART;
+}
+
+AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod)
+{
+ apr_status_t rv;
+
+ rv = apr_file_close(pod->pod_out);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ rv = apr_file_close(pod->pod_in);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t pod_signal_internal(ap_pod_t *pod)
+{
+ apr_status_t rv;
+ char char_of_death = '!';
+ apr_size_t one = 1;
+
+ rv = apr_file_write(pod->pod_out, &char_of_death, &one);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00053)
+ "write pipe_of_death");
+ }
+
+ return rv;
+}
+
+/* This function connects to the server, then immediately closes the connection.
+ * This permits the MPM to skip the poll when there is only one listening
+ * socket, because it provides a alternate way to unblock an accept() when
+ * the pod is used.
+ */
+static apr_status_t dummy_connection(ap_pod_t *pod)
+{
+ char *srequest;
+ apr_status_t rv;
+ apr_socket_t *sock;
+ apr_pool_t *p;
+ apr_size_t len;
+ ap_listen_rec *lp;
+
+ /* create a temporary pool for the socket. pconf stays around too long */
+ rv = apr_pool_create(&p, pod->p);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ /* If possible, find a listener which is configured for
+ * plain-HTTP, not SSL; using an SSL port would either be
+ * expensive to do correctly (performing a complete SSL handshake)
+ * or cause log spam by doing incorrectly (simply sending EOF). */
+ lp = ap_listeners;
+ while (lp && lp->protocol && strcasecmp(lp->protocol, "http") != 0) {
+ lp = lp->next;
+ }
+ if (!lp) {
+ lp = ap_listeners;
+ }
+
+ rv = apr_socket_create(&sock, lp->bind_addr->family, SOCK_STREAM, 0, p);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00054)
+ "get socket to connect to listener");
+ apr_pool_destroy(p);
+ return rv;
+ }
+
+ /* on some platforms (e.g., FreeBSD), the kernel won't accept many
+ * queued connections before it starts blocking local connects...
+ * we need to keep from blocking too long and instead return an error,
+ * because the MPM won't want to hold up a graceful restart for a
+ * long time
+ */
+ rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00055)
+ "set timeout on socket to connect to listener");
+ apr_socket_close(sock);
+ apr_pool_destroy(p);
+ return rv;
+ }
+
+ rv = apr_socket_connect(sock, lp->bind_addr);
+ if (rv != APR_SUCCESS) {
+ int log_level = APLOG_WARNING;
+
+ if (APR_STATUS_IS_TIMEUP(rv)) {
+ /* probably some server processes bailed out already and there
+ * is nobody around to call accept and clear out the kernel
+ * connection queue; usually this is not worth logging
+ */
+ log_level = APLOG_DEBUG;
+ }
+
+ ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf, APLOGNO(00056)
+ "connect to listener on %pI", lp->bind_addr);
+ }
+
+ /* Create the request string. We include a User-Agent so that
+ * adminstrators can track down the cause of the odd-looking
+ * requests in their logs.
+ */
+ srequest = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ",
+ ap_get_server_description(),
+ " (internal dummy connection)\r\n\r\n", NULL);
+
+ /* Since some operating systems support buffering of data or entire
+ * requests in the kernel, we send a simple request, to make sure
+ * the server pops out of a blocking accept().
+ */
+ /* XXX: This is HTTP specific. We should look at the Protocol for each
+ * listener, and send the correct type of request to trigger any Accept
+ * Filters.
+ */
+ len = strlen(srequest);
+ apr_socket_send(sock, srequest, &len);
+ apr_socket_close(sock);
+ apr_pool_destroy(p);
+
+ return rv;
+}
+
+AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod)
+{
+ apr_status_t rv;
+
+ rv = pod_signal_internal(pod);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ return dummy_connection(pod);
+}
+
+void ap_mpm_pod_killpg(ap_pod_t *pod, int num)
+{
+ int i;
+ apr_status_t rv = APR_SUCCESS;
+
+ /* we don't write anything to the pod here... we assume
+ * that the would-be reader of the pod has another way to
+ * see that it is time to die once we wake it up
+ *
+ * writing lots of things to the pod at once is very
+ * problematic... we can fill the kernel pipe buffer and
+ * be blocked until somebody consumes some bytes or
+ * we hit a timeout... if we hit a timeout we can't just
+ * keep trying because maybe we'll never successfully
+ * write again... but then maybe we'll leave would-be
+ * readers stranded (a number of them could be tied up for
+ * a while serving time-consuming requests)
+ */
+ for (i = 0; i < num && rv == APR_SUCCESS; i++) {
+ rv = dummy_connection(pod);
+ }
+}
+
+static const char *dash_k_arg = NULL;
+static const char *dash_k_arg_noarg = "noarg";
+
+static int send_signal(pid_t pid, int sig)
+{
+ if (kill(pid, sig) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, errno, NULL, APLOGNO(00057)
+ "sending signal to server");
+ return 1;
+ }
+ return 0;
+}
+
+int ap_signal_server(int *exit_status, apr_pool_t *pconf)
+{
+ apr_status_t rv;
+ pid_t otherpid;
+ int running = 0;
+ const char *status;
+
+ *exit_status = 0;
+
+ rv = ap_read_pid(pconf, ap_pid_fname, &otherpid);
+ if (rv != APR_SUCCESS) {
+ if (!APR_STATUS_IS_ENOENT(rv)) {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, NULL, APLOGNO(00058)
+ "Error retrieving pid file %s", ap_pid_fname);
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00059)
+ "Remove it before continuing if it is corrupted.");
+ *exit_status = 1;
+ return 1;
+ }
+ status = "httpd (no pid file) not running";
+ }
+ else {
+ if (kill(otherpid, 0) == 0) {
+ running = 1;
+ status = apr_psprintf(pconf,
+ "httpd (pid %" APR_PID_T_FMT ") already "
+ "running", otherpid);
+ }
+ else {
+ status = apr_psprintf(pconf,
+ "httpd (pid %" APR_PID_T_FMT "?) not running",
+ otherpid);
+ }
+ }
+
+ if (!strcmp(dash_k_arg, "start") || dash_k_arg == dash_k_arg_noarg) {
+ if (running) {
+ printf("%s\n", status);
+ return 1;
+ }
+ }
+
+ if (!strcmp(dash_k_arg, "stop")) {
+ if (!running) {
+ printf("%s\n", status);
+ }
+ else {
+ send_signal(otherpid, SIGTERM);
+ }
+ return 1;
+ }
+
+ if (!strcmp(dash_k_arg, "restart")) {
+ if (!running) {
+ printf("httpd not running, trying to start\n");
+ }
+ else {
+ *exit_status = send_signal(otherpid, SIGHUP);
+ return 1;
+ }
+ }
+
+ if (!strcmp(dash_k_arg, "graceful")) {
+ if (!running) {
+ printf("httpd not running, trying to start\n");
+ }
+ else {
+ *exit_status = send_signal(otherpid, AP_SIG_GRACEFUL);
+ return 1;
+ }
+ }
+
+ if (!strcmp(dash_k_arg, "graceful-stop")) {
+ if (!running) {
+ printf("%s\n", status);
+ }
+ else {
+ *exit_status = send_signal(otherpid, AP_SIG_GRACEFUL_STOP);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+void ap_mpm_rewrite_args(process_rec *process)
+{
+ apr_array_header_t *mpm_new_argv;
+ apr_status_t rv;
+ apr_getopt_t *opt;
+ char optbuf[3];
+ const char *optarg;
+
+ mpm_new_argv = apr_array_make(process->pool, process->argc,
+ sizeof(const char **));
+ *(const char **)apr_array_push(mpm_new_argv) = process->argv[0];
+ apr_getopt_init(&opt, process->pool, process->argc, process->argv);
+ opt->errfn = NULL;
+ optbuf[0] = '-';
+ /* option char returned by apr_getopt() will be stored in optbuf[1] */
+ optbuf[2] = '\0';
+ while ((rv = apr_getopt(opt, "k:" AP_SERVER_BASEARGS,
+ optbuf + 1, &optarg)) == APR_SUCCESS) {
+ switch(optbuf[1]) {
+ case 'k':
+ if (!dash_k_arg) {
+ if (!strcmp(optarg, "start") || !strcmp(optarg, "stop") ||
+ !strcmp(optarg, "restart") || !strcmp(optarg, "graceful") ||
+ !strcmp(optarg, "graceful-stop")) {
+ dash_k_arg = optarg;
+ break;
+ }
+ }
+ default:
+ *(const char **)apr_array_push(mpm_new_argv) =
+ apr_pstrdup(process->pool, optbuf);
+ if (optarg) {
+ *(const char **)apr_array_push(mpm_new_argv) = optarg;
+ }
+ }
+ }
+
+ /* back up to capture the bad argument */
+ if (rv == APR_BADCH || rv == APR_BADARG) {
+ opt->ind--;
+ }
+
+ while (opt->ind < opt->argc) {
+ *(const char **)apr_array_push(mpm_new_argv) =
+ apr_pstrdup(process->pool, opt->argv[opt->ind++]);
+ }
+
+ process->argc = mpm_new_argv->nelts;
+ process->argv = (const char * const *)mpm_new_argv->elts;
+
+ if (NULL == dash_k_arg) {
+ dash_k_arg = dash_k_arg_noarg;
+ }
+
+ APR_REGISTER_OPTIONAL_FN(ap_signal_server);
+}
+
+static pid_t parent_pid, my_pid;
+static apr_pool_t *pconf;
+
+#if AP_ENABLE_EXCEPTION_HOOK
+
+static int exception_hook_enabled;
+
+const char *ap_mpm_set_exception_hook(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (cmd->server->is_virtual) {
+ return "EnableExceptionHook directive not allowed in <VirtualHost>";
+ }
+
+ if (strcasecmp(arg, "on") == 0) {
+ exception_hook_enabled = 1;
+ }
+ else if (strcasecmp(arg, "off") == 0) {
+ exception_hook_enabled = 0;
+ }
+ else {
+ return "parameter must be 'on' or 'off'";
+ }
+
+ return NULL;
+}
+
+static void run_fatal_exception_hook(int sig)
+{
+ ap_exception_info_t ei = {0};
+
+ if (exception_hook_enabled &&
+ geteuid() != 0 &&
+ my_pid != parent_pid) {
+ ei.sig = sig;
+ ei.pid = my_pid;
+ ap_run_fatal_exception(&ei);
+ }
+}
+#endif /* AP_ENABLE_EXCEPTION_HOOK */
+
+/* handle all varieties of core dumping signals */
+static void sig_coredump(int sig)
+{
+ apr_filepath_set(ap_coredump_dir, pconf);
+ apr_signal(sig, SIG_DFL);
+#if AP_ENABLE_EXCEPTION_HOOK
+ run_fatal_exception_hook(sig);
+#endif
+ /* linuxthreads issue calling getpid() here:
+ * This comparison won't match if the crashing thread is
+ * some module's thread that runs in the parent process.
+ * The fallout, which is limited to linuxthreads:
+ * The special log message won't be written when such a
+ * thread in the parent causes the parent to crash.
+ */
+ if (getpid() == parent_pid) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE,
+ 0, ap_server_conf, APLOGNO(00060)
+ "seg fault or similar nasty error detected "
+ "in the parent process");
+ /* XXX we can probably add some rudimentary cleanup code here,
+ * like getting rid of the pid file. If any additional bad stuff
+ * happens, we are protected from recursive errors taking down the
+ * system since this function is no longer the signal handler GLA
+ */
+ }
+ kill(getpid(), sig);
+ /* At this point we've got sig blocked, because we're still inside
+ * the signal handler. When we leave the signal handler it will
+ * be unblocked, and we'll take the signal... and coredump or whatever
+ * is appropriate for this particular Unix. In addition the parent
+ * will see the real signal we received -- whereas if we called
+ * abort() here, the parent would only see SIGABRT.
+ */
+}
+
+apr_status_t ap_fatal_signal_child_setup(server_rec *s)
+{
+ my_pid = getpid();
+ return APR_SUCCESS;
+}
+
+apr_status_t ap_fatal_signal_setup(server_rec *s, apr_pool_t *in_pconf)
+{
+#ifndef NO_USE_SIGACTION
+ struct sigaction sa;
+
+ sigemptyset(&sa.sa_mask);
+
+#if defined(SA_ONESHOT)
+ sa.sa_flags = SA_ONESHOT;
+#elif defined(SA_RESETHAND)
+ sa.sa_flags = SA_RESETHAND;
+#else
+ sa.sa_flags = 0;
+#endif
+
+ sa.sa_handler = sig_coredump;
+ if (sigaction(SIGSEGV, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00061) "sigaction(SIGSEGV)");
+#ifdef SIGBUS
+ if (sigaction(SIGBUS, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00062) "sigaction(SIGBUS)");
+#endif
+#ifdef SIGABORT
+ if (sigaction(SIGABORT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00063) "sigaction(SIGABORT)");
+#endif
+#ifdef SIGABRT
+ if (sigaction(SIGABRT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00064) "sigaction(SIGABRT)");
+#endif
+#ifdef SIGILL
+ if (sigaction(SIGILL, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00065) "sigaction(SIGILL)");
+#endif
+#ifdef SIGFPE
+ if (sigaction(SIGFPE, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00066) "sigaction(SIGFPE)");
+#endif
+
+#else /* NO_USE_SIGACTION */
+
+ apr_signal(SIGSEGV, sig_coredump);
+#ifdef SIGBUS
+ apr_signal(SIGBUS, sig_coredump);
+#endif /* SIGBUS */
+#ifdef SIGABORT
+ apr_signal(SIGABORT, sig_coredump);
+#endif /* SIGABORT */
+#ifdef SIGABRT
+ apr_signal(SIGABRT, sig_coredump);
+#endif /* SIGABRT */
+#ifdef SIGILL
+ apr_signal(SIGILL, sig_coredump);
+#endif /* SIGILL */
+#ifdef SIGFPE
+ apr_signal(SIGFPE, sig_coredump);
+#endif /* SIGFPE */
+
+#endif /* NO_USE_SIGACTION */
+
+ pconf = in_pconf;
+ parent_pid = my_pid = getpid();
+
+ return APR_SUCCESS;
+}
+
+#endif /* WIN32 */
diff --git a/server/protocol.c b/server/protocol.c
index 55468fc1..733cdaac 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -33,7 +33,6 @@
#define APR_WANT_MEMFUNC
#include "apr_want.h"
-#define CORE_PRIVATE
#include "util_filter.h"
#include "ap_config.h"
#include "httpd.h"
@@ -57,12 +56,17 @@
#include <unistd.h>
#endif
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
APR_HOOK_STRUCT(
+ APR_HOOK_LINK(pre_read_request)
APR_HOOK_LINK(post_read_request)
APR_HOOK_LINK(log_transaction)
APR_HOOK_LINK(http_scheme)
APR_HOOK_LINK(default_port)
+ APR_HOOK_LINK(note_auth_failure)
)
AP_DECLARE_DATA ap_filter_rec_t *ap_old_write_func = NULL;
@@ -95,7 +99,7 @@ AP_DECLARE(void) ap_setup_make_content_type(apr_pool_t *pool)
/*
* Builds the content-type that should be sent to the client from the
* content-type specified. The following rules are followed:
- * - if type is NULL, type is set to ap_default_type(r)
+ * - if type is NULL or "", return NULL (do not set content-type).
* - if charset adding is disabled, stop processing and return type.
* - then, if there are no parameters on type, add the default charset
* - return type
@@ -104,21 +108,19 @@ AP_DECLARE(const char *)ap_make_content_type(request_rec *r, const char *type)
{
const apr_strmatch_pattern **pcset;
core_dir_config *conf =
- (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
+ (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
core_request_config *request_conf;
apr_size_t type_len;
- if (!type) {
- type = ap_default_type(r);
+ if (!type || *type == '\0') {
+ return NULL;
}
if (conf->add_default_charset != ADD_DEFAULT_CHARSET_ON) {
return type;
}
- request_conf =
- ap_get_module_config(r->request_config, &core_module);
+ request_conf = ap_get_core_module_config(r->request_config);
if (request_conf->suppress_charset) {
return type;
}
@@ -222,9 +224,8 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
* against APR_ASCII_LF at the end of the loop if bb only contains
* zero-length buckets.
*/
- if (last_char) {
+ if (last_char)
*last_char = '\0';
- }
for (;;) {
apr_brigade_cleanup(bb);
@@ -433,8 +434,13 @@ AP_DECLARE(apr_status_t) ap_rgetline_core(char **s, apr_size_t n,
}
}
}
-
*read = bytes_handled;
+
+ /* PR#43039: We shouldn't accept NULL bytes within the line */
+ if (strlen(*s) < bytes_handled) {
+ return APR_EINVAL;
+ }
+
return APR_SUCCESS;
}
@@ -603,7 +609,7 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
* buffer before finding the end-of-line. This is only going to
* happen if it exceeds the configured limit for a request-line.
*/
- if (rv == APR_ENOSPC) {
+ if (APR_STATUS_IS_ENOSPC(rv)) {
r->status = HTTP_REQUEST_URI_TOO_LARGE;
r->proto_num = HTTP_VERSION(1,0);
r->protocol = apr_pstrdup(r->pool, "HTTP/1.0");
@@ -611,10 +617,19 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
else if (APR_STATUS_IS_TIMEUP(rv)) {
r->status = HTTP_REQUEST_TIME_OUT;
}
+ else if (APR_STATUS_IS_EINVAL(rv)) {
+ r->status = HTTP_BAD_REQUEST;
+ }
return 0;
}
} while ((len <= 0) && (++num_blank_lines < max_blank_lines));
+ if (APLOGrtrace5(r)) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r,
+ "Request received from client: %s",
+ ap_escape_logitem(r->pool, r->the_request));
+ }
+
/* we've probably got something to do, ignore graceful restart requests */
r->request_time = apr_time_now();
@@ -640,6 +655,26 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
ap_parse_uri(r, uri);
+ /* RFC 2616:
+ * Request-URI = "*" | absoluteURI | abs_path | authority
+ *
+ * authority is a special case for CONNECT. If the request is not
+ * using CONNECT, and the parsed URI does not have scheme, and
+ * it does not begin with '/', and it is not '*', then, fail
+ * and give a 400 response. */
+ if (r->method_number != M_CONNECT
+ && !r->parsed_uri.scheme
+ && uri[0] != '/'
+ && !(uri[0] == '*' && uri[1] == '\0')) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00559)
+ "invalid request-URI %s", uri);
+ r->args = NULL;
+ r->hostname = NULL;
+ r->status = HTTP_BAD_REQUEST;
+ r->uri = apr_pstrdup(r->pool, uri);
+ return 0;
+ }
+
if (ll[0]) {
r->assbackwards = 0;
pro = ll;
@@ -670,6 +705,35 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
return 1;
}
+static int table_do_fn_check_lengths(void *r_, const char *key,
+ const char *value)
+{
+ request_rec *r = r_;
+ if (value == NULL || r->server->limit_req_fieldsize >= strlen(value) )
+ return 1;
+
+ r->status = HTTP_BAD_REQUEST;
+ apr_table_setn(r->notes, "error-notes",
+ apr_pstrcat(r->pool, "Size of a request header field "
+ "after merging exceeds server limit.<br />"
+ "\n<pre>\n",
+ ap_escape_html(r->pool, key),
+ "</pre>\n", NULL));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00560) "Request header "
+ "exceeds LimitRequestFieldSize after merging: %s", key);
+ return 0;
+}
+
+/* get the length of the field name for logging, but no more than 80 bytes */
+#define LOG_NAME_MAX_LEN 80
+static int field_name_len(const char *field)
+{
+ const char *end = ap_strchr_c(field, ':');
+ if (end == NULL || end - field > LOG_NAME_MAX_LEN)
+ return LOG_NAME_MAX_LEN;
+ return end - field;
+}
+
AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb)
{
char *last_field = NULL;
@@ -715,6 +779,9 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
"<pre>\n",
ap_escape_html(r->pool, field),
"</pre>\n", NULL));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00561)
+ "Request header exceeds LimitRequestFieldSize: "
+ "%.*s", field_name_len(field), field);
}
return;
}
@@ -742,6 +809,10 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
"<pre>\n",
ap_escape_html(r->pool, last_field),
"</pre>\n", NULL));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00562)
+ "Request header exceeds LimitRequestFieldSize "
+ "after folding: %.*s",
+ field_name_len(last_field), last_field);
return;
}
@@ -767,6 +838,9 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
apr_table_setn(r->notes, "error-notes",
"The number of request header fields "
"exceeds this server's limit.");
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00563)
+ "Number of request headers exceeds "
+ "LimitRequestFields");
return;
}
@@ -780,6 +854,10 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
ap_escape_html(r->pool,
last_field),
"</pre>\n", NULL));
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00564)
+ "Request header field is missing ':' "
+ "separator: %.*s", (int)LOG_NAME_MAX_LEN,
+ last_field);
return;
}
@@ -831,7 +909,13 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
}
}
+ /* Combine multiple message-header fields with the same
+ * field-name, following RFC 2616, 4.2.
+ */
apr_table_compress(r->headers_in, APR_OVERLAP_TABLES_MERGE);
+
+ /* enforce LimitRequestFieldSize for merged headers */
+ apr_table_do(table_do_fn_check_lengths, r, r->headers_in, NULL);
}
AP_DECLARE(void) ap_get_mime_headers(request_rec *r)
@@ -852,9 +936,11 @@ request_rec *ap_read_request(conn_rec *conn)
apr_socket_t *csd;
apr_interval_time_t cur_timeout;
+
apr_pool_create(&p, conn->pool);
apr_pool_tag(p, "request");
r = apr_pcalloc(p, sizeof(request_rec));
+ AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn);
r->pool = p;
r->connection = conn;
r->server = conn->base_server;
@@ -893,18 +979,31 @@ request_rec *ap_read_request(conn_rec *conn)
*/
r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
+ r->useragent_addr = conn->client_addr;
+ r->useragent_ip = conn->client_ip;
+
tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ ap_run_pre_read_request(r, conn);
+
/* Get the request... */
if (!read_request_line(r, tmp_bb)) {
- if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "request failed: URI too long (longer than %d)", r->server->limit_req_line);
+ if (r->status == HTTP_REQUEST_URI_TOO_LARGE
+ || r->status == HTTP_BAD_REQUEST) {
+ if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00565)
+ "request failed: URI too long (longer than %d)",
+ r->server->limit_req_line);
+ }
+ else if (r->method == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00566)
+ "request failed: invalid characters in URI");
+ }
ap_send_error_response(r, 0);
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
ap_run_log_transaction(r);
apr_brigade_destroy(tmp_bb);
- return r;
+ goto traceout;
}
else if (r->status == HTTP_REQUEST_TIME_OUT) {
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
@@ -912,18 +1011,19 @@ request_rec *ap_read_request(conn_rec *conn)
ap_run_log_transaction(r);
}
apr_brigade_destroy(tmp_bb);
- return r;
+ goto traceout;
}
apr_brigade_destroy(tmp_bb);
- return NULL;
+ r = NULL;
+ goto traceout;
}
/* We may have been in keep_alive_timeout mode, so toggle back
* to the normal timeout mode as we fetch the header lines,
* as necessary.
*/
- csd = ap_get_module_config(conn->conn_config, &core_module);
+ csd = ap_get_conn_socket(conn);
apr_socket_timeout_get(csd, &cur_timeout);
if (cur_timeout != conn->base_server->timeout) {
apr_socket_timeout_set(csd, conn->base_server->timeout);
@@ -933,13 +1033,13 @@ request_rec *ap_read_request(conn_rec *conn)
if (!r->assbackwards) {
ap_get_mime_headers_core(r, tmp_bb);
if (r->status != HTTP_OK) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00567)
"request failed: error reading the headers");
ap_send_error_response(r, 0);
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
ap_run_log_transaction(r);
apr_brigade_destroy(tmp_bb);
- return r;
+ goto traceout;
}
if (apr_table_get(r->headers_in, "Transfer-Encoding")
@@ -958,7 +1058,7 @@ request_rec *ap_read_request(conn_rec *conn)
* headers! Have to dink things just to make sure the error message
* comes through...
*/
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00568)
"client sent invalid HTTP/0.9 request: HEAD %s",
r->uri);
r->header_only = 0;
@@ -967,7 +1067,7 @@ request_rec *ap_read_request(conn_rec *conn)
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
ap_run_log_transaction(r);
apr_brigade_destroy(tmp_bb);
- return r;
+ goto traceout;
}
}
@@ -1000,7 +1100,7 @@ request_rec *ap_read_request(conn_rec *conn)
* a Host: header, and the server MUST respond with 400 if it doesn't.
*/
r->status = HTTP_BAD_REQUEST;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00569)
"client sent HTTP/1.1 request without hostname "
"(see RFC2616 section 14.23): %s", r->uri);
}
@@ -1019,14 +1119,15 @@ request_rec *ap_read_request(conn_rec *conn)
ap_send_error_response(r, 0);
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
ap_run_log_transaction(r);
- return r;
+ goto traceout;
}
if ((access_status = ap_run_post_read_request(r))) {
ap_die(access_status, r);
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
ap_run_log_transaction(r);
- return NULL;
+ r = NULL;
+ goto traceout;
}
if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
@@ -1042,16 +1143,20 @@ request_rec *ap_read_request(conn_rec *conn)
}
else {
r->status = HTTP_EXPECTATION_FAILED;
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570)
"client sent an unrecognized expectation value of "
"Expect: %s", expect);
ap_send_error_response(r, 0);
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
ap_run_log_transaction(r);
- return r;
+ goto traceout;
}
}
+ AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, (char *)r->uri, (char *)r->server->defn_name, r->status);
+ return r;
+ traceout:
+ AP_READ_REQUEST_FAILURE((uintptr_t)r);
return r;
}
@@ -1095,13 +1200,13 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew,
rnew->status = HTTP_OK;
- rnew->headers_in = apr_table_copy(rnew->pool, r->headers_in);
+ rnew->headers_in = apr_table_copy(rnew->pool, r->headers_in);
/* did the original request have a body? (e.g. POST w/SSI tags)
* if so, make sure the subrequest doesn't inherit body headers
*/
- if (apr_table_get(r->headers_in, "Content-Length")
- || apr_table_get(r->headers_in, "Transfer-Encoding")) {
+ if (!r->kept_body && (apr_table_get(r->headers_in, "Content-Length")
+ || apr_table_get(r->headers_in, "Transfer-Encoding"))) {
strip_headers_request_body(rnew);
}
rnew->subprocess_env = apr_table_copy(rnew->pool, r->subprocess_env);
@@ -1158,42 +1263,22 @@ AP_DECLARE(void) ap_note_auth_failure(request_rec *r)
{
const char *type = ap_auth_type(r);
if (type) {
- if (!strcasecmp(type, "Basic"))
- ap_note_basic_auth_failure(r);
- else if (!strcasecmp(type, "Digest"))
- ap_note_digest_auth_failure(r);
+ ap_run_note_auth_failure(r, type);
}
else {
ap_log_rerror(APLOG_MARK, APLOG_ERR,
- 0, r, "need AuthType to note auth failure: %s", r->uri);
+ 0, r, APLOGNO(00571) "need AuthType to note auth failure: %s", r->uri);
}
}
AP_DECLARE(void) ap_note_basic_auth_failure(request_rec *r)
{
- const char *type = ap_auth_type(r);
-
- /* if there is no AuthType configure or it is something other than
- * Basic, let ap_note_auth_failure() deal with it
- */
- if (!type || strcasecmp(type, "Basic"))
- ap_note_auth_failure(r);
- else
- apr_table_setn(r->err_headers_out,
- (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate"
- : "WWW-Authenticate",
- apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r),
- "\"", NULL));
+ ap_note_auth_failure(r);
}
AP_DECLARE(void) ap_note_digest_auth_failure(request_rec *r)
{
- apr_table_setn(r->err_headers_out,
- (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate"
- : "WWW-Authenticate",
- apr_psprintf(r->pool, "Digest realm=\"%s\", nonce=\""
- "%" APR_UINT64_T_HEX_FMT "\"",
- ap_auth_name(r), (apr_uint64_t)r->request_time));
+ ap_note_auth_failure(r);
}
AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
@@ -1209,20 +1294,20 @@ AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
if (!ap_auth_name(r)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR,
- 0, r, "need AuthName: %s", r->uri);
+ 0, r, APLOGNO(00572) "need AuthName: %s", r->uri);
return HTTP_INTERNAL_SERVER_ERROR;
}
if (!auth_line) {
- ap_note_basic_auth_failure(r);
+ ap_note_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
/* Client tried to authenticate using wrong auth scheme */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00573)
"client used wrong authentication scheme: %s", r->uri);
- ap_note_basic_auth_failure(r);
+ ap_note_auth_failure(r);
return HTTP_UNAUTHORIZED;
}
@@ -1244,6 +1329,7 @@ struct content_length_ctx {
* least one bucket on to the next output filter
* for this request
*/
+ apr_bucket_brigade *tmpbb;
};
/* This filter computes the content length, but it also computes the number
@@ -1264,6 +1350,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(
if (!ctx) {
f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx));
ctx->data_sent = 0;
+ ctx->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
}
/* Loop through this set of buckets to compute their length
@@ -1293,16 +1380,17 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(
* do a blocking read on the next batch.
*/
if (e != APR_BRIGADE_FIRST(b)) {
- apr_bucket_brigade *split = apr_brigade_split(b, e);
- apr_bucket *flush = apr_bucket_flush_create(r->connection->bucket_alloc);
+ apr_bucket *flush;
+ apr_brigade_split_ex(b, e, ctx->tmpbb);
+ flush = apr_bucket_flush_create(r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(b, flush);
rv = ap_pass_brigade(f->next, b);
if (rv != APR_SUCCESS || f->c->aborted) {
- apr_brigade_destroy(split);
return rv;
}
- b = split;
+ apr_brigade_cleanup(b);
+ APR_BRIGADE_CONCAT(b, ctx->tmpbb);
e = APR_BRIGADE_FIRST(b);
ctx->data_sent = 1;
@@ -1311,7 +1399,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(
continue;
}
else {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00574)
"ap_content_length_filter: "
"apr_bucket_read() failed");
return rv;
@@ -1334,13 +1422,13 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(
* by something like proxy. the brigade only has an EOS bucket
* in this case, making r->bytes_sent zero.
*
- * if r->bytes_sent > 0 we have a (temporary) body whose length may
- * have been changed by a filter. the C-L header might not have been
- * updated so we do it here. long term it would be cleaner to have
- * such filters update or remove the C-L header, and just use it
+ * if r->bytes_sent > 0 we have a (temporary) body whose length may
+ * have been changed by a filter. the C-L header might not have been
+ * updated so we do it here. long term it would be cleaner to have
+ * such filters update or remove the C-L header, and just use it
* if present.
*/
- !(r->header_only && r->bytes_sent == 0 &&
+ !(r->header_only && r->bytes_sent == 0 &&
apr_table_get(r->headers_out, "Content-Length"))) {
ap_set_content_length(r, r->bytes_sent);
}
@@ -1358,12 +1446,11 @@ AP_DECLARE(apr_status_t) ap_send_fd(apr_file_t *fd, request_rec *r,
{
conn_rec *c = r->connection;
apr_bucket_brigade *bb = NULL;
- apr_bucket *b;
apr_status_t rv;
bb = apr_brigade_create(r->pool, c->bucket_alloc);
- b = apr_bucket_file_create(fd, offset, len, r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, b);
+
+ apr_brigade_insert_file(bb, fd, offset, len, r->pool);
rv = ap_pass_brigade(r->output_filters, bb);
if (rv != APR_SUCCESS) {
@@ -1396,6 +1483,7 @@ AP_DECLARE(size_t) ap_send_mmap(apr_mmap_t *mm, request_rec *r, size_t offset,
typedef struct {
apr_bucket_brigade *bb;
+ apr_bucket_brigade *tmpbb;
} old_write_filter_ctx;
AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(
@@ -1405,7 +1493,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(
AP_DEBUG_ASSERT(ctx);
- if (ctx->bb != 0) {
+ if (ctx->bb != NULL) {
/* whatever is coming down the pipe (we don't care), we
* can simply insert our buffered data at the front and
* pass the whole bundle down the chain.
@@ -1416,16 +1504,11 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_old_write_filter(
return ap_pass_brigade(f->next, bb);
}
-static apr_status_t buffer_output(request_rec *r,
- const char *str, apr_size_t len)
+static ap_filter_t *insert_old_write_filter(request_rec *r)
{
- conn_rec *c = r->connection;
ap_filter_t *f;
old_write_filter_ctx *ctx;
- if (len == 0)
- return APR_SUCCESS;
-
/* future optimization: record some flags in the request_rec to
* say whether we've added our filter, and whether it is first.
*/
@@ -1439,24 +1522,41 @@ static apr_status_t buffer_output(request_rec *r,
if (f == NULL) {
/* our filter hasn't been added yet */
ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+ ctx->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
ap_add_output_filter("OLD_WRITE", ctx, r, r->connection);
f = r->output_filters;
}
+ return f;
+}
+
+static apr_status_t buffer_output(request_rec *r,
+ const char *str, apr_size_t len)
+{
+ conn_rec *c = r->connection;
+ ap_filter_t *f;
+ old_write_filter_ctx *ctx;
+
+ if (len == 0)
+ return APR_SUCCESS;
+
+ f = insert_old_write_filter(r);
+ ctx = f->ctx;
+
/* if the first filter is not our buffering filter, then we have to
* deliver the content through the normal filter chain
*/
if (f != r->output_filters) {
- apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc);
+ apr_status_t rv;
apr_bucket *b = apr_bucket_transient_create(str, len, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, b);
+ APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, b);
- return ap_pass_brigade(r->output_filters, bb);
+ rv = ap_pass_brigade(r->output_filters, ctx->tmpbb);
+ apr_brigade_cleanup(ctx->tmpbb);
+ return rv;
}
- /* grab the context from our filter */
- ctx = r->output_filters->ctx;
-
if (ctx->bb == NULL) {
ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
}
@@ -1478,19 +1578,6 @@ AP_DECLARE(int) ap_rputc(int c, request_rec *r)
return c;
}
-AP_DECLARE(int) ap_rputs(const char *str, request_rec *r)
-{
- apr_size_t len;
-
- if (r->connection->aborted)
- return -1;
-
- if (buffer_output(r, str, len = strlen(str)) != APR_SUCCESS)
- return -1;
-
- return len;
-}
-
AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
{
if (r->connection->aborted)
@@ -1611,13 +1698,20 @@ AP_DECLARE_NONSTD(int) ap_rvputs(request_rec *r, ...)
AP_DECLARE(int) ap_rflush(request_rec *r)
{
conn_rec *c = r->connection;
- apr_bucket_brigade *bb;
apr_bucket *b;
+ ap_filter_t *f;
+ old_write_filter_ctx *ctx;
+ apr_status_t rv;
+
+ f = insert_old_write_filter(r);
+ ctx = f->ctx;
- bb = apr_brigade_create(r->pool, c->bucket_alloc);
b = apr_bucket_flush_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, b);
- if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS)
+ APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, b);
+
+ rv = ap_pass_brigade(r->output_filters, ctx->tmpbb);
+ apr_brigade_cleanup(ctx->tmpbb);
+ if (rv != APR_SUCCESS)
return -1;
return 0;
@@ -1643,14 +1737,12 @@ typedef struct hdr_ptr {
ap_filter_t *f;
apr_bucket_brigade *bb;
} hdr_ptr;
-
static int send_header(void *data, const char *key, const char *val)
{
ap_fputstrs(((hdr_ptr*)data)->f, ((hdr_ptr*)data)->bb,
key, ": ", val, CRLF, NULL);
return 1;
}
-
AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers)
{
hdr_ptr x;
@@ -1662,10 +1754,18 @@ AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers)
return;
}
if (!ap_is_HTTP_INFO(r->status)) {
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00575)
"Status is %d - not sending interim response", r->status);
return;
}
+ if ((r->status == HTTP_CONTINUE) && !r->expecting_100) {
+ /*
+ * Don't send 100-Continue when there was no Expect: 100-continue
+ * in the request headers. For origin servers this is a SHOULD NOT
+ * for proxies it is a MUST NOT according to RFC 2616 8.2.3
+ */
+ return;
+ }
/* if we send an interim response, we're no longer in a state of
* expecting one. Also, this could feasibly be in a subrequest,
@@ -1686,11 +1786,15 @@ AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers)
apr_table_do(send_header, &x, r->headers_out, NULL);
apr_table_clear(r->headers_out);
}
- ap_fputs(x.f, x.bb, CRLF);
+ ap_fputs(x.f, x.bb, CRLF_ASCII);
ap_fflush(x.f, x.bb);
apr_brigade_destroy(x.bb);
}
+
+AP_IMPLEMENT_HOOK_VOID(pre_read_request,
+ (request_rec *r, conn_rec *c),
+ (r, c))
AP_IMPLEMENT_HOOK_RUN_ALL(int,post_read_request,
(request_rec *r), (r), OK, DECLINED)
AP_IMPLEMENT_HOOK_RUN_ALL(int,log_transaction,
@@ -1699,3 +1803,6 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(const char *,http_scheme,
(const request_rec *r), (r), NULL)
AP_IMPLEMENT_HOOK_RUN_FIRST(unsigned short,default_port,
(const request_rec *r), (r), 0)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, note_auth_failure,
+ (request_rec *r, const char *auth_type),
+ (r, auth_type), DECLINED)
diff --git a/server/request.c b/server/request.c
index f076e637..bb2a054d 100644
--- a/server/request.c
+++ b/server/request.c
@@ -32,8 +32,8 @@
#define APR_WANT_STRFUNC
#include "apr_want.h"
-#define CORE_PRIVATE
#include "ap_config.h"
+#include "ap_provider.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
@@ -44,13 +44,20 @@
#include "util_filter.h"
#include "util_charset.h"
#include "util_script.h"
+#include "ap_expr.h"
+#include "mod_request.h"
#include "mod_core.h"
+#include "mod_auth.h"
#if APR_HAVE_STDARG_H
#include <stdarg.h>
#endif
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
APR_HOOK_STRUCT(
APR_HOOK_LINK(translate_name)
APR_HOOK_LINK(map_to_storage)
@@ -58,6 +65,7 @@ APR_HOOK_STRUCT(
APR_HOOK_LINK(fixups)
APR_HOOK_LINK(type_checker)
APR_HOOK_LINK(access_checker)
+ APR_HOOK_LINK(access_checker_ex)
APR_HOOK_LINK(auth_checker)
APR_HOOK_LINK(insert_filter)
APR_HOOK_LINK(create_request)
@@ -75,21 +83,30 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(int,type_checker,
(request_rec *r), (r), DECLINED)
AP_IMPLEMENT_HOOK_RUN_ALL(int,access_checker,
(request_rec *r), (r), OK, DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,access_checker_ex,
+ (request_rec *r), (r), DECLINED)
AP_IMPLEMENT_HOOK_RUN_FIRST(int,auth_checker,
(request_rec *r), (r), DECLINED)
AP_IMPLEMENT_HOOK_VOID(insert_filter, (request_rec *r), (r))
AP_IMPLEMENT_HOOK_RUN_ALL(int, create_request,
(request_rec *r), (r), OK, DECLINED)
+static int auth_internal_per_conf = 0;
+static int auth_internal_per_conf_hooks = 0;
+static int auth_internal_per_conf_providers = 0;
+
-static int decl_die(int status, char *phase, request_rec *r)
+static int decl_die(int status, const char *phase, request_rec *r)
{
if (status == DECLINED) {
- ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(00025)
"configuration error: couldn't %s: %s", phase, r->uri);
return HTTP_INTERNAL_SERVER_ERROR;
}
else {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
+ "auth phase '%s' gave status %d: %s", phase,
+ status, r->uri);
return status;
}
}
@@ -103,13 +120,13 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
{
int file_req = (r->main && r->filename);
int access_status;
+ core_dir_config *d;
/* Ignore embedded %2F's in path for proxy requests */
if (!r->proxyreq && r->parsed_uri.path) {
- core_dir_config *d;
- d = ap_get_module_config(r->per_dir_config, &core_module);
+ d = ap_get_core_module_config(r->per_dir_config);
if (d->allow_encoded_slashes) {
- access_status = ap_unescape_url_keep2f_ex(r->parsed_uri.path, d->decode_encoded_slashes);
+ access_status = ap_unescape_url_keep2f(r->parsed_uri.path, d->decode_encoded_slashes);
}
else {
access_status = ap_unescape_url(r->parsed_uri.path);
@@ -117,7 +134,7 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
if (access_status) {
if (access_status == HTTP_NOT_FOUND) {
if (! d->allow_encoded_slashes) {
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00026)
"found %%2f (encoded '/') in URI "
"(decoded='%s'), returning 404",
r->parsed_uri.path);
@@ -137,6 +154,14 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
if ((access_status = ap_location_walk(r))) {
return access_status;
}
+ if ((access_status = ap_if_walk(r))) {
+ return access_status;
+ }
+
+ d = ap_get_core_module_config(r->per_dir_config);
+ if (d->log) {
+ r->log = d->log;
+ }
if ((access_status = ap_run_translate_name(r))) {
return decl_die(access_status, "translate", r);
@@ -157,6 +182,14 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
if ((access_status = ap_location_walk(r))) {
return access_status;
}
+ if ((access_status = ap_if_walk(r))) {
+ return access_status;
+ }
+
+ d = ap_get_core_module_config(r->per_dir_config);
+ if (d->log) {
+ r->log = d->log;
+ }
/* Only on the main request! */
if (r->main == NULL) {
@@ -170,61 +203,81 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
* functions in map_to_storage that use the same merge results given
* identical input.) If the config changes, we must re-auth.
*/
- if (r->main && (r->main->per_dir_config == r->per_dir_config)) {
- r->user = r->main->user;
- r->ap_auth_type = r->main->ap_auth_type;
- }
- else if (r->prev && (r->prev->per_dir_config == r->per_dir_config)) {
+ if (r->prev && (r->prev->per_dir_config == r->per_dir_config)) {
r->user = r->prev->user;
r->ap_auth_type = r->prev->ap_auth_type;
}
+ else if (r->main && (r->main->per_dir_config == r->per_dir_config)) {
+ r->user = r->main->user;
+ r->ap_auth_type = r->main->ap_auth_type;
+ }
else {
switch (ap_satisfies(r)) {
case SATISFY_ALL:
case SATISFY_NOSPEC:
- if ((access_status = ap_run_access_checker(r)) != 0) {
- return decl_die(access_status, "check access", r);
+ if ((access_status = ap_run_access_checker(r)) != OK) {
+ return decl_die(access_status,
+ "check access (with Satisfy All)", r);
}
- if (ap_some_auth_required(r)) {
- if (((access_status = ap_run_check_user_id(r)) != 0)
- || !ap_auth_type(r)) {
- return decl_die(access_status, ap_auth_type(r)
- ? "check user. Check your authn provider!"
- : "perform authentication. AuthType not set!",
- r);
+ access_status = ap_run_access_checker_ex(r);
+ if (access_status == OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
+ "request authorized without authentication by "
+ "access_checker_ex hook: %s", r->uri);
+ }
+ else if (access_status != DECLINED) {
+ return decl_die(access_status, "check access", r);
+ }
+ else {
+ if ((access_status = ap_run_check_user_id(r)) != OK) {
+ return decl_die(access_status, "check user", r);
}
-
- if (((access_status = ap_run_auth_checker(r)) != 0)
- || !ap_auth_type(r)) {
- return decl_die(access_status, ap_auth_type(r)
- ? "check access. Check your 'Require' directive"
- : "perform authentication. AuthType not set!",
- r);
+ if (r->user == NULL) {
+ /* don't let buggy authn module crash us in authz */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00027)
+ "Buggy authn provider failed to set user for %s",
+ r->uri);
+ access_status = HTTP_INTERNAL_SERVER_ERROR;
+ return decl_die(access_status, "check user", r);
+ }
+ if ((access_status = ap_run_auth_checker(r)) != OK) {
+ return decl_die(access_status, "check authorization", r);
}
}
break;
-
case SATISFY_ANY:
- if (((access_status = ap_run_access_checker(r)) != 0)) {
- if (!ap_some_auth_required(r)) {
- return decl_die(access_status, "check access", r);
- }
+ if ((access_status = ap_run_access_checker(r)) == OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
+ "request authorized without authentication by "
+ "access_checker hook and 'Satisfy any': %s",
+ r->uri);
+ break;
+ }
- if (((access_status = ap_run_check_user_id(r)) != 0)
- || !ap_auth_type(r)) {
- return decl_die(access_status, ap_auth_type(r)
- ? "check user. Check your authn provider!"
- : "perform authentication. AuthType not set!",
- r);
+ access_status = ap_run_access_checker_ex(r);
+ if (access_status == OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
+ "request authorized without authentication by "
+ "access_checker_ex hook: %s", r->uri);
+ }
+ else if (access_status != DECLINED) {
+ return decl_die(access_status, "check access", r);
+ }
+ else {
+ if ((access_status = ap_run_check_user_id(r)) != OK) {
+ return decl_die(access_status, "check user", r);
}
-
- if (((access_status = ap_run_auth_checker(r)) != 0)
- || !ap_auth_type(r)) {
- return decl_die(access_status, ap_auth_type(r)
- ? "check access. Check your 'Require' directive"
- : "perform authentication. AuthType not set!",
- r);
+ if (r->user == NULL) {
+ /* don't let buggy authn module crash us in authz */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00028)
+ "Buggy authn provider failed to set user for %s",
+ r->uri);
+ access_status = HTTP_INTERNAL_SERVER_ERROR;
+ return decl_die(access_status, "check user", r);
+ }
+ if ((access_status = ap_run_auth_checker(r)) != OK) {
+ return decl_die(access_status, "check authorization", r);
}
}
break;
@@ -234,11 +287,13 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
* in mod-proxy for r->proxyreq && r->parsed_uri.scheme
* && !strcmp(r->parsed_uri.scheme, "http")
*/
- if ((access_status = ap_run_type_checker(r)) != 0) {
+ if ((access_status = ap_run_type_checker(r)) != OK) {
return decl_die(access_status, "find types", r);
}
- if ((access_status = ap_run_fixups(r)) != 0) {
+ if ((access_status = ap_run_fixups(r)) != OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "fixups hook gave %d: %s",
+ access_status, r->uri);
return access_status;
}
@@ -267,44 +322,59 @@ typedef struct walk_cache_t {
ap_conf_vector_t *dir_conf_merged; /* Base per_dir_config */
ap_conf_vector_t *per_dir_result; /* per_dir_config += walked result */
apr_array_header_t *walked; /* The list of walk_walked_t results */
+ struct walk_cache_t *prev; /* Prev cache of same call in this (sub)req */
+ int count; /* Number of prev invocations of same call in this (sub)req */
} walk_cache_t;
static walk_cache_t *prep_walk_cache(apr_size_t t, request_rec *r)
{
- walk_cache_t *cache;
- void **note;
-
- /* Find the most relevant, recent entry to work from. That would be
- * this request (on the second call), or the parent request of a
- * subrequest, or the prior request of an internal redirect. Provide
- * this _walk()er with a copy it is allowed to munge. If there is no
- * parent or prior cached request, then create a new walk cache.
+ void **note, **inherit_note;
+ walk_cache_t *cache, *prev_cache, *copy_cache;
+ int count;
+
+ /* Find the most relevant, recent walk cache to work from and provide
+ * a copy the caller is allowed to munge. In the case of a sub-request
+ * or internal redirect, this is the cache corresponding to the equivalent
+ * invocation of the same function call in the "parent" request, if such
+ * a cache exists. Otherwise it is the walk cache of the previous
+ * invocation of the same function call in the current request, if
+ * that exists; if not, then create a new walk cache.
*/
note = ap_get_request_note(r, t);
- if (!note) {
- return NULL;
- }
-
- if (!(cache = *note)) {
- void **inherit_note;
-
- if ((r->main
- && ((inherit_note = ap_get_request_note(r->main, t)))
- && *inherit_note)
- || (r->prev
- && ((inherit_note = ap_get_request_note(r->prev, t)))
- && *inherit_note)) {
- cache = apr_pmemdup(r->pool, *inherit_note,
- sizeof(*cache));
- cache->walked = apr_array_copy(r->pool, cache->walked);
+ AP_DEBUG_ASSERT(note != NULL);
+
+ copy_cache = prev_cache = *note;
+ count = prev_cache ? (prev_cache->count + 1) : 0;
+
+ if ((r->prev
+ && (inherit_note = ap_get_request_note(r->prev, t))
+ && *inherit_note)
+ || (r->main
+ && (inherit_note = ap_get_request_note(r->main, t))
+ && *inherit_note)) {
+ walk_cache_t *inherit_cache = *inherit_note;
+
+ while (inherit_cache->count > count) {
+ inherit_cache = inherit_cache->prev;
}
- else {
- cache = apr_pcalloc(r->pool, sizeof(*cache));
- cache->walked = apr_array_make(r->pool, 4, sizeof(walk_walked_t));
+ if (inherit_cache->count == count) {
+ copy_cache = inherit_cache;
}
+ }
- *note = cache;
+ if (copy_cache) {
+ cache = apr_pmemdup(r->pool, copy_cache, sizeof(*cache));
+ cache->walked = apr_array_copy(r->pool, cache->walked);
+ cache->prev = prev_cache;
+ cache->count = count;
+ }
+ else {
+ cache = apr_pcalloc(r->pool, sizeof(*cache));
+ cache->walked = apr_array_make(r->pool, 4, sizeof(walk_walked_t));
}
+
+ *note = cache;
+
return cache;
}
@@ -341,7 +411,6 @@ static walk_cache_t *prep_walk_cache(apr_size_t t, request_rec *r)
static int resolve_symlink(char *d, apr_finfo_t *lfi, int opts, apr_pool_t *p)
{
apr_finfo_t fi;
- int res;
const char *savename;
if (!(opts & (OPT_SYM_OWNER | OPT_SYM_LINKS))) {
@@ -353,9 +422,9 @@ static int resolve_symlink(char *d, apr_finfo_t *lfi, int opts, apr_pool_t *p)
/* if OPT_SYM_OWNER is unset, we only need to check target accessible */
if (!(opts & OPT_SYM_OWNER)) {
- if ((res = apr_stat(&fi, d, lfi->valid & ~(APR_FINFO_NAME
- | APR_FINFO_LINK), p))
- != APR_SUCCESS) {
+ if (apr_stat(&fi, d, lfi->valid & ~(APR_FINFO_NAME | APR_FINFO_LINK), p)
+ != APR_SUCCESS)
+ {
return HTTP_FORBIDDEN;
}
@@ -374,15 +443,14 @@ static int resolve_symlink(char *d, apr_finfo_t *lfi, int opts, apr_pool_t *p)
* owner of the symlink, then get the info of the target.
*/
if (!(lfi->valid & APR_FINFO_OWNER)) {
- if ((res = apr_stat(lfi, d,
- lfi->valid | APR_FINFO_LINK | APR_FINFO_OWNER, p))
- != APR_SUCCESS) {
+ if (apr_stat(lfi, d, lfi->valid | APR_FINFO_LINK | APR_FINFO_OWNER, p)
+ != APR_SUCCESS)
+ {
return HTTP_FORBIDDEN;
}
}
- if ((res = apr_stat(&fi, d, lfi->valid & ~(APR_FINFO_NAME), p))
- != APR_SUCCESS) {
+ if (apr_stat(&fi, d, lfi->valid & ~(APR_FINFO_NAME), p) != APR_SUCCESS) {
return HTTP_FORBIDDEN;
}
@@ -418,11 +486,12 @@ typedef struct core_opts_t {
allow_options_t remove;
overrides_t override;
overrides_t override_opts;
+ apr_table_t *override_list;
} core_opts_t;
static void core_opts_merge(const ap_conf_vector_t *sec, core_opts_t *opts)
{
- core_dir_config *this_dir = ap_get_module_config(sec, &core_module);
+ core_dir_config *this_dir = ap_get_core_module_config(sec);
if (!this_dir) {
return;
@@ -445,6 +514,11 @@ static void core_opts_merge(const ap_conf_vector_t *sec, core_opts_t *opts)
opts->override = this_dir->override;
opts->override_opts = this_dir->override_opts;
}
+
+ if (this_dir->override_list != NULL) {
+ opts->override_list = this_dir->override_list;
+ }
+
}
@@ -466,13 +540,14 @@ static void core_opts_merge(const ap_conf_vector_t *sec, core_opts_t *opts)
AP_DECLARE(int) ap_directory_walk(request_rec *r)
{
ap_conf_vector_t *now_merged = NULL;
- core_server_config *sconf = ap_get_module_config(r->server->module_config,
- &core_module);
+ core_server_config *sconf =
+ ap_get_core_module_config(r->server->module_config);
ap_conf_vector_t **sec_ent = (ap_conf_vector_t **) sconf->sec_dir->elts;
int num_sec = sconf->sec_dir->nelts;
walk_cache_t *cache;
char *entry_dir;
apr_status_t rv;
+ int cached;
/* XXX: Better (faster) tests needed!!!
*
@@ -483,7 +558,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
* handler. Leave INFO notes here for module debugging.
*/
if (r->filename == NULL) {
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00029)
"Module bug? Request filename is missing for URI %s",
r->uri);
return OK;
@@ -496,7 +571,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
if ((rv = apr_filepath_merge(&entry_dir, NULL, r->filename,
APR_FILEPATH_NOTRELATIVE, r->pool))
!= APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00030)
"Module bug? Request filename path %s is invalid or "
"or not absolute for uri %s",
r->filename, r->uri);
@@ -510,6 +585,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
r->filename = entry_dir;
cache = prep_walk_cache(AP_NOTE_DIRECTORY_WALK, r);
+ cached = (cache->cached != NULL);
/* If this is not a dirent subrequest with a preconstructed
* r->finfo value, then we can simply stat the filename to
@@ -521,7 +597,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
* types of failure, such as APR_ENOTDIR. We can do something
* with APR_ENOENT, knowing that the path is good.
*/
- if (!r->finfo.filetype || r->finfo.filetype == APR_LNK) {
+ if (r->finfo.filetype == APR_NOFILE || r->finfo.filetype == APR_LNK) {
rv = apr_stat(&r->finfo, r->filename, APR_FINFO_MIN, r->pool);
/* some OSs will return APR_SUCCESS/APR_REG if we stat
@@ -535,10 +611,10 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
* Also reset if the stat failed, just for safety.
*/
if ((rv != APR_SUCCESS) ||
- (r->finfo.filetype &&
+ (r->finfo.filetype != APR_NOFILE &&
(r->finfo.filetype != APR_DIR) &&
(r->filename[strlen(r->filename) - 1] == '/'))) {
- r->finfo.filetype = 0; /* forget what we learned */
+ r->finfo.filetype = APR_NOFILE; /* forget what we learned */
}
}
@@ -553,13 +629,12 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
* and the vhost's list of directory sections hasn't changed,
* we can skip rewalking the directory_walk entries.
*/
- if (cache->cached
+ if (cached
&& ((r->finfo.filetype == APR_REG)
|| ((r->finfo.filetype == APR_DIR)
&& (!r->path_info || !*r->path_info)))
&& (cache->dir_conf_tested == sec_ent)
&& (strcmp(entry_dir, cache->cached) == 0)) {
-
int familiar = 0;
/* Well this looks really familiar! If our end-result (per_dir_result)
@@ -571,17 +646,18 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
familiar = 1;
}
- else if (r->per_dir_config == cache->dir_conf_merged) {
+ if (r->per_dir_config == cache->dir_conf_merged) {
r->per_dir_config = cache->per_dir_result;
familiar = 1;
}
+
if (familiar) {
apr_finfo_t thisinfo;
int res;
allow_options_t opts;
core_dir_config *this_dir;
- this_dir = ap_get_module_config(r->per_dir_config, &core_module);
+ this_dir = ap_get_core_module_config(r->per_dir_config);
opts = this_dir->opts;
/*
* If Symlinks are allowed in general we do not need the following
@@ -607,7 +683,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
* with a HTTP_FORBIDDEN in case we hit a race condition
* here.
*/
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00031)
"access to %s failed; stat of '%s' failed.",
r->uri, r->filename);
return r->status = HTTP_FORBIDDEN;
@@ -616,7 +692,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
/* Is this a possibly acceptable symlink? */
if ((res = resolve_symlink(r->filename, &thisinfo,
opts, r->pool)) != OK) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00032)
"Symbolic link not allowed "
"or link target not accessible: %s",
r->filename);
@@ -638,6 +714,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
*/
int sec_idx;
int matches = cache->walked->nelts;
+ int cached_matches = matches;
walk_walked_t *last_walk = (walk_walked_t*)cache->walked->elts;
core_dir_config *this_dir;
core_opts_t opts;
@@ -655,18 +732,21 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
apr_size_t canonical_len;
#endif
+ cached &= auth_internal_per_conf;
+
/*
* We must play our own mini-merge game here, for the few
* running dir_config values we care about within dir_walk.
* We didn't start the merge from r->per_dir_config, so we
* accumulate opts and override as we merge, from the globals.
*/
- this_dir = ap_get_module_config(r->per_dir_config, &core_module);
+ this_dir = ap_get_core_module_config(r->per_dir_config);
opts.opts = this_dir->opts;
opts.add = this_dir->opts_add;
opts.remove = this_dir->opts_remove;
opts.override = this_dir->override;
opts.override_opts = this_dir->override_opts;
+ opts.override_list = this_dir->override_list;
/* Set aside path_info to merge back onto path_info later.
* If r->filename is a directory, we must remerge the path_info,
@@ -682,7 +762,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
r->path_info,
APR_FILEPATH_NOTABOVEROOT, r->pool))
!= APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00033)
"dir_walk error, path_info %s is not relative "
"to the filename path %s for uri %s",
r->path_info, r->filename, r->uri);
@@ -749,7 +829,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
#endif
if (rv != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00034)
"dir_walk error, could not determine the root "
"path of filename %s%s for uri %s",
r->filename, r->path_info, r->uri);
@@ -802,7 +882,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
ap_conf_vector_t *entry_config = sec_ent[sec_idx];
core_dir_config *entry_core;
- entry_core = ap_get_module_config(entry_config, &core_module);
+ entry_core = ap_get_core_module_config(entry_config);
/* No more possible matches for this many segments?
* We are done when we find relative/regex/longer components.
@@ -846,6 +926,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
*/
cache->walked->nelts -= matches;
matches = 0;
+ cached = 0;
}
if (now_merged) {
@@ -872,12 +953,13 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
/* No htaccess in an incomplete root path,
* nor if it's disabled
*/
- if (seg < startseg || !opts.override) {
+ if (seg < startseg || (!opts.override && opts.override_list == NULL)) {
break;
}
+
res = ap_parse_htaccess(&htaccess_conf, r, opts.override,
- opts.override_opts,
+ opts.override_opts, opts.override_list,
apr_pstrdup(r->pool, r->filename),
sconf->access_name);
if (res) {
@@ -913,6 +995,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
*/
cache->walked->nelts -= matches;
matches = 0;
+ cached = 0;
}
if (now_merged) {
@@ -979,7 +1062,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
* if...we have allowed symlinks
* skip the lstat and dummy up an APR_DIR value for thisinfo.
*/
- if (r->finfo.filetype
+ if (r->finfo.filetype != APR_NOFILE
#ifdef CASE_BLIND_FILESYSTEM
&& (filename_len <= canonical_len)
#endif
@@ -1010,8 +1093,10 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
break;
}
else if (APR_STATUS_IS_EACCES(rv)) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- "access to %s denied", r->uri);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00035)
+ "access to %s denied because search "
+ "permissions are missing on a component "
+ "of the path", r->uri);
return r->status = HTTP_FORBIDDEN;
}
else if ((rv != APR_SUCCESS && rv != APR_INCOMPLETE)
@@ -1019,7 +1104,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
/* If we hit ENOTDIR, we must have over-optimized, deny
* rather than assume not found.
*/
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00036)
"access to %s failed", r->uri);
return r->status = HTTP_FORBIDDEN;
}
@@ -1041,7 +1126,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
*/
if ((res = resolve_symlink(r->filename, &thisinfo,
opts.opts, r->pool)) != OK) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00037)
"Symbolic link not allowed "
"or link target not accessible: %s",
r->filename);
@@ -1058,7 +1143,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
break;
}
else if (thisinfo.filetype != APR_DIR) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00038)
"Forbidden: %s doesn't point to "
"a file or directory",
r->filename);
@@ -1071,7 +1156,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
/* If we have _not_ optimized, this is the time to recover
* the final stat result.
*/
- if (!r->finfo.filetype || r->finfo.filetype == APR_LNK) {
+ if (r->finfo.filetype == APR_NOFILE || r->finfo.filetype == APR_LNK) {
r->finfo = thisinfo;
}
@@ -1094,13 +1179,13 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
for (; sec_idx < num_sec; ++sec_idx) {
core_dir_config *entry_core;
- entry_core = ap_get_module_config(sec_ent[sec_idx], &core_module);
+ entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
if (!entry_core->r) {
continue;
}
- if (ap_regexec(entry_core->r, r->filename, 0, NULL, AP_REG_NOTEOL)) {
+ if (ap_regexec(entry_core->r, r->filename, 0, NULL, 0)) {
continue;
}
@@ -1125,6 +1210,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
*/
cache->walked->nelts -= matches;
matches = 0;
+ cached = 0;
}
if (now_merged) {
@@ -1141,11 +1227,16 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
last_walk->merged = now_merged;
}
- /* Whoops - everything matched in sequence, but the original walk
- * found some additional matches. Truncate them.
+ /* Whoops - everything matched in sequence, but either the original
+ * walk found some additional matches (which we need to truncate), or
+ * this walk found some additional matches.
*/
if (matches) {
cache->walked->nelts -= matches;
+ cached = 0;
+ }
+ else if (cache->walked->nelts > cached_matches) {
+ cached = 0;
}
}
@@ -1185,6 +1276,12 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
cache->cached = ap_make_dirstr_parent(r->pool, r->filename);
}
+ if (cached
+ && r->per_dir_config == cache->dir_conf_merged) {
+ r->per_dir_config = cache->per_dir_result;
+ return OK;
+ }
+
cache->dir_conf_tested = sec_ent;
cache->dir_conf_merged = r->per_dir_config;
@@ -1205,12 +1302,13 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
AP_DECLARE(int) ap_location_walk(request_rec *r)
{
ap_conf_vector_t *now_merged = NULL;
- core_server_config *sconf = ap_get_module_config(r->server->module_config,
- &core_module);
+ core_server_config *sconf =
+ ap_get_core_module_config(r->server->module_config);
ap_conf_vector_t **sec_ent = (ap_conf_vector_t **)sconf->sec_url->elts;
int num_sec = sconf->sec_url->nelts;
walk_cache_t *cache;
const char *entry_uri;
+ int cached;
/* No tricks here, there are no <Locations > to parse in this vhost.
* We won't destroy the cache, just in case _this_ redirect is later
@@ -1221,6 +1319,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
}
cache = prep_walk_cache(AP_NOTE_LOCATION_WALK, r);
+ cached = (cache->cached != NULL);
/* Location and LocationMatch differ on their behaviour w.r.t. multiple
* slashes. Location matches multiple slashes with a single slash,
@@ -1240,7 +1339,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
* and the vhost's list of locations hasn't changed, we can skip
* rewalking the location_walk entries.
*/
- if (cache->cached
+ if (cached
&& (cache->dir_conf_tested == sec_ent)
&& (strcmp(entry_uri, cache->cached) == 0)) {
/* Well this looks really familiar! If our end-result (per_dir_result)
@@ -1252,11 +1351,6 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
return OK;
}
- if (r->per_dir_config == cache->dir_conf_merged) {
- r->per_dir_config = cache->per_dir_result;
- return OK;
- }
-
if (cache->walked->nelts) {
now_merged = ((walk_walked_t*)cache->walked->elts)
[cache->walked->nelts - 1].merged;
@@ -1268,7 +1362,10 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
*/
int len, sec_idx;
int matches = cache->walked->nelts;
+ int cached_matches = matches;
walk_walked_t *last_walk = (walk_walked_t*)cache->walked->elts;
+
+ cached &= auth_internal_per_conf;
cache->cached = entry_uri;
/* Go through the location entries, and check for matches.
@@ -1278,7 +1375,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
for (sec_idx = 0; sec_idx < num_sec; ++sec_idx) {
core_dir_config *entry_core;
- entry_core = ap_get_module_config(sec_ent[sec_idx], &core_module);
+ entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
/* ### const strlen can be optimized in location config parsing */
len = strlen(entry_core->d);
@@ -1293,7 +1390,8 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
: (entry_core->d_is_fnmatch
? apr_fnmatch(entry_core->d, cache->cached, APR_FNM_PATHNAME)
: (strncmp(entry_core->d, cache->cached, len)
- || (entry_core->d[len - 1] != '/'
+ || (len > 0
+ && entry_core->d[len - 1] != '/'
&& cache->cached[len] != '/'
&& cache->cached[len] != '\0')))) {
continue;
@@ -1314,6 +1412,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
*/
cache->walked->nelts -= matches;
matches = 0;
+ cached = 0;
}
if (now_merged) {
@@ -1330,14 +1429,25 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
last_walk->merged = now_merged;
}
- /* Whoops - everything matched in sequence, but the original walk
- * found some additional matches. Truncate them.
+ /* Whoops - everything matched in sequence, but either the original
+ * walk found some additional matches (which we need to truncate), or
+ * this walk found some additional matches.
*/
if (matches) {
cache->walked->nelts -= matches;
+ cached = 0;
+ }
+ else if (cache->walked->nelts > cached_matches) {
+ cached = 0;
}
}
+ if (cached
+ && r->per_dir_config == cache->dir_conf_merged) {
+ r->per_dir_config = cache->per_dir_result;
+ return OK;
+ }
+
cache->dir_conf_tested = sec_ent;
cache->dir_conf_merged = r->per_dir_config;
@@ -1357,12 +1467,17 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
AP_DECLARE(int) ap_file_walk(request_rec *r)
{
ap_conf_vector_t *now_merged = NULL;
- core_dir_config *dconf = ap_get_module_config(r->per_dir_config,
- &core_module);
- ap_conf_vector_t **sec_ent = (ap_conf_vector_t **)dconf->sec_file->elts;
- int num_sec = dconf->sec_file->nelts;
+ core_dir_config *dconf = ap_get_core_module_config(r->per_dir_config);
+ ap_conf_vector_t **sec_ent = NULL;
+ int num_sec = 0;
walk_cache_t *cache;
const char *test_file;
+ int cached;
+
+ if (dconf->sec_file) {
+ sec_ent = (ap_conf_vector_t **)dconf->sec_file->elts;
+ num_sec = dconf->sec_file->nelts;
+ }
/* To allow broken modules to proceed, we allow missing filenames to pass.
* We will catch it later if it's heading for the core handler.
@@ -1373,6 +1488,7 @@ AP_DECLARE(int) ap_file_walk(request_rec *r)
}
cache = prep_walk_cache(AP_NOTE_FILE_WALK, r);
+ cached = (cache->cached != NULL);
/* No tricks here, there are just no <Files > to parse in this context.
* We won't destroy the cache, just in case _this_ redirect is later
@@ -1397,7 +1513,7 @@ AP_DECLARE(int) ap_file_walk(request_rec *r)
* and the directory's list of file sections hasn't changed, we
* can skip rewalking the file_walk entries.
*/
- if (cache->cached
+ if (cached
&& (cache->dir_conf_tested == sec_ent)
&& (strcmp(test_file, cache->cached) == 0)) {
/* Well this looks really familiar! If our end-result (per_dir_result)
@@ -1409,11 +1525,6 @@ AP_DECLARE(int) ap_file_walk(request_rec *r)
return OK;
}
- if (r->per_dir_config == cache->dir_conf_merged) {
- r->per_dir_config = cache->per_dir_result;
- return OK;
- }
-
if (cache->walked->nelts) {
now_merged = ((walk_walked_t*)cache->walked->elts)
[cache->walked->nelts - 1].merged;
@@ -1425,7 +1536,10 @@ AP_DECLARE(int) ap_file_walk(request_rec *r)
*/
int sec_idx;
int matches = cache->walked->nelts;
+ int cached_matches = matches;
walk_walked_t *last_walk = (walk_walked_t*)cache->walked->elts;
+
+ cached &= auth_internal_per_conf;
cache->cached = test_file;
/* Go through the location entries, and check for matches.
@@ -1433,9 +1547,8 @@ AP_DECLARE(int) ap_file_walk(request_rec *r)
* really try them with the most general first.
*/
for (sec_idx = 0; sec_idx < num_sec; ++sec_idx) {
-
core_dir_config *entry_core;
- entry_core = ap_get_module_config(sec_ent[sec_idx], &core_module);
+ entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
if (entry_core->r
? ap_regexec(entry_core->r, cache->cached , 0, NULL, 0)
@@ -1460,6 +1573,7 @@ AP_DECLARE(int) ap_file_walk(request_rec *r)
*/
cache->walked->nelts -= matches;
matches = 0;
+ cached = 0;
}
if (now_merged) {
@@ -1476,12 +1590,155 @@ AP_DECLARE(int) ap_file_walk(request_rec *r)
last_walk->merged = now_merged;
}
- /* Whoops - everything matched in sequence, but the original walk
- * found some additional matches. Truncate them.
+ /* Whoops - everything matched in sequence, but either the original
+ * walk found some additional matches (which we need to truncate), or
+ * this walk found some additional matches.
*/
if (matches) {
cache->walked->nelts -= matches;
+ cached = 0;
}
+ else if (cache->walked->nelts > cached_matches) {
+ cached = 0;
+ }
+ }
+
+ if (cached
+ && r->per_dir_config == cache->dir_conf_merged) {
+ r->per_dir_config = cache->per_dir_result;
+ return OK;
+ }
+
+ cache->dir_conf_tested = sec_ent;
+ cache->dir_conf_merged = r->per_dir_config;
+
+ /* Merge our cache->dir_conf_merged construct with the r->per_dir_configs,
+ * and note the end result to (potentially) skip this step next time.
+ */
+ if (now_merged) {
+ r->per_dir_config = ap_merge_per_dir_configs(r->pool,
+ r->per_dir_config,
+ now_merged);
+ }
+ cache->per_dir_result = r->per_dir_config;
+
+ return OK;
+}
+
+AP_DECLARE(int) ap_if_walk(request_rec *r)
+{
+ ap_conf_vector_t *now_merged = NULL;
+ core_dir_config *dconf = ap_get_core_module_config(r->per_dir_config);
+ ap_conf_vector_t **sec_ent = NULL;
+ int num_sec = 0;
+ walk_cache_t *cache;
+ int cached;
+ int sec_idx;
+ int matches;
+ int cached_matches;
+ int prev_result = -1;
+ walk_walked_t *last_walk;
+
+ if (dconf->sec_if) {
+ sec_ent = (ap_conf_vector_t **)dconf->sec_if->elts;
+ num_sec = dconf->sec_if->nelts;
+ }
+
+ /* No tricks here, there are just no <If > to parse in this context.
+ * We won't destroy the cache, just in case _this_ redirect is later
+ * redirected again to a context containing the same or similar <If >.
+ */
+ if (!num_sec) {
+ return OK;
+ }
+
+ cache = prep_walk_cache(AP_NOTE_IF_WALK, r);
+ cached = (cache->cached != NULL);
+ cache->cached = (void *)1;
+ matches = cache->walked->nelts;
+ cached_matches = matches;
+ last_walk = (walk_walked_t*)cache->walked->elts;
+
+ cached &= auth_internal_per_conf;
+
+ /* Go through the if entries, and check for matches */
+ for (sec_idx = 0; sec_idx < num_sec; ++sec_idx) {
+ const char *err = NULL;
+ core_dir_config *entry_core;
+ int rc;
+ entry_core = ap_get_core_module_config(sec_ent[sec_idx]);
+
+ AP_DEBUG_ASSERT(entry_core->condition_ifelse != 0);
+ if (entry_core->condition_ifelse & AP_CONDITION_ELSE) {
+ AP_DEBUG_ASSERT(prev_result != -1);
+ if (prev_result == 1)
+ continue;
+ }
+
+ if (entry_core->condition_ifelse & AP_CONDITION_IF) {
+ rc = ap_expr_exec(r, entry_core->condition, &err);
+ if (rc <= 0) {
+ if (rc < 0)
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00039)
+ "Failed to evaluate <If > condition: %s",
+ err);
+ prev_result = 0;
+ continue;
+ }
+ prev_result = 1;
+ }
+ else {
+ prev_result = -1;
+ }
+
+ /* If we merged this same section last time, reuse it
+ */
+ if (matches) {
+ if (last_walk->matched == sec_ent[sec_idx]) {
+ now_merged = last_walk->merged;
+ ++last_walk;
+ --matches;
+ continue;
+ }
+
+ /* We fell out of sync. This is our own copy of walked,
+ * so truncate the remaining matches and reset remaining.
+ */
+ cache->walked->nelts -= matches;
+ matches = 0;
+ cached = 0;
+ }
+
+ if (now_merged) {
+ now_merged = ap_merge_per_dir_configs(r->pool,
+ now_merged,
+ sec_ent[sec_idx]);
+ }
+ else {
+ now_merged = sec_ent[sec_idx];
+ }
+
+ last_walk = (walk_walked_t*)apr_array_push(cache->walked);
+ last_walk->matched = sec_ent[sec_idx];
+ last_walk->merged = now_merged;
+ }
+
+ /* Everything matched in sequence, but it may be that the original
+ * walk found some additional matches (which we need to truncate), or
+ * this walk found some additional matches.
+ */
+ if (matches) {
+ cache->walked->nelts -= matches;
+ cached = 0;
+ }
+ else if (cache->walked->nelts > cached_matches) {
+ cached = 0;
+ }
+
+ if (cached
+ && r->per_dir_config == cache->dir_conf_merged) {
+ r->per_dir_config = cache->per_dir_result;
+ return OK;
}
cache->dir_conf_tested = sec_ent;
@@ -1531,6 +1788,7 @@ static request_rec *make_sub_request(const request_rec *r,
rnew->request_time = r->request_time;
rnew->connection = r->connection;
rnew->server = r->server;
+ rnew->log = r->log;
rnew->request_config = ap_create_request_config(rnew->pool);
@@ -1574,6 +1832,9 @@ static request_rec *make_sub_request(const request_rec *r,
rnew->output_filters = r->proto_output_filters;
}
+ rnew->useragent_addr = r->useragent_addr;
+ rnew->useragent_ip = r->useragent_ip;
+
/* no input filters for a subrequest */
ap_set_sub_req_protocol(rnew, r);
@@ -1588,6 +1849,9 @@ static request_rec *make_sub_request(const request_rec *r,
*/
rnew->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
+ /* Pass on the kept body (if any) into the new request. */
+ rnew->kept_body = r->kept_body;
+
return rnew;
}
@@ -1607,30 +1871,124 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_sub_req_output_filter(ap_filter_t *f,
return APR_SUCCESS;
}
+extern APR_OPTIONAL_FN_TYPE(authz_some_auth_required) *ap__authz_ap_some_auth_required;
AP_DECLARE(int) ap_some_auth_required(request_rec *r)
{
/* Is there a require line configured for the type of *this* req? */
+ if (ap__authz_ap_some_auth_required) {
+ return ap__authz_ap_some_auth_required(r);
+ }
+ else
+ return 0;
+}
+
+AP_DECLARE(void) ap_clear_auth_internal(void)
+{
+ auth_internal_per_conf_hooks = 0;
+ auth_internal_per_conf_providers = 0;
+}
- const apr_array_header_t *reqs_arr = ap_requires(r);
- require_line *reqs;
- int i;
+AP_DECLARE(void) ap_setup_auth_internal(apr_pool_t *ptemp)
+{
+ int total_auth_hooks = 0;
+ int total_auth_providers = 0;
- if (!reqs_arr) {
- return 0;
+ auth_internal_per_conf = 0;
+
+ if (_hooks.link_access_checker) {
+ total_auth_hooks += _hooks.link_access_checker->nelts;
+ }
+ if (_hooks.link_access_checker_ex) {
+ total_auth_hooks += _hooks.link_access_checker_ex->nelts;
+ }
+ if (_hooks.link_check_user_id) {
+ total_auth_hooks += _hooks.link_check_user_id->nelts;
+ }
+ if (_hooks.link_auth_checker) {
+ total_auth_hooks += _hooks.link_auth_checker->nelts;
}
- reqs = (require_line *) reqs_arr->elts;
+ if (total_auth_hooks > auth_internal_per_conf_hooks) {
+ return;
+ }
- for (i = 0; i < reqs_arr->nelts; ++i) {
- if (reqs[i].method_mask & (AP_METHOD_BIT << r->method_number)) {
- return 1;
- }
+ total_auth_providers +=
+ ap_list_provider_names(ptemp, AUTHN_PROVIDER_GROUP,
+ AUTHN_PROVIDER_VERSION)->nelts;
+ total_auth_providers +=
+ ap_list_provider_names(ptemp, AUTHZ_PROVIDER_GROUP,
+ AUTHZ_PROVIDER_VERSION)->nelts;
+
+ if (total_auth_providers > auth_internal_per_conf_providers) {
+ return;
+ }
+
+ auth_internal_per_conf = 1;
+}
+
+AP_DECLARE(apr_status_t) ap_register_auth_provider(apr_pool_t *pool,
+ const char *provider_group,
+ const char *provider_name,
+ const char *provider_version,
+ const void *provider,
+ int type)
+{
+ if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
+ ++auth_internal_per_conf_providers;
+ }
+
+ return ap_register_provider(pool, provider_group, provider_name,
+ provider_version, provider);
+}
+
+AP_DECLARE(void) ap_hook_check_access(ap_HOOK_access_checker_t *pf,
+ const char * const *aszPre,
+ const char * const *aszSucc,
+ int nOrder, int type)
+{
+ if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
+ ++auth_internal_per_conf_hooks;
}
- return 0;
+ ap_hook_access_checker(pf, aszPre, aszSucc, nOrder);
}
+AP_DECLARE(void) ap_hook_check_access_ex(ap_HOOK_access_checker_ex_t *pf,
+ const char * const *aszPre,
+ const char * const *aszSucc,
+ int nOrder, int type)
+{
+ if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
+ ++auth_internal_per_conf_hooks;
+ }
+
+ ap_hook_access_checker_ex(pf, aszPre, aszSucc, nOrder);
+}
+
+AP_DECLARE(void) ap_hook_check_authn(ap_HOOK_check_user_id_t *pf,
+ const char * const *aszPre,
+ const char * const *aszSucc,
+ int nOrder, int type)
+{
+ if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
+ ++auth_internal_per_conf_hooks;
+ }
+
+ ap_hook_check_user_id(pf, aszPre, aszSucc, nOrder);
+}
+
+AP_DECLARE(void) ap_hook_check_authz(ap_HOOK_auth_checker_t *pf,
+ const char * const *aszPre,
+ const char * const *aszSucc,
+ int nOrder, int type)
+{
+ if ((type & AP_AUTH_INTERNAL_MASK) == AP_AUTH_INTERNAL_PER_CONF) {
+ ++auth_internal_per_conf_hooks;
+ }
+
+ ap_hook_auth_checker(pf, aszPre, aszSucc, nOrder);
+}
AP_DECLARE(request_rec *) ap_sub_req_method_uri(const char *method,
const char *new_uri,
@@ -1761,7 +2119,7 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_dirent(const apr_finfo_t *dirent,
if (((rv = apr_stat(&rnew->finfo, rnew->filename,
APR_FINFO_MIN, rnew->pool)) != APR_SUCCESS)
&& (rv != APR_INCOMPLETE)) {
- rnew->finfo.filetype = 0;
+ rnew->finfo.filetype = APR_NOFILE;
}
}
else {
@@ -1769,7 +2127,7 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_dirent(const apr_finfo_t *dirent,
APR_FINFO_LINK | APR_FINFO_MIN,
rnew->pool)) != APR_SUCCESS)
&& (rv != APR_INCOMPLETE)) {
- rnew->finfo.filetype = 0;
+ rnew->finfo.filetype = APR_NOFILE;
}
}
}
@@ -1867,7 +2225,7 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
if (((rv = apr_stat(&rnew->finfo, rnew->filename,
APR_FINFO_MIN, rnew->pool)) != APR_SUCCESS)
&& (rv != APR_INCOMPLETE)) {
- rnew->finfo.filetype = 0;
+ rnew->finfo.filetype = APR_NOFILE;
}
}
else {
@@ -1875,7 +2233,7 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
APR_FINFO_LINK | APR_FINFO_MIN,
rnew->pool)) != APR_SUCCESS)
&& (rv != APR_INCOMPLETE)) {
- rnew->finfo.filetype = 0;
+ rnew->finfo.filetype = APR_NOFILE;
}
}
@@ -1924,7 +2282,7 @@ AP_DECLARE(int) ap_run_sub_req(request_rec *r)
/* Run the quick handler if the subrequest is not a dirent or file
* subrequest
*/
- if (!(r->filename && r->finfo.filetype)) {
+ if (!(r->filename && r->finfo.filetype != APR_NOFILE)) {
retval = ap_run_quick_handler(r, 0);
}
if (retval != OK) {
@@ -1962,3 +2320,4 @@ AP_DECLARE(int) ap_is_initial_req(request_rec *r)
return (r->main == NULL) /* otherwise, this is a sub-request */
&& (r->prev == NULL); /* otherwise, this is an internal redirect */
}
+
diff --git a/server/scoreboard.c b/server/scoreboard.c
index 85f37557..72aa0704 100644
--- a/server/scoreboard.c
+++ b/server/scoreboard.c
@@ -34,14 +34,52 @@
#include "http_config.h"
#include "ap_mpm.h"
-#include "mpm.h"
#include "scoreboard.h"
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
AP_DECLARE_DATA scoreboard *ap_scoreboard_image = NULL;
AP_DECLARE_DATA const char *ap_scoreboard_fname = NULL;
+
+const char * ap_set_scoreboard(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_scoreboard_fname = arg;
+ return NULL;
+}
+
+/* Default to false when mod_status is not loaded */
AP_DECLARE_DATA int ap_extended_status = 0;
+
+const char *ap_set_extended_status(cmd_parms *cmd, void *dummy, int arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+ ap_extended_status = arg;
+ return NULL;
+}
+
AP_DECLARE_DATA int ap_mod_status_reqtail = 0;
+const char *ap_set_reqtail(cmd_parms *cmd, void *dummy, int arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+ ap_mod_status_reqtail = arg;
+ return NULL;
+}
+
#if APR_HAS_SHARED_MEMORY
#include "apr_shm.h"
@@ -61,15 +99,15 @@ AP_IMPLEMENT_HOOK_RUN_ALL(int,pre_mpm,
(apr_pool_t *p, ap_scoreboard_e sb_type),
(p, sb_type),OK,DECLINED)
-static APR_OPTIONAL_FN_TYPE(ap_proxy_lb_workers)
- *proxy_lb_workers;
+static APR_OPTIONAL_FN_TYPE(ap_logio_get_last_bytes)
+ *pfn_ap_logio_get_last_bytes;
struct ap_sb_handle_t {
int child_num;
int thread_num;
};
-static int server_limit, thread_limit, lb_limit;
+static int server_limit, thread_limit;
static apr_size_t scoreboard_size;
/*
@@ -95,18 +133,11 @@ AP_DECLARE(int) ap_calc_scoreboard_size(void)
ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
- if (!proxy_lb_workers)
- proxy_lb_workers = APR_RETRIEVE_OPTIONAL_FN(ap_proxy_lb_workers);
- if (proxy_lb_workers)
- lb_limit = proxy_lb_workers();
- else
- lb_limit = 0;
-
scoreboard_size = sizeof(global_score);
scoreboard_size += sizeof(process_score) * server_limit;
scoreboard_size += sizeof(worker_score) * server_limit * thread_limit;
- if (lb_limit)
- scoreboard_size += sizeof(lb_score) * lb_limit;
+
+ pfn_ap_logio_get_last_bytes = APR_RETRIEVE_OPTIONAL_FN(ap_logio_get_last_bytes);
return scoreboard_size;
}
@@ -118,7 +149,7 @@ void ap_init_scoreboard(void *shared_score)
ap_calc_scoreboard_size();
ap_scoreboard_image =
- calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *));
+ ap_calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *));
more_storage = shared_score;
ap_scoreboard_image->global = (global_score *)more_storage;
more_storage += sizeof(global_score);
@@ -130,14 +161,9 @@ void ap_init_scoreboard(void *shared_score)
ap_scoreboard_image->servers[i] = (worker_score *)more_storage;
more_storage += thread_limit * sizeof(worker_score);
}
- if (lb_limit) {
- ap_scoreboard_image->balancers = (lb_score *)more_storage;
- more_storage += lb_limit * sizeof(lb_score);
- }
ap_assert(more_storage == (char*)shared_score + scoreboard_size);
ap_scoreboard_image->global->server_limit = server_limit;
ap_scoreboard_image->global->thread_limit = thread_limit;
- ap_scoreboard_image->global->lb_limit = lb_limit;
}
/**
@@ -156,7 +182,7 @@ static apr_status_t create_namebased_scoreboard(apr_pool_t *pool,
rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, fname, pool);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00001)
"unable to create or access scoreboard \"%s\" "
"(name-based shared memory failure)", fname);
return rv;
@@ -181,9 +207,9 @@ static apr_status_t open_scoreboard(apr_pool_t *pconf)
*/
rv = apr_pool_create(&global_pool, NULL);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00002)
"Fatal error: unable to create global pool "
- "for use with by the scoreboard");
+ "for use by the scoreboard");
return rv;
}
@@ -192,7 +218,7 @@ static apr_status_t open_scoreboard(apr_pool_t *pconf)
/* make sure it's an absolute pathname */
fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
if (!fname) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EBADPATH, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, APR_EBADPATH, ap_server_conf, APLOGNO(00003)
"Fatal error: Invalid Scoreboard path %s",
ap_scoreboard_fname);
return APR_EBADPATH;
@@ -203,7 +229,7 @@ static apr_status_t open_scoreboard(apr_pool_t *pconf)
rv = apr_shm_create(&ap_scoreboard_shm, scoreboard_size, NULL,
global_pool); /* anonymous shared memory */
if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf, APLOGNO(00004)
"Unable to create or access scoreboard "
"(anonymous shared memory failure)");
return rv;
@@ -221,7 +247,7 @@ static apr_status_t open_scoreboard(apr_pool_t *pconf)
return APR_SUCCESS;
}
-/* If detach is non-zero, this is a seperate child process,
+/* If detach is non-zero, this is a separate child process,
* if zero, it is a forked child.
*/
apr_status_t ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm, int detached)
@@ -231,7 +257,7 @@ apr_status_t ap_reopen_scoreboard(apr_pool_t *p, apr_shm_t **shm, int detached)
return APR_SUCCESS;
}
if (apr_shm_size_get(ap_scoreboard_shm) < scoreboard_size) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf, APLOGNO(00005)
"Fatal error: shared scoreboard too small for child!");
apr_shm_detach(ap_scoreboard_shm);
ap_scoreboard_shm = NULL;
@@ -271,6 +297,8 @@ int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
apr_status_t rv;
#endif
+ pfn_ap_logio_get_last_bytes = APR_RETRIEVE_OPTIONAL_FN(ap_logio_get_last_bytes);
+
if (ap_scoreboard_image) {
ap_scoreboard_image->global->restart_time = apr_time_now();
memset(ap_scoreboard_image->parent, 0,
@@ -279,11 +307,6 @@ int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
memset(ap_scoreboard_image->servers[i], 0,
sizeof(worker_score) * thread_limit);
}
- /* Clean up the lb workers data */
- if (lb_limit) {
- memset(ap_scoreboard_image->balancers, 0,
- sizeof(lb_score) * lb_limit);
- }
return OK;
}
@@ -302,13 +325,7 @@ int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type)
#endif
{
/* A simple malloc will suffice */
- void *sb_mem = calloc(1, scoreboard_size);
- if (sb_mem == NULL) {
- ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
- "(%d)%s: cannot allocate scoreboard",
- errno, strerror(errno));
- return HTTP_INTERNAL_SERVER_ERROR;
- }
+ void *sb_mem = ap_calloc(1, scoreboard_size);
ap_init_scoreboard(sb_mem);
}
@@ -340,11 +357,21 @@ AP_DECLARE(int) ap_exists_scoreboard_image(void)
AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
{
worker_score *ws;
+ apr_off_t bytes;
if (!sb)
return;
ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
+ if (pfn_ap_logio_get_last_bytes != NULL) {
+ bytes = pfn_ap_logio_get_last_bytes(r->connection);
+ }
+ else if (r->method_number == M_GET && r->method[0] == 'H') {
+ bytes = 0;
+ }
+ else {
+ bytes = r->bytes_sent;
+ }
#ifdef HAVE_TIMES
times(&ws->times);
@@ -352,12 +379,12 @@ AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
ws->access_count++;
ws->my_access_count++;
ws->conn_count++;
- ws->bytes_served += r->bytes_sent;
- ws->my_bytes_served += r->bytes_sent;
- ws->conn_bytes += r->bytes_sent;
+ ws->bytes_served += bytes;
+ ws->my_bytes_served += bytes;
+ ws->conn_bytes += bytes;
}
-int find_child_by_pid(apr_proc_t *pid)
+AP_DECLARE(int) ap_find_child_by_pid(apr_proc_t *pid)
{
int i;
int max_daemons_limit;
@@ -417,18 +444,16 @@ static void copy_request(char *rbuf, apr_size_t rbuflen, request_rec *r)
}
}
-AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
- int thread_num,
- int status,
- request_rec *r)
+static int update_child_status_internal(int child_num,
+ int thread_num,
+ int status,
+ conn_rec *c,
+ request_rec *r)
{
int old_status;
worker_score *ws;
process_score *ps;
-
- if (child_num < 0) {
- return -1;
- }
+ int mpm_generation;
ws = &ap_scoreboard_image->servers[child_num][thread_num];
old_status = ws->status;
@@ -439,7 +464,8 @@ AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
if (status == SERVER_READY
&& old_status == SERVER_STARTING) {
ws->thread_num = child_num * thread_limit + thread_num;
- ps->generation = ap_my_generation;
+ ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
+ ps->generation = mpm_generation;
}
if (ap_extended_status) {
@@ -456,29 +482,62 @@ AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
ws->conn_bytes = 0;
}
if (r) {
- conn_rec *c = r->connection;
apr_cpystrn(ws->client, ap_get_remote_host(c, r->per_dir_config,
REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
copy_request(ws->request, sizeof(ws->request), r);
- apr_cpystrn(ws->vhost, r->server->server_hostname,
- sizeof(ws->vhost));
+ if (r->server) {
+ apr_cpystrn(ws->vhost, r->server->server_hostname,
+ sizeof(ws->vhost));
+ }
+ }
+ else if (c) {
+ apr_cpystrn(ws->client, ap_get_remote_host(c, NULL,
+ REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
+ ws->request[0]='\0';
+ ws->vhost[0]='\0';
}
}
return old_status;
}
+AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
+ int thread_num,
+ int status,
+ request_rec *r)
+{
+ if (child_num < 0) {
+ return -1;
+ }
+
+ return update_child_status_internal(child_num, thread_num, status,
+ r ? r->connection : NULL,
+ r);
+}
+
AP_DECLARE(int) ap_update_child_status(ap_sb_handle_t *sbh, int status,
request_rec *r)
{
if (!sbh)
return -1;
- return ap_update_child_status_from_indexes(sbh->child_num, sbh->thread_num,
- status, r);
+ return update_child_status_internal(sbh->child_num, sbh->thread_num,
+ status,
+ r ? r->connection : NULL,
+ r);
}
-void ap_time_process_request(ap_sb_handle_t *sbh, int status)
+AP_DECLARE(int) ap_update_child_status_from_conn(ap_sb_handle_t *sbh, int status,
+ conn_rec *c)
+{
+ if (!sbh)
+ return -1;
+
+ return update_child_status_internal(sbh->child_num, sbh->thread_num,
+ status, c, NULL);
+}
+
+AP_DECLARE(void) ap_time_process_request(ap_sb_handle_t *sbh, int status)
{
worker_score *ws;
@@ -499,18 +558,27 @@ void ap_time_process_request(ap_sb_handle_t *sbh, int status)
}
}
-AP_DECLARE(worker_score *) ap_get_scoreboard_worker(int x, int y)
+AP_DECLARE(worker_score *) ap_get_scoreboard_worker_from_indexes(int x, int y)
{
- if (((x < 0) || (server_limit < x)) ||
- ((y < 0) || (thread_limit < y))) {
+ if (((x < 0) || (x >= server_limit)) ||
+ ((y < 0) || (y >= thread_limit))) {
return(NULL); /* Out of range */
}
return &ap_scoreboard_image->servers[x][y];
}
+AP_DECLARE(worker_score *) ap_get_scoreboard_worker(ap_sb_handle_t *sbh)
+{
+ if (!sbh)
+ return NULL;
+
+ return ap_get_scoreboard_worker_from_indexes(sbh->child_num,
+ sbh->thread_num);
+}
+
AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
{
- if ((x < 0) || (server_limit < x)) {
+ if ((x < 0) || (x >= server_limit)) {
return(NULL); /* Out of range */
}
return &ap_scoreboard_image->parent[x];
@@ -520,11 +588,3 @@ AP_DECLARE(global_score *) ap_get_scoreboard_global()
{
return ap_scoreboard_image->global;
}
-
-AP_DECLARE(lb_score *) ap_get_scoreboard_lb(int lb_num)
-{
- if (((lb_num < 0) || (lb_limit < lb_num))) {
- return(NULL); /* Out of range */
- }
- return &ap_scoreboard_image->balancers[lb_num];
-}
diff --git a/server/util.c b/server/util.c
index d0b90c6a..9ff8e394 100644
--- a/server/util.c
+++ b/server/util.c
@@ -38,12 +38,13 @@
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
+#if APR_HAVE_PROCESS_H
+#include <process.h> /* for getpid() on Win32 */
+#endif
#if APR_HAVE_NETDB_H
#include <netdb.h> /* for gethostbyname() */
#endif
-#define CORE_PRIVATE
-
#include "ap_config.h"
#include "apr_base64.h"
#include "httpd.h"
@@ -51,7 +52,9 @@
#include "http_log.h"
#include "http_protocol.h"
#include "http_config.h"
+#include "http_core.h"
#include "util_ebcdic.h"
+#include "util_varbuf.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
@@ -78,10 +81,16 @@
*/
#ifdef CASE_BLIND_FILESYSTEM
#define IS_SLASH(s) ((s == '/') || (s == '\\'))
+#define SLASHES "/\\"
#else
#define IS_SLASH(s) (s == '/')
+#define SLASHES "/"
#endif
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
/*
* Examine a field value (such as a media-/content-type) string and return
@@ -349,54 +358,79 @@ AP_DECLARE(const char *) ap_stripprefix(const char *bigstring,
* passed ap_regexec(). pmatch should not be greater than the maximum number
* of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
*
+ * nmatch must be <=AP_MAX_REG_MATCH (10).
+ *
* input should be the string with the $-expressions, source should be the
* string that was matched against.
*
- * It returns the substituted string, or NULL on error.
+ * It returns the substituted string, or NULL if a vbuf is used.
+ * On errors, returns the orig string.
*
* Parts of this code are based on Henry Spencer's regsub(), from his
* AT&T V8 regexp package.
*/
-AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
- const char *source, size_t nmatch,
- ap_regmatch_t pmatch[])
+static apr_status_t regsub_core(apr_pool_t *p, char **result,
+ struct ap_varbuf *vb, const char *input,
+ const char *source, size_t nmatch,
+ ap_regmatch_t pmatch[], apr_size_t maxlen)
{
const char *src = input;
- char *dest, *dst;
+ char *dst;
char c;
size_t no;
- int len;
-
- if (!source)
- return NULL;
- if (!nmatch)
- return apr_pstrdup(p, src);
+ apr_size_t len = 0;
+
+ AP_DEBUG_ASSERT((result && p && !vb) || (vb && !p && !result));
+ if (!source || nmatch>AP_MAX_REG_MATCH)
+ return APR_EINVAL;
+ if (!nmatch) {
+ len = strlen(src);
+ if (maxlen > 0 && len >= maxlen)
+ return APR_ENOMEM;
+ if (!vb) {
+ *result = apr_pstrmemdup(p, src, len);
+ return APR_SUCCESS;
+ }
+ else {
+ ap_varbuf_strmemcat(vb, src, len);
+ return APR_SUCCESS;
+ }
+ }
/* First pass, find the size */
-
- len = 0;
-
while ((c = *src++) != '\0') {
- if (c == '&')
- no = 0;
- else if (c == '$' && apr_isdigit(*src))
+ if (c == '$' && apr_isdigit(*src))
no = *src++ - '0';
else
- no = 10;
+ no = AP_MAX_REG_MATCH;
- if (no > 9) { /* Ordinary character. */
- if (c == '\\' && (*src == '$' || *src == '&'))
+ if (no >= AP_MAX_REG_MATCH) { /* Ordinary character. */
+ if (c == '\\' && *src)
src++;
len++;
}
else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+ if (APR_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so)
+ return APR_ENOMEM;
len += pmatch[no].rm_eo - pmatch[no].rm_so;
}
}
- dest = dst = apr_pcalloc(p, len + 1);
+ if (len >= maxlen && maxlen > 0)
+ return APR_ENOMEM;
+
+ if (!vb) {
+ *result = dst = apr_palloc(p, len + 1);
+ }
+ else {
+ if (vb->buf && vb->strlen == AP_VARBUF_UNKNOWN)
+ vb->strlen = strlen(vb->buf);
+ ap_varbuf_grow(vb, vb->strlen + len);
+ dst = vb->buf + vb->strlen;
+ vb->strlen += len;
+ }
/* Now actually fill in the string */
@@ -408,9 +442,9 @@ AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
else if (c == '$' && apr_isdigit(*src))
no = *src++ - '0';
else
- no = 10;
+ no = AP_MAX_REG_MATCH;
- if (no > 9) { /* Ordinary character. */
+ if (no >= AP_MAX_REG_MATCH) { /* Ordinary character. */
if (c == '\\' && (*src == '$' || *src == '&'))
c = *src++;
*dst++ = c;
@@ -424,7 +458,34 @@ AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
}
*dst = '\0';
- return dest;
+ return APR_SUCCESS;
+}
+
+#ifndef AP_PREGSUB_MAXLEN
+#define AP_PREGSUB_MAXLEN (HUGE_STRING_LEN * 8)
+#endif
+AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
+ const char *source, size_t nmatch,
+ ap_regmatch_t pmatch[])
+{
+ char *result;
+ apr_status_t rc = regsub_core(p, &result, NULL, input, source, nmatch,
+ pmatch, AP_PREGSUB_MAXLEN);
+ if (rc != APR_SUCCESS)
+ result = NULL;
+ return result;
+}
+
+AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result,
+ const char *input, const char *source,
+ size_t nmatch, ap_regmatch_t pmatch[],
+ apr_size_t maxlen)
+{
+ apr_status_t rc = regsub_core(p, result, NULL, input, source, nmatch,
+ pmatch, maxlen);
+ if (rc != APR_SUCCESS)
+ *result = NULL;
+ return rc;
}
/*
@@ -580,9 +641,8 @@ AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
return apr_pstrdup(p, "");
}
l = (last_slash - s) + 1;
- d = apr_palloc(p, l + 1);
- memcpy(d, s, l);
- d[l] = 0;
+ d = apr_pstrmemdup(p, s, l);
+
return (d);
}
@@ -613,9 +673,7 @@ AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop)
}
len = pos - *line;
- res = (char *)apr_palloc(atrans, len + 1);
- memcpy(res, *line, len);
- res[len] = 0;
+ res = apr_pstrmemdup(atrans, *line, len);
if (stop) {
while (*pos == stop) {
@@ -643,9 +701,7 @@ AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line)
}
len = pos - *line;
- res = (char *)apr_palloc(atrans, len + 1);
- memcpy(res, *line, len);
- res[len] = 0;
+ res = apr_pstrmemdup(atrans, *line, len);
while (apr_isspace(*pos)) {
++pos;
@@ -669,8 +725,9 @@ AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
char *res;
if (!pos) {
- res = apr_pstrdup(atrans, *line);
- *line += strlen(*line);
+ size_t len = strlen(*line);
+ res = apr_pstrmemdup(atrans, *line, len);
+ *line += len;
return res;
}
@@ -759,128 +816,29 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
return res;
}
-/* Check a string for any ${ENV} environment variable
- * construct and replace each them by the value of
- * that environment variable, if it exists. If the
- * environment value does not exist, leave the ${ENV}
- * construct alone; it means something else.
- */
-AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word)
-{
-# define SMALL_EXPANSION 5
- struct sll {
- struct sll *next;
- const char *string;
- apr_size_t len;
- } *result, *current, sresult[SMALL_EXPANSION];
- char *res_buf, *cp;
- const char *s, *e, *ep;
- unsigned spc;
- apr_size_t outlen;
-
- s = ap_strchr_c(word, '$');
- if (!s) {
- return word;
- }
-
- /* well, actually something to do */
- ep = word + strlen(word);
- spc = 0;
- result = current = &(sresult[spc++]);
- current->next = NULL;
- current->string = word;
- current->len = s - word;
- outlen = current->len;
-
- do {
- /* prepare next entry */
- if (current->len) {
- current->next = (spc < SMALL_EXPANSION)
- ? &(sresult[spc++])
- : (struct sll *)apr_palloc(p,
- sizeof(*current->next));
- current = current->next;
- current->next = NULL;
- current->len = 0;
- }
-
- if (*s == '$') {
- if (s[1] == '{' && (e = ap_strchr_c(s, '}'))) {
- word = getenv(apr_pstrndup(p, s+2, e-s-2));
- if (word) {
- current->string = word;
- current->len = strlen(word);
- outlen += current->len;
- }
- else {
- current->string = s;
- current->len = e - s + 1;
- outlen += current->len;
- }
- s = e + 1;
- }
- else {
- current->string = s++;
- current->len = 1;
- ++outlen;
- }
- }
- else {
- word = s;
- s = ap_strchr_c(s, '$');
- current->string = word;
- current->len = s ? s - word : ep - word;
- outlen += current->len;
- }
- } while (s && *s);
-
- /* assemble result */
- res_buf = cp = apr_palloc(p, outlen + 1);
- do {
- if (result->len) {
- memcpy(cp, result->string, result->len);
- cp += result->len;
- }
- result = result->next;
- } while (result);
- res_buf[outlen] = '\0';
-
- return res_buf;
-}
-
AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
{
#ifdef DEBUG
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00551)
"Done with config file %s", cfp->name);
#endif
return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
}
+/* we can't use apr_file_* directly because of linking issues on Windows */
static apr_status_t cfg_close(void *param)
{
- apr_file_t *cfp = (apr_file_t *) param;
- return (apr_file_close(cfp));
+ return apr_file_close(param);
}
-static int cfg_getch(void *param)
+static apr_status_t cfg_getch(char *ch, void *param)
{
- char ch;
- apr_file_t *cfp = (apr_file_t *) param;
- if (apr_file_getc(&ch, cfp) == APR_SUCCESS)
- return ch;
- return (int)EOF;
+ return apr_file_getc(ch, param);
}
-static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
+static apr_status_t cfg_getstr(void *buf, size_t bufsiz, void *param)
{
- apr_file_t *cfp = (apr_file_t *) param;
- apr_status_t rv;
- rv = apr_file_gets(buf, bufsiz, cfp);
- if (rv == APR_SUCCESS) {
- return buf;
- }
- return NULL;
+ return apr_file_gets(buf, bufsiz, param);
}
/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
@@ -896,7 +854,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
#endif
if (name == NULL) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00552)
"Internal error: pcfg_openfile() called with NULL filename");
return APR_EBADF;
}
@@ -904,7 +862,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
APR_OS_DEFAULT, p);
#ifdef DEBUG
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00553)
"Opening config file %s (%s)",
name, (status != APR_SUCCESS) ?
apr_strerror(status, buf, sizeof(buf)) : "successful");
@@ -922,7 +880,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
#else
strcmp(name, "/dev/null") != 0) {
#endif /* WIN32 || OS2 */
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00554)
"Access to file %s denied by server: not a regular file",
name);
apr_file_close(file);
@@ -955,9 +913,9 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
new_cfg = apr_palloc(p, sizeof(*new_cfg));
new_cfg->param = file;
new_cfg->name = apr_pstrdup(p, name);
- new_cfg->getch = (int (*)(void *)) cfg_getch;
- new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr;
- new_cfg->close = (int (*)(void *)) cfg_close;
+ new_cfg->getch = cfg_getch;
+ new_cfg->getstr = cfg_getstr;
+ new_cfg->close = cfg_close;
new_cfg->line_number = 0;
*ret_cfg = new_cfg;
return APR_SUCCESS;
@@ -965,170 +923,200 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
/* Allocate a ap_configfile_t handle with user defined functions and params */
-AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(apr_pool_t *p,
- const char *descr,
- void *param,
- int(*getch)(void *param),
- void *(*getstr) (void *buf, size_t bufsiz, void *param),
- int(*close_func)(void *param))
+AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(
+ apr_pool_t *p, const char *descr, void *param,
+ apr_status_t (*getc_func) (char *ch, void *param),
+ apr_status_t (*gets_func) (void *buf, size_t bufsize, void *param),
+ apr_status_t (*close_func) (void *param))
{
ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
-#ifdef DEBUG
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
- "Opening config handler %s", descr);
-#endif
new_cfg->param = param;
new_cfg->name = descr;
- new_cfg->getch = getch;
- new_cfg->getstr = getstr;
+ new_cfg->getch = getc_func;
+ new_cfg->getstr = gets_func;
new_cfg->close = close_func;
new_cfg->line_number = 0;
return new_cfg;
}
/* Read one character from a configfile_t */
-AP_DECLARE(int) ap_cfg_getc(ap_configfile_t *cfp)
+AP_DECLARE(apr_status_t) ap_cfg_getc(char *ch, ap_configfile_t *cfp)
{
- register int ch = cfp->getch(cfp->param);
- if (ch == LF)
+ apr_status_t rc = cfp->getch(ch, cfp->param);
+ if (rc == APR_SUCCESS && *ch == LF)
++cfp->line_number;
- return ch;
+ return rc;
+}
+
+AP_DECLARE(const char *) ap_pcfg_strerror(apr_pool_t *p, ap_configfile_t *cfp,
+ apr_status_t rc)
+{
+ char buf[MAX_STRING_LEN];
+ if (rc == APR_SUCCESS)
+ return NULL;
+ return apr_psprintf(p, "Error reading %s at line %d: %s",
+ cfp->name, cfp->line_number,
+ rc == APR_ENOSPC ? "Line too long"
+ : apr_strerror(rc, buf, sizeof(buf)));
}
/* Read one line from open ap_configfile_t, strip LF, increase line number */
/* If custom handler does not define a getstr() function, read char by char */
-AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
+static apr_status_t ap_cfg_getline_core(char *buf, size_t bufsize,
+ ap_configfile_t *cfp)
{
+ apr_status_t rc;
/* If a "get string" function is defined, use it */
if (cfp->getstr != NULL) {
- char *src, *dst;
char *cp;
char *cbuf = buf;
size_t cbufsize = bufsize;
while (1) {
++cfp->line_number;
- if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
- return 1;
+ rc = cfp->getstr(cbuf, cbufsize, cfp->param);
+ if (rc == APR_EOF) {
+ if (cbuf != buf) {
+ *cbuf = '\0';
+ break;
+ }
+ else {
+ return APR_EOF;
+ }
+ }
+ if (rc != APR_SUCCESS) {
+ return rc;
+ }
/*
* check for line continuation,
* i.e. match [^\\]\\[\r]\n only
*/
cp = cbuf;
- while (cp < cbuf+cbufsize && *cp != '\0')
- cp++;
+ cp += strlen(cp);
if (cp > cbuf && cp[-1] == LF) {
cp--;
if (cp > cbuf && cp[-1] == CR)
cp--;
if (cp > cbuf && cp[-1] == '\\') {
cp--;
- if (!(cp > cbuf && cp[-1] == '\\')) {
- /*
- * line continuation requested -
- * then remove backslash and continue
- */
- cbufsize -= (cp-cbuf);
- cbuf = cp;
- continue;
- }
- else {
- /*
- * no real continuation because escaped -
- * then just remove escape character
- */
- for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
- cp[0] = cp[1];
- }
+ /*
+ * line continuation requested -
+ * then remove backslash and continue
+ */
+ cbufsize -= (cp-cbuf);
+ cbuf = cp;
+ continue;
}
}
+ else if (cp - buf >= bufsize - 1) {
+ return APR_ENOSPC;
+ }
break;
}
-
- /*
- * Leading and trailing white space is eliminated completely
- */
- src = buf;
- while (apr_isspace(*src))
- ++src;
- /* blast trailing whitespace */
- dst = &src[strlen(src)];
- while (--dst >= src && apr_isspace(*dst))
- *dst = '\0';
- /* Zap leading whitespace by shifting */
- if (src != buf)
- for (dst = buf; (*dst++ = *src++) != '\0'; )
- ;
-
-#ifdef DEBUG_CFG_LINES
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf);
-#endif
- return 0;
} else {
/* No "get string" function defined; read character by character */
- register int c;
- register size_t i = 0;
-
- buf[0] = '\0';
- /* skip leading whitespace */
- do {
- c = cfp->getch(cfp->param);
- } while (c == '\t' || c == ' ');
-
- if (c == EOF)
- return 1;
+ size_t i = 0;
- if(bufsize < 2) {
+ if (bufsize < 2) {
/* too small, assume caller is crazy */
- return 1;
+ return APR_EINVAL;
}
+ buf[0] = '\0';
while (1) {
- if ((c == '\t') || (c == ' ')) {
- buf[i++] = ' ';
- while ((c == '\t') || (c == ' '))
- c = cfp->getch(cfp->param);
- }
- if (c == CR) {
- /* silently ignore CR (_assume_ that a LF follows) */
- c = cfp->getch(cfp->param);
+ char c;
+ rc = cfp->getch(&c, cfp->param);
+ if (rc == APR_EOF) {
+ if (i > 0)
+ break;
+ else
+ return APR_EOF;
}
+ if (rc != APR_SUCCESS)
+ return rc;
if (c == LF) {
- /* increase line number and return on LF */
++cfp->line_number;
- }
- if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
- /*
- * check for line continuation
- */
+ /* check for line continuation */
if (i > 0 && buf[i-1] == '\\') {
i--;
- if (!(i > 0 && buf[i-1] == '\\')) {
- /* line is continued */
- c = cfp->getch(cfp->param);
- continue;
- }
- /* else nothing needs be done because
- * then the backslash is escaped and
- * we just strip to a single one
- */
+ continue;
}
- /* blast trailing whitespace */
- while (i > 0 && apr_isspace(buf[i - 1]))
- --i;
- buf[i] = '\0';
-#ifdef DEBUG_CFG_LINES
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
- "Read config: %s", buf);
-#endif
- return 0;
+ else {
+ break;
+ }
+ }
+ else if (i >= bufsize - 2) {
+ return APR_ENOSPC;
}
buf[i] = c;
++i;
- c = cfp->getch(cfp->param);
}
+ buf[i] = '\0';
+ }
+ return APR_SUCCESS;
+}
+
+static int cfg_trim_line(char *buf)
+{
+ char *start, *end;
+ /*
+ * Leading and trailing white space is eliminated completely
+ */
+ start = buf;
+ while (apr_isspace(*start))
+ ++start;
+ /* blast trailing whitespace */
+ end = &start[strlen(start)];
+ while (--end >= start && apr_isspace(*end))
+ *end = '\0';
+ /* Zap leading whitespace by shifting */
+ if (start != buf)
+ memmove(buf, start, end - start + 2);
+#ifdef DEBUG_CFG_LINES
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf);
+#endif
+ return end - start + 1;
+}
+
+/* Read one line from open ap_configfile_t, strip LF, increase line number */
+/* If custom handler does not define a getstr() function, read char by char */
+AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
+{
+ apr_status_t rc = ap_cfg_getline_core(buf, bufsize, cfp);
+ if (rc == APR_SUCCESS)
+ cfg_trim_line(buf);
+ return rc;
+}
+
+AP_DECLARE(apr_status_t) ap_varbuf_cfg_getline(struct ap_varbuf *vb,
+ ap_configfile_t *cfp,
+ apr_size_t max_len)
+{
+ apr_status_t rc;
+ vb->strlen = 0;
+ *vb->buf = '\0';
+
+ for (;;) {
+ apr_size_t new_len;
+ rc = ap_cfg_getline_core(vb->buf + vb->strlen, vb->avail - vb->strlen, cfp);
+ if (rc == APR_ENOSPC || rc == APR_SUCCESS)
+ vb->strlen += strlen(vb->buf + vb->strlen);
+ if (rc != APR_ENOSPC)
+ break;
+ if (vb->avail >= max_len)
+ return APR_ENOSPC;
+ new_len = vb->avail * 2;
+ if (new_len > max_len)
+ new_len = max_len;
+ ap_varbuf_grow(vb, new_len);
+ --cfp->line_number;
}
+ if (vb->strlen > max_len)
+ return APR_ENOSPC;
+ if (rc == APR_SUCCESS)
+ vb->strlen = cfg_trim_line(vb->buf);
+ return rc;
}
/* Size an HTTP header field list item, as separated by a comma.
@@ -1542,54 +1530,15 @@ static char x2c(const char *what)
}
/*
- * Unescapes a URL.
+ * Unescapes a URL, leaving reserved characters intact.
* Returns 0 on success, non-zero on error
* Failure is due to
* bad % escape returns HTTP_BAD_REQUEST
*
- * decoding %00 -> \0 (the null character)
- * decoding %2f -> / (a special character)
- * returns HTTP_NOT_FOUND
+ * decoding %00 or a forbidden character returns HTTP_NOT_FOUND
*/
-AP_DECLARE(int) ap_unescape_url(char *url)
-{
- register int badesc, badpath;
- char *x, *y;
-
- badesc = 0;
- badpath = 0;
- /* Initial scan for first '%'. Don't bother writing values before
- * seeing a '%' */
- y = strchr(url, '%');
- if (y == NULL) {
- return OK;
- }
- for (x = y; *y; ++x, ++y) {
- if (*y != '%')
- *x = *y;
- else {
- if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
- badesc = 1;
- *x = '%';
- }
- else {
- *x = x2c(y + 1);
- y += 2;
- if (IS_SLASH(*x) || *x == '\0')
- badpath = 1;
- }
- }
- }
- *x = '\0';
- if (badesc)
- return HTTP_BAD_REQUEST;
- else if (badpath)
- return HTTP_NOT_FOUND;
- else
- return OK;
-}
-AP_DECLARE(int) ap_unescape_url_keep2f_ex(char *url, int decode_2f)
+static int unescape_url(char *url, const char *forbid, const char *reserved)
{
register int badesc, badpath;
char *x, *y;
@@ -1614,11 +1563,15 @@ AP_DECLARE(int) ap_unescape_url_keep2f_ex(char *url, int decode_2f)
else {
char decoded;
decoded = x2c(y + 1);
- if (decoded == '\0') {
+ if ((decoded == '\0')
+ || (forbid && ap_strchr_c(forbid, decoded))) {
badpath = 1;
+ *x = decoded;
+ y += 2;
}
- else if (IS_SLASH(decoded) && !decode_2f) {
- /* do not decode, just let it go by as-is */
+ else if (reserved && ap_strchr_c(reserved, decoded)) {
+ *x++ = *y++;
+ *x++ = *y++;
*x = *y;
}
else {
@@ -1639,10 +1592,54 @@ AP_DECLARE(int) ap_unescape_url_keep2f_ex(char *url, int decode_2f)
return OK;
}
}
+AP_DECLARE(int) ap_unescape_url(char *url)
+{
+ /* Traditional */
+ return unescape_url(url, SLASHES, NULL);
+}
+AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes)
+{
+ /* AllowEncodedSlashes (corrected) */
+ if (decode_slashes) {
+ /* no chars reserved */
+ return unescape_url(url, NULL, NULL);
+ } else {
+ /* reserve (do not decode) encoded slashes */
+ return unescape_url(url, NULL, SLASHES);
+ }
+}
+#ifdef NEW_APIS
+/* IFDEF these out until they've been thought through.
+ * Just a germ of an API extension for now
+ */
+AP_DECLARE(int) ap_unescape_url_proxy(char *url)
+{
+ /* leave RFC1738 reserved characters intact, * so proxied URLs
+ * don't get mangled. Where does that leave encoded '&' ?
+ */
+ return unescape_url(url, NULL, "/;?");
+}
+AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved)
+{
+ return unescape_url(url, NULL, reserved);
+}
+#endif
-AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
+AP_DECLARE(int) ap_unescape_urlencoded(char *query)
{
- return ap_unescape_url_keep2f_ex(url, 1);
+ char *slider;
+
+ /* replace plus with a space */
+ if (query) {
+ for (slider = query; *slider; slider++) {
+ if (*slider == '+') {
+ *slider = ' ';
+ }
+ }
+ }
+
+ /* unescape everything else */
+ return unescape_url(query, NULL, NULL);
}
AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
@@ -1656,6 +1653,11 @@ AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
}
}
+AP_DECLARE(int) ap_unescape_all(char *url)
+{
+ return unescape_url(url, NULL, NULL);
+}
+
/* c2x takes an unsigned, and expects the caller has guaranteed that
* 0 <= what < 256... which usually means that you have to cast to
* unsigned char first, because (unsigned)(char)(x) first goes through
@@ -1695,9 +1697,8 @@ static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
* something with a '/' in it (and thus does not prefix "./").
*/
-AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
+AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy, const char *segment)
{
- char *copy = apr_palloc(p, 3 * strlen(segment) + 1);
const unsigned char *s = (const unsigned char *)segment;
unsigned char *d = (unsigned char *)copy;
unsigned c;
@@ -1715,6 +1716,11 @@ AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
return copy;
}
+AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
+{
+ return ap_escape_path_segment_buffer(apr_palloc(p, 3 * strlen(segment) + 1), segment);
+}
+
AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial)
{
char *copy = apr_palloc(p, 3 * strlen(path) + 3);
@@ -1744,6 +1750,33 @@ AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partia
return copy;
}
+AP_DECLARE(char *) ap_escape_urlencoded_buffer(char *copy, const char *buffer)
+{
+ const unsigned char *s = (const unsigned char *)buffer;
+ unsigned char *d = (unsigned char *)copy;
+ unsigned c;
+
+ while ((c = *s)) {
+ if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) {
+ d = c2x(c, '%', d);
+ }
+ else if (c == ' ') {
+ *d++ = '+';
+ }
+ else {
+ *d++ = c;
+ }
+ ++s;
+ }
+ *d = '\0';
+ return copy;
+}
+
+AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer)
+{
+ return ap_escape_urlencoded_buffer(apr_palloc(p, 3 * strlen(buffer) + 1), buffer);
+}
+
/* ap_escape_uri is now a macro for os_escape_path */
AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
@@ -1794,10 +1827,6 @@ AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
x[j] = '\0';
return x;
}
-AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
-{
- return ap_escape_html2(p, s, 0);
-}
AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
{
char *ret;
@@ -2004,6 +2033,14 @@ AP_DECLARE(void) ap_str_tolower(char *str)
}
}
+AP_DECLARE(void) ap_str_toupper(char *str)
+{
+ while (*str) {
+ *str = apr_toupper(*str);
+ ++str;
+ }
+}
+
/*
* We must return a FQDN
*/
@@ -2018,7 +2055,7 @@ char *ap_get_local_host(apr_pool_t *a)
char *hostname;
if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) {
- ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00556)
"%s: apr_gethostname() failed to determine ServerName",
ap_server_argv0);
} else {
@@ -2035,7 +2072,7 @@ char *ap_get_local_host(apr_pool_t *a)
server_hostname = apr_pstrdup(a, hostname);
}
} else {
- ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00557)
"%s: apr_sockaddr_info_get() failed for %s",
ap_server_argv0, str);
}
@@ -2044,9 +2081,10 @@ char *ap_get_local_host(apr_pool_t *a)
if (!server_hostname)
server_hostname = apr_pstrdup(a, "127.0.0.1");
- ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a,
+ ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a, APLOGNO(00558)
"%s: Could not reliably determine the server's fully qualified "
- "domain name, using %s for ServerName",
+ "domain name, using %s. Set the 'ServerName' directive globally "
+ "to suppress this message",
ap_server_argv0, server_hostname);
return server_hostname;
@@ -2215,7 +2253,7 @@ AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
break;
case 'm':
switch (*(++time_str)) {
- /* Time is in miliseconds */
+ /* Time is in milliseconds */
case 's':
*timeout = (apr_interval_time_t) tout * 1000;
break;
@@ -2233,3 +2271,504 @@ AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
return APR_SUCCESS;
}
+/**
+ * Determine if a request has a request body or not.
+ *
+ * @param r the request_rec of the request
+ * @return truth value
+ */
+AP_DECLARE(int) ap_request_has_body(request_rec *r)
+{
+ apr_off_t cl;
+ char *estr;
+ const char *cls;
+ int has_body;
+
+ has_body = (!r->header_only
+ && (r->kept_body
+ || apr_table_get(r->headers_in, "Transfer-Encoding")
+ || ( (cls = apr_table_get(r->headers_in, "Content-Length"))
+ && (apr_strtoff(&cl, cls, &estr, 10) == APR_SUCCESS)
+ && (!*estr)
+ && (cl > 0) )
+ )
+ );
+ return has_body;
+}
+
+AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_)
+{
+ void **ptr = (void **)data_;
+ *ptr = NULL;
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
+
+ for ( ; *src; src++, dest++)
+ {
+ if (!apr_isprint(*src))
+ *dest = 'x';
+ else if (!apr_isalnum(*src))
+ *dest = '_';
+ else
+ *dest = (char)*src;
+ }
+ *dest = '\0';
+ return APR_SUCCESS;
+
+}
+
+AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
+ const char **dest)
+{
+ char *new = apr_palloc(p, strlen(src)+1);
+ if (!new)
+ return APR_ENOMEM;
+ *dest = new;
+ return ap_str2_alnum(src, new);
+}
+
+/**
+ * Read the body and parse any form found, which must be of the
+ * type application/x-www-form-urlencoded.
+ *
+ * Name/value pairs are returned in an array, with the names as
+ * strings with a maximum length of HUGE_STRING_LEN, and the
+ * values as bucket brigades. This allows values to be arbitrarily
+ * large.
+ *
+ * All url-encoding is removed from both the names and the values
+ * on the fly. The names are interpreted as strings, while the
+ * values are interpreted as blocks of binary data, that may
+ * contain the 0 character.
+ *
+ * In order to ensure that resource limits are not exceeded, a
+ * maximum size must be provided. If the sum of the lengths of
+ * the names and the values exceed this size, this function
+ * will return HTTP_REQUEST_ENTITY_TOO_LARGE.
+ *
+ * An optional number of parameters can be provided, if the number
+ * of parameters provided exceeds this amount, this function will
+ * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
+ * no limit is imposed, and the number of parameters is in turn
+ * constrained by the size parameter above.
+ *
+ * This function honours any kept_body configuration, and the
+ * original raw request body will be saved to the kept_body brigade
+ * if so configured, just as ap_discard_request_body does.
+ *
+ * NOTE: File upload is not yet supported, but can be without change
+ * to the function call.
+ */
+
+/* form parsing stuff */
+typedef enum {
+ FORM_NORMAL,
+ FORM_AMP,
+ FORM_NAME,
+ FORM_VALUE,
+ FORM_PERCENTA,
+ FORM_PERCENTB,
+ FORM_ABORT
+} ap_form_type_t;
+
+AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
+ apr_array_header_t **ptr,
+ apr_size_t num, apr_size_t usize)
+{
+ apr_bucket_brigade *bb = NULL;
+ int seen_eos = 0;
+ char buffer[HUGE_STRING_LEN + 1];
+ const char *ct;
+ apr_size_t offset = 0;
+ apr_ssize_t size;
+ ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
+ ap_form_pair_t *pair = NULL;
+ apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
+
+ char hi = 0;
+ char low = 0;
+
+ *ptr = pairs;
+
+ /* sanity check - we only support forms for now */
+ ct = apr_table_get(r->headers_in, "Content-Type");
+ if (!ct || strcmp("application/x-www-form-urlencoded", ct)) {
+ return ap_discard_request_body(r);
+ }
+
+ if (usize > APR_SIZE_MAX >> 1)
+ size = APR_SIZE_MAX >> 1;
+ else
+ size = usize;
+
+ if (!f) {
+ f = r->input_filters;
+ }
+
+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ do {
+ apr_bucket *bucket = NULL, *last = NULL;
+
+ int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
+ APR_BLOCK_READ, HUGE_STRING_LEN);
+ if (rv != APR_SUCCESS) {
+ apr_brigade_destroy(bb);
+ return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST;
+ }
+
+ for (bucket = APR_BRIGADE_FIRST(bb);
+ bucket != APR_BRIGADE_SENTINEL(bb);
+ last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
+ const char *data;
+ apr_size_t len, slide;
+
+ if (last) {
+ apr_bucket_delete(last);
+ }
+ if (APR_BUCKET_IS_EOS(bucket)) {
+ seen_eos = 1;
+ break;
+ }
+ if (bucket->length == 0) {
+ continue;
+ }
+
+ rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ apr_brigade_destroy(bb);
+ return HTTP_BAD_REQUEST;
+ }
+
+ slide = len;
+ while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
+ char c = *data++;
+ if ('+' == c) {
+ c = ' ';
+ }
+ else if ('&' == c) {
+ state = FORM_AMP;
+ }
+ if ('%' == c) {
+ percent = FORM_PERCENTA;
+ continue;
+ }
+ if (FORM_PERCENTA == percent) {
+ if (c >= 'a') {
+ hi = c - 'a' + 10;
+ }
+ else if (c >= 'A') {
+ hi = c - 'A' + 10;
+ }
+ else if (c >= '0') {
+ hi = c - '0';
+ }
+ hi = hi << 4;
+ percent = FORM_PERCENTB;
+ continue;
+ }
+ if (FORM_PERCENTB == percent) {
+ if (c >= 'a') {
+ low = c - 'a' + 10;
+ }
+ else if (c >= 'A') {
+ low = c - 'A' + 10;
+ }
+ else if (c >= '0') {
+ low = c - '0';
+ }
+ c = low | hi;
+ percent = FORM_NORMAL;
+ }
+ switch (state) {
+ case FORM_AMP:
+ if (pair) {
+ const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+ apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pair->value, b);
+ }
+ state = FORM_NAME;
+ pair = NULL;
+ offset = 0;
+ num--;
+ break;
+ case FORM_NAME:
+ if (offset < HUGE_STRING_LEN) {
+ if ('=' == c) {
+ buffer[offset] = 0;
+ offset = 0;
+ pair = (ap_form_pair_t *) apr_array_push(pairs);
+ pair->name = apr_pstrdup(r->pool, buffer);
+ pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ state = FORM_VALUE;
+ }
+ else {
+ buffer[offset++] = c;
+ size--;
+ }
+ }
+ else {
+ state = FORM_ABORT;
+ }
+ break;
+ case FORM_VALUE:
+ if (offset >= HUGE_STRING_LEN) {
+ const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+ apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pair->value, b);
+ offset = 0;
+ }
+ buffer[offset++] = c;
+ size--;
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+
+ apr_brigade_cleanup(bb);
+ } while (!seen_eos);
+
+ if (FORM_ABORT == state || size < 0 || num == 0) {
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+ else if (FORM_VALUE == state && pair && offset > 0) {
+ const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+ apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pair->value, b);
+ }
+
+ return OK;
+
+}
+
+#define VARBUF_SMALL_SIZE 2048
+#define VARBUF_MAX_SIZE (APR_SIZE_MAX - 1 - \
+ APR_ALIGN_DEFAULT(sizeof(struct ap_varbuf_info)))
+
+struct ap_varbuf_info {
+ struct apr_memnode_t *node;
+ apr_allocator_t *allocator;
+};
+
+static apr_status_t varbuf_cleanup(void *info_)
+{
+ struct ap_varbuf_info *info = info_;
+ info->node->next = NULL;
+ apr_allocator_free(info->allocator, info->node);
+ return APR_SUCCESS;
+}
+
+const char nul = '\0';
+static char * const varbuf_empty = (char *)&nul;
+
+AP_DECLARE(void) ap_varbuf_init(apr_pool_t *p, struct ap_varbuf *vb,
+ apr_size_t init_size)
+{
+ vb->buf = varbuf_empty;
+ vb->avail = 0;
+ vb->strlen = AP_VARBUF_UNKNOWN;
+ vb->pool = p;
+ vb->info = NULL;
+
+ ap_varbuf_grow(vb, init_size);
+}
+
+AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
+{
+ apr_memnode_t *new_node = NULL;
+ apr_allocator_t *allocator;
+ struct ap_varbuf_info *new_info;
+ char *new;
+
+ if (new_len <= vb->avail)
+ return;
+
+ if (new_len < 2 * vb->avail && vb->avail < VARBUF_MAX_SIZE/2) {
+ /* at least double the size, to avoid repeated reallocations */
+ new_len = 2 * vb->avail;
+ }
+ else if (new_len > VARBUF_MAX_SIZE) {
+ apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool);
+ ap_assert(abort_fn != NULL);
+ abort_fn(APR_ENOMEM);
+ return;
+ }
+
+ new_len++; /* add space for trailing \0 */
+ if (new_len <= VARBUF_SMALL_SIZE) {
+ new_len = APR_ALIGN_DEFAULT(new_len);
+ new = apr_palloc(vb->pool, new_len);
+ if (vb->avail && vb->strlen != 0) {
+ AP_DEBUG_ASSERT(vb->buf != NULL);
+ AP_DEBUG_ASSERT(vb->buf != varbuf_empty);
+ if (new == vb->buf + vb->avail + 1) {
+ /* We are lucky: the new memory lies directly after our old
+ * buffer, we can now use both.
+ */
+ vb->avail += new_len;
+ return;
+ }
+ else {
+ /* copy up to vb->strlen + 1 bytes */
+ memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
+ vb->avail + 1 : vb->strlen + 1);
+ }
+ }
+ else {
+ *new = '\0';
+ }
+ vb->avail = new_len - 1;
+ vb->buf = new;
+ return;
+ }
+
+ /* The required block is rather larger. Use allocator directly so that
+ * the memory can be freed independently from the pool. */
+ allocator = apr_pool_allocator_get(vb->pool);
+ if (new_len <= VARBUF_MAX_SIZE)
+ new_node = apr_allocator_alloc(allocator,
+ new_len + APR_ALIGN_DEFAULT(sizeof(*new_info)));
+ if (!new_node) {
+ apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool);
+ ap_assert(abort_fn != NULL);
+ abort_fn(APR_ENOMEM);
+ return;
+ }
+ new_info = (struct ap_varbuf_info *)new_node->first_avail;
+ new_node->first_avail += APR_ALIGN_DEFAULT(sizeof(*new_info));
+ new_info->node = new_node;
+ new_info->allocator = allocator;
+ new = new_node->first_avail;
+ AP_DEBUG_ASSERT(new_node->endp - new_node->first_avail >= new_len);
+ new_len = new_node->endp - new_node->first_avail;
+
+ if (vb->avail && vb->strlen != 0)
+ memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
+ vb->avail + 1 : vb->strlen + 1);
+ else
+ *new = '\0';
+ if (vb->info)
+ apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup);
+ apr_pool_cleanup_register(vb->pool, new_info, varbuf_cleanup,
+ apr_pool_cleanup_null);
+ vb->info = new_info;
+ vb->buf = new;
+ vb->avail = new_len - 1;
+}
+
+AP_DECLARE(void) ap_varbuf_strmemcat(struct ap_varbuf *vb, const char *str,
+ int len)
+{
+ if (len == 0)
+ return;
+ if (!vb->avail) {
+ ap_varbuf_grow(vb, len);
+ memcpy(vb->buf, str, len);
+ vb->buf[len] = '\0';
+ vb->strlen = len;
+ return;
+ }
+ if (vb->strlen == AP_VARBUF_UNKNOWN)
+ vb->strlen = strlen(vb->buf);
+ ap_varbuf_grow(vb, vb->strlen + len);
+ memcpy(vb->buf + vb->strlen, str, len);
+ vb->strlen += len;
+ vb->buf[vb->strlen] = '\0';
+}
+
+AP_DECLARE(void) ap_varbuf_free(struct ap_varbuf *vb)
+{
+ if (vb->info) {
+ apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup);
+ vb->info = NULL;
+ }
+ vb->buf = NULL;
+}
+
+AP_DECLARE(char *) ap_varbuf_pdup(apr_pool_t *p, struct ap_varbuf *buf,
+ const char *prepend, apr_size_t prepend_len,
+ const char *append, apr_size_t append_len,
+ apr_size_t *new_len)
+{
+ apr_size_t i = 0;
+ struct iovec vec[3];
+
+ if (prepend) {
+ vec[i].iov_base = (void *)prepend;
+ vec[i].iov_len = prepend_len;
+ i++;
+ }
+ if (buf->avail && buf->strlen) {
+ vec[i].iov_base = (void *)buf->buf;
+ vec[i].iov_len = (buf->strlen == AP_VARBUF_UNKNOWN) ? buf->avail
+ : buf->strlen;
+ i++;
+ }
+ if (append) {
+ vec[i].iov_base = (void *)append;
+ vec[i].iov_len = append_len;
+ i++;
+ }
+ if (i)
+ return apr_pstrcatv(p, vec, i, new_len);
+
+ if (new_len)
+ *new_len = 0;
+ return "";
+}
+
+AP_DECLARE(apr_status_t) ap_varbuf_regsub(struct ap_varbuf *vb,
+ const char *input,
+ const char *source, size_t nmatch,
+ ap_regmatch_t pmatch[],
+ apr_size_t maxlen)
+{
+ return regsub_core(NULL, NULL, vb, input, source, nmatch, pmatch, maxlen);
+}
+
+static const char * const oom_message = "[crit] Memory allocation failed, "
+ "aborting process." APR_EOL_STR;
+
+AP_DECLARE(void) ap_abort_on_oom()
+{
+ int written, count = strlen(oom_message);
+ const char *buf = oom_message;
+ do {
+ written = write(STDERR_FILENO, buf, count);
+ if (written == count)
+ break;
+ if (written > 0) {
+ buf += written;
+ count -= written;
+ }
+ } while (written >= 0 || errno == EINTR);
+ abort();
+}
+
+AP_DECLARE(void *) ap_malloc(size_t size)
+{
+ void *p = malloc(size);
+ if (p == NULL && size != 0)
+ ap_abort_on_oom();
+ return p;
+}
+
+AP_DECLARE(void *) ap_calloc(size_t nelem, size_t size)
+{
+ void *p = calloc(nelem, size);
+ if (p == NULL && nelem != 0 && size != 0)
+ ap_abort_on_oom();
+ return p;
+}
+
+AP_DECLARE(void *) ap_realloc(void *ptr, size_t size)
+{
+ void *p = realloc(ptr, size);
+ if (p == NULL && size != 0)
+ ap_abort_on_oom();
+ return p;
+}
diff --git a/server/util_cfgtree.c b/server/util_cfgtree.c
index ac284a7b..a7142e3d 100644
--- a/server/util_cfgtree.c
+++ b/server/util_cfgtree.c
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#define CORE_PRIVATE
#include "util_cfgtree.h"
#include <stdlib.h>
diff --git a/server/util_cookies.c b/server/util_cookies.c
new file mode 100644
index 00000000..dc8c457b
--- /dev/null
+++ b/server/util_cookies.c
@@ -0,0 +1,290 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util_cookies.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+
+#define LOG_PREFIX "ap_cookie: "
+
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
+/**
+ * Write an RFC2109 compliant cookie.
+ *
+ * @param r The request
+ * @param name The name of the cookie.
+ * @param val The value to place in the cookie.
+ * @param attrs The string containing additional cookie attributes. If NULL, the
+ * DEFAULT_ATTRS will be used.
+ * @param maxage If non zero, a Max-Age header will be added to the cookie.
+ */
+AP_DECLARE(apr_status_t) ap_cookie_write(request_rec * r, const char *name, const char *val,
+ const char *attrs, long maxage, ...)
+{
+
+ const char *buffer;
+ const char *rfc2109;
+ apr_table_t *t;
+ va_list vp;
+
+ /* handle expiry */
+ buffer = "";
+ if (maxage) {
+ buffer = apr_pstrcat(r->pool, "Max-Age=", apr_ltoa(r->pool, maxage), ";", NULL);
+ }
+
+ /* create RFC2109 compliant cookie */
+ rfc2109 = apr_pstrcat(r->pool, name, "=", val, ";", buffer,
+ attrs && *attrs ? attrs : DEFAULT_ATTRS, NULL);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00007) LOG_PREFIX
+ "user '%s' set cookie: '%s'", r->user, rfc2109);
+
+ /* write the cookie to the header table(s) provided */
+ va_start(vp, maxage);
+ while ((t = va_arg(vp, apr_table_t *))) {
+ apr_table_addn(t, SET_COOKIE, rfc2109);
+ }
+ va_end(vp);
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * Write an RFC2965 compliant cookie.
+ *
+ * @param r The request
+ * @param name2 The name of the cookie.
+ * @param val The value to place in the cookie.
+ * @param attrs2 The string containing additional cookie attributes. If NULL, the
+ * DEFAULT_ATTRS will be used.
+ * @param maxage If non zero, a Max-Age header will be added to the cookie.
+ */
+AP_DECLARE(apr_status_t) ap_cookie_write2(request_rec * r, const char *name2, const char *val,
+ const char *attrs2, long maxage, ...)
+{
+
+ const char *buffer;
+ const char *rfc2965;
+ apr_table_t *t;
+ va_list vp;
+
+ /* handle expiry */
+ buffer = "";
+ if (maxage) {
+ buffer = apr_pstrcat(r->pool, "Max-Age=", apr_ltoa(r->pool, maxage), ";", NULL);
+ }
+
+ /* create RFC2965 compliant cookie */
+ rfc2965 = apr_pstrcat(r->pool, name2, "=", val, ";", buffer,
+ attrs2 && *attrs2 ? attrs2 : DEFAULT_ATTRS, NULL);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00008) LOG_PREFIX
+ "user '%s' set cookie2: '%s'", r->user, rfc2965);
+
+ /* write the cookie to the header table(s) provided */
+ va_start(vp, maxage);
+ while ((t = va_arg(vp, apr_table_t *))) {
+ apr_table_addn(t, SET_COOKIE2, rfc2965);
+ }
+ va_end(vp);
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * Remove an RFC2109 compliant cookie.
+ *
+ * @param r The request
+ * @param name The name of the cookie.
+ */
+AP_DECLARE(apr_status_t) ap_cookie_remove(request_rec * r, const char *name, const char *attrs, ...)
+{
+ apr_table_t *t;
+ va_list vp;
+
+ /* create RFC2109 compliant cookie */
+ const char *rfc2109 = apr_pstrcat(r->pool, name, "=;Max-Age=0;",
+ attrs ? attrs : CLEAR_ATTRS, NULL);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00009) LOG_PREFIX
+ "user '%s' removed cookie: '%s'", r->user, rfc2109);
+
+ /* write the cookie to the header table(s) provided */
+ va_start(vp, attrs);
+ while ((t = va_arg(vp, apr_table_t *))) {
+ apr_table_addn(t, SET_COOKIE, rfc2109);
+ }
+ va_end(vp);
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * Remove an RFC2965 compliant cookie.
+ *
+ * @param r The request
+ * @param name2 The name of the cookie.
+ */
+AP_DECLARE(apr_status_t) ap_cookie_remove2(request_rec * r, const char *name2, const char *attrs2, ...)
+{
+ apr_table_t *t;
+ va_list vp;
+
+ /* create RFC2965 compliant cookie */
+ const char *rfc2965 = apr_pstrcat(r->pool, name2, "=;Max-Age=0;",
+ attrs2 ? attrs2 : CLEAR_ATTRS, NULL);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00010) LOG_PREFIX
+ "user '%s' removed cookie2: '%s'", r->user, rfc2965);
+
+ /* write the cookie to the header table(s) provided */
+ va_start(vp, attrs2);
+ while ((t = va_arg(vp, apr_table_t *))) {
+ apr_table_addn(t, SET_COOKIE2, rfc2965);
+ }
+ va_end(vp);
+
+ return APR_SUCCESS;
+
+}
+
+/* Iterate through the cookies, isolate our cookie and then remove it.
+ *
+ * If our cookie appears two or more times, but with different values,
+ * remove it twice and set the duplicated flag to true. Remove any
+ * $path or other attributes following our cookie if present. If we end
+ * up with an empty cookie, remove the whole header.
+ */
+static int extract_cookie_line(ap_cookie_do * v, const char *key, const char *val)
+{
+ char *last1, *last2;
+ char *cookie = apr_pstrdup(v->r->pool, val);
+ const char *name = apr_pstrcat(v->r->pool, v->name ? v->name : "", "=", NULL);
+ size_t len = strlen(name);
+ const char *new_cookie = "";
+ const char *comma = ",";
+ char *next1;
+ const char *semi = ";";
+ char *next2;
+ const char *sep = "";
+ int cookies = 0;
+
+ /* find the cookie called name */
+ int eat = 0;
+ next1 = apr_strtok(cookie, comma, &last1);
+ while (next1) {
+ next2 = apr_strtok(next1, semi, &last2);
+ while (next2) {
+ char *trim = next2;
+ while (apr_isspace(*trim)) {
+ trim++;
+ }
+ if (!strncmp(trim, name, len)) {
+ if (v->encoded) {
+ if (strcmp(v->encoded, trim + len)) {
+ v->duplicated = 1;
+ }
+ }
+ v->encoded = apr_pstrdup(v->r->pool, trim + len);
+ eat = 1;
+ }
+ else {
+ if (*trim != '$') {
+ cookies++;
+ eat = 0;
+ }
+ if (!eat) {
+ new_cookie = apr_pstrcat(v->r->pool, new_cookie, sep, next2, NULL);
+ }
+ }
+ next2 = apr_strtok(NULL, semi, &last2);
+ sep = semi;
+ }
+
+ next1 = apr_strtok(NULL, comma, &last1);
+ sep = comma;
+ }
+
+ /* any cookies left over? */
+ if (cookies) {
+ apr_table_addn(v->new_cookies, key, new_cookie);
+ }
+
+ return 1;
+}
+
+/**
+ * Read a cookie called name, placing its value in val.
+ *
+ * Both the Cookie and Cookie2 headers are scanned for the cookie.
+ *
+ * If the cookie is duplicated, this function returns APR_EGENERAL. If found,
+ * and if remove is non zero, the cookie will be removed from the headers, and
+ * thus kept private from the backend.
+ */
+AP_DECLARE(apr_status_t) ap_cookie_read(request_rec * r, const char *name, const char **val,
+ int remove)
+{
+
+ ap_cookie_do v;
+ v.r = r;
+ v.encoded = NULL;
+ v.new_cookies = apr_table_make(r->pool, 10);
+ v.duplicated = 0;
+ v.name = name;
+
+ apr_table_do((int (*) (void *, const char *, const char *))
+ extract_cookie_line, (void *) &v, r->headers_in,
+ "Cookie", "Cookie2", NULL);
+ if (v.duplicated) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00011) LOG_PREFIX
+ "client submitted cookie '%s' more than once: %s", v.name, r->uri);
+ return APR_EGENERAL;
+ }
+
+ /* remove our cookie(s), and replace them */
+ if (remove) {
+ apr_table_unset(r->headers_in, "Cookie");
+ apr_table_unset(r->headers_in, "Cookie2");
+ r->headers_in = apr_table_overlay(r->pool, r->headers_in, v.new_cookies);
+ }
+
+ *val = v.encoded;
+
+ return APR_SUCCESS;
+
+}
+
+/**
+ * Sanity check a given string that it exists, is not empty,
+ * and does not contain the special characters '=', ';' and '&'.
+ *
+ * It is used to sanity check the cookie names.
+ */
+AP_DECLARE(apr_status_t) ap_cookie_check_string(const char *string)
+{
+ if (!string || !*string || ap_strchr_c(string, '=') || ap_strchr_c(string, '&') ||
+ ap_strchr_c(string, ';')) {
+ return APR_EGENERAL;
+ }
+ return APR_SUCCESS;
+}
diff --git a/server/util_debug.c b/server/util_debug.c
index 0fefc5c8..41250da6 100644
--- a/server/util_debug.c
+++ b/server/util_debug.c
@@ -19,6 +19,7 @@
#include "httpd.h"
#include "http_config.h"
+#include "http_core.h"
/* Possibly get rid of the macros we defined in httpd.h */
#if defined(strchr)
@@ -106,6 +107,91 @@ AP_DECLARE(void *) ap_get_module_config(const ap_conf_vector_t *cv,
return ((void **)cv)[m->module_index];
}
+#if defined(ap_get_core_module_config)
+#undef ap_get_core_module_config
+AP_DECLARE(void *) ap_get_core_module_config(const ap_conf_vector_t *cv);
+#endif
+
+AP_DECLARE(void *) ap_get_core_module_config(const ap_conf_vector_t *cv)
+{
+ return ((void **)cv)[AP_CORE_MODULE_INDEX];
+}
+
+
+#if defined(ap_get_server_module_loglevel)
+#undef ap_get_server_module_loglevel
+AP_DECLARE(int) ap_get_server_module_loglevel(const server_rec *s, int module_index);
+#endif
+
+AP_DECLARE(int) ap_get_server_module_loglevel(const server_rec *s, int module_index)
+{
+ if (module_index < 0 || s->log.module_levels == NULL ||
+ s->log.module_levels[module_index] < 0)
+ {
+ return s->log.level;
+ }
+
+ return s->log.module_levels[module_index];
+}
+
+#if defined(ap_get_conn_module_loglevel)
+#undef ap_get_conn_module_loglevel
+AP_DECLARE(int) ap_get_conn_module_loglevel(const conn_rec *c, int module_index);
+#endif
+
+AP_DECLARE(int) ap_get_conn_module_loglevel(const conn_rec *c, int module_index)
+{
+ const struct ap_logconf *l = (c)->log ? (c)->log : &(c)->base_server->log;
+ if (module_index < 0 || l->module_levels == NULL ||
+ l->module_levels[module_index] < 0)
+ {
+ return l->level;
+ }
+
+ return l->module_levels[module_index];
+}
+
+#if defined(ap_get_conn_server_module_loglevel)
+#undef ap_get_conn_server_module_loglevel
+AP_DECLARE(int) ap_get_conn_server_module_loglevel(const conn_rec *c,
+ const server_rec *s,
+ int module_index);
+#endif
+
+AP_DECLARE(int) ap_get_conn_server_module_loglevel(const conn_rec *c,
+ const server_rec *s,
+ int module_index)
+{
+ const struct ap_logconf *l = (c->log && c->log != &c->base_server->log) ?
+ c->log : &s->log;
+ if (module_index < 0 || l->module_levels == NULL ||
+ l->module_levels[module_index] < 0)
+ {
+ return l->level;
+ }
+
+ return l->module_levels[module_index];
+}
+
+#if defined(ap_get_request_module_loglevel)
+#undef ap_get_request_module_loglevel
+AP_DECLARE(int) ap_get_request_module_loglevel(const request_rec *c, int module_index);
+#endif
+
+AP_DECLARE(int) ap_get_request_module_loglevel(const request_rec *r, int module_index)
+{
+ const struct ap_logconf *l = r->log ? r->log :
+ r->connection->log ? r->connection->log :
+ &r->server->log;
+ if (module_index < 0 || l->module_levels == NULL ||
+ l->module_levels[module_index] < 0)
+ {
+ return l->level;
+ }
+
+ return l->module_levels[module_index];
+}
+
/**
* Generic accessors for other modules to set at their own module-specific
* data
@@ -113,7 +199,7 @@ AP_DECLARE(void *) ap_get_module_config(const ap_conf_vector_t *cv,
* usually r->per_dir_config or s->module_config
* @param m The module to set the data for.
* @param val The module-specific data to set
- * @deffunc void ap_set_module_config(ap_conf_vector_t *cv, const module *m, void *val)
+ * @fn void ap_set_module_config(ap_conf_vector_t *cv, const module *m, void *val)
*/
#if defined(ap_set_module_config)
#undef ap_set_module_config
@@ -126,3 +212,14 @@ AP_DECLARE(void) ap_set_module_config(ap_conf_vector_t *cv, const module *m,
{
((void **)cv)[m->module_index] = val;
}
+
+
+#if defined(ap_set_core_module_config)
+#undef ap_set_core_module_config
+AP_DECLARE(void) ap_set_core_module_config(ap_conf_vector_t *cv, void *val);
+#endif
+
+AP_DECLARE(void) ap_set_core_module_config(ap_conf_vector_t *cv, void *val)
+{
+ ((void **)cv)[AP_CORE_MODULE_INDEX] = val;
+}
diff --git a/server/util_ebcdic.c b/server/util_ebcdic.c
index 78cf2739..1a8c0ba3 100644
--- a/server/util_ebcdic.c
+++ b/server/util_ebcdic.c
@@ -24,42 +24,45 @@
#include "http_core.h"
#include "util_ebcdic.h"
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
apr_status_t ap_init_ebcdic(apr_pool_t *pool)
{
apr_status_t rv;
- char buf[80];
rv = apr_xlate_open(&ap_hdrs_to_ascii, "ISO-8859-1", APR_DEFAULT_CHARSET, pool);
if (rv) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00040)
"apr_xlate_open() failed");
return rv;
}
rv = apr_xlate_open(&ap_hdrs_from_ascii, APR_DEFAULT_CHARSET, "ISO-8859-1", pool);
if (rv) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00041)
"apr_xlate_open() failed");
return rv;
}
rv = apr_MD5InitEBCDIC(ap_hdrs_to_ascii);
if (rv) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00042)
"apr_MD5InitEBCDIC() failed");
return rv;
}
rv = apr_base64init_ebcdic(ap_hdrs_to_ascii, ap_hdrs_from_ascii);
if (rv) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00043)
"apr_base64init_ebcdic() failed");
return rv;
}
rv = apr_SHA1InitEBCDIC(ap_hdrs_to_ascii);
if (rv) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00044)
"apr_SHA1InitEBCDIC() failed");
return rv;
}
diff --git a/server/util_expr_eval.c b/server/util_expr_eval.c
new file mode 100644
index 00000000..6c543f54
--- /dev/null
+++ b/server/util_expr_eval.c
@@ -0,0 +1,1723 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* _ _
+ * ap_expr_eval.c, based on ssl_expr_eval.c from mod_ssl
+ */
+
+#include "httpd.h"
+#include "http_log.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "ap_provider.h"
+#include "util_expr_private.h"
+
+#include "apr_lib.h"
+#include "apr_fnmatch.h"
+
+#include <limits.h> /* for INT_MAX */
+
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
+APR_HOOK_STRUCT(
+ APR_HOOK_LINK(expr_lookup)
+)
+
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, expr_lookup, (ap_expr_lookup_parms *parms),
+ (parms), DECLINED)
+
+#define LOG_MARK(info) __FILE__, __LINE__, (info)->module_index
+
+static const char *ap_expr_eval_string_func(ap_expr_eval_ctx_t *ctx,
+ const ap_expr_t *info,
+ const ap_expr_t *args);
+static const char *ap_expr_eval_re_backref(ap_expr_eval_ctx_t *ctx,
+ unsigned int n);
+static const char *ap_expr_eval_var(ap_expr_eval_ctx_t *ctx,
+ ap_expr_var_func_t *func,
+ const void *data);
+
+/* define AP_EXPR_DEBUG to log the parse tree when parsing an expression */
+#ifdef AP_EXPR_DEBUG
+static void expr_dump_tree(const ap_expr_t *e, const server_rec *s,
+ int loglevel, int indent);
+#endif
+
+/*
+ * To reduce counting overhead, we only count calls to
+ * ap_expr_eval_word() and ap_expr_eval(). The max number of
+ * stack frames is larger by some factor.
+ */
+#define AP_EXPR_MAX_RECURSION 20
+static int inc_rec(ap_expr_eval_ctx_t *ctx)
+{
+ if (ctx->reclvl < AP_EXPR_MAX_RECURSION) {
+ ctx->reclvl++;
+ return 0;
+ }
+ *ctx->err = "Recursion limit reached";
+ /* short circuit further evaluation */
+ ctx->reclvl = INT_MAX;
+ return 1;
+}
+
+static const char *ap_expr_eval_word(ap_expr_eval_ctx_t *ctx,
+ const ap_expr_t *node)
+{
+ const char *result = "";
+ if (inc_rec(ctx))
+ return result;
+ switch (node->node_op) {
+ case op_Digit:
+ case op_String:
+ result = node->node_arg1;
+ break;
+ case op_Var:
+ result = ap_expr_eval_var(ctx, (ap_expr_var_func_t *)node->node_arg1,
+ node->node_arg2);
+ break;
+ case op_Concat:
+ if (((ap_expr_t *)node->node_arg2)->node_op != op_Concat) {
+ const char *s1 = ap_expr_eval_word(ctx, node->node_arg1);
+ const char *s2 = ap_expr_eval_word(ctx, node->node_arg2);
+ if (!*s1)
+ result = s2;
+ else if (!*s2)
+ result = s1;
+ else
+ result = apr_pstrcat(ctx->p, s1, s2, NULL);
+ }
+ else {
+ const ap_expr_t *nodep = node;
+ int i = 1;
+ struct iovec *vec;
+ do {
+ nodep = nodep->node_arg2;
+ i++;
+ } while (nodep->node_op == op_Concat);
+ vec = apr_palloc(ctx->p, i * sizeof(struct iovec));
+ nodep = node;
+ i = 0;
+ do {
+ vec[i].iov_base = (void *)ap_expr_eval_word(ctx,
+ nodep->node_arg1);
+ vec[i].iov_len = strlen(vec[i].iov_base);
+ i++;
+ nodep = nodep->node_arg2;
+ } while (nodep->node_op == op_Concat);
+ vec[i].iov_base = (void *)ap_expr_eval_word(ctx, nodep);
+ vec[i].iov_len = strlen(vec[i].iov_base);
+ i++;
+ result = apr_pstrcatv(ctx->p, vec, i, NULL);
+ }
+ break;
+ case op_StringFuncCall: {
+ const ap_expr_t *info = node->node_arg1;
+ const ap_expr_t *args = node->node_arg2;
+ result = ap_expr_eval_string_func(ctx, info, args);
+ break;
+ }
+ case op_RegexBackref: {
+ const unsigned int *np = node->node_arg1;
+ result = ap_expr_eval_re_backref(ctx, *np);
+ break;
+ }
+ default:
+ *ctx->err = "Internal evaluation error: Unknown word expression node";
+ break;
+ }
+ if (!result)
+ result = "";
+ ctx->reclvl--;
+ return result;
+}
+
+static const char *ap_expr_eval_var(ap_expr_eval_ctx_t *ctx,
+ ap_expr_var_func_t *func,
+ const void *data)
+{
+ AP_DEBUG_ASSERT(func != NULL);
+ AP_DEBUG_ASSERT(data != NULL);
+ return (*func)(ctx, data);
+}
+
+static const char *ap_expr_eval_re_backref(ap_expr_eval_ctx_t *ctx, unsigned int n)
+{
+ int len;
+
+ if (!ctx->re_pmatch || !ctx->re_source || *ctx->re_source == '\0' ||
+ ctx->re_nmatch < n + 1)
+ return "";
+
+ len = ctx->re_pmatch[n].rm_eo - ctx->re_pmatch[n].rm_so;
+ if (len == 0)
+ return "";
+
+ return apr_pstrndup(ctx->p, *ctx->re_source + ctx->re_pmatch[n].rm_so, len);
+}
+
+static const char *ap_expr_eval_string_func(ap_expr_eval_ctx_t *ctx,
+ const ap_expr_t *info,
+ const ap_expr_t *arg)
+{
+ ap_expr_string_func_t *func = (ap_expr_string_func_t *)info->node_arg1;
+ const void *data = info->node_arg2;
+
+ AP_DEBUG_ASSERT(info->node_op == op_StringFuncInfo);
+ AP_DEBUG_ASSERT(func != NULL);
+ AP_DEBUG_ASSERT(data != NULL);
+ return (*func)(ctx, data, ap_expr_eval_word(ctx, arg));
+}
+
+static int intstrcmp(const char *s1, const char *s2)
+{
+ apr_int64_t i1 = apr_atoi64(s1);
+ apr_int64_t i2 = apr_atoi64(s2);
+
+ if (i1 < i2)
+ return -1;
+ else if (i1 == i2)
+ return 0;
+ else
+ return 1;
+}
+
+static int ap_expr_eval_comp(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
+{
+ const ap_expr_t *e1 = node->node_arg1;
+ const ap_expr_t *e2 = node->node_arg2;
+ switch (node->node_op) {
+ case op_EQ:
+ return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0);
+ case op_NE:
+ return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0);
+ case op_LT:
+ return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0);
+ case op_LE:
+ return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0);
+ case op_GT:
+ return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0);
+ case op_GE:
+ return (intstrcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
+ case op_STR_EQ:
+ return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0);
+ case op_STR_NE:
+ return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0);
+ case op_STR_LT:
+ return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0);
+ case op_STR_LE:
+ return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0);
+ case op_STR_GT:
+ return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0);
+ case op_STR_GE:
+ return (strcmp(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
+ case op_IN: {
+ const char *needle = ap_expr_eval_word(ctx, e1);
+ if (e2->node_op == op_ListElement) {
+ do {
+ const ap_expr_t *val = e2->node_arg1;
+ AP_DEBUG_ASSERT(e2->node_op == op_ListElement);
+ if (strcmp(needle, ap_expr_eval_word(ctx, val)) == 0) {
+ return 1;
+ break;
+ }
+ e2 = e2->node_arg2;
+ } while (e2 != NULL);
+ }
+ else if (e2->node_op == op_ListFuncCall) {
+ const ap_expr_t *info = e2->node_arg1;
+ const ap_expr_t *arg = e2->node_arg2;
+ ap_expr_list_func_t *func = (ap_expr_list_func_t *)info->node_arg1;
+ apr_array_header_t *haystack;
+ int i = 0;
+ AP_DEBUG_ASSERT(func != NULL);
+ AP_DEBUG_ASSERT(info->node_op == op_ListFuncInfo);
+ haystack = (*func)(ctx, info->node_arg2, ap_expr_eval_word(ctx, arg));
+ if (haystack == NULL)
+ return 0;
+ for (; i < haystack->nelts; i++) {
+ if (strcmp(needle, APR_ARRAY_IDX(haystack,i,char *)) == 0)
+ return 1;
+ }
+ }
+ return 0;
+ }
+ case op_REG:
+ case op_NRE: {
+ const char *word = ap_expr_eval_word(ctx, e1);
+ const ap_regex_t *regex = e2->node_arg1;
+ int result;
+
+ /*
+ * $0 ... $9 may contain stuff the user wants to keep. Therefore
+ * we only set them if there are capturing parens in the regex.
+ */
+ if (regex->re_nsub > 0) {
+ result = (0 == ap_regexec(regex, word, ctx->re_nmatch,
+ ctx->re_pmatch, 0));
+ *ctx->re_source = result ? word : NULL;
+ }
+ else {
+ result = (0 == ap_regexec(regex, word, 0, NULL, 0));
+ }
+
+ if (node->node_op == op_REG)
+ return result;
+ else
+ return !result;
+ }
+ default:
+ *ctx->err = "Internal evaluation error: Unknown comp expression node";
+ return -1;
+ }
+}
+
+/* combined string/int comparison for compatibility with ssl_expr */
+static int strcmplex(const char *str1, const char *str2)
+{
+ int i, n1, n2;
+
+ if (str1 == NULL)
+ return -1;
+ if (str2 == NULL)
+ return +1;
+ n1 = strlen(str1);
+ n2 = strlen(str2);
+ if (n1 > n2)
+ return 1;
+ if (n1 < n2)
+ return -1;
+ for (i = 0; i < n1; i++) {
+ if (str1[i] > str2[i])
+ return 1;
+ if (str1[i] < str2[i])
+ return -1;
+ }
+ return 0;
+}
+
+static int ssl_expr_eval_comp(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
+{
+ const ap_expr_t *e1 = node->node_arg1;
+ const ap_expr_t *e2 = node->node_arg2;
+ switch (node->node_op) {
+ case op_EQ:
+ case op_STR_EQ:
+ return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) == 0);
+ case op_NE:
+ case op_STR_NE:
+ return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) != 0);
+ case op_LT:
+ case op_STR_LT:
+ return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) < 0);
+ case op_LE:
+ case op_STR_LE:
+ return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) <= 0);
+ case op_GT:
+ case op_STR_GT:
+ return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) > 0);
+ case op_GE:
+ case op_STR_GE:
+ return (strcmplex(ap_expr_eval_word(ctx, e1), ap_expr_eval_word(ctx, e2)) >= 0);
+ default:
+ return ap_expr_eval_comp(ctx, node);
+ }
+}
+
+AP_DECLARE_NONSTD(int) ap_expr_lookup_default(ap_expr_lookup_parms *parms)
+{
+ return ap_run_expr_lookup(parms);
+}
+
+AP_DECLARE(const char *) ap_expr_parse(apr_pool_t *pool, apr_pool_t *ptemp,
+ ap_expr_info_t *info, const char *expr,
+ ap_expr_lookup_fn_t *lookup_fn)
+{
+ ap_expr_parse_ctx_t ctx;
+ int rc;
+
+ ctx.pool = pool;
+ ctx.ptemp = ptemp;
+ ctx.inputbuf = expr;
+ ctx.inputlen = strlen(expr);
+ ctx.inputptr = ctx.inputbuf;
+ ctx.expr = NULL;
+ ctx.error = NULL; /* generic bison error message (XXX: usually not very useful, should be axed) */
+ ctx.error2 = NULL; /* additional error message */
+ ctx.flags = info->flags;
+ ctx.scan_del = '\0';
+ ctx.scan_buf[0] = '\0';
+ ctx.scan_ptr = ctx.scan_buf;
+ ctx.lookup_fn = lookup_fn ? lookup_fn : ap_expr_lookup_default;
+ ctx.at_start = 1;
+
+ ap_expr_yylex_init(&ctx.scanner);
+ ap_expr_yyset_extra(&ctx, ctx.scanner);
+ rc = ap_expr_yyparse(&ctx);
+ ap_expr_yylex_destroy(ctx.scanner);
+ if (ctx.error) {
+ if (ctx.error2)
+ return apr_psprintf(pool, "%s: %s", ctx.error, ctx.error2);
+ else
+ return ctx.error;
+ }
+ else if (ctx.error2) {
+ return ctx.error2;
+ }
+
+ if (rc) /* XXX can this happen? */
+ return "syntax error";
+
+#ifdef AP_EXPR_DEBUG
+ if (ctx.expr)
+ expr_dump_tree(ctx.expr, NULL, APLOG_NOTICE, 2);
+#endif
+
+ info->root_node = ctx.expr;
+
+ return NULL;
+}
+
+AP_DECLARE(ap_expr_info_t*) ap_expr_parse_cmd_mi(const cmd_parms *cmd,
+ const char *expr,
+ unsigned int flags,
+ const char **err,
+ ap_expr_lookup_fn_t *lookup_fn,
+ int module_index)
+{
+ ap_expr_info_t *info = apr_pcalloc(cmd->pool, sizeof(ap_expr_info_t));
+ info->filename = cmd->directive->filename;
+ info->line_number = cmd->directive->line_num;
+ info->flags = flags;
+ info->module_index = module_index;
+ *err = ap_expr_parse(cmd->pool, cmd->temp_pool, info, expr, lookup_fn);
+
+ if (*err)
+ return NULL;
+
+ return info;
+}
+
+ap_expr_t *ap_expr_make(ap_expr_node_op_e op, const void *a1, const void *a2,
+ ap_expr_parse_ctx_t *ctx)
+{
+ ap_expr_t *node = apr_palloc(ctx->pool, sizeof(ap_expr_t));
+ node->node_op = op;
+ node->node_arg1 = a1;
+ node->node_arg2 = a2;
+ return node;
+}
+
+static ap_expr_t *ap_expr_info_make(int type, const char *name,
+ ap_expr_parse_ctx_t *ctx,
+ const ap_expr_t *arg)
+{
+ ap_expr_t *info = apr_palloc(ctx->pool, sizeof(ap_expr_t));
+ ap_expr_lookup_parms parms;
+ parms.type = type;
+ parms.flags = ctx->flags;
+ parms.pool = ctx->pool;
+ parms.ptemp = ctx->ptemp;
+ parms.name = name;
+ parms.func = &info->node_arg1;
+ parms.data = &info->node_arg2;
+ parms.err = &ctx->error2;
+ parms.arg = (arg && arg->node_op == op_String) ? arg->node_arg1 : NULL;
+ if (ctx->lookup_fn(&parms) != OK)
+ return NULL;
+ return info;
+}
+
+ap_expr_t *ap_expr_str_func_make(const char *name, const ap_expr_t *arg,
+ ap_expr_parse_ctx_t *ctx)
+{
+ ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_STRING, name, ctx, arg);
+ if (!info)
+ return NULL;
+
+ info->node_op = op_StringFuncInfo;
+ return ap_expr_make(op_StringFuncCall, info, arg, ctx);
+}
+
+ap_expr_t *ap_expr_list_func_make(const char *name, const ap_expr_t *arg,
+ ap_expr_parse_ctx_t *ctx)
+{
+ ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_LIST, name, ctx, arg);
+ if (!info)
+ return NULL;
+
+ info->node_op = op_ListFuncInfo;
+ return ap_expr_make(op_ListFuncCall, info, arg, ctx);
+}
+
+ap_expr_t *ap_expr_unary_op_make(const char *name, const ap_expr_t *arg,
+ ap_expr_parse_ctx_t *ctx)
+{
+ ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_OP_UNARY, name, ctx, arg);
+ if (!info)
+ return NULL;
+
+ info->node_op = op_UnaryOpInfo;
+ return ap_expr_make(op_UnaryOpCall, info, arg, ctx);
+}
+
+ap_expr_t *ap_expr_binary_op_make(const char *name, const ap_expr_t *arg1,
+ const ap_expr_t *arg2, ap_expr_parse_ctx_t *ctx)
+{
+ ap_expr_t *args;
+ ap_expr_t *info = ap_expr_info_make(AP_EXPR_FUNC_OP_BINARY, name, ctx,
+ arg2);
+ if (!info)
+ return NULL;
+
+ info->node_op = op_BinaryOpInfo;
+ args = ap_expr_make(op_BinaryOpArgs, arg1, arg2, ctx);
+ return ap_expr_make(op_BinaryOpCall, info, args, ctx);
+}
+
+
+ap_expr_t *ap_expr_var_make(const char *name, ap_expr_parse_ctx_t *ctx)
+{
+ ap_expr_t *node = ap_expr_info_make(AP_EXPR_FUNC_VAR, name, ctx, NULL);
+ if (!node)
+ return NULL;
+
+ node->node_op = op_Var;
+ return node;
+}
+
+#ifdef AP_EXPR_DEBUG
+
+#define MARK APLOG_MARK,loglevel,0,s
+#define DUMP_E_E(op, e1, e2) \
+ do { ap_log_error(MARK,"%*s%s: %pp %pp", indent, " ", op, e1, e2); \
+ if (e1) expr_dump_tree(e1, s, loglevel, indent + 2); \
+ if (e2) expr_dump_tree(e2, s, loglevel, indent + 2); \
+ } while (0)
+#define DUMP_S_E(op, s1, e1) \
+ do { ap_log_error(MARK,"%*s%s: '%s' %pp", indent, " ", op, (char *)s1, e1); \
+ if (e1) expr_dump_tree(e1, s, loglevel, indent + 2); \
+ } while (0)
+#define DUMP_S_P(op, s1, p1) \
+ ap_log_error(MARK,"%*s%s: '%s' %pp", indent, " ", op, (char *)s1, p1);
+#define DUMP_P_P(op, p1, p2) \
+ ap_log_error(MARK,"%*s%s: %pp %pp", indent, " ", op, p1, p2);
+#define DUMP_S_S(op, s1, s2) \
+ ap_log_error(MARK,"%*s%s: '%s' '%s'", indent, " ", op, (char *)s1, (char *)s2)
+#define DUMP_P(op, p1) \
+ ap_log_error(MARK,"%*s%s: %pp", indent, " ", op, p1);
+#define DUMP_IP(op, p1) \
+ ap_log_error(MARK,"%*s%s: %d", indent, " ", op, *(int *)p1);
+#define DUMP_S(op, s1) \
+ ap_log_error(MARK,"%*s%s: '%s'", indent, " ", op, (char *)s1)
+
+#define CASE_OP(op) case op: name = #op ; break;
+
+static void expr_dump_tree(const ap_expr_t *e, const server_rec *s,
+ int loglevel, int indent)
+{
+ switch (e->node_op) {
+ /* no arg */
+ case op_NOP:
+ case op_True:
+ case op_False:
+ {
+ char *name;
+ switch (e->node_op) {
+ CASE_OP(op_NOP);
+ CASE_OP(op_True);
+ CASE_OP(op_False);
+ default:
+ ap_assert(0);
+ }
+ ap_log_error(MARK, "%*s%s", indent, " ", name);
+ }
+ break;
+
+ /* arg1: string, arg2: expr */
+ case op_UnaryOpCall:
+ case op_BinaryOpCall:
+ case op_BinaryOpArgs:
+ {
+ char *name;
+ switch (e->node_op) {
+ CASE_OP(op_BinaryOpCall);
+ CASE_OP(op_UnaryOpCall);
+ CASE_OP(op_BinaryOpArgs);
+ default:
+ ap_assert(0);
+ }
+ DUMP_S_E(name, e->node_arg1, e->node_arg2);
+ }
+ break;
+
+ /* arg1: expr, arg2: expr */
+ case op_Comp:
+ case op_Not:
+ case op_Or:
+ case op_And:
+ case op_EQ:
+ case op_NE:
+ case op_LT:
+ case op_LE:
+ case op_GT:
+ case op_GE:
+ case op_STR_EQ:
+ case op_STR_NE:
+ case op_STR_LT:
+ case op_STR_LE:
+ case op_STR_GT:
+ case op_STR_GE:
+ case op_IN:
+ case op_REG:
+ case op_NRE:
+ case op_Concat:
+ case op_StringFuncCall:
+ case op_ListFuncCall:
+ case op_ListElement:
+ {
+ char *name;
+ switch (e->node_op) {
+ CASE_OP(op_Comp);
+ CASE_OP(op_Not);
+ CASE_OP(op_Or);
+ CASE_OP(op_And);
+ CASE_OP(op_EQ);
+ CASE_OP(op_NE);
+ CASE_OP(op_LT);
+ CASE_OP(op_LE);
+ CASE_OP(op_GT);
+ CASE_OP(op_GE);
+ CASE_OP(op_STR_EQ);
+ CASE_OP(op_STR_NE);
+ CASE_OP(op_STR_LT);
+ CASE_OP(op_STR_LE);
+ CASE_OP(op_STR_GT);
+ CASE_OP(op_STR_GE);
+ CASE_OP(op_IN);
+ CASE_OP(op_REG);
+ CASE_OP(op_NRE);
+ CASE_OP(op_Concat);
+ CASE_OP(op_StringFuncCall);
+ CASE_OP(op_ListFuncCall);
+ CASE_OP(op_ListElement);
+ default:
+ ap_assert(0);
+ }
+ DUMP_E_E(name, e->node_arg1, e->node_arg2);
+ }
+ break;
+ /* arg1: string */
+ case op_Digit:
+ case op_String:
+ {
+ char *name;
+ switch (e->node_op) {
+ CASE_OP(op_Digit);
+ CASE_OP(op_String);
+ default:
+ ap_assert(0);
+ }
+ DUMP_S(name, e->node_arg1);
+ }
+ break;
+ /* arg1: pointer, arg2: pointer */
+ case op_Var:
+ case op_StringFuncInfo:
+ case op_UnaryOpInfo:
+ case op_BinaryOpInfo:
+ case op_ListFuncInfo:
+ {
+ char *name;
+ switch (e->node_op) {
+ CASE_OP(op_Var);
+ CASE_OP(op_StringFuncInfo);
+ CASE_OP(op_UnaryOpInfo);
+ CASE_OP(op_BinaryOpInfo);
+ CASE_OP(op_ListFuncInfo);
+ default:
+ ap_assert(0);
+ }
+ DUMP_P_P(name, e->node_arg1, e->node_arg2);
+ }
+ break;
+ /* arg1: pointer */
+ case op_Regex:
+ DUMP_P("op_Regex", e->node_arg1);
+ break;
+ /* arg1: pointer to int */
+ case op_RegexBackref:
+ DUMP_IP("op_RegexBackref", e->node_arg1);
+ break;
+ default:
+ ap_log_error(MARK, "%*sERROR: INVALID OP %d", indent, " ", e->node_op);
+ break;
+ }
+}
+#endif /* AP_EXPR_DEBUG */
+
+static int ap_expr_eval_unary_op(ap_expr_eval_ctx_t *ctx, const ap_expr_t *info,
+ const ap_expr_t *arg)
+{
+ ap_expr_op_unary_t *op_func = (ap_expr_op_unary_t *)info->node_arg1;
+ const void *data = info->node_arg2;
+
+ AP_DEBUG_ASSERT(info->node_op == op_UnaryOpInfo);
+ AP_DEBUG_ASSERT(op_func != NULL);
+ AP_DEBUG_ASSERT(data != NULL);
+ return (*op_func)(ctx, data, ap_expr_eval_word(ctx, arg));
+}
+
+static int ap_expr_eval_binary_op(ap_expr_eval_ctx_t *ctx,
+ const ap_expr_t *info,
+ const ap_expr_t *args)
+{
+ ap_expr_op_binary_t *op_func = (ap_expr_op_binary_t *)info->node_arg1;
+ const void *data = info->node_arg2;
+ const ap_expr_t *a1 = args->node_arg1;
+ const ap_expr_t *a2 = args->node_arg2;
+
+ AP_DEBUG_ASSERT(info->node_op == op_BinaryOpInfo);
+ AP_DEBUG_ASSERT(args->node_op == op_BinaryOpArgs);
+ AP_DEBUG_ASSERT(op_func != NULL);
+ AP_DEBUG_ASSERT(data != NULL);
+ return (*op_func)(ctx, data, ap_expr_eval_word(ctx, a1),
+ ap_expr_eval_word(ctx, a2));
+}
+
+
+static int ap_expr_eval(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
+{
+ const ap_expr_t *e1 = node->node_arg1;
+ const ap_expr_t *e2 = node->node_arg2;
+ int result = FALSE;
+ if (inc_rec(ctx))
+ return result;
+ while (1) {
+ switch (node->node_op) {
+ case op_True:
+ result ^= TRUE;
+ goto out;
+ case op_False:
+ result ^= FALSE;
+ goto out;
+ case op_Not:
+ result = !result;
+ node = e1;
+ break;
+ case op_Or:
+ do {
+ if (e1->node_op == op_Not) {
+ if (!ap_expr_eval(ctx, e1->node_arg1)) {
+ result ^= TRUE;
+ goto out;
+ }
+ }
+ else {
+ if (ap_expr_eval(ctx, e1)) {
+ result ^= TRUE;
+ goto out;
+ }
+ }
+ node = node->node_arg2;
+ e1 = node->node_arg1;
+ } while (node->node_op == op_Or);
+ break;
+ case op_And:
+ do {
+ if (e1->node_op == op_Not) {
+ if (ap_expr_eval(ctx, e1->node_arg1)) {
+ result ^= FALSE;
+ goto out;
+ }
+ }
+ else {
+ if (!ap_expr_eval(ctx, e1)) {
+ result ^= FALSE;
+ goto out;
+ }
+ }
+ node = node->node_arg2;
+ e1 = node->node_arg1;
+ } while (node->node_op == op_And);
+ break;
+ case op_UnaryOpCall:
+ result ^= ap_expr_eval_unary_op(ctx, e1, e2);
+ goto out;
+ case op_BinaryOpCall:
+ result ^= ap_expr_eval_binary_op(ctx, e1, e2);
+ goto out;
+ case op_Comp:
+ if (ctx->info->flags & AP_EXPR_FLAG_SSL_EXPR_COMPAT)
+ result ^= ssl_expr_eval_comp(ctx, e1);
+ else
+ result ^= ap_expr_eval_comp(ctx, e1);
+ goto out;
+ default:
+ *ctx->err = "Internal evaluation error: Unknown expression node";
+ goto out;
+ }
+ e1 = node->node_arg1;
+ e2 = node->node_arg2;
+ }
+out:
+ ctx->reclvl--;
+ return result;
+}
+
+AP_DECLARE(int) ap_expr_exec(request_rec *r, const ap_expr_info_t *info,
+ const char **err)
+{
+ return ap_expr_exec_re(r, info, 0, NULL, NULL, err);
+}
+
+AP_DECLARE(int) ap_expr_exec_ctx(ap_expr_eval_ctx_t *ctx)
+{
+ int rc;
+
+ AP_DEBUG_ASSERT(ctx->p != NULL);
+ /* XXX: allow r, c == NULL */
+ AP_DEBUG_ASSERT(ctx->r != NULL);
+ AP_DEBUG_ASSERT(ctx->c != NULL);
+ AP_DEBUG_ASSERT(ctx->s != NULL);
+ AP_DEBUG_ASSERT(ctx->err != NULL);
+ AP_DEBUG_ASSERT(ctx->info != NULL);
+ if (ctx->re_pmatch) {
+ AP_DEBUG_ASSERT(ctx->re_source != NULL);
+ AP_DEBUG_ASSERT(ctx->re_nmatch > 0);
+ }
+ ctx->reclvl = 0;
+
+ *ctx->err = NULL;
+ if (ctx->info->flags & AP_EXPR_FLAG_STRING_RESULT) {
+ *ctx->result_string = ap_expr_eval_word(ctx, ctx->info->root_node);
+ if (*ctx->err != NULL) {
+ ap_log_rerror(LOG_MARK(ctx->info), APLOG_ERR, 0, ctx->r,
+ "Evaluation of expression from %s:%d failed: %s",
+ ctx->info->filename, ctx->info->line_number, *ctx->err);
+ return -1;
+ } else {
+ ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE4, 0, ctx->r,
+ "Evaluation of string expression from %s:%d gave: %s",
+ ctx->info->filename, ctx->info->line_number,
+ *ctx->result_string);
+ return 1;
+ }
+ }
+ else {
+ rc = ap_expr_eval(ctx, ctx->info->root_node);
+ if (*ctx->err != NULL) {
+ ap_log_rerror(LOG_MARK(ctx->info), APLOG_ERR, 0, ctx->r,
+ "Evaluation of expression from %s:%d failed: %s",
+ ctx->info->filename, ctx->info->line_number, *ctx->err);
+ return -1;
+ } else {
+ rc = rc ? 1 : 0;
+ ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE4, 0, ctx->r,
+ "Evaluation of expression from %s:%d gave: %d",
+ ctx->info->filename, ctx->info->line_number, rc);
+
+ if (ctx->vary_this && *ctx->vary_this)
+ apr_table_merge(ctx->r->headers_out, "Vary", *ctx->vary_this);
+
+ return rc;
+ }
+ }
+}
+
+AP_DECLARE(int) ap_expr_exec_re(request_rec *r, const ap_expr_info_t *info,
+ apr_size_t nmatch, ap_regmatch_t *pmatch,
+ const char **source, const char **err)
+{
+ ap_expr_eval_ctx_t ctx;
+ int dont_vary = (info->flags & AP_EXPR_FLAG_DONT_VARY);
+ const char *tmp_source = NULL, *vary_this = NULL;
+ ap_regmatch_t tmp_pmatch[AP_MAX_REG_MATCH];
+
+ AP_DEBUG_ASSERT((info->flags & AP_EXPR_FLAG_STRING_RESULT) == 0);
+
+ ctx.r = r;
+ ctx.c = r->connection;
+ ctx.s = r->server;
+ ctx.p = r->pool;
+ ctx.err = err;
+ ctx.info = info;
+ ctx.re_nmatch = nmatch;
+ ctx.re_pmatch = pmatch;
+ ctx.re_source = source;
+ ctx.vary_this = dont_vary ? NULL : &vary_this;
+ ctx.data = NULL;
+
+ if (!pmatch) {
+ ctx.re_nmatch = AP_MAX_REG_MATCH;
+ ctx.re_pmatch = tmp_pmatch;
+ ctx.re_source = &tmp_source;
+ }
+
+ return ap_expr_exec_ctx(&ctx);
+}
+
+AP_DECLARE(const char *) ap_expr_str_exec_re(request_rec *r,
+ const ap_expr_info_t *info,
+ apr_size_t nmatch,
+ ap_regmatch_t *pmatch,
+ const char **source,
+ const char **err)
+{
+ ap_expr_eval_ctx_t ctx;
+ int dont_vary, rc;
+ const char *tmp_source = NULL, *vary_this = NULL;
+ ap_regmatch_t tmp_pmatch[AP_MAX_REG_MATCH];
+ const char *result;
+
+ AP_DEBUG_ASSERT(info->flags & AP_EXPR_FLAG_STRING_RESULT);
+
+ if (info->root_node->node_op == op_String) {
+ /* short-cut for constant strings */
+ *err = NULL;
+ return (const char *)info->root_node->node_arg1;
+ }
+
+ dont_vary = (info->flags & AP_EXPR_FLAG_DONT_VARY);
+
+ ctx.r = r;
+ ctx.c = r->connection;
+ ctx.s = r->server;
+ ctx.p = r->pool;
+ ctx.err = err;
+ ctx.info = info;
+ ctx.re_nmatch = nmatch;
+ ctx.re_pmatch = pmatch;
+ ctx.re_source = source;
+ ctx.vary_this = dont_vary ? NULL : &vary_this;
+ ctx.data = NULL;
+ ctx.result_string = &result;
+
+ if (!pmatch) {
+ ctx.re_nmatch = AP_MAX_REG_MATCH;
+ ctx.re_pmatch = tmp_pmatch;
+ ctx.re_source = &tmp_source;
+ }
+
+ rc = ap_expr_exec_ctx(&ctx);
+ if (rc > 0)
+ return result;
+ else if (rc < 0)
+ return NULL;
+ else
+ ap_assert(0);
+}
+
+AP_DECLARE(const char *) ap_expr_str_exec(request_rec *r,
+ const ap_expr_info_t *info,
+ const char **err)
+{
+ return ap_expr_str_exec_re(r, info, 0, NULL, NULL, err);
+}
+
+
+static void add_vary(ap_expr_eval_ctx_t *ctx, const char *name)
+{
+ if (!ctx->vary_this)
+ return;
+
+ if (*ctx->vary_this) {
+ *ctx->vary_this = apr_pstrcat(ctx->p, *ctx->vary_this, ", ", name,
+ NULL);
+ }
+ else {
+ *ctx->vary_this = name;
+ }
+}
+
+static const char *req_table_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg)
+{
+ const char *name = (const char *)data;
+ apr_table_t *t;
+ if (!ctx->r)
+ return "";
+
+ if (name[2] == 's') { /* resp */
+ /* Try r->headers_out first, fall back on err_headers_out. */
+ const char *v = apr_table_get(ctx->r->headers_out, arg);
+ if (v) {
+ return v;
+ }
+ t = ctx->r->err_headers_out;
+ }
+ else if (name[0] == 'n') /* notes */
+ t = ctx->r->notes;
+ else if (name[3] == 'e') /* reqenv */
+ t = ctx->r->subprocess_env;
+ else { /* req, http */
+ t = ctx->r->headers_in;
+ add_vary(ctx, arg);
+ }
+ return apr_table_get(t, arg);
+}
+
+static const char *env_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg)
+{
+ const char *res;
+ /* this order is for ssl_expr compatibility */
+ if (ctx->r) {
+ if ((res = apr_table_get(ctx->r->notes, arg)) != NULL)
+ return res;
+ else if ((res = apr_table_get(ctx->r->subprocess_env, arg)) != NULL)
+ return res;
+ }
+ return getenv(arg);
+}
+
+static const char *osenv_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg)
+{
+ return getenv(arg);
+}
+
+static const char *tolower_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg)
+{
+ char *result = apr_pstrdup(ctx->p, arg);
+ ap_str_tolower(result);
+ return result;
+}
+
+static const char *toupper_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg)
+{
+ char *result = apr_pstrdup(ctx->p, arg);
+ ap_str_toupper(result);
+ return result;
+}
+
+static const char *escape_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg)
+{
+ return ap_escape_uri(ctx->p, arg);
+}
+
+#define MAX_FILE_SIZE 10*1024*1024
+static const char *file_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ char *arg)
+{
+ apr_file_t *fp;
+ char *buf;
+ apr_off_t offset;
+ apr_size_t len;
+ apr_finfo_t finfo;
+
+ if (apr_file_open(&fp, arg, APR_READ|APR_BUFFERED,
+ APR_OS_DEFAULT, ctx->p) != APR_SUCCESS) {
+ *ctx->err = apr_psprintf(ctx->p, "Cannot open file %s", arg);
+ return "";
+ }
+ apr_file_info_get(&finfo, APR_FINFO_SIZE, fp);
+ if (finfo.size > MAX_FILE_SIZE) {
+ *ctx->err = apr_psprintf(ctx->p, "File %s too large", arg);
+ apr_file_close(fp);
+ return "";
+ }
+ len = (apr_size_t)finfo.size;
+ if (len == 0) {
+ apr_file_close(fp);
+ return "";
+ }
+ else {
+ if ((buf = (char *)apr_palloc(ctx->p, sizeof(char)*(len+1))) == NULL) {
+ *ctx->err = "Cannot allocate memory";
+ apr_file_close(fp);
+ return "";
+ }
+ offset = 0;
+ apr_file_seek(fp, APR_SET, &offset);
+ if (apr_file_read(fp, buf, &len) != APR_SUCCESS) {
+ *ctx->err = apr_psprintf(ctx->p, "Cannot read from file %s", arg);
+ apr_file_close(fp);
+ return "";
+ }
+ buf[len] = '\0';
+ }
+ apr_file_close(fp);
+ return buf;
+}
+
+static const char *filesize_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ char *arg)
+{
+ apr_finfo_t sb;
+ if (apr_stat(&sb, arg, APR_FINFO_MIN, ctx->p) == APR_SUCCESS
+ && sb.filetype == APR_REG && sb.size > 0)
+ return apr_psprintf(ctx->p, "%" APR_OFF_T_FMT, sb.size);
+ else
+ return "0";
+}
+
+static const char *unescape_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg)
+{
+ char *result = apr_pstrdup(ctx->p, arg);
+ int ret = ap_unescape_url_keep2f(result, 0);
+ if (ret == OK)
+ return result;
+ ap_log_rerror(LOG_MARK(ctx->info), APLOG_DEBUG, 0, ctx->r, APLOGNO(00538)
+ "%s %% escape in unescape('%s') at %s:%d",
+ ret == HTTP_BAD_REQUEST ? "Bad" : "Forbidden", arg,
+ ctx->info->filename, ctx->info->line_number);
+ return "";
+}
+
+static int op_nz(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
+{
+ const char *name = (const char *)data;
+ if (name[0] == 'z')
+ return (arg[0] == '\0');
+ else
+ return (arg[0] != '\0');
+}
+
+static int op_file_min(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
+{
+ apr_finfo_t sb;
+ const char *name = (const char *)data;
+ if (apr_stat(&sb, arg, APR_FINFO_MIN, ctx->p) != APR_SUCCESS)
+ return FALSE;
+ switch (name[0]) {
+ case 'd':
+ return (sb.filetype == APR_DIR);
+ case 'e':
+ return TRUE;
+ case 'f':
+ return (sb.filetype == APR_REG);
+ case 's':
+ return (sb.filetype == APR_REG && sb.size > 0);
+ default:
+ ap_assert(0);
+ }
+ return FALSE;
+}
+
+static int op_file_link(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
+{
+ apr_finfo_t sb;
+#if !defined(OS2)
+ if (apr_stat(&sb, arg, APR_FINFO_MIN | APR_FINFO_LINK, ctx->p) == APR_SUCCESS
+ && sb.filetype == APR_LNK) {
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+static int op_file_xbit(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
+{
+ apr_finfo_t sb;
+ if (apr_stat(&sb, arg, APR_FINFO_PROT| APR_FINFO_LINK, ctx->p) == APR_SUCCESS
+ && (sb.protection & (APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE))) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int op_url_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
+{
+ int rc = FALSE;
+ request_rec *rsub, *r = ctx->r;
+ if (!r)
+ return FALSE;
+ /* avoid some infinite recursions */
+ if (r->main && r->main->uri && r->uri && strcmp(r->main->uri, r->uri) == 0)
+ return FALSE;
+
+ rsub = ap_sub_req_lookup_uri(arg, r, NULL);
+ if (rsub->status < 400) {
+ rc = TRUE;
+ }
+ ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r,
+ "Subrequest for -U %s at %s:%d gave status: %d",
+ arg, ctx->info->filename, ctx->info->line_number,
+ rsub->status);
+ ap_destroy_sub_req(rsub);
+ return rc;
+}
+
+static int op_file_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
+{
+ int rc = FALSE;
+ apr_finfo_t sb;
+ request_rec *rsub, *r = ctx->r;
+ if (!r)
+ return FALSE;
+ rsub = ap_sub_req_lookup_file(arg, r, NULL);
+ if (rsub->status < 300 &&
+ /* double-check that file exists since default result is 200 */
+ apr_stat(&sb, rsub->filename, APR_FINFO_MIN, ctx->p) == APR_SUCCESS) {
+ rc = TRUE;
+ }
+ ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r,
+ "Subrequest for -F %s at %s:%d gave status: %d",
+ arg, ctx->info->filename, ctx->info->line_number,
+ rsub->status);
+ ap_destroy_sub_req(rsub);
+ return rc;
+}
+
+
+APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
+static APR_OPTIONAL_FN_TYPE(ssl_is_https) *is_https = NULL;
+
+static const char *conn_var_names[] = {
+ "HTTPS", /* 0 */
+ "IPV6", /* 1 */
+ "CONN_LOG_ID", /* 2 */
+ "CONN_REMOTE_ADDR", /* 3 */
+ NULL
+};
+
+static const char *conn_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
+{
+ int index = ((const char **)data - conn_var_names);
+ conn_rec *c = ctx->c;
+ if (!c)
+ return "";
+
+ switch (index) {
+ case 0:
+ if (is_https && is_https(c))
+ return "on";
+ else
+ return "off";
+ case 1:
+#if APR_HAVE_IPV6
+ {
+ apr_sockaddr_t *addr = c->client_addr;
+ if (addr->family == AF_INET6
+ && !IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr))
+ return "on";
+ else
+ return "off";
+ }
+#else
+ return "off";
+#endif
+ case 2:
+ return c->log_id;
+ case 3:
+ return c->client_ip;
+ default:
+ ap_assert(0);
+ return NULL;
+ }
+}
+
+static const char *request_var_names[] = {
+ "REQUEST_METHOD", /* 0 */
+ "REQUEST_SCHEME", /* 1 */
+ "REQUEST_URI", /* 2 */
+ "REQUEST_FILENAME", /* 3 */
+ "REMOTE_HOST", /* 4 */
+ "REMOTE_IDENT", /* 5 */
+ "REMOTE_USER", /* 6 */
+ "SERVER_ADMIN", /* 7 */
+ "SERVER_NAME", /* 8 */
+ "SERVER_PORT", /* 9 */
+ "SERVER_PROTOCOL", /* 10 */
+ "SCRIPT_FILENAME", /* 11 */
+ "PATH_INFO", /* 12 */
+ "QUERY_STRING", /* 13 */
+ "IS_SUBREQ", /* 14 */
+ "DOCUMENT_ROOT", /* 15 */
+ "AUTH_TYPE", /* 16 */
+ "THE_REQUEST", /* 17 */
+ "CONTENT_TYPE", /* 18 */
+ "HANDLER", /* 19 */
+ "REQUEST_LOG_ID", /* 20 */
+ "SCRIPT_USER", /* 21 */
+ "SCRIPT_GROUP", /* 22 */
+ "DOCUMENT_URI", /* 23 */
+ "LAST_MODIFIED", /* 24 */
+ "CONTEXT_PREFIX", /* 25 */
+ "CONTEXT_DOCUMENT_ROOT", /* 26 */
+ "REQUEST_STATUS", /* 27 */
+ "REMOTE_ADDR", /* 28 */
+ NULL
+};
+
+static const char *request_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
+{
+ int index = ((const char **)data - request_var_names);
+ request_rec *r = ctx->r;
+ if (!r)
+ return "";
+
+ switch (index) {
+ case 0:
+ return r->method;
+ case 1:
+ return ap_http_scheme(r);
+ case 2:
+ return r->uri;
+ case 3:
+ return r->filename;
+ case 4:
+ return ap_get_remote_host(r->connection, r->per_dir_config,
+ REMOTE_NAME, NULL);
+ case 5:
+ return ap_get_remote_logname(r);
+ case 6:
+ return r->user;
+ case 7:
+ return r->server->server_admin;
+ case 8:
+ return ap_get_server_name_for_url(r);
+ case 9:
+ return apr_psprintf(ctx->p, "%u", ap_get_server_port(r));
+ case 10:
+ return r->protocol;
+ case 11:
+ return r->filename;
+ case 12:
+ return r->path_info;
+ case 13:
+ return r->args;
+ case 14:
+ return (r->main != NULL ? "true" : "false");
+ case 15:
+ return ap_document_root(r);
+ case 16:
+ return r->ap_auth_type;
+ case 17:
+ return r->the_request;
+ case 18:
+ return r->content_type;
+ case 19:
+ return r->handler;
+ case 20:
+ return r->log_id;
+ case 21:
+ {
+ char *result = "";
+ if (r->finfo.valid & APR_FINFO_USER)
+ apr_uid_name_get(&result, r->finfo.user, ctx->p);
+ return result;
+ }
+ case 22:
+ {
+ char *result = "";
+ if (r->finfo.valid & APR_FINFO_USER)
+ apr_gid_name_get(&result, r->finfo.group, ctx->p);
+ return result;
+ }
+ case 23:
+ return r->uri;
+ case 24:
+ {
+ apr_time_exp_t tm;
+ apr_time_exp_lt(&tm, r->mtime);
+ return apr_psprintf(ctx->p, "%02d%02d%02d%02d%02d%02d%02d",
+ (tm.tm_year / 100) + 19, (tm.tm_year % 100),
+ tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min,
+ tm.tm_sec);
+ }
+ case 25:
+ return ap_context_prefix(r);
+ case 26:
+ return ap_context_document_root(r);
+ case 27:
+ return r->status ? apr_psprintf(ctx->p, "%d", r->status) : "";
+ case 28:
+ return r->useragent_ip;
+ default:
+ ap_assert(0);
+ return NULL;
+ }
+}
+
+static const char *req_header_var_names[] = {
+ "HTTP_USER_AGENT",
+ "HTTP_PROXY_CONNECTION",
+ "HTTP_REFERER",
+ "HTTP_COOKIE",
+ "HTTP_FORWARDED",
+ "HTTP_HOST",
+ "HTTP_ACCEPT",
+ NULL
+};
+
+static const char *req_header_header_names[] = {
+ "User-Agent",
+ "Proxy-Connection",
+ "Referer",
+ "Cookie",
+ "Forwarded",
+ "Host",
+ "Accept"
+};
+
+static const char *req_header_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
+{
+ const char **varname = (const char **)data;
+ int index = (varname - req_header_var_names);
+ const char *name;
+
+ AP_DEBUG_ASSERT(index < 6);
+ if (!ctx->r)
+ return "";
+
+ name = req_header_header_names[index];
+ add_vary(ctx, name);
+ return apr_table_get(ctx->r->headers_in, name);
+}
+
+static const char *misc_var_names[] = {
+ "TIME_YEAR", /* 0 */
+ "TIME_MON", /* 1 */
+ "TIME_DAY", /* 2 */
+ "TIME_HOUR", /* 3 */
+ "TIME_MIN", /* 4 */
+ "TIME_SEC", /* 5 */
+ "TIME_WDAY", /* 6 */
+ "TIME", /* 7 */
+ "SERVER_SOFTWARE", /* 8 */
+ "API_VERSION", /* 9 */
+ NULL
+};
+
+static const char *misc_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
+{
+ apr_time_exp_t tm;
+ int index = ((const char **)data - misc_var_names);
+ apr_time_exp_lt(&tm, apr_time_now());
+
+ switch (index) {
+ case 0:
+ return apr_psprintf(ctx->p, "%02d%02d", (tm.tm_year / 100) + 19,
+ tm.tm_year % 100);
+ case 1:
+ return apr_psprintf(ctx->p, "%02d", tm.tm_mon+1);
+ case 2:
+ return apr_psprintf(ctx->p, "%02d", tm.tm_mday);
+ case 3:
+ return apr_psprintf(ctx->p, "%02d", tm.tm_hour);
+ case 4:
+ return apr_psprintf(ctx->p, "%02d", tm.tm_min);
+ case 5:
+ return apr_psprintf(ctx->p, "%02d", tm.tm_sec);
+ case 6:
+ return apr_psprintf(ctx->p, "%d", tm.tm_wday);
+ case 7:
+ return apr_psprintf(ctx->p, "%02d%02d%02d%02d%02d%02d%02d",
+ (tm.tm_year / 100) + 19, (tm.tm_year % 100),
+ tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min,
+ tm.tm_sec);
+ case 8:
+ return ap_get_server_banner();
+ case 9:
+ return apr_itoa(ctx->p, MODULE_MAGIC_NUMBER);
+ default:
+ ap_assert(0);
+ }
+
+ return NULL;
+}
+
+static int subnet_parse_arg(ap_expr_lookup_parms *parms)
+{
+ apr_ipsubnet_t *subnet;
+ const char *addr = parms->arg;
+ const char *mask;
+ apr_status_t ret;
+
+ if (!parms->arg) {
+ *parms->err = apr_psprintf(parms->ptemp,
+ "-%s requires subnet/netmask as constant argument",
+ parms->name);
+ return !OK;
+ }
+
+ mask = ap_strchr_c(addr, '/');
+ if (mask) {
+ addr = apr_pstrmemdup(parms->ptemp, addr, mask - addr);
+ mask++;
+ }
+
+ ret = apr_ipsubnet_create(&subnet, addr, mask, parms->pool);
+ if (ret != APR_SUCCESS) {
+ *parms->err = "parsing of subnet/netmask failed";
+ return !OK;
+ }
+
+ *parms->data = subnet;
+ return OK;
+}
+
+static int op_ipmatch(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg1,
+ const char *arg2)
+{
+ apr_ipsubnet_t *subnet = (apr_ipsubnet_t *)data;
+ apr_sockaddr_t *saddr;
+
+ AP_DEBUG_ASSERT(subnet != NULL);
+
+ /* maybe log an error if this goes wrong? */
+ if (apr_sockaddr_info_get(&saddr, arg1, APR_UNSPEC, 0, 0, ctx->p) != APR_SUCCESS)
+ return FALSE;
+
+ return apr_ipsubnet_test(subnet, saddr);
+}
+
+static int op_R(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg1)
+{
+ apr_ipsubnet_t *subnet = (apr_ipsubnet_t *)data;
+
+ AP_DEBUG_ASSERT(subnet != NULL);
+
+ if (!ctx->r)
+ return FALSE;
+
+ return apr_ipsubnet_test(subnet, ctx->r->useragent_addr);
+}
+
+static int op_T(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
+{
+ switch (arg[0]) {
+ case '\0':
+ return FALSE;
+ case 'o':
+ case 'O':
+ return strcasecmp(arg, "off") == 0 ? FALSE : TRUE;
+ case 'n':
+ case 'N':
+ return strcasecmp(arg, "no") == 0 ? FALSE : TRUE;
+ case 'f':
+ case 'F':
+ return strcasecmp(arg, "false") == 0 ? FALSE : TRUE;
+ case '0':
+ return arg[1] == '\0' ? FALSE : TRUE;
+ default:
+ return TRUE;
+ }
+}
+
+static int op_fnmatch(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg1, const char *arg2)
+{
+ return (APR_SUCCESS == apr_fnmatch(arg2, arg1, APR_FNM_PATHNAME));
+}
+
+static int op_strmatch(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg1, const char *arg2)
+{
+ return (APR_SUCCESS == apr_fnmatch(arg2, arg1, 0));
+}
+
+static int op_strcmatch(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg1, const char *arg2)
+{
+ return (APR_SUCCESS == apr_fnmatch(arg2, arg1, APR_FNM_CASE_BLIND));
+}
+
+struct expr_provider_single {
+ const void *func;
+ const char *name;
+ ap_expr_lookup_fn_t *arg_parsing_func;
+ int restricted;
+};
+
+struct expr_provider_multi {
+ const void *func;
+ const char **names;
+};
+
+static const struct expr_provider_multi var_providers[] = {
+ { misc_var_fn, misc_var_names },
+ { req_header_var_fn, req_header_var_names },
+ { request_var_fn, request_var_names },
+ { conn_var_fn, conn_var_names },
+ { NULL, NULL }
+};
+
+static const struct expr_provider_single string_func_providers[] = {
+ { osenv_func, "osenv", NULL, 0 },
+ { env_func, "env", NULL, 0 },
+ { req_table_func, "resp", NULL, 0 },
+ { req_table_func, "req", NULL, 0 },
+ /* 'http' as alias for 'req' for compatibility with ssl_expr */
+ { req_table_func, "http", NULL, 0 },
+ { req_table_func, "note", NULL, 0 },
+ { req_table_func, "reqenv", NULL, 0 },
+ { tolower_func, "tolower", NULL, 0 },
+ { toupper_func, "toupper", NULL, 0 },
+ { escape_func, "escape", NULL, 0 },
+ { unescape_func, "unescape", NULL, 0 },
+ { file_func, "file", NULL, 1 },
+ { filesize_func, "filesize", NULL, 1 },
+ { NULL, NULL, NULL}
+};
+/* XXX: base64 encode/decode ? */
+
+static const struct expr_provider_single unary_op_providers[] = {
+ { op_nz, "n", NULL, 0 },
+ { op_nz, "z", NULL, 0 },
+ { op_R, "R", subnet_parse_arg, 0 },
+ { op_T, "T", NULL, 0 },
+ { op_file_min, "d", NULL, 1 },
+ { op_file_min, "e", NULL, 1 },
+ { op_file_min, "f", NULL, 1 },
+ { op_file_min, "s", NULL, 1 },
+ { op_file_link, "L", NULL, 1 },
+ { op_file_link, "h", NULL, 1 },
+ { op_file_xbit, "x", NULL, 1 },
+ { op_file_subr, "F", NULL, 0 },
+ { op_url_subr, "U", NULL, 0 },
+ { op_url_subr, "A", NULL, 0 },
+ { NULL, NULL, NULL }
+};
+
+static const struct expr_provider_single binary_op_providers[] = {
+ { op_ipmatch, "ipmatch", subnet_parse_arg, 0 },
+ { op_fnmatch, "fnmatch", NULL, 0 },
+ { op_strmatch, "strmatch", NULL, 0 },
+ { op_strcmatch, "strcmatch", NULL, 0 },
+ { NULL, NULL, NULL }
+};
+
+static int core_expr_lookup(ap_expr_lookup_parms *parms)
+{
+ switch (parms->type) {
+ case AP_EXPR_FUNC_VAR: {
+ const struct expr_provider_multi *prov = var_providers;
+ while (prov->func) {
+ const char **name = prov->names;
+ while (*name) {
+ if (strcasecmp(*name, parms->name) == 0) {
+ *parms->func = prov->func;
+ *parms->data = name;
+ return OK;
+ }
+ name++;
+ }
+ prov++;
+ }
+ }
+ break;
+ case AP_EXPR_FUNC_STRING:
+ case AP_EXPR_FUNC_OP_UNARY:
+ case AP_EXPR_FUNC_OP_BINARY: {
+ const struct expr_provider_single *prov;
+ switch (parms->type) {
+ case AP_EXPR_FUNC_STRING:
+ prov = string_func_providers;
+ break;
+ case AP_EXPR_FUNC_OP_UNARY:
+ prov = unary_op_providers;
+ break;
+ case AP_EXPR_FUNC_OP_BINARY:
+ prov = binary_op_providers;
+ break;
+ default:
+ ap_assert(0);
+ }
+ while (prov->func) {
+ int match;
+ if (parms->type == AP_EXPR_FUNC_OP_UNARY)
+ match = !strcmp(prov->name, parms->name);
+ else
+ match = !strcasecmp(prov->name, parms->name);
+ if (match) {
+ if ((parms->flags & AP_EXPR_FLAG_RESTRICTED)
+ && prov->restricted) {
+ *parms->err =
+ apr_psprintf(parms->ptemp,
+ "%s%s not available in restricted context",
+ (parms->type == AP_EXPR_FUNC_STRING) ? "" : "-",
+ prov->name);
+ return !OK;
+ }
+ *parms->func = prov->func;
+ if (prov->arg_parsing_func) {
+ return prov->arg_parsing_func(parms);
+ }
+ else {
+ *parms->data = prov->name;
+ return OK;
+ }
+ }
+ prov++;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return DECLINED;
+}
+
+static int expr_lookup_not_found(ap_expr_lookup_parms *parms)
+{
+ const char *type;
+ const char *prefix = "";
+
+ switch (parms->type) {
+ case AP_EXPR_FUNC_VAR:
+ type = "Variable";
+ break;
+ case AP_EXPR_FUNC_STRING:
+ type = "Function";
+ break;
+ case AP_EXPR_FUNC_LIST:
+ type = "List-returning function";
+ break;
+ case AP_EXPR_FUNC_OP_UNARY:
+ type = "Unary operator";
+ break;
+ case AP_EXPR_FUNC_OP_BINARY:
+ type = "Binary operator";
+ break;
+ default:
+ *parms->err = "Inavalid expression type in expr_lookup";
+ return !OK;
+ }
+ if ( parms->type == AP_EXPR_FUNC_OP_UNARY
+ || parms->type == AP_EXPR_FUNC_OP_BINARY) {
+ prefix = "-";
+ }
+ *parms->err = apr_psprintf(parms->ptemp, "%s '%s%s' does not exist", type,
+ prefix, parms->name);
+ return !OK;
+}
+
+static int ap_expr_post_config(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
+ apr_pool_cleanup_register(pconf, &is_https, ap_pool_cleanup_set_null,
+ apr_pool_cleanup_null);
+ return OK;
+}
+
+void ap_expr_init(apr_pool_t *p)
+{
+ ap_hook_expr_lookup(core_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_expr_lookup(expr_lookup_not_found, NULL, NULL, APR_HOOK_REALLY_LAST);
+ ap_hook_post_config(ap_expr_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
diff --git a/server/util_expr_parse.c b/server/util_expr_parse.c
new file mode 100644
index 00000000..bcf0173b
--- /dev/null
+++ b/server/util_expr_parse.c
@@ -0,0 +1,2130 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.5"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse ap_expr_yyparse
+#define yylex ap_expr_yylex
+#define yyerror ap_expr_yyerror
+#define yylval ap_expr_yylval
+#define yychar ap_expr_yychar
+#define yydebug ap_expr_yydebug
+#define yynerrs ap_expr_yynerrs
+
+
+/* Copy the first part of user declarations. */
+
+/* Line 268 of yacc.c */
+#line 31 "util_expr_parse.y"
+
+#include "util_expr_private.h"
+
+
+/* Line 268 of yacc.c */
+#line 84 "util_expr_parse.c"
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_TRUE = 258,
+ T_FALSE = 259,
+ T_EXPR_BOOL = 260,
+ T_EXPR_STRING = 261,
+ T_ERROR = 262,
+ T_DIGIT = 263,
+ T_ID = 264,
+ T_STRING = 265,
+ T_REGEX = 266,
+ T_REGEX_I = 267,
+ T_REGEX_BACKREF = 268,
+ T_OP_UNARY = 269,
+ T_OP_BINARY = 270,
+ T_STR_BEGIN = 271,
+ T_STR_END = 272,
+ T_VAR_BEGIN = 273,
+ T_VAR_END = 274,
+ T_OP_EQ = 275,
+ T_OP_NE = 276,
+ T_OP_LT = 277,
+ T_OP_LE = 278,
+ T_OP_GT = 279,
+ T_OP_GE = 280,
+ T_OP_REG = 281,
+ T_OP_NRE = 282,
+ T_OP_IN = 283,
+ T_OP_STR_EQ = 284,
+ T_OP_STR_NE = 285,
+ T_OP_STR_LT = 286,
+ T_OP_STR_LE = 287,
+ T_OP_STR_GT = 288,
+ T_OP_STR_GE = 289,
+ T_OP_CONCAT = 290,
+ T_OP_OR = 291,
+ T_OP_AND = 292,
+ T_OP_NOT = 293
+ };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 293 of yacc.c */
+#line 35 "util_expr_parse.y"
+
+ char *cpVal;
+ ap_expr_t *exVal;
+ int num;
+
+
+
+/* Line 293 of yacc.c */
+#line 166 "util_expr_parse.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+
+/* Line 343 of yacc.c */
+#line 102 "util_expr_parse.y"
+
+#include "util_expr_private.h"
+#define yyscanner ctx->scanner
+
+int ap_expr_yylex(YYSTYPE *lvalp, void *scanner);
+
+
+/* Line 343 of yacc.c */
+#line 186 "util_expr_parse.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 28
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 128
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 45
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 14
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 53
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 96
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 293
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 39, 40, 2, 2, 43, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 44, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 41, 2, 42, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 6, 9, 11, 13, 15, 18, 22,
+ 26, 28, 31, 35, 39, 41, 45, 49, 53, 57,
+ 61, 65, 69, 73, 77, 81, 85, 89, 93, 97,
+ 101, 103, 107, 109, 113, 116, 118, 120, 122, 124,
+ 126, 130, 136, 138, 142, 144, 146, 148, 152, 155,
+ 157, 159, 161, 166
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 46, 0, -1, 5, 47, -1, 6, 51, -1, 7,
+ -1, 3, -1, 4, -1, 38, 47, -1, 47, 36,
+ 47, -1, 47, 37, 47, -1, 48, -1, 14, 54,
+ -1, 54, 15, 54, -1, 39, 47, 40, -1, 7,
+ -1, 54, 20, 54, -1, 54, 21, 54, -1, 54,
+ 22, 54, -1, 54, 23, 54, -1, 54, 24, 54,
+ -1, 54, 25, 54, -1, 54, 29, 54, -1, 54,
+ 30, 54, -1, 54, 31, 54, -1, 54, 32, 54,
+ -1, 54, 33, 54, -1, 54, 34, 54, -1, 54,
+ 28, 49, -1, 54, 26, 55, -1, 54, 27, 55,
+ -1, 57, -1, 41, 50, 42, -1, 54, -1, 50,
+ 43, 54, -1, 51, 52, -1, 52, -1, 7, -1,
+ 10, -1, 53, -1, 56, -1, 18, 9, 19, -1,
+ 18, 9, 44, 51, 19, -1, 8, -1, 54, 35,
+ 54, -1, 53, -1, 56, -1, 58, -1, 16, 51,
+ 17, -1, 16, 17, -1, 11, -1, 12, -1, 13,
+ -1, 9, 39, 54, 40, -1, 9, 39, 54, 40,
+ -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 112, 112, 113, 114, 117, 118, 119, 120, 121,
+ 122, 123, 124, 125, 126, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+ 146, 147, 150, 151, 154, 155, 156, 159, 160, 161,
+ 164, 165, 168, 169, 170, 171, 172, 173, 174, 177,
+ 186, 197, 204, 207
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "T_TRUE", "T_FALSE", "T_EXPR_BOOL",
+ "T_EXPR_STRING", "T_ERROR", "T_DIGIT", "T_ID", "T_STRING", "T_REGEX",
+ "T_REGEX_I", "T_REGEX_BACKREF", "T_OP_UNARY", "T_OP_BINARY",
+ "T_STR_BEGIN", "T_STR_END", "T_VAR_BEGIN", "T_VAR_END", "T_OP_EQ",
+ "T_OP_NE", "T_OP_LT", "T_OP_LE", "T_OP_GT", "T_OP_GE", "T_OP_REG",
+ "T_OP_NRE", "T_OP_IN", "T_OP_STR_EQ", "T_OP_STR_NE", "T_OP_STR_LT",
+ "T_OP_STR_LE", "T_OP_STR_GT", "T_OP_STR_GE", "T_OP_CONCAT", "T_OP_OR",
+ "T_OP_AND", "T_OP_NOT", "'('", "')'", "'{'", "'}'", "','", "':'",
+ "$accept", "root", "expr", "comparison", "wordlist", "words", "string",
+ "strpart", "var", "word", "regex", "backref", "lstfunccall",
+ "strfunccall", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 40,
+ 41, 123, 125, 44, 58
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 45, 46, 46, 46, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 49, 49, 50, 50, 51, 51, 51, 52, 52, 52,
+ 53, 53, 54, 54, 54, 54, 54, 54, 54, 55,
+ 55, 56, 57, 58
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 2, 2, 1, 1, 1, 2, 3, 3,
+ 1, 2, 3, 3, 1, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 1, 3, 1, 3, 2, 1, 1, 1, 1, 1,
+ 3, 5, 1, 3, 1, 1, 1, 3, 2, 1,
+ 1, 1, 4, 4
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 0, 0, 4, 0, 5, 6, 14, 42, 0,
+ 51, 0, 0, 0, 0, 0, 2, 10, 44, 0,
+ 45, 46, 36, 37, 3, 35, 38, 39, 1, 0,
+ 11, 48, 0, 0, 7, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 34, 0, 47, 40, 0,
+ 13, 8, 9, 12, 15, 16, 17, 18, 19, 20,
+ 49, 50, 28, 29, 0, 0, 27, 30, 21, 22,
+ 23, 24, 25, 26, 43, 53, 0, 0, 0, 32,
+ 41, 0, 31, 0, 52, 33
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 4, 16, 17, 76, 88, 24, 25, 18, 19,
+ 72, 20, 77, 21
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -35
+static const yytype_int8 yypact[] =
+{
+ 48, 60, 73, -35, 7, -35, -35, -35, -35, -34,
+ -35, 43, 8, 11, 60, 60, 86, -35, -35, 80,
+ -35, -35, -35, -35, 108, -35, -35, -35, -35, 43,
+ 25, -35, 79, -17, -35, -8, 60, 60, 43, 43,
+ 43, 43, 43, 43, 43, 5, 5, 0, 43, 43,
+ 43, 43, 43, 43, 43, -35, -27, -35, -35, 73,
+ -35, 86, 3, 25, 25, 25, 25, 25, 25, 25,
+ -35, -35, -35, -35, 23, 43, -35, -35, 25, 25,
+ 25, 25, 25, 25, 25, -35, 106, 43, 85, 25,
+ -35, -21, -35, 43, -35, 25
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -35, -35, 57, -35, -35, -35, -9, -20, -2, -5,
+ -4, -1, -35, -35
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 26, 27, 58, 32, 55, 29, 30, 28, 54, 74,
+ 26, 27, 55, 85, 54, 22, 70, 71, 23, 94,
+ 33, 10, 26, 27, 56, 31, 13, 59, 36, 37,
+ 26, 27, 60, 63, 64, 65, 66, 67, 68, 69,
+ 37, 75, 73, 78, 79, 80, 81, 82, 83, 84,
+ 86, 8, 9, 1, 2, 3, 10, 26, 27, 12,
+ 54, 13, 87, 5, 6, 0, 55, 7, 8, 9,
+ 89, 34, 35, 10, 11, 0, 12, 0, 13, 0,
+ 22, 0, 91, 23, 26, 27, 10, 0, 95, 23,
+ 0, 13, 10, 61, 62, 38, 57, 13, 14, 15,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 23, 0, 23, 10,
+ 0, 10, 36, 37, 13, 90, 13, 92, 93
+};
+
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-35))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+ 2, 2, 19, 12, 24, 39, 11, 0, 35, 9,
+ 12, 12, 32, 40, 35, 7, 11, 12, 10, 40,
+ 9, 13, 24, 24, 29, 17, 18, 44, 36, 37,
+ 32, 32, 40, 38, 39, 40, 41, 42, 43, 44,
+ 37, 41, 46, 48, 49, 50, 51, 52, 53, 54,
+ 59, 8, 9, 5, 6, 7, 13, 59, 59, 16,
+ 35, 18, 39, 3, 4, -1, 86, 7, 8, 9,
+ 75, 14, 15, 13, 14, -1, 16, -1, 18, -1,
+ 7, -1, 87, 10, 86, 86, 13, -1, 93, 10,
+ -1, 18, 13, 36, 37, 15, 17, 18, 38, 39,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 10, -1, 10, 13,
+ -1, 13, 36, 37, 18, 19, 18, 42, 43
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 5, 6, 7, 46, 3, 4, 7, 8, 9,
+ 13, 14, 16, 18, 38, 39, 47, 48, 53, 54,
+ 56, 58, 7, 10, 51, 52, 53, 56, 0, 39,
+ 54, 17, 51, 9, 47, 47, 36, 37, 15, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 52, 54, 17, 19, 44,
+ 40, 47, 47, 54, 54, 54, 54, 54, 54, 54,
+ 11, 12, 55, 55, 9, 41, 49, 57, 54, 54,
+ 54, 54, 54, 54, 54, 40, 51, 39, 50, 54,
+ 19, 54, 42, 43, 40, 54
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
+
+#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (ctx, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, yyscanner)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, ctx); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, ap_expr_parse_ctx_t *ctx)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, ctx)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ ap_expr_parse_ctx_t *ctx;
+#endif
+{
+ if (!yyvaluep)
+ return;
+ YYUSE (ctx);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, ap_expr_parse_ctx_t *ctx)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, ctx)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ ap_expr_parse_ctx_t *ctx;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, ctx);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule, ap_expr_parse_ctx_t *ctx)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule, ctx)
+ YYSTYPE *yyvsp;
+ int yyrule;
+ ap_expr_parse_ctx_t *ctx;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , ctx);
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule, ctx); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = 0;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, ap_expr_parse_ctx_t *ctx)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, ctx)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ ap_expr_parse_ctx_t *ctx;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (ctx);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (ap_expr_parse_ctx_t *ctx);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (ap_expr_parse_ctx_t *ctx)
+#else
+int
+yyparse (ctx)
+ ap_expr_parse_ctx_t *ctx;
+#endif
+#endif
+{
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+ /* Number of syntax errors so far. */
+ int yynerrs;
+
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+
+/* Line 1806 of yacc.c */
+#line 112 "util_expr_parse.y"
+ { ctx->expr = (yyvsp[(2) - (2)].exVal); }
+ break;
+
+ case 3:
+
+/* Line 1806 of yacc.c */
+#line 113 "util_expr_parse.y"
+ { ctx->expr = (yyvsp[(2) - (2)].exVal); }
+ break;
+
+ case 4:
+
+/* Line 1806 of yacc.c */
+#line 114 "util_expr_parse.y"
+ { YYABORT; }
+ break;
+
+ case 5:
+
+/* Line 1806 of yacc.c */
+#line 117 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_True, NULL, NULL, ctx); }
+ break;
+
+ case 6:
+
+/* Line 1806 of yacc.c */
+#line 118 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_False, NULL, NULL, ctx); }
+ break;
+
+ case 7:
+
+/* Line 1806 of yacc.c */
+#line 119 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_Not, (yyvsp[(2) - (2)].exVal), NULL, ctx); }
+ break;
+
+ case 8:
+
+/* Line 1806 of yacc.c */
+#line 120 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_Or, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 9:
+
+/* Line 1806 of yacc.c */
+#line 121 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_And, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 10:
+
+/* Line 1806 of yacc.c */
+#line 122 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_Comp, (yyvsp[(1) - (1)].exVal), NULL, ctx); }
+ break;
+
+ case 11:
+
+/* Line 1806 of yacc.c */
+#line 123 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_unary_op_make( (yyvsp[(1) - (2)].cpVal), (yyvsp[(2) - (2)].exVal), ctx); }
+ break;
+
+ case 12:
+
+/* Line 1806 of yacc.c */
+#line 124 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_binary_op_make((yyvsp[(2) - (3)].cpVal), (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 13:
+
+/* Line 1806 of yacc.c */
+#line 125 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
+ break;
+
+ case 14:
+
+/* Line 1806 of yacc.c */
+#line 126 "util_expr_parse.y"
+ { YYABORT; }
+ break;
+
+ case 15:
+
+/* Line 1806 of yacc.c */
+#line 129 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_EQ, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 16:
+
+/* Line 1806 of yacc.c */
+#line 130 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_NE, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 17:
+
+/* Line 1806 of yacc.c */
+#line 131 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_LT, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 18:
+
+/* Line 1806 of yacc.c */
+#line 132 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_LE, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 19:
+
+/* Line 1806 of yacc.c */
+#line 133 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_GT, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 20:
+
+/* Line 1806 of yacc.c */
+#line 134 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_GE, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 21:
+
+/* Line 1806 of yacc.c */
+#line 135 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_STR_EQ, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 22:
+
+/* Line 1806 of yacc.c */
+#line 136 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_STR_NE, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 23:
+
+/* Line 1806 of yacc.c */
+#line 137 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_STR_LT, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 24:
+
+/* Line 1806 of yacc.c */
+#line 138 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_STR_LE, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 25:
+
+/* Line 1806 of yacc.c */
+#line 139 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_STR_GT, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 26:
+
+/* Line 1806 of yacc.c */
+#line 140 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_STR_GE, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 27:
+
+/* Line 1806 of yacc.c */
+#line 141 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_IN, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 28:
+
+/* Line 1806 of yacc.c */
+#line 142 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_REG, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 29:
+
+/* Line 1806 of yacc.c */
+#line 143 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_NRE, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 30:
+
+/* Line 1806 of yacc.c */
+#line 146 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+ break;
+
+ case 31:
+
+/* Line 1806 of yacc.c */
+#line 147 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
+ break;
+
+ case 32:
+
+/* Line 1806 of yacc.c */
+#line 150 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_ListElement, (yyvsp[(1) - (1)].exVal), NULL, ctx); }
+ break;
+
+ case 33:
+
+/* Line 1806 of yacc.c */
+#line 151 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_ListElement, (yyvsp[(3) - (3)].exVal), (yyvsp[(1) - (3)].exVal), ctx); }
+ break;
+
+ case 34:
+
+/* Line 1806 of yacc.c */
+#line 154 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_Concat, (yyvsp[(1) - (2)].exVal), (yyvsp[(2) - (2)].exVal), ctx); }
+ break;
+
+ case 35:
+
+/* Line 1806 of yacc.c */
+#line 155 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+ break;
+
+ case 36:
+
+/* Line 1806 of yacc.c */
+#line 156 "util_expr_parse.y"
+ { YYABORT; }
+ break;
+
+ case 37:
+
+/* Line 1806 of yacc.c */
+#line 159 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_String, (yyvsp[(1) - (1)].cpVal), NULL, ctx); }
+ break;
+
+ case 38:
+
+/* Line 1806 of yacc.c */
+#line 160 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+ break;
+
+ case 39:
+
+/* Line 1806 of yacc.c */
+#line 161 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+ break;
+
+ case 40:
+
+/* Line 1806 of yacc.c */
+#line 164 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_var_make((yyvsp[(2) - (3)].cpVal), ctx); }
+ break;
+
+ case 41:
+
+/* Line 1806 of yacc.c */
+#line 165 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_str_func_make((yyvsp[(2) - (5)].cpVal), (yyvsp[(4) - (5)].exVal), ctx); }
+ break;
+
+ case 42:
+
+/* Line 1806 of yacc.c */
+#line 168 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_Digit, (yyvsp[(1) - (1)].cpVal), NULL, ctx); }
+ break;
+
+ case 43:
+
+/* Line 1806 of yacc.c */
+#line 169 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_Concat, (yyvsp[(1) - (3)].exVal), (yyvsp[(3) - (3)].exVal), ctx); }
+ break;
+
+ case 44:
+
+/* Line 1806 of yacc.c */
+#line 170 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+ break;
+
+ case 45:
+
+/* Line 1806 of yacc.c */
+#line 171 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+ break;
+
+ case 46:
+
+/* Line 1806 of yacc.c */
+#line 172 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(1) - (1)].exVal); }
+ break;
+
+ case 47:
+
+/* Line 1806 of yacc.c */
+#line 173 "util_expr_parse.y"
+ { (yyval.exVal) = (yyvsp[(2) - (3)].exVal); }
+ break;
+
+ case 48:
+
+/* Line 1806 of yacc.c */
+#line 174 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_make(op_String, "", NULL, ctx); }
+ break;
+
+ case 49:
+
+/* Line 1806 of yacc.c */
+#line 177 "util_expr_parse.y"
+ {
+ ap_regex_t *regex;
+ if ((regex = ap_pregcomp(ctx->pool, (yyvsp[(1) - (1)].cpVal),
+ AP_REG_EXTENDED|AP_REG_NOSUB)) == NULL) {
+ ctx->error = "Failed to compile regular expression";
+ YYERROR;
+ }
+ (yyval.exVal) = ap_expr_make(op_Regex, regex, NULL, ctx);
+ }
+ break;
+
+ case 50:
+
+/* Line 1806 of yacc.c */
+#line 186 "util_expr_parse.y"
+ {
+ ap_regex_t *regex;
+ if ((regex = ap_pregcomp(ctx->pool, (yyvsp[(1) - (1)].cpVal),
+ AP_REG_EXTENDED|AP_REG_NOSUB|AP_REG_ICASE)) == NULL) {
+ ctx->error = "Failed to compile regular expression";
+ YYERROR;
+ }
+ (yyval.exVal) = ap_expr_make(op_Regex, regex, NULL, ctx);
+ }
+ break;
+
+ case 51:
+
+/* Line 1806 of yacc.c */
+#line 197 "util_expr_parse.y"
+ {
+ int *n = apr_palloc(ctx->pool, sizeof(int));
+ *n = (yyvsp[(1) - (1)].num);
+ (yyval.exVal) = ap_expr_make(op_RegexBackref, n, NULL, ctx);
+ }
+ break;
+
+ case 52:
+
+/* Line 1806 of yacc.c */
+#line 204 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_list_func_make((yyvsp[(1) - (4)].cpVal), (yyvsp[(3) - (4)].exVal), ctx); }
+ break;
+
+ case 53:
+
+/* Line 1806 of yacc.c */
+#line 207 "util_expr_parse.y"
+ { (yyval.exVal) = ap_expr_str_func_make((yyvsp[(1) - (4)].cpVal), (yyvsp[(3) - (4)].exVal), ctx); }
+ break;
+
+
+
+/* Line 1806 of yacc.c */
+#line 1891 "util_expr_parse.c"
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (ctx, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (ctx, yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, ctx);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, ctx);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (ctx, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, ctx);
+ }
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, ctx);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+
+/* Line 2067 of yacc.c */
+#line 210 "util_expr_parse.y"
+
+
+void yyerror(ap_expr_parse_ctx_t *ctx, const char *s)
+{
+ /* s is allocated on the stack */
+ ctx->error = apr_pstrdup(ctx->ptemp, s);
+}
+
+
diff --git a/server/util_expr_parse.h b/server/util_expr_parse.h
new file mode 100644
index 00000000..8540ec6e
--- /dev/null
+++ b/server/util_expr_parse.h
@@ -0,0 +1,104 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_TRUE = 258,
+ T_FALSE = 259,
+ T_EXPR_BOOL = 260,
+ T_EXPR_STRING = 261,
+ T_ERROR = 262,
+ T_DIGIT = 263,
+ T_ID = 264,
+ T_STRING = 265,
+ T_REGEX = 266,
+ T_REGEX_I = 267,
+ T_REGEX_BACKREF = 268,
+ T_OP_UNARY = 269,
+ T_OP_BINARY = 270,
+ T_STR_BEGIN = 271,
+ T_STR_END = 272,
+ T_VAR_BEGIN = 273,
+ T_VAR_END = 274,
+ T_OP_EQ = 275,
+ T_OP_NE = 276,
+ T_OP_LT = 277,
+ T_OP_LE = 278,
+ T_OP_GT = 279,
+ T_OP_GE = 280,
+ T_OP_REG = 281,
+ T_OP_NRE = 282,
+ T_OP_IN = 283,
+ T_OP_STR_EQ = 284,
+ T_OP_STR_NE = 285,
+ T_OP_STR_LT = 286,
+ T_OP_STR_LE = 287,
+ T_OP_STR_GT = 288,
+ T_OP_STR_GE = 289,
+ T_OP_CONCAT = 290,
+ T_OP_OR = 291,
+ T_OP_AND = 292,
+ T_OP_NOT = 293
+ };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 2068 of yacc.c */
+#line 35 "util_expr_parse.y"
+
+ char *cpVal;
+ ap_expr_t *exVal;
+ int num;
+
+
+
+/* Line 2068 of yacc.c */
+#line 96 "util_expr_parse.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+
+
diff --git a/server/util_expr_parse.y b/server/util_expr_parse.y
new file mode 100644
index 00000000..85ed1231
--- /dev/null
+++ b/server/util_expr_parse.y
@@ -0,0 +1,217 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* based on ap_expr_parse.y from mod_ssl */
+
+/* _________________________________________________________________
+**
+** Expression Parser
+** _________________________________________________________________
+*/
+
+%pure-parser
+%error-verbose
+%defines
+%lex-param { void *yyscanner }
+%parse-param { ap_expr_parse_ctx_t *ctx }
+
+%{
+#include "util_expr_private.h"
+%}
+
+%union {
+ char *cpVal;
+ ap_expr_t *exVal;
+ int num;
+}
+
+%token T_TRUE
+%token T_FALSE
+
+%token T_EXPR_BOOL
+%token T_EXPR_STRING
+
+%token <cpVal> T_ERROR
+
+%token <cpVal> T_DIGIT
+%token <cpVal> T_ID
+%token <cpVal> T_STRING
+%token <cpVal> T_REGEX
+%token <cpVal> T_REGEX_I
+%token <num> T_REGEX_BACKREF
+%token <cpVal> T_OP_UNARY
+%token <cpVal> T_OP_BINARY
+
+%token T_STR_BEGIN
+%token T_STR_END
+%token T_VAR_BEGIN
+%token T_VAR_END
+
+%token T_OP_EQ
+%token T_OP_NE
+%token T_OP_LT
+%token T_OP_LE
+%token T_OP_GT
+%token T_OP_GE
+%token T_OP_REG
+%token T_OP_NRE
+%token T_OP_IN
+%token T_OP_STR_EQ
+%token T_OP_STR_NE
+%token T_OP_STR_LT
+%token T_OP_STR_LE
+%token T_OP_STR_GT
+%token T_OP_STR_GE
+%token T_OP_CONCAT
+
+%token T_OP_OR
+%token T_OP_AND
+%token T_OP_NOT
+
+%right T_OP_OR
+%right T_OP_AND
+%right T_OP_NOT
+%right T_OP_CONCAT
+
+%type <exVal> expr
+%type <exVal> comparison
+%type <exVal> strfunccall
+%type <exVal> lstfunccall
+%type <exVal> regex
+%type <exVal> words
+%type <exVal> wordlist
+%type <exVal> word
+%type <exVal> string
+%type <exVal> strpart
+%type <exVal> var
+%type <exVal> backref
+
+%{
+#include "util_expr_private.h"
+#define yyscanner ctx->scanner
+
+int ap_expr_yylex(YYSTYPE *lvalp, void *scanner);
+%}
+
+
+%%
+
+root : T_EXPR_BOOL expr { ctx->expr = $2; }
+ | T_EXPR_STRING string { ctx->expr = $2; }
+ | T_ERROR { YYABORT; }
+ ;
+
+expr : T_TRUE { $$ = ap_expr_make(op_True, NULL, NULL, ctx); }
+ | T_FALSE { $$ = ap_expr_make(op_False, NULL, NULL, ctx); }
+ | T_OP_NOT expr { $$ = ap_expr_make(op_Not, $2, NULL, ctx); }
+ | expr T_OP_OR expr { $$ = ap_expr_make(op_Or, $1, $3, ctx); }
+ | expr T_OP_AND expr { $$ = ap_expr_make(op_And, $1, $3, ctx); }
+ | comparison { $$ = ap_expr_make(op_Comp, $1, NULL, ctx); }
+ | T_OP_UNARY word { $$ = ap_expr_unary_op_make( $1, $2, ctx); }
+ | word T_OP_BINARY word { $$ = ap_expr_binary_op_make($2, $1, $3, ctx); }
+ | '(' expr ')' { $$ = $2; }
+ | T_ERROR { YYABORT; }
+ ;
+
+comparison: word T_OP_EQ word { $$ = ap_expr_make(op_EQ, $1, $3, ctx); }
+ | word T_OP_NE word { $$ = ap_expr_make(op_NE, $1, $3, ctx); }
+ | word T_OP_LT word { $$ = ap_expr_make(op_LT, $1, $3, ctx); }
+ | word T_OP_LE word { $$ = ap_expr_make(op_LE, $1, $3, ctx); }
+ | word T_OP_GT word { $$ = ap_expr_make(op_GT, $1, $3, ctx); }
+ | word T_OP_GE word { $$ = ap_expr_make(op_GE, $1, $3, ctx); }
+ | word T_OP_STR_EQ word { $$ = ap_expr_make(op_STR_EQ, $1, $3, ctx); }
+ | word T_OP_STR_NE word { $$ = ap_expr_make(op_STR_NE, $1, $3, ctx); }
+ | word T_OP_STR_LT word { $$ = ap_expr_make(op_STR_LT, $1, $3, ctx); }
+ | word T_OP_STR_LE word { $$ = ap_expr_make(op_STR_LE, $1, $3, ctx); }
+ | word T_OP_STR_GT word { $$ = ap_expr_make(op_STR_GT, $1, $3, ctx); }
+ | word T_OP_STR_GE word { $$ = ap_expr_make(op_STR_GE, $1, $3, ctx); }
+ | word T_OP_IN wordlist { $$ = ap_expr_make(op_IN, $1, $3, ctx); }
+ | word T_OP_REG regex { $$ = ap_expr_make(op_REG, $1, $3, ctx); }
+ | word T_OP_NRE regex { $$ = ap_expr_make(op_NRE, $1, $3, ctx); }
+ ;
+
+wordlist : lstfunccall { $$ = $1; }
+ | '{' words '}' { $$ = $2; }
+ ;
+
+words : word { $$ = ap_expr_make(op_ListElement, $1, NULL, ctx); }
+ | words ',' word { $$ = ap_expr_make(op_ListElement, $3, $1, ctx); }
+ ;
+
+string : string strpart { $$ = ap_expr_make(op_Concat, $1, $2, ctx); }
+ | strpart { $$ = $1; }
+ | T_ERROR { YYABORT; }
+ ;
+
+strpart : T_STRING { $$ = ap_expr_make(op_String, $1, NULL, ctx); }
+ | var { $$ = $1; }
+ | backref { $$ = $1; }
+ ;
+
+var : T_VAR_BEGIN T_ID T_VAR_END { $$ = ap_expr_var_make($2, ctx); }
+ | T_VAR_BEGIN T_ID ':' string T_VAR_END { $$ = ap_expr_str_func_make($2, $4, ctx); }
+ ;
+
+word : T_DIGIT { $$ = ap_expr_make(op_Digit, $1, NULL, ctx); }
+ | word T_OP_CONCAT word { $$ = ap_expr_make(op_Concat, $1, $3, ctx); }
+ | var { $$ = $1; }
+ | backref { $$ = $1; }
+ | strfunccall { $$ = $1; }
+ | T_STR_BEGIN string T_STR_END { $$ = $2; }
+ | T_STR_BEGIN T_STR_END { $$ = ap_expr_make(op_String, "", NULL, ctx); }
+ ;
+
+regex : T_REGEX {
+ ap_regex_t *regex;
+ if ((regex = ap_pregcomp(ctx->pool, $1,
+ AP_REG_EXTENDED|AP_REG_NOSUB)) == NULL) {
+ ctx->error = "Failed to compile regular expression";
+ YYERROR;
+ }
+ $$ = ap_expr_make(op_Regex, regex, NULL, ctx);
+ }
+ | T_REGEX_I {
+ ap_regex_t *regex;
+ if ((regex = ap_pregcomp(ctx->pool, $1,
+ AP_REG_EXTENDED|AP_REG_NOSUB|AP_REG_ICASE)) == NULL) {
+ ctx->error = "Failed to compile regular expression";
+ YYERROR;
+ }
+ $$ = ap_expr_make(op_Regex, regex, NULL, ctx);
+ }
+ ;
+
+backref : T_REGEX_BACKREF {
+ int *n = apr_palloc(ctx->pool, sizeof(int));
+ *n = $1;
+ $$ = ap_expr_make(op_RegexBackref, n, NULL, ctx);
+ }
+ ;
+
+lstfunccall : T_ID '(' word ')' { $$ = ap_expr_list_func_make($1, $3, ctx); }
+ ;
+
+strfunccall : T_ID '(' word ')' { $$ = ap_expr_str_func_make($1, $3, ctx); }
+ ;
+
+%%
+
+void yyerror(ap_expr_parse_ctx_t *ctx, const char *s)
+{
+ /* s is allocated on the stack */
+ ctx->error = apr_pstrdup(ctx->ptemp, s);
+}
+
diff --git a/server/util_expr_private.h b/server/util_expr_private.h
new file mode 100644
index 00000000..14cc4e3c
--- /dev/null
+++ b/server/util_expr_private.h
@@ -0,0 +1,141 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AP_EXPR_PRIVATE_H__
+#define __AP_EXPR_PRIVATE_H__
+
+#include "httpd.h"
+#include "apr_strings.h"
+#include "apr_tables.h"
+#include "ap_expr.h"
+
+#ifndef YY_NULL
+#define YY_NULL 0
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#if !APR_HAVE_UNISTD_H
+#define YY_NO_UNISTD_H
+#endif
+
+#ifdef _MSC_VER
+/* Avoid some warnings with Visual Studio (likely due to a bug in bison) */
+#define YYMALLOC malloc
+#define YYFREE free
+#endif
+
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+
+/** The operations in a parse tree node */
+typedef enum {
+ op_NOP,
+ op_True, op_False,
+ op_Not, op_Or, op_And,
+ op_Comp,
+ op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE, op_IN,
+ op_REG, op_NRE,
+ op_STR_EQ, op_STR_NE, op_STR_LT, op_STR_LE, op_STR_GT, op_STR_GE,
+ op_Concat,
+ op_Digit, op_String, op_Regex, op_RegexBackref,
+ op_Var,
+ op_ListElement,
+ /*
+ * call external functions/operators.
+ * The info node contains the function pointer and some function specific
+ * info.
+ * For Binary operators, the Call node links to the Info node and the
+ * Args node, which in turn links to the left and right operand.
+ * For all other variants, the Call node links to the Info node and the
+ * argument.
+ */
+ op_UnaryOpCall, op_UnaryOpInfo,
+ op_BinaryOpCall, op_BinaryOpInfo, op_BinaryOpArgs,
+ op_StringFuncCall, op_StringFuncInfo,
+ op_ListFuncCall, op_ListFuncInfo
+} ap_expr_node_op_e;
+
+/** The basic parse tree node */
+struct ap_expr_node {
+ ap_expr_node_op_e node_op;
+ const void *node_arg1;
+ const void *node_arg2;
+};
+
+/** The context used by scanner and parser */
+typedef struct {
+ /* internal state of the scanner */
+ const char *inputbuf;
+ int inputlen;
+ const char *inputptr;
+ void *scanner;
+ char *scan_ptr;
+ char scan_buf[MAX_STRING_LEN];
+ char scan_del;
+ int at_start;
+
+ /* pools for result and temporary usage */
+ apr_pool_t *pool;
+ apr_pool_t *ptemp;
+
+ /* The created parse tree */
+ ap_expr_t *expr;
+
+ const char *error;
+ const char *error2;
+ unsigned flags;
+
+ /*
+ * The function to use to lookup provider functions for variables
+ * and funtctions
+ */
+ ap_expr_lookup_fn_t *lookup_fn;
+} ap_expr_parse_ctx_t;
+
+/* flex/bison functions */
+int ap_expr_yyparse(ap_expr_parse_ctx_t *context);
+void ap_expr_yyerror(ap_expr_parse_ctx_t *context, const char *err);
+int ap_expr_yylex_init(void **scanner);
+int ap_expr_yylex_destroy(void *scanner);
+void ap_expr_yyset_extra(ap_expr_parse_ctx_t *context, void *scanner);
+
+/* create a parse tree node */
+ap_expr_t *ap_expr_make(ap_expr_node_op_e op, const void *arg1,
+ const void *arg2, ap_expr_parse_ctx_t *ctx);
+/* create parse tree node for the string-returning function 'name' */
+ap_expr_t *ap_expr_str_func_make(const char *name, const ap_expr_t *arg,
+ ap_expr_parse_ctx_t *ctx);
+/* create parse tree node for the list-returning function 'name' */
+ap_expr_t *ap_expr_list_func_make(const char *name, const ap_expr_t *arg,
+ ap_expr_parse_ctx_t *ctx);
+/* create parse tree node for the variable 'name' */
+ap_expr_t *ap_expr_var_make(const char *name, ap_expr_parse_ctx_t *ctx);
+/* create parse tree node for the unary operator 'name' */
+ap_expr_t *ap_expr_unary_op_make(const char *name, const ap_expr_t *arg,
+ ap_expr_parse_ctx_t *ctx);
+/* create parse tree node for the binary operator 'name' */
+ap_expr_t *ap_expr_binary_op_make(const char *name, const ap_expr_t *arg1,
+ const ap_expr_t *arg2,
+ ap_expr_parse_ctx_t *ctx);
+
+
+#endif /* __AP_EXPR_PRIVATE_H__ */
+/** @} */
+
diff --git a/server/util_expr_scan.c b/server/util_expr_scan.c
new file mode 100644
index 00000000..ba759f22
--- /dev/null
+++ b/server/util_expr_scan.c
@@ -0,0 +1,2669 @@
+#line 2 "util_expr_scan.c"
+
+#line 4 "util_expr_scan.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE ap_expr_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via ap_expr_yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void ap_expr_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void ap_expr_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE ap_expr_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void ap_expr_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void ap_expr_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void ap_expr_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void ap_expr_yypop_buffer_state (yyscan_t yyscanner );
+
+static void ap_expr_yyensure_buffer_stack (yyscan_t yyscanner );
+static void ap_expr_yy_load_buffer_state (yyscan_t yyscanner );
+static void ap_expr_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER ap_expr_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE ap_expr_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE ap_expr_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE ap_expr_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *ap_expr_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *ap_expr_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void ap_expr_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer ap_expr_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ ap_expr_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ap_expr_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ ap_expr_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ap_expr_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define ap_expr_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 67
+#define YY_END_OF_BUFFER 68
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[124] =
+ { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 68, 66, 1, 43, 2, 66, 66, 66,
+ 65, 66, 44, 26, 63, 32, 30, 34, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 66,
+ 14, 4, 3, 17, 17, 67, 17, 23, 4, 22,
+ 20, 21, 67, 16, 16, 24, 27, 29, 28, 1,
+ 31, 37, 19, 18, 39, 63, 59, 59, 59, 59,
+ 59, 59, 33, 30, 36, 35, 64, 64, 57, 64,
+ 55, 54, 58, 53, 52, 25, 25, 56, 64, 40,
+ 64, 41, 14, 13, 15, 12, 5, 6, 10, 11,
+
+ 7, 8, 9, 20, 60, 46, 48, 50, 45, 49,
+ 51, 47, 38, 64, 42, 64, 5, 6, 64, 61,
+ 5, 62, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 6, 7, 8, 9, 5, 10,
+ 10, 1, 1, 11, 12, 13, 14, 15, 15, 15,
+ 15, 15, 15, 15, 15, 16, 16, 17, 6, 18,
+ 19, 20, 6, 1, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 1, 22, 1, 6, 23, 1, 24, 25, 21, 26,
+
+ 27, 28, 29, 21, 30, 21, 21, 31, 32, 33,
+ 34, 21, 35, 36, 37, 38, 39, 21, 21, 21,
+ 21, 21, 40, 41, 42, 43, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[44] =
+ { 0,
+ 1, 1, 2, 1, 2, 1, 2, 2, 1, 1,
+ 1, 1, 1, 1, 3, 3, 1, 1, 1, 1,
+ 3, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 1,
+ 1, 2, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[133] =
+ { 0,
+ 0, 0, 41, 47, 89, 0, 130, 136, 0, 0,
+ 147, 146, 175, 275, 54, 28, 275, 43, 134, 164,
+ 275, 164, 275, 275, 45, 152, 32, 151, 0, 136,
+ 133, 143, 26, 133, 35, 194, 38, 129, 128, 122,
+ 0, 275, 275, 51, 122, 221, 275, 275, 275, 275,
+ 0, 275, 275, 61, 121, 275, 275, 275, 275, 76,
+ 275, 275, 275, 275, 275, 65, 0, 125, 47, 126,
+ 107, 130, 275, 275, 275, 275, 0, 130, 0, 124,
+ 0, 0, 0, 0, 0, 275, 0, 0, 104, 0,
+ 101, 275, 0, 275, 275, 275, 71, 131, 275, 275,
+
+ 275, 275, 275, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 99, 0, 61, 133, 135, 57, 0,
+ 138, 0, 275, 259, 262, 265, 79, 67, 268, 271,
+ 65, 42
+ } ;
+
+static yyconst flex_int16_t yy_def[133] =
+ { 0,
+ 123, 1, 124, 124, 123, 5, 124, 124, 125, 125,
+ 126, 126, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 127, 123, 123, 123, 123, 123, 123, 128, 128,
+ 128, 128, 128, 128, 128, 128, 128, 128, 128, 123,
+ 129, 123, 123, 123, 123, 130, 123, 123, 123, 123,
+ 131, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 132, 132, 132, 132,
+ 132, 132, 123, 123, 123, 123, 128, 128, 128, 128,
+ 128, 128, 128, 128, 128, 123, 128, 128, 128, 128,
+ 128, 123, 129, 123, 123, 123, 123, 123, 123, 123,
+
+ 123, 123, 123, 131, 132, 132, 132, 132, 132, 132,
+ 132, 132, 128, 128, 128, 128, 123, 123, 128, 128,
+ 123, 128, 0, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123
+ } ;
+
+static yyconst flex_int16_t yy_nxt[319] =
+ { 0,
+ 14, 15, 15, 16, 17, 14, 18, 19, 20, 21,
+ 21, 22, 23, 24, 25, 25, 21, 26, 27, 28,
+ 29, 14, 14, 30, 29, 29, 31, 32, 33, 34,
+ 35, 36, 37, 38, 29, 29, 29, 39, 29, 21,
+ 40, 21, 14, 42, 105, 43, 61, 44, 45, 42,
+ 74, 43, 81, 44, 45, 60, 60, 63, 63, 66,
+ 66, 84, 46, 82, 88, 94, 94, 104, 46, 77,
+ 62, 89, 85, 107, 75, 94, 94, 60, 60, 66,
+ 66, 67, 47, 122, 108, 117, 118, 120, 47, 48,
+ 48, 49, 48, 48, 48, 48, 48, 48, 48, 48,
+
+ 48, 48, 48, 48, 48, 50, 48, 48, 48, 51,
+ 48, 48, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 51, 51, 48, 48,
+ 52, 48, 42, 110, 53, 119, 54, 55, 42, 116,
+ 53, 115, 54, 55, 111, 118, 118, 121, 118, 118,
+ 118, 46, 118, 118, 114, 113, 112, 46, 109, 106,
+ 95, 95, 92, 91, 90, 83, 80, 79, 78, 76,
+ 73, 56, 65, 64, 123, 59, 59, 56, 66, 66,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 68, 123, 69, 70, 71, 123, 72, 86, 86, 86,
+
+ 86, 86, 123, 123, 86, 86, 86, 86, 123, 123,
+ 86, 123, 123, 123, 123, 123, 87, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 86, 97, 98, 123, 123, 123,
+ 123, 123, 123, 123, 123, 99, 123, 123, 100, 123,
+ 123, 123, 123, 101, 123, 123, 102, 123, 103, 41,
+ 41, 41, 57, 57, 57, 58, 58, 58, 93, 123,
+ 93, 96, 96, 96, 13, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123
+ } ;
+
+static yyconst flex_int16_t yy_chk[319] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 3, 132, 3, 16, 3, 3, 4,
+ 27, 4, 33, 4, 4, 15, 15, 18, 18, 25,
+ 25, 35, 3, 33, 37, 44, 44, 131, 4, 128,
+ 16, 37, 35, 69, 27, 54, 54, 60, 60, 66,
+ 66, 127, 3, 119, 69, 97, 97, 116, 4, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 7, 71, 7, 114, 7, 7, 8, 91,
+ 8, 89, 8, 8, 71, 98, 98, 117, 117, 118,
+ 118, 7, 121, 121, 80, 78, 72, 8, 70, 68,
+ 55, 45, 40, 39, 38, 34, 32, 31, 30, 28,
+ 26, 7, 20, 19, 13, 12, 11, 8, 22, 22,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 22, 0, 22, 22, 22, 0, 22, 36, 36, 36,
+
+ 36, 36, 0, 0, 36, 36, 36, 36, 0, 0,
+ 36, 0, 0, 0, 0, 0, 36, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 36, 46, 46, 0, 0, 0,
+ 0, 0, 0, 0, 0, 46, 0, 0, 46, 0,
+ 0, 0, 0, 46, 0, 0, 46, 0, 46, 124,
+ 124, 124, 125, 125, 125, 126, 126, 126, 129, 0,
+ 129, 130, 130, 130, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+
+ 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
+ 123, 123, 123, 123, 123, 123, 123, 123
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "util_expr_scan.l"
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * ap_expr_scan.l, based on ssl_expr_scan.l from mod_ssl
+ */
+/* _________________________________________________________________
+**
+** Expression Scanner
+** _________________________________________________________________
+*/
+#define YY_NO_INPUT 1
+
+
+
+
+#line 43 "util_expr_scan.l"
+#include "util_expr_private.h"
+#include "util_expr_parse.h"
+
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+{ \
+ if ((result = MIN(max_size, yyextra->inputbuf \
+ + yyextra->inputlen \
+ - yyextra->inputptr)) <= 0) \
+ { \
+ result = YY_NULL; \
+ } \
+ else { \
+ memcpy(buf, yyextra->inputptr, result); \
+ yyextra->inputptr += result; \
+ } \
+}
+
+#define YY_EXTRA_TYPE ap_expr_parse_ctx_t*
+
+#define PERROR(msg) do { yyextra->error2 = msg ; return T_ERROR; } while (0)
+
+#define str_ptr (yyextra->scan_ptr)
+#define str_buf (yyextra->scan_buf)
+#define str_del (yyextra->scan_del)
+
+#define STR_APPEND(c) do { \
+ *str_ptr++ = (c); \
+ if (str_ptr >= str_buf + sizeof(str_buf)) \
+ PERROR("String too long"); \
+ } while (0)
+
+#line 615 "util_expr_scan.c"
+
+#define INITIAL 0
+#define str 1
+#define var 2
+#define vararg 3
+#define regex 4
+#define regex_flags 5
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ YYSTYPE * yylval_r;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+int ap_expr_yylex_init (yyscan_t* scanner);
+
+int ap_expr_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int ap_expr_yylex_destroy (yyscan_t yyscanner );
+
+int ap_expr_yyget_debug (yyscan_t yyscanner );
+
+void ap_expr_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE ap_expr_yyget_extra (yyscan_t yyscanner );
+
+void ap_expr_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *ap_expr_yyget_in (yyscan_t yyscanner );
+
+void ap_expr_yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *ap_expr_yyget_out (yyscan_t yyscanner );
+
+void ap_expr_yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+int ap_expr_yyget_leng (yyscan_t yyscanner );
+
+char *ap_expr_yyget_text (yyscan_t yyscanner );
+
+int ap_expr_yyget_lineno (yyscan_t yyscanner );
+
+void ap_expr_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+YYSTYPE * ap_expr_yyget_lval (yyscan_t yyscanner );
+
+void ap_expr_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int ap_expr_yywrap (yyscan_t yyscanner );
+#else
+extern int ap_expr_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+ static void yy_push_state (int new_state ,yyscan_t yyscanner);
+
+ static void yy_pop_state (yyscan_t yyscanner );
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int ap_expr_yylex \
+ (YYSTYPE * yylval_param ,yyscan_t yyscanner);
+
+#define YY_DECL int ap_expr_yylex \
+ (YYSTYPE * yylval_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 78 "util_expr_scan.l"
+
+
+ char regex_buf[MAX_STRING_LEN];
+ char *regex_ptr = NULL;
+ char regex_del = '\0';
+
+
+ /*
+ * Set initial state for string expressions
+ */
+ if (yyextra->at_start) {
+ yyextra->at_start = 0;
+ if (yyextra->flags & AP_EXPR_FLAG_STRING_RESULT) {
+ BEGIN(str);
+ return T_EXPR_STRING;
+ }
+ else {
+ return T_EXPR_BOOL;
+ }
+ }
+
+
+ /*
+ * Whitespaces
+ */
+#line 886 "util_expr_scan.c"
+
+ yylval = yylval_param;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ ap_expr_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ ap_expr_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ ap_expr_yy_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 124 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 123 );
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+#line 103 "util_expr_scan.l"
+{
+ /* NOP */
+}
+ YY_BREAK
+/*
+ * strings ("..." and '...')
+ */
+case 2:
+YY_RULE_SETUP
+#line 110 "util_expr_scan.l"
+{
+ str_ptr = str_buf;
+ str_del = yytext[0];
+ BEGIN(str);
+ return T_STR_BEGIN;
+}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 116 "util_expr_scan.l"
+{
+ if (yytext[0] == str_del) {
+ if (YY_START == var) {
+ PERROR("Unterminated variable in string");
+ }
+ else if (str_ptr == str_buf) {
+ BEGIN(INITIAL);
+ return T_STR_END;
+ }
+ else {
+ /* return what we have so far and scan delimiter again */
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ yyless(0);
+ str_ptr = str_buf;
+ return T_STRING;
+ }
+ }
+ else {
+ STR_APPEND(yytext[0]);
+ }
+}
+ YY_BREAK
+case 4:
+/* rule 4 can match eol */
+YY_RULE_SETUP
+#line 138 "util_expr_scan.l"
+{
+ PERROR("Unterminated string or variable");
+}
+ YY_BREAK
+case YY_STATE_EOF(var):
+case YY_STATE_EOF(vararg):
+#line 141 "util_expr_scan.l"
+{
+ PERROR("Unterminated string or variable");
+}
+ YY_BREAK
+case YY_STATE_EOF(str):
+#line 144 "util_expr_scan.l"
+{
+ if (!(yyextra->flags & AP_EXPR_FLAG_STRING_RESULT)) {
+ PERROR("Unterminated string or variable");
+ }
+ else {
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ str_ptr = str_buf;
+ BEGIN(INITIAL);
+ return T_STRING;
+ }
+}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 157 "util_expr_scan.l"
+{
+ int result;
+
+ (void)sscanf(yytext+1, "%o", &result);
+ if (result > 0xff) {
+ PERROR("Escape sequence out of bound");
+ }
+ else {
+ STR_APPEND(result);
+ }
+}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 168 "util_expr_scan.l"
+{
+ PERROR("Bad escape sequence");
+}
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 171 "util_expr_scan.l"
+{ STR_APPEND('\n'); }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 172 "util_expr_scan.l"
+{ STR_APPEND('\r'); }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 173 "util_expr_scan.l"
+{ STR_APPEND('\t'); }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 174 "util_expr_scan.l"
+{ STR_APPEND('\b'); }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 175 "util_expr_scan.l"
+{ STR_APPEND('\f'); }
+ YY_BREAK
+case 12:
+/* rule 12 can match eol */
+YY_RULE_SETUP
+#line 176 "util_expr_scan.l"
+{ STR_APPEND(yytext[1]); }
+ YY_BREAK
+/* regexp backref inside string/arg */
+case 13:
+YY_RULE_SETUP
+#line 179 "util_expr_scan.l"
+{
+ if (str_ptr != str_buf) {
+ /* return what we have so far and scan '$x' again */
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ str_ptr = str_buf;
+ yyless(0);
+ return T_STRING;
+ }
+ else {
+ yylval->num = yytext[1] - '0';
+ return T_REGEX_BACKREF;
+ }
+}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 194 "util_expr_scan.l"
+{
+ char *cp = yytext;
+ while (*cp != '\0') {
+ STR_APPEND(*cp);
+ cp++;
+ }
+}
+ YY_BREAK
+/* variable inside string/arg */
+case 15:
+YY_RULE_SETUP
+#line 203 "util_expr_scan.l"
+{
+ if (str_ptr != str_buf) {
+ /* return what we have so far and scan '%{' again */
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ yyless(0);
+ str_ptr = str_buf;
+ return T_STRING;
+ }
+ else {
+ yy_push_state(var, yyscanner);
+ return T_VAR_BEGIN;
+ }
+}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 218 "util_expr_scan.l"
+{
+ STR_APPEND(yytext[0]);
+}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 222 "util_expr_scan.l"
+{
+ STR_APPEND(yytext[0]);
+}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 226 "util_expr_scan.l"
+{
+ yy_push_state(var, yyscanner);
+ return T_VAR_BEGIN;
+}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 231 "util_expr_scan.l"
+{
+ yylval->num = yytext[1] - '0';
+ return T_REGEX_BACKREF;
+}
+ YY_BREAK
+/*
+ * fixed name variable expansion %{XXX} and function call in %{func:arg} syntax
+ */
+case 20:
+YY_RULE_SETUP
+#line 239 "util_expr_scan.l"
+{
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
+ return T_ID;
+}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 244 "util_expr_scan.l"
+{
+ yy_pop_state(yyscanner);
+ return T_VAR_END;
+}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 249 "util_expr_scan.l"
+{
+ BEGIN(vararg);
+ return yytext[0];
+}
+ YY_BREAK
+case 23:
+/* rule 23 can match eol */
+YY_RULE_SETUP
+#line 254 "util_expr_scan.l"
+{
+ char *msg = apr_psprintf(yyextra->pool,
+ "Invalid character in variable name '%c'", yytext[0]);
+ PERROR(msg);
+}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 260 "util_expr_scan.l"
+{
+ if (str_ptr != str_buf) {
+ /* return what we have so far and scan '}' again */
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ str_ptr = str_buf;
+ yyless(0);
+ return T_STRING;
+ }
+ else {
+ yy_pop_state(yyscanner);
+ return T_VAR_END;
+ }
+}
+ YY_BREAK
+/*
+ * Regular Expression
+ */
+case 25:
+YY_RULE_SETUP
+#line 278 "util_expr_scan.l"
+{
+ regex_del = yytext[1];
+ regex_ptr = regex_buf;
+ BEGIN(regex);
+}
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 283 "util_expr_scan.l"
+{
+ regex_del = yytext[0];
+ regex_ptr = regex_buf;
+ BEGIN(regex);
+}
+ YY_BREAK
+case 27:
+/* rule 27 can match eol */
+YY_RULE_SETUP
+#line 288 "util_expr_scan.l"
+{
+ if (yytext[0] == regex_del) {
+ *regex_ptr = '\0';
+ BEGIN(regex_flags);
+ }
+ else {
+ *regex_ptr++ = yytext[0];
+ if (regex_ptr >= regex_buf + sizeof(regex_buf))
+ PERROR("Regexp too long");
+ }
+}
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 299 "util_expr_scan.l"
+{
+ yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
+ BEGIN(INITIAL);
+ return T_REGEX_I;
+}
+ YY_BREAK
+case 29:
+/* rule 29 can match eol */
+YY_RULE_SETUP
+#line 304 "util_expr_scan.l"
+{
+ yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
+ yyless(0);
+ BEGIN(INITIAL);
+ return T_REGEX;
+}
+ YY_BREAK
+case YY_STATE_EOF(regex_flags):
+#line 310 "util_expr_scan.l"
+{
+ yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
+ BEGIN(INITIAL);
+ return T_REGEX;
+}
+ YY_BREAK
+/*
+ * Operators
+ */
+case 30:
+YY_RULE_SETUP
+#line 319 "util_expr_scan.l"
+{ return T_OP_STR_EQ; }
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 320 "util_expr_scan.l"
+{ return T_OP_STR_NE; }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 321 "util_expr_scan.l"
+{ return T_OP_STR_LT; }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 322 "util_expr_scan.l"
+{ return T_OP_STR_LE; }
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 323 "util_expr_scan.l"
+{ return T_OP_STR_GT; }
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 324 "util_expr_scan.l"
+{ return T_OP_STR_GE; }
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 325 "util_expr_scan.l"
+{ return T_OP_REG; }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 326 "util_expr_scan.l"
+{ return T_OP_NRE; }
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 327 "util_expr_scan.l"
+{ return T_OP_AND; }
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 328 "util_expr_scan.l"
+{ return T_OP_AND; }
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 329 "util_expr_scan.l"
+{ return T_OP_OR; }
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 330 "util_expr_scan.l"
+{ return T_OP_OR; }
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 331 "util_expr_scan.l"
+{ return T_OP_NOT; }
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 332 "util_expr_scan.l"
+{ return T_OP_NOT; }
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 333 "util_expr_scan.l"
+{ return T_OP_CONCAT; }
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 334 "util_expr_scan.l"
+{ return T_OP_IN; }
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 335 "util_expr_scan.l"
+{ return T_OP_EQ; }
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 336 "util_expr_scan.l"
+{ return T_OP_NE; }
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 337 "util_expr_scan.l"
+{ return T_OP_GE; }
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 338 "util_expr_scan.l"
+{ return T_OP_LE; }
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 339 "util_expr_scan.l"
+{ return T_OP_GT; }
+ YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 340 "util_expr_scan.l"
+{ return T_OP_LT; }
+ YY_BREAK
+/* for compatibility with ssl_expr */
+case 52:
+YY_RULE_SETUP
+#line 343 "util_expr_scan.l"
+{ return T_OP_LT; }
+ YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 344 "util_expr_scan.l"
+{ return T_OP_LE; }
+ YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 345 "util_expr_scan.l"
+{ return T_OP_GT; }
+ YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 346 "util_expr_scan.l"
+{ return T_OP_GE; }
+ YY_BREAK
+case 56:
+YY_RULE_SETUP
+#line 347 "util_expr_scan.l"
+{ return T_OP_NE; }
+ YY_BREAK
+case 57:
+YY_RULE_SETUP
+#line 348 "util_expr_scan.l"
+{ return T_OP_EQ; }
+ YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 349 "util_expr_scan.l"
+{ return T_OP_IN; }
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 351 "util_expr_scan.l"
+{
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
+ return T_OP_UNARY;
+}
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 356 "util_expr_scan.l"
+{
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
+ return T_OP_BINARY;
+}
+ YY_BREAK
+/*
+ * Specials
+ */
+case 61:
+YY_RULE_SETUP
+#line 364 "util_expr_scan.l"
+{ return T_TRUE; }
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 365 "util_expr_scan.l"
+{ return T_FALSE; }
+ YY_BREAK
+/*
+ * Digits
+ */
+case 63:
+YY_RULE_SETUP
+#line 370 "util_expr_scan.l"
+{
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
+ return T_DIGIT;
+}
+ YY_BREAK
+/*
+ * Identifiers
+ */
+case 64:
+YY_RULE_SETUP
+#line 378 "util_expr_scan.l"
+{
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
+ return T_ID;
+}
+ YY_BREAK
+/*
+ * These are parts of the grammar and are returned as is
+ */
+case 65:
+YY_RULE_SETUP
+#line 386 "util_expr_scan.l"
+{
+ return yytext[0];
+}
+ YY_BREAK
+/*
+ * Anything else is an error
+ */
+case 66:
+/* rule 66 can match eol */
+YY_RULE_SETUP
+#line 393 "util_expr_scan.l"
+{
+ char *msg = apr_psprintf(yyextra->pool, "Parse error near '%c'", yytext[0]);
+ PERROR(msg);
+}
+ YY_BREAK
+case 67:
+YY_RULE_SETUP
+#line 398 "util_expr_scan.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+#line 1523 "util_expr_scan.c"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(regex):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * ap_expr_yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( ap_expr_yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of ap_expr_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ ap_expr_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ ap_expr_yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) ap_expr_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 124 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ register char *yy_cp = yyg->yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 124 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 123);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ ap_expr_yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( ap_expr_yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void ap_expr_yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ ap_expr_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ ap_expr_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ ap_expr_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ ap_expr_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void ap_expr_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * ap_expr_yypop_buffer_state();
+ * ap_expr_yypush_buffer_state(new_buffer);
+ */
+ ap_expr_yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ ap_expr_yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (ap_expr_yywrap()) processing, but the only time this flag
+ * is looked at is after ap_expr_yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void ap_expr_yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE ap_expr_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) ap_expr_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ap_expr_yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) ap_expr_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ap_expr_yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ ap_expr_yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with ap_expr_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void ap_expr_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ ap_expr_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ ap_expr_yyfree((void *) b ,yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a ap_expr_yyrestart() or at EOF.
+ */
+ static void ap_expr_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ ap_expr_yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then ap_expr_yy_init_buffer was _probably_
+ * called from ap_expr_yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void ap_expr_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ ap_expr_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void ap_expr_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ ap_expr_yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from ap_expr_yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from ap_expr_yy_switch_to_buffer. */
+ ap_expr_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void ap_expr_yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ ap_expr_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ ap_expr_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void ap_expr_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ int num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)ap_expr_yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in ap_expr_yyensure_buffer_stack()" );
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)ap_expr_yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in ap_expr_yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ap_expr_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) ap_expr_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ap_expr_yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ ap_expr_yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to ap_expr_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * ap_expr_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE ap_expr_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return ap_expr_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to ap_expr_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ap_expr_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) ap_expr_yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ap_expr_yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = ap_expr_yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in ap_expr_yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+ static void yy_push_state (int new_state , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( yyg->yy_start_stack_ptr >= yyg->yy_start_stack_depth )
+ {
+ yy_size_t new_size;
+
+ yyg->yy_start_stack_depth += YY_START_STACK_INCR;
+ new_size = yyg->yy_start_stack_depth * sizeof( int );
+
+ if ( ! yyg->yy_start_stack )
+ yyg->yy_start_stack = (int *) ap_expr_yyalloc(new_size ,yyscanner );
+
+ else
+ yyg->yy_start_stack = (int *) ap_expr_yyrealloc((void *) yyg->yy_start_stack,new_size ,yyscanner );
+
+ if ( ! yyg->yy_start_stack )
+ YY_FATAL_ERROR( "out of memory expanding start-condition stack" );
+ }
+
+ yyg->yy_start_stack[yyg->yy_start_stack_ptr++] = YY_START;
+
+ BEGIN(new_state);
+}
+
+ static void yy_pop_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( --yyg->yy_start_stack_ptr < 0 )
+ YY_FATAL_ERROR( "start-condition stack underflow" );
+
+ BEGIN(yyg->yy_start_stack[yyg->yy_start_stack_ptr]);
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE ap_expr_yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int ap_expr_yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ap_expr_yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ap_expr_yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int ap_expr_yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *ap_expr_yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void ap_expr_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void ap_expr_yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "ap_expr_yyset_lineno called with no buffer" , yyscanner);
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see ap_expr_yy_switch_to_buffer
+ */
+void ap_expr_yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void ap_expr_yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int ap_expr_yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void ap_expr_yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * ap_expr_yyget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+void ap_expr_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+/* User-visible API */
+
+/* ap_expr_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int ap_expr_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) ap_expr_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* ap_expr_yylex_init_extra has the same functionality as ap_expr_yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to ap_expr_yyalloc in
+ * the yyextra field.
+ */
+
+int ap_expr_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+ struct yyguts_t dummy_yyguts;
+
+ ap_expr_yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) ap_expr_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ ap_expr_yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from ap_expr_yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * ap_expr_yylex_init()
+ */
+ return 0;
+}
+
+/* ap_expr_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int ap_expr_yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ ap_expr_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ ap_expr_yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ ap_expr_yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ ap_expr_yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * ap_expr_yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ ap_expr_yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *ap_expr_yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *ap_expr_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void ap_expr_yyfree (void * ptr , yyscan_t yyscanner)
+{
+ free( (char *) ptr ); /* see ap_expr_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 398 "util_expr_scan.l"
+
+
+
+
+
diff --git a/server/util_expr_scan.l b/server/util_expr_scan.l
new file mode 100644
index 00000000..513236a9
--- /dev/null
+++ b/server/util_expr_scan.l
@@ -0,0 +1,400 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ap_expr_scan.l, based on ssl_expr_scan.l from mod_ssl
+ */
+
+/* _________________________________________________________________
+**
+** Expression Scanner
+** _________________________________________________________________
+*/
+
+%pointer
+%option batch
+%option never-interactive
+%option nodefault
+%option noyywrap
+%option reentrant
+%option bison-bridge
+%option warn
+%option noinput nounput noyy_top_state
+%option stack
+%x str
+%x var
+%x vararg
+%x regex regex_flags
+
+%{
+#include "util_expr_private.h"
+#include "util_expr_parse.h"
+
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+{ \
+ if ((result = MIN(max_size, yyextra->inputbuf \
+ + yyextra->inputlen \
+ - yyextra->inputptr)) <= 0) \
+ { \
+ result = YY_NULL; \
+ } \
+ else { \
+ memcpy(buf, yyextra->inputptr, result); \
+ yyextra->inputptr += result; \
+ } \
+}
+
+#define YY_EXTRA_TYPE ap_expr_parse_ctx_t*
+
+#define PERROR(msg) do { yyextra->error2 = msg ; return T_ERROR; } while (0)
+
+#define str_ptr (yyextra->scan_ptr)
+#define str_buf (yyextra->scan_buf)
+#define str_del (yyextra->scan_del)
+
+#define STR_APPEND(c) do { \
+ *str_ptr++ = (c); \
+ if (str_ptr >= str_buf + sizeof(str_buf)) \
+ PERROR("String too long"); \
+ } while (0)
+
+%}
+
+
+%%
+
+ char regex_buf[MAX_STRING_LEN];
+ char *regex_ptr = NULL;
+ char regex_del = '\0';
+
+%{
+ /*
+ * Set initial state for string expressions
+ */
+ if (yyextra->at_start) {
+ yyextra->at_start = 0;
+ if (yyextra->flags & AP_EXPR_FLAG_STRING_RESULT) {
+ BEGIN(str);
+ return T_EXPR_STRING;
+ }
+ else {
+ return T_EXPR_BOOL;
+ }
+ }
+%}
+
+ /*
+ * Whitespaces
+ */
+[ \t\n]+ {
+ /* NOP */
+}
+
+ /*
+ * strings ("..." and '...')
+ */
+["'] {
+ str_ptr = str_buf;
+ str_del = yytext[0];
+ BEGIN(str);
+ return T_STR_BEGIN;
+}
+<str>["'] {
+ if (yytext[0] == str_del) {
+ if (YY_START == var) {
+ PERROR("Unterminated variable in string");
+ }
+ else if (str_ptr == str_buf) {
+ BEGIN(INITIAL);
+ return T_STR_END;
+ }
+ else {
+ /* return what we have so far and scan delimiter again */
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ yyless(0);
+ str_ptr = str_buf;
+ return T_STRING;
+ }
+ }
+ else {
+ STR_APPEND(yytext[0]);
+ }
+}
+<str,var,vararg>\n {
+ PERROR("Unterminated string or variable");
+}
+<var,vararg><<EOF>> {
+ PERROR("Unterminated string or variable");
+}
+<str><<EOF>> {
+ if (!(yyextra->flags & AP_EXPR_FLAG_STRING_RESULT)) {
+ PERROR("Unterminated string or variable");
+ }
+ else {
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ str_ptr = str_buf;
+ BEGIN(INITIAL);
+ return T_STRING;
+ }
+}
+
+<str,vararg>\\[0-7]{1,3} {
+ int result;
+
+ (void)sscanf(yytext+1, "%o", &result);
+ if (result > 0xff) {
+ PERROR("Escape sequence out of bound");
+ }
+ else {
+ STR_APPEND(result);
+ }
+}
+<str,vararg>\\[0-9]+ {
+ PERROR("Bad escape sequence");
+}
+<str,vararg>\\n { STR_APPEND('\n'); }
+<str,vararg>\\r { STR_APPEND('\r'); }
+<str,vararg>\\t { STR_APPEND('\t'); }
+<str,vararg>\\b { STR_APPEND('\b'); }
+<str,vararg>\\f { STR_APPEND('\f'); }
+<str,vararg>\\(.|\n) { STR_APPEND(yytext[1]); }
+
+ /* regexp backref inside string/arg */
+<str,vararg>[$][0-9] {
+ if (str_ptr != str_buf) {
+ /* return what we have so far and scan '$x' again */
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ str_ptr = str_buf;
+ yyless(0);
+ return T_STRING;
+ }
+ else {
+ yylval->num = yytext[1] - '0';
+ return T_REGEX_BACKREF;
+ }
+}
+
+<str,vararg>[^\\\n"'%}$]+ {
+ char *cp = yytext;
+ while (*cp != '\0') {
+ STR_APPEND(*cp);
+ cp++;
+ }
+}
+
+ /* variable inside string/arg */
+<str,vararg>%\{ {
+ if (str_ptr != str_buf) {
+ /* return what we have so far and scan '%{' again */
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ yyless(0);
+ str_ptr = str_buf;
+ return T_STRING;
+ }
+ else {
+ yy_push_state(var, yyscanner);
+ return T_VAR_BEGIN;
+ }
+}
+
+<vararg>[%$] {
+ STR_APPEND(yytext[0]);
+}
+
+<str>[%}$] {
+ STR_APPEND(yytext[0]);
+}
+
+%\{ {
+ yy_push_state(var, yyscanner);
+ return T_VAR_BEGIN;
+}
+
+[$][0-9] {
+ yylval->num = yytext[1] - '0';
+ return T_REGEX_BACKREF;
+}
+
+ /*
+ * fixed name variable expansion %{XXX} and function call in %{func:arg} syntax
+ */
+<var>[a-zA-Z][a-zA-Z0-9_]* {
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
+ return T_ID;
+}
+
+<var>\} {
+ yy_pop_state(yyscanner);
+ return T_VAR_END;
+}
+
+<var>: {
+ BEGIN(vararg);
+ return yytext[0];
+}
+
+<var>.|\n {
+ char *msg = apr_psprintf(yyextra->pool,
+ "Invalid character in variable name '%c'", yytext[0]);
+ PERROR(msg);
+}
+
+<vararg>\} {
+ if (str_ptr != str_buf) {
+ /* return what we have so far and scan '}' again */
+ *str_ptr = '\0';
+ yylval->cpVal = apr_pstrdup(yyextra->pool, str_buf);
+ str_ptr = str_buf;
+ yyless(0);
+ return T_STRING;
+ }
+ else {
+ yy_pop_state(yyscanner);
+ return T_VAR_END;
+ }
+}
+
+ /*
+ * Regular Expression
+ */
+"m"[/#$%^,;:_\?\|\^\-\!\.\'\"] {
+ regex_del = yytext[1];
+ regex_ptr = regex_buf;
+ BEGIN(regex);
+}
+"/" {
+ regex_del = yytext[0];
+ regex_ptr = regex_buf;
+ BEGIN(regex);
+}
+<regex>.|\n {
+ if (yytext[0] == regex_del) {
+ *regex_ptr = '\0';
+ BEGIN(regex_flags);
+ }
+ else {
+ *regex_ptr++ = yytext[0];
+ if (regex_ptr >= regex_buf + sizeof(regex_buf))
+ PERROR("Regexp too long");
+ }
+}
+<regex_flags>i {
+ yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
+ BEGIN(INITIAL);
+ return T_REGEX_I;
+}
+<regex_flags>.|\n {
+ yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
+ yyless(0);
+ BEGIN(INITIAL);
+ return T_REGEX;
+}
+<regex_flags><<EOF>> {
+ yylval->cpVal = apr_pstrdup(yyextra->pool, regex_buf);
+ BEGIN(INITIAL);
+ return T_REGEX;
+}
+
+ /*
+ * Operators
+ */
+==? { return T_OP_STR_EQ; }
+"!=" { return T_OP_STR_NE; }
+"<" { return T_OP_STR_LT; }
+"<=" { return T_OP_STR_LE; }
+">" { return T_OP_STR_GT; }
+">=" { return T_OP_STR_GE; }
+"=~" { return T_OP_REG; }
+"!~" { return T_OP_NRE; }
+"and" { return T_OP_AND; }
+"&&" { return T_OP_AND; }
+"or" { return T_OP_OR; }
+"||" { return T_OP_OR; }
+"not" { return T_OP_NOT; }
+"!" { return T_OP_NOT; }
+"." { return T_OP_CONCAT; }
+"-in" { return T_OP_IN; }
+"-eq" { return T_OP_EQ; }
+"-ne" { return T_OP_NE; }
+"-ge" { return T_OP_GE; }
+"-le" { return T_OP_LE; }
+"-gt" { return T_OP_GT; }
+"-lt" { return T_OP_LT; }
+
+ /* for compatibility with ssl_expr */
+"lt" { return T_OP_LT; }
+"le" { return T_OP_LE; }
+"gt" { return T_OP_GT; }
+"ge" { return T_OP_GE; }
+"ne" { return T_OP_NE; }
+"eq" { return T_OP_EQ; }
+"in" { return T_OP_IN; }
+
+"-"[a-zA-Z_] {
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
+ return T_OP_UNARY;
+}
+
+"-"[a-zA-Z_][a-zA-Z_0-9]+ {
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext + 1);
+ return T_OP_BINARY;
+}
+
+ /*
+ * Specials
+ */
+"true" { return T_TRUE; }
+"false" { return T_FALSE; }
+
+ /*
+ * Digits
+ */
+-?[0-9]+ {
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
+ return T_DIGIT;
+}
+
+ /*
+ * Identifiers
+ */
+[a-zA-Z][a-zA-Z0-9_]* {
+ yylval->cpVal = apr_pstrdup(yyextra->pool, yytext);
+ return T_ID;
+}
+
+ /*
+ * These are parts of the grammar and are returned as is
+ */
+[(){},:] {
+ return yytext[0];
+}
+
+ /*
+ * Anything else is an error
+ */
+.|\n {
+ char *msg = apr_psprintf(yyextra->pool, "Parse error near '%c'", yytext[0]);
+ PERROR(msg);
+}
+
+%%
+
+
diff --git a/server/util_filter.c b/server/util_filter.c
index b2e7b582..7121eced 100644
--- a/server/util_filter.c
+++ b/server/util_filter.c
@@ -21,6 +21,8 @@
#include "apr_strings.h"
#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
#include "http_log.h"
#include "util_filter.h"
@@ -28,7 +30,7 @@
so we depend on a global to hold the correct pool
*/
#define FILTER_POOL apr_hook_global_pool
-#include "apr_hooks.h" /* for apr_hook_global_pool */
+#include "ap_hooks.h" /* for apr_hook_global_pool */
/*
** This macro returns true/false if a given filter should be inserted BEFORE
@@ -44,6 +46,10 @@
* filter names to filters
*/
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
typedef struct filter_trie_node filter_trie_node;
typedef struct {
@@ -248,15 +254,15 @@ AP_DECLARE(ap_filter_rec_t *) ap_register_input_filter(const char *name,
&registered_input_filters);
}
-/* Prepare to make this a #define in 2.2 */
AP_DECLARE(ap_filter_rec_t *) ap_register_output_filter(const char *name,
ap_out_filter_func filter_func,
ap_init_filter_func filter_init,
ap_filter_type ftype)
{
return ap_register_output_filter_protocol(name, filter_func,
- filter_init, ftype, 0) ;
+ filter_init, ftype, 0);
}
+
AP_DECLARE(ap_filter_rec_t *) ap_register_output_filter_protocol(
const char *name,
ap_out_filter_func filter_func,
@@ -279,7 +285,7 @@ static ap_filter_t *add_any_filter_handle(ap_filter_rec_t *frec, void *ctx,
ap_filter_t **p_filters,
ap_filter_t **c_filters)
{
- apr_pool_t* p = r ? r->pool : c->pool;
+ apr_pool_t *p = frec->ftype < AP_FTYPE_CONNECTION && r ? r->pool : c->pool;
ap_filter_t *f = apr_palloc(p, sizeof(*f));
ap_filter_t **outf;
@@ -288,8 +294,8 @@ static ap_filter_t *add_any_filter_handle(ap_filter_rec_t *frec, void *ctx,
outf = r_filters;
}
else {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
- "a content filter was added without a request: %s", frec->name);
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(00080)
+ "a content filter was added without a request: %s", frec->name);
return NULL;
}
}
@@ -298,8 +304,8 @@ static ap_filter_t *add_any_filter_handle(ap_filter_rec_t *frec, void *ctx,
outf = p_filters;
}
else {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
- "a protocol filter was added without a request: %s", frec->name);
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(00081)
+ "a protocol filter was added without a request: %s", frec->name);
return NULL;
}
}
@@ -309,7 +315,8 @@ static ap_filter_t *add_any_filter_handle(ap_filter_rec_t *frec, void *ctx,
f->frec = frec;
f->ctx = ctx;
- f->r = r;
+ /* f->r must always be NULL for connection filters */
+ f->r = frec->ftype < AP_FTYPE_CONNECTION ? r : NULL;
f->c = c;
f->next = NULL;
@@ -394,8 +401,8 @@ static ap_filter_t *add_any_filter(const char *name, void *ctx,
}
}
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
- "an unknown filter was not added: %s", name);
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, r ? r->connection : c, APLOGNO(00082)
+ "an unknown filter was not added: %s", name);
return NULL;
}
@@ -528,6 +535,41 @@ AP_DECLARE(apr_status_t) ap_pass_brigade(ap_filter_t *next,
return AP_NOBODY_WROTE;
}
+/* Pass the buckets to the next filter in the filter stack
+ * checking return status for filter errors.
+ * returns: OK if ap_pass_brigade returns APR_SUCCESS
+ * AP_FILTER_ERROR if filter error exists
+ * HTTP_INTERNAL_SERVER_ERROR for all other cases
+ * logged with optional errmsg
+ */
+AP_DECLARE(apr_status_t) ap_pass_brigade_fchk(request_rec *r,
+ apr_bucket_brigade *bb,
+ const char *fmt,
+ ...)
+{
+ apr_status_t rv;
+
+ rv = ap_pass_brigade(r->output_filters, bb);
+ if (rv != APR_SUCCESS) {
+ if (rv != AP_FILTER_ERROR) {
+ if (!fmt)
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(00083)
+ "ap_pass_brigade returned %d", rv);
+ else {
+ va_list ap;
+ const char *res;
+ va_start(ap, fmt);
+ res = apr_pvsprintf(r->pool, fmt, ap);
+ va_end(ap);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, "%s", res);
+ }
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ return AP_FILTER_ERROR;
+ }
+ return OK;
+}
+
AP_DECLARE(apr_status_t) ap_save_brigade(ap_filter_t *f,
apr_bucket_brigade **saveto,
apr_bucket_brigade **b, apr_pool_t *p)
diff --git a/server/util_mutex.c b/server/util_mutex.c
new file mode 100644
index 00000000..0ff9483d
--- /dev/null
+++ b/server/util_mutex.c
@@ -0,0 +1,560 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * util_mutex.c: Useful functions for determining allowable
+ * mutexes and mutex settings
+ */
+
+
+#include "apr.h"
+#include "apr_hash.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_main.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "util_mutex.h"
+#if AP_NEED_SET_MUTEX_PERMS
+#include "unixd.h"
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* getpid() */
+#endif
+
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
+AP_DECLARE(apr_status_t) ap_parse_mutex(const char *arg, apr_pool_t *pool,
+ apr_lockmech_e *mutexmech,
+ const char **mutexfile)
+{
+ /* Split arg into meth and file */
+ char *meth = apr_pstrdup(pool, arg);
+ char *file = strchr(meth, ':');
+ if (file) {
+ *(file++) = '\0';
+ if (!*file) {
+ file = NULL;
+ }
+ }
+
+ /* APR determines temporary filename unless overridden below,
+ * we presume file indicates an mutexfile is a file path
+ * unless the method sets mutexfile=file and NULLs file
+ */
+ *mutexfile = NULL;
+
+ if (!strcasecmp(meth, "none") || !strcasecmp(meth, "no")) {
+ return APR_ENOLOCK;
+ }
+
+ /* NOTE: previously, 'yes' implied 'sem' */
+ if (!strcasecmp(meth, "default") || !strcasecmp(meth, "yes")) {
+ *mutexmech = APR_LOCK_DEFAULT;
+ }
+#if APR_HAS_FCNTL_SERIALIZE
+ else if (!strcasecmp(meth, "fcntl") || !strcasecmp(meth, "file")) {
+ *mutexmech = APR_LOCK_FCNTL;
+ }
+#endif
+#if APR_HAS_FLOCK_SERIALIZE
+ else if (!strcasecmp(meth, "flock") || !strcasecmp(meth, "file")) {
+ *mutexmech = APR_LOCK_FLOCK;
+ }
+#endif
+#if APR_HAS_POSIXSEM_SERIALIZE
+ else if (!strcasecmp(meth, "posixsem") || !strcasecmp(meth, "sem")) {
+ *mutexmech = APR_LOCK_POSIXSEM;
+ /* Posix/SysV semaphores aren't file based, use the literal name
+ * if provided and fall back on APR's default if not. Today, APR
+ * will ignore it, but once supported it has an absurdly short limit.
+ */
+ if (file) {
+ *mutexfile = apr_pstrdup(pool, file);
+
+ file = NULL;
+ }
+ }
+#endif
+#if APR_HAS_SYSVSEM_SERIALIZE
+ else if (!strcasecmp(meth, "sysvsem") || !strcasecmp(meth, "sem")) {
+ *mutexmech = APR_LOCK_SYSVSEM;
+ }
+#endif
+#if APR_HAS_PROC_PTHREAD_SERIALIZE
+ else if (!strcasecmp(meth, "pthread")) {
+ *mutexmech = APR_LOCK_PROC_PTHREAD;
+ }
+#endif
+ else {
+ return APR_ENOTIMPL;
+ }
+
+ /* Unless the method above assumed responsibility for setting up
+ * mutexfile and NULLing out file, presume it is a file we
+ * are looking to use
+ */
+ if (file) {
+ *mutexfile = ap_server_root_relative(pool, file);
+ if (!*mutexfile) {
+ return APR_BADARG;
+ }
+ }
+
+ return APR_SUCCESS;
+}
+
+typedef struct {
+ apr_int32_t options;
+ int set;
+ int none;
+ int omit_pid;
+ apr_lockmech_e mech;
+ const char *dir;
+} mutex_cfg_t;
+
+/* hash is created the first time a module calls ap_mutex_register(),
+ * rather than attempting to be the REALLY_REALLY_FIRST pre-config
+ * hook; it is cleaned up when the associated pool goes away; assume
+ * pconf is the pool passed to ap_mutex_register()
+ */
+static apr_hash_t *mxcfg_by_type;
+
+AP_DECLARE_NONSTD(void) ap_mutex_init(apr_pool_t *p)
+{
+ mutex_cfg_t *def;
+
+ if (mxcfg_by_type) {
+ return;
+ }
+
+ mxcfg_by_type = apr_hash_make(p);
+ apr_pool_cleanup_register(p, &mxcfg_by_type, ap_pool_cleanup_set_null,
+ apr_pool_cleanup_null);
+
+ /* initialize default mutex configuration */
+ def = apr_pcalloc(p, sizeof *def);
+ def->mech = APR_LOCK_DEFAULT;
+ def->dir = DEFAULT_REL_RUNTIMEDIR;
+ apr_hash_set(mxcfg_by_type, "default", APR_HASH_KEY_STRING, def);
+}
+
+AP_DECLARE_NONSTD(const char *)ap_set_mutex(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ apr_pool_t *p = cmd->pool;
+ const char **elt;
+ const char *mechdir;
+ int no_mutex = 0, omit_pid = 0;
+ apr_array_header_t *type_list;
+ apr_lockmech_e mech;
+ apr_status_t rv;
+ const char *mutexdir;
+ mutex_cfg_t *mxcfg;
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+ if (err != NULL) {
+ return err;
+ }
+
+ mechdir = ap_getword_conf(cmd->pool, &arg);
+ if (*mechdir == '\0') {
+ return "Mutex requires at least a mechanism argument ("
+ AP_ALL_AVAILABLE_MUTEXES_STRING ")";
+ }
+
+ rv = ap_parse_mutex(mechdir, p, &mech, &mutexdir);
+ if (rv == APR_ENOTIMPL) {
+ return apr_pstrcat(p, "Invalid Mutex argument ", mechdir,
+ " (" AP_ALL_AVAILABLE_MUTEXES_STRING ")", NULL);
+ }
+ else if (rv == APR_BADARG
+ || (mutexdir && !ap_is_directory(p, mutexdir))) {
+ return apr_pstrcat(p, "Invalid Mutex directory in argument ",
+ mechdir, NULL);
+ }
+ else if (rv == APR_ENOLOCK) { /* "none" */
+ no_mutex = 1;
+ }
+
+ /* "OmitPID" can appear at the end of the list, so build a list of
+ * mutex type names while looking for "OmitPID" (anywhere) or the end
+ */
+ type_list = apr_array_make(cmd->pool, 4, sizeof(const char *));
+ while (*arg) {
+ const char *s = ap_getword_conf(cmd->pool, &arg);
+
+ if (!strcasecmp(s, "omitpid")) {
+ omit_pid = 1;
+ }
+ else {
+ const char **new_type = (const char **)apr_array_push(type_list);
+ *new_type = s;
+ }
+ }
+
+ if (apr_is_empty_array(type_list)) { /* no mutex type? assume "default" */
+ const char **new_type = (const char **)apr_array_push(type_list);
+ *new_type = "default";
+ }
+
+ while ((elt = (const char **)apr_array_pop(type_list)) != NULL) {
+ const char *type = *elt;
+ mxcfg = apr_hash_get(mxcfg_by_type, type, APR_HASH_KEY_STRING);
+ if (!mxcfg) {
+ return apr_psprintf(p, "Mutex type %s is not valid", type);
+ }
+
+ mxcfg->none = 0; /* in case that was the default */
+ mxcfg->omit_pid = omit_pid;
+
+ mxcfg->set = 1;
+ if (no_mutex) {
+ if (!(mxcfg->options & AP_MUTEX_ALLOW_NONE)) {
+ return apr_psprintf(p,
+ "None is not allowed for mutex type %s",
+ type);
+ }
+ mxcfg->none = 1;
+ }
+ else {
+ mxcfg->mech = mech;
+ if (mutexdir) { /* retain mutex default if not configured */
+ mxcfg->dir = mutexdir;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+AP_DECLARE(apr_status_t) ap_mutex_register(apr_pool_t *pconf,
+ const char *type,
+ const char *default_dir,
+ apr_lockmech_e default_mech,
+ apr_int32_t options)
+{
+ mutex_cfg_t *mxcfg = apr_pcalloc(pconf, sizeof *mxcfg);
+
+ if ((options & ~(AP_MUTEX_ALLOW_NONE | AP_MUTEX_DEFAULT_NONE))) {
+ return APR_EINVAL;
+ }
+
+ ap_mutex_init(pconf); /* in case this mod's pre-config ran before core's */
+
+ mxcfg->options = options;
+ if (options & AP_MUTEX_DEFAULT_NONE) {
+ mxcfg->none = 1;
+ }
+ mxcfg->dir = default_dir; /* usually NULL */
+ mxcfg->mech = default_mech; /* usually APR_LOCK_DEFAULT */
+ apr_hash_set(mxcfg_by_type, type, APR_HASH_KEY_STRING, mxcfg);
+
+ return APR_SUCCESS;
+}
+
+static int mutex_needs_file(apr_lockmech_e mech)
+{
+ if (mech != APR_LOCK_FLOCK
+ && mech != APR_LOCK_FCNTL
+#if APR_USE_FLOCK_SERIALIZE || APR_USE_FCNTL_SERIALIZE
+ && mech != APR_LOCK_DEFAULT
+#endif
+ ) {
+ return 0;
+ }
+ return 1;
+}
+
+static const char *get_mutex_filename(apr_pool_t *p, mutex_cfg_t *mxcfg,
+ const char *type,
+ const char *instance_id)
+{
+ const char *pid_suffix = "";
+
+ if (!mutex_needs_file(mxcfg->mech)) {
+ return NULL;
+ }
+
+#if HAVE_UNISTD_H
+ if (!mxcfg->omit_pid) {
+ pid_suffix = apr_psprintf(p, ".%" APR_PID_T_FMT, getpid());
+ }
+#endif
+
+ return ap_server_root_relative(p,
+ apr_pstrcat(p,
+ mxcfg->dir,
+ "/",
+ type,
+ instance_id ? "-" : "",
+ instance_id ? instance_id : "",
+ pid_suffix,
+ NULL));
+}
+
+static mutex_cfg_t *mxcfg_lookup(apr_pool_t *p, const char *type)
+{
+ mutex_cfg_t *defcfg, *mxcfg, *newcfg;
+
+ defcfg = apr_hash_get(mxcfg_by_type, "default", APR_HASH_KEY_STRING);
+
+ /* MUST exist in table, or wasn't registered */
+ mxcfg = apr_hash_get(mxcfg_by_type, type, APR_HASH_KEY_STRING);
+ if (!mxcfg) {
+ return NULL;
+ }
+
+ /* order of precedence:
+ * 1. Mutex directive for this mutex
+ * 2. Mutex directive for "default"
+ * 3. Defaults for this mutex from ap_mutex_register()
+ * 4. Global defaults
+ */
+
+ if (mxcfg->set) {
+ newcfg = mxcfg;
+ }
+ else if (defcfg->set) {
+ newcfg = defcfg;
+ }
+ else if (mxcfg->none || mxcfg->mech != APR_LOCK_DEFAULT) {
+ newcfg = mxcfg;
+ }
+ else {
+ newcfg = defcfg;
+ }
+
+ if (!newcfg->none && mutex_needs_file(newcfg->mech) && !newcfg->dir) {
+ /* a file-based mutex mechanism was configured, but
+ * without a mutex file directory; go back through
+ * the chain to find the directory, store in new
+ * mutex cfg structure
+ */
+ newcfg = apr_pmemdup(p, newcfg, sizeof *newcfg);
+
+ /* !true if dir not already set: mxcfg->set && defcfg->dir */
+ if (defcfg->set && defcfg->dir) {
+ newcfg->dir = defcfg->dir;
+ }
+ else if (mxcfg->dir) {
+ newcfg->dir = mxcfg->dir;
+ }
+ else {
+ newcfg->dir = defcfg->dir;
+ }
+ }
+
+ return newcfg;
+}
+
+static void log_bad_create_options(server_rec *s, const char *type)
+{
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(00021)
+ "Invalid options were specified when creating the %s mutex",
+ type);
+}
+
+static void log_unknown_type(server_rec *s, const char *type)
+{
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(00022)
+ "Can't create mutex of unknown type %s", type);
+}
+
+static void log_create_failure(apr_status_t rv, server_rec *s, const char *type,
+ const char *fname)
+{
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00023)
+ "Couldn't create the %s mutex %s%s%s", type,
+ fname ? "(file " : "",
+ fname ? fname : "",
+ fname ? ")" : "");
+}
+
+#ifdef AP_NEED_SET_MUTEX_PERMS
+static void log_perms_failure(apr_status_t rv, server_rec *s, const char *type)
+{
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO(00024)
+ "Couldn't set permissions on the %s mutex; "
+ "check User and Group directives",
+ type);
+}
+#endif
+
+AP_DECLARE(apr_status_t) ap_global_mutex_create(apr_global_mutex_t **mutex,
+ const char **name,
+ const char *type,
+ const char *instance_id,
+ server_rec *s, apr_pool_t *p,
+ apr_int32_t options)
+{
+ apr_status_t rv;
+ const char *fname;
+ mutex_cfg_t *mxcfg = mxcfg_lookup(p, type);
+
+ if (options) {
+ log_bad_create_options(s, type);
+ return APR_EINVAL;
+ }
+
+ if (!mxcfg) {
+ log_unknown_type(s, type);
+ return APR_EINVAL;
+ }
+
+ if (mxcfg->none) {
+ *mutex = NULL;
+ return APR_SUCCESS;
+ }
+
+ fname = get_mutex_filename(p, mxcfg, type, instance_id);
+
+ rv = apr_global_mutex_create(mutex, fname, mxcfg->mech, p);
+ if (rv != APR_SUCCESS) {
+ log_create_failure(rv, s, type, fname);
+ return rv;
+ }
+
+ if (name)
+ *name = fname;
+
+#ifdef AP_NEED_SET_MUTEX_PERMS
+ rv = ap_unixd_set_global_mutex_perms(*mutex);
+ if (rv != APR_SUCCESS) {
+ log_perms_failure(rv, s, type);
+ }
+#endif
+
+ return rv;
+}
+
+AP_DECLARE(apr_status_t) ap_proc_mutex_create(apr_proc_mutex_t **mutex,
+ const char **name,
+ const char *type,
+ const char *instance_id,
+ server_rec *s, apr_pool_t *p,
+ apr_int32_t options)
+{
+ apr_status_t rv;
+ const char *fname;
+ mutex_cfg_t *mxcfg = mxcfg_lookup(p, type);
+
+ if (options) {
+ log_bad_create_options(s, type);
+ return APR_EINVAL;
+ }
+
+ if (!mxcfg) {
+ log_unknown_type(s, type);
+ return APR_EINVAL;
+ }
+
+ if (mxcfg->none) {
+ *mutex = NULL;
+ return APR_SUCCESS;
+ }
+
+ fname = get_mutex_filename(p, mxcfg, type, instance_id);
+
+ rv = apr_proc_mutex_create(mutex, fname, mxcfg->mech, p);
+ if (rv != APR_SUCCESS) {
+ log_create_failure(rv, s, type, fname);
+ return rv;
+ }
+
+ if (name)
+ *name = fname;
+
+#ifdef AP_NEED_SET_MUTEX_PERMS
+ rv = ap_unixd_set_proc_mutex_perms(*mutex);
+ if (rv != APR_SUCCESS) {
+ log_perms_failure(rv, s, type);
+ }
+#endif
+
+ return rv;
+}
+
+AP_CORE_DECLARE(void) ap_dump_mutexes(apr_pool_t *p, server_rec *s, apr_file_t *out)
+{
+ apr_hash_index_t *idx;
+ mutex_cfg_t *defcfg = apr_hash_get(mxcfg_by_type, "default", APR_HASH_KEY_STRING);
+ for (idx = apr_hash_first(p, mxcfg_by_type); idx; idx = apr_hash_next(idx))
+ {
+ mutex_cfg_t *mxcfg;
+ const char *name, *mech;
+ const void *name_;
+ const char *dir = "";
+ apr_hash_this(idx, &name_, NULL, NULL);
+ name = name_;
+ mxcfg = mxcfg_lookup(p, name);
+ if (mxcfg == defcfg && strcmp(name, "default") != 0) {
+ apr_file_printf(out, "Mutex %s: using_defaults\n", name);
+ continue;
+ }
+ if (mxcfg->none) {
+ apr_file_printf(out, "Mutex %s: none\n", name);
+ continue;
+ }
+ switch (mxcfg->mech) {
+ case APR_LOCK_DEFAULT:
+ mech = "default";
+ break;
+#if APR_HAS_FCNTL_SERIALIZE
+ case APR_LOCK_FCNTL:
+ mech = "fcntl";
+ break;
+#endif
+#if APR_HAS_FLOCK_SERIALIZE
+ case APR_LOCK_FLOCK:
+ mech = "flock";
+ break;
+#endif
+#if APR_HAS_POSIXSEM_SERIALIZE
+ case APR_LOCK_POSIXSEM:
+ mech = "posixsem";
+ break;
+#endif
+#if APR_HAS_SYSVSEM_SERIALIZE
+ case APR_LOCK_SYSVSEM:
+ mech = "sysvsem";
+ break;
+#endif
+#if APR_HAS_PROC_PTHREAD_SERIALIZE
+ case APR_LOCK_PROC_PTHREAD:
+ mech = "pthread";
+ break;
+#endif
+ default:
+ ap_assert(0);
+ }
+
+ if (mxcfg->dir)
+ dir = ap_server_root_relative(p, mxcfg->dir);
+
+ apr_file_printf(out, "Mutex %s: dir=\"%s\" mechanism=%s %s\n", name, dir, mech,
+ mxcfg->omit_pid ? "[OmitPid]" : "");
+ }
+}
diff --git a/server/util_pcre.c b/server/util_pcre.c
index c69c9785..7196878d 100644
--- a/server/util_pcre.c
+++ b/server/util_pcre.c
@@ -1,6 +1,6 @@
/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
+ * Perl-Compatible Regular Expressions *
+ *************************************************/
/*
This is a library of functions to support regular expressions whose syntax
@@ -55,176 +55,196 @@ POSSIBILITY OF SUCH DAMAGE.
#endif
/* Table of error strings corresponding to POSIX error codes; must be
- * kept in synch with include/ap_regex.h's AP_REG_E* definitions. */
+ * kept in synch with include/ap_regex.h's AP_REG_E* definitions.
+ */
static const char *const pstring[] = {
- "", /* Dummy for value 0 */
- "internal error", /* AP_REG_ASSERT */
- "failed to get memory", /* AP_REG_ESPACE */
- "bad argument", /* AP_REG_INVARG */
- "match failed" /* AP_REG_NOMATCH */
+ "", /* Dummy for value 0 */
+ "internal error", /* AP_REG_ASSERT */
+ "failed to get memory", /* AP_REG_ESPACE */
+ "bad argument", /* AP_REG_INVARG */
+ "match failed" /* AP_REG_NOMATCH */
};
AP_DECLARE(apr_size_t) ap_regerror(int errcode, const ap_regex_t *preg,
char *errbuf, apr_size_t errbuf_size)
{
-const char *message, *addmessage;
-apr_size_t length, addlength;
-
-message = (errcode >= (int)(sizeof(pstring)/sizeof(char *)))?
- "unknown error code" : pstring[errcode];
-length = strlen(message) + 1;
-
-addmessage = " at offset ";
-addlength = (preg != NULL && (int)preg->re_erroffset != -1)?
- strlen(addmessage) + 6 : 0;
-
-if (errbuf_size > 0)
- {
- if (addlength > 0 && errbuf_size >= length + addlength)
- apr_snprintf(errbuf, sizeof errbuf,
- "%s%s%-6d", message, addmessage, (int)preg->re_erroffset);
- else
- {
- strncpy(errbuf, message, errbuf_size - 1);
- errbuf[errbuf_size-1] = 0;
+ const char *message, *addmessage;
+ apr_size_t length, addlength;
+
+ message = (errcode >= (int)(sizeof(pstring) / sizeof(char *))) ?
+ "unknown error code" : pstring[errcode];
+ length = strlen(message) + 1;
+
+ addmessage = " at offset ";
+ addlength = (preg != NULL && (int)preg->re_erroffset != -1) ?
+ strlen(addmessage) + 6 : 0;
+
+ if (errbuf_size > 0) {
+ if (addlength > 0 && errbuf_size >= length + addlength)
+ apr_snprintf(errbuf, errbuf_size, "%s%s%-6d", message, addmessage,
+ (int)preg->re_erroffset);
+ else
+ apr_cpystrn(errbuf, message, errbuf_size);
}
- }
-return length + addlength;
+ return length + addlength;
}
/*************************************************
-* Free store held by a regex *
-*************************************************/
+ * Free store held by a regex *
+ *************************************************/
AP_DECLARE(void) ap_regfree(ap_regex_t *preg)
{
-(pcre_free)(preg->re_pcre);
+ (pcre_free)(preg->re_pcre);
}
/*************************************************
-* Compile a regular expression *
-*************************************************/
+ * Compile a regular expression *
+ *************************************************/
/*
-Arguments:
- preg points to a structure for recording the compiled expression
- pattern the pattern to compile
- cflags compilation flags
-
-Returns: 0 on success
- various non-zero codes on failure
+ * Arguments:
+ * preg points to a structure for recording the compiled expression
+ * pattern the pattern to compile
+ * cflags compilation flags
+ *
+ * Returns: 0 on success
+ * various non-zero codes on failure
*/
-
-AP_DECLARE(int) ap_regcomp(ap_regex_t *preg, const char *pattern, int cflags)
+AP_DECLARE(int) ap_regcomp(ap_regex_t * preg, const char *pattern, int cflags)
{
-const char *errorptr;
-int erroffset;
-int options = 0;
-
-if ((cflags & AP_REG_ICASE) != 0) options |= PCRE_CASELESS;
-if ((cflags & AP_REG_NEWLINE) != 0) options |= PCRE_MULTILINE;
-
-preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL);
-preg->re_erroffset = erroffset;
-
-if (preg->re_pcre == NULL) return AP_REG_INVARG;
-
-preg->re_nsub = pcre_info((const pcre *)preg->re_pcre, NULL, NULL);
-return 0;
+ const char *errorptr;
+ int erroffset;
+ int options = 0;
+
+ if ((cflags & AP_REG_ICASE) != 0)
+ options |= PCRE_CASELESS;
+ if ((cflags & AP_REG_NEWLINE) != 0)
+ options |= PCRE_MULTILINE;
+ if ((cflags & AP_REG_DOTALL) != 0)
+ options |= PCRE_DOTALL;
+
+ preg->re_pcre =
+ pcre_compile(pattern, options, &errorptr, &erroffset, NULL);
+ preg->re_erroffset = erroffset;
+
+ if (preg->re_pcre == NULL)
+ return AP_REG_INVARG;
+
+ preg->re_nsub = pcre_info((const pcre *)preg->re_pcre, NULL, NULL);
+ return 0;
}
/*************************************************
-* Match a regular expression *
-*************************************************/
+ * Match a regular expression *
+ *************************************************/
/* Unfortunately, PCRE requires 3 ints of working space for each captured
-substring, so we have to get and release working store instead of just using
-the POSIX structures as was done in earlier releases when PCRE needed only 2
-ints. However, if the number of possible capturing brackets is small, use a
-block of store on the stack, to reduce the use of malloc/free. The threshold is
-in a macro that can be changed at configure time. */
-
+ * substring, so we have to get and release working store instead of just using
+ * the POSIX structures as was done in earlier releases when PCRE needed only 2
+ * ints. However, if the number of possible capturing brackets is small, use a
+ * block of store on the stack, to reduce the use of malloc/free. The threshold
+ * is in a macro that can be changed at configure time.
+ */
AP_DECLARE(int) ap_regexec(const ap_regex_t *preg, const char *string,
- apr_size_t nmatch, ap_regmatch_t pmatch[],
+ apr_size_t nmatch, ap_regmatch_t *pmatch,
int eflags)
{
-int rc;
-int options = 0;
-int *ovector = NULL;
-int small_ovector[POSIX_MALLOC_THRESHOLD * 3];
-int allocated_ovector = 0;
-
-if ((eflags & AP_REG_NOTBOL) != 0) options |= PCRE_NOTBOL;
-if ((eflags & AP_REG_NOTEOL) != 0) options |= PCRE_NOTEOL;
-
-((ap_regex_t *)preg)->re_erroffset = (apr_size_t)(-1); /* Only has meaning after compile */
-
-if (nmatch > 0)
- {
- if (nmatch <= POSIX_MALLOC_THRESHOLD)
- {
- ovector = &(small_ovector[0]);
- }
- else
- {
- ovector = (int *)malloc(sizeof(int) * nmatch * 3);
- if (ovector == NULL) return AP_REG_ESPACE;
- allocated_ovector = 1;
- }
- }
-
-rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string, (int)strlen(string),
- 0, options, ovector, nmatch * 3);
+ return ap_regexec_len(preg, string, strlen(string), nmatch, pmatch,
+ eflags);
+}
-if (rc == 0) rc = nmatch; /* All captured slots were filled in */
+AP_DECLARE(int) ap_regexec_len(const ap_regex_t *preg, const char *buff,
+ apr_size_t len, apr_size_t nmatch,
+ ap_regmatch_t *pmatch, int eflags)
+{
+ int rc;
+ int options = 0;
+ int *ovector = NULL;
+ int small_ovector[POSIX_MALLOC_THRESHOLD * 3];
+ int allocated_ovector = 0;
+
+ if ((eflags & AP_REG_NOTBOL) != 0)
+ options |= PCRE_NOTBOL;
+ if ((eflags & AP_REG_NOTEOL) != 0)
+ options |= PCRE_NOTEOL;
+
+ ((ap_regex_t *)preg)->re_erroffset = (apr_size_t)(-1); /* Only has meaning after compile */
+
+ if (nmatch > 0) {
+ if (nmatch <= POSIX_MALLOC_THRESHOLD) {
+ ovector = &(small_ovector[0]);
+ }
+ else {
+ ovector = (int *)malloc(sizeof(int) * nmatch * 3);
+ if (ovector == NULL)
+ return AP_REG_ESPACE;
+ allocated_ovector = 1;
+ }
+ }
-if (rc >= 0)
- {
- apr_size_t i;
- for (i = 0; i < (apr_size_t)rc; i++)
- {
- pmatch[i].rm_so = ovector[i*2];
- pmatch[i].rm_eo = ovector[i*2+1];
+ rc = pcre_exec((const pcre *)preg->re_pcre, NULL, buff, (int)len,
+ 0, options, ovector, nmatch * 3);
+
+ if (rc == 0)
+ rc = nmatch; /* All captured slots were filled in */
+
+ if (rc >= 0) {
+ apr_size_t i;
+ for (i = 0; i < (apr_size_t)rc; i++) {
+ pmatch[i].rm_so = ovector[i * 2];
+ pmatch[i].rm_eo = ovector[i * 2 + 1];
+ }
+ if (allocated_ovector)
+ free(ovector);
+ for (; i < nmatch; i++)
+ pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+ return 0;
}
- if (allocated_ovector) free(ovector);
- for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1;
- return 0;
- }
-
-else
- {
- if (allocated_ovector) free(ovector);
- switch(rc)
- {
- case PCRE_ERROR_NOMATCH: return AP_REG_NOMATCH;
- case PCRE_ERROR_NULL: return AP_REG_INVARG;
- case PCRE_ERROR_BADOPTION: return AP_REG_INVARG;
- case PCRE_ERROR_BADMAGIC: return AP_REG_INVARG;
- case PCRE_ERROR_UNKNOWN_NODE: return AP_REG_ASSERT;
- case PCRE_ERROR_NOMEMORY: return AP_REG_ESPACE;
+
+ else {
+ if (allocated_ovector)
+ free(ovector);
+ switch (rc) {
+ case PCRE_ERROR_NOMATCH:
+ return AP_REG_NOMATCH;
+ case PCRE_ERROR_NULL:
+ return AP_REG_INVARG;
+ case PCRE_ERROR_BADOPTION:
+ return AP_REG_INVARG;
+ case PCRE_ERROR_BADMAGIC:
+ return AP_REG_INVARG;
+ case PCRE_ERROR_UNKNOWN_NODE:
+ return AP_REG_ASSERT;
+ case PCRE_ERROR_NOMEMORY:
+ return AP_REG_ESPACE;
#ifdef PCRE_ERROR_MATCHLIMIT
- case PCRE_ERROR_MATCHLIMIT: return AP_REG_ESPACE;
+ case PCRE_ERROR_MATCHLIMIT:
+ return AP_REG_ESPACE;
#endif
#ifdef PCRE_ERROR_BADUTF8
- case PCRE_ERROR_BADUTF8: return AP_REG_INVARG;
+ case PCRE_ERROR_BADUTF8:
+ return AP_REG_INVARG;
#endif
#ifdef PCRE_ERROR_BADUTF8_OFFSET
- case PCRE_ERROR_BADUTF8_OFFSET: return AP_REG_INVARG;
+ case PCRE_ERROR_BADUTF8_OFFSET:
+ return AP_REG_INVARG;
#endif
- default: return AP_REG_ASSERT;
+ default:
+ return AP_REG_ASSERT;
+ }
}
- }
}
/* End of pcreposix.c */
diff --git a/server/util_regex.c b/server/util_regex.c
new file mode 100644
index 00000000..73eccec7
--- /dev/null
+++ b/server/util_regex.c
@@ -0,0 +1,210 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "apr_lib.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+#include "ap_config.h"
+#include "ap_regex.h"
+#include "httpd.h"
+
+static apr_status_t rxplus_cleanup(void *preg)
+{
+ ap_regfree((ap_regex_t *) preg);
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(ap_rxplus_t*) ap_rxplus_compile(apr_pool_t *pool,
+ const char *pattern)
+{
+ /* perl style patterns
+ * add support for more as and when wanted
+ * substitute: s/rx/subs/
+ * match: m/rx/ or just /rx/
+ */
+
+ /* allow any nonalnum delimiter as first or second char.
+ * If we ever use this with non-string pattern we'll need an extra check
+ */
+ const char *endp = 0;
+ const char *str = pattern;
+ const char *rxstr;
+ ap_rxplus_t *ret = apr_pcalloc(pool, sizeof(ap_rxplus_t));
+ char delim = 0;
+ enum { SUBSTITUTE = 's', MATCH = 'm'} action = MATCH;
+ if (!apr_isalnum(pattern[0])) {
+ delim = *str++;
+ }
+ else if (pattern[0] == 's' && !apr_isalnum(pattern[1])) {
+ action = SUBSTITUTE;
+ delim = pattern[1];
+ str += 2;
+ }
+ else if (pattern[0] == 'm' && !apr_isalnum(pattern[1])) {
+ delim = pattern[1];
+ str += 2;
+ }
+ /* TODO: support perl's after/before */
+ /* FIXME: fix these simplminded delims */
+
+ /* we think there's a delimiter. Allow for it not to be if unmatched */
+ if (delim) {
+ endp = ap_strchr_c(str, delim);
+ }
+ if (!endp) { /* there's no delim or flags */
+ if (ap_regcomp(&ret->rx, pattern, 0) == 0) {
+ apr_pool_cleanup_register(pool, &ret->rx, rxplus_cleanup,
+ apr_pool_cleanup_null);
+ return ret;
+ }
+ else {
+ return NULL;
+ }
+ }
+
+ /* We have a delimiter. Use it to extract the regexp */
+ rxstr = apr_pstrndup(pool, str, endp-str);
+
+ /* If it's a substitution, we need the replacement string
+ * TODO: possible future enhancement - support other parsing
+ * in the replacement string.
+ */
+ if (action == SUBSTITUTE) {
+ str = endp+1;
+ if (!*str || (endp = ap_strchr_c(str, delim), !endp)) {
+ /* missing replacement string is an error */
+ return NULL;
+ }
+ ret->subs = apr_pstrndup(pool, str, (endp-str));
+ }
+
+ /* anything after the current delimiter is flags */
+ while (*++endp) {
+ switch (*endp) {
+ case 'i': ret->flags |= AP_REG_ICASE; break;
+ case 'm': ret->flags |= AP_REG_NEWLINE; break;
+ case 'n': ret->flags |= AP_REG_NOMEM; break;
+ case 'g': ret->flags |= AP_REG_MULTI; break;
+ case 's': ret->flags |= AP_REG_DOTALL; break;
+ case '^': ret->flags |= AP_REG_NOTBOL; break;
+ case '$': ret->flags |= AP_REG_NOTEOL; break;
+ default: break; /* we should probably be stricter here */
+ }
+ }
+ if (ap_regcomp(&ret->rx, rxstr, ret->flags) == 0) {
+ apr_pool_cleanup_register(pool, &ret->rx, rxplus_cleanup,
+ apr_pool_cleanup_null);
+ }
+ else {
+ return NULL;
+ }
+ if (!(ret->flags & AP_REG_NOMEM)) {
+ /* count size of memory required, starting at 1 for the whole-match
+ * Simpleminded should be fine 'cos regcomp already checked syntax
+ */
+ ret->nmatch = 1;
+ while (*rxstr) {
+ switch (*rxstr++) {
+ case '\\': /* next char is escaped - skip it */
+ if (*rxstr != 0) {
+ ++rxstr;
+ }
+ break;
+ case '(': /* unescaped bracket implies memory */
+ ++ret->nmatch;
+ break;
+ default:
+ break;
+ }
+ }
+ ret->pmatch = apr_palloc(pool, ret->nmatch*sizeof(ap_regmatch_t));
+ }
+ return ret;
+}
+
+AP_DECLARE(int) ap_rxplus_exec(apr_pool_t *pool, ap_rxplus_t *rx,
+ const char *pattern, char **newpattern)
+{
+ int ret = 1;
+ int startl, oldl, newl, diffsz;
+ const char *remainder;
+ char *subs;
+/* snrf process_regexp from mod_headers */
+ if (ap_regexec(&rx->rx, pattern, rx->nmatch, rx->pmatch, rx->flags) != 0) {
+ rx->match = NULL;
+ return 0; /* no match, nothing to do */
+ }
+ rx->match = pattern;
+ if (rx->subs) {
+ *newpattern = ap_pregsub(pool, rx->subs, pattern,
+ rx->nmatch, rx->pmatch);
+ if (!*newpattern) {
+ return 0; /* FIXME - should we do more to handle error? */
+ }
+ startl = rx->pmatch[0].rm_so;
+ oldl = rx->pmatch[0].rm_eo - startl;
+ newl = strlen(*newpattern);
+ diffsz = newl - oldl;
+ remainder = pattern + startl + oldl;
+ if (rx->flags & AP_REG_MULTI) {
+ /* recurse to do any further matches */
+ char *subs;
+ ret += ap_rxplus_exec(pool, rx, remainder, &subs);
+ if (ret > 1) {
+ /* a further substitution happened */
+ diffsz += strlen(subs) - strlen(remainder);
+ remainder = subs;
+ }
+ }
+ subs = apr_palloc(pool, strlen(pattern) + 1 + diffsz);
+ memcpy(subs, pattern, startl);
+ memcpy(subs+startl, *newpattern, newl);
+ strcpy(subs+startl+newl, remainder);
+ *newpattern = subs;
+ }
+ return ret;
+}
+#ifdef DOXYGEN
+AP_DECLARE(int) ap_rxplus_nmatch(ap_rxplus_t *rx)
+{
+ return (rx->match != NULL) ? rx->nmatch : 0;
+}
+#endif
+
+/* If this blows up on you, see the notes in the header/apidoc
+ * rx->match is a pointer and it's your responsibility to ensure
+ * it hasn't gone out-of-scope since the last ap_rxplus_exec
+ */
+AP_DECLARE(void) ap_rxplus_match(ap_rxplus_t *rx, int n, int *len,
+ const char **match)
+{
+ if (n >= 0 && n < ap_rxplus_nmatch(rx)) {
+ *match = rx->match + rx->pmatch[n].rm_so;
+ *len = rx->pmatch[n].rm_eo - rx->pmatch[n].rm_so;
+ }
+ else {
+ *len = -1;
+ *match = NULL;
+ }
+}
+AP_DECLARE(char*) ap_rxplus_pmatch(apr_pool_t *pool, ap_rxplus_t *rx, int n)
+{
+ int len;
+ const char *match;
+ ap_rxplus_match(rx, n, &len, &match);
+ return (match != NULL) ? apr_pstrndup(pool, match, len) : NULL;
+}
diff --git a/server/util_script.c b/server/util_script.c
index 1300951b..38dfa0ef 100644
--- a/server/util_script.c
+++ b/server/util_script.c
@@ -25,7 +25,6 @@
#include <stdlib.h>
#endif
-#define CORE_PRIVATE
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
@@ -50,12 +49,13 @@
* where there don't have to be).
*/
-#define MALFORMED_MESSAGE "malformed header from script. Bad header="
-#define MALFORMED_HEADER_LENGTH_TO_SHOW 30
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
-static char *http2env(apr_pool_t *a, const char *w)
+static char *http2env(request_rec *r, const char *w)
{
- char *res = (char *)apr_palloc(a, sizeof("HTTP_") + strlen(w));
+ char *res = (char *)apr_palloc(r->pool, sizeof("HTTP_") + strlen(w));
char *cp = res;
char c;
@@ -66,11 +66,17 @@ static char *http2env(apr_pool_t *a, const char *w)
*cp++ = '_';
while ((c = *w++) != 0) {
- if (!apr_isalnum(c)) {
+ if (apr_isalnum(c)) {
+ *cp++ = apr_toupper(c);
+ }
+ else if (c == '-') {
*cp++ = '_';
}
else {
- *cp++ = apr_toupper(c);
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "Not exporting header with invalid name as envvar: %s",
+ ap_escape_logitem(r->pool, w));
+ return NULL;
}
}
*cp = 0;
@@ -78,6 +84,18 @@ static char *http2env(apr_pool_t *a, const char *w)
return res;
}
+static void add_unless_null(apr_table_t *table, const char *name, const char *val)
+{
+ if (name && val) {
+ apr_table_addn(table, name, val);
+ }
+}
+
+static void env2env(apr_table_t *table, const char *name)
+{
+ add_unless_null(table, name, getenv(name));
+}
+
AP_DECLARE(char **) ap_create_environment(apr_pool_t *p, apr_table_t *t)
{
const apr_array_header_t *env_arr = apr_table_elts(t);
@@ -121,12 +139,7 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r)
apr_table_t *e;
server_rec *s = r->server;
conn_rec *c = r->connection;
- const char *rem_logname;
- char *env_path;
-#if defined(WIN32) || defined(OS2) || defined(BEOS)
- char *env_temp;
-#endif
- const char *host;
+ const char *env_temp;
const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in);
const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
int i;
@@ -176,69 +189,61 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r)
continue;
}
#endif
- else {
- apr_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
- }
- }
-
- if (!(env_path = getenv("PATH"))) {
- env_path = DEFAULT_PATH;
- }
- apr_table_addn(e, "PATH", apr_pstrdup(r->pool, env_path));
-
-#ifdef WIN32
- if ((env_temp = getenv("SystemRoot")) != NULL) {
- apr_table_addn(e, "SystemRoot", env_temp);
- }
- if ((env_temp = getenv("COMSPEC")) != NULL) {
- apr_table_addn(e, "COMSPEC", env_temp);
- }
- if ((env_temp = getenv("PATHEXT")) != NULL) {
- apr_table_addn(e, "PATHEXT", env_temp);
- }
- if ((env_temp = getenv("WINDIR")) != NULL) {
- apr_table_addn(e, "WINDIR", env_temp);
- }
-#endif
-
-#ifdef OS2
- if ((env_temp = getenv("COMSPEC")) != NULL) {
- apr_table_addn(e, "COMSPEC", env_temp);
- }
- if ((env_temp = getenv("ETC")) != NULL) {
- apr_table_addn(e, "ETC", env_temp);
- }
- if ((env_temp = getenv("DPATH")) != NULL) {
- apr_table_addn(e, "DPATH", env_temp);
- }
- if ((env_temp = getenv("PERLLIB_PREFIX")) != NULL) {
- apr_table_addn(e, "PERLLIB_PREFIX", env_temp);
- }
-#endif
-
-#ifdef BEOS
- if ((env_temp = getenv("LIBRARY_PATH")) != NULL) {
- apr_table_addn(e, "LIBRARY_PATH", env_temp);
- }
+ else
+ add_unless_null(e, http2env(r, hdrs[i].key), hdrs[i].val);
+ }
+
+ env_temp = apr_table_get(r->subprocess_env, "PATH");
+ if (env_temp == NULL) {
+ env_temp = getenv("PATH");
+ }
+ if (env_temp == NULL) {
+ env_temp = DEFAULT_PATH;
+ }
+ apr_table_addn(e, "PATH", apr_pstrdup(r->pool, env_temp));
+
+#if defined(WIN32)
+ env2env(e, "SystemRoot");
+ env2env(e, "COMSPEC");
+ env2env(e, "PATHEXT");
+ env2env(e, "WINDIR");
+#elif defined(OS2)
+ env2env(e, "COMSPEC");
+ env2env(e, "ETC");
+ env2env(e, "DPATH");
+ env2env(e, "PERLLIB_PREFIX");
+#elif defined(BEOS)
+ env2env(e, "LIBRARY_PATH");
+#elif defined(DARWIN)
+ env2env(e, "DYLD_LIBRARY_PATH");
+#elif defined(_AIX)
+ env2env(e, "LIBPATH");
+#elif defined(__HPUX__)
+ /* HPUX PARISC 2.0W knows both, otherwise redundancy is harmless */
+ env2env(e, "SHLIB_PATH");
+ env2env(e, "LD_LIBRARY_PATH");
+#else /* Some Unix */
+ env2env(e, "LD_LIBRARY_PATH");
#endif
apr_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r));
apr_table_addn(e, "SERVER_SOFTWARE", ap_get_server_banner());
apr_table_addn(e, "SERVER_NAME",
- ap_escape_html(r->pool, ap_get_server_name(r)));
+ ap_escape_html(r->pool, ap_get_server_name_for_url(r)));
apr_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */
apr_table_addn(e, "SERVER_PORT",
apr_psprintf(r->pool, "%u", ap_get_server_port(r)));
- host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST, NULL);
- if (host) {
- apr_table_addn(e, "REMOTE_HOST", host);
- }
- apr_table_addn(e, "REMOTE_ADDR", c->remote_ip);
+ add_unless_null(e, "REMOTE_HOST",
+ ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST, NULL));
+ apr_table_addn(e, "REMOTE_ADDR", r->useragent_ip);
apr_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r)); /* Apache */
+ apr_table_setn(e, "REQUEST_SCHEME", ap_http_scheme(r));
+ apr_table_addn(e, "CONTEXT_PREFIX", ap_context_prefix(r));
+ apr_table_addn(e, "CONTEXT_DOCUMENT_ROOT", ap_context_document_root(r));
apr_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */
apr_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */
- rport = c->remote_addr->port;
+ rport = c->client_addr->port;
apr_table_addn(e, "REMOTE_PORT", apr_itoa(r->pool, rport));
if (r->user) {
@@ -255,23 +260,17 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r)
back = back->prev;
}
}
- if (r->ap_auth_type) {
- apr_table_addn(e, "AUTH_TYPE", r->ap_auth_type);
- }
- rem_logname = ap_get_remote_logname(r);
- if (rem_logname) {
- apr_table_addn(e, "REMOTE_IDENT", apr_pstrdup(r->pool, rem_logname));
+ add_unless_null(e, "AUTH_TYPE", r->ap_auth_type);
+ env_temp = ap_get_remote_logname(r);
+ if (env_temp) {
+ apr_table_addn(e, "REMOTE_IDENT", apr_pstrdup(r->pool, env_temp));
}
/* Apache custom error responses. If we have redirected set two new vars */
if (r->prev) {
- if (r->prev->args) {
- apr_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args);
- }
- if (r->prev->uri) {
- apr_table_addn(e, "REDIRECT_URL", r->prev->uri);
- }
+ add_unless_null(e, "REDIRECT_QUERY_STRING", r->prev->args);
+ add_unless_null(e, "REDIRECT_URL", r->prev->uri);
}
if (e != r->subprocess_env) {
@@ -399,10 +398,12 @@ static int set_cookie_doo_doo(void *v, const char *key, const char *val)
}
#define HTTP_UNSET (-HTTP_OK)
+#define SCRIPT_LOG_MARK __FILE__,__LINE__,module_index
-AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
+AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer,
int (*getsfunc) (char *, int, void *),
- void *getsfunc_data)
+ void *getsfunc_data,
+ int module_index)
{
char x[MAX_STRING_LEN];
char *w, *l;
@@ -410,6 +411,8 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
int cgi_status = HTTP_UNSET;
apr_table_t *merge;
apr_table_t *cookie_table;
+ int trace_log = APLOG_R_MODULE_IS_LEVEL(r, module_index, APLOG_TRACE1);
+ int first_header = 1;
if (buffer) {
*buffer = '\0';
@@ -432,13 +435,16 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
int rv = (*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data);
if (rv == 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
- "Premature end of script headers: %s",
+ const char *msg = "Premature end of script headers";
+ if (first_header)
+ msg = "End of script output before headers";
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
+ "%s: %s", msg,
apr_filepath_name_get(r->filename));
return HTTP_INTERNAL_SERVER_ERROR;
}
else if (rv == -1) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
"Script timed out before returning headers: %s",
apr_filepath_name_get(r->filename));
return HTTP_GATEWAY_TIME_OUT;
@@ -475,17 +481,17 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
if (w[0] == '\0') {
int cond_status = OK;
- /* PR#38070: This fails because it gets confused when a
- * CGI Status header overrides ap_meets_conditions.
- *
- * We can fix that by dropping ap_meets_conditions when
- * Status has been set. Since this is the only place
- * cgi_status gets used, let's test it explicitly.
- *
- * The alternative would be to ignore CGI Status when
- * ap_meets_conditions returns anything interesting.
- * That would be safer wrt HTTP, but would break CGI.
- */
+ /* PR#38070: This fails because it gets confused when a
+ * CGI Status header overrides ap_meets_conditions.
+ *
+ * We can fix that by dropping ap_meets_conditions when
+ * Status has been set. Since this is the only place
+ * cgi_status gets used, let's test it explicitly.
+ *
+ * The alternative would be to ignore CGI Status when
+ * ap_meets_conditions returns anything interesting.
+ * That would be safer wrt HTTP, but would break CGI.
+ */
if ((cgi_status == HTTP_UNSET) && (r->method_number == M_GET)) {
cond_status = ap_meets_conditions(r);
}
@@ -500,6 +506,14 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
return cond_status;
}
+ if (trace_log) {
+ if (first_header)
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r,
+ "Headers from script '%s':",
+ apr_filepath_name_get(r->filename));
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r, " %s", w);
+ }
+
/* if we see a bogus header don't ignore it. Shout and scream */
#if APR_CHARSET_EBCDIC
@@ -519,7 +533,7 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
++maybeASCII;
}
if (maybeASCII > maybeEBCDIC) {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+ ap_log_error(SCRIPT_LOG_MARK, APLOG_ERR, 0, r->server,
"CGI Interface Error: Script headers apparently ASCII: (CGI = %s)",
r->filename);
inbytes_left = outbytes_left = cp - w;
@@ -529,12 +543,6 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
}
#endif /*APR_CHARSET_EBCDIC*/
if (!(l = strchr(w, ':'))) {
- char malformed[(sizeof MALFORMED_MESSAGE) + 1
- + MALFORMED_HEADER_LENGTH_TO_SHOW];
-
- strcpy(malformed, MALFORMED_MESSAGE);
- strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW);
-
if (!buffer) {
/* Soak up all the script output - may save an outright kill */
while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) {
@@ -542,9 +550,9 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
}
}
- ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
- "%s: %s", malformed,
- apr_filepath_name_get(r->filename));
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
+ "malformed header from script '%s': Bad header: %.30s",
+ apr_filepath_name_get(r->filename), w);
return HTTP_INTERNAL_SERVER_ERROR;
}
@@ -573,6 +581,14 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
*/
else if (!strcasecmp(w, "Status")) {
r->status = cgi_status = atoi(l);
+ if (!ap_is_HTTP_VALID_RESPONSE(cgi_status))
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
+ "Invalid status line from script '%s': %s",
+ apr_filepath_name_get(r->filename), w);
+ else
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE1, 0, r,
+ "Status line from script '%s': %s",
+ apr_filepath_name_get(r->filename), w);
r->status_line = apr_pstrdup(r->pool, l);
}
else if (!strcasecmp(w, "Location")) {
@@ -587,6 +603,9 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
else if (!strcasecmp(w, "Transfer-Encoding")) {
apr_table_set(r->headers_out, w, l);
}
+ else if (!strcasecmp(w, "ETag")) {
+ apr_table_set(r->headers_out, w, l);
+ }
/*
* If the script gave us a Last-Modified header, we can't just
* pass it on blindly because of restrictions on future values.
@@ -601,11 +620,21 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
else {
apr_table_add(merge, w, l);
}
+ first_header = 0;
}
-
+ /* never reached - we leave this function within the while loop above */
return OK;
}
+AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
+ int (*getsfunc) (char *, int, void *),
+ void *getsfunc_data)
+{
+ return ap_scan_script_header_err_core_ex(r, buffer, getsfunc,
+ getsfunc_data,
+ APLOG_MODULE_INDEX);
+}
+
static int getsfunc_FILE(char *buf, int len, void *f)
{
return apr_file_gets(buf, len, (apr_file_t *) f) == APR_SUCCESS;
@@ -614,9 +643,18 @@ static int getsfunc_FILE(char *buf, int len, void *f)
AP_DECLARE(int) ap_scan_script_header_err(request_rec *r, apr_file_t *f,
char *buffer)
{
- return ap_scan_script_header_err_core(r, buffer, getsfunc_FILE, f);
+ return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_FILE, f,
+ APLOG_MODULE_INDEX);
+}
+
+AP_DECLARE(int) ap_scan_script_header_err_ex(request_rec *r, apr_file_t *f,
+ char *buffer, int module_index)
+{
+ return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_FILE, f,
+ module_index);
}
+
static int getsfunc_BRIGADE(char *buf, int len, void *arg)
{
apr_bucket_brigade *bb = (apr_bucket_brigade *)arg;
@@ -666,9 +704,20 @@ AP_DECLARE(int) ap_scan_script_header_err_brigade(request_rec *r,
apr_bucket_brigade *bb,
char *buffer)
{
- return ap_scan_script_header_err_core(r, buffer, getsfunc_BRIGADE, bb);
+ return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_BRIGADE, bb,
+ APLOG_MODULE_INDEX);
+}
+
+AP_DECLARE(int) ap_scan_script_header_err_brigade_ex(request_rec *r,
+ apr_bucket_brigade *bb,
+ char *buffer,
+ int module_index)
+{
+ return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_BRIGADE, bb,
+ module_index);
}
+
struct vastrs {
va_list args;
int arg;
@@ -707,6 +756,28 @@ static int getsfunc_STRING(char *w, int len, void *pvastrs)
* character is returned to **arg, **data. (The first optional arg is
* counted as 0.)
*/
+AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs_ex(request_rec *r,
+ char *buffer,
+ int module_index,
+ const char **termch,
+ int *termarg, ...)
+{
+ struct vastrs strs;
+ int res;
+
+ va_start(strs.args, termarg);
+ strs.arg = 0;
+ strs.curpos = va_arg(strs.args, char*);
+ res = ap_scan_script_header_err_core_ex(r, buffer, getsfunc_STRING,
+ (void *) &strs, module_index);
+ if (termch)
+ *termch = strs.curpos;
+ if (termarg)
+ *termarg = strs.arg;
+ va_end(strs.args);
+ return res;
+}
+
AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r,
char *buffer,
const char **termch,
@@ -718,7 +789,8 @@ AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r,
va_start(strs.args, termarg);
strs.arg = 0;
strs.curpos = va_arg(strs.args, char*);
- res = ap_scan_script_header_err_core(r, buffer, getsfunc_STRING, (void *) &strs);
+ res = ap_scan_script_header_err_core_ex(r, buffer, getsfunc_STRING,
+ (void *) &strs, APLOG_MODULE_INDEX);
if (termch)
*termch = strs.curpos;
if (termarg)
@@ -726,3 +798,38 @@ AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r,
va_end(strs.args);
return res;
}
+
+static void
+argstr_to_table(char *str, apr_table_t *parms)
+{
+ char *key;
+ char *value;
+ char *strtok_state;
+
+ if (str == NULL) {
+ return;
+ }
+
+ key = apr_strtok(str, "&", &strtok_state);
+ while (key) {
+ value = strchr(key, '=');
+ if (value) {
+ *value = '\0'; /* Split the string in two */
+ value++; /* Skip passed the = */
+ }
+ else {
+ value = "1";
+ }
+ ap_unescape_url(key);
+ ap_unescape_url(value);
+ apr_table_set(parms, key, value);
+ key = apr_strtok(NULL, "&", &strtok_state);
+ }
+}
+
+AP_DECLARE(void) ap_args_to_table(request_rec *r, apr_table_t **table)
+{
+ apr_table_t *t = apr_table_make(r->pool, 10);
+ argstr_to_table(apr_pstrdup(r->pool, r->args), t);
+ *table = t;
+}
diff --git a/server/util_time.c b/server/util_time.c
index 172cfa52..3632d0d5 100644
--- a/server/util_time.c
+++ b/server/util_time.c
@@ -16,6 +16,16 @@
#include "util_time.h"
+
+/* Number of characters needed to format the microsecond part of a timestamp.
+ * Microseconds have 6 digits plus one separator character makes 7.
+ * */
+#define AP_CTIME_USEC_LENGTH 7
+
+/* Length of ISO 8601 date/time */
+#define AP_CTIME_COMPACT_LEN 20
+
+
/* Cache for exploded values of recent timestamps
*/
@@ -145,27 +155,73 @@ AP_DECLARE(apr_status_t) ap_explode_recent_gmt(apr_time_exp_t * tm,
AP_DECLARE(apr_status_t) ap_recent_ctime(char *date_str, apr_time_t t)
{
+ int len = APR_CTIME_LEN;
+ return ap_recent_ctime_ex(date_str, t, AP_CTIME_OPTION_NONE, &len);
+}
+
+AP_DECLARE(apr_status_t) ap_recent_ctime_ex(char *date_str, apr_time_t t,
+ int option, int *len)
+{
/* ### This code is a clone of apr_ctime(), except that it
* uses ap_explode_recent_localtime() instead of apr_time_exp_lt().
*/
apr_time_exp_t xt;
const char *s;
int real_year;
+ int needed;
+
+
+ /* Calculate the needed buffer length */
+ if (option & AP_CTIME_OPTION_COMPACT)
+ needed = AP_CTIME_COMPACT_LEN;
+ else
+ needed = APR_CTIME_LEN;
+
+ if (option & AP_CTIME_OPTION_USEC) {
+ needed += AP_CTIME_USEC_LENGTH;
+ }
+
+ /* Check the provided buffer length */
+ if (len && *len >= needed) {
+ *len = needed;
+ }
+ else {
+ if (len != NULL) {
+ *len = 0;
+ }
+ return APR_ENOMEM;
+ }
- /* example: "Wed Jun 30 21:49:08 1993" */
- /* 123456789012345678901234 */
+ /* example without options: "Wed Jun 30 21:49:08 1993" */
+ /* 123456789012345678901234 */
+ /* example for compact format: "1993-06-30 21:49:08" */
+ /* 1234567890123456789 */
ap_explode_recent_localtime(&xt, t);
- s = &apr_day_snames[xt.tm_wday][0];
- *date_str++ = *s++;
- *date_str++ = *s++;
- *date_str++ = *s++;
- *date_str++ = ' ';
- s = &apr_month_snames[xt.tm_mon][0];
- *date_str++ = *s++;
- *date_str++ = *s++;
- *date_str++ = *s++;
- *date_str++ = ' ';
+ real_year = 1900 + xt.tm_year;
+ if (option & AP_CTIME_OPTION_COMPACT) {
+ int real_month = xt.tm_mon + 1;
+ *date_str++ = real_year / 1000 + '0';
+ *date_str++ = real_year % 1000 / 100 + '0';
+ *date_str++ = real_year % 100 / 10 + '0';
+ *date_str++ = real_year % 10 + '0';
+ *date_str++ = '-';
+ *date_str++ = real_month / 10 + '0';
+ *date_str++ = real_month % 10 + '0';
+ *date_str++ = '-';
+ }
+ else {
+ s = &apr_day_snames[xt.tm_wday][0];
+ *date_str++ = *s++;
+ *date_str++ = *s++;
+ *date_str++ = *s++;
+ *date_str++ = ' ';
+ s = &apr_month_snames[xt.tm_mon][0];
+ *date_str++ = *s++;
+ *date_str++ = *s++;
+ *date_str++ = *s++;
+ *date_str++ = ' ';
+ }
*date_str++ = xt.tm_mday / 10 + '0';
*date_str++ = xt.tm_mday % 10 + '0';
*date_str++ = ' ';
@@ -177,12 +233,22 @@ AP_DECLARE(apr_status_t) ap_recent_ctime(char *date_str, apr_time_t t)
*date_str++ = ':';
*date_str++ = xt.tm_sec / 10 + '0';
*date_str++ = xt.tm_sec % 10 + '0';
- *date_str++ = ' ';
- real_year = 1900 + xt.tm_year;
- *date_str++ = real_year / 1000 + '0';
- *date_str++ = real_year % 1000 / 100 + '0';
- *date_str++ = real_year % 100 / 10 + '0';
- *date_str++ = real_year % 10 + '0';
+ if (option & AP_CTIME_OPTION_USEC) {
+ int div;
+ int usec = (int)xt.tm_usec;
+ *date_str++ = '.';
+ for (div=100000; div>0; div=div/10) {
+ *date_str++ = usec / div + '0';
+ usec = usec % div;
+ }
+ }
+ if (!(option & AP_CTIME_OPTION_COMPACT)) {
+ *date_str++ = ' ';
+ *date_str++ = real_year / 1000 + '0';
+ *date_str++ = real_year % 1000 / 100 + '0';
+ *date_str++ = real_year % 100 / 10 + '0';
+ *date_str++ = real_year % 10 + '0';
+ }
*date_str++ = 0;
return APR_SUCCESS;
diff --git a/server/util_xml.c b/server/util_xml.c
index dc88b3dd..26f1c6e8 100644
--- a/server/util_xml.c
+++ b/server/util_xml.c
@@ -29,6 +29,10 @@
#define READ_BLOCKSIZE 2048
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
AP_DECLARE(int) ap_xml_parse_input(request_rec * r, apr_xml_doc **pdoc)
{
apr_xml_parser *parser;
@@ -81,7 +85,7 @@ AP_DECLARE(int) ap_xml_parse_input(request_rec * r, apr_xml_doc **pdoc)
total_read += len;
if (limit_xml_body && total_read > limit_xml_body) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00539)
"XML request body is larger than the configured "
"limit of %lu", (unsigned long)limit_xml_body);
result = HTTP_REQUEST_ENTITY_TOO_LARGE;
@@ -107,7 +111,7 @@ AP_DECLARE(int) ap_xml_parse_input(request_rec * r, apr_xml_doc **pdoc)
*pdoc = NULL;
return OK;
}
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00540)
"XML parser error (at end). status=%d", status);
return HTTP_BAD_REQUEST;
}
@@ -119,7 +123,7 @@ AP_DECLARE(int) ap_xml_parse_input(request_rec * r, apr_xml_doc **pdoc)
parser_error:
(void) apr_xml_parser_geterror(parser, errbuf, sizeof(errbuf));
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00541)
"XML Parser Error: %s", errbuf);
/* FALLTHRU */
diff --git a/server/vhost.c b/server/vhost.c
index b8e9ca75..302e4084 100644
--- a/server/vhost.c
+++ b/server/vhost.c
@@ -27,7 +27,6 @@
#define APR_WANT_STRFUNC
#include "apr_want.h"
-#define CORE_PRIVATE
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
@@ -40,6 +39,10 @@
#include <arpa/inet.h>
#endif
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
/*
* After all the definitions there's an explanation of how it's all put
* together.
@@ -68,6 +71,8 @@ struct ipaddr_chain {
server_rec *server; /* the server to use if this matches */
name_chain *names; /* if non-NULL then a list of name-vhosts
* sharing this address */
+ name_chain *initialnames; /* no runtime use, temporary storage of first
+ * NVH'es names */
};
/* This defines the size of the hash table used for hashing ip addresses
@@ -92,9 +97,12 @@ static ipaddr_chain *iphash_table[IPHASH_TABLE_SIZE];
/* list of the _default_ servers */
static ipaddr_chain *default_list;
-/* list of the NameVirtualHost addresses */
-static server_addr_rec *name_vhost_list;
-static server_addr_rec **name_vhost_list_tail;
+/* whether a config error was seen */
+static int config_error = 0;
+
+/* config check function */
+static int vhost_check_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s);
/*
* How it's used:
@@ -124,8 +132,7 @@ AP_DECLARE(void) ap_init_vhost_config(apr_pool_t *p)
{
memset(iphash_table, 0, sizeof(iphash_table));
default_list = NULL;
- name_vhost_list = NULL;
- name_vhost_list_tail = &name_vhost_list;
+ ap_hook_check_config(vhost_check_config, NULL, NULL, APR_HOOK_MIDDLE);
}
@@ -151,9 +158,9 @@ static const char *get_addresses(apr_pool_t *p, const char *w_,
if (*w_ == '\0')
return NULL;
- w = apr_pstrdup(p, w_);
+ wlen = strlen(w_); /* wlen must be > 0 at this point */
+ w = apr_pstrmemdup(p, w_, wlen);
/* apr_parse_addr_port() doesn't understand ":*" so handle that first. */
- wlen = strlen(w); /* wlen must be > 0 at this point */
wild_port = 0;
if (w[wlen - 1] == '*') {
if (wlen < 2) {
@@ -181,25 +188,17 @@ static const char *get_addresses(apr_pool_t *p, const char *w_,
port = default_port;
}
- if (strcmp(host, "*") == 0) {
- rv = apr_sockaddr_info_get(&my_addr, "0.0.0.0", APR_INET, port, 0, p);
- if (rv) {
- return "Could not resolve address '0.0.0.0' -- "
- "check resolver configuration.";
- }
- }
- else if (strcasecmp(host, "_default_") == 0
- || strcmp(host, "255.255.255.255") == 0) {
- rv = apr_sockaddr_info_get(&my_addr, "255.255.255.255", APR_INET, port, 0, p);
+ if (strcmp(host, "*") == 0 || strcasecmp(host, "_default_") == 0) {
+ rv = apr_sockaddr_info_get(&my_addr, NULL, APR_UNSPEC, port, 0, p);
if (rv) {
- return "Could not resolve address '255.255.255.255' -- "
+ return "Could not determine a wildcard address ('0.0.0.0') -- "
"check resolver configuration.";
}
}
else {
rv = apr_sockaddr_info_get(&my_addr, host, APR_UNSPEC, port, 0, p);
if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00547)
"Could not resolve host name %s -- ignoring!", host);
return NULL;
}
@@ -250,12 +249,20 @@ const char *ap_parse_vhost_addrs(apr_pool_t *p,
}
-const char *ap_set_name_virtual_host(cmd_parms *cmd, void *dummy,
- const char *arg)
+AP_DECLARE_NONSTD(const char *)ap_set_name_virtual_host(cmd_parms *cmd,
+ void *dummy,
+ const char *arg)
{
- /* use whatever port the main server has at this point */
- return get_addresses(cmd->pool, arg, &name_vhost_list_tail,
- cmd->server->port);
+ static int warnonce = 0;
+ if (++warnonce == 1) {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE|APLOG_STARTUP, APR_SUCCESS, NULL, APLOGNO(00548)
+ "NameVirtualHost has no effect and will be removed in the "
+ "next release %s:%d",
+ cmd->directive->filename,
+ cmd->directive->line_num);
+ }
+
+ return NULL;
}
@@ -347,6 +354,7 @@ static ipaddr_chain *new_ipaddr_chain(apr_pool_t *p,
new = apr_palloc(p, sizeof(*new));
new->names = NULL;
+ new->initialnames = NULL;
new->server = s;
new->sar = sar;
new->next = NULL;
@@ -370,7 +378,8 @@ static name_chain *new_name_chain(apr_pool_t *p,
static APR_INLINE ipaddr_chain *find_ipaddr(apr_sockaddr_t *sa)
{
unsigned bucket;
- ipaddr_chain *trav;
+ ipaddr_chain *trav = NULL;
+ ipaddr_chain *wild_match = NULL;
/* scan the hash table for an exact match first */
bucket = hash_addr(sa);
@@ -378,30 +387,48 @@ static APR_INLINE ipaddr_chain *find_ipaddr(apr_sockaddr_t *sa)
server_addr_rec *sar = trav->sar;
apr_sockaddr_t *cur = sar->host_addr;
- if (cur->port == 0 || sa->port == 0 || cur->port == sa->port) {
+ if (cur->port == sa->port) {
if (apr_sockaddr_equal(cur, sa)) {
return trav;
}
}
+ if (wild_match == NULL && (cur->port == 0 || sa->port == 0)) {
+ if (apr_sockaddr_equal(cur, sa)) {
+ /* don't break, continue looking for an exact match */
+ wild_match = trav;
+ }
+ }
}
- return NULL;
+ return wild_match;
}
static ipaddr_chain *find_default_server(apr_port_t port)
{
server_addr_rec *sar;
- ipaddr_chain *trav;
+ ipaddr_chain *trav = NULL;
+ ipaddr_chain *wild_match = NULL;
for (trav = default_list; trav; trav = trav->next) {
sar = trav->sar;
- if (sar->host_port == 0 || sar->host_port == port) {
+ if (sar->host_port == port) {
/* match! */
return trav;
}
+ if (wild_match == NULL && sar->host_port == 0) {
+ /* don't break, continue looking for an exact match */
+ wild_match = trav;
+ }
}
- return NULL;
+ return wild_match;
}
+#if APR_HAVE_IPV6
+#define IS_IN6_ANYADDR(ad) ((ad)->family == APR_INET6 \
+ && IN6_IS_ADDR_UNSPECIFIED(&(ad)->sa.sin6.sin6_addr))
+#else
+#define IS_IN6_ANYADDR(ad) (0)
+#endif
+
static void dump_a_vhost(apr_file_t *f, ipaddr_chain *ic)
{
name_chain *nc;
@@ -409,13 +436,8 @@ static void dump_a_vhost(apr_file_t *f, ipaddr_chain *ic)
char buf[MAX_STRING_LEN];
apr_sockaddr_t *ha = ic->sar->host_addr;
- if (ha->family == APR_INET &&
- ha->sa.sin.sin_addr.s_addr == DEFAULT_VHOST_ADDR) {
- len = apr_snprintf(buf, sizeof(buf), "_default_:%u",
- ic->sar->host_port);
- }
- else if (ha->family == APR_INET &&
- ha->sa.sin.sin_addr.s_addr == INADDR_ANY) {
+ if ((ha->family == APR_INET && ha->sa.sin.sin_addr.s_addr == INADDR_ANY)
+ || IS_IN6_ANYADDR(ha)) {
len = apr_snprintf(buf, sizeof(buf), "*:%u",
ic->sar->host_port);
}
@@ -445,6 +467,26 @@ static void dump_a_vhost(apr_file_t *f, ipaddr_chain *ic)
apr_file_printf(f, "namevhost %s (%s:%u)\n",
nc->server->server_hostname,
nc->server->defn_name, nc->server->defn_line_number);
+ if (nc->server->names) {
+ apr_array_header_t *names = nc->server->names;
+ char **name = (char **)names->elts;
+ int i;
+ for (i = 0; i < names->nelts; ++i) {
+ if (name[i]) {
+ apr_file_printf(f, "%16s alias %s\n", "", name[i]);
+ }
+ }
+ }
+ if (nc->server->wild_names) {
+ apr_array_header_t *names = nc->server->wild_names;
+ char **name = (char **)names->elts;
+ int i;
+ for (i = 0; i < names->nelts; ++i) {
+ if (name[i]) {
+ apr_file_printf(f, "%16s wild alias %s\n", "", name[i]);
+ }
+ }
+ }
}
}
@@ -454,67 +496,57 @@ static void dump_vhost_config(apr_file_t *f)
int i;
apr_file_printf(f, "VirtualHost configuration:\n");
+
+ /* non-wildcard servers */
for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
for (ic = iphash_table[i]; ic; ic = ic->next) {
dump_a_vhost(f, ic);
}
}
- if (default_list) {
- apr_file_printf(f, "wildcard NameVirtualHosts and _default_ servers:\n");
- for (ic = default_list; ic; ic = ic->next) {
- dump_a_vhost(f, ic);
- }
+
+ /* wildcard servers */
+ for (ic = default_list; ic; ic = ic->next) {
+ dump_a_vhost(f, ic);
}
}
+
/*
- * Two helper functions for ap_fini_vhost_config()
+ * When a second or later virtual host maps to the same IP chain,
+ * add the relevant server names to the chain. Special care is taken
+ * to avoid adding ic->names until we're sure there are multiple VH'es.
*/
-static int add_name_vhost_config(apr_pool_t *p, server_rec *main_s,
+static void add_name_vhost_config(apr_pool_t *p, server_rec *main_s,
server_rec *s, server_addr_rec *sar,
ipaddr_chain *ic)
{
- /* the first time we encounter a NameVirtualHost address
- * ic->server will be NULL, on subsequent encounters
- * ic->names will be non-NULL.
- */
- if (ic->names || ic->server == NULL) {
- name_chain *nc = new_name_chain(p, s, sar);
- nc->next = ic->names;
- ic->names = nc;
- ic->server = s;
- if (sar->host_port != ic->sar->host_port) {
- /* one of the two is a * port, the other isn't */
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_s,
- "VirtualHost %s:%u -- mixing * "
- "ports and non-* ports with "
- "a NameVirtualHost address is not supported,"
- " proceeding with undefined results",
- sar->virthost, sar->host_port);
- }
- return 1;
- }
- else {
- /* IP-based vhosts are handled by the caller */
- return 0;
- }
-}
-static void remove_unused_name_vhosts(server_rec *main_s, ipaddr_chain **pic)
-{
- while (*pic) {
- ipaddr_chain *ic = *pic;
-
- if (ic->server == NULL) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_s,
- "NameVirtualHost %s:%u has no VirtualHosts",
- ic->sar->virthost, ic->sar->host_port);
- *pic = ic->next;
- }
- else {
- pic = &ic->next;
- }
- }
+ name_chain *nc = new_name_chain(p, s, sar);
+ nc->next = ic->names;
+
+ /* iterating backwards, so each one we see becomes the current default server */
+ ic->server = s;
+
+ if (ic->names == NULL) {
+ if (ic->initialnames == NULL) {
+ /* first pass, set these names aside in case we see another VH.
+ * Until then, this looks like an IP-based VH to runtime.
+ */
+ ic->initialnames = nc;
+ }
+ else {
+ /* second pass through this chain -- this really is an NVH, and we
+ * have two sets of names to link in.
+ */
+ nc->next = ic->initialnames;
+ ic->names = nc;
+ ic->initialnames = NULL;
+ }
+ }
+ else {
+ /* 3rd or more -- just keep stacking the names */
+ ic->names = nc;
+ }
}
/* compile the tables and such we need to do the run-time vhost lookups */
@@ -526,9 +558,6 @@ AP_DECLARE(void) ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
int i;
ipaddr_chain **iphash_table_tail[IPHASH_TABLE_SIZE];
- /* terminate the name_vhost list */
- *name_vhost_list_tail = NULL;
-
/* Main host first */
s = main_s;
@@ -541,33 +570,6 @@ AP_DECLARE(void) ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
iphash_table_tail[i] = &iphash_table[i];
}
- /* The first things to go into the hash table are the NameVirtualHosts
- * Since name_vhost_list is in the same order that the directives
- * occured in the config file, we'll copy it in that order.
- */
- for (sar = name_vhost_list; sar; sar = sar->next) {
- char inaddr_any[16] = {0}; /* big enough to handle IPv4 or IPv6 */
- unsigned bucket = hash_addr(sar->host_addr);
- ipaddr_chain *ic = new_ipaddr_chain(p, NULL, sar);
-
- if (memcmp(sar->host_addr->ipaddr_ptr, inaddr_any,
- sar->host_addr->ipaddr_len)) { /* not IN[6]ADDR_ANY */
- *iphash_table_tail[bucket] = ic;
- iphash_table_tail[bucket] = &ic->next;
- }
- else {
- /* A wildcard NameVirtualHost goes on the default_list so
- * that it can catch incoming requests on any address.
- */
- ic->next = default_list;
- default_list = ic;
- }
- /* Notice that what we've done is insert an ipaddr_chain with
- * both server and names NULL. This fact is used to spot name-
- * based vhosts in add_name_vhost_config().
- */
- }
-
/* The next things to go into the hash table are the virtual hosts
* themselves. They're listed off of main_s->next in the reverse
* order they occured in the config file, so we insert them at
@@ -580,17 +582,13 @@ AP_DECLARE(void) ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
ipaddr_chain *ic;
char inaddr_any[16] = {0}; /* big enough to handle IPv4 or IPv6 */
- if ((sar->host_addr->family == AF_INET &&
- sar->host_addr->sa.sin.sin_addr.s_addr == DEFAULT_VHOST_ADDR)
- || !memcmp(sar->host_addr->ipaddr_ptr, inaddr_any, sar->host_addr->ipaddr_len)) {
+ if (!memcmp(sar->host_addr->ipaddr_ptr, inaddr_any, sar->host_addr->ipaddr_len)) {
ic = find_default_server(sar->host_port);
- if (!ic || !add_name_vhost_config(p, main_s, s, sar, ic)) {
- if (ic && ic->sar->host_port != 0) {
- ap_log_error(APLOG_MARK, APLOG_WARNING,
- 0, main_s, "_default_ VirtualHost "
- "overlap on port %u, the first has "
- "precedence", sar->host_port);
- }
+ if (!ic || sar->host_port != ic->sar->host_port) {
+ /* No default server, or we found a default server but
+ ** exactly one of us is a wildcard port, which means we want
+ ** two ip-based vhosts not an NVH with two names
+ */
ic = new_ipaddr_chain(p, s, sar);
ic->next = default_list;
default_list = ic;
@@ -601,25 +599,18 @@ AP_DECLARE(void) ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
/* see if it matches something we've already got */
ic = find_ipaddr(sar->host_addr);
- if (!ic) {
+ if (!ic || sar->host_port != ic->sar->host_port) {
+ /* No matching server, or we found a matching server but
+ ** exactly one of us is a wildcard port, which means we want
+ ** two ip-based vhosts not an NVH with two names
+ */
unsigned bucket = hash_addr(sar->host_addr);
-
ic = new_ipaddr_chain(p, s, sar);
ic->next = *iphash_table_tail[bucket];
*iphash_table_tail[bucket] = ic;
}
- else if (!add_name_vhost_config(p, main_s, s, sar, ic)) {
- ap_log_error(APLOG_MARK, APLOG_WARNING,
- 0, main_s, "VirtualHost %s:%u overlaps "
- "with VirtualHost %s:%u, the first has "
- "precedence, perhaps you need a "
- "NameVirtualHost directive",
- sar->virthost, sar->host_port,
- ic->sar->virthost, ic->sar->host_port);
- ic->sar = sar;
- ic->server = s;
- }
}
+ add_name_vhost_config(p, main_s, s, sar, ic);
}
/* Ok now we want to set up a server_hostname if the user was
@@ -652,7 +643,7 @@ AP_DECLARE(void) ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
char *ipaddr_str;
apr_sockaddr_ip_get(&ipaddr_str, s->addrs->host_addr);
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, main_s,
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, main_s, APLOGNO(00549)
"Failed to resolve server name "
"for %s (check DNS) -- or specify an explicit "
"ServerName",
@@ -664,14 +655,6 @@ AP_DECLARE(void) ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
}
}
- /* now go through and delete any NameVirtualHosts that didn't have any
- * hosts associated with them. Lamers.
- */
- for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
- remove_unused_name_vhosts(main_s, &iphash_table[i]);
- }
- remove_unused_name_vhosts(main_s, &default_list);
-
#ifdef IPHASH_STATISTICS
dump_iphash_statistics(main_s);
#endif
@@ -682,6 +665,11 @@ AP_DECLARE(void) ap_fini_vhost_config(apr_pool_t *p, server_rec *main_s)
}
}
+static int vhost_check_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ return config_error ? !OK : OK;
+}
/*****************************************************************************
* run-time vhost matching functions
@@ -766,7 +754,7 @@ static void fix_hostname(request_rec *r)
bad:
r->status = HTTP_BAD_REQUEST;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00550)
"Client sent malformed Host header");
return;
}
@@ -872,9 +860,11 @@ static void check_hostalias(request_rec *r)
const char *host = r->hostname;
apr_port_t port;
server_rec *s;
+ server_rec *virthost_s;
server_rec *last_s;
name_chain *src;
+ virthost_s = NULL;
last_s = NULL;
port = r->connection->local_addr->port;
@@ -901,23 +891,34 @@ static void check_hostalias(request_rec *r)
s = src->server;
- /* does it match the virthost from the sar? */
- if (!strcasecmp(host, sar->virthost)) {
- goto found;
- }
-
- if (s == last_s) {
- /* we've already done ServerName and ServerAlias checks for this
- * vhost
- */
- continue;
+ /* If we still need to do ServerName and ServerAlias checks for this
+ * server, do them now.
+ */
+ if (s != last_s) {
+ /* does it match any ServerName or ServerAlias directive? */
+ if (matches_aliases(s, host)) {
+ goto found;
+ }
}
last_s = s;
- if (matches_aliases(s, host)) {
- goto found;
+ /* Fallback: does it match the virthost from the sar? */
+ if (!strcasecmp(host, sar->virthost)) {
+ /* only the first match is used */
+ if (virthost_s == NULL) {
+ virthost_s = s;
+ }
}
}
+
+ /* If ServerName and ServerAlias check failed, we end up here. If it
+ * matches a VirtualHost, virthost_s is set. Use that as fallback
+ */
+ if (virthost_s) {
+ s = virthost_s;
+ goto found;
+ }
+
return;
found: