1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* The purpose of this file is to store the code that MOST mpm's will need
18 * this does not mean a function only goes into this file if every MPM needs
19 * it. It means that if a function is needed by more than one MPM, and
20 * future maintenance would be served by making the code common, then the
21 * function belongs here.
23 * This is going in src/main because it is not platform specific, it is
24 * specific to multi-process servers, but NOT to Unix. Which is why it
25 * does not belong in src/os/unix
29 #include "apr_thread_proc.h"
30 #include "apr_signal.h"
31 #include "apr_strings.h"
32 #define APR_WANT_STRFUNC
34 #include "apr_getopt.h"
35 #include "apr_optional.h"
36 #include "apr_allocator.h"
39 #include "http_config.h"
41 #include "http_main.h"
42 #include "mpm_common.h"
44 #include "ap_listen.h"
45 #include "util_mutex.h"
47 #include "scoreboard.h"
59 APLOG_USE_MODULE(core);
61 #if AP_ENABLE_EXCEPTION_HOOK
63 APR_HOOK_LINK(fatal_exception)
64 APR_HOOK_LINK(monitor)
65 APR_HOOK_LINK(drop_privileges)
67 APR_HOOK_LINK(mpm_query)
68 APR_HOOK_LINK(mpm_register_timed_callback)
69 APR_HOOK_LINK(mpm_get_name)
70 APR_HOOK_LINK(end_generation)
71 APR_HOOK_LINK(child_status)
73 AP_IMPLEMENT_HOOK_RUN_ALL(int, fatal_exception,
74 (ap_exception_info_t *ei), (ei), OK, DECLINED)
77 APR_HOOK_LINK(monitor)
78 APR_HOOK_LINK(drop_privileges)
80 APR_HOOK_LINK(mpm_query)
81 APR_HOOK_LINK(mpm_register_timed_callback)
82 APR_HOOK_LINK(mpm_get_name)
83 APR_HOOK_LINK(end_generation)
84 APR_HOOK_LINK(child_status)
87 AP_IMPLEMENT_HOOK_RUN_ALL(int, monitor,
88 (apr_pool_t *p, server_rec *s), (p, s), OK, DECLINED)
89 AP_IMPLEMENT_HOOK_RUN_ALL(int, drop_privileges,
90 (apr_pool_t * pchild, server_rec * s),
91 (pchild, s), OK, DECLINED)
92 AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm,
93 (apr_pool_t *pconf, apr_pool_t *plog, server_rec *s),
94 (pconf, plog, s), DECLINED)
95 AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm_query,
96 (int query_code, int *result, apr_status_t *_rv),
97 (query_code, result, _rv), DECLINED)
98 AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_timed_callback,
99 (apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton),
100 (t, cbfn, baton), APR_ENOTIMPL)
101 AP_IMPLEMENT_HOOK_RUN_FIRST(const char *, mpm_get_name,
104 AP_IMPLEMENT_HOOK_VOID(end_generation,
105 (server_rec *s, ap_generation_t gen),
107 AP_IMPLEMENT_HOOK_VOID(child_status,
108 (server_rec *s, pid_t pid, ap_generation_t gen, int slot, mpm_child_status status),
109 (s,pid,gen,slot,status))
111 typedef struct mpm_gen_info_t {
112 APR_RING_ENTRY(mpm_gen_info_t) link;
113 int gen; /* which gen? */
114 int active; /* number of active processes */
117 APR_RING_HEAD(mpm_gen_info_head_t, mpm_gen_info_t);
118 static struct mpm_gen_info_head_t geninfo, unused_geninfo;
119 static int gen_head_init; /* yuck */
121 /* variables representing config directives implemented here */
122 const char *ap_pid_fname;
123 int ap_max_requests_per_child;
124 char ap_coredump_dir[MAX_STRING_LEN];
125 int ap_coredumpdir_configured;
126 int ap_graceful_shutdown_timeout;
127 apr_uint32_t ap_max_mem_free;
128 apr_size_t ap_thread_stacksize;
130 /* Set defaults for config directives implemented here. This is
131 * called from core's pre-config hook, so MPMs which need to override
132 * one of these should run their pre-config hook after that of core.
134 void mpm_common_pre_config(apr_pool_t *pconf)
136 ap_pid_fname = DEFAULT_PIDLOG;
137 ap_max_requests_per_child = 0; /* unlimited */
138 apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
139 ap_coredumpdir_configured = 0;
140 ap_graceful_shutdown_timeout = 0; /* unlimited */
141 ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
142 ap_thread_stacksize = 0; /* use system default */
145 /* number of calls to wait_or_timeout between writable probes */
146 #ifndef INTERVAL_OF_WRITABLE_PROBES
147 #define INTERVAL_OF_WRITABLE_PROBES 10
149 static int wait_or_timeout_counter;
151 void ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode, apr_proc_t *ret,
152 apr_pool_t *p, server_rec *s)
156 ++wait_or_timeout_counter;
157 if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
158 wait_or_timeout_counter = 0;
159 ap_run_monitor(p, s);
162 rv = apr_proc_wait_all_procs(ret, exitcode, status, APR_NOWAIT, p);
163 if (APR_STATUS_IS_EINTR(rv)) {
168 if (APR_STATUS_IS_CHILD_DONE(rv)) {
172 apr_sleep(apr_time_from_sec(1));
177 #if defined(TCP_NODELAY)
178 void ap_sock_disable_nagle(apr_socket_t *s)
180 /* The Nagle algorithm says that we should delay sending partial
181 * packets in hopes of getting more data. We don't want to do
182 * this; we are not telnet. There are bad interactions between
183 * persistent connections and Nagle's algorithm that have very severe
184 * performance penalties. (Failing to disable Nagle is not much of a
185 * problem with simple HTTP.)
187 * In spite of these problems, failure here is not a shooting offense.
189 apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1);
191 if (status != APR_SUCCESS) {
192 ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf,
193 "apr_socket_opt_set: (TCP_NODELAY)");
199 AP_DECLARE(uid_t) ap_uname2id(const char *name)
204 return (atoi(&name[1]));
206 if (!(ent = getpwnam(name))) {
207 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
208 "%s: bad user name %s", ap_server_argv0, name);
212 return (ent->pw_uid);
217 AP_DECLARE(gid_t) ap_gname2id(const char *name)
222 return (atoi(&name[1]));
224 if (!(ent = getgrnam(name))) {
225 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
226 "%s: bad group name %s", ap_server_argv0, name);
230 return (ent->gr_gid);
234 #ifndef HAVE_INITGROUPS
235 int initgroups(const char *name, gid_t basegid)
237 #if defined(_OSD_POSIX) || defined(OS2) || defined(WIN32) || defined(NETWARE)
240 gid_t groups[NGROUPS_MAX];
246 groups[index++] = basegid;
248 while (index < NGROUPS_MAX && ((g = getgrent()) != NULL)) {
249 if (g->gr_gid != basegid) {
252 for (names = g->gr_mem; *names != NULL; ++names) {
253 if (!strcmp(*names, name))
254 groups[index++] = g->gr_gid;
261 return setgroups(index, groups);
264 #endif /* def HAVE_INITGROUPS */
266 /* standard mpm configuration handling */
268 const char *ap_mpm_set_pidfile(cmd_parms *cmd, void *dummy,
271 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
276 if (cmd->server->is_virtual) {
277 return "PidFile directive not allowed in <VirtualHost>";
284 const char *ap_mpm_set_max_requests(cmd_parms *cmd, void *dummy,
287 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
292 if (!strcasecmp(cmd->cmd->name, "MaxRequestsPerChild")) {
293 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
294 "MaxRequestsPerChild is deprecated, use "
295 "MaxConnectionsPerChild instead.");
298 ap_max_requests_per_child = atoi(arg);
303 const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy,
308 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
313 fname = ap_server_root_relative(cmd->pool, arg);
315 return apr_pstrcat(cmd->pool, "Invalid CoreDumpDirectory path ",
318 if (apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool) != APR_SUCCESS) {
319 return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
320 " does not exist", NULL);
322 if (finfo.filetype != APR_DIR) {
323 return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
324 " is not a directory", NULL);
326 apr_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
327 ap_coredumpdir_configured = 1;
331 const char * ap_mpm_set_graceful_shutdown(cmd_parms *cmd, void *dummy,
334 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
338 ap_graceful_shutdown_timeout = atoi(arg);
342 const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy,
346 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
351 value = strtol(arg, NULL, 0);
352 if (value < 0 || errno == ERANGE)
353 return apr_pstrcat(cmd->pool, "Invalid MaxMemFree value: ",
356 ap_max_mem_free = (apr_uint32_t)value * 1024;
361 const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy,
365 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
370 value = strtol(arg, NULL, 0);
371 if (value < 0 || errno == ERANGE)
372 return apr_pstrcat(cmd->pool, "Invalid ThreadStackSize value: ",
375 ap_thread_stacksize = (apr_size_t)value;
380 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
384 if (ap_run_mpm_query(query_code, result, &rv) == DECLINED) {
391 /* core's child-status hook
392 * tracks number of remaining children per generation and
393 * runs the end-generation hook when a generation finishes
395 void ap_core_child_status(server_rec *s, pid_t pid,
396 ap_generation_t gen, int slot,
397 mpm_child_status status)
400 const char *status_msg = "unknown status";
402 if (!gen_head_init) { /* where to run this? */
404 APR_RING_INIT(&geninfo, mpm_gen_info_t, link);
405 APR_RING_INIT(&unused_geninfo, mpm_gen_info_t, link);
408 cur = APR_RING_FIRST(&geninfo);
409 while (cur != APR_RING_SENTINEL(&geninfo, mpm_gen_info_t, link) &&
411 cur = APR_RING_NEXT(cur, link);
415 case MPM_CHILD_STARTED:
416 status_msg = "started";
417 if (cur == APR_RING_SENTINEL(&geninfo, mpm_gen_info_t, link)) {
418 /* first child for this generation */
419 if (!APR_RING_EMPTY(&unused_geninfo, mpm_gen_info_t, link)) {
420 cur = APR_RING_FIRST(&unused_geninfo);
421 APR_RING_REMOVE(cur, link);
424 cur = apr_pcalloc(s->process->pool, sizeof *cur);
427 APR_RING_ELEM_INIT(cur, link);
428 APR_RING_INSERT_HEAD(&geninfo, cur, mpm_gen_info_t, link);
432 case MPM_CHILD_EXITED:
433 status_msg = "exited";
434 if (cur == APR_RING_SENTINEL(&geninfo, mpm_gen_info_t, link)) {
435 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
436 "no record of generation %d of exiting child %" APR_PID_T_FMT,
442 ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf,
443 "end of generation %d", gen);
444 ap_run_end_generation(ap_server_conf, gen);
445 APR_RING_REMOVE(cur, link);
446 APR_RING_INSERT_HEAD(&unused_geninfo, cur, mpm_gen_info_t, link);
450 case MPM_CHILD_LOST_SLOT:
451 status_msg = "lost slot";
452 /* we don't track by slot, so it doesn't matter */
455 ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s,
456 "mpm child %" APR_PID_T_FMT " (gen %d/slot %d) %s",
457 pid, gen, slot, status_msg);
460 AP_DECLARE(apr_status_t) ap_mpm_register_timed_callback(apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton)
462 return ap_run_mpm_register_timed_callback(t, cbfn, baton);
465 AP_DECLARE(const char *)ap_show_mpm(void)
467 const char *name = ap_run_mpm_get_name();
476 AP_DECLARE(const char *)ap_check_mpm(void)
478 static const char *last_mpm_name = NULL;
480 if (!_hooks.link_mpm || _hooks.link_mpm->nelts == 0)
481 return "No MPM loaded.";
482 else if (_hooks.link_mpm->nelts > 1)
483 return "More than one MPM loaded.";
486 if (strcmp(last_mpm_name, ap_show_mpm())) {
487 return "The MPM cannot be changed during restart.";
491 last_mpm_name = apr_pstrdup(ap_pglobal, ap_show_mpm());