/src/httpd/server/mpm_common.c
Line | Count | Source (jump to first uncovered line) |
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 |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
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. |
15 | | */ |
16 | | |
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. |
22 | | * |
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 |
26 | | */ |
27 | | |
28 | | #include "apr.h" |
29 | | #include "apr_thread_proc.h" |
30 | | #include "apr_signal.h" |
31 | | #include "apr_strings.h" |
32 | | #define APR_WANT_STRFUNC |
33 | | #include "apr_want.h" |
34 | | #include "apr_getopt.h" |
35 | | #include "apr_optional.h" |
36 | | #include "apr_allocator.h" |
37 | | |
38 | | #include "httpd.h" |
39 | | #include "http_config.h" |
40 | | #include "http_core.h" |
41 | | #include "http_log.h" |
42 | | #include "http_main.h" |
43 | | #include "mpm_common.h" |
44 | | #include "mod_core.h" |
45 | | #include "ap_mpm.h" |
46 | | #include "ap_listen.h" |
47 | | #include "util_mutex.h" |
48 | | |
49 | | #include "scoreboard.h" |
50 | | |
51 | | #ifdef HAVE_PWD_H |
52 | | #include <pwd.h> |
53 | | #endif |
54 | | #ifdef HAVE_GRP_H |
55 | | #include <grp.h> |
56 | | #endif |
57 | | #if APR_HAVE_UNISTD_H |
58 | | #include <unistd.h> |
59 | | #endif |
60 | | |
61 | | /* we know core's module_index is 0 */ |
62 | | #undef APLOG_MODULE_INDEX |
63 | | #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX |
64 | | |
65 | | #define DEFAULT_HOOK_LINKS \ |
66 | | APR_HOOK_LINK(monitor) \ |
67 | | APR_HOOK_LINK(drop_privileges) \ |
68 | | APR_HOOK_LINK(mpm) \ |
69 | | APR_HOOK_LINK(mpm_query) \ |
70 | | APR_HOOK_LINK(mpm_register_timed_callback) \ |
71 | | APR_HOOK_LINK(mpm_register_poll_callback) \ |
72 | | APR_HOOK_LINK(mpm_register_poll_callback_timeout) \ |
73 | | APR_HOOK_LINK(mpm_get_name) \ |
74 | | APR_HOOK_LINK(mpm_resume_suspended) \ |
75 | | APR_HOOK_LINK(end_generation) \ |
76 | | APR_HOOK_LINK(child_status) \ |
77 | | APR_HOOK_LINK(output_pending) \ |
78 | | APR_HOOK_LINK(input_pending) \ |
79 | | APR_HOOK_LINK(suspend_connection) \ |
80 | | APR_HOOK_LINK(resume_connection) \ |
81 | | APR_HOOK_LINK(child_stopping) \ |
82 | | APR_HOOK_LINK(child_stopped) |
83 | | |
84 | | #if AP_ENABLE_EXCEPTION_HOOK |
85 | | APR_HOOK_STRUCT( |
86 | | APR_HOOK_LINK(fatal_exception) |
87 | | DEFAULT_HOOK_LINKS |
88 | | ) |
89 | | AP_IMPLEMENT_HOOK_RUN_ALL(int, fatal_exception, |
90 | | (ap_exception_info_t *ei), (ei), OK, DECLINED) |
91 | | #else |
92 | | APR_HOOK_STRUCT( |
93 | | DEFAULT_HOOK_LINKS |
94 | | ) |
95 | | #endif |
96 | | AP_IMPLEMENT_HOOK_RUN_ALL(int, monitor, |
97 | | (apr_pool_t *p, server_rec *s), (p, s), OK, DECLINED) |
98 | | AP_IMPLEMENT_HOOK_RUN_ALL(int, drop_privileges, |
99 | | (apr_pool_t * pchild, server_rec * s), |
100 | | (pchild, s), OK, DECLINED) |
101 | | |
102 | | AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm, |
103 | | (apr_pool_t *pconf, apr_pool_t *plog, server_rec *s), |
104 | | (pconf, plog, s), DECLINED) |
105 | | AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm_query, |
106 | | (int query_code, int *result, apr_status_t *_rv), |
107 | | (query_code, result, _rv), DECLINED) |
108 | | AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_timed_callback, |
109 | | (apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton), |
110 | | (t, cbfn, baton), APR_ENOTIMPL) |
111 | | AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_resume_suspended, |
112 | | (conn_rec *c), |
113 | | (c), APR_ENOTIMPL) |
114 | | AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_poll_callback, |
115 | | (apr_pool_t *p, const apr_array_header_t *pds, |
116 | | ap_mpm_callback_fn_t *cbfn, void *baton), |
117 | | (p, pds, cbfn, baton), APR_ENOTIMPL) |
118 | | AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_poll_callback_timeout, |
119 | | (apr_pool_t *p, const apr_array_header_t *pds, |
120 | | ap_mpm_callback_fn_t *cbfn, |
121 | | ap_mpm_callback_fn_t *tofn, |
122 | | void *baton, apr_time_t timeout), |
123 | | (p, pds, cbfn, tofn, baton, timeout), APR_ENOTIMPL) |
124 | | AP_IMPLEMENT_HOOK_RUN_FIRST(int, output_pending, |
125 | | (conn_rec *c), (c), DECLINED) |
126 | | AP_IMPLEMENT_HOOK_RUN_FIRST(int, input_pending, |
127 | | (conn_rec *c), (c), DECLINED) |
128 | | |
129 | | AP_IMPLEMENT_HOOK_VOID(end_generation, |
130 | | (server_rec *s, ap_generation_t gen), |
131 | | (s, gen)) |
132 | | AP_IMPLEMENT_HOOK_VOID(child_status, |
133 | | (server_rec *s, pid_t pid, ap_generation_t gen, int slot, mpm_child_status status), |
134 | | (s,pid,gen,slot,status)) |
135 | | AP_IMPLEMENT_HOOK_VOID(suspend_connection, |
136 | | (conn_rec *c, request_rec *r), |
137 | | (c, r)) |
138 | | AP_IMPLEMENT_HOOK_VOID(resume_connection, |
139 | | (conn_rec *c, request_rec *r), |
140 | | (c, r)) |
141 | | AP_IMPLEMENT_HOOK_VOID(child_stopping, |
142 | | (apr_pool_t *pchild, int graceful), |
143 | | (pchild, graceful)) |
144 | | AP_IMPLEMENT_HOOK_VOID(child_stopped, |
145 | | (apr_pool_t *pchild, int graceful), |
146 | | (pchild, graceful)) |
147 | | |
148 | | /* hooks with no args are implemented last, after disabling APR hook probes */ |
149 | | #if defined(APR_HOOK_PROBES_ENABLED) |
150 | | #undef APR_HOOK_PROBES_ENABLED |
151 | | #undef APR_HOOK_PROBE_ENTRY |
152 | | #define APR_HOOK_PROBE_ENTRY(ud,ns,name,args) |
153 | | #undef APR_HOOK_PROBE_RETURN |
154 | | #define APR_HOOK_PROBE_RETURN(ud,ns,name,rv,args) |
155 | | #undef APR_HOOK_PROBE_INVOKE |
156 | | #define APR_HOOK_PROBE_INVOKE(ud,ns,name,src,args) |
157 | | #undef APR_HOOK_PROBE_COMPLETE |
158 | | #define APR_HOOK_PROBE_COMPLETE(ud,ns,name,src,rv,args) |
159 | | #undef APR_HOOK_INT_DCL_UD |
160 | | #define APR_HOOK_INT_DCL_UD |
161 | | #endif |
162 | | AP_IMPLEMENT_HOOK_RUN_FIRST(const char *, mpm_get_name, |
163 | | (void), |
164 | | (), NULL) |
165 | | |
166 | | typedef struct mpm_gen_info_t { |
167 | | APR_RING_ENTRY(mpm_gen_info_t) link; |
168 | | int gen; /* which gen? */ |
169 | | int active; /* number of active processes */ |
170 | | int done; /* gen finished? (whether or not active processes) */ |
171 | | } mpm_gen_info_t; |
172 | | |
173 | | APR_RING_HEAD(mpm_gen_info_head_t, mpm_gen_info_t); |
174 | | static struct mpm_gen_info_head_t *geninfo, *unused_geninfo; |
175 | | static int gen_head_init; /* yuck */ |
176 | | |
177 | | /* variables representing config directives implemented here */ |
178 | | AP_DECLARE_DATA const char *ap_pid_fname; |
179 | | AP_DECLARE_DATA int ap_max_requests_per_child; |
180 | | AP_DECLARE_DATA char ap_coredump_dir[MAX_STRING_LEN]; |
181 | | AP_DECLARE_DATA int ap_coredumpdir_configured; |
182 | | AP_DECLARE_DATA int ap_graceful_shutdown_timeout; |
183 | | AP_DECLARE_DATA apr_size_t ap_thread_stacksize; |
184 | | |
185 | 0 | #define ALLOCATOR_MAX_FREE_DEFAULT (2048*1024) |
186 | | AP_DECLARE_DATA apr_uint32_t ap_max_mem_free = ALLOCATOR_MAX_FREE_DEFAULT; |
187 | | |
188 | | /* Set defaults for config directives implemented here. This is |
189 | | * called from core's pre-config hook, so MPMs which need to override |
190 | | * one of these should run their pre-config hook after that of core. |
191 | | */ |
192 | | void mpm_common_pre_config(apr_pool_t *pconf) |
193 | 0 | { |
194 | 0 | ap_pid_fname = DEFAULT_PIDLOG; |
195 | 0 | ap_max_requests_per_child = 0; /* unlimited */ |
196 | 0 | apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); |
197 | 0 | ap_coredumpdir_configured = 0; |
198 | 0 | ap_graceful_shutdown_timeout = 0; /* unlimited */ |
199 | 0 | ap_max_mem_free = ALLOCATOR_MAX_FREE_DEFAULT; |
200 | 0 | ap_thread_stacksize = 0; /* use system default */ |
201 | 0 | } |
202 | | |
203 | | /* number of calls to wait_or_timeout between writable probes */ |
204 | | #ifndef INTERVAL_OF_WRITABLE_PROBES |
205 | 0 | #define INTERVAL_OF_WRITABLE_PROBES 10 |
206 | | #endif |
207 | | static int wait_or_timeout_counter; |
208 | | |
209 | | AP_DECLARE(void) ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode, |
210 | | apr_proc_t *ret, apr_pool_t *p, |
211 | | server_rec *s) |
212 | 0 | { |
213 | 0 | apr_status_t rv; |
214 | |
|
215 | 0 | ++wait_or_timeout_counter; |
216 | 0 | if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) { |
217 | 0 | wait_or_timeout_counter = 0; |
218 | 0 | ap_run_monitor(p, s); |
219 | 0 | } |
220 | |
|
221 | 0 | rv = apr_proc_wait_all_procs(ret, exitcode, status, APR_NOWAIT, p); |
222 | 0 | ap_update_global_status(); |
223 | |
|
224 | 0 | if (APR_STATUS_IS_EINTR(rv)) { |
225 | 0 | ret->pid = -1; |
226 | 0 | return; |
227 | 0 | } |
228 | | |
229 | 0 | if (APR_STATUS_IS_CHILD_DONE(rv)) { |
230 | 0 | return; |
231 | 0 | } |
232 | | |
233 | 0 | apr_sleep(apr_time_from_sec(1)); |
234 | 0 | ret->pid = -1; |
235 | 0 | } |
236 | | |
237 | | #if defined(TCP_NODELAY) |
238 | | void ap_sock_disable_nagle(apr_socket_t *s) |
239 | 0 | { |
240 | | /* The Nagle algorithm says that we should delay sending partial |
241 | | * packets in hopes of getting more data. We don't want to do |
242 | | * this; we are not telnet. There are bad interactions between |
243 | | * persistent connections and Nagle's algorithm that have very severe |
244 | | * performance penalties. (Failing to disable Nagle is not much of a |
245 | | * problem with simple HTTP.) |
246 | | * |
247 | | * In spite of these problems, failure here is not a shooting offense. |
248 | | */ |
249 | 0 | apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1); |
250 | |
|
251 | 0 | if (status != APR_SUCCESS) { |
252 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf, APLOGNO(00542) |
253 | 0 | "apr_socket_opt_set: (TCP_NODELAY)"); |
254 | 0 | } |
255 | 0 | } |
256 | | #endif |
257 | | |
258 | | #ifdef HAVE_GETPWNAM |
259 | | AP_DECLARE(uid_t) ap_uname2id(const char *name) |
260 | 0 | { |
261 | 0 | struct passwd *ent; |
262 | |
|
263 | 0 | if (name[0] == '#') |
264 | 0 | return (atoi(&name[1])); |
265 | | |
266 | 0 | if (!(ent = getpwnam(name))) { |
267 | 0 | ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00543) |
268 | 0 | "%s: bad user name %s", ap_server_argv0, name); |
269 | 0 | exit(1); |
270 | 0 | } |
271 | | |
272 | 0 | return (ent->pw_uid); |
273 | 0 | } |
274 | | #endif |
275 | | |
276 | | #ifdef HAVE_GETGRNAM |
277 | | AP_DECLARE(gid_t) ap_gname2id(const char *name) |
278 | 0 | { |
279 | 0 | struct group *ent; |
280 | |
|
281 | 0 | if (name[0] == '#') |
282 | 0 | return (atoi(&name[1])); |
283 | | |
284 | 0 | if (!(ent = getgrnam(name))) { |
285 | 0 | ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00544) |
286 | 0 | "%s: bad group name %s", ap_server_argv0, name); |
287 | 0 | exit(1); |
288 | 0 | } |
289 | | |
290 | 0 | return (ent->gr_gid); |
291 | 0 | } |
292 | | #endif |
293 | | |
294 | | #ifndef HAVE_INITGROUPS |
295 | | int initgroups(const char *name, gid_t basegid) |
296 | | { |
297 | | #if defined(_OSD_POSIX) || defined(OS2) || defined(WIN32) || defined(NETWARE) |
298 | | return 0; |
299 | | #else |
300 | | gid_t groups[NGROUPS_MAX]; |
301 | | struct group *g; |
302 | | int index = 0; |
303 | | |
304 | | setgrent(); |
305 | | |
306 | | groups[index++] = basegid; |
307 | | |
308 | | while (index < NGROUPS_MAX && ((g = getgrent()) != NULL)) { |
309 | | if (g->gr_gid != basegid) { |
310 | | char **names; |
311 | | |
312 | | for (names = g->gr_mem; *names != NULL; ++names) { |
313 | | if (!strcmp(*names, name)) |
314 | | groups[index++] = g->gr_gid; |
315 | | } |
316 | | } |
317 | | } |
318 | | |
319 | | endgrent(); |
320 | | |
321 | | return setgroups(index, groups); |
322 | | #endif |
323 | | } |
324 | | #endif /* def HAVE_INITGROUPS */ |
325 | | |
326 | | /* standard mpm configuration handling */ |
327 | | |
328 | | const char *ap_mpm_set_pidfile(cmd_parms *cmd, void *dummy, |
329 | | const char *arg) |
330 | 0 | { |
331 | 0 | const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
332 | 0 | if (err != NULL) { |
333 | 0 | return err; |
334 | 0 | } |
335 | | |
336 | 0 | if (cmd->server->is_virtual) { |
337 | 0 | return "PidFile directive not allowed in <VirtualHost>"; |
338 | 0 | } |
339 | | |
340 | 0 | ap_pid_fname = arg; |
341 | 0 | return NULL; |
342 | 0 | } |
343 | | |
344 | | void ap_mpm_dump_pidfile(apr_pool_t *p, apr_file_t *out) |
345 | 0 | { |
346 | 0 | apr_file_printf(out, "PidFile: \"%s\"\n", |
347 | 0 | ap_runtime_dir_relative(p, ap_pid_fname)); |
348 | 0 | } |
349 | | |
350 | | const char *ap_mpm_set_max_requests(cmd_parms *cmd, void *dummy, |
351 | | const char *arg) |
352 | 0 | { |
353 | 0 | const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
354 | 0 | if (err != NULL) { |
355 | 0 | return err; |
356 | 0 | } |
357 | | |
358 | 0 | if (!strcasecmp(cmd->cmd->name, "MaxRequestsPerChild")) { |
359 | 0 | ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, APLOGNO(00545) |
360 | 0 | "MaxRequestsPerChild is deprecated, use " |
361 | 0 | "MaxConnectionsPerChild instead."); |
362 | 0 | } |
363 | |
|
364 | 0 | ap_max_requests_per_child = atoi(arg); |
365 | |
|
366 | 0 | return NULL; |
367 | 0 | } |
368 | | |
369 | | const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy, |
370 | | const char *arg) |
371 | 0 | { |
372 | 0 | apr_finfo_t finfo; |
373 | 0 | const char *fname; |
374 | 0 | const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
375 | 0 | if (err != NULL) { |
376 | 0 | return err; |
377 | 0 | } |
378 | | |
379 | 0 | fname = ap_server_root_relative(cmd->temp_pool, arg); |
380 | 0 | if (!fname) { |
381 | 0 | return apr_pstrcat(cmd->pool, "Invalid CoreDumpDirectory path ", |
382 | 0 | arg, NULL); |
383 | 0 | } |
384 | 0 | if (apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool) != APR_SUCCESS) { |
385 | 0 | return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, |
386 | 0 | " does not exist", NULL); |
387 | 0 | } |
388 | 0 | if (finfo.filetype != APR_DIR) { |
389 | 0 | return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, |
390 | 0 | " is not a directory", NULL); |
391 | 0 | } |
392 | 0 | apr_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir)); |
393 | 0 | ap_coredumpdir_configured = 1; |
394 | 0 | return NULL; |
395 | 0 | } |
396 | | |
397 | | AP_DECLARE(const char *)ap_mpm_set_graceful_shutdown(cmd_parms *cmd, |
398 | | void *dummy, |
399 | | const char *arg) |
400 | 0 | { |
401 | 0 | const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
402 | 0 | if (err != NULL) { |
403 | 0 | return err; |
404 | 0 | } |
405 | 0 | ap_graceful_shutdown_timeout = atoi(arg); |
406 | 0 | return NULL; |
407 | 0 | } |
408 | | |
409 | | const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy, |
410 | | const char *arg) |
411 | 0 | { |
412 | 0 | long value; |
413 | 0 | const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
414 | 0 | if (err != NULL) { |
415 | 0 | return err; |
416 | 0 | } |
417 | | |
418 | 0 | errno = 0; |
419 | 0 | value = strtol(arg, NULL, 10); |
420 | 0 | if (value < 0 || errno == ERANGE) |
421 | 0 | return apr_pstrcat(cmd->pool, "Invalid MaxMemFree value: ", |
422 | 0 | arg, NULL); |
423 | | |
424 | 0 | ap_max_mem_free = (apr_uint32_t)value * 1024; |
425 | |
|
426 | 0 | return NULL; |
427 | 0 | } |
428 | | |
429 | | const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy, |
430 | | const char *arg) |
431 | 0 | { |
432 | 0 | long value; |
433 | 0 | const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
434 | 0 | if (err != NULL) { |
435 | 0 | return err; |
436 | 0 | } |
437 | | |
438 | 0 | errno = 0; |
439 | 0 | value = strtol(arg, NULL, 10); |
440 | 0 | if (value < 0 || errno == ERANGE) |
441 | 0 | return apr_pstrcat(cmd->pool, "Invalid ThreadStackSize value: ", |
442 | 0 | arg, NULL); |
443 | | |
444 | 0 | ap_thread_stacksize = (apr_size_t)value; |
445 | |
|
446 | 0 | return NULL; |
447 | 0 | } |
448 | | |
449 | | AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result) |
450 | 0 | { |
451 | 0 | apr_status_t rv; |
452 | |
|
453 | 0 | if (ap_run_mpm_query(query_code, result, &rv) == DECLINED) { |
454 | 0 | rv = APR_EGENERAL; |
455 | 0 | } |
456 | |
|
457 | 0 | return rv; |
458 | 0 | } |
459 | | |
460 | | static void end_gen(mpm_gen_info_t *gi) |
461 | 0 | { |
462 | 0 | ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf, |
463 | 0 | "end of generation %d", gi->gen); |
464 | 0 | ap_run_end_generation(ap_server_conf, gi->gen); |
465 | 0 | APR_RING_REMOVE(gi, link); |
466 | 0 | APR_RING_INSERT_HEAD(unused_geninfo, gi, mpm_gen_info_t, link); |
467 | 0 | } |
468 | | |
469 | | apr_status_t ap_mpm_end_gen_helper(void *unused) /* cleanup on pconf */ |
470 | 0 | { |
471 | 0 | int gen = ap_config_generation - 1; /* differs from MPM generation */ |
472 | 0 | mpm_gen_info_t *cur; |
473 | |
|
474 | 0 | if (geninfo == NULL) { |
475 | | /* initial pconf teardown, MPM hasn't run */ |
476 | 0 | return APR_SUCCESS; |
477 | 0 | } |
478 | | |
479 | 0 | cur = APR_RING_FIRST(geninfo); |
480 | 0 | while (cur != APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link) && |
481 | 0 | cur->gen != gen) { |
482 | 0 | cur = APR_RING_NEXT(cur, link); |
483 | 0 | } |
484 | |
|
485 | 0 | if (cur == APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link)) { |
486 | | /* last child of generation already exited */ |
487 | 0 | ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf, |
488 | 0 | "no record of generation %d", gen); |
489 | 0 | } |
490 | 0 | else { |
491 | 0 | cur->done = 1; |
492 | 0 | if (cur->active == 0) { |
493 | 0 | end_gen(cur); |
494 | 0 | } |
495 | 0 | } |
496 | |
|
497 | 0 | return APR_SUCCESS; |
498 | 0 | } |
499 | | |
500 | | /* core's child-status hook |
501 | | * tracks number of remaining children per generation and |
502 | | * runs the end-generation hook when the last child of |
503 | | * a generation exits |
504 | | */ |
505 | | void ap_core_child_status(server_rec *s, pid_t pid, |
506 | | ap_generation_t gen, int slot, |
507 | | mpm_child_status status) |
508 | 0 | { |
509 | 0 | mpm_gen_info_t *cur; |
510 | 0 | const char *status_msg = "unknown status"; |
511 | |
|
512 | 0 | if (!gen_head_init) { /* where to run this? */ |
513 | 0 | gen_head_init = 1; |
514 | 0 | geninfo = apr_pcalloc(s->process->pool, sizeof *geninfo); |
515 | 0 | unused_geninfo = apr_pcalloc(s->process->pool, sizeof *unused_geninfo); |
516 | 0 | APR_RING_INIT(geninfo, mpm_gen_info_t, link); |
517 | 0 | APR_RING_INIT(unused_geninfo, mpm_gen_info_t, link); |
518 | 0 | } |
519 | |
|
520 | 0 | cur = APR_RING_FIRST(geninfo); |
521 | 0 | while (cur != APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link) && |
522 | 0 | cur->gen != gen) { |
523 | 0 | cur = APR_RING_NEXT(cur, link); |
524 | 0 | } |
525 | |
|
526 | 0 | switch(status) { |
527 | 0 | case MPM_CHILD_STARTED: |
528 | 0 | status_msg = "started"; |
529 | 0 | if (cur == APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link)) { |
530 | | /* first child for this generation */ |
531 | 0 | if (!APR_RING_EMPTY(unused_geninfo, mpm_gen_info_t, link)) { |
532 | 0 | cur = APR_RING_FIRST(unused_geninfo); |
533 | 0 | APR_RING_REMOVE(cur, link); |
534 | 0 | cur->active = cur->done = 0; |
535 | 0 | } |
536 | 0 | else { |
537 | 0 | cur = apr_pcalloc(s->process->pool, sizeof *cur); |
538 | 0 | } |
539 | 0 | cur->gen = gen; |
540 | 0 | APR_RING_ELEM_INIT(cur, link); |
541 | 0 | APR_RING_INSERT_HEAD(geninfo, cur, mpm_gen_info_t, link); |
542 | 0 | } |
543 | 0 | ap_random_parent_after_fork(); |
544 | 0 | ++cur->active; |
545 | 0 | break; |
546 | 0 | case MPM_CHILD_EXITED: |
547 | 0 | ap_update_global_status(); |
548 | 0 | status_msg = "exited"; |
549 | 0 | if (cur == APR_RING_SENTINEL(geninfo, mpm_gen_info_t, link)) { |
550 | 0 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00546) |
551 | 0 | "no record of generation %d of exiting child %" APR_PID_T_FMT, |
552 | 0 | gen, pid); |
553 | 0 | } |
554 | 0 | else { |
555 | 0 | --cur->active; |
556 | 0 | if (!cur->active && cur->done) { /* no children, server has stopped/restarted */ |
557 | 0 | end_gen(cur); |
558 | 0 | } |
559 | 0 | } |
560 | 0 | break; |
561 | 0 | case MPM_CHILD_LOST_SLOT: |
562 | 0 | status_msg = "lost slot"; |
563 | | /* we don't track by slot, so it doesn't matter */ |
564 | 0 | break; |
565 | 0 | } |
566 | 0 | ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s, |
567 | 0 | "mpm child %" APR_PID_T_FMT " (gen %d/slot %d) %s", |
568 | 0 | pid, gen, slot, status_msg); |
569 | 0 | } |
570 | | |
571 | | AP_DECLARE(apr_status_t) ap_mpm_resume_suspended(conn_rec *c) |
572 | 0 | { |
573 | 0 | return ap_run_mpm_resume_suspended(c); |
574 | 0 | } |
575 | | |
576 | | AP_DECLARE(apr_status_t) ap_mpm_register_timed_callback(apr_time_t t, |
577 | | ap_mpm_callback_fn_t *cbfn, void *baton) |
578 | 0 | { |
579 | 0 | return ap_run_mpm_register_timed_callback(t, cbfn, baton); |
580 | 0 | } |
581 | | |
582 | | AP_DECLARE(apr_status_t) ap_mpm_register_poll_callback( |
583 | | apr_pool_t *p, const apr_array_header_t *pfds, |
584 | | ap_mpm_callback_fn_t *cbfn, void *baton) |
585 | 0 | { |
586 | 0 | return ap_run_mpm_register_poll_callback(p, pfds, cbfn, baton); |
587 | 0 | } |
588 | | |
589 | | AP_DECLARE(apr_status_t) ap_mpm_register_poll_callback_timeout( |
590 | | apr_pool_t *p, const apr_array_header_t *pfds, |
591 | | ap_mpm_callback_fn_t *cbfn, ap_mpm_callback_fn_t *tofn, |
592 | | void *baton, apr_time_t timeout) |
593 | 0 | { |
594 | 0 | return ap_run_mpm_register_poll_callback_timeout(p, pfds, cbfn, tofn, |
595 | 0 | baton, timeout); |
596 | 0 | } |
597 | | |
598 | | AP_DECLARE(const char *)ap_show_mpm(void) |
599 | 0 | { |
600 | 0 | const char *name = ap_run_mpm_get_name(); |
601 | |
|
602 | 0 | if (!name) { |
603 | 0 | name = ""; |
604 | 0 | } |
605 | |
|
606 | 0 | return name; |
607 | 0 | } |
608 | | |
609 | | AP_DECLARE(const char *)ap_check_mpm(void) |
610 | 0 | { |
611 | 0 | static const char *last_mpm_name = NULL; |
612 | |
|
613 | 0 | if (!_hooks.link_mpm || _hooks.link_mpm->nelts == 0) |
614 | 0 | return "No MPM loaded."; |
615 | 0 | else if (_hooks.link_mpm->nelts > 1) |
616 | 0 | return "More than one MPM loaded."; |
617 | | |
618 | 0 | if (last_mpm_name) { |
619 | 0 | if (strcmp(last_mpm_name, ap_show_mpm())) { |
620 | 0 | return "The MPM cannot be changed during restart."; |
621 | 0 | } |
622 | 0 | } |
623 | 0 | else { |
624 | 0 | last_mpm_name = apr_pstrdup(ap_pglobal, ap_show_mpm()); |
625 | 0 | } |
626 | | |
627 | 0 | return NULL; |
628 | 0 | } |