From d5e8fc0a7026f74e89fabf3333d97e6d6928035a Mon Sep 17 00:00:00 2001 From: Benedikt Boehm Date: Fri, 7 Sep 2007 21:47:54 +0000 Subject: update for 2.0.61/2.2.6; backport init script from 2.2 --- 2.2/patches/04_all_mod_dbd_r553563.patch | 1279 ----------- 2.2/patches/20_all_peruser_0.2.1.patch | 3268 ---------------------------- 2.2/patches/20_all_peruser_0.2.2.patch | 3392 ++++++++++++++++++++++++++++++ 2.2/patches/21_all_itk_20070425-00.patch | 18 +- 2.2/patches/80_all_CVE-2006-5752.patch | 40 - 2.2/patches/81_all_CVE-2007-1862.patch | 51 - 2.2/patches/82_all_CVE-2007-1863.patch | 74 - 2.2/patches/83_all_CVE-2007-3304.patch | 261 --- 8 files changed, 3399 insertions(+), 4984 deletions(-) delete mode 100644 2.2/patches/04_all_mod_dbd_r553563.patch delete mode 100644 2.2/patches/20_all_peruser_0.2.1.patch create mode 100644 2.2/patches/20_all_peruser_0.2.2.patch delete mode 100644 2.2/patches/80_all_CVE-2006-5752.patch delete mode 100644 2.2/patches/81_all_CVE-2007-1862.patch delete mode 100644 2.2/patches/82_all_CVE-2007-1863.patch delete mode 100644 2.2/patches/83_all_CVE-2007-3304.patch (limited to '2.2/patches') diff --git a/2.2/patches/04_all_mod_dbd_r553563.patch b/2.2/patches/04_all_mod_dbd_r553563.patch deleted file mode 100644 index 51dd29b..0000000 --- a/2.2/patches/04_all_mod_dbd_r553563.patch +++ /dev/null @@ -1,1279 +0,0 @@ -diff -NurpP httpd-2.2.4.orig/modules/database/mod_dbd.c httpd-2.2.4/modules/database/mod_dbd.c ---- httpd-2.2.4.orig/modules/database/mod_dbd.c 2006-12-12 15:17:17.000000000 +0100 -+++ httpd-2.2.4/modules/database/mod_dbd.c 2007-08-19 15:47:47.822568162 +0200 -@@ -20,15 +20,21 @@ - * http://apache.webthing.com/database/ - */ - --#include -+#include "apr_reslist.h" -+#include "apr_strings.h" -+#include "apr_hash.h" -+#include "apr_tables.h" -+#include "apr_lib.h" -+#include "apr_dbd.h" -+ -+#define APR_WANT_MEMFUNC -+#define APR_WANT_STRFUNC -+#include "apr_want.h" - - #include "http_protocol.h" - #include "http_config.h" - #include "http_log.h" - #include "http_request.h" --#include "apr_reslist.h" --#include "apr_strings.h" --#include "apr_dbd.h" - #include "mod_dbd.h" - - extern module AP_MODULE_DECLARE_DATA dbd_module; -@@ -40,126 +46,199 @@ extern module AP_MODULE_DECLARE_DATA dbd - #define NMAX_SET 0x4 - #define EXPTIME_SET 0x8 - --typedef struct dbd_prepared { -- const char *label; -- const char *query; -- struct dbd_prepared *next; --} dbd_prepared; --typedef struct svr_cfg { -+typedef struct { -+ server_rec *server; - const char *name; - const char *params; - int persist; -- dbd_prepared *prepared; - #if APR_HAS_THREADS -- apr_thread_mutex_t *mutex; -- apr_pool_t *pool; -- apr_reslist_t *dbpool; - int nmin; - int nkeep; - int nmax; - int exptime; -+ int set; -+#endif -+ apr_hash_t *queries; -+} dbd_cfg_t; -+ -+typedef struct dbd_group_t dbd_group_t; -+ -+struct dbd_group_t { -+ dbd_cfg_t *cfg; -+ dbd_group_t *next; -+ apr_pool_t *pool; -+#if APR_HAS_THREADS -+ apr_thread_mutex_t *mutex; -+ apr_reslist_t *reslist; -+ int destroyed; - #else -- ap_dbd_t *conn; -+ ap_dbd_t *rec; - #endif -- unsigned int set; -+}; -+ -+typedef struct { -+ dbd_cfg_t *cfg; -+ dbd_group_t *group; - } svr_cfg; - - typedef enum { cmd_name, cmd_params, cmd_persist, - cmd_min, cmd_keep, cmd_max, cmd_exp - } cmd_parts; - --static apr_hash_t *dbd_prepared_defns; -+static apr_pool_t *config_pool; -+static dbd_group_t *group_list; - - /* a default DBDriver value that'll generate meaningful error messages */ - static const char *const no_dbdriver = "[DBDriver unset]"; - --#define ISINT(val) \ -- for (p = val; *p; ++p) \ -- if (!isdigit(*p)) \ -- return "Argument must be numeric!" --static const char *dbd_param(cmd_parms *cmd, void *cfg, const char *val) -+/* A default nmin of >0 will help with generating meaningful -+ * startup error messages if the database is down. -+ */ -+#define DEFAULT_NMIN 1 -+#define DEFAULT_NKEEP 2 -+#define DEFAULT_NMAX 10 -+#define DEFAULT_EXPTIME 300 -+ -+static void *create_dbd_config(apr_pool_t *pool, server_rec *s) -+{ -+ svr_cfg *svr = apr_pcalloc(pool, sizeof(svr_cfg)); -+ dbd_cfg_t *cfg = svr->cfg = apr_pcalloc(pool, sizeof(dbd_cfg_t)); -+ -+ cfg->server = s; -+ cfg->name = no_dbdriver; /* to generate meaningful error messages */ -+ cfg->params = ""; /* don't risk segfault on misconfiguration */ -+ cfg->persist = -1; -+#if APR_HAS_THREADS -+ cfg->nmin = DEFAULT_NMIN; -+ cfg->nkeep = DEFAULT_NKEEP; -+ cfg->nmax = DEFAULT_NMAX; -+ cfg->exptime = DEFAULT_EXPTIME; -+#endif -+ cfg->queries = apr_hash_make(pool); -+ -+ return svr; -+} -+ -+static void *merge_dbd_config(apr_pool_t *pool, void *basev, void *addv) -+{ -+ dbd_cfg_t *base = ((svr_cfg*) basev)->cfg; -+ dbd_cfg_t *add = ((svr_cfg*) addv)->cfg; -+ svr_cfg *svr = apr_pcalloc(pool, sizeof(svr_cfg)); -+ dbd_cfg_t *new = svr->cfg = apr_pcalloc(pool, sizeof(dbd_cfg_t)); -+ -+ new->server = add->server; -+ new->name = (add->name != no_dbdriver) ? add->name : base->name; -+ new->params = strcmp(add->params, "") ? add->params : base->params; -+ new->persist = (add->persist != -1) ? add->persist : base->persist; -+#if APR_HAS_THREADS -+ new->nmin = (add->set&NMIN_SET) ? add->nmin : base->nmin; -+ new->nkeep = (add->set&NKEEP_SET) ? add->nkeep : base->nkeep; -+ new->nmax = (add->set&NMAX_SET) ? add->nmax : base->nmax; -+ new->exptime = (add->set&EXPTIME_SET) ? add->exptime : base->exptime; -+#endif -+ new->queries = apr_hash_overlay(pool, add->queries, base->queries); -+ -+ return svr; -+} -+ -+#define ISINT(val) do { \ -+ const char *p; \ -+ \ -+ for (p = val; *p; ++p) { \ -+ if (!apr_isdigit(*p)) { \ -+ return "Argument must be numeric!"; \ -+ } \ -+ } \ -+ } while (0) -+ -+static const char *dbd_param(cmd_parms *cmd, void *dconf, const char *val) - { -- const char *p; - const apr_dbd_driver_t *driver = NULL; -- svr_cfg *svr = (svr_cfg*) ap_get_module_config -- (cmd->server->module_config, &dbd_module); -+ svr_cfg *svr = ap_get_module_config(cmd->server->module_config, -+ &dbd_module); -+ dbd_cfg_t *cfg = svr->cfg; - - switch ((long) cmd->info) { - case cmd_name: -- svr->name = val; -+ cfg->name = val; - /* loading the driver involves once-only dlloading that is - * best done at server startup. This also guarantees that - * we won't return an error later. - */ -- switch (apr_dbd_get_driver(cmd->pool, svr->name, &driver)) { -+ switch (apr_dbd_get_driver(cmd->pool, cfg->name, &driver)) { - case APR_ENOTIMPL: -- return apr_psprintf(cmd->pool, "DBD: No driver for %s", svr->name); -+ return apr_psprintf(cmd->pool, "DBD: No driver for %s", cfg->name); - case APR_EDSOOPEN: - return apr_psprintf(cmd->pool, -+#ifdef NETWARE -+ "DBD: Can't load driver file dbd%s.nlm", -+#else - "DBD: Can't load driver file apr_dbd_%s.so", -- svr->name); -+#endif -+ cfg->name); - case APR_ESYMNOTFOUND: - return apr_psprintf(cmd->pool, - "DBD: Failed to load driver apr_dbd_%s_driver", -- svr->name); -+ cfg->name); - } - break; - case cmd_params: -- svr->params = val; -+ cfg->params = val; - break; - #if APR_HAS_THREADS - case cmd_min: - ISINT(val); -- svr->nmin = atoi(val); -- svr->set |= NMIN_SET; -+ cfg->nmin = atoi(val); -+ cfg->set |= NMIN_SET; - break; - case cmd_keep: - ISINT(val); -- svr->nkeep = atoi(val); -- svr->set |= NKEEP_SET; -+ cfg->nkeep = atoi(val); -+ cfg->set |= NKEEP_SET; - break; - case cmd_max: - ISINT(val); -- svr->nmax = atoi(val); -- svr->set |= NMAX_SET; -+ cfg->nmax = atoi(val); -+ cfg->set |= NMAX_SET; - break; - case cmd_exp: - ISINT(val); -- svr->exptime = atoi(val); -- svr->set |= EXPTIME_SET; -+ cfg->exptime = atoi(val); -+ cfg->set |= EXPTIME_SET; - break; - #endif - } -+ - return NULL; - } --static const char *dbd_param_flag(cmd_parms *cmd, void *cfg, int flag) -+ -+static const char *dbd_param_flag(cmd_parms *cmd, void *dconf, int flag) - { -- svr_cfg *svr = (svr_cfg*) ap_get_module_config -- (cmd->server->module_config, &dbd_module); -+ svr_cfg *svr = ap_get_module_config(cmd->server->module_config, -+ &dbd_module); - - switch ((long) cmd->info) { - case cmd_persist: -- svr->persist = flag; -+ svr->cfg->persist = flag; - break; - } -+ - return NULL; - } --DBD_DECLARE_NONSTD(void) ap_dbd_prepare(server_rec *s, const char *query, -- const char *label) --{ -- dbd_prepared *prepared = apr_pcalloc(s->process->pool, sizeof(dbd_prepared)); -- const char *key = apr_psprintf(s->process->pool, "%pp", s); -- prepared->label = label; -- prepared->query = query; -- prepared->next = apr_hash_get(dbd_prepared_defns, key, APR_HASH_KEY_STRING); -- apr_hash_set(dbd_prepared_defns, key, APR_HASH_KEY_STRING, prepared); --} --static const char *dbd_prepare(cmd_parms *cmd, void *cfg, const char *query, -+ -+static const char *dbd_prepare(cmd_parms *cmd, void *dconf, const char *query, - const char *label) - { -+ if (!label) { -+ label = query; -+ query = ""; -+ } -+ - ap_dbd_prepare(cmd->server, query, label); -+ - return NULL; - } -+ - static const command_rec dbd_cmds[] = { - AP_INIT_TAKE1("DBDriver", dbd_param, (void*)cmd_name, RSRC_CONF, - "SQL Driver"), -@@ -167,8 +246,9 @@ static const command_rec dbd_cmds[] = { - "SQL Driver Params"), - AP_INIT_FLAG("DBDPersist", dbd_param_flag, (void*)cmd_persist, RSRC_CONF, - "Use persistent connection/pool"), -- AP_INIT_TAKE2("DBDPrepareSQL", dbd_prepare, NULL, RSRC_CONF, -- "Prepared SQL statement, label"), -+ AP_INIT_TAKE12("DBDPrepareSQL", dbd_prepare, NULL, RSRC_CONF, -+ "SQL statement to prepare (or nothing, to override " -+ "statement inherited from main server) and label"), - #if APR_HAS_THREADS - AP_INIT_TAKE1("DBDMin", dbd_param, (void*)cmd_min, RSRC_CONF, - "Minimum number of connections"), -@@ -183,370 +263,545 @@ static const command_rec dbd_cmds[] = { - #endif - {NULL} - }; --static void *dbd_merge(apr_pool_t *pool, void *BASE, void *ADD) { -- svr_cfg *base = (svr_cfg*) BASE; -- svr_cfg *add = (svr_cfg*) ADD; -- svr_cfg *cfg = apr_pcalloc(pool, sizeof(svr_cfg)); -- cfg->name = (add->name != no_dbdriver) ? add->name : base->name; -- cfg->params = strcmp(add->params, "") ? add->params : base->params; -- cfg->persist = (add->persist == -1) ? base->persist : add->persist; --#if APR_HAS_THREADS -- cfg->nmin = (add->set&NMIN_SET) ? add->nmin : base->nmin; -- cfg->nkeep = (add->set&NKEEP_SET) ? add->nkeep : base->nkeep; -- cfg->nmax = (add->set&NMAX_SET) ? add->nmax : base->nmax; -- cfg->exptime = (add->set&EXPTIME_SET) ? add->exptime : base->exptime; --#endif -- cfg->set = add->set | base->set; -- cfg->prepared = (add->prepared != NULL) ? add->prepared : base->prepared; -- return (void*) cfg; -+ -+static int dbd_pre_config(apr_pool_t *pconf, apr_pool_t *plog, -+ apr_pool_t *ptemp) -+{ -+ config_pool = pconf; -+ group_list = NULL; -+ return OK; - } --/* A default nmin of >0 will help with generating meaningful -- * startup error messages if the database is down. -- */ --#define DEFAULT_NMIN 1 --#define DEFAULT_NKEEP 2 --#define DEFAULT_NMAX 10 --#define DEFAULT_EXPTIME 300 --static void *dbd_cfg(apr_pool_t *p, server_rec *x) -+ -+DBD_DECLARE_NONSTD(void) ap_dbd_prepare(server_rec *s, const char *query, -+ const char *label) -+{ -+ svr_cfg *svr; -+ -+ svr = ap_get_module_config(s->module_config, &dbd_module); -+ if (!svr) { -+ /* some modules may call from within config directive handlers, and -+ * if these are called in a server context that contains no mod_dbd -+ * config directives, then we have to create our own server config -+ */ -+ svr = create_dbd_config(config_pool, s); -+ ap_set_module_config(s->module_config, &dbd_module, svr); -+ } -+ -+ if (apr_hash_get(svr->cfg->queries, label, APR_HASH_KEY_STRING) -+ && strcmp(query, "")) { -+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, -+ "conflicting SQL statements with label %s", label); -+ } -+ -+ apr_hash_set(svr->cfg->queries, label, APR_HASH_KEY_STRING, query); -+} -+ -+typedef struct { -+ const char *label, *query; -+} dbd_query_t; -+ -+static int dbd_post_config(apr_pool_t *pconf, apr_pool_t *plog, -+ apr_pool_t *ptemp, server_rec *s) - { -- svr_cfg *svr = (svr_cfg*) apr_pcalloc(p, sizeof(svr_cfg)); -- svr->params = ""; /* don't risk segfault on misconfiguration */ -- svr->name = no_dbdriver; /* to generate meaningful error messages */ -- svr->persist = -1; --#if APR_HAS_THREADS -- svr->nmin = DEFAULT_NMIN; -- svr->nkeep = DEFAULT_NKEEP; -- svr->nmax = DEFAULT_NMAX; -- svr->exptime = DEFAULT_EXPTIME; -+ server_rec *sp; -+ apr_array_header_t *add_queries = apr_array_make(ptemp, 10, -+ sizeof(dbd_query_t)); -+ -+ for (sp = s; sp; sp = sp->next) { -+ svr_cfg *svr = ap_get_module_config(sp->module_config, &dbd_module); -+ dbd_cfg_t *cfg = svr->cfg; -+ apr_hash_index_t *hi_first = apr_hash_first(ptemp, cfg->queries); -+ dbd_group_t *group; -+ -+ /* dbd_setup in 2.2.3 and under was causing spurious error messages -+ * when dbd isn't configured. We can stop that with a quick check here -+ * together with a similar check in ap_dbd_open (where being -+ * unconfigured is a genuine error that must be reported). -+ */ -+ if (cfg->name == no_dbdriver || !cfg->persist) { -+ continue; -+ } -+ -+ for (group = group_list; group; group = group->next) { -+ dbd_cfg_t *group_cfg = group->cfg; -+ apr_hash_index_t *hi; -+ int group_ok = 1; -+ -+ if (strcmp(cfg->name, group_cfg->name) -+ || strcmp(cfg->params, group_cfg->params)) { -+ continue; -+ } -+ -+#if APR_HAS_THREADS -+ if (cfg->nmin != group_cfg->nmin -+ || cfg->nkeep != group_cfg->nkeep -+ || cfg->nmax != group_cfg->nmax -+ || cfg->exptime != group_cfg->exptime) { -+ continue; -+ } - #endif -- return svr; -+ -+ add_queries->nelts = 0; -+ -+ for (hi = hi_first; hi; hi = apr_hash_next(hi)) { -+ const char *label, *query; -+ const char *group_query; -+ -+ apr_hash_this(hi, (void*) &label, NULL, (void*) &query); -+ -+ group_query = apr_hash_get(group_cfg->queries, label, -+ APR_HASH_KEY_STRING); -+ -+ if (!group_query) { -+ dbd_query_t *add_query = apr_array_push(add_queries); -+ -+ add_query->label = label; -+ add_query->query = query; -+ } -+ else if (strcmp(query, group_query)) { -+ group_ok = 0; -+ break; -+ } -+ } -+ -+ if (group_ok) { -+ int i; -+ -+ for (i = 0; i < add_queries->nelts; ++i) { -+ dbd_query_t *add_query = ((dbd_query_t*) add_queries->elts) -+ + i; -+ -+ apr_hash_set(group_cfg->queries, add_query->label, -+ APR_HASH_KEY_STRING, add_query->query); -+ } -+ -+ svr->group = group; -+ break; -+ } -+ } -+ -+ if (!svr->group) { -+ svr->group = group = apr_pcalloc(pconf, sizeof(dbd_group_t)); -+ -+ group->cfg = cfg; -+ -+ group->next = group_list; -+ group_list = group; -+ } -+ } -+ -+ return OK; - } --static apr_status_t dbd_prepared_init(apr_pool_t *pool, svr_cfg *svr, -- ap_dbd_t *dbd) -+ -+static apr_status_t dbd_prepared_init(apr_pool_t *pool, dbd_cfg_t *cfg, -+ ap_dbd_t *rec) - { -- dbd_prepared *p; -- apr_status_t ret = APR_SUCCESS; -- apr_dbd_prepared_t *stmt; -- dbd->prepared = apr_hash_make(pool); -+ apr_hash_index_t *hi; -+ apr_status_t rv = APR_SUCCESS; -+ -+ rec->prepared = apr_hash_make(pool); -+ -+ for (hi = apr_hash_first(pool, cfg->queries); hi; -+ hi = apr_hash_next(hi)) { -+ const char *label, *query; -+ apr_dbd_prepared_t *stmt; -+ -+ apr_hash_this(hi, (void*) &label, NULL, (void*) &query); -+ -+ if (!strcmp(query, "")) { -+ continue; -+ } - -- for (p = svr->prepared; p; p = p->next) { - stmt = NULL; -- if (apr_dbd_prepare(dbd->driver, pool, dbd->handle, p->query, -- p->label, &stmt) == 0) { -- apr_hash_set(dbd->prepared, p->label, APR_HASH_KEY_STRING, stmt); -+ if (apr_dbd_prepare(rec->driver, pool, rec->handle, query, -+ label, &stmt)) { -+ rv = APR_EGENERAL; - } - else { -- ret = APR_EGENERAL; -+ apr_hash_set(rec->prepared, label, APR_HASH_KEY_STRING, stmt); - } - } -- return ret; -+ -+ return rv; - } --/************ svr cfg: manage db connection pool ****************/ -+ -+static apr_status_t dbd_close(void *data) -+{ -+ ap_dbd_t *rec = data; -+ -+ return apr_dbd_close(rec->driver, rec->handle); -+} -+ -+#if APR_HAS_THREADS -+static apr_status_t dbd_destruct(void *data, void *params, apr_pool_t *pool) -+{ -+ dbd_group_t *group = params; -+ -+ if (!group->destroyed) { -+ ap_dbd_t *rec = data; -+ -+ apr_pool_destroy(rec->pool); -+ } -+ -+ return APR_SUCCESS; -+} -+#endif -+ - /* an apr_reslist_constructor for SQL connections - * Also use this for opening in non-reslist modes, since it gives - * us all the error-handling in one place. - */ --static apr_status_t dbd_construct(void **db, void *params, apr_pool_t *pool) -+static apr_status_t dbd_construct(void **data_ptr, -+ void *params, apr_pool_t *pool) - { -- svr_cfg *svr = (svr_cfg*) params; -- ap_dbd_t *rec = apr_pcalloc(pool, sizeof(ap_dbd_t)); -+ dbd_group_t *group = params; -+ dbd_cfg_t *cfg = group->cfg; -+ apr_pool_t *rec_pool, *prepared_pool; -+ ap_dbd_t *rec; - apr_status_t rv; - -- /* this pool is mostly so dbd_close can destroy the prepared stmts */ -- rv = apr_pool_create(&rec->pool, pool); -+ rv = apr_pool_create(&rec_pool, pool); - if (rv != APR_SUCCESS) { -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, pool, -- "DBD: Failed to create memory pool"); -+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, cfg->server, -+ "DBD: Failed to create memory pool"); -+ return rv; - } - --/* The driver is loaded at config time now, so this just checks a hash. -- * If that changes, the driver DSO could be registered to unload against -- * our pool, which is probably not what we want. Error checking isn't -- * necessary now, but in case that changes in the future ... -- */ -- rv = apr_dbd_get_driver(rec->pool, svr->name, &rec->driver); -- switch (rv) { -- case APR_ENOTIMPL: -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, rec->pool, -- "DBD: driver for %s not available", svr->name); -- return rv; -- case APR_EDSOOPEN: -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, rec->pool, -- "DBD: can't find driver for %s", svr->name); -- return rv; -- case APR_ESYMNOTFOUND: -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, rec->pool, -- "DBD: driver for %s is invalid or corrupted", svr->name); -- return rv; -- default: -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, rec->pool, -- "DBD: mod_dbd not compatible with apr in get_driver"); -+ rec = apr_pcalloc(rec_pool, sizeof(ap_dbd_t)); -+ -+ rec->pool = rec_pool; -+ -+ /* The driver is loaded at config time now, so this just checks a hash. -+ * If that changes, the driver DSO could be registered to unload against -+ * our pool, which is probably not what we want. Error checking isn't -+ * necessary now, but in case that changes in the future ... -+ */ -+ rv = apr_dbd_get_driver(rec->pool, cfg->name, &rec->driver); -+ if (rv != APR_SUCCESS) { -+ switch (rv) { -+ case APR_ENOTIMPL: -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server, -+ "DBD: driver for %s not available", cfg->name); -+ break; -+ case APR_EDSOOPEN: -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server, -+ "DBD: can't find driver for %s", cfg->name); -+ break; -+ case APR_ESYMNOTFOUND: -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server, -+ "DBD: driver for %s is invalid or corrupted", -+ cfg->name); -+ break; -+ default: -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server, -+ "DBD: mod_dbd not compatible with APR in get_driver"); -+ break; -+ } -+ -+ apr_pool_destroy(rec->pool); - return rv; -- case APR_SUCCESS: -- break; - } - -- rv = apr_dbd_open(rec->driver, rec->pool, svr->params, &rec->handle); -- switch (rv) { -- case APR_EGENERAL: -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, rec->pool, -- "DBD: Can't connect to %s", svr->name); -+ rv = apr_dbd_open(rec->driver, rec->pool, cfg->params, &rec->handle); -+ if (rv != APR_SUCCESS) { -+ switch (rv) { -+ case APR_EGENERAL: -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server, -+ "DBD: Can't connect to %s", cfg->name); -+ break; -+ default: -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server, -+ "DBD: mod_dbd not compatible with APR in open"); -+ break; -+ } -+ -+ apr_pool_destroy(rec->pool); - return rv; -- default: -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, rec->pool, -- "DBD: mod_dbd not compatible with apr in open"); -+ } -+ -+ apr_pool_cleanup_register(rec->pool, rec, dbd_close, -+ apr_pool_cleanup_null); -+ -+ /* we use a sub-pool for the prepared statements for each connection so -+ * that they will be cleaned up first, before the connection is closed -+ */ -+ rv = apr_pool_create(&prepared_pool, rec->pool); -+ if (rv != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, cfg->server, -+ "DBD: Failed to create memory pool"); -+ -+ apr_pool_destroy(rec->pool); - return rv; -- case APR_SUCCESS: -- break; - } -- *db = rec; -- rv = dbd_prepared_init(rec->pool, svr, rec); -+ -+ rv = dbd_prepared_init(prepared_pool, cfg, rec); - if (rv != APR_SUCCESS) { - const char *errmsg = apr_dbd_error(rec->driver, rec->handle, rv); -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, rec->pool, -- "DBD: failed to initialise prepared SQL statements: %s", -- (errmsg ? errmsg : "[???]")); -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, cfg->server, -+ "DBD: failed to prepare SQL statements: %s", -+ (errmsg ? errmsg : "[???]")); -+ -+ apr_pool_destroy(rec->pool); -+ return rv; - } -- return rv; --} --static apr_status_t dbd_close(void *CONN) --{ -- ap_dbd_t *conn = CONN; -- apr_status_t rv = apr_dbd_close(conn->driver, conn->handle); -- apr_pool_destroy(conn->pool); -- return rv; -+ -+ *data_ptr = rec; -+ -+ return APR_SUCCESS; - } -+ - #if APR_HAS_THREADS --static apr_status_t dbd_destruct(void *sql, void *params, apr_pool_t *pool) -+static apr_status_t dbd_destroy(void *data) - { -- return dbd_close(sql); -+ dbd_group_t *group = data; -+ -+ group->destroyed = 1; -+ -+ return APR_SUCCESS; - } - --static apr_status_t dbd_setup(apr_pool_t *pool, svr_cfg *svr) -+static apr_status_t dbd_setup(server_rec *s, dbd_group_t *group) - { -+ dbd_cfg_t *cfg = group->cfg; - apr_status_t rv; - -- /* create a pool just for the reslist from a process-lifetime pool; -- * that pool (s->process->pool in the dbd_setup_lock case, -- * whatever was passed to ap_run_child_init in the dbd_setup_init case) -- * will be shared with other threads doing other non-mod_dbd things -- * so we can't use it for the reslist directly -+ /* We create the reslist using a sub-pool of the pool passed to our -+ * child_init hook. No other threads can be here because we're -+ * either in the child_init phase or dbd_setup_lock() acquired our mutex. -+ * No other threads will use this sub-pool after this, except via -+ * reslist calls, which have an internal mutex. -+ * -+ * We need to short-circuit the cleanup registered internally by -+ * apr_reslist_create(). We do this by registering dbd_destroy() -+ * as a cleanup afterwards, so that it will run before the reslist's -+ * internal cleanup. -+ * -+ * If we didn't do this, then we could free memory twice when the pool -+ * was destroyed. When apr_pool_destroy() runs, it first destroys all -+ * all the per-connection sub-pools created in dbd_construct(), and -+ * then it runs the reslist's cleanup. The cleanup calls dbd_destruct() -+ * on each resource, which would then attempt to destroy the sub-pools -+ * a second time. - */ -- rv = apr_pool_create(&svr->pool, pool); -+ rv = apr_reslist_create(&group->reslist, -+ cfg->nmin, cfg->nkeep, cfg->nmax, -+ apr_time_from_sec(cfg->exptime), -+ dbd_construct, dbd_destruct, group, -+ group->pool); - if (rv != APR_SUCCESS) { -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, pool, -- "DBD: Failed to create reslist memory pool"); -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, -+ "DBD: failed to initialise"); - return rv; - } - -- rv = apr_reslist_create(&svr->dbpool, svr->nmin, svr->nkeep, svr->nmax, -- apr_time_from_sec(svr->exptime), -- dbd_construct, dbd_destruct, svr, svr->pool); -- if (rv == APR_SUCCESS) { -- apr_pool_cleanup_register(svr->pool, svr->dbpool, -- (void*)apr_reslist_destroy, -- apr_pool_cleanup_null); -- } -- else { -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, svr->pool, -- "DBD: failed to initialise"); -- apr_pool_destroy(svr->pool); -- svr->pool = NULL; -- } -+ apr_pool_cleanup_register(group->pool, group, dbd_destroy, -+ apr_pool_cleanup_null); - -- return rv; -+ return APR_SUCCESS; - } -+#endif -+ - static apr_status_t dbd_setup_init(apr_pool_t *pool, server_rec *s) - { -- svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module); -- apr_status_t rv; -+ dbd_group_t *group; -+ apr_status_t rv = APR_SUCCESS; - -- /* dbd_setup in 2.2.3 and under was causing spurious error messages -- * when dbd isn't configured. We can stop that with a quick check here -- * together with a similar check in ap_dbd_open (where being -- * unconfigured is a genuine error that must be reported). -- */ -- if (svr->name == no_dbdriver) { -- return APR_SUCCESS; -- } -+ for (group = group_list; group; group = group->next) { -+ apr_status_t rv2; - -- if (!svr->persist) { -- return APR_SUCCESS; -- } -+ rv2 = apr_pool_create(&group->pool, pool); -+ if (rv2 != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv2, s, -+ "DBD: Failed to create reslist cleanup memory pool"); -+ return rv2; -+ } - -- rv = dbd_setup(pool, svr); -- if (rv == APR_SUCCESS) { -- return rv; -- } -+#if APR_HAS_THREADS -+ rv2 = dbd_setup(s, group); -+ if (rv2 == APR_SUCCESS) { -+ continue; -+ } -+ else if (rv == APR_SUCCESS) { -+ rv = rv2; -+ } - -- /* we failed, so create a mutex so that subsequent competing callers -- * to ap_dbd_open can serialize themselves while they retry -- */ -- rv = apr_thread_mutex_create(&svr->mutex, APR_THREAD_MUTEX_DEFAULT, pool); -- if (rv != APR_SUCCESS) { -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, pool, -- "DBD: Failed to create thread mutex"); -+ /* we failed, so create a mutex so that subsequent competing callers -+ * to ap_dbd_open can serialize themselves while they retry -+ */ -+ rv2 = apr_thread_mutex_create(&group->mutex, -+ APR_THREAD_MUTEX_DEFAULT, pool); -+ if (rv2 != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv2, s, -+ "DBD: Failed to create thread mutex"); -+ return rv2; -+ } -+#endif - } -+ - return rv; - } --static apr_status_t dbd_setup_lock(apr_pool_t *pool, server_rec *s) -+ -+#if APR_HAS_THREADS -+static apr_status_t dbd_setup_lock(server_rec *s, dbd_group_t *group) - { -- svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module); -- apr_status_t rv, rv2 = APR_SUCCESS; -+ apr_status_t rv = APR_SUCCESS, rv2; - - /* several threads could be here at the same time, all trying to - * initialize the reslist because dbd_setup_init failed to do so - */ -- if (!svr->mutex) { -+ if (!group->mutex) { - /* we already logged an error when the mutex couldn't be created */ - return APR_EGENERAL; - } - -- rv = apr_thread_mutex_lock(svr->mutex); -- if (rv != APR_SUCCESS) { -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, pool, -- "DBD: Failed to acquire thread mutex"); -- return rv; -- } -- -- if (!svr->dbpool) { -- rv2 = dbd_setup(s->process->pool, svr); -- } -- -- rv = apr_thread_mutex_unlock(svr->mutex); -- if (rv != APR_SUCCESS) { -- ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, pool, -- "DBD: Failed to release thread mutex"); -- if (rv2 == APR_SUCCESS) { -- rv2 = rv; -+ rv2 = apr_thread_mutex_lock(group->mutex); -+ if (rv2 != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv2, s, -+ "DBD: Failed to acquire thread mutex"); -+ return rv2; -+ } -+ -+ if (!group->reslist) { -+ rv = dbd_setup(s, group); -+ } -+ -+ rv2 = apr_thread_mutex_unlock(group->mutex); -+ if (rv2 != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv2, s, -+ "DBD: Failed to release thread mutex"); -+ if (rv == APR_SUCCESS) { -+ rv = rv2; - } - } -- return rv2; -+ -+ return rv; - } - #endif - -- - /* Functions we export for modules to use: - - open acquires a connection from the pool (opens one if necessary) - - close releases it back in to the pool - */ --DBD_DECLARE_NONSTD(void) ap_dbd_close(server_rec *s, ap_dbd_t *sql) -+DBD_DECLARE_NONSTD(void) ap_dbd_close(server_rec *s, ap_dbd_t *rec) - { - svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module); -- if (!svr->persist) { -- dbd_close((void*) sql); -+ -+ if (!svr->cfg->persist) { -+ apr_pool_destroy(rec->pool); - } - #if APR_HAS_THREADS - else { -- apr_reslist_release(svr->dbpool, sql); -+ apr_reslist_release(svr->group->reslist, rec); - } - #endif - } --#define arec ((ap_dbd_t*)rec) --#if APR_HAS_THREADS -+ -+static apr_status_t dbd_check(apr_pool_t *pool, server_rec *s, ap_dbd_t *rec) -+{ -+ svr_cfg *svr; -+ apr_status_t rv = apr_dbd_check_conn(rec->driver, pool, rec->handle); -+ const char *errmsg; -+ -+ if ((rv == APR_SUCCESS) || (rv == APR_ENOTIMPL)) { -+ return APR_SUCCESS; -+ } -+ -+ /* we don't have a driver-specific error code, so we'll just pass -+ * a "success" value and rely on the driver to ignore it -+ */ -+ errmsg = apr_dbd_error(rec->driver, rec->handle, 0); -+ if (!errmsg) { -+ errmsg = "(unknown)"; -+ } -+ -+ svr = ap_get_module_config(s->module_config, &dbd_module); -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, -+ "DBD [%s] Error: %s", svr->cfg->name, errmsg); -+ return rv; -+} -+ - DBD_DECLARE_NONSTD(ap_dbd_t*) ap_dbd_open(apr_pool_t *pool, server_rec *s) - { -- void *rec = NULL; - svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module); -- apr_status_t rv = APR_SUCCESS; -- const char *errmsg; -+ dbd_group_t *group = svr->group; -+ dbd_cfg_t *cfg = svr->cfg; -+ ap_dbd_t *rec = NULL; -+#if APR_HAS_THREADS -+ apr_status_t rv; -+#endif - - /* If nothing is configured, we shouldn't be here */ -- if (svr->name == no_dbdriver) { -- ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool, "DBD: not configured"); -+ if (cfg->name == no_dbdriver) { -+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "DBD: not configured"); - return NULL; - } - -- if (!svr->persist) { -+ if (!cfg->persist) { - /* Return a once-only connection */ -- rv = dbd_construct(&rec, svr, s->process->pool); -- return (rv == APR_SUCCESS) ? arec : NULL; -+ group = apr_pcalloc(pool, sizeof(dbd_group_t)); -+ -+ group->cfg = cfg; -+ -+ dbd_construct((void*) &rec, group, pool); -+ return rec; - } - -- if (!svr->dbpool) { -- if (dbd_setup_lock(pool, s) != APR_SUCCESS) { -+#if APR_HAS_THREADS -+ if (!group->reslist) { -+ if (dbd_setup_lock(s, group) != APR_SUCCESS) { - return NULL; - } - } -- rv = apr_reslist_acquire(svr->dbpool, &rec); -+ -+ rv = apr_reslist_acquire(group->reslist, (void*) &rec); - if (rv != APR_SUCCESS) { -- ap_log_perror(APLOG_MARK, APLOG_ERR, rv, pool, -- "Failed to acquire DBD connection from pool!"); -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, -+ "Failed to acquire DBD connection from pool!"); - return NULL; - } -- rv = apr_dbd_check_conn(arec->driver, pool, arec->handle); -- if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) { -- errmsg = apr_dbd_error(arec->driver, arec->handle, rv); -- if (!errmsg) { -- errmsg = "(unknown)"; -- } -- ap_log_perror(APLOG_MARK, APLOG_ERR, rv, pool, -- "DBD[%s] Error: %s", svr->name, errmsg ); -- apr_reslist_invalidate(svr->dbpool, rec); -+ -+ if (dbd_check(pool, s, rec) != APR_SUCCESS) { -+ apr_reslist_invalidate(group->reslist, rec); - return NULL; - } -- return arec; --} - #else --DBD_DECLARE_NONSTD(ap_dbd_t*) ap_dbd_open(apr_pool_t *pool, server_rec *s) --{ -- apr_status_t rv = APR_SUCCESS; -- const char *errmsg; -- void *rec = NULL; -- svr_cfg *svr = ap_get_module_config(s->module_config, &dbd_module); -- -- /* If nothing is configured, we shouldn't be here */ -- if (svr->name == no_dbdriver) { -- ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool, "DBD: not configured"); -- return NULL; -+ /* If we have a persistent connection and it's good, we'll use it; -+ * since this is non-threaded, we can update without a mutex -+ */ -+ rec = group->rec; -+ if (rec) { -+ if (dbd_check(pool, s, rec) != APR_SUCCESS) { -+ apr_pool_destroy(rec->pool); -+ rec = NULL; -+ } - } - -- if (!svr->persist) { -- /* Return a once-only connection */ -- rv = dbd_construct(&rec, svr, s->process->pool); -- return (rv == APR_SUCCESS) ? arec : NULL; -+ /* We don't have a connection right now, so we'll open one */ -+ if (!rec) { -+ dbd_construct((void*) &rec, group, group->pool); -+ group->rec = rec; - } -+#endif - --/* since we're in nothread-land, we can mess with svr->conn with impunity */ --/* If we have a persistent connection and it's good, we'll use it */ -- if (svr->conn) { -- rv = apr_dbd_check_conn(svr->conn->driver, pool, svr->conn->handle); -- if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) { -- errmsg = apr_dbd_error(arec->driver, arec->handle, rv); -- if (!errmsg) { -- errmsg = "(unknown)"; -- } -- ap_log_perror(APLOG_MARK, APLOG_ERR, rv, pool, -- "DBD[%s] Error: %s", svr->name, errmsg); -- svr->conn = NULL; -- } -- } --/* We don't have a connection right now, so we'll open one */ -- if (!svr->conn) { -- if (dbd_construct(&rec, svr, s->process->pool) == APR_SUCCESS) { -- svr->conn = arec ; -- apr_pool_cleanup_register(s->process->pool, svr->conn, -- dbd_close, apr_pool_cleanup_null); -- } -- } -- return svr->conn; -+ return rec; - } --#endif -+ - #if APR_HAS_THREADS - typedef struct { -- ap_dbd_t *conn; -- apr_reslist_t *dbpool; --} dbd_pool_rec; --static apr_status_t dbd_release(void *REQ) -+ ap_dbd_t *rec; -+ apr_reslist_t *reslist; -+} dbd_acquire_t; -+ -+static apr_status_t dbd_release(void *data) - { -- dbd_pool_rec *req = REQ; -- apr_reslist_release(req->dbpool, req->conn); -+ dbd_acquire_t *acq = data; -+ apr_reslist_release(acq->reslist, acq->rec); - return APR_SUCCESS; - } -+ - DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_acquire(request_rec *r) - { -- svr_cfg *svr; -- dbd_pool_rec *req; -+ dbd_acquire_t *acq; - - while (!ap_is_initial_req(r)) { - if (r->prev) { -@@ -557,54 +812,52 @@ DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_ac - } - } - -- req = ap_get_module_config(r->request_config, &dbd_module); -- if (!req) { -- req = apr_palloc(r->pool, sizeof(dbd_pool_rec)); -- req->conn = ap_dbd_open(r->pool, r->server); -- if (req->conn) { -- svr = ap_get_module_config(r->server->module_config, &dbd_module); -- ap_set_module_config(r->request_config, &dbd_module, req); -- if (svr->persist) { -- req->dbpool = svr->dbpool; -- apr_pool_cleanup_register(r->pool, req, dbd_release, -- apr_pool_cleanup_null); -- } -- else { -- apr_pool_cleanup_register(r->pool, req->conn, dbd_close, -+ acq = ap_get_module_config(r->request_config, &dbd_module); -+ if (!acq) { -+ acq = apr_palloc(r->pool, sizeof(dbd_acquire_t)); -+ acq->rec = ap_dbd_open(r->pool, r->server); -+ if (acq->rec) { -+ svr_cfg *svr = ap_get_module_config(r->server->module_config, -+ &dbd_module); -+ -+ ap_set_module_config(r->request_config, &dbd_module, acq); -+ if (svr->cfg->persist) { -+ acq->reslist = svr->group->reslist; -+ apr_pool_cleanup_register(r->pool, acq, dbd_release, - apr_pool_cleanup_null); - } - } - } -- return req->conn; -+ -+ return acq->rec; - } -+ - DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_cacquire(conn_rec *c) - { -- svr_cfg *svr; -- dbd_pool_rec *req = ap_get_module_config(c->conn_config, &dbd_module); -- if (!req) { -- req = apr_palloc(c->pool, sizeof(dbd_pool_rec)); -- req->conn = ap_dbd_open(c->pool, c->base_server); -- if (req->conn) { -- svr = ap_get_module_config(c->base_server->module_config, &dbd_module); -- ap_set_module_config(c->conn_config, &dbd_module, req); -- if (svr->persist) { -- req->dbpool = svr->dbpool; -- apr_pool_cleanup_register(c->pool, req, dbd_release, -- apr_pool_cleanup_null); -- } -- else { -- apr_pool_cleanup_register(c->pool, req->conn, dbd_close, -+ dbd_acquire_t *acq = ap_get_module_config(c->conn_config, &dbd_module); -+ -+ if (!acq) { -+ acq = apr_palloc(c->pool, sizeof(dbd_acquire_t)); -+ acq->rec = ap_dbd_open(c->pool, c->base_server); -+ if (acq->rec) { -+ svr_cfg *svr = ap_get_module_config(c->base_server->module_config, -+ &dbd_module); -+ -+ ap_set_module_config(c->conn_config, &dbd_module, acq); -+ if (svr->cfg->persist) { -+ acq->reslist = svr->group->reslist; -+ apr_pool_cleanup_register(c->pool, acq, dbd_release, - apr_pool_cleanup_null); - } - } - } -- return req->conn; -+ -+ return acq->rec; - } - #else - DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_acquire(request_rec *r) - { -- svr_cfg *svr; -- ap_dbd_t *ret; -+ ap_dbd_t *rec; - - while (!ap_is_initial_req(r)) { - if (r->prev) { -@@ -615,80 +868,54 @@ DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_ac - } - } - -- ret = ap_get_module_config(r->request_config, &dbd_module); -- if (!ret) { -- svr = ap_get_module_config(r->server->module_config, &dbd_module); -- ret = ap_dbd_open(r->pool, r->server); -- if (ret) { -- ap_set_module_config(r->request_config, &dbd_module, ret); -- if (!svr->persist) { -- apr_pool_cleanup_register(r->pool, svr->conn, dbd_close, -- apr_pool_cleanup_null); -- } -- /* if persist then dbd_open registered cleanup on proc pool */ -+ rec = ap_get_module_config(r->request_config, &dbd_module); -+ if (!rec) { -+ rec = ap_dbd_open(r->pool, r->server); -+ if (rec) { -+ ap_set_module_config(r->request_config, &dbd_module, rec); - } - } -- return ret; -+ -+ return rec; - } -+ - DBD_DECLARE_NONSTD(ap_dbd_t *) ap_dbd_cacquire(conn_rec *c) - { -- svr_cfg *svr; -- ap_dbd_t *ret = ap_get_module_config(c->conn_config, &dbd_module); -- if (!ret) { -- svr = ap_get_module_config(c->base_server->module_config, &dbd_module); -- ret = ap_dbd_open(c->pool, c->base_server); -- if (ret) { -- ap_set_module_config(c->conn_config, &dbd_module, ret); -- if (!svr->persist) { -- apr_pool_cleanup_register(c->pool, svr->conn, dbd_close, -- apr_pool_cleanup_null); -- } -- /* if persist then dbd_open registered cleanup on proc pool */ -+ ap_dbd_t *rec = ap_get_module_config(c->conn_config, &dbd_module); -+ -+ if (!rec) { -+ rec = ap_dbd_open(c->pool, c->base_server); -+ if (rec) { -+ ap_set_module_config(c->conn_config, &dbd_module, rec); - } - } -- return ret; -+ -+ return rec; - } - #endif - --static int dbd_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) --{ -- dbd_prepared_defns = apr_hash_make(ptemp); -- return OK; --} --static int dbd_post_config(apr_pool_t *pconf, apr_pool_t *plog, -- apr_pool_t *ptemp, server_rec *s) --{ -- svr_cfg *svr; -- server_rec *sp; -- for (sp = s; sp; sp = sp->next) { -- const char *key = apr_psprintf(s->process->pool, "%pp", s); -- svr = ap_get_module_config(sp->module_config, &dbd_module); -- svr->prepared = apr_hash_get(dbd_prepared_defns, key, -- APR_HASH_KEY_STRING); -- } -- return OK; --} - static void dbd_hooks(apr_pool_t *pool) - { --#if APR_HAS_THREADS -+ ap_hook_pre_config(dbd_pre_config, NULL, NULL, APR_HOOK_MIDDLE); -+ ap_hook_post_config(dbd_post_config, NULL, NULL, APR_HOOK_MIDDLE); - ap_hook_child_init((void*)dbd_setup_init, NULL, NULL, APR_HOOK_MIDDLE); --#endif -+ -+ APR_REGISTER_OPTIONAL_FN(ap_dbd_prepare); - APR_REGISTER_OPTIONAL_FN(ap_dbd_open); - APR_REGISTER_OPTIONAL_FN(ap_dbd_close); - APR_REGISTER_OPTIONAL_FN(ap_dbd_acquire); - APR_REGISTER_OPTIONAL_FN(ap_dbd_cacquire); -- APR_REGISTER_OPTIONAL_FN(ap_dbd_prepare); -+ - apr_dbd_init(pool); -- ap_hook_pre_config(dbd_pre_config, NULL, NULL, APR_HOOK_MIDDLE); -- ap_hook_post_config(dbd_post_config, NULL, NULL, APR_HOOK_MIDDLE); - } - - module AP_MODULE_DECLARE_DATA dbd_module = { - STANDARD20_MODULE_STUFF, - NULL, - NULL, -- dbd_cfg, -- dbd_merge, -+ create_dbd_config, -+ merge_dbd_config, - dbd_cmds, - dbd_hooks - }; -+ diff --git a/2.2/patches/20_all_peruser_0.2.1.patch b/2.2/patches/20_all_peruser_0.2.1.patch deleted file mode 100644 index 93d2028..0000000 --- a/2.2/patches/20_all_peruser_0.2.1.patch +++ /dev/null @@ -1,3268 +0,0 @@ -diff -Nur httpd-2.2.3/server/mpm/config.m4 httpd-2.2.3-new/server/mpm/config.m4 ---- httpd-2.2.3/server/mpm/config.m4 2005-10-30 10:05:26.000000000 -0700 -+++ httpd-2.2.3-new/server/mpm/config.m4 2007-02-05 06:47:03.000000000 -0700 -@@ -1,7 +1,7 @@ - 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}),[ -+ MPM={beos|event|worker|prefork|mpmt_os2|peruser}),[ - APACHE_MPM=$withval - ],[ - if test "x$APACHE_MPM" = "x"; then -@@ -23,7 +23,7 @@ - - ap_mpm_is_experimental () - { -- if test "$apache_cv_mpm" = "event" ; then -+ if test "$apache_cv_mpm" = "event" -o "$apache_cv_mpm" = "peruser" ; then - return 0 - else - return 1 -diff -Nur httpd-2.2.3/server/mpm/experimental/peruser/Makefile.in httpd-2.2.3-new/server/mpm/experimental/peruser/Makefile.in ---- httpd-2.2.3/server/mpm/experimental/peruser/Makefile.in 1969-12-31 17:00:00.000000000 -0700 -+++ httpd-2.2.3-new/server/mpm/experimental/peruser/Makefile.in 2007-02-05 06:47:03.000000000 -0700 -@@ -0,0 +1,5 @@ -+ -+LTLIBRARY_NAME = libperuser.la -+LTLIBRARY_SOURCES = peruser.c -+ -+include $(top_srcdir)/build/ltlib.mk -diff -Nur httpd-2.2.3/server/mpm/experimental/peruser/config.m4 httpd-2.2.3-new/server/mpm/experimental/peruser/config.m4 ---- httpd-2.2.3/server/mpm/experimental/peruser/config.m4 1969-12-31 17:00:00.000000000 -0700 -+++ httpd-2.2.3-new/server/mpm/experimental/peruser/config.m4 2007-02-05 06:47:03.000000000 -0700 -@@ -0,0 +1,3 @@ -+if test "$MPM_NAME" = "peruser" ; then -+ APACHE_FAST_OUTPUT(server/mpm/experimental/$MPM_NAME/Makefile) -+fi -diff -Nur httpd-2.2.3/server/mpm/experimental/peruser/mpm.h httpd-2.2.3-new/server/mpm/experimental/peruser/mpm.h ---- httpd-2.2.3/server/mpm/experimental/peruser/mpm.h 1969-12-31 17:00:00.000000000 -0700 -+++ httpd-2.2.3-new/server/mpm/experimental/peruser/mpm.h 2007-02-05 06:47:03.000000000 -0700 -@@ -0,0 +1,103 @@ -+/* ==================================================================== -+ * The Apache Software License, Version 1.1 -+ * -+ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights -+ * reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in -+ * the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * 3. The end-user documentation included with the redistribution, -+ * if any, must include the following acknowledgment: -+ * "This product includes software developed by the -+ * Apache Software Foundation (http://www.apache.org/)." -+ * Alternately, this acknowledgment may appear in the software itself, -+ * if and wherever such third-party acknowledgments normally appear. -+ * -+ * 4. The names "Apache" and "Apache Software Foundation" must -+ * not be used to endorse or promote products derived from this -+ * software without prior written permission. For written -+ * permission, please contact apache@apache.org. -+ * -+ * 5. Products derived from this software may not be called "Apache", -+ * nor may "Apache" appear in their name, without prior written -+ * permission of the Apache Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR -+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -+ * SUCH DAMAGE. -+ * ==================================================================== -+ * -+ * This software consists of voluntary contributions made by many -+ * individuals on behalf of the Apache Software Foundation. For more -+ * information on the Apache Software Foundation, please see -+ * . -+ * -+ * Portions of this software are based upon public domain software -+ * originally written at the National Center for Supercomputing Applications, -+ * University of Illinois, Urbana-Champaign. -+ */ -+ -+#include "httpd.h" -+#include "mpm_default.h" -+#include "scoreboard.h" -+#include "unixd.h" -+ -+#ifndef APACHE_MPM_PERUSER_H -+#define APACHE_MPM_PERUSER_H -+ -+#define PERUSER_MPM -+ -+#define MPM_NAME "Peruser" -+ -+#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_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; -+ -+/* Table of child status */ -+#define SERVER_DEAD 0 -+#define SERVER_DYING 1 -+#define SERVER_ALIVE 2 -+ -+typedef struct ap_ctable { -+ pid_t pid; -+ unsigned char status; -+} ap_ctable; -+ -+#endif /* APACHE_MPM_PERUSER_H */ -diff -Nur httpd-2.2.3/server/mpm/experimental/peruser/mpm_default.h httpd-2.2.3-new/server/mpm/experimental/peruser/mpm_default.h ---- httpd-2.2.3/server/mpm/experimental/peruser/mpm_default.h 1969-12-31 17:00:00.000000000 -0700 -+++ httpd-2.2.3-new/server/mpm/experimental/peruser/mpm_default.h 2007-02-05 06:47:03.000000000 -0700 -@@ -0,0 +1,110 @@ -+/* ==================================================================== -+ * The Apache Software License, Version 1.1 -+ * -+ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights -+ * reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in -+ * the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * 3. The end-user documentation included with the redistribution, -+ * if any, must include the following acknowledgment: -+ * "This product includes software developed by the -+ * Apache Software Foundation (http://www.apache.org/)." -+ * Alternately, this acknowledgment may appear in the software itself, -+ * if and wherever such third-party acknowledgments normally appear. -+ * -+ * 4. The names "Apache" and "Apache Software Foundation" must -+ * not be used to endorse or promote products derived from this -+ * software without prior written permission. For written -+ * permission, please contact apache@apache.org. -+ * -+ * 5. Products derived from this software may not be called "Apache", -+ * nor may "Apache" appear in their name, without prior written -+ * permission of the Apache Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR -+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -+ * SUCH DAMAGE. -+ * ==================================================================== -+ * -+ * This software consists of voluntary contributions made by many -+ * individuals on behalf of the Apache Software Foundation. For more -+ * information on the Apache Software Foundation, please see -+ * . -+ * -+ * Portions of this software are based upon public domain software -+ * originally written at the National Center for Supercomputing Applications, -+ * University of Illinois, Urbana-Champaign. -+ */ -+ -+#ifndef APACHE_MPM_DEFAULT_H -+#define APACHE_MPM_DEFAULT_H -+ -+/* Number of processors to spawn off for each ServerEnvironment by default */ -+ -+#ifndef DEFAULT_START_PROCESSORS -+#define DEFAULT_START_PROCESSORS 0 -+#endif -+ -+/* Minimum number of running processors per ServerEnvironment */ -+ -+#ifndef DEFAULT_MIN_PROCESSORS -+#define DEFAULT_MIN_PROCESSORS 0 -+#endif -+ -+/* Minimum --- fewer than this, and more will be created */ -+ -+#ifndef DEFAULT_MIN_FREE_PROCESSORS -+#define DEFAULT_MIN_FREE_PROCESSORS 2 -+#endif -+ -+/* Maximum processors per ServerEnvironment */ -+ -+#ifndef DEFAULT_MAX_PROCESSORS -+#define DEFAULT_MAX_PROCESSORS 10 -+#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 -Nur httpd-2.2.3/server/mpm/experimental/peruser/peruser.c httpd-2.2.3-new/server/mpm/experimental/peruser/peruser.c ---- httpd-2.2.3/server/mpm/experimental/peruser/peruser.c 1969-12-31 17:00:00.000000000 -0700 -+++ httpd-2.2.3-new/server/mpm/experimental/peruser/peruser.c 2007-02-05 07:16:52.000000000 -0700 -@@ -0,0 +1,3006 @@ -+/* ==================================================================== -+ * The Apache Software License, Version 1.1 -+ * -+ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights -+ * reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in -+ * the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * 3. The end-user documentation included with the redistribution, -+ * if any, must include the following acknowledgment: -+ * "This product includes software developed by the -+ * Apache Software Foundation (http://www.apache.org/)." -+ * Alternately, this acknowledgment may appear in the software itself, -+ * if and wherever such third-party acknowledgments normally appear. -+ * -+ * 4. The names "Apache" and "Apache Software Foundation" must -+ * not be used to endorse or promote products derived from this -+ * software without prior written permission. For written -+ * permission, please contact apache@apache.org. -+ * -+ * 5. Products derived from this software may not be called "Apache", -+ * nor may "Apache" appear in their name, without prior written -+ * permission of the Apache Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED -+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR -+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -+ * SUCH DAMAGE. -+ * ==================================================================== -+ * -+ * This software consists of voluntary contributions made by many -+ * individuals on behalf of the Apache Software Foundation. For more -+ * information on the Apache Software Foundation, please see -+ * . -+ * -+ * Portions of this software are based upon public domain software -+ * originally written at the National Center for Supercomputing Applications, -+ * University of Illinois, Urbana-Champaign. -+ */ -+ -+/* #define MPM_PERUSER_DEBUG */ -+ -+#include "apr.h" -+#include "apr_hash.h" -+#include "apr_pools.h" -+#include "apr_file_io.h" -+#include "apr_portable.h" -+#include "apr_strings.h" -+#include "apr_thread_proc.h" -+#include "apr_signal.h" -+#define APR_WANT_STDIO -+#define APR_WANT_STRFUNC -+#define APR_WANT_IOVEC -+#include "apr_want.h" -+ -+#if APR_HAVE_UNISTD_H -+#include -+#endif -+#if APR_HAVE_SYS_TYPES_H -+#include -+#endif -+ -+#define CORE_PRIVATE -+ -+#include "ap_config.h" -+#include "httpd.h" -+#include "mpm_default.h" -+#include "http_main.h" -+#include "http_log.h" -+#include "http_config.h" -+#include "http_core.h" /* for get_remote_host */ -+#include "http_connection.h" -+#include "http_protocol.h" /* for ap_hook_post_read_request */ -+#include "scoreboard.h" -+#include "ap_mpm.h" -+#include "unixd.h" -+#include "mpm_common.h" -+#include "ap_listen.h" -+#include "ap_mmn.h" -+#include "apr_poll.h" -+#include "util_ebcdic.h" -+#include "mod_status.h" -+ -+#ifdef HAVE_BSTRING_H -+#include /* for IRIX, FD_SET calls bzero() */ -+#endif -+ -+#ifdef HAVE_TIME_H -+#include -+#endif -+ -+#ifdef HAVE_SYS_PROCESSOR_H -+#include /* for bindprocessor() */ -+#endif -+ -+#if APR_HAS_SHARED_MEMORY -+#include "apr_shm.h" -+#else -+#error "Peruser MPM requres shared memory support." -+#endif -+ -+ -+/* should be APR-ized */ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+ -+#ifdef MPM_PERUSER_DEBUG -+# define _DBG(text,par...) \ -+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, \ -+ "(peruser: pid=%d uid=%d child=%d) %s(): " text, \ -+ getpid(), getuid(), my_child_num, __FUNCTION__, ##par, 0) -+ -+# define _TRACE_CALL(text,par...) _DBG("calling " text, ##par) -+# define _TRACE_RET(text,par...) _DBG("returned from " text, ##par) -+#else -+# define _DBG(text,par...) -+# define _TRACE_RET(text,par...) -+# define _TRACE_CALL(text,par...) -+#endif /* MPM_PERUSER_DEBUG */ -+ -+/* char of death - for signalling children to die */ -+#define AP_PERUSER_CHAR_OF_DEATH '!' -+ -+#define PERUSER_SERVER_CONF(cf) \ -+ ((peruser_server_conf *) ap_get_module_config(cf, &mpm_peruser_module)) -+ -+#define SCOREBOARD_STATUS(i) ap_scoreboard_image->servers[i][0].status -+ -+/* -+ * Define some magic numbers that we use for the state of the incomming -+ * request. These must be < 0 so they don't collide with a file descriptor. -+ */ -+#define AP_PERUSER_THISCHILD -1 -+#define AP_PERUSER_OTHERCHILD -2 -+ -+ -+/* Limit on the total --- clients will be locked out if more servers than -+ * this are needed. It is intended solely to keep the server from crashing -+ * when things get out of hand. -+ * -+ * We keep a hard maximum number of servers, for two reasons --- first off, -+ * in case something goes seriously wrong, we want to stop the fork bomb -+ * short of actually crashing the machine we're running on by filling some -+ * kernel table. Secondly, it keeps the size of the scoreboard file small -+ * enough that we can read the whole thing without worrying too much about -+ * the overhead. -+ */ -+#ifndef DEFAULT_SERVER_LIMIT -+#define DEFAULT_SERVER_LIMIT 256 -+#endif -+ -+/* Admin can't tune ServerLimit beyond MAX_SERVER_LIMIT. We want -+ * some sort of compile-time limit to help catch typos. -+ */ -+#ifndef MAX_SERVER_LIMIT -+#define MAX_SERVER_LIMIT 20000 -+#endif -+ -+#ifndef HARD_THREAD_LIMIT -+#define HARD_THREAD_LIMIT 1 -+#endif -+ -+#define CHILD_TYPE_UNKNOWN 0 -+#define CHILD_TYPE_MULTIPLEXER 1 -+#define CHILD_TYPE_PROCESSOR 2 -+#define CHILD_TYPE_WORKER 3 -+ -+#define CHILD_STATUS_STANDBY 0 /* wait for a request before starting */ -+#define CHILD_STATUS_STARTING 1 /* wait for socket creation */ -+#define CHILD_STATUS_READY 2 /* wait for mux to restart */ -+#define CHILD_STATUS_ACTIVE 3 /* ready to take requests */ -+#define CHILD_STATUS_RESTART 4 /* child about to die and restart */ -+ -+/* config globals */ -+ -+int ap_threads_per_child=0; /* Worker threads per child */ -+static apr_proc_mutex_t *accept_mutex; -+static int ap_min_processors=DEFAULT_MIN_PROCESSORS; -+static int ap_min_free_processors=DEFAULT_MIN_FREE_PROCESSORS; -+static int ap_max_processors=DEFAULT_MAX_PROCESSORS; -+static int ap_daemons_limit=0; /* MaxClients */ -+static int expire_timeout=1800; -+static int idle_timeout=900; -+static int server_limit = DEFAULT_SERVER_LIMIT; -+static int first_server_limit; -+static int changed_limit_at_restart; -+static int requests_this_child; -+static int mpm_state = AP_MPMQ_STARTING; -+static ap_pod_t *pod; -+ -+/* === configuration stuff === */ -+ -+typedef struct -+{ -+ int processor_id; -+ -+ /* security settings */ -+ uid_t uid; /* user id */ -+ gid_t gid; /* group id */ -+ const char *chroot; /* directory to chroot() to, can be null */ -+ -+ /* resource settings */ -+ int min_processors; -+ int min_free_processors; -+ int max_processors; -+ -+ /* sockets */ -+ int input; /* The socket descriptor */ -+ int output; /* The socket descriptor */ -+} server_env_t; -+ -+typedef struct -+{ -+ apr_size_t num; -+} server_env_control; -+ -+typedef struct -+{ -+ server_env_control *control; -+ server_env_t *table; -+} server_env; -+ -+ -+typedef struct -+{ -+ /* identification */ -+ int id; /* index in child_info_table */ -+ pid_t pid; /* process id */ -+ int status; /* status of child */ -+ int type; /* multiplexer or processor */ -+ server_env_t *senv; -+ -+ /* sockets */ -+ int sock_fd; -+ -+ /* stack context saved state */ -+ jmp_buf jmpbuffer; -+} child_info_t; -+ -+typedef struct -+{ -+ apr_size_t num; -+} child_info_control; -+ -+typedef struct -+{ -+ child_info_control *control; -+ child_info_t *table; -+} child_info; -+ -+ -+typedef struct -+{ -+ server_env_t *senv; -+} peruser_server_conf; -+ -+ -+typedef struct peruser_header -+{ -+ char *headers; -+ apr_pool_t *p; -+} peruser_header; -+ -+ -+/* Tables used to determine the user and group each child process should -+ * run as. The hash table is used to correlate a server name with a child -+ * process. -+ */ -+static apr_size_t child_info_size; -+static child_info *child_info_image; -+struct ap_ctable *ap_child_table; -+ -+#define NUM_CHILDS (child_info_image != NULL ? child_info_image->control->num : 0) -+#define CHILD_INFO_TABLE (child_info_image != NULL ? child_info_image->table : NULL) -+ -+static apr_size_t server_env_size; -+static server_env *server_env_image; -+ -+#define NUM_SENV (server_env_image != NULL ? server_env_image->control->num : 0) -+#define SENV (server_env_image != NULL ? server_env_image->table : NULL) -+ -+#if APR_HAS_SHARED_MEMORY -+#ifndef WIN32 -+static /* but must be exported to mpm_winnt */ -+#endif -+ apr_shm_t *child_info_shm = NULL; -+ apr_shm_t *server_env_shm = NULL; -+#endif -+ -+/* -+ * 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; -+server_rec *ap_server_conf; -+ -+module AP_MODULE_DECLARE_DATA mpm_peruser_module; -+ -+/* -- replace the pipe-of-death by an control socket -- */ -+static apr_file_t *pipe_of_death_in = NULL; -+static apr_file_t *pipe_of_death_out = NULL; -+ -+ -+/* one_process --- debugging mode variable; can be set from the command line -+ * with the -X flag. If set, this gets you the child_main loop running -+ * in the process which originally started up (no detach, no make_child), -+ * which is a pretty nice debugging environment. (You'll get a SIGHUP -+ * early in standalone_main; just continue through. This is the server -+ * trying to kill off any child processes which it might have lying -+ * around --- Apache doesn't keep track of their pids, it just sends -+ * SIGHUP to the process group, ignoring it in the root process. -+ * Continue through and you'll be fine.). -+ */ -+ -+static int one_process = 0; -+ -+static apr_pool_t *pconf; /* Pool for config stuff */ -+static apr_pool_t *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; -+static int my_child_num; -+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 int die_now = 0; -+ -+#ifdef GPROF -+/* -+ * change directory for gprof to plop the gmon.out file -+ * configure in httpd.conf: -+ * GprofDir $RuntimeDir/ -> $ServerRoot/$RuntimeDir/gmon.out -+ * GprofDir $RuntimeDir/% -> $ServerRoot/$RuntimeDir/gprof.$pid/gmon.out -+ */ -+static void chdir_for_gprof(void) -+{ -+ core_server_config *sconf = -+ ap_get_module_config(ap_server_conf->module_config, &core_module); -+ char *dir = sconf->gprof_dir; -+ const char *use_dir; -+ -+ if(dir) { -+ apr_status_t res; -+ char buf[512]; -+ int len = strlen(sconf->gprof_dir) - 1; -+ if(*(dir + len) == '%') { -+ dir[len] = '\0'; -+ apr_snprintf(buf, sizeof(buf), "%sgprof.%d", dir, (int)getpid()); -+ } -+ use_dir = ap_server_root_relative(pconf, buf[0] ? buf : dir); -+ res = apr_dir_make(use_dir, 0755, pconf); -+ if(res != APR_SUCCESS && !APR_STATUS_IS_EEXIST(res)) { -+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, ap_server_conf, -+ "gprof: error creating directory %s", dir); -+ } -+ } -+ else { -+ use_dir = ap_server_root_relative(pconf, DEFAULT_REL_RUNTIMEDIR); -+ } -+ -+ chdir(use_dir); -+} -+#else -+#define chdir_for_gprof() -+#endif -+ -+char* child_type_string(int type) -+{ -+ switch(type) -+ { -+ case CHILD_TYPE_MULTIPLEXER: return "MULTIPLEXER"; -+ case CHILD_TYPE_PROCESSOR: return "PROCESSOR"; -+ case CHILD_TYPE_WORKER: return "WORKER"; -+ } -+ -+ return "UNKNOWN"; -+} -+ -+char* child_status_string(int status) -+{ -+ switch(status) -+ { -+ case CHILD_STATUS_STANDBY: return "STANDBY"; -+ case CHILD_STATUS_STARTING: return "STARTING"; -+ case CHILD_STATUS_READY: return "READY"; -+ case CHILD_STATUS_ACTIVE: return "ACTIVE"; -+ case CHILD_STATUS_RESTART: return "RESTART"; -+ } -+ -+ return "UNKNOWN"; -+} -+ -+void dump_child_table() -+{ -+ int x; -+ server_env_t *senv; -+ -+ _DBG("%-3s %-5s %-8s %-12s %-4s %-4s %-25s %5s %6s %7s", -+ "ID", "PID", "STATUS", "TYPE", "UID", "GID", "CHROOT", "INPUT", "OUTPUT", "SOCK_FD"); -+ -+ for(x = 0; x < NUM_CHILDS; x++) -+ { -+ senv = CHILD_INFO_TABLE[x].senv; -+ _DBG("%-3d %-5d %-8s %-12s %-4d %-4d %-25s %-5d %-6d %-7d", -+ CHILD_INFO_TABLE[x].id, -+ CHILD_INFO_TABLE[x].pid, -+ child_status_string(CHILD_INFO_TABLE[x].status), -+ child_type_string(CHILD_INFO_TABLE[x].type), -+ senv == NULL ? -1 : senv->uid, -+ senv == NULL ? -1 : senv->gid, -+ senv == NULL ? NULL : senv->chroot, -+ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->input, -+ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->output, -+ CHILD_INFO_TABLE[x].sock_fd); -+ } -+} -+ -+/* XXX - I don't know if TPF will ever use this module or not, so leave -+ * the ap_check_signals calls in but disable them - manoj */ -+#define ap_check_signals() -+ -+/* a clean exit from a child with proper cleanup */ -+static inline int clean_child_exit(int code) __attribute__ ((noreturn)); -+static inline int clean_child_exit(int code) -+{ -+ int retval; -+ -+ mpm_state = AP_MPMQ_STOPPING; -+ -+ if (CHILD_INFO_TABLE[my_child_num].type != CHILD_TYPE_MULTIPLEXER && -+ CHILD_INFO_TABLE[my_child_num].senv) -+ { -+ retval = close(CHILD_INFO_TABLE[my_child_num].senv->input); -+ _DBG("close(CHILD_INFO_TABLE[%d].senv->input) = %d", -+ my_child_num, retval); -+ -+ retval = close(CHILD_INFO_TABLE[my_child_num].senv->output); -+ _DBG("close(CHILD_INFO_TABLE[%d].senv->output) = %d", -+ my_child_num, retval); -+ } -+ -+ if (pchild) { -+ apr_pool_destroy(pchild); -+ } -+ ap_mpm_pod_close(pod); -+ chdir_for_gprof(); -+ exit(code); -+} -+ -+static void accept_mutex_on(void) -+{ -+/* for some reason this fails if we listen on the pipe_of_death. -+ fortunately I don't think we currently need it */ -+ -+#if 0 -+ apr_status_t rv = apr_proc_mutex_lock(accept_mutex); -+ if (rv != APR_SUCCESS) { -+ const char *msg = "couldn't grab the accept mutex"; -+ -+ if (ap_my_generation != -+ ap_scoreboard_image->global->running_generation) { -+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, NULL, msg); -+ clean_child_exit(0); -+ } -+ else { -+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, NULL, msg); -+ exit(APEXIT_CHILDFATAL); -+ } -+ } -+#endif -+} -+ -+static void accept_mutex_off(void) -+{ -+#if 0 -+ apr_status_t rv = apr_proc_mutex_unlock(accept_mutex); -+ if (rv != APR_SUCCESS) { -+ const char *msg = "couldn't release the accept mutex"; -+ -+ if (ap_my_generation != -+ ap_scoreboard_image->global->running_generation) { -+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, NULL, 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, msg); -+ exit(APEXIT_CHILDFATAL); -+ } -+ } -+#endif -+} -+ -+/* On some architectures it's safe to do unserialized accept()s in the single -+ * Listen case. But it's never safe to do it in the case where there's -+ * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT -+ * when it's safe in the single Listen case. -+ */ -+#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT -+#define SAFE_ACCEPT(stmt) do {if (ap_listeners->next) {stmt;}} while(0) -+#else -+#define SAFE_ACCEPT(stmt) do {stmt;} while(0) -+#endif -+ -+AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result) -+{ -+ switch(query_code){ -+ case AP_MPMQ_MAX_DAEMON_USED: -+ *result = ap_daemons_limit; -+ return APR_SUCCESS; -+ case AP_MPMQ_IS_THREADED: -+ *result = AP_MPMQ_NOT_SUPPORTED; -+ return APR_SUCCESS; -+ case AP_MPMQ_IS_FORKED: -+ *result = AP_MPMQ_DYNAMIC; -+ return APR_SUCCESS; -+ case AP_MPMQ_HARD_LIMIT_DAEMONS: -+ *result = server_limit; -+ return APR_SUCCESS; -+ case AP_MPMQ_HARD_LIMIT_THREADS: -+ *result = HARD_THREAD_LIMIT; -+ return APR_SUCCESS; -+ case AP_MPMQ_MAX_THREADS: -+ *result = 0; -+ return APR_SUCCESS; -+ case AP_MPMQ_MIN_SPARE_DAEMONS: -+ *result = ap_min_free_processors; -+ return APR_SUCCESS; -+ case AP_MPMQ_MIN_SPARE_THREADS: -+ *result = 0; -+ return APR_SUCCESS; -+ case AP_MPMQ_MAX_SPARE_THREADS: -+ *result = 0; -+ return APR_SUCCESS; -+ case AP_MPMQ_MAX_REQUESTS_DAEMON: -+ *result = ap_max_requests_per_child; -+ return APR_SUCCESS; -+ case AP_MPMQ_MAX_DAEMONS: -+ *result = server_limit; -+ return APR_SUCCESS; -+ case AP_MPMQ_MPM_STATE: -+ *result = mpm_state; -+ return APR_SUCCESS; -+ } -+ return APR_ENOTIMPL; -+} -+ -+#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) -+{ -+ 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; -+} -+#endif -+ -+/* handle all varieties of core dumping signals */ -+static void sig_coredump(int sig) -+{ -+ chdir(ap_coredump_dir); -+ apr_signal(sig, SIG_DFL); -+ if (ap_my_pid == parent_pid) { -+ ap_log_error(APLOG_MARK, APLOG_NOTICE, -+ 0, ap_server_conf, -+ "seg fault or similar nasty error detected " -+ "in the parent process"); -+ } -+ 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. -+ */ -+} -+ -+/***************************************************************** -+ * Connection structures and accounting... -+ */ -+ -+static void just_die(int sig) -+{ -+_DBG("function called"); -+ clean_child_exit(0); -+} -+ -+/* volatile just in case */ -+static int volatile shutdown_pending; -+static int volatile restart_pending; -+static int volatile is_graceful; -+/* XXX static int volatile child_fatal; */ -+ -+static void sig_term(int sig) -+{ -+ if (shutdown_pending == 1) { -+ /* Um, is this _probably_ not an error, if the user has -+ * tried to do a shutdown twice quickly, so we won't -+ * worry about reporting it. -+ */ -+ return; -+ } -+ shutdown_pending = 1; -+} -+ -+/* restart() is the signal handler for SIGHUP and AP_SIG_GRACEFUL -+ * in the parent process, unless running in ONE_PROCESS mode -+ */ -+static void restart(int sig) -+{ -+ if (restart_pending == 1) { -+ /* Probably not an error - don't bother reporting it */ -+ return; -+ } -+ restart_pending = 1; -+ is_graceful = (sig == AP_SIG_GRACEFUL); -+} -+ -+/* Sets die_now if we received a character on the pipe_of_death */ -+static apr_status_t check_pipe_of_death -+( -+ void **csd, -+ ap_listen_rec *lr, -+ apr_pool_t *ptrans -+) -+{ -+ int ret; -+ char pipe_read_char; -+ apr_size_t n = 1; -+ -+ _DBG("WATCH: die_now=%d", die_now); -+ -+ if (die_now) return APR_SUCCESS; -+ -+ /* apr_thread_mutex_lock(pipe_of_death_mutex); */ -+ ret = apr_socket_recv(lr->sd, &pipe_read_char, &n); -+ if (APR_STATUS_IS_EAGAIN(ret)) -+ { -+ /* It lost the lottery. It must continue to suffer -+ * through a life of servitude. */ -+ } -+ else -+ { -+ if (pipe_read_char != AP_PERUSER_CHAR_OF_DEATH) -+ { -+ _DBG("got wrong char %c", pipe_read_char); -+ return APR_SUCCESS; -+ } -+ /* It won the lottery (or something else is very -+ * wrong). Embrace death with open arms. */ -+ die_now = 1; -+ _DBG("WATCH: die_now=%d", die_now); -+ } -+ /* apr_thread_mutex_unlock(pipe_of_death_mutex); */ -+ return APR_SUCCESS; -+} -+ -+static void set_signals(void) -+{ -+#ifndef NO_USE_SIGACTION -+ struct sigaction sa; -+ -+ sigemptyset(&sa.sa_mask); -+ sa.sa_flags = 0; -+ -+ if (!one_process) { -+ sa.sa_handler = sig_coredump; -+#if defined(SA_ONESHOT) -+ sa.sa_flags = SA_ONESHOT; -+#elif defined(SA_RESETHAND) -+ sa.sa_flags = SA_RESETHAND; -+#endif -+ if (sigaction(SIGSEGV, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGSEGV)"); -+#ifdef SIGBUS -+ if (sigaction(SIGBUS, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGBUS)"); -+#endif -+#ifdef SIGABORT -+ if (sigaction(SIGABORT, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABORT)"); -+#endif -+#ifdef SIGABRT -+ if (sigaction(SIGABRT, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABRT)"); -+#endif -+#ifdef SIGILL -+ if (sigaction(SIGILL, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGILL)"); -+#endif -+ sa.sa_flags = 0; -+ } -+ sa.sa_handler = sig_term; -+ if (sigaction(SIGTERM, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)"); -+#ifdef SIGINT -+ if (sigaction(SIGINT, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)"); -+#endif -+#ifdef SIGXCPU -+ sa.sa_handler = SIG_DFL; -+ if (sigaction(SIGXCPU, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXCPU)"); -+#endif -+#ifdef SIGXFSZ -+ sa.sa_handler = SIG_IGN; -+ if (sigaction(SIGXFSZ, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXFSZ)"); -+#endif -+#ifdef SIGPIPE -+ sa.sa_handler = SIG_IGN; -+ if (sigaction(SIGPIPE, &sa, NULL) < 0) -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGPIPE)"); -+#endif -+ -+ /* 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 ")"); -+#else -+ if (!one_process) { -+ 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 SIGXCPU -+ apr_signal(SIGXCPU, SIG_DFL); -+#endif /* SIGXCPU */ -+#ifdef SIGXFSZ -+ apr_signal(SIGXFSZ, SIG_DFL); -+#endif /* SIGXFSZ */ -+ } -+ -+ apr_signal(SIGTERM, sig_term); -+#ifdef SIGHUP -+ apr_signal(SIGHUP, restart); -+#endif /* SIGHUP */ -+#ifdef AP_SIG_GRACEFUL -+ apr_signal(AP_SIG_GRACEFUL, restart); -+#endif /* AP_SIG_GRACEFUL */ -+#ifdef SIGPIPE -+ apr_signal(SIGPIPE, SIG_IGN); -+#endif /* SIGPIPE */ -+ -+#endif -+} -+ -+/***************************************************************** -+ * Child process main loop. -+ * The following vars are static to avoid getting clobbered by longjmp(); -+ * they are really private to child_main. -+ */ -+ -+static int requests_this_child; -+static int num_listensocks = 0; -+static ap_listen_rec *listensocks; -+ -+int ap_graceful_stop_signalled(void) -+{ -+ /* not ever called anymore... */ -+ return 0; -+} -+ -+/* -+ * This function sends a raw socket over to a processor. It uses the same -+ * on-wire format as pass_request. The recipient can determine if he got -+ * a socket or a whole request by inspecting the header_length of the -+ * message. If it is zero then only a socket was sent. -+ */ -+static int pass_socket(apr_socket_t *thesock, child_info_t *processor, apr_pool_t *pool) -+{ -+ int rv; -+ struct msghdr msg; -+ struct cmsghdr *cmsg; -+ int sock_fd; -+ char *body = ""; -+ struct iovec iov[4]; -+ apr_size_t len = 0; -+ apr_size_t header_len = 0; -+ apr_size_t body_len = 0; -+ peruser_header h; -+ apr_bucket *bucket; -+ const apr_array_header_t *headers_in_array; -+ const apr_table_entry_t *headers_in; -+ int counter; -+ -+ if (!processor) -+ { -+ _DBG("server %s in child %d has no child_info associated", -+ "(unkonwn)", my_child_num); -+ return -1; -+ } -+ -+ _DBG("passing request to another child.", 0); -+ -+ apr_os_sock_get(&sock_fd, thesock); -+ -+ header_len = 0; -+ body_len = 0; -+ -+ iov[0].iov_base = &header_len; -+ iov[0].iov_len = sizeof(header_len); -+ iov[1].iov_base = &body_len; -+ iov[1].iov_len = sizeof(body_len); -+ iov[2].iov_base = h.headers; -+ iov[2].iov_len = 0; -+ iov[3].iov_base = body; -+ iov[3].iov_len = body_len; -+ -+ msg.msg_name = NULL; -+ msg.msg_namelen = 0; -+ msg.msg_iov = iov; -+ msg.msg_iovlen = 4; -+ -+ cmsg = apr_palloc(pool, sizeof(*cmsg) + sizeof(sock_fd)); -+ cmsg->cmsg_len = sizeof(*cmsg) + sizeof(sock_fd); -+ cmsg->cmsg_level = SOL_SOCKET; -+ cmsg->cmsg_type = SCM_RIGHTS; -+ -+ memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(sock_fd)); -+ -+ msg.msg_control = cmsg; -+ msg.msg_controllen = cmsg->cmsg_len; -+ -+ if (processor->status == CHILD_STATUS_STANDBY) -+ { -+ _DBG("Activating child #%d", processor->id); -+ processor->status = CHILD_STATUS_STARTING; -+ } -+ -+ _DBG("Writing message to %d, passing sock_fd: %d", processor->senv->output, sock_fd); -+ _DBG("header_len=%d headers=\"%s\"", header_len, h.headers); -+ _DBG("body_len=%d body=\"%s\"", body_len, body); -+ -+ if ((rv = sendmsg(processor->senv->output, &msg, 0)) == -1) -+ { -+ apr_pool_destroy(pool); -+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, -+ "Writing message failed %d %d", rv, errno); -+ return -1; -+ } -+ -+ _DBG("Writing message succeeded %d", rv); -+ -+ /* -- close the socket on our side -- */ -+ _DBG("closing socket %d on our side", sock_fd); -+ apr_socket_close(thesock); -+ -+ apr_pool_destroy(pool); -+ return 1; -+} -+ -+static void process_socket(apr_pool_t *p, apr_socket_t *sock, long conn_id, -+ apr_bucket_alloc_t *bucket_alloc, apr_pool_t *pool) -+{ -+ conn_rec *current_conn; -+ int sock_fd; -+ apr_status_t rv; -+ ap_sb_handle_t *sbh; -+ child_info_t *processor; -+ apr_pool_t *ptrans; -+ peruser_server_conf *sconf; -+ -+ _DBG("Creating dummy connection to use the vhost lookup api", 0); -+ -+ ap_create_sb_handle(&sbh, p, conn_id, 0); -+ current_conn = ap_run_create_connection(p, ap_server_conf, sock, conn_id, -+ sbh, bucket_alloc); -+ _DBG("Looking up the right vhost"); -+ if (current_conn) { -+ ap_update_vhost_given_ip(current_conn); -+ _DBG("Base server is %s, name based vhosts %s", current_conn->base_server->server_hostname, -+ current_conn->vhost_lookup_data ? "on" : "off"); -+ } -+ -+ if (current_conn && !current_conn->vhost_lookup_data && CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER) { -+ _DBG("We are not using name based vhosts, we'll directly pass the socket."); -+ -+ sconf = PERUSER_SERVER_CONF(current_conn->base_server->module_config); -+ processor = &CHILD_INFO_TABLE[sconf->senv->processor_id]; -+ -+ _DBG("Forwarding without further inspection, processor %d", processor->id); -+ if (processor->status == CHILD_STATUS_STANDBY) -+ { -+ _DBG("Activating child #%d", processor->id); -+ processor->status = CHILD_STATUS_STARTING; -+ } -+ -+ _DBG("Creating new pool",0); -+ apr_pool_create(&ptrans, pool); -+ _DBG("Passing request.",0); -+ if (pass_socket(sock, processor, ptrans) == -1) -+ { -+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, -+ ap_server_conf, "Could not pass request to proper " -+ "child, request will not be honoured."); -+ return; -+ } -+ if (current_conn) -+ { -+ _DBG("freeing connection",0); -+ ap_lingering_close(current_conn); -+ } -+ _DBG("doing longjmp",0); -+ longjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer, 1); -+ return; -+ } -+ -+ if ((rv = apr_os_sock_get(&sock_fd, sock)) != APR_SUCCESS) -+ { -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, "apr_os_sock_get"); -+ } -+ -+ _DBG("child_num=%d sock=%ld sock_fd=%d\n", my_child_num, sock, sock_fd); -+ _DBG("type=%s %d", child_type_string(CHILD_INFO_TABLE[my_child_num].type), my_child_num); -+ -+ if (sock_fd >= FD_SETSIZE) -+ { -+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, -+ "new file descriptor %d is too large; you probably need " -+ "to rebuild Apache with a larger FD_SETSIZE " -+ "(currently %d)", -+ sock_fd, FD_SETSIZE); -+ apr_socket_close(sock); -+ _DBG("child_num=%d: exiting with error", my_child_num); -+ return; -+ } -+ -+ if (CHILD_INFO_TABLE[my_child_num].sock_fd < 0) -+ { -+ ap_sock_disable_nagle(sock); -+ } -+ -+ if (!current_conn) { -+ ap_create_sb_handle(&sbh, p, conn_id, 0); -+ current_conn = ap_run_create_connection(p, ap_server_conf, sock, conn_id, -+ sbh, bucket_alloc); -+ } -+ -+ if (current_conn) -+ { -+ ap_process_connection(current_conn, sock); -+ ap_lingering_close(current_conn); -+ } -+} -+ -+static int peruser_process_connection(conn_rec *conn) -+{ -+ ap_filter_t *filter; -+ apr_bucket_brigade *bb; -+ core_net_rec *net; -+ -+ _DBG("function entered",0); -+ -+ /* -- fetch our sockets from the pool -- */ -+ apr_pool_userdata_get((void **)&bb, "PERUSER_SOCKETS", conn->pool); -+ if (bb != NULL) -+ { -+ /* -- find the 'core' filter and give the socket data to it -- */ -+ for (filter = conn->output_filters; filter != NULL; filter = filter->next) -+ { -+ if (!strcmp(filter->frec->name, "core")) break; -+ } -+ if (filter != NULL) -+ { -+ net = filter->ctx; -+ net->in_ctx = apr_palloc(conn->pool, sizeof(*net->in_ctx)); -+ net->in_ctx->b = bb; -+ net->in_ctx->tmpbb = apr_brigade_create(net->in_ctx->b->p, -+ net->in_ctx->b->bucket_alloc); -+ } -+ } -+ _DBG("leaving (DECLINED)", 0); -+ return DECLINED; -+} -+ -+static int total_processors(int child_num) -+{ -+ int i, total; -+ -+ for(i = 0, total = 0; i < NUM_CHILDS; ++i) -+ { -+ if(CHILD_INFO_TABLE[i].senv == CHILD_INFO_TABLE[child_num].senv) -+ total++; -+ } -+ -+ return total; -+} -+ -+static int idle_processors(int child_num) -+{ -+ int i, total; -+ -+ for(i = 0, total = 0; i < NUM_CHILDS; ++i) -+ { -+ if(CHILD_INFO_TABLE[i].senv == CHILD_INFO_TABLE[child_num].senv && -+ (SCOREBOARD_STATUS(i) == SERVER_STARTING || -+ SCOREBOARD_STATUS(i) == SERVER_READY)) -+ { -+ total++; -+ } -+ } -+ -+ return total; -+} -+ -+static int pass_request(request_rec *r, child_info_t *processor) -+{ -+ int rv; -+ struct msghdr msg; -+ struct cmsghdr *cmsg; -+ int sock_fd; -+ char *body = ""; -+ struct iovec iov[4]; -+ conn_rec *c = r->connection; -+ apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc); -+ apr_bucket_brigade *body_bb = NULL; -+ apr_size_t len = 0; -+ apr_size_t header_len = 0; -+ apr_size_t body_len = 0; -+ peruser_header h; -+ apr_bucket *bucket; -+ const apr_array_header_t *headers_in_array; -+ const apr_table_entry_t *headers_in; -+ int counter; -+ -+ apr_socket_t *thesock = ap_get_module_config(r->connection->conn_config, &core_module); -+ -+ if ((!r->the_request) || (!strlen(r->the_request))) -+ { -+ _DBG("empty request. dropping it (%ld)", r->the_request); -+ return -1; -+ } -+ -+ if (!processor) -+ { -+ _DBG("server %s in child %d has no child_info associated", -+ r->hostname, my_child_num); -+ return -1; -+ } -+ -+ _DBG("passing request to another child. Vhost: %s, child %d %d", -+ apr_table_get(r->headers_in, "Host"), my_child_num, processor->senv->output); -+ _DBG("r->the_request=\"%s\" len=%d", r->the_request, strlen(r->the_request)); -+ -+ ap_get_brigade(r->connection->input_filters, bb, AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, len); -+ -+ /* Scan the brigade looking for heap-buckets */ -+ -+ _DBG("Scanning the brigade",0); -+ bucket = APR_BRIGADE_FIRST(bb); -+ while (bucket != APR_BRIGADE_SENTINEL(bb) && -+ APR_BUCKET_IS_HEAP(bucket)) { -+ _DBG("HEAP BUCKET is found, length=%d", bucket->length); -+ bucket = APR_BUCKET_NEXT(bucket); -+ if (!APR_BUCKET_IS_HEAP(bucket)) { -+ _DBG("NON-HEAP BUCKET is found, extracting the part of brigade before it",0); -+ body_bb = bb; -+ bb = apr_brigade_split(body_bb, bucket); -+ /* Do we need to apr_destroy_brigade(bb) here? -+ * Yeah, I know we do apr_pool_destroy(r->pool) before return, but -+ * ap_get_brigade is in non-blocking mode (however len is zero). -+ */ -+ if (apr_brigade_pflatten(body_bb, &body, &body_len, r->pool) != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, -+ "Unable to flatten brigade, declining request"); -+ apr_pool_destroy(r->pool); -+ return DECLINED; -+ } -+ _DBG("Brigade is flattened as body (body_len=%d)", body_len); -+ } -+ } -+ _DBG("Scanning is finished",0); -+ -+ apr_os_sock_get(&sock_fd, thesock); -+ -+ h.p = r->pool; -+ -+ headers_in_array = apr_table_elts(r->headers_in); -+ headers_in = (const apr_table_entry_t *) headers_in_array->elts; -+ -+ h.headers = apr_pstrcat(h.p, r->the_request, CRLF, NULL); -+ for (counter = 0; counter < headers_in_array->nelts; counter++) { -+ if (headers_in[counter].key == NULL -+ || headers_in[counter].val == NULL) { -+ continue; -+ } -+ h.headers = apr_pstrcat(h.p, h.headers, headers_in[counter].key, ": ", -+ headers_in[counter].val, CRLF, NULL); -+ -+ } -+ h.headers = apr_pstrcat(h.p, h.headers, CRLF, NULL); -+ ap_xlate_proto_to_ascii(h.headers, strlen(h.headers)); -+ -+ header_len = strlen(h.headers); -+ -+ iov[0].iov_base = &header_len; -+ iov[0].iov_len = sizeof(header_len); -+ iov[1].iov_base = &body_len; -+ iov[1].iov_len = sizeof(body_len); -+ iov[2].iov_base = h.headers; -+ iov[2].iov_len = strlen(h.headers) + 1; -+ iov[3].iov_base = body; -+ iov[3].iov_len = body_len; -+ -+ msg.msg_name = NULL; -+ msg.msg_namelen = 0; -+ msg.msg_iov = iov; -+ msg.msg_iovlen = 4; -+ -+ cmsg = apr_palloc(r->pool, sizeof(*cmsg) + sizeof(sock_fd)); -+ cmsg->cmsg_len = sizeof(*cmsg) + sizeof(sock_fd); -+ cmsg->cmsg_level = SOL_SOCKET; -+ cmsg->cmsg_type = SCM_RIGHTS; -+ -+ memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(sock_fd)); -+ -+ msg.msg_control = cmsg; -+ msg.msg_controllen = cmsg->cmsg_len; -+ -+ -+ if (processor->status == CHILD_STATUS_STANDBY) -+ { -+ _DBG("Activating child #%d", processor->id); -+ processor->status = CHILD_STATUS_STARTING; -+ } -+ -+ _DBG("Writing message to %d, passing sock_fd: %d", processor->senv->output, sock_fd); -+ _DBG("header_len=%d headers=\"%s\"", header_len, h.headers); -+ _DBG("body_len=%d body=\"%s\"", body_len, body); -+ -+ if ((rv = sendmsg(processor->senv->output, &msg, 0)) == -1) -+ { -+ apr_pool_destroy(r->pool); -+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, -+ "Writing message failed %d %d", rv, errno); -+ return -1; -+ } -+ -+ _DBG("Writing message succeeded %d", rv); -+ -+ /* -- close the socket on our side -- */ -+ _DBG("closing socket %d on our side", sock_fd); -+ apr_socket_close(thesock); -+ -+ apr_pool_destroy(r->pool); -+ return 1; -+} -+ -+ -+static apr_status_t receive_from_multiplexer( -+ void **trans_sock, /* will be filled out w/ the received socket */ -+ ap_listen_rec *lr, /* listener to receive from */ -+ apr_pool_t *ptrans /* transaction wide pool */ -+) -+{ -+ struct msghdr msg; -+ struct cmsghdr *cmsg; -+ char buff[HUGE_STRING_LEN] = ""; -+ char headers[HUGE_STRING_LEN] = ""; -+ char *body = ""; -+ apr_size_t header_len, body_len; -+ struct iovec iov[3]; -+ int ret, fd_tmp; -+ apr_os_sock_t ctrl_sock_fd; -+ apr_os_sock_t trans_sock_fd; -+ -+ /* -- bucket's, brigades and their allocators */ -+ apr_bucket_alloc_t *alloc = apr_bucket_alloc_create(ptrans); -+ apr_bucket_brigade *bb = apr_brigade_create(ptrans, alloc); -+ apr_bucket *bucket; -+ -+ /* prepare the buffers for receiving data from remote side */ -+ iov[0].iov_base = &header_len; -+ iov[0].iov_len = sizeof(header_len); -+ iov[1].iov_base = &body_len; -+ iov[1].iov_len = sizeof(body_len); -+ iov[2].iov_base = (char*)&buff; -+ iov[2].iov_len = HUGE_STRING_LEN; -+ -+ cmsg = apr_palloc(ptrans, sizeof(*cmsg) + sizeof(trans_sock_fd)); -+ cmsg->cmsg_len = sizeof(*cmsg) + sizeof(trans_sock_fd); -+ -+ msg.msg_name = NULL; -+ msg.msg_namelen = 0; -+ msg.msg_iov = iov; -+ msg.msg_iovlen = 3; -+ msg.msg_control = cmsg; -+ msg.msg_controllen = cmsg->cmsg_len; -+ -+ /* -- receive data from socket -- */ -+ apr_os_sock_get(&ctrl_sock_fd, lr->sd); -+ _DBG("receiving from sock_fd=%d", ctrl_sock_fd); -+ ret = recvmsg(ctrl_sock_fd, &msg, 0); -+ -+ if(ret == -1) -+ _DBG("recvmsg failed with error \"%s\"", strerror(errno)); -+ else -+ _DBG("recvmsg returned %d", ret); -+ -+ /* -- extract socket from the cmsg -- */ -+ memcpy(&trans_sock_fd, CMSG_DATA(cmsg), sizeof(trans_sock_fd)); -+ apr_os_sock_put((apr_socket_t **)trans_sock, &trans_sock_fd, ptrans); -+ apr_os_sock_get(&fd_tmp, *trans_sock); -+ -+ _DBG("trans_sock=%ld fdx=%d sock_fd=%d", -+ *trans_sock, trans_sock_fd, fd_tmp); -+ -+ apr_cpystrn(headers, buff, header_len + 1); -+ _DBG("header_len=%d headers=\"%s\"", header_len, headers); -+ -+if (header_len) { -+ _DBG("header_len > 0, we got a request", 0); -+ /* -- store received data into an brigade and add -+ it to the current transaction's pool -- */ -+ bucket = apr_bucket_eos_create(alloc); -+ APR_BRIGADE_INSERT_HEAD(bb, bucket); -+ bucket = apr_bucket_socket_create(*trans_sock, alloc); -+ APR_BRIGADE_INSERT_HEAD(bb, bucket); -+ -+ if (body_len) { -+ body = (char*)&buff[header_len + 1]; -+ _DBG("body_len=%d body=\"%s\"", body_len, body); -+ -+ bucket = apr_bucket_heap_create(body, body_len, NULL, alloc); -+ APR_BRIGADE_INSERT_HEAD(bb, bucket); -+ } else { -+ _DBG("There is no body",0); -+ } -+ -+ bucket = apr_bucket_heap_create(headers, header_len, NULL, alloc); -+ -+ APR_BRIGADE_INSERT_HEAD(bb, bucket); -+ apr_pool_userdata_set(bb, "PERUSER_SOCKETS", NULL, ptrans); -+} else { -+ _DBG("header_len == 0, we got a socket only", 0); -+} -+ _DBG("returning 0", 0); -+ return 0; -+} -+ -+ -+/* Set group privileges. -+ * -+ * Note that we use the username as set in the config files, rather than -+ * the lookup of to uid --- the same uid may have multiple passwd entries, -+ * with different sets of groups for each. -+ */ -+ -+static int set_group_privs(uid_t uid, gid_t gid) -+{ -+ if (!geteuid()) -+ { -+ struct passwd *ent; -+ const char *name; -+ -+ /* -+ * Set the GID before initgroups(), since on some platforms -+ * setgid() is known to zap the group list. -+ */ -+ if (setgid(gid) == -1) -+ { -+ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, -+ "setgid: unable to set group id to Group %u", -+ (unsigned)gid); -+ return -1; -+ } -+ -+ /* if getpwuid() fails, just skip initgroups() */ -+ -+ if ((ent = getpwuid(uid)) != NULL) -+ { -+ name = ent->pw_name; -+ -+ /* Reset `groups' attributes. */ -+ -+ if (initgroups(name, gid) == -1) -+ { -+ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, -+ "initgroups: unable to set groups for User %s " -+ "and Group %u", name, (unsigned)gid); -+ return -1; -+ } -+ } -+ } -+ return 0; -+} -+ -+static int peruser_setup_child(int childnum) -+{ -+ server_env_t *senv = CHILD_INFO_TABLE[childnum].senv; -+ -+ if(senv->chroot) { -+ _DBG("chroot to %s", senv->chroot); -+ if(chroot(senv->chroot)) { -+ _DBG("chroot failure %s", senv->chroot); -+ return; -+ } -+ } -+ -+ if (senv->uid == -1 && senv->gid == -1) { -+ return unixd_setup_child(); -+ } -+ if (set_group_privs(senv->uid, senv->gid)) { -+ return -1; -+ } -+ /* Only try to switch if we're running as root */ -+ if (!geteuid() -+ && ( -+#ifdef _OSD_POSIX -+ os_init_job_environment(ap_server_conf, unixd_config.user_name, -+ one_process) != 0 || -+#endif -+ setuid(senv->uid) == -1)) { -+ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, -+ "setuid: unable to change to uid: %ld", -+ (long) senv->uid); -+ return -1; -+ } -+ return 0; -+} -+ -+static int check_signal(int signum) -+{ -+ _DBG("signum=%d", signum); -+ switch (signum) { -+ case SIGTERM: -+ case SIGINT: -+ just_die(signum); -+ return 1; -+ } -+ return 0; -+} -+ -+/* Send a single HTTP header field to the client. Note that this function -+ * is used in calls to table_do(), so their interfaces are co-dependent. -+ * In other words, don't change this one without checking table_do in alloc.c. -+ * It returns true unless there was a write error of some kind. -+ */ -+static int peruser_header_field(peruser_header *h, -+ const char *fieldname, const char *fieldval) -+{ -+ apr_pstrcat(h->p, h->headers, fieldname, ": ", fieldval, CRLF, NULL); -+ return 1; -+} -+ -+static inline ap_listen_rec* listen_add(apr_pool_t* pool, apr_socket_t *sock, void* accept_func) -+{ -+ ap_listen_rec *lr_walk, *lr_new; -+ -+ _DBG("function entered", 0); -+ /* -- create an new listener for this child -- */ -+ lr_new = apr_palloc(pool, sizeof(*lr_new)); -+ lr_new->sd = sock; -+ lr_new->active = 1; -+ lr_new->accept_func = accept_func; -+ lr_new->next = NULL; -+ -+ /* -- add the new listener_rec into the list -- */ -+ /* FIXME: should we somehow lock this list ? */ -+ lr_walk = ap_listeners; -+ if (lr_walk) -+ { -+ while (lr_walk->next) lr_walk = lr_walk->next; -+ lr_walk->next = lr_new; -+ } -+ else -+ { -+ ap_listeners = lr_walk = lr_new; -+ } -+ num_listensocks++; -+ return lr_new; -+} -+ -+static inline void listen_clear() -+{ -+ ap_listen_rec *lr_walk; -+ -+ _DBG("function entered", 0); -+ -+ /* FIXME: should we somehow lock this list ? */ -+ while (ap_listeners) -+ { -+ lr_walk = ap_listeners->next; -+ apr_socket_close(ap_listeners->sd); -+ ap_listeners = lr_walk; -+ } -+ num_listensocks=0; -+} -+ -+apr_status_t cleanup_child_info(void *d) -+{ -+ if (child_info_image == NULL) { -+ return APR_SUCCESS; -+ } -+ -+ free(child_info_image); -+ child_info_image = NULL; -+ apr_shm_destroy(child_info_shm); -+ -+ return APR_SUCCESS; -+} -+ -+apr_status_t cleanup_server_environments(void *d) -+{ -+ if (server_env_image == NULL) { -+ return APR_SUCCESS; -+ } -+ -+ free(server_env_image); -+ server_env_image = NULL; -+ apr_shm_destroy(server_env_shm); -+ -+ return APR_SUCCESS; -+} -+ -+static const char* child_clone(); -+ -+static void child_main(int child_num_arg) -+{ -+ apr_pool_t *ptrans; -+ apr_allocator_t *allocator; -+ conn_rec *current_conn; -+ apr_status_t status = APR_EINIT; -+ int i; -+ ap_listen_rec *lr; -+ int curr_pollfd, last_pollfd = 0; -+ apr_pollfd_t *pollset; -+ int offset; -+ ap_sb_handle_t *sbh; -+ apr_status_t rv; -+ apr_bucket_alloc_t *bucket_alloc; -+ int fd; -+ void* sock; -+ void* pod_sock; -+ -+ mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this -+ * child initializes -+ */ -+ -+ my_child_num = child_num_arg; -+ ap_my_pid = getpid(); -+ requests_this_child = 0; -+ -+ _DBG("sock_fd_in=%d sock_fd_out=%d", -+ CHILD_INFO_TABLE[my_child_num].senv->input, -+ CHILD_INFO_TABLE[my_child_num].senv->output); -+ -+ /* Get a sub context for global allocations in this child, so that -+ * we can have cleanups occur when the child exits. -+ */ -+ apr_allocator_create(&allocator); -+ 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_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); -+ -+ rv = apr_proc_mutex_child_init(&accept_mutex, ap_lock_fname, pchild); -+ if (rv != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, -+ "Couldn't initialize cross-process lock in child"); -+ clean_child_exit(APEXIT_CHILDFATAL); -+ } -+ -+ switch(CHILD_INFO_TABLE[my_child_num].type) -+ { -+ case CHILD_TYPE_MULTIPLEXER: -+ _DBG("MULTIPLEXER %d", my_child_num); -+ -+ /* update status on processors that are ready to accept requests */ -+ _DBG("updating processor stati", 0); -+ for(i = 0; i < NUM_CHILDS; ++i) -+ { -+ if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY) -+ CHILD_INFO_TABLE[i].status = CHILD_STATUS_ACTIVE; -+ } -+ -+ break; -+ -+ case CHILD_TYPE_PROCESSOR: -+ case CHILD_TYPE_WORKER: -+ _DBG("%s %d", child_type_string(CHILD_INFO_TABLE[my_child_num].type), my_child_num); -+ -+ /* -- create new listener to receive from multiplexer -- */ -+ apr_os_sock_put((void*)&sock, &CHILD_INFO_TABLE[my_child_num].senv->input, pconf); -+ listen_clear(); -+ listen_add(pconf, sock, receive_from_multiplexer); -+ -+ break; -+ -+ default: -+ _DBG("unspecified child type for %d sleeping a while ...", my_child_num); -+ sleep(5); -+ return; -+ } -+ -+ apr_os_file_get(&fd, pipe_of_death_in); -+ apr_os_sock_put((void*)&pod_sock, &fd, pconf); -+ listen_add(pconf, pod_sock, check_pipe_of_death); -+ -+ (peruser_setup_child(my_child_num) && clean_child_exit(APEXIT_CHILDFATAL)); -+ ap_run_child_init(pchild, ap_server_conf); -+ -+ ap_create_sb_handle(&sbh, pchild, my_child_num, 0); -+ (void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL); -+ -+ /* Set up the pollfd array */ -+ listensocks = apr_pcalloc(pchild, -+ sizeof(*listensocks) * (num_listensocks)); -+ for (lr = ap_listeners, i = 0; i < num_listensocks; lr = lr->next, i++) { -+ listensocks[i].accept_func = lr->accept_func; -+ listensocks[i].sd = lr->sd; -+ } -+ -+ pollset = apr_palloc(pchild, sizeof(*pollset) * num_listensocks); -+ pollset[0].p = pchild; -+ for (i = 0; i < num_listensocks; i++) { -+ pollset[i].desc.s = listensocks[i].sd; -+ pollset[i].desc_type = APR_POLL_SOCKET; -+ pollset[i].reqevents = APR_POLLIN; -+ } -+ -+ mpm_state = AP_MPMQ_RUNNING; -+ -+ bucket_alloc = apr_bucket_alloc_create(pchild); -+ -+ while (!die_now) { -+ /* -+ * (Re)initialize this child to a pre-connection state. -+ */ -+ -+ current_conn = NULL; -+ -+ apr_pool_clear(ptrans); -+ -+ if (CHILD_INFO_TABLE[my_child_num].type != CHILD_TYPE_MULTIPLEXER -+ && ap_max_requests_per_child > 0 -+ && requests_this_child++ >= ap_max_requests_per_child) { -+ _DBG("max requests reached, dying now", 0); -+ clean_child_exit(0); -+ } -+ -+ (void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL); -+ -+ /* -+ * Wait for an acceptable connection to arrive. -+ */ -+ -+ /* Lock around "accept", if necessary */ -+ SAFE_ACCEPT(accept_mutex_on()); -+ -+ if (num_listensocks == 1) { -+ offset = 0; -+ } -+ else { -+ /* multiple listening sockets - need to poll */ -+ for (;;) { -+ apr_status_t ret; -+ apr_int32_t n; -+ -+ ret = apr_poll(pollset, num_listensocks, &n, -1); -+ if (ret != APR_SUCCESS) { -+ if (APR_STATUS_IS_EINTR(ret)) { -+ continue; -+ } -+ /* Single Unix documents select as returning errnos -+ * EBADF, EINTR, and EINVAL... and in none of those -+ * cases does it make sense to continue. In fact -+ * on Linux 2.0.x we seem to end up with EFAULT -+ * occasionally, and we'd loop forever due to it. -+ */ -+ ap_log_error(APLOG_MARK, APLOG_ERR, ret, ap_server_conf, -+ "apr_poll: (listen)"); -+ clean_child_exit(1); -+ } -+ /* find a listener */ -+ curr_pollfd = last_pollfd; -+ do { -+ curr_pollfd++; -+ if (curr_pollfd >= num_listensocks) { -+ curr_pollfd = 0; -+ } -+ /* XXX: Should we check for POLLERR? */ -+ if (pollset[curr_pollfd].rtnevents & APR_POLLIN) { -+ last_pollfd = curr_pollfd; -+ offset = curr_pollfd; -+ goto got_fd; -+ } -+ } while (curr_pollfd != last_pollfd); -+ -+ continue; -+ } -+ } -+ got_fd: -+ _DBG("input available ... resetting socket.",0); -+ sock = NULL; /* important! */ -+ -+ /* if we accept() something we don't want to die, so we have to -+ * defer the exit -+ */ -+ status = listensocks[offset].accept_func(&sock, &listensocks[offset], ptrans); -+ SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ -+ -+ if (status == APR_EGENERAL) { -+ /* resource shortage or should-not-occur occured */ -+ clean_child_exit(1); -+ } -+ else if (status != APR_SUCCESS || die_now) { -+ continue; -+ } -+ -+ if (CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_PROCESSOR || -+ CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER) -+ { -+ _DBG("CHECKING IF WE SHOULD CLONE A CHILD..."); -+ -+ _DBG("total_processors = %d, max_processors = %d", -+ total_processors(my_child_num), -+ CHILD_INFO_TABLE[my_child_num].senv->max_processors); -+ -+ _DBG("idle_processors = %d, min_free_processors = %d", -+ idle_processors(my_child_num), -+ CHILD_INFO_TABLE[my_child_num].senv->min_free_processors); -+ -+ if(total_processors(my_child_num) < -+ CHILD_INFO_TABLE[my_child_num].senv->max_processors && -+ idle_processors(my_child_num) <= -+ CHILD_INFO_TABLE[my_child_num].senv->min_free_processors) -+ { -+ _DBG("CLONING CHILD"); -+ child_clone(); -+ } -+ } -+ -+ if (!setjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer)) -+ { -+ _DBG("marked jmpbuffer",0); -+ _TRACE_CALL("process_socket()",0); -+ process_socket(ptrans, sock, my_child_num, bucket_alloc, pchild); -+ _TRACE_RET("process_socket()",0); -+ } -+ else -+ { -+ _DBG("landed from longjmp",0); -+ CHILD_INFO_TABLE[my_child_num].sock_fd = AP_PERUSER_THISCHILD; -+ } -+ -+ /* Check the pod and the generation number after processing a -+ * connection so that we'll go away if a graceful restart occurred -+ * while we were processing the connection or we are the lucky -+ * idle server process that gets to die. -+ */ -+ if (ap_mpm_pod_check(pod) == APR_SUCCESS) { /* selected as idle? */ -+ _DBG("ap_mpm_pod_check(pod) = APR_SUCCESS; dying now", 0); -+ die_now = 1; -+ } -+ else 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? -+ */ -+ _DBG("ap_my_generation != ap_scoreboard_image->global->running_generation; dying now", 0); -+ die_now = 1; -+ } -+ -+ if(CHILD_INFO_TABLE[my_child_num].status == CHILD_STATUS_RESTART) -+ { -+ _DBG("restarting", 0); -+ die_now = 1; -+ } -+ } -+ -+ _DBG("clean_child_exit(0)"); -+ clean_child_exit(0); -+} -+ -+static server_env_t* senv_add(int uid, int gid, const char* chroot) -+{ -+ int i; -+ int socks[2]; -+ -+ _DBG("Searching for matching senv..."); -+ -+ for(i = 0; i < NUM_SENV; i++) -+ { -+ if(SENV[i].uid == uid && SENV[i].gid == gid && -+ (SENV[i].chroot == NULL || !strcmp(SENV[i].chroot, chroot))) -+ { -+ _DBG("Found existing senv: %i", i); -+ return &SENV[i]; -+ } -+ } -+ -+ if(NUM_SENV >= server_limit) -+ { -+ _DBG("server_limit reached!"); -+ return NULL; -+ } -+ -+ _DBG("Creating new senv"); -+ -+ SENV[NUM_SENV].uid = uid; -+ SENV[NUM_SENV].gid = gid; -+ SENV[NUM_SENV].chroot = chroot; -+ -+ SENV[NUM_SENV].min_processors = ap_min_processors; -+ SENV[NUM_SENV].min_free_processors = ap_min_free_processors; -+ SENV[NUM_SENV].max_processors = ap_max_processors; -+ -+ socketpair(PF_UNIX, SOCK_STREAM, 0, socks); -+ SENV[NUM_SENV].input = socks[0]; -+ SENV[NUM_SENV].output = socks[1]; -+ -+ return &SENV[server_env_image->control->num++]; -+} -+ -+static const char* child_clone() -+{ -+ int i; -+ child_info_t *this; -+ child_info_t *new; -+ -+ if(NUM_CHILDS >= server_limit) -+ { -+ _DBG("Trying to use more child ID's than NumServers. " -+ "Increase NumServers in your config file."); -+ return NULL; -+ } -+ -+ for(i = 0; i < NUM_CHILDS; i++) -+ { -+ if(CHILD_INFO_TABLE[i].pid == 0 && -+ CHILD_INFO_TABLE[i].type == CHILD_TYPE_UNKNOWN) break; -+ } -+ -+ _DBG("cloning child #%d from #%d", i, my_child_num); -+ -+ this = &CHILD_INFO_TABLE[my_child_num]; -+ new = &CHILD_INFO_TABLE[i]; -+ -+ new->senv = this->senv; -+ new->type = CHILD_TYPE_WORKER; -+ new->sock_fd = this->sock_fd; -+ new->status = CHILD_STATUS_STARTING; -+ -+ if(i == NUM_CHILDS) child_info_image->control->num++; -+ return NULL; -+} -+ -+static const char* child_add(int type, int status, -+ uid_t uid, gid_t gid, const char* chroot) -+{ -+ _DBG("adding child #%d", NUM_CHILDS); -+ -+ if(NUM_CHILDS >= server_limit) -+ { -+ return "Trying to use more child ID's than NumServers. " -+ "Increase NumServers in your config file."; -+ } -+ -+ CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(uid, gid, chroot); -+ -+ if(CHILD_INFO_TABLE[NUM_CHILDS].senv == NULL) -+ { -+ return "Trying to use more server environments than NumServers. " -+ "Increase NumServers in your config file."; -+ } -+ -+ if(type != CHILD_TYPE_WORKER) -+ CHILD_INFO_TABLE[NUM_CHILDS].senv->processor_id = NUM_CHILDS; -+ -+ CHILD_INFO_TABLE[NUM_CHILDS].type = type; -+ CHILD_INFO_TABLE[NUM_CHILDS].sock_fd = AP_PERUSER_THISCHILD; -+ CHILD_INFO_TABLE[NUM_CHILDS].status = status; -+ -+ _DBG("[%d] uid=%d gid=%d type=%d chroot=%s", -+ NUM_CHILDS, uid, gid, type, -+ chroot); -+ -+ if (uid == 0 || gid == 0) -+ { -+ _DBG("Assigning root user/group to a child.", 0); -+ } -+ -+ server_env_image->control->num++; -+ child_info_image->control->num++; -+ -+ return NULL; -+} -+ -+static int make_child(server_rec *s, int slot) -+{ -+ int pid; -+ int socks[2]; -+ child_info_t *multiplexer; -+ -+ _DBG("function entered", 0); -+ -+ switch (CHILD_INFO_TABLE[slot].type) -+ { -+ case CHILD_TYPE_MULTIPLEXER: break; -+ case CHILD_TYPE_PROCESSOR: break; -+ case CHILD_TYPE_WORKER: break; -+ -+ default: -+ _DBG("no valid client in slot %d", slot); -+ /* sleep(1); */ -+ return 0; -+ } -+ -+ if (slot + 1 > ap_max_daemons_limit) { -+ ap_max_daemons_limit = slot + 1; -+ } -+ -+ if (one_process) { -+ apr_signal(SIGHUP, just_die); -+ /* Don't catch AP_SIG_GRACEFUL in ONE_PROCESS mode :) */ -+ apr_signal(SIGINT, just_die); -+#ifdef SIGQUIT -+ apr_signal(SIGQUIT, SIG_DFL); -+#endif -+ apr_signal(SIGTERM, just_die); -+ child_main(slot); -+ } -+ -+ (void) ap_update_child_status_from_indexes(slot, 0, SERVER_STARTING, -+ (request_rec *) NULL); -+ -+ CHILD_INFO_TABLE[slot].status = CHILD_STATUS_ACTIVE; -+ -+ -+#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) { -+#else -+ if ((pid = fork()) == -1) { -+#endif -+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "fork: Unable to fork new process"); -+ -+ /* fork didn't succeed. Fix the scoreboard or else -+ * it will say SERVER_STARTING forever and ever -+ */ -+ (void) ap_update_child_status_from_indexes(slot, 0, SERVER_DEAD, -+ (request_rec *) NULL); -+ -+ /* In case system resources are maxxed out, we don't want -+ Apache running away with the CPU trying to fork over and -+ over and over again. */ -+ sleep(10); -+ -+ return -1; -+ } -+ -+ if (!pid) { -+#ifdef HAVE_BINDPROCESSOR -+ /* by default AIX binds to a single processor -+ * this bit unbinds children which will then bind to another cpu -+ */ -+ 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); -+ } -+#endif -+ RAISE_SIGSTOP(MAKE_CHILD); -+ AP_MONCONTROL(1); -+ /* Disable the parent's signal handlers and set up proper handling in -+ * the child. -+ */ -+ apr_signal(SIGHUP, just_die); -+ apr_signal(SIGTERM, just_die); -+ /* The child process doesn't do anything for AP_SIG_GRACEFUL. -+ * Instead, the pod is used for signalling graceful restart. -+ */ -+ /* apr_signal(AP_SIG_GRACEFUL, restart); */ -+ child_main(slot); -+ clean_child_exit(0); -+ } -+ -+ ap_scoreboard_image->parent[slot].pid = pid; -+ CHILD_INFO_TABLE[slot].pid = pid; -+ -+ ap_child_table[slot].pid = pid; -+ ap_child_table[slot].status = SERVER_ALIVE; -+ -+ return 0; -+} -+ -+ -+/* -+ * 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 int total_processes(int child_num) -+{ -+ int i, total; -+ for(i = 0, total = 0; i < NUM_CHILDS; ++i) -+ { -+ if(CHILD_INFO_TABLE[i].senv == CHILD_INFO_TABLE[child_num].senv && -+ (!(CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR && -+ CHILD_INFO_TABLE[i].status == CHILD_STATUS_STANDBY))) -+ { -+ total++; -+ } -+ } -+ return total; -+} -+ -+static void perform_idle_server_maintenance(apr_pool_t *p) -+{ -+ int i; -+ apr_time_t now; -+ -+ /* _DBG("function entered", 0); */ -+ -+ now = apr_time_now(); -+ -+ for (i = 0; i < NUM_CHILDS; ++i) -+ { -+ if(CHILD_INFO_TABLE[i].pid == 0) -+ { -+ if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_STARTING) -+ make_child(ap_server_conf, i); -+ } -+ else if(((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR || -+ CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER) && -+ ap_scoreboard_image->parent[i].pid > 1) && -+ (idle_processors (i) > 1 || total_processes (i) == 1) && ( -+ (expire_timeout > 0 && ap_scoreboard_image->servers[i][0].status != SERVER_DEAD && -+ apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > expire_timeout) || -+ (idle_timeout > 0 && ap_scoreboard_image->servers[i][0].status == SERVER_READY && -+ apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > idle_timeout))) -+ { -+ CHILD_INFO_TABLE[i].pid = 0; -+ CHILD_INFO_TABLE[i].status = CHILD_STATUS_STANDBY; -+ -+ if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER) -+ { -+ /* completely free up this slot */ -+ -+ CHILD_INFO_TABLE[i].senv = (server_env_t*)NULL; -+ CHILD_INFO_TABLE[i].type = CHILD_TYPE_UNKNOWN; -+ CHILD_INFO_TABLE[i].sock_fd = -3; /* -1 and -2 are taken */ -+ } -+ if(kill(ap_scoreboard_image->parent[i].pid, SIGTERM) == -1) -+ { -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, -+ ap_server_conf, "kill SIGTERM"); -+ } -+ -+ -+ ap_update_child_status_from_indexes(i, 0, SERVER_DEAD, NULL); -+ } -+ } -+} -+ -+ -+/***************************************************************** -+ * Executive routines. -+ */ -+ -+int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) -+{ -+ int i; -+/* int fd; */ -+ apr_status_t rv; -+ apr_size_t one = 1; -+/* apr_socket_t *sock = NULL; */ -+ -+ 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; -+ } -+ -+ ap_server_conf = s; -+ -+ /* 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); -+ 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 0 -+#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; -+ } -+ } -+#endif -+ -+ if (!is_graceful) { -+ if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) { -+ mpm_state = AP_MPMQ_STOPPING; -+ return 1; -+ } -+ /* fix the generation number in the global score; we just got a new, -+ * cleared scoreboard -+ */ -+ ap_scoreboard_image->global->running_generation = ap_my_generation; -+ } -+ -+ /* Initialize the child table */ -+ if (!is_graceful) -+ { -+ for (i = 0; i < server_limit; i++) -+ { -+ ap_child_table[i].pid = 0; -+ } -+ } -+ -+ /* We need to put the new listeners at the end of the ap_listeners -+ * list. If we don't, then the pool will be cleared before the -+ * open_logs phase is called for the second time, and ap_listeners -+ * will have only invalid data. If that happens, then the sockets -+ * that we opened using make_sock() will be lost, and the server -+ * won't start. -+ */ -+ -+/* -+ apr_os_file_get(&fd, pipe_of_death_in); -+ apr_os_sock_put(&sock, &fd, pconf); -+ -+ listen_add(pconf, sock, check_pipe_of_death); -+*/ -+ set_signals(); -+ -+ if (one_process) { -+ AP_MONCONTROL(1); -+ } -+ -+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, -+ "%s configured -- resuming normal operations", -+ ap_get_server_version()); -+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, -+ "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 -+ restart_pending = shutdown_pending = 0; -+ -+ mpm_state = AP_MPMQ_RUNNING; -+ -+ _DBG("sizeof(child_info_t) = %d", sizeof(child_info_t)); -+ -+ while (!restart_pending && !shutdown_pending) { -+ int child_slot; -+ apr_exit_why_e exitwhy; -+ int status, processed_status; -+ /* this is a memory leak, but I'll fix it later. */ -+ apr_proc_t pid; -+ -+ ap_wait_or_timeout(&exitwhy, &status, &pid, pconf); -+ -+ /* XXX: if it takes longer than 1 second for all our children -+ * to start up and get into IDLE state then we may spawn an -+ * extra child -+ */ -+ if (pid.pid != -1) { -+ processed_status = ap_process_child_status(&pid, exitwhy, status); -+ if (processed_status == APEXIT_CHILDFATAL) { -+ mpm_state = AP_MPMQ_STOPPING; -+ return 1; -+ } -+ -+ /* non-fatal death... note that it's gone in the scoreboard. */ -+ child_slot = find_child_by_pid(&pid); -+ _DBG("child #%d has died ...", child_slot); -+ -+ for (i = 0; i < ap_max_daemons_limit; ++i) -+ { -+ if (ap_child_table[i].pid == pid.pid) -+ { -+ child_slot = i; -+ break; -+ } -+ } -+ -+ if (child_slot >= 0) { -+ ap_child_table[child_slot].pid = 0; -+ _TRACE_CALL("ap_update_child_status_from_indexes", 0); -+ (void) ap_update_child_status_from_indexes(child_slot, 0, SERVER_DEAD, -+ (request_rec *) NULL); -+ _TRACE_RET("ap_update_child_status_from_indexes", 0); -+ -+ if (processed_status == APEXIT_CHILDSICK) { -+ /* child detected a resource shortage (E[NM]FILE, ENOBUFS, etc) -+ * cut the fork rate to the minimum -+ */ -+ _DBG("processed_status = APEXIT_CHILDSICK", 0); -+ idle_spawn_rate = 1; -+ } -+ else if (CHILD_INFO_TABLE[child_slot].status == CHILD_STATUS_STANDBY) { -+ _DBG("leaving child in standby state", 0); -+ } -+ else if (child_slot < ap_daemons_limit && -+ CHILD_INFO_TABLE[child_slot].type != -+ CHILD_TYPE_UNKNOWN) { -+ /* we're still doing a 1-for-1 replacement of dead -+ * children with new children -+ */ -+ _DBG("replacing by new child ...", 0); -+ make_child(ap_server_conf, child_slot); -+ } -+#if APR_HAS_OTHER_CHILD -+ } -+ else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH, status) == APR_SUCCESS) { -+ _DBG("Already handled", 0); -+ /* handled */ -+#endif -+ } -+ else if (is_graceful) { -+ /* Great, we've probably just lost a slot in the -+ * scoreboard. Somehow we don't know about this -+ * child. -+ */ -+ _DBG("long lost child came home, whatever that means", 0); -+ -+ ap_log_error(APLOG_MARK, APLOG_WARNING, -+ 0, ap_server_conf, -+ "long lost child came home! (pid %ld)", (long)pid.pid); -+ } -+ /* Don't perform idle maintenance when a child dies, -+ * only do it when there's a timeout. Remember only a -+ * finite number of children can die, and it's pretty -+ * pathological for a lot to die suddenly. -+ */ -+ continue; -+ } -+ -+ perform_idle_server_maintenance(pconf); -+#ifdef TPF -+ shutdown_pending = os_check_server(tpf_server_name); -+ ap_check_signals(); -+ sleep(1); -+#endif /*TPF */ -+ } -+ -+ mpm_state = AP_MPMQ_STOPPING; -+ -+ if (shutdown_pending) { -+ /* Time to gracefully 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"); -+ } -+ ap_reclaim_child_processes(1); /* Start with SIGTERM */ -+ -+ /* cleanup pid file on normal shutdown */ -+ { -+ const char *pidfile = NULL; -+ pidfile = ap_server_root_relative (pconf, ap_pid_fname); -+ if ( pidfile != NULL && unlink(pidfile) == 0) -+ ap_log_error(APLOG_MARK, APLOG_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, -+ "caught SIGTERM, shutting down"); -+ return 1; -+ } -+ -+ /* we've been told to restart */ -+ apr_signal(SIGHUP, SIG_IGN); -+ if (one_process) { -+ /* not worth thinking about */ -+ return 1; -+ } -+ -+ /* advance to the next generation */ -+ /* XXX: we really need to make sure this new generation number isn't in -+ * use by any of the children. -+ */ -+ ++ap_my_generation; -+ ap_scoreboard_image->global->running_generation = ap_my_generation; -+ -+ if (is_graceful) { -+ char char_of_death = AP_PERUSER_CHAR_OF_DEATH; -+ -+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, -+ "Graceful restart requested, doing restart"); -+ -+#if 0 -+ /* kill off the idle ones */ -+ ap_mpm_pod_killpg(pod, ap_max_daemons_limit); -+ -+ /* This is mostly for debugging... so that we know what is still -+ * gracefully dealing with existing request. This will break -+ * in a very nasty way if we ever have the scoreboard totally -+ * file-based (no shared memory) -+ */ -+ for (i = 0; i < ap_daemons_limit; ++i) { -+ if (ap_scoreboard_image->servers[i][0].status != SERVER_DEAD) { -+ ap_scoreboard_image->servers[i][0].status = SERVER_GRACEFUL; -+ } -+ } -+#endif -+ -+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, -+ ap_server_conf, AP_SIG_GRACEFUL_STRING " received. " -+ "Doing graceful restart"); -+ -+ /* This is mostly for debugging... so that we know what is still -+ * gracefully dealing with existing request. -+ */ -+ -+ for (i = 0; i < NUM_CHILDS; ++i) -+ { -+ ((ap_child_table[i].pid) && (ap_child_table[i].status = SERVER_DYING)); -+ } -+ -+ /* give the children the signal to die */ -+ for (i = 0; i < NUM_CHILDS;) -+ { -+ if ((rv = apr_file_write(pipe_of_death_out, &char_of_death, &one)) != APR_SUCCESS) -+ { -+ if (APR_STATUS_IS_EINTR(rv)) continue; -+ ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, -+ "write pipe_of_death"); -+ } -+ i++; -+ } -+ -+ for (i = 0; i < NUM_SENV; i++) -+ { -+ close(SENV[i].input); -+ close(SENV[i].output); -+ } -+ } -+ else { -+ /* Kill 'em off */ -+ if (unixd_killpg(getpgrp(), SIGHUP) < 0) { -+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGHUP"); -+ } -+ ap_reclaim_child_processes(0); /* Not when just starting up */ -+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, -+ "SIGHUP received. Attempting to restart"); -+ } -+ -+ return 0; -+} -+ -+/* == allocate an private server config structure == */ -+static void *peruser_create_config(apr_pool_t *p, server_rec *s) -+{ -+ peruser_server_conf *c = (peruser_server_conf *) -+ apr_pcalloc(p, sizeof(peruser_server_conf)); -+ return c; -+} -+ -+/* 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 peruser_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) -+{ -+ apr_status_t rv; -+ -+ pconf = p; -+ ap_server_conf = s; -+ -+ 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"); -+ return DONE; -+ } -+ -+ ap_log_pid(pconf, ap_pid_fname); -+ -+ 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."); -+ return DONE; -+ } -+ -+ if ((rv = apr_file_pipe_create(&pipe_of_death_in, &pipe_of_death_out, -+ pconf)) != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, -+ (const server_rec*) ap_server_conf, -+ "apr_file_pipe_create (pipe_of_death)"); -+ exit(1); -+ } -+ if ((rv = apr_file_pipe_timeout_set(pipe_of_death_in, 0)) != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, -+ (const server_rec*) ap_server_conf, -+ "apr_file_pipe_timeout_set (pipe_of_death)"); -+ exit(1); -+ } -+ -+ return OK; -+} -+ -+static int peruser_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) -+{ -+ static int restart_num = 0; -+ int no_detach, debug, foreground, i; -+ int tmp_server_limit = DEFAULT_SERVER_LIMIT; -+ ap_directive_t *pdir; -+ apr_status_t rv; -+ apr_pool_t *global_pool; -+ void *shmem; -+ -+ mpm_state = AP_MPMQ_STARTING; -+ -+ debug = ap_exists_config_define("DEBUG"); -+ -+ if (debug) { -+ foreground = one_process = 1; -+ no_detach = 0; -+ } -+ else -+ { -+ no_detach = ap_exists_config_define("NO_DETACH"); -+ one_process = ap_exists_config_define("ONE_PROCESS"); -+ 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; -+ } -+ } -+ -+ parent_pid = ap_my_pid = getpid(); -+ } -+ -+ unixd_pre_config(ptemp); -+ ap_listen_pre_config(); -+ ap_min_processors = DEFAULT_MIN_PROCESSORS; -+ ap_min_free_processors = DEFAULT_MIN_FREE_PROCESSORS; -+ ap_max_processors = DEFAULT_MAX_PROCESSORS; -+ 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 = 1; -+#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)); -+ -+ /* we need to know ServerLimit and ThreadLimit before we start processing -+ * the tree because we need to already have allocated child_info_table -+ */ -+ for (pdir = ap_conftree; pdir != NULL; pdir = pdir->next) -+ { -+ if (!strcasecmp(pdir->directive, "ServerLimit")) -+ { -+ if (atoi(pdir->args) > tmp_server_limit) -+ { -+ tmp_server_limit = atoi(pdir->args); -+ if (tmp_server_limit > MAX_SERVER_LIMIT) -+ { -+ tmp_server_limit = MAX_SERVER_LIMIT; -+ } -+ } -+ } -+ } -+ -+ /* We don't want to have to recreate the scoreboard after -+ * restarts, so we'll create a global pool and never clean it. -+ */ -+ rv = apr_pool_create(&global_pool, NULL); -+ if (rv != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, -+ "Fatal error: unable to create global pool"); -+ return rv; -+ } -+ -+ -+ _DBG("Initializing child_info_table", 0); -+ child_info_size = tmp_server_limit * sizeof(child_info_t) + sizeof(apr_size_t); -+ -+ rv = apr_shm_create(&child_info_shm, child_info_size, NULL, global_pool); -+ -+/* if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) { */ -+ if (rv != APR_SUCCESS) { -+ _DBG("shared memory creation failed", 0); -+ -+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, -+ "Unable to create shared memory segment " -+ "(anonymous shared memory failure)"); -+ } -+ else if (rv == APR_ENOTIMPL) { -+ _DBG("anonymous shared memory not available", 0); -+ /* TODO: make up a filename and do name-based shmem */ -+ } -+ -+ if (rv || !(shmem = apr_shm_baseaddr_get(child_info_shm))) { -+ _DBG("apr_shm_baseaddr_get() failed", 0); -+ return HTTP_INTERNAL_SERVER_ERROR; -+ } -+ -+ memset(shmem, 0, sizeof(child_info_size)); -+ child_info_image = (child_info*)calloc(1, sizeof(child_info_size)); -+ child_info_image->control = (child_info_control*)shmem; -+ shmem += sizeof(child_info_control*); -+ child_info_image->table = (child_info_t*)shmem; -+ -+ child_info_image->control->num = 0; -+ -+ for (i = 0; i < tmp_server_limit; i++) -+ { -+ CHILD_INFO_TABLE[i].pid = 0; -+ CHILD_INFO_TABLE[i].senv = (server_env_t*)NULL; -+ CHILD_INFO_TABLE[i].type = CHILD_TYPE_UNKNOWN; -+ CHILD_INFO_TABLE[i].status = CHILD_STATUS_STANDBY; -+ CHILD_INFO_TABLE[i].sock_fd = -3; /* -1 and -2 are taken */ -+ CHILD_INFO_TABLE[i].id = i; -+ } -+ -+ _DBG("Initializing server_environments_table", 0); -+ server_env_size = tmp_server_limit * sizeof(server_env_t) + sizeof(apr_size_t); -+ -+ rv = apr_shm_create(&server_env_shm, server_env_size, NULL, global_pool); -+ -+ if (rv != APR_SUCCESS) { -+ _DBG("shared memory creation failed", 0); -+ -+ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, -+ "Unable to create shared memory segment " -+ "(anonymous shared memory failure)"); -+ } -+ else if (rv == APR_ENOTIMPL) { -+ _DBG("anonymous shared memory not available", 0); -+ /* TODO: make up a filename and do name-based shmem */ -+ } -+ -+ if (rv || !(shmem = apr_shm_baseaddr_get(server_env_shm))) { -+ _DBG("apr_shm_baseaddr_get() failed", 0); -+ return HTTP_INTERNAL_SERVER_ERROR; -+ } -+ -+ memset(shmem, 0, sizeof(server_env_size)); -+ server_env_image = (server_env*)calloc(1, sizeof(server_env_size)); -+ server_env_image->control = (server_env_control*)shmem; -+ shmem += sizeof(server_env_control*); -+ server_env_image->table = (server_env_t*)shmem; -+ -+ server_env_image->control->num = 0; -+ -+ for (i = 0; i < tmp_server_limit; i++) -+ { -+ SENV[i].processor_id = -1; -+ SENV[i].uid = -1; -+ SENV[i].gid = -1; -+ SENV[i].chroot = NULL; -+ SENV[i].input = -1; -+ SENV[i].output = -1; -+ } -+ -+ return OK; -+} -+ -+static int peruser_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *server_list) -+{ -+ ap_child_table = (ap_ctable *)apr_pcalloc(p, server_limit * sizeof(ap_ctable)); -+ -+ return OK; -+} -+ -+static int peruser_post_read(request_rec *r) -+{ -+ peruser_server_conf *sconf = PERUSER_SERVER_CONF(r->server->module_config); -+ child_info_t *processor; -+ -+ if(CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER) -+ processor = &CHILD_INFO_TABLE[sconf->senv->processor_id]; -+ else -+ processor = &CHILD_INFO_TABLE[r->connection->id]; -+ -+ -+ if (!strlen(r->the_request)) -+ { -+ _DBG("corrupt request. aborting",0); -+ return DECLINED; -+ } -+ -+ if (processor->sock_fd != AP_PERUSER_THISCHILD) -+ { -+ apr_socket_t *sock = NULL; -+ -+ apr_os_sock_put(&sock, &processor->sock_fd, r->connection->pool); -+ ap_sock_disable_nagle(sock); -+ ap_set_module_config(r->connection->conn_config, &core_module, sock); -+ _DBG("not the right socket?", 0); -+ return OK; -+ } -+ -+ switch (CHILD_INFO_TABLE[my_child_num].type) -+ { -+ case CHILD_TYPE_MULTIPLEXER: -+ { -+ _DBG("MULTIPLEXER => Determining if request should be passed. " -+ "Child Num: %d, dest-child: %d, hostname from server: %s r->hostname=%s r->the_request=\"%s\"", -+ my_child_num, processor->id, r->server->server_hostname, r->hostname, r->the_request); -+ -+ if (processor->id != my_child_num) -+ { -+ if (processor->status == CHILD_STATUS_STANDBY) -+ { -+ _DBG("Activating child #%d", processor->id); -+ processor->status = CHILD_STATUS_STARTING; -+ } -+ -+ _DBG("Passing request.",0); -+ if (pass_request(r, processor) == -1) -+ { -+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, -+ ap_server_conf, "Could not pass request to proper " "child, request will not be honoured."); -+ return DECLINED; -+ } -+ _DBG("doing longjmp",0); -+ longjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer, 1); -+ _DBG("request declined at our site",0); -+ return DECLINED; -+ _DBG("OUH! we should never reach this point",0); -+ } -+ _DBG("WTF: the server is assigned to the multiplexer! ... dropping request",0); -+ return DECLINED; -+ } -+ case CHILD_TYPE_PROCESSOR: -+ case CHILD_TYPE_WORKER: -+ { -+ _DBG("%s %d", child_type_string(CHILD_INFO_TABLE[my_child_num].type), my_child_num); -+ _DBG("request for %s / (server %s) seems to be for us", r->hostname, r->server->server_hostname); -+ -+ return OK; -+ } -+ default: -+ { -+ _DBG("unspecified child type %d in %d, dropping request", -+ CHILD_INFO_TABLE[my_child_num].type, my_child_num); -+ return DECLINED; -+ } -+ } -+ -+ _DBG("THIS POINT SHOULD NOT BE REACHED!",0); -+ return OK; -+} -+ -+static int peruser_status_hook(request_rec *r, int flags) -+{ -+ int x; -+ server_env_t *senv; -+ -+ if (flags & AP_STATUS_SHORT) -+ return OK; -+ -+ ap_rputs("
\n", r); -+ ap_rputs("

peruser status

\n", r); -+ ap_rputs("\n", r); -+ ap_rputs("" -+ "" -+ "" -+ "" -+ "\n", r); -+ for (x = 0; x < NUM_CHILDS; x++) -+ { -+ senv = CHILD_INFO_TABLE[x].senv; -+ ap_rprintf(r, "" -+ "" -+ "" -+ "\n", -+ CHILD_INFO_TABLE[x].id, -+ CHILD_INFO_TABLE[x].pid, -+ child_status_string(CHILD_INFO_TABLE[x].status), -+ child_type_string(CHILD_INFO_TABLE[x].type), -+ senv == NULL ? -1 : senv->uid, -+ senv == NULL ? -1 : senv->gid, -+ senv == NULL ? NULL : senv->chroot, -+ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->input, -+ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->output, -+ CHILD_INFO_TABLE[x].sock_fd, -+ total_processors(x), -+ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->max_processors, -+ idle_processors(x), -+ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->min_free_processors -+ ); -+ } -+ ap_rputs("
IDPIDSTATUSTYPEUIDGIDCHROOTINPUTOUTPUTSOCK_FDTOTAL PROCESSORSMAX PROCESSORSIDLE PROCESSORSMIN FREE PROCESSORS
%3d%5d%8s%12s%4d%4d%25s%5d%6d%7d%d%d%d%d
\n", r); -+ return OK; -+} -+ -+static void peruser_hooks(apr_pool_t *p) -+{ -+ /* The peruser open_logs phase 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(peruser_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE); -+ ap_hook_pre_config(peruser_pre_config, NULL, NULL, APR_HOOK_MIDDLE); -+ ap_hook_post_config(peruser_post_config, NULL, NULL, APR_HOOK_MIDDLE); -+ -+ /* Both of these must be run absolutely first. If this request isn't for -+ * this server then we need to forward it to the proper child. No sense -+ * tying up this server running more post_read request hooks if it is -+ * just going to be forwarded along. The process_connection hook allows -+ * peruser to receive the passed request correctly, by automatically -+ * filling in the core_input_filter's ctx pointer. -+ */ -+ ap_hook_post_read_request(peruser_post_read, NULL, NULL, -+ APR_HOOK_REALLY_FIRST); -+ ap_hook_process_connection(peruser_process_connection, NULL, NULL, -+ APR_HOOK_REALLY_FIRST); -+ -+ APR_OPTIONAL_HOOK(ap, status_hook, peruser_status_hook, NULL, NULL, APR_HOOK_MIDDLE); -+} -+ -+/* we define an Processor w/ specific uid/gid */ -+static const char *cf_Processor(cmd_parms *cmd, void *dummy, -+ const char *user_name, const char *group_name, const char *chroot) -+{ -+ uid_t uid = ap_uname2id(user_name); -+ gid_t gid = ap_gname2id(group_name); -+ -+ _DBG("user=%s:%d group=%s:%d chroot=%s", -+ user_name, uid, group_name, gid, chroot); -+ -+ return child_add(CHILD_TYPE_PROCESSOR, CHILD_STATUS_STANDBY, -+ uid, gid, chroot); -+} -+ -+/* we define an Multiplexer child w/ specific uid/gid */ -+static const char *cf_Multiplexer(cmd_parms *cmd, void *dummy, -+ const char *user_name, const char *group_name, const char *chroot) -+{ -+ uid_t uid = ap_uname2id(user_name); -+ gid_t gid = ap_gname2id(group_name); -+ -+ _DBG("user=%s:%d group=%s:%d chroot=%s [multiplexer id %d]", -+ user_name, uid, group_name, gid, chroot, NUM_CHILDS); -+ -+ return child_add(CHILD_TYPE_MULTIPLEXER, CHILD_STATUS_STARTING, -+ uid, gid, chroot); -+} -+ -+static const char* cf_ServerEnvironment(cmd_parms *cmd, void *dummy, -+ const char *user_name, const char *group_name, const char *chroot) -+{ -+ int uid = ap_uname2id(user_name); -+ int gid = ap_gname2id(group_name); -+ peruser_server_conf *sconf = PERUSER_SERVER_CONF(cmd->server->module_config); -+ -+ _DBG("function entered", 0); -+ -+ sconf->senv = senv_add(uid, gid, chroot); -+ -+ _DBG("user=%s:%d group=%s:%d chroot=%s numchilds=%d", -+ user_name, uid, group_name, gid, chroot, NUM_CHILDS); -+ -+ return NULL; -+} -+ -+static const char *set_min_free_servers(cmd_parms *cmd, void *dummy, const char *arg) -+{ -+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); -+ if (err != NULL) { -+ return err; -+ } -+ -+ ap_min_free_processors = atoi(arg); -+ if (ap_min_free_processors <= 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_min_free_processors = 1; -+ } -+ -+ return NULL; -+} -+ -+static const char *set_max_clients (cmd_parms *cmd, void *dummy, const char *arg) -+{ -+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); -+ if (err != NULL) { -+ return err; -+ } -+ -+ ap_daemons_limit = atoi(arg); -+ if (ap_daemons_limit > 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; -+ } -+ return NULL; -+} -+ -+static const char *set_min_processors (cmd_parms *cmd, void *dummy, const char *arg) -+{ -+ peruser_server_conf *sconf; -+ int min_procs; -+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); -+ -+ if (err != NULL) { -+ return err; -+ } -+ -+ min_procs = atoi(arg); -+ -+ if (min_procs < 1) { -+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, -+ "WARNING: Require MaxProcessors > 0, setting to 1"); -+ min_procs = 1; -+ } -+ -+ if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) { -+ sconf = PERUSER_SERVER_CONF(cmd->server->module_config); -+ sconf->senv->min_processors = min_procs; -+ } -+ else { -+ ap_min_processors = min_procs; -+ } -+ -+ return NULL; -+} -+ -+static const char *set_min_free_processors (cmd_parms *cmd, void *dummy, const char *arg) -+{ -+ peruser_server_conf *sconf; -+ int min_free_procs; -+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); -+ -+ if (err != NULL) { -+ return err; -+ } -+ -+ min_free_procs = atoi(arg); -+ -+ if (min_free_procs < 1) { -+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, -+ "WARNING: Require MinSpareProcessors > 0, setting to 1"); -+ min_free_procs = 1; -+ } -+ -+ if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) { -+ sconf = PERUSER_SERVER_CONF(cmd->server->module_config); -+ sconf->senv->min_free_processors = min_free_procs; -+ } -+ else { -+ ap_min_free_processors = min_free_procs; -+ } -+ -+ return NULL; -+} -+ -+static const char *set_max_processors (cmd_parms *cmd, void *dummy, const char *arg) -+{ -+ peruser_server_conf *sconf; -+ int max_procs; -+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); -+ -+ if (err != NULL) { -+ return err; -+ } -+ -+ max_procs = atoi(arg); -+ -+ if (max_procs < 1) { -+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, -+ "WARNING: Require MaxProcessors > 0, setting to 1"); -+ max_procs = 1; -+ } -+ -+ if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) { -+ sconf = PERUSER_SERVER_CONF(cmd->server->module_config); -+ sconf->senv->max_processors = max_procs; -+ } -+ else { -+ ap_max_processors = max_procs; -+ } -+ -+ 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; -+ } -+ return NULL; -+} -+ -+static const char *set_expire_timeout (cmd_parms *cmd, void *dummy, const char *arg) { -+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); -+ if (err != NULL) { -+ return err; -+ } -+ -+ expire_timeout = atoi(arg); -+ -+ return NULL; -+} -+ -+static const char *set_idle_timeout (cmd_parms *cmd, void *dummy, const char *arg) { -+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); -+ if (err != NULL) { -+ return err; -+ } -+ -+ idle_timeout = atoi(arg); -+ -+ return NULL; -+} -+ -+static const command_rec peruser_cmds[] = { -+UNIX_DAEMON_COMMANDS, -+LISTEN_COMMANDS, -+AP_INIT_TAKE1("MinSpareProcessors", set_min_free_processors, NULL, RSRC_CONF, -+ "Minimum number of idle children, to handle request spikes"), -+AP_INIT_TAKE1("MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, -+ "Minimum number of idle children, to handle request spikes"), -+AP_INIT_TAKE1("MaxClients", set_max_clients, NULL, RSRC_CONF, -+ "Maximum number of children alive at the same time"), -+AP_INIT_TAKE1("MinProcessors", set_min_processors, NULL, RSRC_CONF, -+ "Minimum number of processors per vhost"), -+AP_INIT_TAKE1("MaxProcessors", set_max_processors, NULL, RSRC_CONF, -+ "Maximum number of processors per vhost"), -+AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF, -+ "Maximum value of MaxClients for this run of Apache"), -+AP_INIT_TAKE1("ExpireTimeout", set_expire_timeout, NULL, RSRC_CONF, -+ "Maximum idle time before a child is killed, 0 to disable"), -+AP_INIT_TAKE1("IdleTimeout", set_idle_timeout, NULL, RSRC_CONF, -+ "Maximum time before a child is killed after being idle, 0 to disable"), -+AP_INIT_TAKE23("Multiplexer", cf_Multiplexer, NULL, RSRC_CONF, -+ "Specify an Multiplexer Child configuration."), -+AP_INIT_TAKE23("Processor", cf_Processor, NULL, RSRC_CONF, -+ "Specify a User and Group for a specific child process."), -+AP_INIT_TAKE23("ServerEnvironment", cf_ServerEnvironment, NULL, RSRC_CONF, -+ "Specify the server environment for this virtual host."), -+{ NULL } -+}; -+ -+module AP_MODULE_DECLARE_DATA mpm_peruser_module = { -+ MPM20_MODULE_STUFF, -+ ap_mpm_rewrite_args, /* hook to run before apache parses args */ -+ NULL, /* create per-directory config structure */ -+ NULL, /* merge per-directory config structures */ -+ peruser_create_config, /* create per-server config structure */ -+ NULL, /* merge per-server config structures */ -+ peruser_cmds, /* command apr_table_t */ -+ peruser_hooks, /* register hooks */ -+}; diff --git a/2.2/patches/20_all_peruser_0.2.2.patch b/2.2/patches/20_all_peruser_0.2.2.patch new file mode 100644 index 0000000..adcfd14 --- /dev/null +++ b/2.2/patches/20_all_peruser_0.2.2.patch @@ -0,0 +1,3392 @@ +diff -Nur httpd-2.2.3/server/mpm/config.m4 httpd-2.2.3-new/server/mpm/config.m4 +--- httpd-2.2.3/server/mpm/config.m4 2005-10-30 10:05:26.000000000 -0700 ++++ httpd-2.2.3-new/server/mpm/config.m4 2007-08-17 18:23:23.000000000 -0600 +@@ -1,7 +1,7 @@ + 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}),[ ++ MPM={beos|event|worker|prefork|mpmt_os2|peruser}),[ + APACHE_MPM=$withval + ],[ + if test "x$APACHE_MPM" = "x"; then +@@ -23,7 +23,7 @@ + + ap_mpm_is_experimental () + { +- if test "$apache_cv_mpm" = "event" ; then ++ if test "$apache_cv_mpm" = "event" -o "$apache_cv_mpm" = "peruser" ; then + return 0 + else + return 1 +diff -Nur httpd-2.2.3/server/mpm/experimental/peruser/Makefile.in httpd-2.2.3-new/server/mpm/experimental/peruser/Makefile.in +--- httpd-2.2.3/server/mpm/experimental/peruser/Makefile.in 1969-12-31 17:00:00.000000000 -0700 ++++ httpd-2.2.3-new/server/mpm/experimental/peruser/Makefile.in 2007-08-17 18:23:23.000000000 -0600 +@@ -0,0 +1,5 @@ ++ ++LTLIBRARY_NAME = libperuser.la ++LTLIBRARY_SOURCES = peruser.c ++ ++include $(top_srcdir)/build/ltlib.mk +diff -Nur httpd-2.2.3/server/mpm/experimental/peruser/config.m4 httpd-2.2.3-new/server/mpm/experimental/peruser/config.m4 +--- httpd-2.2.3/server/mpm/experimental/peruser/config.m4 1969-12-31 17:00:00.000000000 -0700 ++++ httpd-2.2.3-new/server/mpm/experimental/peruser/config.m4 2007-08-17 18:23:23.000000000 -0600 +@@ -0,0 +1,3 @@ ++if test "$MPM_NAME" = "peruser" ; then ++ APACHE_FAST_OUTPUT(server/mpm/experimental/$MPM_NAME/Makefile) ++fi +diff -Nur httpd-2.2.3/server/mpm/experimental/peruser/mpm.h httpd-2.2.3-new/server/mpm/experimental/peruser/mpm.h +--- httpd-2.2.3/server/mpm/experimental/peruser/mpm.h 1969-12-31 17:00:00.000000000 -0700 ++++ httpd-2.2.3-new/server/mpm/experimental/peruser/mpm.h 2007-08-17 18:23:23.000000000 -0600 +@@ -0,0 +1,103 @@ ++/* ==================================================================== ++ * The Apache Software License, Version 1.1 ++ * ++ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights ++ * reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * 3. The end-user documentation included with the redistribution, ++ * if any, must include the following acknowledgment: ++ * "This product includes software developed by the ++ * Apache Software Foundation (http://www.apache.org/)." ++ * Alternately, this acknowledgment may appear in the software itself, ++ * if and wherever such third-party acknowledgments normally appear. ++ * ++ * 4. The names "Apache" and "Apache Software Foundation" must ++ * not be used to endorse or promote products derived from this ++ * software without prior written permission. For written ++ * permission, please contact apache@apache.org. ++ * ++ * 5. Products derived from this software may not be called "Apache", ++ * nor may "Apache" appear in their name, without prior written ++ * permission of the Apache Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ++ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ++ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ++ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ==================================================================== ++ * ++ * This software consists of voluntary contributions made by many ++ * individuals on behalf of the Apache Software Foundation. For more ++ * information on the Apache Software Foundation, please see ++ * . ++ * ++ * Portions of this software are based upon public domain software ++ * originally written at the National Center for Supercomputing Applications, ++ * University of Illinois, Urbana-Champaign. ++ */ ++ ++#include "httpd.h" ++#include "mpm_default.h" ++#include "scoreboard.h" ++#include "unixd.h" ++ ++#ifndef APACHE_MPM_PERUSER_H ++#define APACHE_MPM_PERUSER_H ++ ++#define PERUSER_MPM ++ ++#define MPM_NAME "Peruser" ++ ++#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_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; ++ ++/* Table of child status */ ++#define SERVER_DEAD 0 ++#define SERVER_DYING 1 ++#define SERVER_ALIVE 2 ++ ++typedef struct ap_ctable { ++ pid_t pid; ++ unsigned char status; ++} ap_ctable; ++ ++#endif /* APACHE_MPM_PERUSER_H */ +diff -Nur httpd-2.2.3/server/mpm/experimental/peruser/mpm_default.h httpd-2.2.3-new/server/mpm/experimental/peruser/mpm_default.h +--- httpd-2.2.3/server/mpm/experimental/peruser/mpm_default.h 1969-12-31 17:00:00.000000000 -0700 ++++ httpd-2.2.3-new/server/mpm/experimental/peruser/mpm_default.h 2007-08-17 18:23:23.000000000 -0600 +@@ -0,0 +1,110 @@ ++/* ==================================================================== ++ * The Apache Software License, Version 1.1 ++ * ++ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights ++ * reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * 3. The end-user documentation included with the redistribution, ++ * if any, must include the following acknowledgment: ++ * "This product includes software developed by the ++ * Apache Software Foundation (http://www.apache.org/)." ++ * Alternately, this acknowledgment may appear in the software itself, ++ * if and wherever such third-party acknowledgments normally appear. ++ * ++ * 4. The names "Apache" and "Apache Software Foundation" must ++ * not be used to endorse or promote products derived from this ++ * software without prior written permission. For written ++ * permission, please contact apache@apache.org. ++ * ++ * 5. Products derived from this software may not be called "Apache", ++ * nor may "Apache" appear in their name, without prior written ++ * permission of the Apache Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ++ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ++ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ++ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ==================================================================== ++ * ++ * This software consists of voluntary contributions made by many ++ * individuals on behalf of the Apache Software Foundation. For more ++ * information on the Apache Software Foundation, please see ++ * . ++ * ++ * Portions of this software are based upon public domain software ++ * originally written at the National Center for Supercomputing Applications, ++ * University of Illinois, Urbana-Champaign. ++ */ ++ ++#ifndef APACHE_MPM_DEFAULT_H ++#define APACHE_MPM_DEFAULT_H ++ ++/* Number of processors to spawn off for each ServerEnvironment by default */ ++ ++#ifndef DEFAULT_START_PROCESSORS ++#define DEFAULT_START_PROCESSORS 0 ++#endif ++ ++/* Minimum number of running processors per ServerEnvironment */ ++ ++#ifndef DEFAULT_MIN_PROCESSORS ++#define DEFAULT_MIN_PROCESSORS 0 ++#endif ++ ++/* Minimum --- fewer than this, and more will be created */ ++ ++#ifndef DEFAULT_MIN_FREE_PROCESSORS ++#define DEFAULT_MIN_FREE_PROCESSORS 2 ++#endif ++ ++/* Maximum processors per ServerEnvironment */ ++ ++#ifndef DEFAULT_MAX_PROCESSORS ++#define DEFAULT_MAX_PROCESSORS 10 ++#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 -Nur httpd-2.2.3/server/mpm/experimental/peruser/peruser.c httpd-2.2.3-new/server/mpm/experimental/peruser/peruser.c +--- httpd-2.2.3/server/mpm/experimental/peruser/peruser.c 1969-12-31 17:00:00.000000000 -0700 ++++ httpd-2.2.3-new/server/mpm/experimental/peruser/peruser.c 2007-08-17 18:23:59.000000000 -0600 +@@ -0,0 +1,3130 @@ ++/* ==================================================================== ++ * The Apache Software License, Version 1.1 ++ * ++ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights ++ * reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * 3. The end-user documentation included with the redistribution, ++ * if any, must include the following acknowledgment: ++ * "This product includes software developed by the ++ * Apache Software Foundation (http://www.apache.org/)." ++ * Alternately, this acknowledgment may appear in the software itself, ++ * if and wherever such third-party acknowledgments normally appear. ++ * ++ * 4. The names "Apache" and "Apache Software Foundation" must ++ * not be used to endorse or promote products derived from this ++ * software without prior written permission. For written ++ * permission, please contact apache@apache.org. ++ * ++ * 5. Products derived from this software may not be called "Apache", ++ * nor may "Apache" appear in their name, without prior written ++ * permission of the Apache Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ++ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ++ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ++ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ++ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ==================================================================== ++ * ++ * This software consists of voluntary contributions made by many ++ * individuals on behalf of the Apache Software Foundation. For more ++ * information on the Apache Software Foundation, please see ++ * . ++ * ++ * Portions of this software are based upon public domain software ++ * originally written at the National Center for Supercomputing Applications, ++ * University of Illinois, Urbana-Champaign. ++ */ ++ ++/* #define MPM_PERUSER_DEBUG */ ++ ++#include "apr.h" ++#include "apr_hash.h" ++#include "apr_pools.h" ++#include "apr_file_io.h" ++#include "apr_portable.h" ++#include "apr_strings.h" ++#include "apr_thread_proc.h" ++#include "apr_signal.h" ++#define APR_WANT_STDIO ++#define APR_WANT_STRFUNC ++#define APR_WANT_IOVEC ++#include "apr_want.h" ++ ++#if APR_HAVE_UNISTD_H ++#include ++#endif ++#if APR_HAVE_SYS_TYPES_H ++#include ++#endif ++ ++#define CORE_PRIVATE ++ ++#include "ap_config.h" ++#include "httpd.h" ++#include "mpm_default.h" ++#include "http_main.h" ++#include "http_log.h" ++#include "http_config.h" ++#include "http_core.h" /* for get_remote_host */ ++#include "http_connection.h" ++#include "http_protocol.h" /* for ap_hook_post_read_request */ ++#include "scoreboard.h" ++#include "ap_mpm.h" ++#include "unixd.h" ++#include "mpm_common.h" ++#include "ap_listen.h" ++#include "ap_mmn.h" ++#include "apr_poll.h" ++#include "util_ebcdic.h" ++#include "mod_status.h" ++ ++#ifdef HAVE_BSTRING_H ++#include /* for IRIX, FD_SET calls bzero() */ ++#endif ++ ++#ifdef HAVE_TIME_H ++#include ++#endif ++ ++#ifdef HAVE_SYS_PROCESSOR_H ++#include /* for bindprocessor() */ ++#endif ++ ++#if APR_HAS_SHARED_MEMORY ++#include "apr_shm.h" ++#else ++#error "Peruser MPM requres shared memory support." ++#endif ++ ++ ++/* should be APR-ized */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++#ifdef MPM_PERUSER_DEBUG ++# define _DBG(text,par...) \ ++ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, \ ++ "(peruser: pid=%d uid=%d child=%d) %s(): " text, \ ++ getpid(), getuid(), my_child_num, __FUNCTION__, ##par, 0) ++ ++# define _TRACE_CALL(text,par...) _DBG("calling " text, ##par) ++# define _TRACE_RET(text,par...) _DBG("returned from " text, ##par) ++#else ++# define _DBG(text,par...) ++# define _TRACE_RET(text,par...) ++# define _TRACE_CALL(text,par...) ++#endif /* MPM_PERUSER_DEBUG */ ++ ++/* char of death - for signalling children to die */ ++#define AP_PERUSER_CHAR_OF_DEATH '!' ++ ++#define PERUSER_SERVER_CONF(cf) \ ++ ((peruser_server_conf *) ap_get_module_config(cf, &mpm_peruser_module)) ++ ++#define SCOREBOARD_STATUS(i) ap_scoreboard_image->servers[i][0].status ++ ++/* ++ * Define some magic numbers that we use for the state of the incomming ++ * request. These must be < 0 so they don't collide with a file descriptor. ++ */ ++#define AP_PERUSER_THISCHILD -1 ++#define AP_PERUSER_OTHERCHILD -2 ++ ++ ++/* Limit on the total --- clients will be locked out if more servers than ++ * this are needed. It is intended solely to keep the server from crashing ++ * when things get out of hand. ++ * ++ * We keep a hard maximum number of servers, for two reasons --- first off, ++ * in case something goes seriously wrong, we want to stop the fork bomb ++ * short of actually crashing the machine we're running on by filling some ++ * kernel table. Secondly, it keeps the size of the scoreboard file small ++ * enough that we can read the whole thing without worrying too much about ++ * the overhead. ++ */ ++#ifndef DEFAULT_SERVER_LIMIT ++#define DEFAULT_SERVER_LIMIT 256 ++#endif ++ ++/* Admin can't tune ServerLimit beyond MAX_SERVER_LIMIT. We want ++ * some sort of compile-time limit to help catch typos. ++ */ ++#ifndef MAX_SERVER_LIMIT ++#define MAX_SERVER_LIMIT 20000 ++#endif ++ ++#ifndef HARD_THREAD_LIMIT ++#define HARD_THREAD_LIMIT 1 ++#endif ++ ++#define CHILD_TYPE_UNKNOWN 0 ++#define CHILD_TYPE_MULTIPLEXER 1 ++#define CHILD_TYPE_PROCESSOR 2 ++#define CHILD_TYPE_WORKER 3 ++ ++#define CHILD_STATUS_STANDBY 0 /* wait for a request before starting */ ++#define CHILD_STATUS_STARTING 1 /* wait for socket creation */ ++#define CHILD_STATUS_READY 2 /* wait for mux to restart */ ++#define CHILD_STATUS_ACTIVE 3 /* ready to take requests */ ++#define CHILD_STATUS_RESTART 4 /* child about to die and restart */ ++ ++/* config globals */ ++ ++int ap_threads_per_child=0; /* Worker threads per child */ ++static apr_proc_mutex_t *accept_mutex; ++static int ap_min_processors=DEFAULT_MIN_PROCESSORS; ++static int ap_min_free_processors=DEFAULT_MIN_FREE_PROCESSORS; ++static int ap_max_processors=DEFAULT_MAX_PROCESSORS; ++static int ap_daemons_limit=0; /* MaxClients */ ++static int expire_timeout=1800; ++static int idle_timeout=900; ++static int server_limit = DEFAULT_SERVER_LIMIT; ++static int first_server_limit; ++static int changed_limit_at_restart; ++static int requests_this_child; ++static int mpm_state = AP_MPMQ_STARTING; ++static ap_pod_t *pod; ++ ++/* === configuration stuff === */ ++ ++typedef struct ++{ ++ int processor_id; ++ ++ /* security settings */ ++ uid_t uid; /* user id */ ++ gid_t gid; /* group id */ ++ const char *chroot; /* directory to chroot() to, can be null */ ++ ++ /* resource settings */ ++ int min_processors; ++ int min_free_processors; ++ int max_processors; ++ ++ /* sockets */ ++ int input; /* The socket descriptor */ ++ int output; /* The socket descriptor */ ++} server_env_t; ++ ++typedef struct ++{ ++ apr_size_t num; ++} server_env_control; ++ ++typedef struct ++{ ++ server_env_control *control; ++ server_env_t *table; ++} server_env; ++ ++ ++typedef struct ++{ ++ /* identification */ ++ int id; /* index in child_info_table */ ++ pid_t pid; /* process id */ ++ int status; /* status of child */ ++ int type; /* multiplexer or processor */ ++ server_env_t *senv; ++ ++ /* sockets */ ++ int sock_fd; ++ ++ /* stack context saved state */ ++ jmp_buf jmpbuffer; ++} child_info_t; ++ ++typedef struct ++{ ++ /* identification */ ++ int id; /* index in child_info_table */ ++ pid_t pid; /* process id */ ++ int status; /* status of child */ ++ int type; /* multiplexer or processor */ ++ apr_time_t last_used; ++} child_grace_info_t; ++ ++typedef struct ++{ ++ apr_size_t num; ++} child_info_control; ++ ++typedef struct ++{ ++ child_info_control *control; ++ child_info_t *table; ++} child_info; ++ ++typedef struct ++{ ++ server_env_t *senv; ++} peruser_server_conf; ++ ++ ++typedef struct peruser_header ++{ ++ char *headers; ++ apr_pool_t *p; ++} peruser_header; ++ ++ ++/* Tables used to determine the user and group each child process should ++ * run as. The hash table is used to correlate a server name with a child ++ * process. ++ */ ++static apr_size_t child_info_size; ++static child_info *child_info_image; ++static child_grace_info_t *child_grace_info_table; ++struct ap_ctable *ap_child_table; ++ ++#define NUM_CHILDS (child_info_image != NULL ? child_info_image->control->num : 0) ++#define CHILD_INFO_TABLE (child_info_image != NULL ? child_info_image->table : NULL) ++ ++static apr_size_t server_env_size; ++static server_env *server_env_image; ++ ++#define NUM_SENV (server_env_image != NULL ? server_env_image->control->num : 0) ++#define SENV (server_env_image != NULL ? server_env_image->table : NULL) ++ ++#if APR_HAS_SHARED_MEMORY ++#ifndef WIN32 ++static /* but must be exported to mpm_winnt */ ++#endif ++ apr_shm_t *child_info_shm = NULL; ++ apr_shm_t *server_env_shm = NULL; ++#endif ++ ++/* ++ * 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; ++server_rec *ap_server_conf; ++ ++module AP_MODULE_DECLARE_DATA mpm_peruser_module; ++ ++/* -- replace the pipe-of-death by an control socket -- */ ++static apr_file_t *pipe_of_death_in = NULL; ++static apr_file_t *pipe_of_death_out = NULL; ++ ++ ++/* one_process --- debugging mode variable; can be set from the command line ++ * with the -X flag. If set, this gets you the child_main loop running ++ * in the process which originally started up (no detach, no make_child), ++ * which is a pretty nice debugging environment. (You'll get a SIGHUP ++ * early in standalone_main; just continue through. This is the server ++ * trying to kill off any child processes which it might have lying ++ * around --- Apache doesn't keep track of their pids, it just sends ++ * SIGHUP to the process group, ignoring it in the root process. ++ * Continue through and you'll be fine.). ++ */ ++ ++static int one_process = 0; ++ ++static apr_pool_t *pconf; /* Pool for config stuff */ ++static apr_pool_t *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; ++static int my_child_num; ++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 int die_now = 0; ++ ++int grace_children = 0; ++int grace_children_alive = 0; ++ ++#ifdef GPROF ++/* ++ * change directory for gprof to plop the gmon.out file ++ * configure in httpd.conf: ++ * GprofDir $RuntimeDir/ -> $ServerRoot/$RuntimeDir/gmon.out ++ * GprofDir $RuntimeDir/% -> $ServerRoot/$RuntimeDir/gprof.$pid/gmon.out ++ */ ++static void chdir_for_gprof(void) ++{ ++ core_server_config *sconf = ++ ap_get_module_config(ap_server_conf->module_config, &core_module); ++ char *dir = sconf->gprof_dir; ++ const char *use_dir; ++ ++ if(dir) { ++ apr_status_t res; ++ char buf[512]; ++ int len = strlen(sconf->gprof_dir) - 1; ++ if(*(dir + len) == '%') { ++ dir[len] = '\0'; ++ apr_snprintf(buf, sizeof(buf), "%sgprof.%d", dir, (int)getpid()); ++ } ++ use_dir = ap_server_root_relative(pconf, buf[0] ? buf : dir); ++ res = apr_dir_make(use_dir, 0755, pconf); ++ if(res != APR_SUCCESS && !APR_STATUS_IS_EEXIST(res)) { ++ ap_log_error(APLOG_MARK, APLOG_ERR, errno, ap_server_conf, ++ "gprof: error creating directory %s", dir); ++ } ++ } ++ else { ++ use_dir = ap_server_root_relative(pconf, DEFAULT_REL_RUNTIMEDIR); ++ } ++ ++ chdir(use_dir); ++} ++#else ++#define chdir_for_gprof() ++#endif ++ ++char* child_type_string(int type) ++{ ++ switch(type) ++ { ++ case CHILD_TYPE_MULTIPLEXER: return "MULTIPLEXER"; ++ case CHILD_TYPE_PROCESSOR: return "PROCESSOR"; ++ case CHILD_TYPE_WORKER: return "WORKER"; ++ } ++ ++ return "UNKNOWN"; ++} ++ ++char* child_status_string(int status) ++{ ++ switch(status) ++ { ++ case CHILD_STATUS_STANDBY: return "STANDBY"; ++ case CHILD_STATUS_STARTING: return "STARTING"; ++ case CHILD_STATUS_READY: return "READY"; ++ case CHILD_STATUS_ACTIVE: return "ACTIVE"; ++ case CHILD_STATUS_RESTART: return "RESTART"; ++ } ++ ++ return "UNKNOWN"; ++} ++ ++void dump_child_table() ++{ ++ int x; ++ server_env_t *senv; ++ ++ _DBG("%-3s %-5s %-8s %-12s %-4s %-4s %-25s %5s %6s %7s", ++ "ID", "PID", "STATUS", "TYPE", "UID", "GID", "CHROOT", "INPUT", "OUTPUT", "SOCK_FD"); ++ ++ for(x = 0; x < NUM_CHILDS; x++) ++ { ++ senv = CHILD_INFO_TABLE[x].senv; ++ _DBG("%-3d %-5d %-8s %-12s %-4d %-4d %-25s %-5d %-6d %-7d", ++ CHILD_INFO_TABLE[x].id, ++ CHILD_INFO_TABLE[x].pid, ++ child_status_string(CHILD_INFO_TABLE[x].status), ++ child_type_string(CHILD_INFO_TABLE[x].type), ++ senv == NULL ? -1 : senv->uid, ++ senv == NULL ? -1 : senv->gid, ++ senv == NULL ? NULL : senv->chroot, ++ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->input, ++ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->output, ++ CHILD_INFO_TABLE[x].sock_fd); ++ } ++} ++ ++/* XXX - I don't know if TPF will ever use this module or not, so leave ++ * the ap_check_signals calls in but disable them - manoj */ ++#define ap_check_signals() ++ ++/* a clean exit from a child with proper cleanup */ ++static inline int clean_child_exit(int code) __attribute__ ((noreturn)); ++static inline int clean_child_exit(int code) ++{ ++ int retval; ++ ++ mpm_state = AP_MPMQ_STOPPING; ++ ++ if (CHILD_INFO_TABLE[my_child_num].type != CHILD_TYPE_MULTIPLEXER && ++ CHILD_INFO_TABLE[my_child_num].senv) ++ { ++ retval = close(CHILD_INFO_TABLE[my_child_num].senv->input); ++ _DBG("close(CHILD_INFO_TABLE[%d].senv->input) = %d", ++ my_child_num, retval); ++ ++ retval = close(CHILD_INFO_TABLE[my_child_num].senv->output); ++ _DBG("close(CHILD_INFO_TABLE[%d].senv->output) = %d", ++ my_child_num, retval); ++ } ++ ++ if (pchild) { ++ apr_pool_destroy(pchild); ++ } ++ ap_mpm_pod_close(pod); ++ chdir_for_gprof(); ++ exit(code); ++} ++ ++static void accept_mutex_on(void) ++{ ++/* for some reason this fails if we listen on the pipe_of_death. ++ fortunately I don't think we currently need it */ ++ ++#if 0 ++ apr_status_t rv = apr_proc_mutex_lock(accept_mutex); ++ if (rv != APR_SUCCESS) { ++ const char *msg = "couldn't grab the accept mutex"; ++ ++ if (ap_my_generation != ++ ap_scoreboard_image->global->running_generation) { ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, NULL, msg); ++ clean_child_exit(0); ++ } ++ else { ++ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, NULL, msg); ++ exit(APEXIT_CHILDFATAL); ++ } ++ } ++#endif ++} ++ ++static void accept_mutex_off(void) ++{ ++#if 0 ++ apr_status_t rv = apr_proc_mutex_unlock(accept_mutex); ++ if (rv != APR_SUCCESS) { ++ const char *msg = "couldn't release the accept mutex"; ++ ++ if (ap_my_generation != ++ ap_scoreboard_image->global->running_generation) { ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, NULL, 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, msg); ++ exit(APEXIT_CHILDFATAL); ++ } ++ } ++#endif ++} ++ ++/* On some architectures it's safe to do unserialized accept()s in the single ++ * Listen case. But it's never safe to do it in the case where there's ++ * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT ++ * when it's safe in the single Listen case. ++ */ ++#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT ++#define SAFE_ACCEPT(stmt) do {if (ap_listeners->next) {stmt;}} while(0) ++#else ++#define SAFE_ACCEPT(stmt) do {stmt;} while(0) ++#endif ++ ++AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result) ++{ ++ switch(query_code){ ++ case AP_MPMQ_MAX_DAEMON_USED: ++ *result = ap_daemons_limit; ++ return APR_SUCCESS; ++ case AP_MPMQ_IS_THREADED: ++ *result = AP_MPMQ_NOT_SUPPORTED; ++ return APR_SUCCESS; ++ case AP_MPMQ_IS_FORKED: ++ *result = AP_MPMQ_DYNAMIC; ++ return APR_SUCCESS; ++ case AP_MPMQ_HARD_LIMIT_DAEMONS: ++ *result = server_limit; ++ return APR_SUCCESS; ++ case AP_MPMQ_HARD_LIMIT_THREADS: ++ *result = HARD_THREAD_LIMIT; ++ return APR_SUCCESS; ++ case AP_MPMQ_MAX_THREADS: ++ *result = 0; ++ return APR_SUCCESS; ++ case AP_MPMQ_MIN_SPARE_DAEMONS: ++ *result = ap_min_free_processors; ++ return APR_SUCCESS; ++ case AP_MPMQ_MIN_SPARE_THREADS: ++ *result = 0; ++ return APR_SUCCESS; ++ case AP_MPMQ_MAX_SPARE_THREADS: ++ *result = 0; ++ return APR_SUCCESS; ++ case AP_MPMQ_MAX_REQUESTS_DAEMON: ++ *result = ap_max_requests_per_child; ++ return APR_SUCCESS; ++ case AP_MPMQ_MAX_DAEMONS: ++ *result = server_limit; ++ return APR_SUCCESS; ++ case AP_MPMQ_MPM_STATE: ++ *result = mpm_state; ++ return APR_SUCCESS; ++ } ++ return APR_ENOTIMPL; ++} ++ ++#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) ++{ ++ 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; ++} ++#endif ++ ++/* handle all varieties of core dumping signals */ ++static void sig_coredump(int sig) ++{ ++ chdir(ap_coredump_dir); ++ apr_signal(sig, SIG_DFL); ++ if (ap_my_pid == parent_pid) { ++ ap_log_error(APLOG_MARK, APLOG_NOTICE, ++ 0, ap_server_conf, ++ "seg fault or similar nasty error detected " ++ "in the parent process"); ++ } ++ 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. ++ */ ++} ++ ++/***************************************************************** ++ * Connection structures and accounting... ++ */ ++ ++static void just_die(int sig) ++{ ++_DBG("function called"); ++ clean_child_exit(0); ++} ++ ++/* volatile just in case */ ++static int volatile shutdown_pending; ++static int volatile restart_pending; ++static int volatile is_graceful; ++/* XXX static int volatile child_fatal; */ ++ ++static void sig_term(int sig) ++{ ++ if (shutdown_pending == 1) { ++ /* Um, is this _probably_ not an error, if the user has ++ * tried to do a shutdown twice quickly, so we won't ++ * worry about reporting it. ++ */ ++ return; ++ } ++ shutdown_pending = 1; ++} ++ ++/* restart() is the signal handler for SIGHUP and AP_SIG_GRACEFUL ++ * in the parent process, unless running in ONE_PROCESS mode ++ */ ++static void restart(int sig) ++{ ++ if (restart_pending == 1) { ++ /* Probably not an error - don't bother reporting it */ ++ return; ++ } ++ restart_pending = 1; ++ is_graceful = (sig == AP_SIG_GRACEFUL); ++} ++ ++/* Sets die_now if we received a character on the pipe_of_death */ ++static apr_status_t check_pipe_of_death ++( ++ void **csd, ++ ap_listen_rec *lr, ++ apr_pool_t *ptrans ++) ++{ ++ int ret; ++ char pipe_read_char; ++ apr_size_t n = 1; ++ ++ _DBG("WATCH: die_now=%d", die_now); ++ ++ if (die_now) return APR_SUCCESS; ++ ++ /* apr_thread_mutex_lock(pipe_of_death_mutex); */ ++ ret = apr_socket_recv(lr->sd, &pipe_read_char, &n); ++ if (APR_STATUS_IS_EAGAIN(ret)) ++ { ++ /* It lost the lottery. It must continue to suffer ++ * through a life of servitude. */ ++ } ++ else ++ { ++ if (pipe_read_char != AP_PERUSER_CHAR_OF_DEATH) ++ { ++ _DBG("got wrong char %c", pipe_read_char); ++ return APR_SUCCESS; ++ } ++ /* It won the lottery (or something else is very ++ * wrong). Embrace death with open arms. */ ++ die_now = 1; ++ _DBG("WATCH: die_now=%d", die_now); ++ } ++ /* apr_thread_mutex_unlock(pipe_of_death_mutex); */ ++ return APR_SUCCESS; ++} ++ ++static void set_signals(void) ++{ ++#ifndef NO_USE_SIGACTION ++ struct sigaction sa; ++ ++ sigemptyset(&sa.sa_mask); ++ sa.sa_flags = 0; ++ ++ if (!one_process) { ++ sa.sa_handler = sig_coredump; ++#if defined(SA_ONESHOT) ++ sa.sa_flags = SA_ONESHOT; ++#elif defined(SA_RESETHAND) ++ sa.sa_flags = SA_RESETHAND; ++#endif ++ if (sigaction(SIGSEGV, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGSEGV)"); ++#ifdef SIGBUS ++ if (sigaction(SIGBUS, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGBUS)"); ++#endif ++#ifdef SIGABORT ++ if (sigaction(SIGABORT, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABORT)"); ++#endif ++#ifdef SIGABRT ++ if (sigaction(SIGABRT, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABRT)"); ++#endif ++#ifdef SIGILL ++ if (sigaction(SIGILL, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGILL)"); ++#endif ++ sa.sa_flags = 0; ++ } ++ sa.sa_handler = sig_term; ++ if (sigaction(SIGTERM, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)"); ++#ifdef SIGINT ++ if (sigaction(SIGINT, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)"); ++#endif ++#ifdef SIGXCPU ++ sa.sa_handler = SIG_DFL; ++ if (sigaction(SIGXCPU, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXCPU)"); ++#endif ++#ifdef SIGXFSZ ++ sa.sa_handler = SIG_IGN; ++ if (sigaction(SIGXFSZ, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXFSZ)"); ++#endif ++#ifdef SIGPIPE ++ sa.sa_handler = SIG_IGN; ++ if (sigaction(SIGPIPE, &sa, NULL) < 0) ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGPIPE)"); ++#endif ++ ++ /* 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 ")"); ++#else ++ if (!one_process) { ++ 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 SIGXCPU ++ apr_signal(SIGXCPU, SIG_DFL); ++#endif /* SIGXCPU */ ++#ifdef SIGXFSZ ++ apr_signal(SIGXFSZ, SIG_DFL); ++#endif /* SIGXFSZ */ ++ } ++ ++ apr_signal(SIGTERM, sig_term); ++#ifdef SIGHUP ++ apr_signal(SIGHUP, restart); ++#endif /* SIGHUP */ ++#ifdef AP_SIG_GRACEFUL ++ apr_signal(AP_SIG_GRACEFUL, restart); ++#endif /* AP_SIG_GRACEFUL */ ++#ifdef SIGPIPE ++ apr_signal(SIGPIPE, SIG_IGN); ++#endif /* SIGPIPE */ ++ ++#endif ++} ++ ++/***************************************************************** ++ * Child process main loop. ++ * The following vars are static to avoid getting clobbered by longjmp(); ++ * they are really private to child_main. ++ */ ++ ++static int requests_this_child; ++static int num_listensocks = 0; ++static ap_listen_rec *listensocks; ++ ++int ap_graceful_stop_signalled(void) ++{ ++ /* not ever called anymore... */ ++ return 0; ++} ++ ++/* ++ * This function sends a raw socket over to a processor. It uses the same ++ * on-wire format as pass_request. The recipient can determine if he got ++ * a socket or a whole request by inspecting the header_length of the ++ * message. If it is zero then only a socket was sent. ++ */ ++static int pass_socket(apr_socket_t *thesock, child_info_t *processor, apr_pool_t *pool) ++{ ++ int rv; ++ struct msghdr msg; ++ struct cmsghdr *cmsg; ++ int sock_fd; ++ char *body = ""; ++ struct iovec iov[4]; ++ apr_size_t len = 0; ++ apr_size_t header_len = 0; ++ apr_size_t body_len = 0; ++ peruser_header h; ++ apr_bucket *bucket; ++ const apr_array_header_t *headers_in_array; ++ const apr_table_entry_t *headers_in; ++ int counter; ++ ++ if (!processor) ++ { ++ _DBG("server %s in child %d has no child_info associated", ++ "(unkonwn)", my_child_num); ++ return -1; ++ } ++ ++ _DBG("passing request to another child.", 0); ++ ++ apr_os_sock_get(&sock_fd, thesock); ++ ++ header_len = 0; ++ body_len = 0; ++ ++ iov[0].iov_base = &header_len; ++ iov[0].iov_len = sizeof(header_len); ++ iov[1].iov_base = &body_len; ++ iov[1].iov_len = sizeof(body_len); ++ iov[2].iov_base = h.headers; ++ iov[2].iov_len = 0; ++ iov[3].iov_base = body; ++ iov[3].iov_len = body_len; ++ ++ msg.msg_name = NULL; ++ msg.msg_namelen = 0; ++ msg.msg_iov = iov; ++ msg.msg_iovlen = 4; ++ ++ cmsg = apr_palloc(pool, sizeof(*cmsg) + sizeof(sock_fd)); ++ cmsg->cmsg_len = sizeof(*cmsg) + sizeof(sock_fd); ++ cmsg->cmsg_level = SOL_SOCKET; ++ cmsg->cmsg_type = SCM_RIGHTS; ++ ++ memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(sock_fd)); ++ ++ msg.msg_control = cmsg; ++ msg.msg_controllen = cmsg->cmsg_len; ++ ++ if (processor->status == CHILD_STATUS_STANDBY) ++ { ++ _DBG("Activating child #%d", processor->id); ++ processor->status = CHILD_STATUS_STARTING; ++ } ++ ++ _DBG("Writing message to %d, passing sock_fd: %d", processor->senv->output, sock_fd); ++ _DBG("header_len=%d headers=\"%s\"", header_len, h.headers); ++ _DBG("body_len=%d body=\"%s\"", body_len, body); ++ ++ if ((rv = sendmsg(processor->senv->output, &msg, 0)) == -1) ++ { ++ apr_pool_destroy(pool); ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, ++ "Writing message failed %d %d", rv, errno); ++ return -1; ++ } ++ ++ _DBG("Writing message succeeded %d", rv); ++ ++ /* -- close the socket on our side -- */ ++ _DBG("closing socket %d on our side", sock_fd); ++ apr_socket_close(thesock); ++ ++ apr_pool_destroy(pool); ++ return 1; ++} ++ ++static void process_socket(apr_pool_t *p, apr_socket_t *sock, long conn_id, ++ apr_bucket_alloc_t *bucket_alloc, apr_pool_t *pool) ++{ ++ conn_rec *current_conn; ++ int sock_fd; ++ apr_status_t rv; ++ ap_sb_handle_t *sbh; ++ child_info_t *processor; ++ apr_pool_t *ptrans; ++ peruser_server_conf *sconf; ++ ++ _DBG("Creating dummy connection to use the vhost lookup api", 0); ++ ++ ap_create_sb_handle(&sbh, p, conn_id, 0); ++ current_conn = ap_run_create_connection(p, ap_server_conf, sock, conn_id, ++ sbh, bucket_alloc); ++ _DBG("Looking up the right vhost"); ++ if (current_conn) { ++ ap_update_vhost_given_ip(current_conn); ++ _DBG("Base server is %s, name based vhosts %s", current_conn->base_server->server_hostname, ++ current_conn->vhost_lookup_data ? "on" : "off"); ++ } ++ ++ if (current_conn && !current_conn->vhost_lookup_data && CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER) { ++ _DBG("We are not using name based vhosts, we'll directly pass the socket."); ++ ++ sconf = PERUSER_SERVER_CONF(current_conn->base_server->module_config); ++ processor = &CHILD_INFO_TABLE[sconf->senv->processor_id]; ++ ++ _DBG("Forwarding without further inspection, processor %d", processor->id); ++ if (processor->status == CHILD_STATUS_STANDBY) ++ { ++ _DBG("Activating child #%d", processor->id); ++ processor->status = CHILD_STATUS_STARTING; ++ } ++ ++ _DBG("Creating new pool",0); ++ apr_pool_create(&ptrans, pool); ++ _DBG("Passing request.",0); ++ if (pass_socket(sock, processor, ptrans) == -1) ++ { ++ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ++ ap_server_conf, "Could not pass request to proper " ++ "child, request will not be honoured."); ++ return; ++ } ++ if (current_conn) ++ { ++ _DBG("freeing connection",0); ++ ap_lingering_close(current_conn); ++ } ++ _DBG("doing longjmp",0); ++ longjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer, 1); ++ return; ++ } ++ ++ if ((rv = apr_os_sock_get(&sock_fd, sock)) != APR_SUCCESS) ++ { ++ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, "apr_os_sock_get"); ++ } ++ ++ _DBG("child_num=%d sock=%ld sock_fd=%d\n", my_child_num, sock, sock_fd); ++ _DBG("type=%s %d", child_type_string(CHILD_INFO_TABLE[my_child_num].type), my_child_num); ++ ++ if (sock_fd >= FD_SETSIZE) ++ { ++ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, ++ "new file descriptor %d is too large; you probably need " ++ "to rebuild Apache with a larger FD_SETSIZE " ++ "(currently %d)", ++ sock_fd, FD_SETSIZE); ++ apr_socket_close(sock); ++ _DBG("child_num=%d: exiting with error", my_child_num); ++ return; ++ } ++ ++ if (CHILD_INFO_TABLE[my_child_num].sock_fd < 0) ++ { ++ ap_sock_disable_nagle(sock); ++ } ++ ++ if (!current_conn) { ++ ap_create_sb_handle(&sbh, p, conn_id, 0); ++ current_conn = ap_run_create_connection(p, ap_server_conf, sock, conn_id, ++ sbh, bucket_alloc); ++ } ++ ++ if (current_conn) ++ { ++ ap_process_connection(current_conn, sock); ++ ap_lingering_close(current_conn); ++ } ++} ++ ++static int peruser_process_connection(conn_rec *conn) ++{ ++ ap_filter_t *filter; ++ apr_bucket_brigade *bb; ++ core_net_rec *net; ++ ++ _DBG("function entered",0); ++ ++ /* -- fetch our sockets from the pool -- */ ++ apr_pool_userdata_get((void **)&bb, "PERUSER_SOCKETS", conn->pool); ++ if (bb != NULL) ++ { ++ /* -- find the 'core' filter and give the socket data to it -- */ ++ for (filter = conn->output_filters; filter != NULL; filter = filter->next) ++ { ++ if (!strcmp(filter->frec->name, "core")) break; ++ } ++ if (filter != NULL) ++ { ++ net = filter->ctx; ++ net->in_ctx = apr_palloc(conn->pool, sizeof(*net->in_ctx)); ++ net->in_ctx->b = bb; ++ net->in_ctx->tmpbb = apr_brigade_create(net->in_ctx->b->p, ++ net->in_ctx->b->bucket_alloc); ++ } ++ } ++ _DBG("leaving (DECLINED)", 0); ++ return DECLINED; ++} ++ ++static int total_processors(int child_num) ++{ ++ int i, total; ++ ++ for(i = 0, total = 0; i < NUM_CHILDS; ++i) ++ { ++ if(CHILD_INFO_TABLE[i].senv == CHILD_INFO_TABLE[child_num].senv) ++ total++; ++ } ++ ++ return total; ++} ++ ++static int idle_processors(int child_num) ++{ ++ int i, total; ++ ++ for(i = 0, total = 0; i < NUM_CHILDS; ++i) ++ { ++ if(CHILD_INFO_TABLE[i].senv == CHILD_INFO_TABLE[child_num].senv && ++ (SCOREBOARD_STATUS(i) == SERVER_STARTING || ++ SCOREBOARD_STATUS(i) == SERVER_READY)) ++ { ++ total++; ++ } ++ } ++ ++ return total; ++} ++ ++static int pass_request(request_rec *r, child_info_t *processor) ++{ ++ int rv; ++ struct msghdr msg; ++ struct cmsghdr *cmsg; ++ int sock_fd; ++ char *body = ""; ++ struct iovec iov[4]; ++ conn_rec *c = r->connection; ++ apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc); ++ apr_bucket_brigade *body_bb = NULL; ++ apr_size_t len = 0; ++ apr_size_t header_len = 0; ++ apr_size_t body_len = 0; ++ peruser_header h; ++ apr_bucket *bucket; ++ const apr_array_header_t *headers_in_array; ++ const apr_table_entry_t *headers_in; ++ int counter; ++ ++ apr_socket_t *thesock = ap_get_module_config(r->connection->conn_config, &core_module); ++ ++ if ((!r->the_request) || (!strlen(r->the_request))) ++ { ++ _DBG("empty request. dropping it (%ld)", r->the_request); ++ return -1; ++ } ++ ++ if (!processor) ++ { ++ _DBG("server %s in child %d has no child_info associated", ++ r->hostname, my_child_num); ++ return -1; ++ } ++ ++ _DBG("passing request to another child. Vhost: %s, child %d %d", ++ apr_table_get(r->headers_in, "Host"), my_child_num, processor->senv->output); ++ _DBG("r->the_request=\"%s\" len=%d", r->the_request, strlen(r->the_request)); ++ ++ ap_get_brigade(r->connection->input_filters, bb, AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, len); ++ ++ /* Scan the brigade looking for heap-buckets */ ++ ++ _DBG("Scanning the brigade",0); ++ bucket = APR_BRIGADE_FIRST(bb); ++ while (bucket != APR_BRIGADE_SENTINEL(bb) && ++ APR_BUCKET_IS_HEAP(bucket)) { ++ _DBG("HEAP BUCKET is found, length=%d", bucket->length); ++ bucket = APR_BUCKET_NEXT(bucket); ++ if (!APR_BUCKET_IS_HEAP(bucket)) { ++ _DBG("NON-HEAP BUCKET is found, extracting the part of brigade before it",0); ++ body_bb = bb; ++ bb = apr_brigade_split(body_bb, bucket); ++ /* Do we need to apr_destroy_brigade(bb) here? ++ * Yeah, I know we do apr_pool_destroy(r->pool) before return, but ++ * ap_get_brigade is in non-blocking mode (however len is zero). ++ */ ++ if (apr_brigade_pflatten(body_bb, &body, &body_len, r->pool) != APR_SUCCESS) { ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, ++ "Unable to flatten brigade, declining request"); ++ apr_pool_destroy(r->pool); ++ return DECLINED; ++ } ++ _DBG("Brigade is flattened as body (body_len=%d)", body_len); ++ } ++ } ++ _DBG("Scanning is finished",0); ++ ++ apr_os_sock_get(&sock_fd, thesock); ++ ++ h.p = r->pool; ++ ++ headers_in_array = apr_table_elts(r->headers_in); ++ headers_in = (const apr_table_entry_t *) headers_in_array->elts; ++ ++ h.headers = apr_pstrcat(h.p, r->the_request, CRLF, NULL); ++ for (counter = 0; counter < headers_in_array->nelts; counter++) { ++ if (headers_in[counter].key == NULL ++ || headers_in[counter].val == NULL) { ++ continue; ++ } ++ h.headers = apr_pstrcat(h.p, h.headers, headers_in[counter].key, ": ", ++ headers_in[counter].val, CRLF, NULL); ++ ++ } ++ h.headers = apr_pstrcat(h.p, h.headers, CRLF, NULL); ++ ap_xlate_proto_to_ascii(h.headers, strlen(h.headers)); ++ ++ header_len = strlen(h.headers); ++ ++ iov[0].iov_base = &header_len; ++ iov[0].iov_len = sizeof(header_len); ++ iov[1].iov_base = &body_len; ++ iov[1].iov_len = sizeof(body_len); ++ iov[2].iov_base = h.headers; ++ iov[2].iov_len = strlen(h.headers) + 1; ++ iov[3].iov_base = body; ++ iov[3].iov_len = body_len; ++ ++ msg.msg_name = NULL; ++ msg.msg_namelen = 0; ++ msg.msg_iov = iov; ++ msg.msg_iovlen = 4; ++ ++ cmsg = apr_palloc(r->pool, sizeof(*cmsg) + sizeof(sock_fd)); ++ cmsg->cmsg_len = sizeof(*cmsg) + sizeof(sock_fd); ++ cmsg->cmsg_level = SOL_SOCKET; ++ cmsg->cmsg_type = SCM_RIGHTS; ++ ++ memcpy(CMSG_DATA(cmsg), &sock_fd, sizeof(sock_fd)); ++ ++ msg.msg_control = cmsg; ++ msg.msg_controllen = cmsg->cmsg_len; ++ ++ ++ if (processor->status == CHILD_STATUS_STANDBY) ++ { ++ _DBG("Activating child #%d", processor->id); ++ processor->status = CHILD_STATUS_STARTING; ++ } ++ ++ _DBG("Writing message to %d, passing sock_fd: %d", processor->senv->output, sock_fd); ++ _DBG("header_len=%d headers=\"%s\"", header_len, h.headers); ++ _DBG("body_len=%d body=\"%s\"", body_len, body); ++ ++ if ((rv = sendmsg(processor->senv->output, &msg, 0)) == -1) ++ { ++ apr_pool_destroy(r->pool); ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, ++ "Writing message failed %d %d", rv, errno); ++ return -1; ++ } ++ ++ _DBG("Writing message succeeded %d", rv); ++ ++ /* -- close the socket on our side -- */ ++ _DBG("closing socket %d on our side", sock_fd); ++ apr_socket_close(thesock); ++ ++ apr_pool_destroy(r->pool); ++ return 1; ++} ++ ++ ++static apr_status_t receive_from_multiplexer( ++ void **trans_sock, /* will be filled out w/ the received socket */ ++ ap_listen_rec *lr, /* listener to receive from */ ++ apr_pool_t *ptrans /* transaction wide pool */ ++) ++{ ++ struct msghdr msg; ++ struct cmsghdr *cmsg; ++ char buff[HUGE_STRING_LEN] = ""; ++ char headers[HUGE_STRING_LEN] = ""; ++ char *body = ""; ++ apr_size_t header_len, body_len; ++ struct iovec iov[3]; ++ int ret, fd_tmp; ++ apr_os_sock_t ctrl_sock_fd; ++ apr_os_sock_t trans_sock_fd; ++ ++ /* -- bucket's, brigades and their allocators */ ++ apr_bucket_alloc_t *alloc = apr_bucket_alloc_create(ptrans); ++ apr_bucket_brigade *bb = apr_brigade_create(ptrans, alloc); ++ apr_bucket *bucket; ++ ++ /* prepare the buffers for receiving data from remote side */ ++ iov[0].iov_base = &header_len; ++ iov[0].iov_len = sizeof(header_len); ++ iov[1].iov_base = &body_len; ++ iov[1].iov_len = sizeof(body_len); ++ iov[2].iov_base = (char*)&buff; ++ iov[2].iov_len = HUGE_STRING_LEN; ++ ++ cmsg = apr_palloc(ptrans, sizeof(*cmsg) + sizeof(trans_sock_fd)); ++ cmsg->cmsg_len = sizeof(*cmsg) + sizeof(trans_sock_fd); ++ ++ msg.msg_name = NULL; ++ msg.msg_namelen = 0; ++ msg.msg_iov = iov; ++ msg.msg_iovlen = 3; ++ msg.msg_control = cmsg; ++ msg.msg_controllen = cmsg->cmsg_len; ++ ++ /* -- receive data from socket -- */ ++ apr_os_sock_get(&ctrl_sock_fd, lr->sd); ++ _DBG("receiving from sock_fd=%d", ctrl_sock_fd); ++ ret = recvmsg(ctrl_sock_fd, &msg, 0); ++ ++ if(ret == -1) ++ _DBG("recvmsg failed with error \"%s\"", strerror(errno)); ++ else ++ _DBG("recvmsg returned %d", ret); ++ ++ /* -- extract socket from the cmsg -- */ ++ memcpy(&trans_sock_fd, CMSG_DATA(cmsg), sizeof(trans_sock_fd)); ++ apr_os_sock_put((apr_socket_t **)trans_sock, &trans_sock_fd, ptrans); ++ apr_os_sock_get(&fd_tmp, *trans_sock); ++ ++ _DBG("trans_sock=%ld fdx=%d sock_fd=%d", ++ *trans_sock, trans_sock_fd, fd_tmp); ++ ++ apr_cpystrn(headers, buff, header_len + 1); ++ _DBG("header_len=%d headers=\"%s\"", header_len, headers); ++ ++if (header_len) { ++ _DBG("header_len > 0, we got a request", 0); ++ /* -- store received data into an brigade and add ++ it to the current transaction's pool -- */ ++ bucket = apr_bucket_eos_create(alloc); ++ APR_BRIGADE_INSERT_HEAD(bb, bucket); ++ bucket = apr_bucket_socket_create(*trans_sock, alloc); ++ APR_BRIGADE_INSERT_HEAD(bb, bucket); ++ ++ if (body_len) { ++ body = (char*)&buff[header_len + 1]; ++ _DBG("body_len=%d body=\"%s\"", body_len, body); ++ ++ bucket = apr_bucket_heap_create(body, body_len, NULL, alloc); ++ APR_BRIGADE_INSERT_HEAD(bb, bucket); ++ } else { ++ _DBG("There is no body",0); ++ } ++ ++ bucket = apr_bucket_heap_create(headers, header_len, NULL, alloc); ++ ++ APR_BRIGADE_INSERT_HEAD(bb, bucket); ++ apr_pool_userdata_set(bb, "PERUSER_SOCKETS", NULL, ptrans); ++} else { ++ _DBG("header_len == 0, we got a socket only", 0); ++} ++ _DBG("returning 0", 0); ++ return 0; ++} ++ ++ ++/* Set group privileges. ++ * ++ * Note that we use the username as set in the config files, rather than ++ * the lookup of to uid --- the same uid may have multiple passwd entries, ++ * with different sets of groups for each. ++ */ ++ ++static int set_group_privs(uid_t uid, gid_t gid) ++{ ++ if (!geteuid()) ++ { ++ struct passwd *ent; ++ const char *name; ++ ++ /* ++ * Set the GID before initgroups(), since on some platforms ++ * setgid() is known to zap the group list. ++ */ ++ if (setgid(gid) == -1) ++ { ++ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, ++ "setgid: unable to set group id to Group %u", ++ (unsigned)gid); ++ return -1; ++ } ++ ++ /* if getpwuid() fails, just skip initgroups() */ ++ ++ if ((ent = getpwuid(uid)) != NULL) ++ { ++ name = ent->pw_name; ++ ++ /* Reset `groups' attributes. */ ++ ++ if (initgroups(name, gid) == -1) ++ { ++ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, ++ "initgroups: unable to set groups for User %s " ++ "and Group %u", name, (unsigned)gid); ++ return -1; ++ } ++ } ++ } ++ return 0; ++} ++ ++static int peruser_setup_child(int childnum) ++{ ++ server_env_t *senv = CHILD_INFO_TABLE[childnum].senv; ++ ++ if(senv->chroot) { ++ _DBG("chroot to %s", senv->chroot); ++ if(chroot(senv->chroot)) { ++ _DBG("chroot failure %s", senv->chroot); ++ return; ++ } ++ } ++ ++ if (senv->uid == -1 && senv->gid == -1) { ++ return unixd_setup_child(); ++ } ++ if (set_group_privs(senv->uid, senv->gid)) { ++ return -1; ++ } ++ /* Only try to switch if we're running as root */ ++ if (!geteuid() ++ && ( ++#ifdef _OSD_POSIX ++ os_init_job_environment(ap_server_conf, unixd_config.user_name, ++ one_process) != 0 || ++#endif ++ setuid(senv->uid) == -1)) { ++ ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, ++ "setuid: unable to change to uid: %ld", ++ (long) senv->uid); ++ return -1; ++ } ++ return 0; ++} ++ ++static int check_signal(int signum) ++{ ++ _DBG("signum=%d", signum); ++ switch (signum) { ++ case SIGTERM: ++ case SIGINT: ++ just_die(signum); ++ return 1; ++ } ++ return 0; ++} ++ ++/* Send a single HTTP header field to the client. Note that this function ++ * is used in calls to table_do(), so their interfaces are co-dependent. ++ * In other words, don't change this one without checking table_do in alloc.c. ++ * It returns true unless there was a write error of some kind. ++ */ ++static int peruser_header_field(peruser_header *h, ++ const char *fieldname, const char *fieldval) ++{ ++ apr_pstrcat(h->p, h->headers, fieldname, ": ", fieldval, CRLF, NULL); ++ return 1; ++} ++ ++static inline ap_listen_rec* listen_add(apr_pool_t* pool, apr_socket_t *sock, void* accept_func) ++{ ++ ap_listen_rec *lr_walk, *lr_new; ++ ++ _DBG("function entered", 0); ++ /* -- create an new listener for this child -- */ ++ lr_new = apr_palloc(pool, sizeof(*lr_new)); ++ lr_new->sd = sock; ++ lr_new->active = 1; ++ lr_new->accept_func = accept_func; ++ lr_new->next = NULL; ++ ++ /* -- add the new listener_rec into the list -- */ ++ /* FIXME: should we somehow lock this list ? */ ++ lr_walk = ap_listeners; ++ if (lr_walk) ++ { ++ while (lr_walk->next) lr_walk = lr_walk->next; ++ lr_walk->next = lr_new; ++ } ++ else ++ { ++ ap_listeners = lr_walk = lr_new; ++ } ++ num_listensocks++; ++ return lr_new; ++} ++ ++static inline void listen_clear() ++{ ++ ap_listen_rec *lr_walk; ++ ++ _DBG("function entered", 0); ++ ++ /* FIXME: should we somehow lock this list ? */ ++ while (ap_listeners) ++ { ++ lr_walk = ap_listeners->next; ++ apr_socket_close(ap_listeners->sd); ++ ap_listeners = lr_walk; ++ } ++ num_listensocks=0; ++} ++ ++apr_status_t cleanup_child_info(void *d) ++{ ++ if (child_info_image == NULL) { ++ return APR_SUCCESS; ++ } ++ ++ free(child_info_image); ++ child_info_image = NULL; ++ apr_shm_destroy(child_info_shm); ++ ++ return APR_SUCCESS; ++} ++ ++apr_status_t cleanup_server_environments(void *d) ++{ ++ if (server_env_image == NULL) { ++ return APR_SUCCESS; ++ } ++ ++ free(server_env_image); ++ server_env_image = NULL; ++ apr_shm_destroy(server_env_shm); ++ ++ return APR_SUCCESS; ++} ++ ++static const char* child_clone(); ++ ++static void child_main(int child_num_arg) ++{ ++ apr_pool_t *ptrans; ++ apr_allocator_t *allocator; ++ conn_rec *current_conn; ++ apr_status_t status = APR_EINIT; ++ int i; ++ ap_listen_rec *lr; ++ int curr_pollfd, last_pollfd = 0; ++ apr_pollfd_t *pollset; ++ int offset; ++ ap_sb_handle_t *sbh; ++ apr_status_t rv; ++ apr_bucket_alloc_t *bucket_alloc; ++ int fd; ++ void* sock; ++ void* pod_sock; ++ ++ mpm_state = AP_MPMQ_STARTING; /* for benefit of any hooks that run as this ++ * child initializes ++ */ ++ ++ my_child_num = child_num_arg; ++ ap_my_pid = getpid(); ++ requests_this_child = 0; ++ ++ _DBG("sock_fd_in=%d sock_fd_out=%d", ++ CHILD_INFO_TABLE[my_child_num].senv->input, ++ CHILD_INFO_TABLE[my_child_num].senv->output); ++ ++ /* Get a sub context for global allocations in this child, so that ++ * we can have cleanups occur when the child exits. ++ */ ++ apr_allocator_create(&allocator); ++ 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_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); ++ ++ rv = apr_proc_mutex_child_init(&accept_mutex, ap_lock_fname, pchild); ++ if (rv != APR_SUCCESS) { ++ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, ++ "Couldn't initialize cross-process lock in child"); ++ clean_child_exit(APEXIT_CHILDFATAL); ++ } ++ ++ switch(CHILD_INFO_TABLE[my_child_num].type) ++ { ++ case CHILD_TYPE_MULTIPLEXER: ++ _DBG("MULTIPLEXER %d", my_child_num); ++ ++ /* update status on processors that are ready to accept requests */ ++ _DBG("updating processor stati", 0); ++ for(i = 0; i < NUM_CHILDS; ++i) ++ { ++ if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_READY) ++ CHILD_INFO_TABLE[i].status = CHILD_STATUS_ACTIVE; ++ } ++ ++ break; ++ ++ case CHILD_TYPE_PROCESSOR: ++ case CHILD_TYPE_WORKER: ++ _DBG("%s %d", child_type_string(CHILD_INFO_TABLE[my_child_num].type), my_child_num); ++ ++ /* -- create new listener to receive from multiplexer -- */ ++ apr_os_sock_put((void*)&sock, &CHILD_INFO_TABLE[my_child_num].senv->input, pconf); ++ listen_clear(); ++ listen_add(pconf, sock, receive_from_multiplexer); ++ ++ break; ++ ++ default: ++ _DBG("unspecified child type for %d sleeping a while ...", my_child_num); ++ sleep(5); ++ return; ++ } ++ ++ apr_os_file_get(&fd, pipe_of_death_in); ++ apr_os_sock_put((void*)&pod_sock, &fd, pconf); ++ listen_add(pconf, pod_sock, check_pipe_of_death); ++ ++ (peruser_setup_child(my_child_num) && clean_child_exit(APEXIT_CHILDFATAL)); ++ ap_run_child_init(pchild, ap_server_conf); ++ ++ ap_create_sb_handle(&sbh, pchild, my_child_num, 0); ++ (void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL); ++ ++ /* Set up the pollfd array */ ++ listensocks = apr_pcalloc(pchild, ++ sizeof(*listensocks) * (num_listensocks)); ++ for (lr = ap_listeners, i = 0; i < num_listensocks; lr = lr->next, i++) { ++ listensocks[i].accept_func = lr->accept_func; ++ listensocks[i].sd = lr->sd; ++ } ++ ++ pollset = apr_palloc(pchild, sizeof(*pollset) * num_listensocks); ++ pollset[0].p = pchild; ++ for (i = 0; i < num_listensocks; i++) { ++ pollset[i].desc.s = listensocks[i].sd; ++ pollset[i].desc_type = APR_POLL_SOCKET; ++ pollset[i].reqevents = APR_POLLIN; ++ } ++ ++ mpm_state = AP_MPMQ_RUNNING; ++ ++ bucket_alloc = apr_bucket_alloc_create(pchild); ++ ++ while (!die_now) { ++ /* ++ * (Re)initialize this child to a pre-connection state. ++ */ ++ ++ current_conn = NULL; ++ ++ apr_pool_clear(ptrans); ++ ++ if (CHILD_INFO_TABLE[my_child_num].type != CHILD_TYPE_MULTIPLEXER ++ && ap_max_requests_per_child > 0 ++ && requests_this_child++ >= ap_max_requests_per_child) { ++ _DBG("max requests reached, dying now", 0); ++ clean_child_exit(0); ++ } ++ ++ (void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL); ++ ++ /* ++ * Wait for an acceptable connection to arrive. ++ */ ++ ++ /* Lock around "accept", if necessary */ ++ SAFE_ACCEPT(accept_mutex_on()); ++ ++ if (num_listensocks == 1) { ++ offset = 0; ++ } ++ else { ++ /* multiple listening sockets - need to poll */ ++ for (;;) { ++ apr_status_t ret; ++ apr_int32_t n; ++ ++ ret = apr_poll(pollset, num_listensocks, &n, -1); ++ if (ret != APR_SUCCESS) { ++ if (APR_STATUS_IS_EINTR(ret)) { ++ continue; ++ } ++ /* Single Unix documents select as returning errnos ++ * EBADF, EINTR, and EINVAL... and in none of those ++ * cases does it make sense to continue. In fact ++ * on Linux 2.0.x we seem to end up with EFAULT ++ * occasionally, and we'd loop forever due to it. ++ */ ++ ap_log_error(APLOG_MARK, APLOG_ERR, ret, ap_server_conf, ++ "apr_poll: (listen)"); ++ clean_child_exit(1); ++ } ++ /* find a listener */ ++ curr_pollfd = last_pollfd; ++ do { ++ curr_pollfd++; ++ if (curr_pollfd >= num_listensocks) { ++ curr_pollfd = 0; ++ } ++ /* XXX: Should we check for POLLERR? */ ++ if (pollset[curr_pollfd].rtnevents & APR_POLLIN) { ++ last_pollfd = curr_pollfd; ++ offset = curr_pollfd; ++ goto got_fd; ++ } ++ } while (curr_pollfd != last_pollfd); ++ ++ continue; ++ } ++ } ++ got_fd: ++ _DBG("input available ... resetting socket.",0); ++ sock = NULL; /* important! */ ++ ++ /* if we accept() something we don't want to die, so we have to ++ * defer the exit ++ */ ++ status = listensocks[offset].accept_func(&sock, &listensocks[offset], ptrans); ++ SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ ++ ++ if (status == APR_EGENERAL) { ++ /* resource shortage or should-not-occur occured */ ++ clean_child_exit(1); ++ } ++ else if (status != APR_SUCCESS || die_now) { ++ continue; ++ } ++ ++ if (CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_PROCESSOR || ++ CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_WORKER) ++ { ++ _DBG("CHECKING IF WE SHOULD CLONE A CHILD..."); ++ ++ _DBG("total_processors = %d, max_processors = %d", ++ total_processors(my_child_num), ++ CHILD_INFO_TABLE[my_child_num].senv->max_processors); ++ ++ _DBG("idle_processors = %d, min_free_processors = %d", ++ idle_processors(my_child_num), ++ CHILD_INFO_TABLE[my_child_num].senv->min_free_processors); ++ ++ if(total_processors(my_child_num) < ++ CHILD_INFO_TABLE[my_child_num].senv->max_processors && ++ idle_processors(my_child_num) <= ++ CHILD_INFO_TABLE[my_child_num].senv->min_free_processors) ++ { ++ _DBG("CLONING CHILD"); ++ child_clone(); ++ } ++ } ++ ++ if (!setjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer)) ++ { ++ _DBG("marked jmpbuffer",0); ++ _TRACE_CALL("process_socket()",0); ++ process_socket(ptrans, sock, my_child_num, bucket_alloc, pchild); ++ _TRACE_RET("process_socket()",0); ++ } ++ else ++ { ++ _DBG("landed from longjmp",0); ++ CHILD_INFO_TABLE[my_child_num].sock_fd = AP_PERUSER_THISCHILD; ++ } ++ ++ /* Check the pod and the generation number after processing a ++ * connection so that we'll go away if a graceful restart occurred ++ * while we were processing the connection or we are the lucky ++ * idle server process that gets to die. ++ */ ++ if (ap_mpm_pod_check(pod) == APR_SUCCESS) { /* selected as idle? */ ++ _DBG("ap_mpm_pod_check(pod) = APR_SUCCESS; dying now", 0); ++ die_now = 1; ++ } ++ else 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? ++ */ ++ _DBG("ap_my_generation != ap_scoreboard_image->global->running_generation; dying now", 0); ++ die_now = 1; ++ } ++ ++ if(CHILD_INFO_TABLE[my_child_num].status == CHILD_STATUS_RESTART) ++ { ++ _DBG("restarting", 0); ++ die_now = 1; ++ } ++ } ++ ++ _DBG("clean_child_exit(0)"); ++ clean_child_exit(0); ++} ++ ++static server_env_t* senv_add(int uid, int gid, const char* chroot) ++{ ++ int i; ++ int socks[2]; ++ ++ _DBG("Searching for matching senv..."); ++ ++ for(i = 0; i < NUM_SENV; i++) ++ { ++ if(SENV[i].uid == uid && SENV[i].gid == gid && ++ (SENV[i].chroot == NULL || !strcmp(SENV[i].chroot, chroot))) ++ { ++ _DBG("Found existing senv: %i", i); ++ return &SENV[i]; ++ } ++ } ++ ++ if(NUM_SENV >= server_limit) ++ { ++ _DBG("server_limit reached!"); ++ return NULL; ++ } ++ ++ _DBG("Creating new senv"); ++ ++ SENV[NUM_SENV].uid = uid; ++ SENV[NUM_SENV].gid = gid; ++ SENV[NUM_SENV].chroot = chroot; ++ ++ SENV[NUM_SENV].min_processors = ap_min_processors; ++ SENV[NUM_SENV].min_free_processors = ap_min_free_processors; ++ SENV[NUM_SENV].max_processors = ap_max_processors; ++ ++ socketpair(PF_UNIX, SOCK_STREAM, 0, socks); ++ SENV[NUM_SENV].input = socks[0]; ++ SENV[NUM_SENV].output = socks[1]; ++ ++ return &SENV[server_env_image->control->num++]; ++} ++ ++static const char* child_clone() ++{ ++ int i; ++ child_info_t *this; ++ child_info_t *new; ++ ++ for(i = 0; i < NUM_CHILDS; i++) ++ { ++ if(CHILD_INFO_TABLE[i].pid == 0 && ++ CHILD_INFO_TABLE[i].type == CHILD_TYPE_UNKNOWN) break; ++ } ++ ++ if(i == NUM_CHILDS && NUM_CHILDS >= server_limit) ++ { ++ _DBG("Trying to use more child ID's than NumServers. " ++ "Increase NumServers in your config file."); ++ return NULL; ++ } ++ ++ _DBG("cloning child #%d from #%d", i, my_child_num); ++ ++ this = &CHILD_INFO_TABLE[my_child_num]; ++ new = &CHILD_INFO_TABLE[i]; ++ ++ new->senv = this->senv; ++ new->type = CHILD_TYPE_WORKER; ++ new->sock_fd = this->sock_fd; ++ new->status = CHILD_STATUS_STARTING; ++ ++ if(i == NUM_CHILDS) child_info_image->control->num++; ++ return NULL; ++} ++ ++static const char* child_add(int type, int status, ++ uid_t uid, gid_t gid, const char* chroot) ++{ ++ _DBG("adding child #%d", NUM_CHILDS); ++ ++ if(NUM_CHILDS >= server_limit) ++ { ++ return "Trying to use more child ID's than NumServers. " ++ "Increase NumServers in your config file."; ++ } ++ ++ CHILD_INFO_TABLE[NUM_CHILDS].senv = senv_add(uid, gid, chroot); ++ ++ if(CHILD_INFO_TABLE[NUM_CHILDS].senv == NULL) ++ { ++ return "Trying to use more server environments than NumServers. " ++ "Increase NumServers in your config file."; ++ } ++ ++ if(type != CHILD_TYPE_WORKER) ++ CHILD_INFO_TABLE[NUM_CHILDS].senv->processor_id = NUM_CHILDS; ++ ++ CHILD_INFO_TABLE[NUM_CHILDS].type = type; ++ CHILD_INFO_TABLE[NUM_CHILDS].sock_fd = AP_PERUSER_THISCHILD; ++ CHILD_INFO_TABLE[NUM_CHILDS].status = status; ++ ++ _DBG("[%d] uid=%d gid=%d type=%d chroot=%s", ++ NUM_CHILDS, uid, gid, type, ++ chroot); ++ ++ if (uid == 0 || gid == 0) ++ { ++ _DBG("Assigning root user/group to a child.", 0); ++ } ++ ++ server_env_image->control->num++; ++ child_info_image->control->num++; ++ ++ return NULL; ++} ++ ++static int make_child(server_rec *s, int slot) ++{ ++ int pid; ++ int socks[2]; ++ child_info_t *multiplexer; ++ ++ _DBG("function entered", 0); ++ ++ switch (CHILD_INFO_TABLE[slot].type) ++ { ++ case CHILD_TYPE_MULTIPLEXER: break; ++ case CHILD_TYPE_PROCESSOR: break; ++ case CHILD_TYPE_WORKER: break; ++ ++ default: ++ _DBG("no valid client in slot %d", slot); ++ /* sleep(1); */ ++ return 0; ++ } ++ ++ if (slot + 1 > ap_max_daemons_limit) { ++ ap_max_daemons_limit = slot + 1; ++ } ++ ++ if (one_process) { ++ apr_signal(SIGHUP, just_die); ++ /* Don't catch AP_SIG_GRACEFUL in ONE_PROCESS mode :) */ ++ apr_signal(SIGINT, just_die); ++#ifdef SIGQUIT ++ apr_signal(SIGQUIT, SIG_DFL); ++#endif ++ apr_signal(SIGTERM, just_die); ++ child_main(slot); ++ } ++ ++ (void) ap_update_child_status_from_indexes(slot, 0, SERVER_STARTING, ++ (request_rec *) NULL); ++ ++ CHILD_INFO_TABLE[slot].status = CHILD_STATUS_ACTIVE; ++ ++ ++#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) { ++#else ++ if ((pid = fork()) == -1) { ++#endif ++ ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "fork: Unable to fork new process"); ++ ++ /* fork didn't succeed. Fix the scoreboard or else ++ * it will say SERVER_STARTING forever and ever ++ */ ++ (void) ap_update_child_status_from_indexes(slot, 0, SERVER_DEAD, ++ (request_rec *) NULL); ++ ++ /* In case system resources are maxxed out, we don't want ++ Apache running away with the CPU trying to fork over and ++ over and over again. */ ++ sleep(10); ++ ++ return -1; ++ } ++ ++ if (!pid) { ++#ifdef HAVE_BINDPROCESSOR ++ /* by default AIX binds to a single processor ++ * this bit unbinds children which will then bind to another cpu ++ */ ++ 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); ++ } ++#endif ++ RAISE_SIGSTOP(MAKE_CHILD); ++ AP_MONCONTROL(1); ++ /* Disable the parent's signal handlers and set up proper handling in ++ * the child. ++ */ ++ apr_signal(SIGHUP, just_die); ++ apr_signal(SIGTERM, just_die); ++ /* The child process doesn't do anything for AP_SIG_GRACEFUL. ++ * Instead, the pod is used for signalling graceful restart. ++ */ ++ /* apr_signal(AP_SIG_GRACEFUL, restart); */ ++ child_main(slot); ++ clean_child_exit(0); ++ } ++ ++ ap_scoreboard_image->parent[slot].pid = pid; ++ CHILD_INFO_TABLE[slot].pid = pid; ++ ++ ap_child_table[slot].pid = pid; ++ ap_child_table[slot].status = SERVER_ALIVE; ++ ++ return 0; ++} ++ ++ ++/* ++ * 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 int total_processes(int child_num) ++{ ++ int i, total; ++ for(i = 0, total = 0; i < NUM_CHILDS; ++i) ++ { ++ if(CHILD_INFO_TABLE[i].senv == CHILD_INFO_TABLE[child_num].senv && ++ (!(CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR && ++ CHILD_INFO_TABLE[i].status == CHILD_STATUS_STANDBY))) ++ { ++ total++; ++ } ++ } ++ return total; ++} ++ ++static void perform_idle_server_maintenance(apr_pool_t *p) ++{ ++ int i; ++ apr_time_t now; ++ ++ /* _DBG("function entered", 0); */ ++ ++ now = apr_time_now(); ++ ++ for (i = 0; i < NUM_CHILDS; ++i) ++ { ++ if(CHILD_INFO_TABLE[i].pid == 0) ++ { ++ if(CHILD_INFO_TABLE[i].status == CHILD_STATUS_STARTING) ++ make_child(ap_server_conf, i); ++ } ++ else if(((CHILD_INFO_TABLE[i].type == CHILD_TYPE_PROCESSOR || ++ CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER) && ++ ap_scoreboard_image->parent[i].pid > 1) && ++ (idle_processors (i) > 1 || total_processes (i) == 1) && ( ++ (expire_timeout > 0 && ap_scoreboard_image->servers[i][0].status != SERVER_DEAD && ++ apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > expire_timeout) || ++ (idle_timeout > 0 && ap_scoreboard_image->servers[i][0].status == SERVER_READY && ++ apr_time_sec(now - ap_scoreboard_image->servers[i][0].last_used) > idle_timeout))) ++ { ++ CHILD_INFO_TABLE[i].pid = 0; ++ CHILD_INFO_TABLE[i].status = CHILD_STATUS_STANDBY; ++ ++ if(CHILD_INFO_TABLE[i].type == CHILD_TYPE_WORKER) ++ { ++ /* completely free up this slot */ ++ ++ CHILD_INFO_TABLE[i].senv = (server_env_t*)NULL; ++ CHILD_INFO_TABLE[i].type = CHILD_TYPE_UNKNOWN; ++ CHILD_INFO_TABLE[i].sock_fd = -3; /* -1 and -2 are taken */ ++ } ++ if(kill(ap_scoreboard_image->parent[i].pid, SIGTERM) == -1) ++ { ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ++ ap_server_conf, "kill SIGTERM"); ++ } ++ ++ ++ ap_update_child_status_from_indexes(i, 0, SERVER_DEAD, NULL); ++ } ++ } ++ ++ for(i=0;i 0 && ++ apr_time_sec(now - child_grace_info_table[i].last_used) > expire_timeout) { ++ ++ _DBG("Killing a child from last graceful (pid=%d,childno=%d,last_used=%d)", ++ child_grace_info_table[i].pid, child_grace_info_table[i].id, ++ child_grace_info_table[i].last_used); ++ ++ if(kill(child_grace_info_table[i].pid, SIGTERM) == -1) ++ { ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ++ ap_server_conf, "kill SIGTERM"); ++ } ++ ++ /* We don't need to do remove_grace_child() here, ++ * because it will be automatically done once ++ * the child dies by ap_mpm_run() */ ++ } ++ } ++} ++ ++int remove_grace_child(int slot) { ++ if (slot < grace_children) { ++ child_grace_info_table[slot].id = 0; ++ child_grace_info_table[slot].pid = 0; ++ child_grace_info_table[slot].status = CHILD_STATUS_STANDBY; ++ child_grace_info_table[slot].type = CHILD_TYPE_UNKNOWN; ++ child_grace_info_table[slot].last_used = 0; ++ grace_children_alive--; ++ ++ if (grace_children_alive <= 0) { /* All children have returned from graceful */ ++ _DBG("Every child has returned from graceful restart - freeing child_grace_info_table"); ++ grace_children_alive = 0; ++ is_graceful = 0; ++ grace_children = 0; ++ free(child_grace_info_table); ++ } ++ return 0; ++ } ++ return 1; ++} ++ ++/***************************************************************** ++ * Executive routines. ++ */ ++ ++int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) ++{ ++ int i; ++/* int fd; */ ++ apr_status_t rv; ++ apr_size_t one = 1; ++/* apr_socket_t *sock = NULL; */ ++ ++ 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; ++ } ++ ++ ap_server_conf = s; ++ ++ /* 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); ++ 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 0 ++#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; ++ } ++ } ++#endif ++ ++ if (!is_graceful) { ++ if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) { ++ mpm_state = AP_MPMQ_STOPPING; ++ return 1; ++ } ++ /* fix the generation number in the global score; we just got a new, ++ * cleared scoreboard ++ */ ++ ap_scoreboard_image->global->running_generation = ap_my_generation; ++ } ++ ++ /* Initialize the child table */ ++ if (!is_graceful) ++ { ++ for (i = 0; i < server_limit; i++) ++ { ++ ap_child_table[i].pid = 0; ++ } ++ } ++ ++ /* We need to put the new listeners at the end of the ap_listeners ++ * list. If we don't, then the pool will be cleared before the ++ * open_logs phase is called for the second time, and ap_listeners ++ * will have only invalid data. If that happens, then the sockets ++ * that we opened using make_sock() will be lost, and the server ++ * won't start. ++ */ ++ ++/* ++ apr_os_file_get(&fd, pipe_of_death_in); ++ apr_os_sock_put(&sock, &fd, pconf); ++ ++ listen_add(pconf, sock, check_pipe_of_death); ++*/ ++ set_signals(); ++ ++ if (one_process) { ++ AP_MONCONTROL(1); ++ } ++ ++ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, ++ "%s configured -- resuming normal operations", ++ ap_get_server_version()); ++ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, ++ "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 ++ restart_pending = shutdown_pending = 0; ++ ++ mpm_state = AP_MPMQ_RUNNING; ++ ++ _DBG("sizeof(child_info_t) = %d", sizeof(child_info_t)); ++ ++ while (!restart_pending && !shutdown_pending) { ++ int child_slot; ++ apr_exit_why_e exitwhy; ++ int status, processed_status; ++ /* this is a memory leak, but I'll fix it later. */ ++ apr_proc_t pid; ++ ++ ap_wait_or_timeout(&exitwhy, &status, &pid, pconf); ++ ++ /* XXX: if it takes longer than 1 second for all our children ++ * to start up and get into IDLE state then we may spawn an ++ * extra child ++ */ ++ if (pid.pid != -1) { ++ processed_status = ap_process_child_status(&pid, exitwhy, status); ++ if (processed_status == APEXIT_CHILDFATAL) { ++ mpm_state = AP_MPMQ_STOPPING; ++ return 1; ++ } ++ ++ if (grace_children > 0) { ++ for(i=0;i= 0) { ++ ap_child_table[child_slot].pid = 0; ++ _TRACE_CALL("ap_update_child_status_from_indexes", 0); ++ (void) ap_update_child_status_from_indexes(child_slot, 0, SERVER_DEAD, ++ (request_rec *) NULL); ++ _TRACE_RET("ap_update_child_status_from_indexes", 0); ++ ++ if (processed_status == APEXIT_CHILDSICK) { ++ /* child detected a resource shortage (E[NM]FILE, ENOBUFS, etc) ++ * cut the fork rate to the minimum ++ */ ++ _DBG("processed_status = APEXIT_CHILDSICK", 0); ++ idle_spawn_rate = 1; ++ } ++ else if (CHILD_INFO_TABLE[child_slot].status == CHILD_STATUS_STANDBY) { ++ _DBG("leaving child in standby state", 0); ++ } ++ else if (child_slot < ap_daemons_limit && ++ CHILD_INFO_TABLE[child_slot].type != ++ CHILD_TYPE_UNKNOWN) { ++ /* we're still doing a 1-for-1 replacement of dead ++ * children with new children ++ */ ++ _DBG("replacing by new child ...", 0); ++ make_child(ap_server_conf, child_slot); ++ } ++#if APR_HAS_OTHER_CHILD ++ } ++ else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH, status) == APR_SUCCESS) { ++ _DBG("Already handled", 0); ++ /* handled */ ++#endif ++ } ++ else if (is_graceful) { ++ /* Great, we've probably just lost a slot in the ++ * scoreboard. Somehow we don't know about this ++ * child. ++ */ ++ _DBG("long lost child came home, whatever that means", 0); ++ ++ ap_log_error(APLOG_MARK, APLOG_WARNING, ++ 0, ap_server_conf, ++ "long lost child came home! (pid %ld)", (long)pid.pid); ++ } ++ /* Don't perform idle maintenance when a child dies, ++ * only do it when there's a timeout. Remember only a ++ * finite number of children can die, and it's pretty ++ * pathological for a lot to die suddenly. ++ */ ++ continue; ++ } ++ ++ perform_idle_server_maintenance(pconf); ++#ifdef TPF ++ shutdown_pending = os_check_server(tpf_server_name); ++ ap_check_signals(); ++ sleep(1); ++#endif /*TPF */ ++ } ++ ++ mpm_state = AP_MPMQ_STOPPING; ++ ++ if (shutdown_pending) { ++ /* Time to gracefully 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"); ++ } ++ ap_reclaim_child_processes(1); /* Start with SIGTERM */ ++ ++ /* cleanup pid file on normal shutdown */ ++ { ++ const char *pidfile = NULL; ++ pidfile = ap_server_root_relative (pconf, ap_pid_fname); ++ if ( pidfile != NULL && unlink(pidfile) == 0) ++ ap_log_error(APLOG_MARK, APLOG_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, ++ "caught SIGTERM, shutting down"); ++ return 1; ++ } ++ ++ /* we've been told to restart */ ++ apr_signal(SIGHUP, SIG_IGN); ++ if (one_process) { ++ /* not worth thinking about */ ++ return 1; ++ } ++ ++ /* advance to the next generation */ ++ /* XXX: we really need to make sure this new generation number isn't in ++ * use by any of the children. ++ */ ++ ++ap_my_generation; ++ ap_scoreboard_image->global->running_generation = ap_my_generation; ++ ++ if (is_graceful) { ++ char char_of_death = AP_PERUSER_CHAR_OF_DEATH; ++ ++ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, ++ "Graceful restart requested, doing restart"); ++ ++#if 0 ++ /* kill off the idle ones */ ++ ap_mpm_pod_killpg(pod, ap_max_daemons_limit); ++ ++ /* This is mostly for debugging... so that we know what is still ++ * gracefully dealing with existing request. This will break ++ * in a very nasty way if we ever have the scoreboard totally ++ * file-based (no shared memory) ++ */ ++ for (i = 0; i < ap_daemons_limit; ++i) { ++ if (ap_scoreboard_image->servers[i][0].status != SERVER_DEAD) { ++ ap_scoreboard_image->servers[i][0].status = SERVER_GRACEFUL; ++ } ++ } ++#endif ++ ++ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ++ ap_server_conf, AP_SIG_GRACEFUL_STRING " received. " ++ "Doing graceful restart"); ++ ++ /* This is mostly for debugging... so that we know what is still ++ * gracefully dealing with existing request. ++ */ ++ ++ int alivechildren = 0; ++ int child_grace_info_size; ++ child_grace_info_t* old_grace_info; ++ void* mem; ++ ++ for (i = 0; i < NUM_CHILDS; ++i) ++ { ++ ((ap_child_table[i].pid) && (ap_child_table[i].status = SERVER_DYING)); ++ ++ if (CHILD_INFO_TABLE[i].pid) { ++ alivechildren++; ++ } ++ } ++ ++ _DBG("Initializing child_grace_info_table", 0); ++ ++ if (alivechildren > 0) { ++ if (grace_children > 0) { ++ old_grace_info = child_grace_info_table; ++ _DBG("%d children still living from last graceful " ++ "- adding to new child_grace_info_table", ++ grace_children); ++ } ++ ++ child_grace_info_table = (child_grace_info_t*)calloc(alivechildren+grace_children, ++ sizeof(child_grace_info_t)); ++ ++ if (grace_children > 0) { ++ for(i=0;iservers[i][0].last_used; ++ grace_children++; ++ grace_children_alive++; ++ } ++ i++; ++ } ++ _DBG("Total children of %d leaving behind for graceful restart (%d living)", ++ grace_children, grace_children_alive); ++ ++ for (i = 0; i < NUM_SENV; i++) ++ { ++ close(SENV[i].input); ++ close(SENV[i].output); ++ } ++ } ++ else { ++ /* Kill 'em off */ ++ if (unixd_killpg(getpgrp(), SIGHUP) < 0) { ++ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGHUP"); ++ } ++ ap_reclaim_child_processes(0); /* Not when just starting up */ ++ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, ++ "SIGHUP received. Attempting to restart"); ++ } ++ ++ return 0; ++} ++ ++/* == allocate an private server config structure == */ ++static void *peruser_create_config(apr_pool_t *p, server_rec *s) ++{ ++ peruser_server_conf *c = (peruser_server_conf *) ++ apr_pcalloc(p, sizeof(peruser_server_conf)); ++ return c; ++} ++ ++/* 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 peruser_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) ++{ ++ apr_status_t rv; ++ ++ pconf = p; ++ ap_server_conf = s; ++ ++ 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"); ++ return DONE; ++ } ++ ++ ap_log_pid(pconf, ap_pid_fname); ++ ++ 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."); ++ return DONE; ++ } ++ ++ if ((rv = apr_file_pipe_create(&pipe_of_death_in, &pipe_of_death_out, ++ pconf)) != APR_SUCCESS) { ++ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ++ (const server_rec*) ap_server_conf, ++ "apr_file_pipe_create (pipe_of_death)"); ++ exit(1); ++ } ++ if ((rv = apr_file_pipe_timeout_set(pipe_of_death_in, 0)) != APR_SUCCESS) { ++ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ++ (const server_rec*) ap_server_conf, ++ "apr_file_pipe_timeout_set (pipe_of_death)"); ++ exit(1); ++ } ++ ++ return OK; ++} ++ ++static int restart_num = 0; ++static int peruser_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) ++{ ++ int no_detach, debug, foreground, i; ++ int tmp_server_limit = DEFAULT_SERVER_LIMIT; ++ ap_directive_t *pdir; ++ apr_status_t rv; ++ apr_pool_t *global_pool; ++ void *shmem; ++ ++ mpm_state = AP_MPMQ_STARTING; ++ ++ debug = ap_exists_config_define("DEBUG"); ++ ++ if (debug) { ++ foreground = one_process = 1; ++ no_detach = 0; ++ } ++ else ++ { ++ no_detach = ap_exists_config_define("NO_DETACH"); ++ one_process = ap_exists_config_define("ONE_PROCESS"); ++ foreground = ap_exists_config_define("FOREGROUND"); ++ } ++ ++ /* sigh, want this only the second time around */ ++ if (restart_num++ == 1) { ++ 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; ++ } ++ } ++ ++ parent_pid = ap_my_pid = getpid(); ++ } ++ ++ unixd_pre_config(ptemp); ++ ap_listen_pre_config(); ++ ap_min_processors = DEFAULT_MIN_PROCESSORS; ++ ap_min_free_processors = DEFAULT_MIN_FREE_PROCESSORS; ++ ap_max_processors = DEFAULT_MAX_PROCESSORS; ++ 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 = 1; ++#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)); ++ ++ /* we need to know ServerLimit and ThreadLimit before we start processing ++ * the tree because we need to already have allocated child_info_table ++ */ ++ for (pdir = ap_conftree; pdir != NULL; pdir = pdir->next) ++ { ++ if (!strcasecmp(pdir->directive, "ServerLimit")) ++ { ++ if (atoi(pdir->args) > tmp_server_limit) ++ { ++ tmp_server_limit = atoi(pdir->args); ++ if (tmp_server_limit > MAX_SERVER_LIMIT) ++ { ++ tmp_server_limit = MAX_SERVER_LIMIT; ++ } ++ } ++ } ++ } ++ ++ /* We don't want to have to recreate the scoreboard after ++ * restarts, so we'll create a global pool and never clean it. ++ */ ++ rv = apr_pool_create(&global_pool, NULL); ++ if (rv != APR_SUCCESS) { ++ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, ++ "Fatal error: unable to create global pool"); ++ return rv; ++ } ++ ++ ++ _DBG("Initializing child_info_table", 0); ++ child_info_size = tmp_server_limit * sizeof(child_info_t) + sizeof(apr_size_t); ++ ++ rv = apr_shm_create(&child_info_shm, child_info_size, NULL, global_pool); ++ ++/* if ((rv != APR_SUCCESS) && (rv != APR_ENOTIMPL)) { */ ++ if (rv != APR_SUCCESS) { ++ _DBG("shared memory creation failed", 0); ++ ++ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, ++ "Unable to create shared memory segment " ++ "(anonymous shared memory failure)"); ++ } ++ else if (rv == APR_ENOTIMPL) { ++ _DBG("anonymous shared memory not available", 0); ++ /* TODO: make up a filename and do name-based shmem */ ++ } ++ ++ if (rv || !(shmem = apr_shm_baseaddr_get(child_info_shm))) { ++ _DBG("apr_shm_baseaddr_get() failed", 0); ++ return HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ memset(shmem, 0, sizeof(child_info_size)); ++ child_info_image = (child_info*)calloc(1, sizeof(child_info_size)); ++ child_info_image->control = (child_info_control*)shmem; ++ shmem += sizeof(child_info_control*); ++ child_info_image->table = (child_info_t*)shmem; ++ ++ child_info_image->control->num = 0; ++ ++ for (i = 0; i < tmp_server_limit; i++) ++ { ++ CHILD_INFO_TABLE[i].pid = 0; ++ CHILD_INFO_TABLE[i].senv = (server_env_t*)NULL; ++ CHILD_INFO_TABLE[i].type = CHILD_TYPE_UNKNOWN; ++ CHILD_INFO_TABLE[i].status = CHILD_STATUS_STANDBY; ++ CHILD_INFO_TABLE[i].sock_fd = -3; /* -1 and -2 are taken */ ++ CHILD_INFO_TABLE[i].id = i; ++ } ++ ++ _DBG("Initializing server_environments_table", 0); ++ server_env_size = tmp_server_limit * sizeof(server_env_t) + sizeof(apr_size_t); ++ ++ rv = apr_shm_create(&server_env_shm, server_env_size, NULL, global_pool); ++ ++ if (rv != APR_SUCCESS) { ++ _DBG("shared memory creation failed", 0); ++ ++ ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, ++ "Unable to create shared memory segment " ++ "(anonymous shared memory failure)"); ++ } ++ else if (rv == APR_ENOTIMPL) { ++ _DBG("anonymous shared memory not available", 0); ++ /* TODO: make up a filename and do name-based shmem */ ++ } ++ ++ if (rv || !(shmem = apr_shm_baseaddr_get(server_env_shm))) { ++ _DBG("apr_shm_baseaddr_get() failed", 0); ++ return HTTP_INTERNAL_SERVER_ERROR; ++ } ++ ++ memset(shmem, 0, sizeof(server_env_size)); ++ server_env_image = (server_env*)calloc(1, sizeof(server_env_size)); ++ server_env_image->control = (server_env_control*)shmem; ++ shmem += sizeof(server_env_control*); ++ server_env_image->table = (server_env_t*)shmem; ++ ++ server_env_image->control->num = 0; ++ ++ for (i = 0; i < tmp_server_limit; i++) ++ { ++ SENV[i].processor_id = -1; ++ SENV[i].uid = -1; ++ SENV[i].gid = -1; ++ SENV[i].chroot = NULL; ++ SENV[i].input = -1; ++ SENV[i].output = -1; ++ } ++ ++ return OK; ++} ++ ++static int peruser_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *server_list) ++{ ++ ap_child_table = (ap_ctable *)apr_pcalloc(p, server_limit * sizeof(ap_ctable)); ++ ++ return OK; ++} ++ ++static int peruser_post_read(request_rec *r) ++{ ++ peruser_server_conf *sconf = PERUSER_SERVER_CONF(r->server->module_config); ++ child_info_t *processor; ++ ++ if(CHILD_INFO_TABLE[my_child_num].type == CHILD_TYPE_MULTIPLEXER) ++ processor = &CHILD_INFO_TABLE[sconf->senv->processor_id]; ++ else ++ processor = &CHILD_INFO_TABLE[r->connection->id]; ++ ++ ++ if (!strlen(r->the_request)) ++ { ++ _DBG("corrupt request. aborting",0); ++ return DECLINED; ++ } ++ ++ if (processor->sock_fd != AP_PERUSER_THISCHILD) ++ { ++ apr_socket_t *sock = NULL; ++ ++ apr_os_sock_put(&sock, &processor->sock_fd, r->connection->pool); ++ ap_sock_disable_nagle(sock); ++ ap_set_module_config(r->connection->conn_config, &core_module, sock); ++ _DBG("not the right socket?", 0); ++ return OK; ++ } ++ ++ switch (CHILD_INFO_TABLE[my_child_num].type) ++ { ++ case CHILD_TYPE_MULTIPLEXER: ++ { ++ _DBG("MULTIPLEXER => Determining if request should be passed. " ++ "Child Num: %d, dest-child: %d, hostname from server: %s r->hostname=%s r->the_request=\"%s\"", ++ my_child_num, processor->id, r->server->server_hostname, r->hostname, r->the_request); ++ ++ if (processor->id != my_child_num) ++ { ++ if (processor->status == CHILD_STATUS_STANDBY) ++ { ++ _DBG("Activating child #%d", processor->id); ++ processor->status = CHILD_STATUS_STARTING; ++ } ++ ++ _DBG("Passing request.",0); ++ if (pass_request(r, processor) == -1) ++ { ++ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ++ ap_server_conf, "Could not pass request to proper " "child, request will not be honoured."); ++ return DECLINED; ++ } ++ _DBG("doing longjmp",0); ++ longjmp(CHILD_INFO_TABLE[my_child_num].jmpbuffer, 1); ++ _DBG("request declined at our site",0); ++ return DECLINED; ++ _DBG("OUH! we should never reach this point",0); ++ } ++ _DBG("WTF: the server is assigned to the multiplexer! ... dropping request",0); ++ return DECLINED; ++ } ++ case CHILD_TYPE_PROCESSOR: ++ case CHILD_TYPE_WORKER: ++ { ++ _DBG("%s %d", child_type_string(CHILD_INFO_TABLE[my_child_num].type), my_child_num); ++ _DBG("request for %s / (server %s) seems to be for us", r->hostname, r->server->server_hostname); ++ ++ return OK; ++ } ++ default: ++ { ++ _DBG("unspecified child type %d in %d, dropping request", ++ CHILD_INFO_TABLE[my_child_num].type, my_child_num); ++ return DECLINED; ++ } ++ } ++ ++ _DBG("THIS POINT SHOULD NOT BE REACHED!",0); ++ return OK; ++} ++ ++static int peruser_status_hook(request_rec *r, int flags) ++{ ++ int x; ++ server_env_t *senv; ++ ++ if (flags & AP_STATUS_SHORT) ++ return OK; ++ ++ ap_rputs("
\n", r); ++ ap_rputs("

peruser status

\n", r); ++ ap_rputs("\n", r); ++ ap_rputs("" ++ "" ++ "" ++ "" ++ "\n", r); ++ for (x = 0; x < NUM_CHILDS; x++) ++ { ++ senv = CHILD_INFO_TABLE[x].senv; ++ ap_rprintf(r, "" ++ "" ++ "" ++ "\n", ++ CHILD_INFO_TABLE[x].id, ++ CHILD_INFO_TABLE[x].pid, ++ child_status_string(CHILD_INFO_TABLE[x].status), ++ child_type_string(CHILD_INFO_TABLE[x].type), ++ senv == NULL ? -1 : senv->uid, ++ senv == NULL ? -1 : senv->gid, ++ senv == NULL ? NULL : senv->chroot, ++ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->input, ++ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->output, ++ CHILD_INFO_TABLE[x].sock_fd, ++ total_processors(x), ++ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->max_processors, ++ idle_processors(x), ++ senv == NULL ? -1 : CHILD_INFO_TABLE[x].senv->min_free_processors ++ ); ++ } ++ ap_rputs("
IDPIDSTATUSTYPEUIDGIDCHROOTINPUTOUTPUTSOCK_FDTOTAL PROCESSORSMAX PROCESSORSIDLE PROCESSORSMIN FREE PROCESSORS
%3d%5d%8s%12s%4d%4d%25s%5d%6d%7d%d%d%d%d
\n", r); ++ ++ if (grace_children > 0) { ++ ap_rputs("

peruser graceful children status

\n", r); ++ ap_rprintf(r, "%d of total %d still living
\n", grace_children_alive, grace_children); ++ ap_rputs("\n", r); ++ ap_rputs("\n", r); ++ for (x = 0; x < grace_children; x++) { ++ ap_rprintf(r, "\n", ++ child_grace_info_table[x].id, ++ child_grace_info_table[x].pid, ++ child_status_string(child_grace_info_table[x].status), ++ child_type_string(child_grace_info_table[x].type) ++ ); ++ } ++ ap_rputs("
IDPIDSTATUSTYPE
%3d%5d%8s%12s
\n", r); ++ } ++ return OK; ++} ++ ++static void peruser_hooks(apr_pool_t *p) ++{ ++ /* The peruser open_logs phase 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(peruser_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE); ++ ap_hook_pre_config(peruser_pre_config, NULL, NULL, APR_HOOK_MIDDLE); ++ ap_hook_post_config(peruser_post_config, NULL, NULL, APR_HOOK_MIDDLE); ++ ++ /* Both of these must be run absolutely first. If this request isn't for ++ * this server then we need to forward it to the proper child. No sense ++ * tying up this server running more post_read request hooks if it is ++ * just going to be forwarded along. The process_connection hook allows ++ * peruser to receive the passed request correctly, by automatically ++ * filling in the core_input_filter's ctx pointer. ++ */ ++ ap_hook_post_read_request(peruser_post_read, NULL, NULL, ++ APR_HOOK_REALLY_FIRST); ++ ap_hook_process_connection(peruser_process_connection, NULL, NULL, ++ APR_HOOK_REALLY_FIRST); ++ ++ APR_OPTIONAL_HOOK(ap, status_hook, peruser_status_hook, NULL, NULL, APR_HOOK_MIDDLE); ++} ++ ++/* we define an Processor w/ specific uid/gid */ ++static const char *cf_Processor(cmd_parms *cmd, void *dummy, ++ const char *user_name, const char *group_name, const char *chroot) ++{ ++ uid_t uid = ap_uname2id(user_name); ++ gid_t gid = ap_gname2id(group_name); ++ ++ _DBG("user=%s:%d group=%s:%d chroot=%s", ++ user_name, uid, group_name, gid, chroot); ++ ++ return child_add(CHILD_TYPE_PROCESSOR, CHILD_STATUS_STANDBY, ++ uid, gid, chroot); ++} ++ ++/* we define an Multiplexer child w/ specific uid/gid */ ++static const char *cf_Multiplexer(cmd_parms *cmd, void *dummy, ++ const char *user_name, const char *group_name, const char *chroot) ++{ ++ uid_t uid = ap_uname2id(user_name); ++ gid_t gid = ap_gname2id(group_name); ++ ++ _DBG("user=%s:%d group=%s:%d chroot=%s [multiplexer id %d]", ++ user_name, uid, group_name, gid, chroot, NUM_CHILDS); ++ ++ return child_add(CHILD_TYPE_MULTIPLEXER, CHILD_STATUS_STARTING, ++ uid, gid, chroot); ++} ++ ++static const char* cf_ServerEnvironment(cmd_parms *cmd, void *dummy, ++ const char *user_name, const char *group_name, const char *chroot) ++{ ++ int uid = ap_uname2id(user_name); ++ int gid = ap_gname2id(group_name); ++ peruser_server_conf *sconf = PERUSER_SERVER_CONF(cmd->server->module_config); ++ ++ _DBG("function entered", 0); ++ ++ sconf->senv = senv_add(uid, gid, chroot); ++ ++ _DBG("user=%s:%d group=%s:%d chroot=%s numchilds=%d", ++ user_name, uid, group_name, gid, chroot, NUM_CHILDS); ++ ++ return NULL; ++} ++ ++static const char *set_min_free_servers(cmd_parms *cmd, void *dummy, const char *arg) ++{ ++ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); ++ if (err != NULL) { ++ return err; ++ } ++ ++ ap_min_free_processors = atoi(arg); ++ if (ap_min_free_processors <= 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_min_free_processors = 1; ++ } ++ ++ return NULL; ++} ++ ++static const char *set_max_clients (cmd_parms *cmd, void *dummy, const char *arg) ++{ ++ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); ++ if (err != NULL) { ++ return err; ++ } ++ ++ ap_daemons_limit = atoi(arg); ++ if (ap_daemons_limit > 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; ++ } ++ return NULL; ++} ++ ++static const char *set_min_processors (cmd_parms *cmd, void *dummy, const char *arg) ++{ ++ peruser_server_conf *sconf; ++ int min_procs; ++ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); ++ ++ if (err != NULL) { ++ return err; ++ } ++ ++ min_procs = atoi(arg); ++ ++ if (min_procs < 1) { ++ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, ++ "WARNING: Require MaxProcessors > 0, setting to 1"); ++ min_procs = 1; ++ } ++ ++ if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) { ++ sconf = PERUSER_SERVER_CONF(cmd->server->module_config); ++ sconf->senv->min_processors = min_procs; ++ } ++ else { ++ ap_min_processors = min_procs; ++ } ++ ++ return NULL; ++} ++ ++static const char *set_min_free_processors (cmd_parms *cmd, void *dummy, const char *arg) ++{ ++ peruser_server_conf *sconf; ++ int min_free_procs; ++ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); ++ ++ if (err != NULL) { ++ return err; ++ } ++ ++ min_free_procs = atoi(arg); ++ ++ if (min_free_procs < 1) { ++ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, ++ "WARNING: Require MinSpareProcessors > 0, setting to 1"); ++ min_free_procs = 1; ++ } ++ ++ if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) { ++ sconf = PERUSER_SERVER_CONF(cmd->server->module_config); ++ sconf->senv->min_free_processors = min_free_procs; ++ } ++ else { ++ ap_min_free_processors = min_free_procs; ++ } ++ ++ return NULL; ++} ++ ++static const char *set_max_processors (cmd_parms *cmd, void *dummy, const char *arg) ++{ ++ peruser_server_conf *sconf; ++ int max_procs; ++ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); ++ ++ if (err != NULL) { ++ return err; ++ } ++ ++ max_procs = atoi(arg); ++ ++ if (max_procs < 1) { ++ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, ++ "WARNING: Require MaxProcessors > 0, setting to 1"); ++ max_procs = 1; ++ } ++ ++ if (ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST) != NULL) { ++ sconf = PERUSER_SERVER_CONF(cmd->server->module_config); ++ sconf->senv->max_processors = max_procs; ++ } ++ else { ++ ap_max_processors = max_procs; ++ } ++ ++ 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; ++ } ++ return NULL; ++} ++ ++static const char *set_expire_timeout (cmd_parms *cmd, void *dummy, const char *arg) { ++ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); ++ if (err != NULL) { ++ return err; ++ } ++ ++ expire_timeout = atoi(arg); ++ ++ return NULL; ++} ++ ++static const char *set_idle_timeout (cmd_parms *cmd, void *dummy, const char *arg) { ++ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); ++ if (err != NULL) { ++ return err; ++ } ++ ++ idle_timeout = atoi(arg); ++ ++ return NULL; ++} ++ ++static const command_rec peruser_cmds[] = { ++UNIX_DAEMON_COMMANDS, ++LISTEN_COMMANDS, ++AP_INIT_TAKE1("MinSpareProcessors", set_min_free_processors, NULL, RSRC_CONF, ++ "Minimum number of idle children, to handle request spikes"), ++AP_INIT_TAKE1("MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, ++ "Minimum number of idle children, to handle request spikes"), ++AP_INIT_TAKE1("MaxClients", set_max_clients, NULL, RSRC_CONF, ++ "Maximum number of children alive at the same time"), ++AP_INIT_TAKE1("MinProcessors", set_min_processors, NULL, RSRC_CONF, ++ "Minimum number of processors per vhost"), ++AP_INIT_TAKE1("MaxProcessors", set_max_processors, NULL, RSRC_CONF, ++ "Maximum number of processors per vhost"), ++AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF, ++ "Maximum value of MaxClients for this run of Apache"), ++AP_INIT_TAKE1("ExpireTimeout", set_expire_timeout, NULL, RSRC_CONF, ++ "Maximum idle time before a child is killed, 0 to disable"), ++AP_INIT_TAKE1("IdleTimeout", set_idle_timeout, NULL, RSRC_CONF, ++ "Maximum time before a child is killed after being idle, 0 to disable"), ++AP_INIT_TAKE23("Multiplexer", cf_Multiplexer, NULL, RSRC_CONF, ++ "Specify an Multiplexer Child configuration."), ++AP_INIT_TAKE23("Processor", cf_Processor, NULL, RSRC_CONF, ++ "Specify a User and Group for a specific child process."), ++AP_INIT_TAKE23("ServerEnvironment", cf_ServerEnvironment, NULL, RSRC_CONF, ++ "Specify the server environment for this virtual host."), ++{ NULL } ++}; ++ ++module AP_MODULE_DECLARE_DATA mpm_peruser_module = { ++ MPM20_MODULE_STUFF, ++ ap_mpm_rewrite_args, /* hook to run before apache parses args */ ++ NULL, /* create per-directory config structure */ ++ NULL, /* merge per-directory config structures */ ++ peruser_create_config, /* create per-server config structure */ ++ NULL, /* merge per-server config structures */ ++ peruser_cmds, /* command apr_table_t */ ++ peruser_hooks, /* register hooks */ ++}; diff --git a/2.2/patches/21_all_itk_20070425-00.patch b/2.2/patches/21_all_itk_20070425-00.patch index 449a5e1..58c9706 100644 --- a/2.2/patches/21_all_itk_20070425-00.patch +++ b/2.2/patches/21_all_itk_20070425-00.patch @@ -11,7 +11,7 @@ Index: httpd-2.2.4/server/mpm/config.m4 APACHE_MPM=$withval ],[ if test "x$APACHE_MPM" = "x"; then -@@ -23,7 +23,7 @@ ap_mpm_is_threaded () +@@ -23,7 +23,7 @@ ap_mpm_is_experimental () { @@ -20,7 +20,7 @@ Index: httpd-2.2.4/server/mpm/config.m4 return 0 else return 1 -@@ -66,6 +66,11 @@ if ap_mpm_is_experimental; then +@@ -66,6 +66,11 @@ else MPM_SUBDIR_NAME=$MPM_NAME fi @@ -33,7 +33,6 @@ Index: httpd-2.2.4/server/mpm/config.m4 MPM_LIB=$MPM_DIR/lib${MPM_NAME}.la Index: httpd-2.2.4/server/mpm/experimental/itk/config.m4 -=================================================================== --- /dev/null +++ httpd-2.2.4/server/mpm/experimental/itk/config.m4 @@ -0,0 +1,3 @@ @@ -41,7 +40,6 @@ Index: httpd-2.2.4/server/mpm/experimental/itk/config.m4 + APACHE_FAST_OUTPUT(server/mpm/$MPM_SUBDIR_NAME/Makefile) +fi Index: httpd-2.2.4/server/mpm/experimental/itk/itk.c -=================================================================== --- /dev/null +++ httpd-2.2.4/server/mpm/experimental/itk/itk.c @@ -0,0 +1,1682 @@ @@ -1246,7 +1244,7 @@ Index: httpd-2.2.4/server/mpm/experimental/itk/itk.c + for (index = 0; index < ap_daemons_limit; ++index) { + if (ap_scoreboard_image->servers[index][0].status != SERVER_DEAD) { + /* Ask each child to close its listeners. */ -+ kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL); ++ ap_mpm_safe_kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL); + active_children++; + } + } @@ -1284,13 +1282,13 @@ Index: httpd-2.2.4/server/mpm/experimental/itk/itk.c + + active_children = 0; + for (index = 0; index < ap_daemons_limit; ++index) { -+ if (MPM_CHILD_PID(index) != 0) { -+ if (kill(MPM_CHILD_PID(index), 0) == 0) { ++ //if (MPM_CHILD_PID(index) != 0) { ++ if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) { + active_children = 1; + /* Having just one child is enough to stay around */ + break; + } -+ } ++ //} + } + } while (!shutdown_pending && active_children && + (!ap_graceful_shutdown_timeout || apr_time_now() < cutoff)); @@ -1341,7 +1339,7 @@ Index: httpd-2.2.4/server/mpm/experimental/itk/itk.c + * piped loggers, etc. They almost certainly won't handle + * it gracefully. + */ -+ kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL); ++ ap_mpm_safe_kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL); + } + } + } @@ -1738,7 +1736,6 @@ Index: httpd-2.2.4/server/mpm/experimental/itk/Makefile.in + +include $(top_srcdir)/build/ltlib.mk Index: httpd-2.2.4/server/mpm/experimental/itk/mpm_default.h -=================================================================== --- /dev/null +++ httpd-2.2.4/server/mpm/experimental/itk/mpm_default.h @@ -0,0 +1,77 @@ @@ -1820,7 +1817,6 @@ Index: httpd-2.2.4/server/mpm/experimental/itk/mpm_default.h +#endif /* AP_MPM_DEFAULT_H */ +/** @} */ Index: httpd-2.2.4/server/mpm/experimental/itk/mpm.h -=================================================================== --- /dev/null +++ httpd-2.2.4/server/mpm/experimental/itk/mpm.h @@ -0,0 +1,65 @@ diff --git a/2.2/patches/80_all_CVE-2006-5752.patch b/2.2/patches/80_all_CVE-2006-5752.patch deleted file mode 100644 index cbf3297..0000000 --- a/2.2/patches/80_all_CVE-2006-5752.patch +++ /dev/null @@ -1,40 +0,0 @@ ---- httpd-2.2.3/modules/generators/mod_status.c.cve5752 -+++ httpd-2.2.3/modules/generators/mod_status.c -@@ -270,7 +270,7 @@ - if (r->method_number != M_GET) - return DECLINED; - -- ap_set_content_type(r, "text/html"); -+ ap_set_content_type(r, "text/html; charset=ISO-8859-1"); - - /* - * Simple table-driven form data set parser that lets you alter the header -@@ -299,7 +299,7 @@ - no_table_report = 1; - break; - case STAT_OPT_AUTO: -- ap_set_content_type(r, "text/plain"); -+ ap_set_content_type(r, "text/plain; charset=ISO-8859-1"); - short_report = 1; - break; - } -@@ -673,7 +673,8 @@ - ap_escape_html(r->pool, - ws_record->client), - ap_escape_html(r->pool, -- ws_record->request), -+ ap_escape_logitem(r->pool, -+ ws_record->request)), - ap_escape_html(r->pool, - ws_record->vhost)); - } -@@ -763,7 +764,8 @@ - ap_escape_html(r->pool, - ws_record->vhost), - ap_escape_html(r->pool, -- ws_record->request)); -+ ap_escape_logitem(r->pool, -+ ws_record->request))); - } /* no_table_report */ - } /* for (j...) */ - } /* for (i...) */ diff --git a/2.2/patches/81_all_CVE-2007-1862.patch b/2.2/patches/81_all_CVE-2007-1862.patch deleted file mode 100644 index 17e6cc5..0000000 --- a/2.2/patches/81_all_CVE-2007-1862.patch +++ /dev/null @@ -1,51 +0,0 @@ ---- httpd-2.2.4/modules/cache/mod_mem_cache.c.cve1862 -+++ httpd-2.2.4/modules/cache/mod_mem_cache.c -@@ -539,12 +539,28 @@ - return OK; - } - -+static apr_table_t *deep_table_copy(apr_pool_t *p, const apr_table_t *table) -+{ -+ const apr_array_header_t *array = apr_table_elts(table); -+ apr_table_entry_t *elts = (apr_table_entry_t *) array->elts; -+ apr_table_t *copy = apr_table_make(p, array->nelts); -+ int i; -+ -+ for (i = 0; i < array->nelts; i++) { -+ if (elts[i].key) { -+ apr_table_add(copy, elts[i].key, elts[i].val); -+ } -+ } -+ -+ return copy; -+} -+ - static apr_status_t recall_headers(cache_handle_t *h, request_rec *r) - { - mem_cache_object_t *mobj = (mem_cache_object_t*) h->cache_obj->vobj; - -- h->req_hdrs = apr_table_copy(r->pool, mobj->req_hdrs); -- h->resp_hdrs = apr_table_copy(r->pool, mobj->header_out); -+ h->req_hdrs = deep_table_copy(r->pool, mobj->req_hdrs); -+ h->resp_hdrs = deep_table_copy(r->pool, mobj->header_out); - - return OK; - } -@@ -585,7 +601,7 @@ - * - The original response headers (for returning with a cached response) - * - The body of the message - */ -- mobj->req_hdrs = apr_table_copy(mobj->pool, r->headers_in); -+ mobj->req_hdrs = deep_table_copy(mobj->pool, r->headers_in); - - /* Precompute how much storage we need to hold the headers */ - headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out, -@@ -599,7 +615,7 @@ - } - - headers_out = apr_table_overlay(r->pool, headers_out, r->err_headers_out); -- mobj->header_out = apr_table_copy(mobj->pool, headers_out); -+ mobj->header_out = deep_table_copy(mobj->pool, headers_out); - - /* Init the info struct */ - obj->info.status = info->status; diff --git a/2.2/patches/82_all_CVE-2007-1863.patch b/2.2/patches/82_all_CVE-2007-1863.patch deleted file mode 100644 index 41c3ba7..0000000 --- a/2.2/patches/82_all_CVE-2007-1863.patch +++ /dev/null @@ -1,74 +0,0 @@ ---- httpd-2.2.3/modules/cache/cache_util.c.cve1863 -+++ httpd-2.2.3/modules/cache/cache_util.c -@@ -231,7 +231,8 @@ - age = ap_cache_current_age(info, age_c, r->request_time); - - /* extract s-maxage */ -- if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "s-maxage", &val)) { -+ if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "s-maxage", &val) -+ && val != NULL) { - smaxage = apr_atoi64(val); - } - else { -@@ -240,7 +241,8 @@ - - /* extract max-age from request */ - if (!conf->ignorecachecontrol -- && cc_req && ap_cache_liststr(r->pool, cc_req, "max-age", &val)) { -+ && cc_req && ap_cache_liststr(r->pool, cc_req, "max-age", &val) -+ && val != NULL) { - maxage_req = apr_atoi64(val); - } - else { -@@ -248,7 +250,8 @@ - } - - /* extract max-age from response */ -- if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "max-age", &val)) { -+ if (cc_cresp && ap_cache_liststr(r->pool, cc_cresp, "max-age", &val) -+ && val != NULL) { - maxage_cresp = apr_atoi64(val); - } - else { -@@ -270,7 +273,20 @@ - - /* extract max-stale */ - if (cc_req && ap_cache_liststr(r->pool, cc_req, "max-stale", &val)) { -- maxstale = apr_atoi64(val); -+ if(val != NULL) { -+ maxstale = apr_atoi64(val); -+ } -+ else { -+ /* -+ * If no value is assigned to max-stale, then the client is willing -+ * to accept a stale response of any age (RFC2616 14.9.3). We will -+ * set it to one year in this case as this situation is somewhat -+ * similar to a "never expires" Expires header (RFC2616 14.21) -+ * which is set to a date one year from the time the response is -+ * sent in this case. -+ */ -+ maxstale = APR_INT64_C(86400*365); -+ } - } - else { - maxstale = 0; -@@ -278,7 +294,8 @@ - - /* extract min-fresh */ - if (!conf->ignorecachecontrol -- && cc_req && ap_cache_liststr(r->pool, cc_req, "min-fresh", &val)) { -+ && cc_req && ap_cache_liststr(r->pool, cc_req, "min-fresh", &val) -+ && val != NULL) { - minfresh = apr_atoi64(val); - } - else { -@@ -407,6 +424,9 @@ - next - val_start); - } - } -+ else { -+ *val = NULL; -+ } - } - return 1; - } diff --git a/2.2/patches/83_all_CVE-2007-3304.patch b/2.2/patches/83_all_CVE-2007-3304.patch deleted file mode 100644 index 7337d6a..0000000 --- a/2.2/patches/83_all_CVE-2007-3304.patch +++ /dev/null @@ -1,261 +0,0 @@ -Index: httpd-2.2.4/server/mpm/prefork/prefork.c -=================================================================== ---- httpd-2.2.4.orig/server/mpm/prefork/prefork.c -+++ httpd-2.2.4/server/mpm/prefork/prefork.c -@@ -1127,7 +1127,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_p - for (index = 0; index < ap_daemons_limit; ++index) { - if (ap_scoreboard_image->servers[index][0].status != SERVER_DEAD) { - /* Ask each child to close its listeners. */ -- kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL); -+ ap_mpm_safe_kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL); - active_children++; - } - } -@@ -1165,12 +1165,10 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_p - - active_children = 0; - for (index = 0; index < ap_daemons_limit; ++index) { -- if (MPM_CHILD_PID(index) != 0) { -- if (kill(MPM_CHILD_PID(index), 0) == 0) { -- active_children = 1; -- /* Having just one child is enough to stay around */ -- break; -- } -+ if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) { -+ active_children = 1; -+ /* Having just one child is enough to stay around */ -+ break; - } - } - } while (!shutdown_pending && active_children && -@@ -1222,7 +1220,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_p - * piped loggers, etc. They almost certainly won't handle - * it gracefully. - */ -- kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL); -+ ap_mpm_safe_kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL); - } - } - } -Index: httpd-2.2.4/server/mpm/worker/worker.c -=================================================================== ---- httpd-2.2.4.orig/server/mpm/worker/worker.c -+++ httpd-2.2.4/server/mpm/worker/worker.c -@@ -1813,12 +1813,10 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_p - - active_children = 0; - for (index = 0; index < ap_daemons_limit; ++index) { -- if (MPM_CHILD_PID(index) != 0) { -- if (kill(MPM_CHILD_PID(index), 0) == 0) { -- active_children = 1; -- /* Having just one child is enough to stay around */ -- break; -- } -+ if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) { -+ active_children = 1; -+ /* Having just one child is enough to stay around */ -+ break; - } - } - } while (!shutdown_pending && active_children && -Index: httpd-2.2.4/server/mpm/experimental/event/event.c -=================================================================== ---- httpd-2.2.4.orig/server/mpm/experimental/event/event.c -+++ httpd-2.2.4/server/mpm/experimental/event/event.c -@@ -1998,12 +1998,10 @@ int ap_mpm_run(apr_pool_t * _pconf, apr_ - - active_children = 0; - for (index = 0; index < ap_daemons_limit; ++index) { -- if (MPM_CHILD_PID(index) != 0) { -- if (kill(MPM_CHILD_PID(index), 0) == 0) { -- active_children = 1; -- /* Having just one child is enough to stay around */ -- break; -- } -+ if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) { -+ active_children = 1; -+ /* Having just one child is enough to stay around */ -+ break; - } - } - } while (!shutdown_pending && active_children && -Index: httpd-2.2.4/server/mpm_common.c -=================================================================== ---- httpd-2.2.4.orig/server/mpm_common.c -+++ httpd-2.2.4/server/mpm_common.c -@@ -126,6 +126,11 @@ static int reclaim_one_pid(pid_t pid, ac - 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) { -@@ -305,6 +310,66 @@ void ap_relieve_child_processes(void) - 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) { -+#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; -+} - #endif /* AP_MPM_WANT_RECLAIM_CHILD_PROCESSES */ - - #ifdef AP_MPM_WANT_WAIT_OR_TIMEOUT -Index: httpd-2.2.4/include/mpm_common.h -=================================================================== ---- httpd-2.2.4.orig/include/mpm_common.h -+++ httpd-2.2.4/include/mpm_common.h -@@ -145,6 +145,19 @@ int ap_unregister_extra_mpm_process(pid_ - #endif - - /** -+ * Safely signal an MPM child process, if the process is in the -+ * current process group. Otherwise fail. -+ * @param pid the process id of a child process to signal -+ * @param sig the signal number to send -+ * @return APR_SUCCESS if signal is sent, otherwise an error as per kill(3); -+ * APR_EINVAL is returned if passed either an invalid (< 1) pid, or if -+ * the pid is not in the current process group -+ */ -+#ifdef AP_MPM_WANT_RECLAIM_CHILD_PROCESSES -+apr_status_t ap_mpm_safe_kill(pid_t pid, int sig); -+#endif -+ -+/** - * Determine if any child process has died. If no child process died, then - * this process sleeps for the amount of time specified by the MPM defined - * macro SCOREBOARD_MAINTENANCE_INTERVAL. -Index: httpd-2.2.4/include/ap_mmn.h -=================================================================== ---- httpd-2.2.4.orig/include/ap_mmn.h -+++ httpd-2.2.4/include/ap_mmn.h -@@ -113,6 +113,8 @@ - * 20051115.3 (2.2.3) Added server_scheme member to server_rec (minor) - * 20051115.4 (2.2.4) Added ap_get_server_banner() and - * ap_get_server_description() (minor) -+ * 20051115.5 (2.2.5) Added ap_mpm_safe_kill() (minor) -+ * - */ - - #define MODULE_MAGIC_COOKIE 0x41503232UL /* "AP22" */ -@@ -120,7 +122,7 @@ - #ifndef MODULE_MAGIC_NUMBER_MAJOR - #define MODULE_MAGIC_NUMBER_MAJOR 20051115 - #endif --#define MODULE_MAGIC_NUMBER_MINOR 4 /* 0...n */ -+#define MODULE_MAGIC_NUMBER_MINOR 5 /* 0...n */ - - /** - * Determine if the server's current MODULE_MAGIC_NUMBER is at least a -Index: httpd-2.2.4/configure.in -=================================================================== ---- httpd-2.2.4.orig/configure.in -+++ httpd-2.2.4/configure.in -@@ -392,6 +392,7 @@ initgroups \ - bindprocessor \ - prctl \ - timegm \ -+getpgid - ) - - dnl confirm that a void pointer is large enough to store a long integer -Index: httpd-2.2.4/server/mpm/experimental/itk/itk.c -=================================================================== ---- httpd-2.2.4.orig/server/mpm/experimental/itk/itk.c -+++ httpd-2.2.4/server/mpm/experimental/itk/itk.c -@@ -1199,7 +1199,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_p - for (index = 0; index < ap_daemons_limit; ++index) { - if (ap_scoreboard_image->servers[index][0].status != SERVER_DEAD) { - /* Ask each child to close its listeners. */ -- kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL); -+ ap_mpm_safe_kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL); - active_children++; - } - } -@@ -1237,12 +1237,10 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_p - - active_children = 0; - for (index = 0; index < ap_daemons_limit; ++index) { -- if (MPM_CHILD_PID(index) != 0) { -- if (kill(MPM_CHILD_PID(index), 0) == 0) { -- active_children = 1; -- /* Having just one child is enough to stay around */ -- break; -- } -+ if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) { -+ active_children = 1; -+ /* Having just one child is enough to stay around */ -+ break; - } - } - } while (!shutdown_pending && active_children && -@@ -1294,7 +1292,7 @@ int ap_mpm_run(apr_pool_t *_pconf, apr_p - * piped loggers, etc. They almost certainly won't handle - * it gracefully. - */ -- kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL); -+ ap_mpm_safe_kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL); - } - } - } -- cgit v1.2.3