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 #if AP_ENABLE_EXCEPTION_HOOK
61 APR_HOOK_LINK(fatal_exception)
62 APR_HOOK_LINK(monitor)
63 APR_HOOK_LINK(drop_privileges)
65 APR_HOOK_LINK(mpm_query)
66 APR_HOOK_LINK(mpm_get_child_pid)
67 APR_HOOK_LINK(mpm_note_child_killed)
68 APR_HOOK_LINK(mpm_register_timed_callback)
69 APR_HOOK_LINK(mpm_get_name)
71 AP_IMPLEMENT_HOOK_RUN_ALL(int, fatal_exception,
72 (ap_exception_info_t *ei), (ei), OK, DECLINED)
75 APR_HOOK_LINK(monitor)
76 APR_HOOK_LINK(drop_privileges)
78 APR_HOOK_LINK(mpm_query)
79 APR_HOOK_LINK(mpm_get_child_pid)
80 APR_HOOK_LINK(mpm_note_child_killed)
81 APR_HOOK_LINK(mpm_register_timed_callback)
82 APR_HOOK_LINK(mpm_get_name)
85 AP_IMPLEMENT_HOOK_RUN_ALL(int, monitor,
86 (apr_pool_t *p), (p), OK, DECLINED)
87 AP_IMPLEMENT_HOOK_RUN_ALL(int, drop_privileges,
88 (apr_pool_t * pchild, server_rec * s),
89 (pchild, s), OK, DECLINED)
90 AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm,
91 (apr_pool_t *pconf, apr_pool_t *plog, server_rec *s),
92 (pconf, plog, s), DECLINED)
93 AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_query,
94 (int query_code, int *result),
95 (query_code, result), APR_ENOTIMPL)
96 AP_IMPLEMENT_HOOK_RUN_FIRST(pid_t, mpm_get_child_pid,
99 AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_note_child_killed,
101 (childnum), APR_ENOTIMPL)
102 AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_timed_callback,
103 (apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton),
104 (t, cbfn, baton), APR_ENOTIMPL)
105 AP_IMPLEMENT_HOOK_RUN_FIRST(const char *, mpm_get_name,
109 /* number of calls to wait_or_timeout between writable probes */
110 #ifndef INTERVAL_OF_WRITABLE_PROBES
111 #define INTERVAL_OF_WRITABLE_PROBES 10
113 static int wait_or_timeout_counter;
115 void ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode, apr_proc_t *ret,
120 ++wait_or_timeout_counter;
121 if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
122 wait_or_timeout_counter = 0;
126 rv = apr_proc_wait_all_procs(ret, exitcode, status, APR_NOWAIT, p);
127 if (APR_STATUS_IS_EINTR(rv)) {
132 if (APR_STATUS_IS_CHILD_DONE(rv)) {
141 #if defined(TCP_NODELAY) && !defined(MPE)
142 void ap_sock_disable_nagle(apr_socket_t *s)
144 /* The Nagle algorithm says that we should delay sending partial
145 * packets in hopes of getting more data. We don't want to do
146 * this; we are not telnet. There are bad interactions between
147 * persistent connections and Nagle's algorithm that have very severe
148 * performance penalties. (Failing to disable Nagle is not much of a
149 * problem with simple HTTP.)
151 * In spite of these problems, failure here is not a shooting offense.
153 apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1);
155 if (status != APR_SUCCESS) {
156 ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf,
157 "apr_socket_opt_set: (TCP_NODELAY)");
163 AP_DECLARE(uid_t) ap_uname2id(const char *name)
168 return (atoi(&name[1]));
170 if (!(ent = getpwnam(name))) {
171 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
172 "%s: bad user name %s", ap_server_argv0, name);
176 return (ent->pw_uid);
181 AP_DECLARE(gid_t) ap_gname2id(const char *name)
186 return (atoi(&name[1]));
188 if (!(ent = getgrnam(name))) {
189 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
190 "%s: bad group name %s", ap_server_argv0, name);
194 return (ent->gr_gid);
198 #ifndef HAVE_INITGROUPS
199 int initgroups(const char *name, gid_t basegid)
201 #if defined(QNX) || defined(MPE) || defined(_OSD_POSIX) || defined(__TANDEM) || defined(WIN32) || defined(NETWARE)
202 /* QNX and MPE do not appear to support supplementary groups. */
205 gid_t groups[NGROUPS_MAX];
211 groups[index++] = basegid;
213 while (index < NGROUPS_MAX && ((g = getgrent()) != NULL)) {
214 if (g->gr_gid != basegid) {
217 for (names = g->gr_mem; *names != NULL; ++names) {
218 if (!strcmp(*names, name))
219 groups[index++] = g->gr_gid;
226 return setgroups(index, groups);
229 #endif /* def NEED_INITGROUPS */
231 /* standard mpm configuration handling */
232 const char *ap_pid_fname = NULL;
234 const char *ap_mpm_set_pidfile(cmd_parms *cmd, void *dummy,
237 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
242 if (cmd->server->is_virtual) {
243 return "PidFile directive not allowed in <VirtualHost>";
250 const char * ap_mpm_set_scoreboard(cmd_parms *cmd, void *dummy,
253 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
258 ap_scoreboard_fname = arg;
262 const char *ap_lock_fname = NULL;
264 const char *ap_mpm_set_lockfile(cmd_parms *cmd, void *dummy,
267 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
276 int ap_max_requests_per_child = 0;
278 const char *ap_mpm_set_max_requests(cmd_parms *cmd, void *dummy,
281 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
286 ap_max_requests_per_child = atoi(arg);
291 char ap_coredump_dir[MAX_STRING_LEN];
292 int ap_coredumpdir_configured;
294 const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy,
300 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
305 fname = ap_server_root_relative(cmd->pool, arg);
307 return apr_pstrcat(cmd->pool, "Invalid CoreDumpDirectory path ",
310 if ((rv = apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool)) != APR_SUCCESS) {
311 return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
312 " does not exist", NULL);
314 if (finfo.filetype != APR_DIR) {
315 return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
316 " is not a directory", NULL);
318 apr_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
319 ap_coredumpdir_configured = 1;
323 int ap_graceful_shutdown_timeout = 0;
325 const char * ap_mpm_set_graceful_shutdown(cmd_parms *cmd, void *dummy,
328 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
332 ap_graceful_shutdown_timeout = atoi(arg);
336 apr_lockmech_e ap_accept_lock_mech = APR_LOCK_DEFAULT;
338 const char *ap_mpm_set_accept_lock_mech(cmd_parms *cmd,
343 const char *lockfile;
344 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
349 rv = ap_parse_mutex(arg, cmd->server->process->pool,
350 &ap_accept_lock_mech, &lockfile);
352 if ((rv == APR_ENOTIMPL) || (rv == APR_ENOLOCK)) {
353 return apr_pstrcat(cmd->pool, "Invalid AcceptMutex argument ", arg,
354 " (" AP_AVAILABLE_MUTEXES_STRING ")", NULL);
355 } else if (rv == APR_BADARG) {
356 return apr_pstrcat(cmd->pool, "Invalid AcceptMutex filepath ",
360 if (lockfile && !ap_lock_fname)
361 ap_lock_fname = lockfile;
365 apr_uint32_t ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
367 const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy,
371 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
376 value = strtol(arg, NULL, 0);
377 if (value < 0 || errno == ERANGE)
378 return apr_pstrcat(cmd->pool, "Invalid MaxMemFree value: ",
381 ap_max_mem_free = (apr_uint32_t)value * 1024;
386 apr_size_t ap_thread_stacksize = 0; /* use system default */
388 const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy,
392 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
397 value = strtol(arg, NULL, 0);
398 if (value < 0 || errno == ERANGE)
399 return apr_pstrcat(cmd->pool, "Invalid ThreadStackSize value: ",
402 ap_thread_stacksize = (apr_size_t)value;
407 AP_DECLARE(int) ap_mpm_run(apr_pool_t *pconf, apr_pool_t *plog, server_rec *server_conf)
409 return ap_run_mpm(pconf, plog, server_conf);
412 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
414 return ap_run_mpm_query(query_code, result);
417 AP_DECLARE(pid_t) ap_mpm_get_child_pid(int childnum)
419 return ap_run_mpm_get_child_pid(childnum);
422 AP_DECLARE(apr_status_t) ap_mpm_note_child_killed(int childnum)
424 return ap_run_mpm_note_child_killed(childnum);
427 AP_DECLARE(apr_status_t) ap_mpm_register_timed_callback(apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton)
429 return ap_run_mpm_register_timed_callback(t, cbfn, baton);
432 AP_DECLARE(const char *)ap_show_mpm(void)
434 const char *name = ap_run_mpm_get_name();