#include "httpd.h"
#include "http_config.h"
+#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "mpm_common.h"
+#include "mod_core.h"
#include "ap_mpm.h"
#include "ap_listen.h"
#include "util_mutex.h"
#include <unistd.h>
#endif
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
+#define DEFAULT_HOOK_LINKS \
+ APR_HOOK_LINK(monitor) \
+ APR_HOOK_LINK(drop_privileges) \
+ APR_HOOK_LINK(mpm) \
+ APR_HOOK_LINK(mpm_query) \
+ APR_HOOK_LINK(mpm_register_timed_callback) \
+ APR_HOOK_LINK(mpm_get_name) \
+ APR_HOOK_LINK(end_generation) \
+ APR_HOOK_LINK(child_status) \
+ APR_HOOK_LINK(suspend_connection) \
+ APR_HOOK_LINK(resume_connection)
+
#if AP_ENABLE_EXCEPTION_HOOK
APR_HOOK_STRUCT(
APR_HOOK_LINK(fatal_exception)
- APR_HOOK_LINK(monitor)
- APR_HOOK_LINK(drop_privileges)
- APR_HOOK_LINK(mpm)
- APR_HOOK_LINK(mpm_query)
- APR_HOOK_LINK(mpm_get_child_pid)
- APR_HOOK_LINK(mpm_note_child_killed)
- APR_HOOK_LINK(mpm_register_timed_callback)
- APR_HOOK_LINK(mpm_get_name)
+ DEFAULT_HOOK_LINKS
)
AP_IMPLEMENT_HOOK_RUN_ALL(int, fatal_exception,
(ap_exception_info_t *ei), (ei), OK, DECLINED)
#else
APR_HOOK_STRUCT(
- APR_HOOK_LINK(monitor)
- APR_HOOK_LINK(drop_privileges)
- APR_HOOK_LINK(mpm)
- APR_HOOK_LINK(mpm_query)
- APR_HOOK_LINK(mpm_get_child_pid)
- APR_HOOK_LINK(mpm_note_child_killed)
- APR_HOOK_LINK(mpm_register_timed_callback)
- APR_HOOK_LINK(mpm_get_name)
+ DEFAULT_HOOK_LINKS
)
#endif
AP_IMPLEMENT_HOOK_RUN_ALL(int, monitor,
- (apr_pool_t *p), (p), OK, DECLINED)
+ (apr_pool_t *p, server_rec *s), (p, s), OK, DECLINED)
AP_IMPLEMENT_HOOK_RUN_ALL(int, drop_privileges,
(apr_pool_t * pchild, server_rec * s),
(pchild, s), OK, DECLINED)
AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm,
(apr_pool_t *pconf, apr_pool_t *plog, server_rec *s),
(pconf, plog, s), DECLINED)
-AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_query,
- (int query_code, int *result),
- (query_code, result), APR_ENOTIMPL)
-AP_IMPLEMENT_HOOK_RUN_FIRST(pid_t, mpm_get_child_pid,
- (int childnum),
- (childnum), 0)
-AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_note_child_killed,
- (int childnum),
- (childnum), APR_ENOTIMPL)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm_query,
+ (int query_code, int *result, apr_status_t *_rv),
+ (query_code, result, _rv), DECLINED)
AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_timed_callback,
(apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton),
(t, cbfn, baton), APR_ENOTIMPL)
+AP_IMPLEMENT_HOOK_VOID(end_generation,
+ (server_rec *s, ap_generation_t gen),
+ (s, gen))
+AP_IMPLEMENT_HOOK_VOID(child_status,
+ (server_rec *s, pid_t pid, ap_generation_t gen, int slot, mpm_child_status status),
+ (s,pid,gen,slot,status))
+AP_IMPLEMENT_HOOK_VOID(suspend_connection,
+ (conn_rec *c, request_rec *r),
+ (c, r))
+AP_IMPLEMENT_HOOK_VOID(resume_connection,
+ (conn_rec *c, request_rec *r),
+ (c, r))
+
+/* hooks with no args are implemented last, after disabling APR hook probes */
+#if defined(APR_HOOK_PROBES_ENABLED)
+#undef APR_HOOK_PROBES_ENABLED
+#undef APR_HOOK_PROBE_ENTRY
+#define APR_HOOK_PROBE_ENTRY(ud,ns,name,args)
+#undef APR_HOOK_PROBE_RETURN
+#define APR_HOOK_PROBE_RETURN(ud,ns,name,rv,args)
+#undef APR_HOOK_PROBE_INVOKE
+#define APR_HOOK_PROBE_INVOKE(ud,ns,name,src,args)
+#undef APR_HOOK_PROBE_COMPLETE
+#define APR_HOOK_PROBE_COMPLETE(ud,ns,name,src,rv,args)
+#undef APR_HOOK_INT_DCL_UD
+#define APR_HOOK_INT_DCL_UD
+#endif
AP_IMPLEMENT_HOOK_RUN_FIRST(const char *, mpm_get_name,
(void),
(), NULL)
+typedef struct mpm_gen_info_t {
+ APR_RING_ENTRY(mpm_gen_info_t) link;
+ int gen; /* which gen? */
+ int active; /* number of active processes */
+ int done; /* gen finished? (whether or not active processes) */
+} mpm_gen_info_t;
+
+APR_RING_HEAD(mpm_gen_info_head_t, mpm_gen_info_t);
+static struct mpm_gen_info_head_t *geninfo, *unused_geninfo;
+static int gen_head_init; /* yuck */
+
+/* variables representing config directives implemented here */
+AP_DECLARE_DATA const char *ap_pid_fname;
+AP_DECLARE_DATA int ap_max_requests_per_child;
+AP_DECLARE_DATA char ap_coredump_dir[MAX_STRING_LEN];
+AP_DECLARE_DATA int ap_coredumpdir_configured;
+AP_DECLARE_DATA int ap_graceful_shutdown_timeout;
+AP_DECLARE_DATA apr_uint32_t ap_max_mem_free;
+AP_DECLARE_DATA apr_size_t ap_thread_stacksize;
+
+#define ALLOCATOR_MAX_FREE_DEFAULT (2048*1024)
+
+/* Set defaults for config directives implemented here. This is
+ * called from core's pre-config hook, so MPMs which need to override
+ * one of these should run their pre-config hook after that of core.
+ */
+void mpm_common_pre_config(apr_pool_t *pconf)
+{
+ ap_pid_fname = DEFAULT_PIDLOG;
+ ap_max_requests_per_child = 0; /* unlimited */
+ apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+ ap_coredumpdir_configured = 0;
+ ap_graceful_shutdown_timeout = 0; /* unlimited */
+ ap_max_mem_free = ALLOCATOR_MAX_FREE_DEFAULT;
+ ap_thread_stacksize = 0; /* use system default */
+}
+
/* number of calls to wait_or_timeout between writable probes */
#ifndef INTERVAL_OF_WRITABLE_PROBES
#define INTERVAL_OF_WRITABLE_PROBES 10
#endif
static int wait_or_timeout_counter;
-void ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode, apr_proc_t *ret,
- apr_pool_t *p)
+AP_DECLARE(void) ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode,
+ apr_proc_t *ret, apr_pool_t *p,
+ server_rec *s)
{
apr_status_t rv;
++wait_or_timeout_counter;
if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
wait_or_timeout_counter = 0;
- ap_run_monitor(p);
+ ap_run_monitor(p, s);
}
rv = apr_proc_wait_all_procs(ret, exitcode, status, APR_NOWAIT, p);
return;
}
- apr_sleep(1000000);
+ apr_sleep(apr_time_from_sec(1));
ret->pid = -1;
- return;
}
#if defined(TCP_NODELAY)
apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1);
if (status != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf,
+ ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf, APLOGNO(00542)
"apr_socket_opt_set: (TCP_NODELAY)");
}
}
return (atoi(&name[1]));
if (!(ent = getpwnam(name))) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00543)
"%s: bad user name %s", ap_server_argv0, name);
exit(1);
}
return (atoi(&name[1]));
if (!(ent = getgrnam(name))) {
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00544)
"%s: bad group name %s", ap_server_argv0, name);
exit(1);
}
#ifndef HAVE_INITGROUPS
int initgroups(const char *name, gid_t basegid)
{
-#if defined(_OSD_POSIX) || defined(WIN32) || defined(NETWARE)
+#if defined(_OSD_POSIX) || defined(OS2) || defined(WIN32) || defined(NETWARE)
return 0;
#else
gid_t groups[NGROUPS_MAX];
return setgroups(index, groups);
#endif
}
-#endif /* def NEED_INITGROUPS */
+#endif /* def HAVE_INITGROUPS */
/* standard mpm configuration handling */
-const char *ap_pid_fname = NULL;
const char *ap_mpm_set_pidfile(cmd_parms *cmd, void *dummy,
const char *arg)
return NULL;
}
-const char * ap_mpm_set_scoreboard(cmd_parms *cmd, void *dummy,
- const char *arg)
+void ap_mpm_dump_pidfile(apr_pool_t *p, apr_file_t *out)
{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- ap_scoreboard_fname = arg;
- return NULL;
-}
-
-const char *ap_lock_fname = NULL;
-
-const char *ap_mpm_set_lockfile(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- ap_lock_fname = arg;
- return NULL;
+ apr_file_printf(out, "PidFile: \"%s\"\n",
+ ap_server_root_relative(p, ap_pid_fname));
}
-int ap_max_requests_per_child = 0;
-
const char *ap_mpm_set_max_requests(cmd_parms *cmd, void *dummy,
const char *arg)
{
return err;
}
+ if (!strcasecmp(cmd->cmd->name, "MaxRequestsPerChild")) {
+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(00545)
+ "MaxRequestsPerChild is deprecated, use "
+ "MaxConnectionsPerChild instead.");
+ }
+
ap_max_requests_per_child = atoi(arg);
return NULL;
}
-char ap_coredump_dir[MAX_STRING_LEN];
-int ap_coredumpdir_configured;
-
const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy,
const char *arg)
{
- apr_status_t rv;
apr_finfo_t finfo;
const char *fname;
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
return err;
}
- fname = ap_server_root_relative(cmd->pool, arg);
+ fname = ap_server_root_relative(cmd->temp_pool, arg);
if (!fname) {
return apr_pstrcat(cmd->pool, "Invalid CoreDumpDirectory path ",
arg, NULL);
}
- if ((rv = apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool)) != APR_SUCCESS) {
+ if (apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool) != APR_SUCCESS) {
return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
" does not exist", NULL);
}
return NULL;
}
-int ap_graceful_shutdown_timeout = 0;
-
-const char * ap_mpm_set_graceful_shutdown(cmd_parms *cmd, void *dummy,
- const char *arg)
+AP_DECLARE(const char *)ap_mpm_set_graceful_shutdown(cmd_parms *cmd,
+ void *dummy,
+ const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return NULL;
}
-apr_lockmech_e ap_accept_lock_mech = APR_LOCK_DEFAULT;
-
-const char *ap_mpm_set_accept_lock_mech(cmd_parms *cmd,
- void *dummy,
- const char *arg)
-{
- apr_status_t rv;
- const char *lockfile;
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- rv = ap_parse_mutex(arg, cmd->server->process->pool,
- &ap_accept_lock_mech, &lockfile);
-
- if ((rv == APR_ENOTIMPL) || (rv == APR_ENOLOCK)) {
- return apr_pstrcat(cmd->pool, "Invalid AcceptMutex argument ", arg,
- " (" AP_AVAILABLE_MUTEXES_STRING ")", NULL);
- } else if (rv == APR_BADARG) {
- return apr_pstrcat(cmd->pool, "Invalid AcceptMutex filepath ",
- arg, NULL);
- }
-
- if (lockfile && !ap_lock_fname)
- ap_lock_fname = lockfile;
- return NULL;
-}
-
-apr_uint32_t ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
-
const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy,
const char *arg)
{
return err;
}
- value = strtol(arg, NULL, 0);
+ errno = 0;
+ value = strtol(arg, NULL, 10);
if (value < 0 || errno == ERANGE)
return apr_pstrcat(cmd->pool, "Invalid MaxMemFree value: ",
arg, NULL);
return NULL;
}
-apr_size_t ap_thread_stacksize = 0; /* use system default */
-
const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy,
const char *arg)
{
return err;
}
- value = strtol(arg, NULL, 0);
+ errno = 0;
+ value = strtol(arg, NULL, 10);
if (value < 0 || errno == ERANGE)
return apr_pstrcat(cmd->pool, "Invalid ThreadStackSize value: ",
arg, NULL);
return NULL;
}
-AP_DECLARE(int) ap_mpm_run(apr_pool_t *pconf, apr_pool_t *plog, server_rec *server_conf)
+AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
{
- return ap_run_mpm(pconf, plog, server_conf);
+ apr_status_t rv;
+
+ if (ap_run_mpm_query(query_code, result, &rv) == DECLINED) {
+ rv = APR_EGENERAL;
+ }
+
+ return rv;
}
-AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
+static void end_gen(mpm_gen_info_t *gi)
{
- return ap_run_mpm_query(query_code, result);
+ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf,
+ "end of generation %d", gi->gen);
+ ap_run_end_generation(ap_server_conf, gi->gen);
+ APR_RING_REMOVE(gi, link);
+ APR_RING_INSERT_HEAD(unused_geninfo, gi, mpm_gen_info_t, link);
}
-AP_DECLARE(pid_t) ap_mpm_get_child_pid(int childnum)
+apr_status_t ap_mpm_end_gen_helper(void *unused) /* cleanup on pconf */
{
- return ap_run_mpm_get_child_pid(childnum);
+ int gen = ap_config_generation - 1; /* differs from MPM generation */
+ mpm_gen_info_t *cur;
+
+ if (geninfo == NULL) {
+ /* initial pconf teardown, MPM hasn't run */
+ return APR_SUCCESS;
+ }
+
+ cur = APR_RING_FIRST(geninfo);
+ while (cur != APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link) &&
+ cur->gen != gen) {
+ cur = APR_RING_NEXT(cur, link);
+ }
+
+ if (cur == APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link)) {
+ /* last child of generation already exited */
+ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf,
+ "no record of generation %d", gen);
+ }
+ else {
+ cur->done = 1;
+ if (cur->active == 0) {
+ end_gen(cur);
+ }
+ }
+
+ return APR_SUCCESS;
}
-AP_DECLARE(apr_status_t) ap_mpm_note_child_killed(int childnum)
+/* core's child-status hook
+ * tracks number of remaining children per generation and
+ * runs the end-generation hook when the last child of
+ * a generation exits
+ */
+void ap_core_child_status(server_rec *s, pid_t pid,
+ ap_generation_t gen, int slot,
+ mpm_child_status status)
{
- return ap_run_mpm_note_child_killed(childnum);
+ mpm_gen_info_t *cur;
+ const char *status_msg = "unknown status";
+
+ if (!gen_head_init) { /* where to run this? */
+ gen_head_init = 1;
+ geninfo = apr_pcalloc(s->process->pool, sizeof *geninfo);
+ unused_geninfo = apr_pcalloc(s->process->pool, sizeof *unused_geninfo);
+ APR_RING_INIT(geninfo, mpm_gen_info_t, link);
+ APR_RING_INIT(unused_geninfo, mpm_gen_info_t, link);
+ }
+
+ cur = APR_RING_FIRST(geninfo);
+ while (cur != APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link) &&
+ cur->gen != gen) {
+ cur = APR_RING_NEXT(cur, link);
+ }
+
+ switch(status) {
+ case MPM_CHILD_STARTED:
+ status_msg = "started";
+ if (cur == APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link)) {
+ /* first child for this generation */
+ if (!APR_RING_EMPTY(unused_geninfo, mpm_gen_info_t, link)) {
+ cur = APR_RING_FIRST(unused_geninfo);
+ APR_RING_REMOVE(cur, link);
+ cur->active = cur->done = 0;
+ }
+ else {
+ cur = apr_pcalloc(s->process->pool, sizeof *cur);
+ }
+ cur->gen = gen;
+ APR_RING_ELEM_INIT(cur, link);
+ APR_RING_INSERT_HEAD(geninfo, cur, mpm_gen_info_t, link);
+ }
+ ap_random_parent_after_fork();
+ ++cur->active;
+ break;
+ case MPM_CHILD_EXITED:
+ status_msg = "exited";
+ if (cur == APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00546)
+ "no record of generation %d of exiting child %" APR_PID_T_FMT,
+ gen, pid);
+ }
+ else {
+ --cur->active;
+ if (!cur->active && cur->done) { /* no children, server has stopped/restarted */
+ end_gen(cur);
+ }
+ }
+ break;
+ case MPM_CHILD_LOST_SLOT:
+ status_msg = "lost slot";
+ /* we don't track by slot, so it doesn't matter */
+ break;
+ }
+ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s,
+ "mpm child %" APR_PID_T_FMT " (gen %d/slot %d) %s",
+ pid, gen, slot, status_msg);
}
AP_DECLARE(apr_status_t) ap_mpm_register_timed_callback(apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton)
return name;
}
+
+AP_DECLARE(const char *)ap_check_mpm(void)
+{
+ static const char *last_mpm_name = NULL;
+
+ if (!_hooks.link_mpm || _hooks.link_mpm->nelts == 0)
+ return "No MPM loaded.";
+ else if (_hooks.link_mpm->nelts > 1)
+ return "More than one MPM loaded.";
+
+ if (last_mpm_name) {
+ if (strcmp(last_mpm_name, ap_show_mpm())) {
+ return "The MPM cannot be changed during restart.";
+ }
+ }
+ else {
+ last_mpm_name = apr_pstrdup(ap_pglobal, ap_show_mpm());
+ }
+
+ return NULL;
+}