]> granicus.if.org Git - apache/blob - server/mpm_common.c
Unix MPMs: Catch SIGFPE so that exception hooks and CoreDumpDirectory
[apache] / server / mpm_common.c
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_log.h"
41 #include "http_main.h"
42 #include "mpm.h"
43 #include "mpm_common.h"
44 #include "ap_mpm.h"
45 #include "ap_listen.h"
46 #include "mpm_default.h"
47 #include "util_mutex.h"
48
49 #ifdef AP_MPM_WANT_SET_SCOREBOARD
50 #include "scoreboard.h"
51 #endif
52
53 #ifdef HAVE_PWD_H
54 #include <pwd.h>
55 #endif
56 #ifdef HAVE_GRP_H
57 #include <grp.h>
58 #endif
59 #if APR_HAVE_UNISTD_H
60 #include <unistd.h>
61 #endif
62
63 #if AP_ENABLE_EXCEPTION_HOOK
64 APR_HOOK_STRUCT(
65     APR_HOOK_LINK(fatal_exception)
66     APR_HOOK_LINK(monitor)
67 )
68 AP_IMPLEMENT_HOOK_RUN_ALL(int, fatal_exception,
69                           (ap_exception_info_t *ei), (ei), OK, DECLINED)
70 #else
71 APR_HOOK_STRUCT(
72     APR_HOOK_LINK(monitor)
73 )
74 #endif
75 AP_IMPLEMENT_HOOK_RUN_ALL(int, monitor,
76                           (apr_pool_t *p), (p), OK, DECLINED)
77
78
79 #ifdef AP_MPM_WANT_RECLAIM_CHILD_PROCESSES
80
81 typedef enum {DO_NOTHING, SEND_SIGTERM, SEND_SIGKILL, GIVEUP} action_t;
82
83 typedef struct extra_process_t {
84     struct extra_process_t *next;
85     pid_t pid;
86 } extra_process_t;
87
88 static extra_process_t *extras;
89
90 void ap_register_extra_mpm_process(pid_t pid)
91 {
92     extra_process_t *p = (extra_process_t *)malloc(sizeof(extra_process_t));
93
94     p->next = extras;
95     p->pid = pid;
96     extras = p;
97 }
98
99 int ap_unregister_extra_mpm_process(pid_t pid)
100 {
101     extra_process_t *cur = extras;
102     extra_process_t *prev = NULL;
103
104     while (cur && cur->pid != pid) {
105         prev = cur;
106         cur = cur->next;
107     }
108
109     if (cur) {
110         if (prev) {
111             prev->next = cur->next;
112         }
113         else {
114             extras = cur->next;
115         }
116         free(cur);
117         return 1; /* found */
118     }
119     else {
120         /* we don't know about any such process */
121         return 0;
122     }
123 }
124
125 static int reclaim_one_pid(pid_t pid, action_t action)
126 {
127     apr_proc_t proc;
128     apr_status_t waitret;
129
130     proc.pid = pid;
131     waitret = apr_proc_wait(&proc, NULL, NULL, APR_NOWAIT);
132     if (waitret != APR_CHILD_NOTDONE) {
133         return 1;
134     }
135
136     switch(action) {
137     case DO_NOTHING:
138         break;
139
140     case SEND_SIGTERM:
141         /* ok, now it's being annoying */
142         ap_log_error(APLOG_MARK, APLOG_WARNING,
143                      0, ap_server_conf,
144                      "child process %" APR_PID_T_FMT
145                      " still did not exit, "
146                      "sending a SIGTERM",
147                      pid);
148         kill(pid, SIGTERM);
149         break;
150
151     case SEND_SIGKILL:
152         ap_log_error(APLOG_MARK, APLOG_ERR,
153                      0, ap_server_conf,
154                      "child process %" APR_PID_T_FMT
155                      " still did not exit, "
156                      "sending a SIGKILL",
157                      pid);
158 #ifndef BEOS
159         kill(pid, SIGKILL);
160 #else
161         /* sending a SIGKILL kills the entire team on BeOS, and as
162          * httpd thread is part of that team it removes any chance
163          * of ever doing a restart.  To counter this I'm changing to
164          * use a kinder, gentler way of killing a specific thread
165          * that is just as effective.
166          */
167         kill_thread(pid);
168 #endif
169         break;
170
171     case GIVEUP:
172         /* gave it our best shot, but alas...  If this really
173          * is a child we are trying to kill and it really hasn't
174          * exited, we will likely fail to bind to the port
175          * after the restart.
176          */
177         ap_log_error(APLOG_MARK, APLOG_ERR,
178                      0, ap_server_conf,
179                      "could not make child process %" APR_PID_T_FMT
180                      " exit, "
181                      "attempting to continue anyway",
182                      pid);
183         break;
184     }
185
186     return 0;
187 }
188
189 void ap_reclaim_child_processes(int terminate)
190 {
191     apr_time_t waittime = 1024 * 16;
192     int i;
193     extra_process_t *cur_extra;
194     int not_dead_yet;
195     int max_daemons;
196     apr_time_t starttime = apr_time_now();
197     /* this table of actions and elapsed times tells what action is taken
198      * at which elapsed time from starting the reclaim
199      */
200     struct {
201         action_t action;
202         apr_time_t action_time;
203     } action_table[] = {
204         {DO_NOTHING, 0}, /* dummy entry for iterations where we reap
205                           * children but take no action against
206                           * stragglers
207                           */
208         {SEND_SIGTERM, apr_time_from_sec(3)},
209         {SEND_SIGTERM, apr_time_from_sec(5)},
210         {SEND_SIGTERM, apr_time_from_sec(7)},
211         {SEND_SIGKILL, apr_time_from_sec(9)},
212         {GIVEUP,       apr_time_from_sec(10)}
213     };
214     int cur_action;      /* index of action we decided to take this
215                           * iteration
216                           */
217     int next_action = 1; /* index of first real action */
218
219     ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons);
220
221     do {
222         apr_sleep(waittime);
223         /* don't let waittime get longer than 1 second; otherwise, we don't
224          * react quickly to the last child exiting, and taking action can
225          * be delayed
226          */
227         waittime = waittime * 4;
228         if (waittime > apr_time_from_sec(1)) {
229             waittime = apr_time_from_sec(1);
230         }
231
232         /* see what action to take, if any */
233         if (action_table[next_action].action_time <= apr_time_now() - starttime) {
234             cur_action = next_action;
235             ++next_action;
236         }
237         else {
238             cur_action = 0; /* nothing to do */
239         }
240
241         /* now see who is done */
242         not_dead_yet = 0;
243         for (i = 0; i < max_daemons; ++i) {
244             pid_t pid = MPM_CHILD_PID(i);
245
246             if (pid == 0) {
247                 continue; /* not every scoreboard entry is in use */
248             }
249
250             if (reclaim_one_pid(pid, action_table[cur_action].action)) {
251                 MPM_NOTE_CHILD_KILLED(i);
252             }
253             else {
254                 ++not_dead_yet;
255             }
256         }
257
258         cur_extra = extras;
259         while (cur_extra) {
260             extra_process_t *next = cur_extra->next;
261
262             if (reclaim_one_pid(cur_extra->pid, action_table[cur_action].action)) {
263                 AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid));
264             }
265             else {
266                 ++not_dead_yet;
267             }
268             cur_extra = next;
269         }
270 #if APR_HAS_OTHER_CHILD
271         apr_proc_other_child_refresh_all(APR_OC_REASON_RESTART);
272 #endif
273
274     } while (not_dead_yet > 0 &&
275              action_table[cur_action].action != GIVEUP);
276 }
277
278 void ap_relieve_child_processes(void)
279 {
280     int i;
281     extra_process_t *cur_extra;
282     int max_daemons;
283
284     ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons);
285
286     /* now see who is done */
287     for (i = 0; i < max_daemons; ++i) {
288         pid_t pid = MPM_CHILD_PID(i);
289
290         if (pid == 0) {
291             continue; /* not every scoreboard entry is in use */
292         }
293
294         if (reclaim_one_pid(pid, DO_NOTHING)) {
295             MPM_NOTE_CHILD_KILLED(i);
296         }
297     }
298
299     cur_extra = extras;
300     while (cur_extra) {
301         extra_process_t *next = cur_extra->next;
302
303         if (reclaim_one_pid(cur_extra->pid, DO_NOTHING)) {
304             AP_DEBUG_ASSERT(1 == ap_unregister_extra_mpm_process(cur_extra->pid));
305         }
306         cur_extra = next;
307     }
308 }
309 #endif /* AP_MPM_WANT_RECLAIM_CHILD_PROCESSES */
310
311 #ifdef AP_MPM_WANT_WAIT_OR_TIMEOUT
312
313 /* number of calls to wait_or_timeout between writable probes */
314 #ifndef INTERVAL_OF_WRITABLE_PROBES
315 #define INTERVAL_OF_WRITABLE_PROBES 10
316 #endif
317 static int wait_or_timeout_counter;
318
319 void ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode, apr_proc_t *ret,
320                         apr_pool_t *p)
321 {
322     apr_status_t rv;
323
324     ++wait_or_timeout_counter;
325     if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
326         wait_or_timeout_counter = 0;
327         ap_run_monitor(p);
328     }
329
330     rv = apr_proc_wait_all_procs(ret, exitcode, status, APR_NOWAIT, p);
331     if (APR_STATUS_IS_EINTR(rv)) {
332         ret->pid = -1;
333         return;
334     }
335
336     if (APR_STATUS_IS_CHILD_DONE(rv)) {
337         return;
338     }
339
340 #ifdef NEED_WAITPID
341     if ((ret = reap_children(exitcode, status)) > 0) {
342         return;
343     }
344 #endif
345
346     apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
347     ret->pid = -1;
348     return;
349 }
350 #endif /* AP_MPM_WANT_WAIT_OR_TIMEOUT */
351
352 #ifdef AP_MPM_WANT_PROCESS_CHILD_STATUS
353 int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status)
354 {
355     int signum = status;
356     const char *sigdesc = apr_signal_description_get(signum);
357
358     /* Child died... if it died due to a fatal error,
359      * we should simply bail out.  The caller needs to
360      * check for bad rc from us and exit, running any
361      * appropriate cleanups.
362      *
363      * If the child died due to a resource shortage,
364      * the parent should limit the rate of forking
365      */
366     if (APR_PROC_CHECK_EXIT(why)) {
367         if (status == APEXIT_CHILDSICK) {
368             return status;
369         }
370
371         if (status == APEXIT_CHILDFATAL) {
372             ap_log_error(APLOG_MARK, APLOG_ALERT,
373                          0, ap_server_conf,
374                          "Child %" APR_PID_T_FMT
375                          " returned a Fatal error... Apache is exiting!",
376                          pid->pid);
377             return APEXIT_CHILDFATAL;
378         }
379
380         return 0;
381     }
382
383     if (APR_PROC_CHECK_SIGNALED(why)) {
384         switch (signum) {
385         case SIGTERM:
386         case SIGHUP:
387         case AP_SIG_GRACEFUL:
388         case SIGKILL:
389             break;
390
391         default:
392             if (APR_PROC_CHECK_CORE_DUMP(why)) {
393                 ap_log_error(APLOG_MARK, APLOG_NOTICE,
394                              0, ap_server_conf,
395                              "child pid %ld exit signal %s (%d), "
396                              "possible coredump in %s",
397                              (long)pid->pid, sigdesc, signum,
398                              ap_coredump_dir);
399             }
400             else {
401                 ap_log_error(APLOG_MARK, APLOG_NOTICE,
402                              0, ap_server_conf,
403                              "child pid %ld exit signal %s (%d)",
404                              (long)pid->pid, sigdesc, signum);
405             }
406         }
407     }
408     return 0;
409 }
410 #endif /* AP_MPM_WANT_PROCESS_CHILD_STATUS */
411
412 #if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF)
413 void ap_sock_disable_nagle(apr_socket_t *s)
414 {
415     /* The Nagle algorithm says that we should delay sending partial
416      * packets in hopes of getting more data.  We don't want to do
417      * this; we are not telnet.  There are bad interactions between
418      * persistent connections and Nagle's algorithm that have very severe
419      * performance penalties.  (Failing to disable Nagle is not much of a
420      * problem with simple HTTP.)
421      *
422      * In spite of these problems, failure here is not a shooting offense.
423      */
424     apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1);
425
426     if (status != APR_SUCCESS) {
427         ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf,
428                      "apr_socket_opt_set: (TCP_NODELAY)");
429     }
430 }
431 #endif
432
433 #ifdef HAVE_GETPWNAM
434 AP_DECLARE(uid_t) ap_uname2id(const char *name)
435 {
436     struct passwd *ent;
437
438     if (name[0] == '#')
439         return (atoi(&name[1]));
440
441     if (!(ent = getpwnam(name))) {
442         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
443                      "%s: bad user name %s", ap_server_argv0, name);
444         exit(1);
445     }
446
447     return (ent->pw_uid);
448 }
449 #endif
450
451 #ifdef HAVE_GETGRNAM
452 AP_DECLARE(gid_t) ap_gname2id(const char *name)
453 {
454     struct group *ent;
455
456     if (name[0] == '#')
457         return (atoi(&name[1]));
458
459     if (!(ent = getgrnam(name))) {
460         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
461                      "%s: bad group name %s", ap_server_argv0, name);
462         exit(1);
463     }
464
465     return (ent->gr_gid);
466 }
467 #endif
468
469 #ifndef HAVE_INITGROUPS
470 int initgroups(const char *name, gid_t basegid)
471 {
472 #if defined(QNX) || defined(MPE) || defined(BEOS) || defined(_OSD_POSIX) || defined(TPF) || defined(__TANDEM) || defined(OS2) || defined(WIN32) || defined(NETWARE)
473 /* QNX, MPE and BeOS do not appear to support supplementary groups. */
474     return 0;
475 #else /* ndef QNX */
476     gid_t groups[NGROUPS_MAX];
477     struct group *g;
478     int index = 0;
479
480     setgrent();
481
482     groups[index++] = basegid;
483
484     while (index < NGROUPS_MAX && ((g = getgrent()) != NULL)) {
485         if (g->gr_gid != basegid) {
486             char **names;
487
488             for (names = g->gr_mem; *names != NULL; ++names) {
489                 if (!strcmp(*names, name))
490                     groups[index++] = g->gr_gid;
491             }
492         }
493     }
494
495     endgrent();
496
497     return setgroups(index, groups);
498 #endif /* def QNX */
499 }
500 #endif /* def NEED_INITGROUPS */
501
502 #ifdef AP_MPM_USES_POD
503
504 AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod)
505 {
506     apr_status_t rv;
507
508     *pod = apr_palloc(p, sizeof(**pod));
509     rv = apr_file_pipe_create(&((*pod)->pod_in), &((*pod)->pod_out), p);
510     if (rv != APR_SUCCESS) {
511         return rv;
512     }
513
514     apr_file_pipe_timeout_set((*pod)->pod_in, 0);
515     (*pod)->p = p;
516
517     /* close these before exec. */
518     apr_file_inherit_unset((*pod)->pod_in);
519     apr_file_inherit_unset((*pod)->pod_out);
520
521     return APR_SUCCESS;
522 }
523
524 AP_DECLARE(apr_status_t) ap_mpm_pod_check(ap_pod_t *pod)
525 {
526     char c;
527     apr_size_t len = 1;
528     apr_status_t rv;
529
530     rv = apr_file_read(pod->pod_in, &c, &len);
531
532     if ((rv == APR_SUCCESS) && (len == 1)) {
533         return APR_SUCCESS;
534     }
535
536     if (rv != APR_SUCCESS) {
537         return rv;
538     }
539
540     return AP_NORESTART;
541 }
542
543 AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod)
544 {
545     apr_status_t rv;
546
547     rv = apr_file_close(pod->pod_out);
548     if (rv != APR_SUCCESS) {
549         return rv;
550     }
551
552     rv = apr_file_close(pod->pod_in);
553     if (rv != APR_SUCCESS) {
554         return rv;
555     }
556
557     return APR_SUCCESS;
558 }
559
560 static apr_status_t pod_signal_internal(ap_pod_t *pod)
561 {
562     apr_status_t rv;
563     char char_of_death = '!';
564     apr_size_t one = 1;
565
566     rv = apr_file_write(pod->pod_out, &char_of_death, &one);
567     if (rv != APR_SUCCESS) {
568         ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
569                      "write pipe_of_death");
570     }
571
572     return rv;
573 }
574
575 /* This function connects to the server, then immediately closes the connection.
576  * This permits the MPM to skip the poll when there is only one listening
577  * socket, because it provides a alternate way to unblock an accept() when
578  * the pod is used.
579  */
580 static apr_status_t dummy_connection(ap_pod_t *pod)
581 {
582     char *srequest;
583     apr_status_t rv;
584     apr_socket_t *sock;
585     apr_pool_t *p;
586     apr_size_t len;
587
588     /* create a temporary pool for the socket.  pconf stays around too long */
589     rv = apr_pool_create(&p, pod->p);
590     if (rv != APR_SUCCESS) {
591         return rv;
592     }
593
594     rv = apr_socket_create(&sock, ap_listeners->bind_addr->family,
595                            SOCK_STREAM, 0, p);
596     if (rv != APR_SUCCESS) {
597         ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
598                      "get socket to connect to listener");
599         apr_pool_destroy(p);
600         return rv;
601     }
602
603     /* on some platforms (e.g., FreeBSD), the kernel won't accept many
604      * queued connections before it starts blocking local connects...
605      * we need to keep from blocking too long and instead return an error,
606      * because the MPM won't want to hold up a graceful restart for a
607      * long time
608      */
609     rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
610     if (rv != APR_SUCCESS) {
611         ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
612                      "set timeout on socket to connect to listener");
613         apr_socket_close(sock);
614         apr_pool_destroy(p);
615         return rv;
616     }
617
618     rv = apr_socket_connect(sock, ap_listeners->bind_addr);
619     if (rv != APR_SUCCESS) {
620         int log_level = APLOG_WARNING;
621
622         if (APR_STATUS_IS_TIMEUP(rv)) {
623             /* probably some server processes bailed out already and there
624              * is nobody around to call accept and clear out the kernel
625              * connection queue; usually this is not worth logging
626              */
627             log_level = APLOG_DEBUG;
628         }
629
630         ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf,
631                      "connect to listener on %pI", ap_listeners->bind_addr);
632     }
633
634     /* Create the request string. We include a User-Agent so that
635      * adminstrators can track down the cause of the odd-looking
636      * requests in their logs.
637      */
638     srequest = apr_pstrcat(p, "GET / HTTP/1.0\r\nUser-Agent: ",
639                            ap_get_server_banner(),
640                            " (internal dummy connection)\r\n\r\n", NULL);
641
642     /* Since some operating systems support buffering of data or entire
643      * requests in the kernel, we send a simple request, to make sure
644      * the server pops out of a blocking accept().
645      */
646     /* XXX: This is HTTP specific. We should look at the Protocol for each
647      * listener, and send the correct type of request to trigger any Accept
648      * Filters.
649      */
650     len = strlen(srequest);
651     apr_socket_send(sock, srequest, &len);
652     apr_socket_close(sock);
653     apr_pool_destroy(p);
654
655     return rv;
656 }
657
658 AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod)
659 {
660     apr_status_t rv;
661
662     rv = pod_signal_internal(pod);
663     if (rv != APR_SUCCESS) {
664         return rv;
665     }
666
667     return dummy_connection(pod);
668 }
669
670 void ap_mpm_pod_killpg(ap_pod_t *pod, int num)
671 {
672     int i;
673     apr_status_t rv = APR_SUCCESS;
674
675     /* we don't write anything to the pod here...  we assume
676      * that the would-be reader of the pod has another way to
677      * see that it is time to die once we wake it up
678      *
679      * writing lots of things to the pod at once is very
680      * problematic... we can fill the kernel pipe buffer and
681      * be blocked until somebody consumes some bytes or
682      * we hit a timeout...  if we hit a timeout we can't just
683      * keep trying because maybe we'll never successfully
684      * write again...  but then maybe we'll leave would-be
685      * readers stranded (a number of them could be tied up for
686      * a while serving time-consuming requests)
687      */
688     for (i = 0; i < num && rv == APR_SUCCESS; i++) {
689         rv = dummy_connection(pod);
690     }
691 }
692 #endif /* #ifdef AP_MPM_USES_POD */
693
694 /* standard mpm configuration handling */
695 #ifdef AP_MPM_WANT_SET_PIDFILE
696 const char *ap_pid_fname = NULL;
697
698 const char *ap_mpm_set_pidfile(cmd_parms *cmd, void *dummy,
699                                const char *arg)
700 {
701     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
702     if (err != NULL) {
703         return err;
704     }
705
706     if (cmd->server->is_virtual) {
707         return "PidFile directive not allowed in <VirtualHost>";
708     }
709
710     ap_pid_fname = arg;
711     return NULL;
712 }
713 #endif
714
715 #ifdef AP_MPM_WANT_SET_SCOREBOARD
716 const char * ap_mpm_set_scoreboard(cmd_parms *cmd, void *dummy,
717                                    const char *arg)
718 {
719     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
720     if (err != NULL) {
721         return err;
722     }
723
724     ap_scoreboard_fname = arg;
725     return NULL;
726 }
727 #endif
728
729 #ifdef AP_MPM_WANT_SET_LOCKFILE
730 const char *ap_lock_fname = NULL;
731
732 const char *ap_mpm_set_lockfile(cmd_parms *cmd, void *dummy,
733                                 const char *arg)
734 {
735     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
736     if (err != NULL) {
737         return err;
738     }
739
740     ap_lock_fname = arg;
741     return NULL;
742 }
743 #endif
744
745 #ifdef AP_MPM_WANT_SET_MAX_REQUESTS
746 int ap_max_requests_per_child = 0;
747
748 const char *ap_mpm_set_max_requests(cmd_parms *cmd, void *dummy,
749                                     const char *arg)
750 {
751     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
752     if (err != NULL) {
753         return err;
754     }
755
756     ap_max_requests_per_child = atoi(arg);
757
758     return NULL;
759 }
760 #endif
761
762 #ifdef AP_MPM_WANT_SET_COREDUMPDIR
763 char ap_coredump_dir[MAX_STRING_LEN];
764 int ap_coredumpdir_configured;
765
766 const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy,
767                                    const char *arg)
768 {
769     apr_status_t rv;
770     apr_finfo_t finfo;
771     const char *fname;
772     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
773     if (err != NULL) {
774         return err;
775     }
776
777     fname = ap_server_root_relative(cmd->pool, arg);
778     if (!fname) {
779         return apr_pstrcat(cmd->pool, "Invalid CoreDumpDirectory path ",
780                            arg, NULL);
781     }
782     if ((rv = apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool)) != APR_SUCCESS) {
783         return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
784                            " does not exist", NULL);
785     }
786     if (finfo.filetype != APR_DIR) {
787         return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
788                            " is not a directory", NULL);
789     }
790     apr_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
791     ap_coredumpdir_configured = 1;
792     return NULL;
793 }
794 #endif
795
796 #ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN
797 int ap_graceful_shutdown_timeout = 0;
798
799 const char * ap_mpm_set_graceful_shutdown(cmd_parms *cmd, void *dummy,
800                                           const char *arg)
801 {
802     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
803     if (err != NULL) {
804         return err;
805     }
806     ap_graceful_shutdown_timeout = atoi(arg);
807     return NULL;
808 }
809 #endif
810
811 #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
812 apr_lockmech_e ap_accept_lock_mech = APR_LOCK_DEFAULT;
813
814 AP_DECLARE(const char *) ap_mpm_set_accept_lock_mech(cmd_parms *cmd,
815                                                      void *dummy,
816                                                      const char *arg)
817 {
818     apr_status_t rv;
819     const char *lockfile;
820     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
821     if (err != NULL) {
822         return err;
823     }
824
825     rv = ap_parse_mutex(arg, cmd->server->process->pool,
826                         &ap_accept_lock_mech, &lockfile);
827
828     if ((rv == APR_ENOTIMPL) || (rv == APR_ENOLOCK)) {
829         return apr_pstrcat(cmd->pool, "Invalid AcceptMutex argument ", arg,
830                            " (", ap_available_mutexes_string, ")", NULL);
831     } else if (rv == APR_BADARG) {
832             return apr_pstrcat(cmd->pool, "Invalid AcceptMutex filepath ",
833                                arg, NULL);
834     }
835
836     /* perchild can't use SysV sems because the permissions on the accept
837      * mutex can't be set to allow all processes to use the mutex and
838      * at the same time keep all users from being able to dink with the
839      * mutex
840      */
841 #if defined(PERCHILD_MPM)
842     if (ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
843         return apr_pstrcat(cmd->pool, "Invalid AcceptMutex argument ", arg,
844                            " (", ap_available_mutexes_string, ")", NULL);
845     }
846 #endif
847     if (lockfile && !ap_lock_fname)
848         ap_lock_fname = lockfile;
849     return NULL;
850 }
851
852 #endif
853
854 #ifdef AP_MPM_WANT_SIGNAL_SERVER
855
856 static const char *dash_k_arg;
857
858 static int send_signal(pid_t pid, int sig)
859 {
860     if (kill(pid, sig) < 0) {
861         ap_log_error(APLOG_MARK, APLOG_STARTUP, errno, NULL,
862                      "sending signal to server");
863         return 1;
864     }
865     return 0;
866 }
867
868 int ap_signal_server(int *exit_status, apr_pool_t *pconf)
869 {
870     apr_status_t rv;
871     pid_t otherpid;
872     int running = 0;
873     int have_pid_file = 0;
874     const char *status;
875
876     *exit_status = 0;
877
878     rv = ap_read_pid(pconf, ap_pid_fname, &otherpid);
879     if (rv != APR_SUCCESS) {
880         if (rv != APR_ENOENT) {
881             ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, NULL,
882                          "Error retrieving pid file %s", ap_pid_fname);
883             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
884                          "Remove it before continuing if it is corrupted.");
885             *exit_status = 1;
886             return 1;
887         }
888         status = "httpd (no pid file) not running";
889     }
890     else {
891         have_pid_file = 1;
892         if (kill(otherpid, 0) == 0) {
893             running = 1;
894             status = apr_psprintf(pconf,
895                                   "httpd (pid %" APR_PID_T_FMT ") already "
896                                   "running", otherpid);
897         }
898         else {
899             status = apr_psprintf(pconf,
900                                   "httpd (pid %" APR_PID_T_FMT "?) not running",
901                                   otherpid);
902         }
903     }
904
905     if (!strcmp(dash_k_arg, "start")) {
906         if (running) {
907             printf("%s\n", status);
908             return 1;
909         }
910     }
911
912     if (!strcmp(dash_k_arg, "stop")) {
913         if (!running) {
914             printf("%s\n", status);
915         }
916         else {
917             send_signal(otherpid, SIGTERM);
918         }
919         return 1;
920     }
921
922     if (!strcmp(dash_k_arg, "restart")) {
923         if (!running) {
924             printf("httpd not running, trying to start\n");
925         }
926         else {
927             *exit_status = send_signal(otherpid, SIGHUP);
928             return 1;
929         }
930     }
931
932     if (!strcmp(dash_k_arg, "graceful")) {
933         if (!running) {
934             printf("httpd not running, trying to start\n");
935         }
936         else {
937             *exit_status = send_signal(otherpid, AP_SIG_GRACEFUL);
938             return 1;
939         }
940     }
941
942     if (!strcmp(dash_k_arg, "graceful-stop")) {
943 #ifdef AP_MPM_WANT_SET_GRACEFUL_SHUTDOWN
944         if (!running) {
945             printf("%s\n", status);
946         }
947         else {
948             *exit_status = send_signal(otherpid, AP_SIG_GRACEFUL_STOP);
949         }
950 #else
951         printf("httpd MPM \"" MPM_NAME "\" does not support graceful-stop\n");
952 #endif
953         return 1;
954     }
955
956     return 0;
957 }
958
959 void ap_mpm_rewrite_args(process_rec *process)
960 {
961     apr_array_header_t *mpm_new_argv;
962     apr_status_t rv;
963     apr_getopt_t *opt;
964     char optbuf[3];
965     const char *optarg;
966     int fixed_args;
967
968     mpm_new_argv = apr_array_make(process->pool, process->argc,
969                                   sizeof(const char **));
970     *(const char **)apr_array_push(mpm_new_argv) = process->argv[0];
971     fixed_args = mpm_new_argv->nelts;
972     apr_getopt_init(&opt, process->pool, process->argc, process->argv);
973     opt->errfn = NULL;
974     optbuf[0] = '-';
975     /* option char returned by apr_getopt() will be stored in optbuf[1] */
976     optbuf[2] = '\0';
977     while ((rv = apr_getopt(opt, "k:" AP_SERVER_BASEARGS,
978                             optbuf + 1, &optarg)) == APR_SUCCESS) {
979         switch(optbuf[1]) {
980         case 'k':
981             if (!dash_k_arg) {
982                 if (!strcmp(optarg, "start") || !strcmp(optarg, "stop") ||
983                     !strcmp(optarg, "restart") || !strcmp(optarg, "graceful") ||
984                     !strcmp(optarg, "graceful-stop")) {
985                     dash_k_arg = optarg;
986                     break;
987                 }
988             }
989         default:
990             *(const char **)apr_array_push(mpm_new_argv) =
991                 apr_pstrdup(process->pool, optbuf);
992             if (optarg) {
993                 *(const char **)apr_array_push(mpm_new_argv) = optarg;
994             }
995         }
996     }
997
998     /* back up to capture the bad argument */
999     if (rv == APR_BADCH || rv == APR_BADARG) {
1000         opt->ind--;
1001     }
1002
1003     while (opt->ind < opt->argc) {
1004         *(const char **)apr_array_push(mpm_new_argv) =
1005             apr_pstrdup(process->pool, opt->argv[opt->ind++]);
1006     }
1007
1008     process->argc = mpm_new_argv->nelts;
1009     process->argv = (const char * const *)mpm_new_argv->elts;
1010
1011     if (dash_k_arg) {
1012         APR_REGISTER_OPTIONAL_FN(ap_signal_server);
1013     }
1014 }
1015
1016 #endif /* AP_MPM_WANT_SIGNAL_SERVER */
1017
1018 #ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
1019 apr_uint32_t ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
1020
1021 const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy,
1022                                     const char *arg)
1023 {
1024     long value;
1025     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1026     if (err != NULL) {
1027         return err;
1028     }
1029
1030     value = strtol(arg, NULL, 0);
1031     if (value < 0 || errno == ERANGE)
1032         return apr_pstrcat(cmd->pool, "Invalid MaxMemFree value: ",
1033                            arg, NULL);
1034
1035     ap_max_mem_free = (apr_uint32_t)value * 1024;
1036
1037     return NULL;
1038 }
1039
1040 #endif /* AP_MPM_WANT_SET_MAX_MEM_FREE */
1041
1042 #ifdef AP_MPM_WANT_SET_STACKSIZE
1043 apr_size_t ap_thread_stacksize = 0; /* use system default */
1044
1045 const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy,
1046                                         const char *arg)
1047 {
1048     long value;
1049     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1050     if (err != NULL) {
1051         return err;
1052     }
1053
1054     value = strtol(arg, NULL, 0);
1055     if (value < 0 || errno == ERANGE)
1056         return apr_pstrcat(cmd->pool, "Invalid ThreadStackSize value: ",
1057                            arg, NULL);
1058
1059     ap_thread_stacksize = (apr_size_t)value;
1060
1061     return NULL;
1062 }
1063
1064 #endif /* AP_MPM_WANT_SET_STACKSIZE */
1065
1066 #ifdef AP_MPM_WANT_FATAL_SIGNAL_HANDLER
1067
1068 static pid_t parent_pid, my_pid;
1069 apr_pool_t *pconf;
1070
1071 #if AP_ENABLE_EXCEPTION_HOOK
1072
1073 static int exception_hook_enabled;
1074
1075 const char *ap_mpm_set_exception_hook(cmd_parms *cmd, void *dummy,
1076                                       const char *arg)
1077 {
1078     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1079     if (err != NULL) {
1080         return err;
1081     }
1082
1083     if (cmd->server->is_virtual) {
1084         return "EnableExceptionHook directive not allowed in <VirtualHost>";
1085     }
1086
1087     if (strcasecmp(arg, "on") == 0) {
1088         exception_hook_enabled = 1;
1089     }
1090     else if (strcasecmp(arg, "off") == 0) {
1091         exception_hook_enabled = 0;
1092     }
1093     else {
1094         return "parameter must be 'on' or 'off'";
1095     }
1096
1097     return NULL;
1098 }
1099
1100 static void run_fatal_exception_hook(int sig)
1101 {
1102     ap_exception_info_t ei = {0};
1103
1104     if (exception_hook_enabled &&
1105         geteuid() != 0 &&
1106         my_pid != parent_pid) {
1107         ei.sig = sig;
1108         ei.pid = my_pid;
1109         ap_run_fatal_exception(&ei);
1110     }
1111 }
1112 #endif /* AP_ENABLE_EXCEPTION_HOOK */
1113
1114 /* handle all varieties of core dumping signals */
1115 static void sig_coredump(int sig)
1116 {
1117     apr_filepath_set(ap_coredump_dir, pconf);
1118     apr_signal(sig, SIG_DFL);
1119 #if AP_ENABLE_EXCEPTION_HOOK
1120     run_fatal_exception_hook(sig);
1121 #endif
1122     /* linuxthreads issue calling getpid() here:
1123      *   This comparison won't match if the crashing thread is
1124      *   some module's thread that runs in the parent process.
1125      *   The fallout, which is limited to linuxthreads:
1126      *   The special log message won't be written when such a
1127      *   thread in the parent causes the parent to crash.
1128      */
1129     if (getpid() == parent_pid) {
1130         ap_log_error(APLOG_MARK, APLOG_NOTICE,
1131                      0, ap_server_conf,
1132                      "seg fault or similar nasty error detected "
1133                      "in the parent process");
1134         /* XXX we can probably add some rudimentary cleanup code here,
1135          * like getting rid of the pid file.  If any additional bad stuff
1136          * happens, we are protected from recursive errors taking down the
1137          * system since this function is no longer the signal handler   GLA
1138          */
1139     }
1140     kill(getpid(), sig);
1141     /* At this point we've got sig blocked, because we're still inside
1142      * the signal handler.  When we leave the signal handler it will
1143      * be unblocked, and we'll take the signal... and coredump or whatever
1144      * is appropriate for this particular Unix.  In addition the parent
1145      * will see the real signal we received -- whereas if we called
1146      * abort() here, the parent would only see SIGABRT.
1147      */
1148 }
1149
1150 apr_status_t ap_fatal_signal_child_setup(server_rec *s)
1151 {
1152     my_pid = getpid();
1153     return APR_SUCCESS;
1154 }
1155
1156 apr_status_t ap_fatal_signal_setup(server_rec *s, apr_pool_t *in_pconf)
1157 {
1158 #ifndef NO_USE_SIGACTION
1159     struct sigaction sa;
1160
1161     sigemptyset(&sa.sa_mask);
1162
1163 #if defined(SA_ONESHOT)
1164     sa.sa_flags = SA_ONESHOT;
1165 #elif defined(SA_RESETHAND)
1166     sa.sa_flags = SA_RESETHAND;
1167 #else
1168     sa.sa_flags = 0;
1169 #endif
1170
1171     sa.sa_handler = sig_coredump;
1172     if (sigaction(SIGSEGV, &sa, NULL) < 0)
1173         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGSEGV)");
1174 #ifdef SIGBUS
1175     if (sigaction(SIGBUS, &sa, NULL) < 0)
1176         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGBUS)");
1177 #endif
1178 #ifdef SIGABORT
1179     if (sigaction(SIGABORT, &sa, NULL) < 0)
1180         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABORT)");
1181 #endif
1182 #ifdef SIGABRT
1183     if (sigaction(SIGABRT, &sa, NULL) < 0)
1184         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABRT)");
1185 #endif
1186 #ifdef SIGILL
1187     if (sigaction(SIGILL, &sa, NULL) < 0)
1188         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGILL)");
1189 #endif
1190 #ifdef SIGFPE
1191     if (sigaction(SIGFPE, &sa, NULL) < 0)
1192         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGFPE)");
1193 #endif
1194
1195 #else /* NO_USE_SIGACTION */
1196
1197     apr_signal(SIGSEGV, sig_coredump);
1198 #ifdef SIGBUS
1199     apr_signal(SIGBUS, sig_coredump);
1200 #endif /* SIGBUS */
1201 #ifdef SIGABORT
1202     apr_signal(SIGABORT, sig_coredump);
1203 #endif /* SIGABORT */
1204 #ifdef SIGABRT
1205     apr_signal(SIGABRT, sig_coredump);
1206 #endif /* SIGABRT */
1207 #ifdef SIGILL
1208     apr_signal(SIGILL, sig_coredump);
1209 #endif /* SIGILL */
1210 #ifdef SIGFPE
1211     apr_signal(SIGFPE, sig_coredump);
1212 #endif /* SIGILL */
1213
1214 #endif /* NO_USE_SIGACTION */
1215
1216     pconf = in_pconf;
1217     parent_pid = my_pid = getpid();
1218
1219     return APR_SUCCESS;
1220 }
1221
1222 #endif /* AP_MPM_WANT_FATAL_SIGNAL_HANDLER */