/src/httpd/server/mpm_unix.c
Line | Count | Source |
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 | | #ifndef WIN32 |
29 | | |
30 | | #include "apr.h" |
31 | | #include "apr_thread_proc.h" |
32 | | #include "apr_signal.h" |
33 | | #include "apr_strings.h" |
34 | | #define APR_WANT_STRFUNC |
35 | | #include "apr_want.h" |
36 | | #include "apr_getopt.h" |
37 | | #include "apr_optional.h" |
38 | | #include "apr_allocator.h" |
39 | | |
40 | | #include "httpd.h" |
41 | | #include "http_config.h" |
42 | | #include "http_core.h" |
43 | | #include "http_log.h" |
44 | | #include "http_main.h" |
45 | | #include "mpm_common.h" |
46 | | #include "ap_mpm.h" |
47 | | #include "ap_listen.h" |
48 | | #include "scoreboard.h" |
49 | | #include "util_mutex.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 | | |
62 | | /* we know core's module_index is 0 */ |
63 | | #undef APLOG_MODULE_INDEX |
64 | | #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX |
65 | | |
66 | | typedef enum { |
67 | | DO_NOTHING, |
68 | | SEND_SIGTERM, |
69 | | SEND_SIGTERM_NOLOG, |
70 | | SEND_SIGKILL, |
71 | | GIVEUP |
72 | | } action_t; |
73 | | |
74 | | typedef struct extra_process_t { |
75 | | struct extra_process_t *next; |
76 | | pid_t pid; |
77 | | ap_generation_t gen; |
78 | | } extra_process_t; |
79 | | |
80 | | static extra_process_t *extras; |
81 | | |
82 | | AP_DECLARE(void) ap_register_extra_mpm_process(pid_t pid, ap_generation_t gen) |
83 | 0 | { |
84 | 0 | extra_process_t *p = (extra_process_t *)ap_malloc(sizeof(extra_process_t)); |
85 | |
|
86 | 0 | p->next = extras; |
87 | 0 | p->pid = pid; |
88 | 0 | p->gen = gen; |
89 | 0 | extras = p; |
90 | 0 | } |
91 | | |
92 | | AP_DECLARE(int) ap_unregister_extra_mpm_process(pid_t pid, ap_generation_t *old_gen) |
93 | 0 | { |
94 | 0 | extra_process_t *cur = extras; |
95 | 0 | extra_process_t *prev = NULL; |
96 | |
|
97 | 0 | while (cur && cur->pid != pid) { |
98 | 0 | prev = cur; |
99 | 0 | cur = cur->next; |
100 | 0 | } |
101 | |
|
102 | 0 | if (cur) { |
103 | 0 | if (prev) { |
104 | 0 | prev->next = cur->next; |
105 | 0 | } |
106 | 0 | else { |
107 | 0 | extras = cur->next; |
108 | 0 | } |
109 | 0 | *old_gen = cur->gen; |
110 | 0 | free(cur); |
111 | 0 | return 1; /* found */ |
112 | 0 | } |
113 | 0 | else { |
114 | | /* we don't know about any such process */ |
115 | 0 | return 0; |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | | static int reclaim_one_pid(pid_t pid, action_t action) |
120 | 0 | { |
121 | 0 | apr_proc_t proc; |
122 | 0 | apr_status_t waitret; |
123 | 0 | apr_exit_why_e why; |
124 | 0 | int status; |
125 | | |
126 | | /* Ensure pid sanity. */ |
127 | 0 | if (pid < 1) { |
128 | 0 | return 1; |
129 | 0 | } |
130 | | |
131 | 0 | proc.pid = pid; |
132 | 0 | waitret = apr_proc_wait(&proc, &status, &why, APR_NOWAIT); |
133 | 0 | if (waitret != APR_CHILD_NOTDONE) { |
134 | 0 | if (waitret == APR_CHILD_DONE) |
135 | 0 | ap_process_child_status(&proc, why, status); |
136 | 0 | return 1; |
137 | 0 | } |
138 | | |
139 | 0 | switch(action) { |
140 | 0 | case DO_NOTHING: |
141 | 0 | break; |
142 | | |
143 | 0 | case SEND_SIGTERM: |
144 | | /* ok, now it's being annoying */ |
145 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, |
146 | 0 | 0, ap_server_conf, APLOGNO(00045) |
147 | 0 | "child process %" APR_PID_T_FMT |
148 | 0 | " still did not exit, " |
149 | 0 | "sending a SIGTERM", |
150 | 0 | pid); |
151 | | /* FALLTHROUGH */ |
152 | 0 | case SEND_SIGTERM_NOLOG: |
153 | 0 | kill(pid, SIGTERM); |
154 | 0 | break; |
155 | | |
156 | 0 | case SEND_SIGKILL: |
157 | 0 | ap_log_error(APLOG_MARK, APLOG_ERR, |
158 | 0 | 0, ap_server_conf, APLOGNO(00046) |
159 | 0 | "child process %" APR_PID_T_FMT |
160 | 0 | " still did not exit, " |
161 | 0 | "sending a SIGKILL", |
162 | 0 | pid); |
163 | 0 | kill(pid, SIGKILL); |
164 | 0 | break; |
165 | | |
166 | 0 | case GIVEUP: |
167 | | /* gave it our best shot, but alas... If this really |
168 | | * is a child we are trying to kill and it really hasn't |
169 | | * exited, we will likely fail to bind to the port |
170 | | * after the restart. |
171 | | */ |
172 | 0 | ap_log_error(APLOG_MARK, APLOG_ERR, |
173 | 0 | 0, ap_server_conf, APLOGNO(00047) |
174 | 0 | "could not make child process %" APR_PID_T_FMT |
175 | 0 | " exit, " |
176 | 0 | "attempting to continue anyway", |
177 | 0 | pid); |
178 | 0 | break; |
179 | 0 | } |
180 | | |
181 | 0 | return 0; |
182 | 0 | } |
183 | | |
184 | | AP_DECLARE(void) ap_reclaim_child_processes(int terminate, |
185 | | ap_reclaim_callback_fn_t *mpm_callback) |
186 | 0 | { |
187 | 0 | apr_time_t waittime = 1024 * 16; |
188 | 0 | int i; |
189 | 0 | extra_process_t *cur_extra; |
190 | 0 | int not_dead_yet; |
191 | 0 | int max_daemons; |
192 | 0 | apr_time_t starttime = apr_time_now(); |
193 | | /* this table of actions and elapsed times tells what action is taken |
194 | | * at which elapsed time from starting the reclaim |
195 | | */ |
196 | 0 | struct { |
197 | 0 | action_t action; |
198 | 0 | apr_time_t action_time; |
199 | 0 | } action_table[] = { |
200 | 0 | {DO_NOTHING, 0}, /* dummy entry for iterations where we reap |
201 | | * children but take no action against |
202 | | * stragglers |
203 | | */ |
204 | 0 | {SEND_SIGTERM_NOLOG, 0}, /* skipped if terminate == 0 */ |
205 | 0 | {SEND_SIGTERM, apr_time_from_sec(3)}, |
206 | 0 | {SEND_SIGTERM, apr_time_from_sec(5)}, |
207 | 0 | {SEND_SIGTERM, apr_time_from_sec(7)}, |
208 | 0 | {SEND_SIGKILL, apr_time_from_sec(9)}, |
209 | 0 | {GIVEUP, apr_time_from_sec(10)} |
210 | 0 | }; |
211 | 0 | int cur_action; /* index of action we decided to take this |
212 | | * iteration |
213 | | */ |
214 | 0 | int next_action = terminate ? 1 : 2; /* index of first real action */ |
215 | |
|
216 | 0 | ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons); |
217 | |
|
218 | 0 | do { |
219 | 0 | if (action_table[next_action].action_time > 0) { |
220 | 0 | apr_sleep(waittime); |
221 | | /* don't let waittime get longer than 1 second; otherwise, we don't |
222 | | * react quickly to the last child exiting, and taking action can |
223 | | * be delayed |
224 | | */ |
225 | 0 | waittime = waittime * 4; |
226 | 0 | if (waittime > apr_time_from_sec(1)) { |
227 | 0 | waittime = apr_time_from_sec(1); |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | | /* see what action to take, if any */ |
232 | 0 | if (action_table[next_action].action_time <= apr_time_now() - starttime) { |
233 | 0 | cur_action = next_action; |
234 | 0 | ++next_action; |
235 | 0 | } |
236 | 0 | else { |
237 | 0 | cur_action = 0; /* nothing to do */ |
238 | 0 | } |
239 | | |
240 | | /* now see who is done */ |
241 | 0 | not_dead_yet = 0; |
242 | 0 | for (i = 0; i < max_daemons; ++i) { |
243 | 0 | process_score *ps = ap_get_scoreboard_process(i); |
244 | 0 | pid_t pid = ps->pid; |
245 | |
|
246 | 0 | if (pid == 0) { |
247 | 0 | continue; /* not every scoreboard entry is in use */ |
248 | 0 | } |
249 | | |
250 | 0 | if (reclaim_one_pid(pid, action_table[cur_action].action)) { |
251 | 0 | mpm_callback(i, 0, 0); |
252 | 0 | } |
253 | 0 | else { |
254 | 0 | ++not_dead_yet; |
255 | 0 | } |
256 | 0 | } |
257 | |
|
258 | 0 | cur_extra = extras; |
259 | 0 | while (cur_extra) { |
260 | 0 | ap_generation_t old_gen; |
261 | 0 | extra_process_t *next = cur_extra->next; |
262 | 0 | pid_t pid = cur_extra->pid; |
263 | |
|
264 | 0 | if (reclaim_one_pid(pid, action_table[cur_action].action)) { |
265 | 0 | if (ap_unregister_extra_mpm_process(pid, &old_gen) == 1) { |
266 | | /* cur_extra dangling pointer from here. */ |
267 | 0 | mpm_callback(-1, pid, old_gen); |
268 | 0 | } |
269 | 0 | else { |
270 | 0 | AP_DEBUG_ASSERT(1 == 0); |
271 | 0 | } |
272 | 0 | } |
273 | 0 | else { |
274 | 0 | ++not_dead_yet; |
275 | 0 | } |
276 | 0 | cur_extra = next; |
277 | 0 | } |
278 | 0 | #if APR_HAS_OTHER_CHILD |
279 | 0 | apr_proc_other_child_refresh_all(APR_OC_REASON_RESTART); |
280 | 0 | #endif |
281 | |
|
282 | 0 | } while (not_dead_yet > 0 && |
283 | 0 | action_table[cur_action].action != GIVEUP); |
284 | 0 | } |
285 | | |
286 | | AP_DECLARE(void) ap_relieve_child_processes(ap_reclaim_callback_fn_t *mpm_callback) |
287 | 0 | { |
288 | 0 | int i; |
289 | 0 | extra_process_t *cur_extra; |
290 | 0 | int max_daemons; |
291 | |
|
292 | 0 | ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons); |
293 | | |
294 | | /* now see who is done */ |
295 | 0 | for (i = 0; i < max_daemons; ++i) { |
296 | 0 | process_score *ps = ap_get_scoreboard_process(i); |
297 | 0 | pid_t pid = ps->pid; |
298 | |
|
299 | 0 | if (pid == 0) { |
300 | 0 | continue; /* not every scoreboard entry is in use */ |
301 | 0 | } |
302 | | |
303 | 0 | if (reclaim_one_pid(pid, DO_NOTHING)) { |
304 | 0 | mpm_callback(i, 0, 0); |
305 | 0 | } |
306 | 0 | } |
307 | |
|
308 | 0 | cur_extra = extras; |
309 | 0 | while (cur_extra) { |
310 | 0 | ap_generation_t old_gen; |
311 | 0 | extra_process_t *next = cur_extra->next; |
312 | 0 | pid_t pid = cur_extra->pid; |
313 | |
|
314 | 0 | if (reclaim_one_pid(pid, DO_NOTHING)) { |
315 | 0 | if (ap_unregister_extra_mpm_process(pid, &old_gen) == 1) { |
316 | | /* cur_extra dangling pointer from here. */ |
317 | 0 | mpm_callback(-1, pid, old_gen); |
318 | 0 | } |
319 | 0 | else { |
320 | 0 | AP_DEBUG_ASSERT(1 == 0); |
321 | 0 | } |
322 | 0 | } |
323 | 0 | cur_extra = next; |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | | /* Before sending the signal to the pid this function verifies that |
328 | | * the pid is a member of the current process group; either using |
329 | | * apr_proc_wait(), where waitpid() guarantees to fail for non-child |
330 | | * processes; or by using getpgid() directly, if available. */ |
331 | | AP_DECLARE(apr_status_t) ap_mpm_safe_kill(pid_t pid, int sig) |
332 | 0 | { |
333 | | #ifndef HAVE_GETPGID |
334 | | apr_proc_t proc; |
335 | | apr_status_t rv; |
336 | | apr_exit_why_e why; |
337 | | int status; |
338 | | |
339 | | /* Ensure pid sanity */ |
340 | | if (pid < 1) { |
341 | | return APR_EINVAL; |
342 | | } |
343 | | |
344 | | proc.pid = pid; |
345 | | rv = apr_proc_wait(&proc, &status, &why, APR_NOWAIT); |
346 | | if (rv == APR_CHILD_DONE) { |
347 | | /* The child already died - log the termination status if |
348 | | * necessary: */ |
349 | | ap_process_child_status(&proc, why, status); |
350 | | return APR_EINVAL; |
351 | | } |
352 | | else if (rv != APR_CHILD_NOTDONE) { |
353 | | /* The child is already dead and reaped, or was a bogus pid - |
354 | | * log this either way. */ |
355 | | ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, APLOGNO(00048) |
356 | | "cannot send signal %d to pid %ld (non-child or " |
357 | | "already dead)", sig, (long)pid); |
358 | | return APR_EINVAL; |
359 | | } |
360 | | #else |
361 | 0 | pid_t pg; |
362 | | |
363 | | /* Ensure pid sanity. */ |
364 | 0 | if (pid < 1) { |
365 | 0 | return APR_EINVAL; |
366 | 0 | } |
367 | | |
368 | 0 | pg = getpgid(pid); |
369 | 0 | if (pg == -1) { |
370 | | /* Process already dead... */ |
371 | 0 | return errno; |
372 | 0 | } |
373 | | |
374 | 0 | if (pg != getpgrp()) { |
375 | 0 | ap_log_error(APLOG_MARK, APLOG_ALERT, 0, ap_server_conf, APLOGNO(00049) |
376 | 0 | "refusing to send signal %d to pid %ld outside " |
377 | 0 | "process group", sig, (long)pid); |
378 | 0 | return APR_EINVAL; |
379 | 0 | } |
380 | 0 | #endif |
381 | | |
382 | 0 | return kill(pid, sig) ? errno : APR_SUCCESS; |
383 | 0 | } |
384 | | |
385 | | |
386 | | AP_DECLARE(int) ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, |
387 | | int status) |
388 | 0 | { |
389 | 0 | int signum = status; |
390 | 0 | const char *sigdesc; |
391 | | |
392 | | /* Child died... if it died due to a fatal error, |
393 | | * we should simply bail out. The caller needs to |
394 | | * check for bad rc from us and exit, running any |
395 | | * appropriate cleanups. |
396 | | * |
397 | | * If the child died due to a resource shortage, |
398 | | * the parent should limit the rate of forking |
399 | | */ |
400 | 0 | if (APR_PROC_CHECK_EXIT(why)) { |
401 | 0 | if (status == APEXIT_CHILDSICK) { |
402 | 0 | return status; |
403 | 0 | } |
404 | | |
405 | 0 | if (status == APEXIT_CHILDFATAL) { |
406 | 0 | ap_log_error(APLOG_MARK, APLOG_ALERT, |
407 | 0 | 0, ap_server_conf, APLOGNO(00050) |
408 | 0 | "Child %" APR_PID_T_FMT |
409 | 0 | " returned a Fatal error... Apache is exiting!", |
410 | 0 | pid->pid); |
411 | 0 | return APEXIT_CHILDFATAL; |
412 | 0 | } |
413 | | |
414 | 0 | return 0; |
415 | 0 | } |
416 | | |
417 | 0 | if (APR_PROC_CHECK_SIGNALED(why)) { |
418 | 0 | sigdesc = apr_signal_description_get(signum); |
419 | |
|
420 | 0 | switch (signum) { |
421 | 0 | case SIGTERM: |
422 | 0 | case SIGHUP: |
423 | 0 | case AP_SIG_GRACEFUL: |
424 | 0 | case SIGKILL: |
425 | 0 | break; |
426 | | |
427 | 0 | default: |
428 | 0 | if (APR_PROC_CHECK_CORE_DUMP(why)) { |
429 | 0 | ap_log_error(APLOG_MARK, APLOG_NOTICE, |
430 | 0 | 0, ap_server_conf, APLOGNO(00051) |
431 | 0 | "child pid %ld exit signal %s (%d), " |
432 | 0 | "possible coredump in %s", |
433 | 0 | (long)pid->pid, sigdesc, signum, |
434 | 0 | ap_coredump_dir); |
435 | 0 | } |
436 | 0 | else { |
437 | 0 | ap_log_error(APLOG_MARK, APLOG_NOTICE, |
438 | 0 | 0, ap_server_conf, APLOGNO(00052) |
439 | 0 | "child pid %ld exit signal %s (%d)", |
440 | 0 | (long)pid->pid, sigdesc, signum); |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | 0 | return 0; |
445 | 0 | } |
446 | | |
447 | | AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod) |
448 | 0 | { |
449 | 0 | apr_status_t rv; |
450 | |
|
451 | 0 | *pod = apr_palloc(p, sizeof(**pod)); |
452 | 0 | rv = apr_file_pipe_create_ex(&((*pod)->pod_in), &((*pod)->pod_out), |
453 | 0 | APR_WRITE_BLOCK, p); |
454 | 0 | if (rv != APR_SUCCESS) { |
455 | 0 | return rv; |
456 | 0 | } |
457 | | |
458 | 0 | apr_file_pipe_timeout_set((*pod)->pod_in, 0); |
459 | 0 | (*pod)->p = p; |
460 | | |
461 | | /* close these before exec. */ |
462 | 0 | apr_file_inherit_unset((*pod)->pod_in); |
463 | 0 | apr_file_inherit_unset((*pod)->pod_out); |
464 | |
|
465 | 0 | return APR_SUCCESS; |
466 | 0 | } |
467 | | |
468 | | AP_DECLARE(apr_status_t) ap_mpm_pod_check(ap_pod_t *pod) |
469 | 0 | { |
470 | 0 | char c; |
471 | 0 | apr_size_t len = 1; |
472 | 0 | apr_status_t rv; |
473 | |
|
474 | 0 | rv = apr_file_read(pod->pod_in, &c, &len); |
475 | |
|
476 | 0 | if ((rv == APR_SUCCESS) && (len == 1)) { |
477 | 0 | return APR_SUCCESS; |
478 | 0 | } |
479 | | |
480 | 0 | if (rv != APR_SUCCESS) { |
481 | 0 | return rv; |
482 | 0 | } |
483 | | |
484 | 0 | return AP_NORESTART; |
485 | 0 | } |
486 | | |
487 | | AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod) |
488 | 0 | { |
489 | 0 | apr_status_t rv; |
490 | |
|
491 | 0 | rv = apr_file_close(pod->pod_out); |
492 | 0 | if (rv != APR_SUCCESS) { |
493 | 0 | return rv; |
494 | 0 | } |
495 | | |
496 | 0 | rv = apr_file_close(pod->pod_in); |
497 | 0 | if (rv != APR_SUCCESS) { |
498 | 0 | return rv; |
499 | 0 | } |
500 | | |
501 | 0 | return APR_SUCCESS; |
502 | 0 | } |
503 | | |
504 | | static apr_status_t pod_signal_internal(ap_pod_t *pod) |
505 | 0 | { |
506 | 0 | apr_status_t rv; |
507 | 0 | char char_of_death = '!'; |
508 | 0 | apr_size_t one = 1; |
509 | |
|
510 | 0 | rv = apr_file_write(pod->pod_out, &char_of_death, &one); |
511 | 0 | if (rv != APR_SUCCESS) { |
512 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00053) |
513 | 0 | "write pipe_of_death"); |
514 | 0 | } |
515 | |
|
516 | 0 | return rv; |
517 | 0 | } |
518 | | |
519 | | AP_DECLARE(apr_status_t) ap_mpm_podx_open(apr_pool_t *p, ap_pod_t **pod) |
520 | 0 | { |
521 | 0 | apr_status_t rv; |
522 | |
|
523 | 0 | *pod = apr_palloc(p, sizeof(**pod)); |
524 | 0 | rv = apr_file_pipe_create(&((*pod)->pod_in), &((*pod)->pod_out), p); |
525 | 0 | if (rv != APR_SUCCESS) { |
526 | 0 | return rv; |
527 | 0 | } |
528 | | /* |
529 | | apr_file_pipe_timeout_set((*pod)->pod_in, 0); |
530 | | */ |
531 | 0 | (*pod)->p = p; |
532 | | |
533 | | /* close these before exec. */ |
534 | 0 | apr_file_inherit_unset((*pod)->pod_in); |
535 | 0 | apr_file_inherit_unset((*pod)->pod_out); |
536 | |
|
537 | 0 | return APR_SUCCESS; |
538 | 0 | } |
539 | | |
540 | | AP_DECLARE(int) ap_mpm_podx_check(ap_pod_t *pod) |
541 | 0 | { |
542 | 0 | char c; |
543 | 0 | apr_os_file_t fd; |
544 | 0 | int rc; |
545 | | |
546 | | /* we need to surface EINTR so we'll have to grab the |
547 | | * native file descriptor and do the OS read() ourselves |
548 | | */ |
549 | 0 | apr_os_file_get(&fd, pod->pod_in); |
550 | 0 | rc = read(fd, &c, 1); |
551 | 0 | if (rc == 1) { |
552 | 0 | switch (c) { |
553 | 0 | case AP_MPM_PODX_RESTART_CHAR: |
554 | 0 | return AP_MPM_PODX_RESTART; |
555 | 0 | case AP_MPM_PODX_GRACEFUL_CHAR: |
556 | 0 | return AP_MPM_PODX_GRACEFUL; |
557 | 0 | } |
558 | 0 | } |
559 | 0 | return AP_MPM_PODX_NORESTART; |
560 | 0 | } |
561 | | |
562 | | AP_DECLARE(apr_status_t) ap_mpm_podx_close(ap_pod_t *pod) |
563 | 0 | { |
564 | 0 | apr_status_t rv; |
565 | |
|
566 | 0 | rv = apr_file_close(pod->pod_out); |
567 | 0 | if (rv != APR_SUCCESS) { |
568 | 0 | return rv; |
569 | 0 | } |
570 | | |
571 | 0 | rv = apr_file_close(pod->pod_in); |
572 | 0 | if (rv != APR_SUCCESS) { |
573 | 0 | return rv; |
574 | 0 | } |
575 | 0 | return rv; |
576 | 0 | } |
577 | | |
578 | | static apr_status_t podx_signal_internal(ap_pod_t *pod, |
579 | | ap_podx_restart_t graceful) |
580 | 0 | { |
581 | 0 | apr_status_t rv; |
582 | 0 | apr_size_t one = 1; |
583 | 0 | char char_of_death = ' '; |
584 | 0 | switch (graceful) { |
585 | 0 | case AP_MPM_PODX_RESTART: |
586 | 0 | char_of_death = AP_MPM_PODX_RESTART_CHAR; |
587 | 0 | break; |
588 | 0 | case AP_MPM_PODX_GRACEFUL: |
589 | 0 | char_of_death = AP_MPM_PODX_GRACEFUL_CHAR; |
590 | 0 | break; |
591 | 0 | case AP_MPM_PODX_NORESTART: |
592 | 0 | break; |
593 | 0 | } |
594 | | |
595 | 0 | rv = apr_file_write(pod->pod_out, &char_of_death, &one); |
596 | 0 | if (rv != APR_SUCCESS) { |
597 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(02404) |
598 | 0 | "write pipe_of_death"); |
599 | 0 | } |
600 | 0 | return rv; |
601 | 0 | } |
602 | | |
603 | | AP_DECLARE(apr_status_t) ap_mpm_podx_signal(ap_pod_t * pod, |
604 | | ap_podx_restart_t graceful) |
605 | 0 | { |
606 | 0 | return podx_signal_internal(pod, graceful); |
607 | 0 | } |
608 | | |
609 | | AP_DECLARE(void) ap_mpm_podx_killpg(ap_pod_t * pod, int num, |
610 | | ap_podx_restart_t graceful) |
611 | 0 | { |
612 | 0 | int i; |
613 | 0 | apr_status_t rv = APR_SUCCESS; |
614 | |
|
615 | 0 | for (i = 0; i < num && rv == APR_SUCCESS; i++) { |
616 | 0 | rv = podx_signal_internal(pod, graceful); |
617 | 0 | } |
618 | 0 | } |
619 | | |
620 | | /* This function connects to the server and sends enough data to |
621 | | * ensure the child wakes up and processes a new connection. This |
622 | | * permits the MPM to skip the poll when there is only one listening |
623 | | * socket, because it provides a alternate way to unblock an accept() |
624 | | * when the pod is used. */ |
625 | | static apr_status_t dummy_connection(ap_pod_t *pod) |
626 | 0 | { |
627 | 0 | const char *data; |
628 | 0 | apr_status_t rv; |
629 | 0 | apr_socket_t *sock; |
630 | 0 | apr_pool_t *p; |
631 | 0 | apr_size_t len; |
632 | 0 | ap_listen_rec *lp; |
633 | | |
634 | | /* create a temporary pool for the socket. pconf stays around too long */ |
635 | 0 | rv = apr_pool_create(&p, pod->p); |
636 | 0 | if (rv != APR_SUCCESS) { |
637 | 0 | return rv; |
638 | 0 | } |
639 | 0 | apr_pool_tag(p, "dummy_connection"); |
640 | | |
641 | | /* If possible, find a listener which is configured for |
642 | | * plain-HTTP, not SSL; using an SSL port would either be |
643 | | * expensive to do correctly (performing a complete SSL handshake) |
644 | | * or cause log spam by doing incorrectly (simply sending EOF). */ |
645 | 0 | lp = ap_listeners; |
646 | 0 | while (lp && lp->protocol && ap_cstr_casecmp(lp->protocol, "http") != 0) { |
647 | 0 | lp = lp->next; |
648 | 0 | } |
649 | 0 | if (!lp) { |
650 | 0 | lp = ap_listeners; |
651 | 0 | } |
652 | |
|
653 | 0 | rv = apr_socket_create(&sock, lp->bind_addr->family, SOCK_STREAM, 0, p); |
654 | 0 | if (rv != APR_SUCCESS) { |
655 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00054) |
656 | 0 | "get socket to connect to listener"); |
657 | 0 | apr_pool_destroy(p); |
658 | 0 | return rv; |
659 | 0 | } |
660 | | |
661 | | /* on some platforms (e.g., FreeBSD), the kernel won't accept many |
662 | | * queued connections before it starts blocking local connects... |
663 | | * we need to keep from blocking too long and instead return an error, |
664 | | * because the MPM won't want to hold up a graceful restart for a |
665 | | * long time |
666 | | */ |
667 | 0 | rv = apr_socket_timeout_set(sock, apr_time_from_sec(3)); |
668 | 0 | if (rv != APR_SUCCESS) { |
669 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00055) |
670 | 0 | "set timeout on socket to connect to listener"); |
671 | 0 | apr_socket_close(sock); |
672 | 0 | apr_pool_destroy(p); |
673 | 0 | return rv; |
674 | 0 | } |
675 | | |
676 | 0 | rv = apr_socket_connect(sock, lp->bind_addr); |
677 | | #ifdef __OpenBSD__ |
678 | | /* OpenBSD's connect() returns EINVAL when the target address is the |
679 | | * wildcard address (INADDR_ANY / IN6ADDR_ANY), |
680 | | * Retry against the loopback address in that case. */ |
681 | | if (rv != APR_SUCCESS && APR_STATUS_IS_EINVAL(rv)) { |
682 | | int is_wildcard = 0; |
683 | | |
684 | | if (lp->bind_addr->family == APR_INET) { |
685 | | is_wildcard = (lp->bind_addr->sa.sin.sin_addr.s_addr == INADDR_ANY); |
686 | | } |
687 | | #if APR_HAVE_IPV6 |
688 | | else if (lp->bind_addr->family == APR_INET6) { |
689 | | is_wildcard = IN6_IS_ADDR_UNSPECIFIED(&lp->bind_addr->sa.sin6.sin6_addr); |
690 | | } |
691 | | #endif |
692 | | |
693 | | if (is_wildcard) { |
694 | | apr_sockaddr_t *loopback; |
695 | | const char *ip = (lp->bind_addr->family == APR_INET6) ? "::1" : "127.0.0.1"; |
696 | | |
697 | | rv = apr_sockaddr_info_get(&loopback, ip, lp->bind_addr->family, |
698 | | lp->bind_addr->port, 0, p); |
699 | | if (rv == APR_SUCCESS) { |
700 | | rv = apr_socket_connect(sock, loopback); |
701 | | } |
702 | | } |
703 | | } |
704 | | #endif /* __OpenBSD__ */ |
705 | 0 | if (rv != APR_SUCCESS) { |
706 | 0 | int log_level = APLOG_WARNING; |
707 | |
|
708 | 0 | if (APR_STATUS_IS_TIMEUP(rv)) { |
709 | | /* probably some server processes bailed out already and there |
710 | | * is nobody around to call accept and clear out the kernel |
711 | | * connection queue; usually this is not worth logging |
712 | | */ |
713 | 0 | log_level = APLOG_DEBUG; |
714 | 0 | } |
715 | |
|
716 | 0 | ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf, APLOGNO(00056) |
717 | 0 | "connect to listener on %pI", lp->bind_addr); |
718 | 0 | apr_pool_destroy(p); |
719 | 0 | return rv; |
720 | 0 | } |
721 | | |
722 | 0 | if (lp->protocol && ap_cstr_casecmp(lp->protocol, "https") == 0) { |
723 | | /* Send a TLS 1.0 close_notify alert. This is perhaps the |
724 | | * "least wrong" way to open and cleanly terminate an SSL |
725 | | * connection. It should "work" without noisy error logs if |
726 | | * the server actually expects SSLv3/TLSv1. With |
727 | | * SSLv23_server_method() OpenSSL's SSL_accept() fails |
728 | | * ungracefully on receipt of this message, since it requires |
729 | | * an 11-byte ClientHello message and this is too short. */ |
730 | 0 | static const unsigned char tls10_close_notify[7] = { |
731 | 0 | '\x15', /* TLSPlainText.type = Alert (21) */ |
732 | 0 | '\x03', '\x01', /* TLSPlainText.version = {3, 1} */ |
733 | 0 | '\x00', '\x02', /* TLSPlainText.length = 2 */ |
734 | 0 | '\x01', /* Alert.level = warning (1) */ |
735 | 0 | '\x00' /* Alert.description = close_notify (0) */ |
736 | 0 | }; |
737 | 0 | data = (const char *)tls10_close_notify; |
738 | 0 | len = sizeof(tls10_close_notify); |
739 | 0 | } |
740 | 0 | else /* ... XXX other request types here? */ { |
741 | | /* Create an HTTP request string. We include a User-Agent so |
742 | | * that administrators can track down the cause of the |
743 | | * odd-looking requests in their logs. A complete request is |
744 | | * used since kernel-level filtering may require that much |
745 | | * data before returning from accept(). */ |
746 | 0 | data = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ", |
747 | 0 | ap_get_server_description(), |
748 | 0 | " (internal dummy connection)\r\n\r\n", NULL); |
749 | 0 | len = strlen(data); |
750 | 0 | } |
751 | |
|
752 | 0 | apr_socket_send(sock, data, &len); |
753 | 0 | apr_socket_close(sock); |
754 | 0 | apr_pool_destroy(p); |
755 | |
|
756 | 0 | return rv; |
757 | 0 | } |
758 | | |
759 | | AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod) |
760 | 0 | { |
761 | 0 | apr_status_t rv; |
762 | |
|
763 | 0 | rv = pod_signal_internal(pod); |
764 | 0 | if (rv != APR_SUCCESS) { |
765 | 0 | return rv; |
766 | 0 | } |
767 | | |
768 | 0 | return dummy_connection(pod); |
769 | 0 | } |
770 | | |
771 | | void ap_mpm_pod_killpg(ap_pod_t *pod, int num) |
772 | 0 | { |
773 | 0 | int i; |
774 | 0 | apr_status_t rv = APR_SUCCESS; |
775 | | |
776 | | /* we don't write anything to the pod here... we assume |
777 | | * that the would-be reader of the pod has another way to |
778 | | * see that it is time to die once we wake it up |
779 | | * |
780 | | * writing lots of things to the pod at once is very |
781 | | * problematic... we can fill the kernel pipe buffer and |
782 | | * be blocked until somebody consumes some bytes or |
783 | | * we hit a timeout... if we hit a timeout we can't just |
784 | | * keep trying because maybe we'll never successfully |
785 | | * write again... but then maybe we'll leave would-be |
786 | | * readers stranded (a number of them could be tied up for |
787 | | * a while serving time-consuming requests) |
788 | | */ |
789 | | /* Recall: we only worry about IDLE child processes here */ |
790 | 0 | for (i = 0; i < num && rv == APR_SUCCESS; i++) { |
791 | 0 | if (ap_scoreboard_image->servers[i][0].status != SERVER_READY || |
792 | 0 | ap_scoreboard_image->servers[i][0].pid == 0) { |
793 | 0 | continue; |
794 | 0 | } |
795 | 0 | rv = dummy_connection(pod); |
796 | 0 | } |
797 | 0 | } |
798 | | |
799 | | static const char *dash_k_arg = NULL; |
800 | | static const char *dash_k_arg_noarg = "noarg"; |
801 | | |
802 | | static int send_signal(pid_t pid, int sig) |
803 | 0 | { |
804 | 0 | if (kill(pid, sig) < 0) { |
805 | 0 | ap_log_error(APLOG_MARK, APLOG_STARTUP, errno, NULL, APLOGNO(00057) |
806 | 0 | "sending signal to server"); |
807 | 0 | return 1; |
808 | 0 | } |
809 | 0 | return 0; |
810 | 0 | } |
811 | | |
812 | | int ap_signal_server(int *exit_status, apr_pool_t *pconf) |
813 | 0 | { |
814 | 0 | apr_status_t rv; |
815 | 0 | pid_t otherpid; |
816 | 0 | int running = 0; |
817 | 0 | const char *status; |
818 | |
|
819 | 0 | *exit_status = 0; |
820 | |
|
821 | 0 | rv = ap_read_pid(pconf, ap_pid_fname, &otherpid); |
822 | 0 | if (rv != APR_SUCCESS) { |
823 | 0 | if (!APR_STATUS_IS_ENOENT(rv)) { |
824 | 0 | ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, NULL, APLOGNO(00058) |
825 | 0 | "Error retrieving pid file %s", ap_pid_fname); |
826 | 0 | ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, APLOGNO(00059) |
827 | 0 | "Remove it before continuing if it is corrupted."); |
828 | 0 | *exit_status = 1; |
829 | 0 | return 1; |
830 | 0 | } |
831 | 0 | status = "httpd (no pid file) not running"; |
832 | 0 | } |
833 | 0 | else { |
834 | | /* With containerization, httpd may get the same PID at each startup, |
835 | | * handle it as if it were not running (it obviously can't). |
836 | | */ |
837 | 0 | if (otherpid != getpid() && kill(otherpid, 0) == 0) { |
838 | 0 | running = 1; |
839 | 0 | status = apr_psprintf(pconf, |
840 | 0 | "httpd (pid %" APR_PID_T_FMT ") already " |
841 | 0 | "running", otherpid); |
842 | 0 | } |
843 | 0 | else { |
844 | 0 | status = apr_psprintf(pconf, |
845 | 0 | "httpd (pid %" APR_PID_T_FMT "?) not running", |
846 | 0 | otherpid); |
847 | 0 | } |
848 | 0 | } |
849 | | |
850 | 0 | if (!strcmp(dash_k_arg, "start") || dash_k_arg == dash_k_arg_noarg) { |
851 | 0 | if (running) { |
852 | 0 | printf("%s\n", status); |
853 | 0 | return 1; |
854 | 0 | } |
855 | 0 | } |
856 | | |
857 | 0 | if (!strcmp(dash_k_arg, "stop")) { |
858 | 0 | if (!running) { |
859 | 0 | printf("%s\n", status); |
860 | 0 | } |
861 | 0 | else { |
862 | 0 | send_signal(otherpid, SIGTERM); |
863 | 0 | } |
864 | 0 | return 1; |
865 | 0 | } |
866 | | |
867 | 0 | if (!strcmp(dash_k_arg, "restart")) { |
868 | 0 | if (!running) { |
869 | 0 | printf("httpd not running, trying to start\n"); |
870 | 0 | } |
871 | 0 | else { |
872 | 0 | *exit_status = send_signal(otherpid, SIGHUP); |
873 | 0 | return 1; |
874 | 0 | } |
875 | 0 | } |
876 | | |
877 | 0 | if (!strcmp(dash_k_arg, "graceful")) { |
878 | 0 | if (!running) { |
879 | 0 | printf("httpd not running, trying to start\n"); |
880 | 0 | } |
881 | 0 | else { |
882 | 0 | *exit_status = send_signal(otherpid, AP_SIG_GRACEFUL); |
883 | 0 | return 1; |
884 | 0 | } |
885 | 0 | } |
886 | | |
887 | 0 | if (!strcmp(dash_k_arg, "graceful-stop")) { |
888 | 0 | if (!running) { |
889 | 0 | printf("%s\n", status); |
890 | 0 | } |
891 | 0 | else { |
892 | 0 | *exit_status = send_signal(otherpid, AP_SIG_GRACEFUL_STOP); |
893 | 0 | } |
894 | 0 | return 1; |
895 | 0 | } |
896 | | |
897 | 0 | return 0; |
898 | 0 | } |
899 | | |
900 | | void ap_mpm_rewrite_args(process_rec *process) |
901 | 0 | { |
902 | 0 | apr_array_header_t *mpm_new_argv; |
903 | 0 | apr_status_t rv; |
904 | 0 | apr_getopt_t *opt; |
905 | 0 | char optbuf[3]; |
906 | 0 | const char *optarg; |
907 | |
|
908 | 0 | mpm_new_argv = apr_array_make(process->pool, process->argc, |
909 | 0 | sizeof(const char **)); |
910 | 0 | *(const char **)apr_array_push(mpm_new_argv) = process->argv[0]; |
911 | 0 | apr_getopt_init(&opt, process->pool, process->argc, process->argv); |
912 | 0 | opt->errfn = NULL; |
913 | 0 | optbuf[0] = '-'; |
914 | | /* option char returned by apr_getopt() will be stored in optbuf[1] */ |
915 | 0 | optbuf[2] = '\0'; |
916 | 0 | while ((rv = apr_getopt(opt, "k:" AP_SERVER_BASEARGS, |
917 | 0 | optbuf + 1, &optarg)) == APR_SUCCESS) { |
918 | 0 | switch(optbuf[1]) { |
919 | 0 | case 'k': |
920 | 0 | if (!dash_k_arg) { |
921 | 0 | if (!strcmp(optarg, "start") || !strcmp(optarg, "stop") || |
922 | 0 | !strcmp(optarg, "restart") || !strcmp(optarg, "graceful") || |
923 | 0 | !strcmp(optarg, "graceful-stop")) { |
924 | 0 | dash_k_arg = optarg; |
925 | 0 | break; |
926 | 0 | } |
927 | 0 | } |
928 | 0 | default: |
929 | 0 | *(const char **)apr_array_push(mpm_new_argv) = |
930 | 0 | apr_pstrdup(process->pool, optbuf); |
931 | 0 | if (optarg) { |
932 | 0 | *(const char **)apr_array_push(mpm_new_argv) = optarg; |
933 | 0 | } |
934 | 0 | } |
935 | 0 | } |
936 | | |
937 | | /* back up to capture the bad argument */ |
938 | 0 | if (rv == APR_BADCH || rv == APR_BADARG) { |
939 | 0 | opt->ind--; |
940 | 0 | } |
941 | |
|
942 | 0 | while (opt->ind < opt->argc) { |
943 | 0 | *(const char **)apr_array_push(mpm_new_argv) = |
944 | 0 | apr_pstrdup(process->pool, opt->argv[opt->ind++]); |
945 | 0 | } |
946 | |
|
947 | 0 | process->argc = mpm_new_argv->nelts; |
948 | 0 | process->argv = (const char * const *)mpm_new_argv->elts; |
949 | |
|
950 | 0 | if (NULL == dash_k_arg) { |
951 | 0 | dash_k_arg = dash_k_arg_noarg; |
952 | 0 | } |
953 | |
|
954 | 0 | APR_REGISTER_OPTIONAL_FN(ap_signal_server); |
955 | 0 | } |
956 | | |
957 | | static pid_t parent_pid, my_pid; |
958 | | static apr_pool_t *pconf; |
959 | | |
960 | | #if AP_ENABLE_EXCEPTION_HOOK |
961 | | |
962 | | static int exception_hook_enabled; |
963 | | |
964 | | const char *ap_mpm_set_exception_hook(cmd_parms *cmd, void *dummy, |
965 | | const char *arg) |
966 | | { |
967 | | const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); |
968 | | if (err != NULL) { |
969 | | return err; |
970 | | } |
971 | | |
972 | | if (cmd->server->is_virtual) { |
973 | | return "EnableExceptionHook directive not allowed in <VirtualHost>"; |
974 | | } |
975 | | |
976 | | if (strcasecmp(arg, "on") == 0) { |
977 | | exception_hook_enabled = 1; |
978 | | } |
979 | | else if (strcasecmp(arg, "off") == 0) { |
980 | | exception_hook_enabled = 0; |
981 | | } |
982 | | else { |
983 | | return "parameter must be 'on' or 'off'"; |
984 | | } |
985 | | |
986 | | return NULL; |
987 | | } |
988 | | |
989 | | static void run_fatal_exception_hook(int sig) |
990 | | { |
991 | | ap_exception_info_t ei = {0}; |
992 | | |
993 | | if (exception_hook_enabled && |
994 | | geteuid() != 0 && |
995 | | my_pid != parent_pid) { |
996 | | ei.sig = sig; |
997 | | ei.pid = my_pid; |
998 | | ap_run_fatal_exception(&ei); |
999 | | } |
1000 | | } |
1001 | | #endif /* AP_ENABLE_EXCEPTION_HOOK */ |
1002 | | |
1003 | | /* handle all varieties of core dumping signals */ |
1004 | | static void sig_coredump(int sig) |
1005 | 0 | { |
1006 | 0 | apr_filepath_set(ap_coredump_dir, pconf); |
1007 | 0 | apr_signal(sig, SIG_DFL); |
1008 | | #if AP_ENABLE_EXCEPTION_HOOK |
1009 | | run_fatal_exception_hook(sig); |
1010 | | #endif |
1011 | | /* linuxthreads issue calling getpid() here: |
1012 | | * This comparison won't match if the crashing thread is |
1013 | | * some module's thread that runs in the parent process. |
1014 | | * The fallout, which is limited to linuxthreads: |
1015 | | * The special log message won't be written when such a |
1016 | | * thread in the parent causes the parent to crash. |
1017 | | */ |
1018 | 0 | if (getpid() == parent_pid) { |
1019 | 0 | ap_log_error(APLOG_MARK, APLOG_NOTICE, |
1020 | 0 | 0, ap_server_conf, APLOGNO(00060) |
1021 | 0 | "seg fault or similar nasty error detected " |
1022 | 0 | "in the parent process"); |
1023 | | /* XXX we can probably add some rudimentary cleanup code here, |
1024 | | * like getting rid of the pid file. If any additional bad stuff |
1025 | | * happens, we are protected from recursive errors taking down the |
1026 | | * system since this function is no longer the signal handler GLA |
1027 | | */ |
1028 | 0 | } |
1029 | 0 | kill(getpid(), sig); |
1030 | | /* At this point we've got sig blocked, because we're still inside |
1031 | | * the signal handler. When we leave the signal handler it will |
1032 | | * be unblocked, and we'll take the signal... and coredump or whatever |
1033 | | * is appropriate for this particular Unix. In addition the parent |
1034 | | * will see the real signal we received -- whereas if we called |
1035 | | * abort() here, the parent would only see SIGABRT. |
1036 | | */ |
1037 | 0 | } |
1038 | | |
1039 | | AP_DECLARE(apr_status_t) ap_fatal_signal_child_setup(server_rec *s) |
1040 | 0 | { |
1041 | 0 | my_pid = getpid(); |
1042 | 0 | return APR_SUCCESS; |
1043 | 0 | } |
1044 | | |
1045 | | /* We can't call sig_coredump (ap_log_error) once pconf is destroyed, so |
1046 | | * avoid double faults by restoring each default signal handler on cleanup. |
1047 | | */ |
1048 | | static apr_status_t fatal_signal_cleanup(void *unused) |
1049 | 0 | { |
1050 | 0 | (void)unused; |
1051 | |
|
1052 | 0 | apr_signal(SIGSEGV, SIG_DFL); |
1053 | 0 | #ifdef SIGBUS |
1054 | 0 | apr_signal(SIGBUS, SIG_DFL); |
1055 | 0 | #endif /* SIGBUS */ |
1056 | | #ifdef SIGABORT |
1057 | | apr_signal(SIGABORT, SIG_DFL); |
1058 | | #endif /* SIGABORT */ |
1059 | 0 | #ifdef SIGABRT |
1060 | 0 | apr_signal(SIGABRT, SIG_DFL); |
1061 | 0 | #endif /* SIGABRT */ |
1062 | 0 | #ifdef SIGILL |
1063 | 0 | apr_signal(SIGILL, SIG_DFL); |
1064 | 0 | #endif /* SIGILL */ |
1065 | 0 | #ifdef SIGFPE |
1066 | 0 | apr_signal(SIGFPE, SIG_DFL); |
1067 | 0 | #endif /* SIGFPE */ |
1068 | |
|
1069 | 0 | return APR_SUCCESS; |
1070 | 0 | } |
1071 | | |
1072 | | AP_DECLARE(apr_status_t) ap_fatal_signal_setup(server_rec *s, |
1073 | | apr_pool_t *in_pconf) |
1074 | 0 | { |
1075 | 0 | #ifndef NO_USE_SIGACTION |
1076 | 0 | struct sigaction sa; |
1077 | |
|
1078 | 0 | memset(&sa, 0, sizeof sa); |
1079 | 0 | sigemptyset(&sa.sa_mask); |
1080 | |
|
1081 | 0 | #if defined(SA_ONESHOT) |
1082 | 0 | sa.sa_flags = SA_ONESHOT; |
1083 | | #elif defined(SA_RESETHAND) |
1084 | | sa.sa_flags = SA_RESETHAND; |
1085 | | #endif |
1086 | |
|
1087 | 0 | sa.sa_handler = sig_coredump; |
1088 | 0 | if (sigaction(SIGSEGV, &sa, NULL) < 0) |
1089 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00061) "sigaction(SIGSEGV)"); |
1090 | 0 | #ifdef SIGBUS |
1091 | 0 | if (sigaction(SIGBUS, &sa, NULL) < 0) |
1092 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00062) "sigaction(SIGBUS)"); |
1093 | 0 | #endif |
1094 | | #ifdef SIGABORT |
1095 | | if (sigaction(SIGABORT, &sa, NULL) < 0) |
1096 | | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00063) "sigaction(SIGABORT)"); |
1097 | | #endif |
1098 | 0 | #ifdef SIGABRT |
1099 | 0 | if (sigaction(SIGABRT, &sa, NULL) < 0) |
1100 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00064) "sigaction(SIGABRT)"); |
1101 | 0 | #endif |
1102 | 0 | #ifdef SIGILL |
1103 | 0 | if (sigaction(SIGILL, &sa, NULL) < 0) |
1104 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00065) "sigaction(SIGILL)"); |
1105 | 0 | #endif |
1106 | 0 | #ifdef SIGFPE |
1107 | 0 | if (sigaction(SIGFPE, &sa, NULL) < 0) |
1108 | 0 | ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, APLOGNO(00066) "sigaction(SIGFPE)"); |
1109 | 0 | #endif |
1110 | |
|
1111 | | #else /* NO_USE_SIGACTION */ |
1112 | | |
1113 | | apr_signal(SIGSEGV, sig_coredump); |
1114 | | #ifdef SIGBUS |
1115 | | apr_signal(SIGBUS, sig_coredump); |
1116 | | #endif /* SIGBUS */ |
1117 | | #ifdef SIGABORT |
1118 | | apr_signal(SIGABORT, sig_coredump); |
1119 | | #endif /* SIGABORT */ |
1120 | | #ifdef SIGABRT |
1121 | | apr_signal(SIGABRT, sig_coredump); |
1122 | | #endif /* SIGABRT */ |
1123 | | #ifdef SIGILL |
1124 | | apr_signal(SIGILL, sig_coredump); |
1125 | | #endif /* SIGILL */ |
1126 | | #ifdef SIGFPE |
1127 | | apr_signal(SIGFPE, sig_coredump); |
1128 | | #endif /* SIGFPE */ |
1129 | | |
1130 | | #endif /* NO_USE_SIGACTION */ |
1131 | |
|
1132 | 0 | pconf = in_pconf; |
1133 | 0 | parent_pid = my_pid = getpid(); |
1134 | 0 | apr_pool_cleanup_register(pconf, NULL, fatal_signal_cleanup, |
1135 | 0 | fatal_signal_cleanup); |
1136 | |
|
1137 | 0 | return APR_SUCCESS; |
1138 | 0 | } |
1139 | | |
1140 | | #endif /* WIN32 */ |