]> granicus.if.org Git - apache/blob - server/mpm_common.c
0fc9d8d3ba60d49648ef4903dd01c9a0098fe3a9
[apache] / server / mpm_common.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /* The purpose of this file is to store the code that MOST mpm's will need
60  * this does not mean a function only goes into this file if every MPM needs
61  * it.  It means that if a function is needed by more than one MPM, and
62  * future maintenance would be served by making the code common, then the
63  * function belongs here.
64  *
65  * This is going in src/main because it is not platform specific, it is
66  * specific to multi-process servers, but NOT to Unix.  Which is why it
67  * does not belong in src/os/unix
68  */
69
70 #include "apr.h"
71 #include "apr_thread_proc.h"
72 #include "apr_signal.h"
73 #include "apr_strings.h"
74 #define APR_WANT_STRFUNC
75 #include "apr_want.h"
76 #include "apr_getopt.h"
77 #include "apr_optional.h"
78 #include "apr_allocator.h"
79
80 #include "httpd.h"
81 #include "http_config.h"
82 #include "http_log.h"
83 #include "http_main.h"
84 #include "mpm.h"
85 #include "mpm_common.h"
86 #include "ap_mpm.h"
87 #include "ap_listen.h"
88 #include "mpm_default.h"
89
90 #ifdef AP_MPM_WANT_SET_SCOREBOARD
91 #include "scoreboard.h"
92 #endif
93
94 #ifdef HAVE_PWD_H
95 #include <pwd.h>
96 #endif
97 #ifdef HAVE_GRP_H
98 #include <grp.h>
99 #endif
100 #if APR_HAVE_UNISTD_H
101 #include <unistd.h>
102 #endif
103
104 #ifdef AP_MPM_WANT_RECLAIM_CHILD_PROCESSES
105 void ap_reclaim_child_processes(int terminate)
106 {
107     int i;
108     long int waittime = 1024 * 16;      /* in usecs */
109     apr_status_t waitret;
110     int tries;
111     int not_dead_yet;
112     int max_daemons;
113
114     ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_daemons);
115
116     for (tries = terminate ? 4 : 1; tries <= 9; ++tries) {
117         /* don't want to hold up progress any more than
118          * necessary, but we need to allow children a few moments to exit.
119          * Set delay with an exponential backoff.
120          */
121         apr_sleep(waittime);
122         waittime = waittime * 4;
123
124         /* now see who is done */
125         not_dead_yet = 0;
126         for (i = 0; i < max_daemons; ++i) {
127             pid_t pid = MPM_CHILD_PID(i);
128             apr_proc_t proc;
129
130             if (pid == 0)
131                 continue;
132
133             proc.pid = pid;
134             waitret = apr_proc_wait(&proc, NULL, NULL, APR_NOWAIT);
135             if (waitret != APR_CHILD_NOTDONE) {
136                 MPM_NOTE_CHILD_KILLED(i);
137                 continue;
138             }
139
140             ++not_dead_yet;
141             switch (tries) {
142             case 1:     /*  16ms */
143             case 2:     /*  82ms */
144             case 3:     /* 344ms */
145             case 4:     /*  16ms */
146                 break;
147
148             case 5:     /*  82ms */
149             case 6:     /* 344ms */
150             case 7:     /* 1.4sec */
151                 /* ok, now it's being annoying */
152                 ap_log_error(APLOG_MARK, APLOG_WARNING,
153                              0, ap_server_conf,
154                              "child process %ld still did not exit, "
155                              "sending a SIGTERM",
156                              (long)pid);
157                 kill(pid, SIGTERM);
158                 break;
159
160             case 8:     /*  6 sec */
161                 /* die child scum */
162                 ap_log_error(APLOG_MARK, APLOG_ERR,
163                              0, ap_server_conf,
164                              "child process %ld still did not exit, "
165                              "sending a SIGKILL",
166                              (long)pid);
167 #ifndef BEOS
168                 kill(pid, SIGKILL);
169 #else
170                 /* sending a SIGKILL kills the entire team on BeOS, and as
171                  * httpd thread is part of that team it removes any chance
172                  * of ever doing a restart.  To counter this I'm changing to
173                  * use a kinder, gentler way of killing a specific thread
174                  * that is just as effective.
175                  */
176                 kill_thread(pid);
177 #endif
178                 break;
179
180             case 9:     /* 14 sec */
181                 /* gave it our best shot, but alas...  If this really
182                  * is a child we are trying to kill and it really hasn't
183                  * exited, we will likely fail to bind to the port
184                  * after the restart.
185                  */
186                 ap_log_error(APLOG_MARK, APLOG_ERR,
187                              0, ap_server_conf,
188                              "could not make child process %ld exit, "
189                              "attempting to continue anyway",
190                              (long)pid);
191                 break;
192             }
193         }
194
195 #if APR_HAS_OTHER_CHILD
196         apr_proc_other_child_check();
197 #endif
198
199         if (!not_dead_yet) {
200             /* nothing left to wait for */
201             break;
202         }
203     }
204 }
205 #endif /* AP_MPM_WANT_RECLAIM_CHILD_PROCESSES */
206
207 #ifdef AP_MPM_WANT_WAIT_OR_TIMEOUT
208
209 /* number of calls to wait_or_timeout between writable probes */
210 #ifndef INTERVAL_OF_WRITABLE_PROBES
211 #define INTERVAL_OF_WRITABLE_PROBES 10
212 #endif
213 static int wait_or_timeout_counter;
214
215 void ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode, apr_proc_t *ret,
216                         apr_pool_t *p)
217 {
218     apr_status_t rv;
219
220     ++wait_or_timeout_counter;
221     if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
222         wait_or_timeout_counter = 0;
223     }
224
225     rv = apr_proc_wait_all_procs(ret, exitcode, status, APR_NOWAIT, p);
226     if (APR_STATUS_IS_EINTR(rv)) {
227         ret->pid = -1;
228         return;
229     }
230
231     if (APR_STATUS_IS_CHILD_DONE(rv)) {
232         return;
233     }
234
235 #ifdef NEED_WAITPID
236     if ((ret = reap_children(exitcode, status)) > 0) {
237         return;
238     }
239 #endif
240
241     apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
242     ret->pid = -1;
243     return;
244 }
245 #endif /* AP_MPM_WANT_WAIT_OR_TIMEOUT */
246
247 #ifdef AP_MPM_WANT_PROCESS_CHILD_STATUS
248 int ap_process_child_status(apr_proc_t *pid, apr_exit_why_e why, int status)
249 {
250     int signum = status;
251     const char *sigdesc = apr_signal_description_get(signum);
252
253     /* Child died... if it died due to a fatal error,
254      * we should simply bail out.  The caller needs to
255      * check for bad rc from us and exit, running any
256      * appropriate cleanups.
257      *
258      * If the child died due to a resource shortage, 
259      * the parent should limit the rate of forking
260      */
261     if (APR_PROC_CHECK_EXIT(why)) {
262         if (status == APEXIT_CHILDSICK) {
263             return status;
264         }
265
266         if (status == APEXIT_CHILDFATAL) {
267             ap_log_error(APLOG_MARK, APLOG_ALERT,
268                          0, ap_server_conf,
269                          "Child %" APR_PID_T_FMT
270                          " returned a Fatal error..." APR_EOL_STR
271                          "Apache is exiting!",
272                          pid->pid);
273             return APEXIT_CHILDFATAL;
274         }
275
276         return 0;
277     }
278
279     if (APR_PROC_CHECK_SIGNALED(why)) {
280         switch (signum) {
281         case SIGTERM:
282         case SIGHUP:
283         case AP_SIG_GRACEFUL:
284         case SIGKILL:
285             break;
286
287         default:
288             if (APR_PROC_CHECK_CORE_DUMP(why)) {
289                 ap_log_error(APLOG_MARK, APLOG_NOTICE,
290                              0, ap_server_conf,
291                              "child pid %ld exit signal %s (%d), "
292                              "possible coredump in %s",
293                              (long)pid->pid, sigdesc, signum,
294                              ap_coredump_dir);
295             }
296             else {
297                 ap_log_error(APLOG_MARK, APLOG_NOTICE,
298                              0, ap_server_conf,
299                              "child pid %ld exit signal %s (%d)",
300                              (long)pid->pid, sigdesc, signum);
301             }
302         }
303     }
304     return 0;
305 }
306 #endif /* AP_MPM_WANT_PROCESS_CHILD_STATUS */
307
308 #if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF)
309 void ap_sock_disable_nagle(apr_socket_t *s)
310 {
311     /* The Nagle algorithm says that we should delay sending partial
312      * packets in hopes of getting more data.  We don't want to do
313      * this; we are not telnet.  There are bad interactions between
314      * persistent connections and Nagle's algorithm that have very severe
315      * performance penalties.  (Failing to disable Nagle is not much of a
316      * problem with simple HTTP.)
317      *
318      * In spite of these problems, failure here is not a shooting offense.
319      */
320     apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1);
321
322     if (status != APR_SUCCESS) {
323         ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf,
324                      "apr_socket_opt_set: (TCP_NODELAY)");
325     }
326 }
327 #endif
328
329 #ifdef HAVE_GETPWNAM
330 AP_DECLARE(uid_t) ap_uname2id(const char *name)
331 {
332     struct passwd *ent;
333
334     if (name[0] == '#')
335         return (atoi(&name[1]));
336
337     if (!(ent = getpwnam(name))) {
338         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
339                      "%s: bad user name %s", ap_server_argv0, name);
340         exit(1);
341     }
342
343     return (ent->pw_uid);
344 }
345 #endif
346
347 #ifdef HAVE_GETGRNAM
348 AP_DECLARE(gid_t) ap_gname2id(const char *name)
349 {
350     struct group *ent;
351
352     if (name[0] == '#')
353         return (atoi(&name[1]));
354
355     if (!(ent = getgrnam(name))) {
356         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
357                      "%s: bad group name %s", ap_server_argv0, name);
358         exit(1);
359     }
360
361     return (ent->gr_gid);
362 }
363 #endif
364
365 #ifndef HAVE_INITGROUPS
366 int initgroups(const char *name, gid_t basegid)
367 {
368 #if defined(QNX) || defined(MPE) || defined(BEOS) || defined(_OSD_POSIX) || defined(TPF) || defined(__TANDEM) || defined(OS2) || defined(WIN32) || defined(NETWARE)
369 /* QNX, MPE and BeOS do not appear to support supplementary groups. */
370     return 0;
371 #else /* ndef QNX */
372     gid_t groups[NGROUPS_MAX];
373     struct group *g;
374     int index = 0;
375
376     setgrent();
377
378     groups[index++] = basegid;
379
380     while (index < NGROUPS_MAX && ((g = getgrent()) != NULL)) {
381         if (g->gr_gid != basegid) {
382             char **names;
383
384             for (names = g->gr_mem; *names != NULL; ++names) {
385                 if (!strcmp(*names, name))
386                     groups[index++] = g->gr_gid;
387             }
388         }
389     }
390
391     endgrent();
392
393     return setgroups(index, groups);
394 #endif /* def QNX */
395 }
396 #endif /* def NEED_INITGROUPS */
397
398 #ifdef AP_MPM_USES_POD
399
400 AP_DECLARE(apr_status_t) ap_mpm_pod_open(apr_pool_t *p, ap_pod_t **pod)
401 {
402     apr_status_t rv;
403
404     *pod = apr_palloc(p, sizeof(**pod));
405     rv = apr_file_pipe_create(&((*pod)->pod_in), &((*pod)->pod_out), p);
406     if (rv != APR_SUCCESS) {
407         return rv;
408     }
409
410     apr_file_pipe_timeout_set((*pod)->pod_in, 0);
411     (*pod)->p = p;
412
413     apr_sockaddr_info_get(&(*pod)->sa, ap_listeners->bind_addr->hostname,
414                           APR_UNSPEC, ap_listeners->bind_addr->port, 0, p);
415
416     /* close these before exec. */
417     apr_file_unset_inherit((*pod)->pod_in);
418     apr_file_unset_inherit((*pod)->pod_out);
419
420     return APR_SUCCESS;
421 }
422
423 AP_DECLARE(apr_status_t) ap_mpm_pod_check(ap_pod_t *pod)
424 {
425     char c;
426     apr_size_t len = 1;
427     apr_status_t rv;
428
429     rv = apr_file_read(pod->pod_in, &c, &len);
430
431     if ((rv == APR_SUCCESS) && (len == 1)) {
432         return APR_SUCCESS;
433     }
434
435     if (rv != APR_SUCCESS) {
436         return rv;
437     }
438
439     return AP_NORESTART;
440 }
441
442 AP_DECLARE(apr_status_t) ap_mpm_pod_close(ap_pod_t *pod)
443 {
444     apr_status_t rv;
445
446     rv = apr_file_close(pod->pod_out);
447     if (rv != APR_SUCCESS) {
448         return rv;
449     }
450
451     rv = apr_file_close(pod->pod_in);
452     if (rv != APR_SUCCESS) {
453         return rv;
454     }
455
456     return APR_SUCCESS;
457 }
458
459 static apr_status_t pod_signal_internal(ap_pod_t *pod)
460 {
461     apr_status_t rv;
462     char char_of_death = '!';
463     apr_size_t one = 1;
464
465     rv = apr_file_write(pod->pod_out, &char_of_death, &one);
466     if (rv != APR_SUCCESS) {
467         ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
468                      "write pipe_of_death");
469     }
470
471     return rv;
472 }
473
474 /* This function connects to the server, then immediately closes the connection.
475  * This permits the MPM to skip the poll when there is only one listening
476  * socket, because it provides a alternate way to unblock an accept() when
477  * the pod is used.
478  */
479 static apr_status_t dummy_connection(ap_pod_t *pod)
480 {
481     apr_status_t rv;
482     apr_socket_t *sock;
483     apr_pool_t *p;
484
485     /* create a temporary pool for the socket.  pconf stays around too long */
486     rv = apr_pool_create(&p, pod->p);
487     if (rv != APR_SUCCESS) {
488         return rv;
489     }
490
491     rv = apr_socket_create(&sock, pod->sa->family, SOCK_STREAM, p);
492     if (rv != APR_SUCCESS) {
493         ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
494                      "get socket to connect to listener");
495         return rv;
496     }
497
498     /* on some platforms (e.g., FreeBSD), the kernel won't accept many
499      * queued connections before it starts blocking local connects...
500      * we need to keep from blocking too long and instead return an error,
501      * because the MPM won't want to hold up a graceful restart for a
502      * long time
503      */
504     rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
505     if (rv != APR_SUCCESS) {
506         ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf,
507                      "set timeout on socket to connect to listener");
508         apr_socket_close(sock);
509         return rv;
510     }
511
512     rv = apr_connect(sock, pod->sa);
513     if (rv != APR_SUCCESS) {
514         int log_level = APLOG_WARNING;
515
516         if (APR_STATUS_IS_TIMEUP(rv)) {
517             /* probably some server processes bailed out already and there
518              * is nobody around to call accept and clear out the kernel
519              * connection queue; usually this is not worth logging
520              */
521             log_level = APLOG_DEBUG;
522         }
523
524         ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf,
525                      "connect to listener");
526     }
527
528     apr_socket_close(sock);
529     apr_pool_destroy(p);
530
531     return rv;
532 }
533
534 AP_DECLARE(apr_status_t) ap_mpm_pod_signal(ap_pod_t *pod)
535 {
536     apr_status_t rv;
537
538     rv = pod_signal_internal(pod);
539     if (rv != APR_SUCCESS) {
540         return rv;
541     }
542
543     return dummy_connection(pod);
544 }
545
546 void ap_mpm_pod_killpg(ap_pod_t *pod, int num)
547 {
548     int i;
549     apr_status_t rv = APR_SUCCESS;
550
551     /* we don't write anything to the pod here...  we assume
552      * that the would-be reader of the pod has another way to
553      * see that it is time to die once we wake it up
554      *
555      * writing lots of things to the pod at once is very
556      * problematic... we can fill the kernel pipe buffer and
557      * be blocked until somebody consumes some bytes or
558      * we hit a timeout...  if we hit a timeout we can't just
559      * keep trying because maybe we'll never successfully
560      * write again...  but then maybe we'll leave would-be
561      * readers stranded (a number of them could be tied up for
562      * a while serving time-consuming requests)
563      */
564     for (i = 0; i < num && rv == APR_SUCCESS; i++) {
565         rv = dummy_connection(pod);
566     }
567 }
568 #endif /* #ifdef AP_MPM_USES_POD */
569
570 /* standard mpm configuration handling */
571 #ifdef AP_MPM_WANT_SET_PIDFILE
572 const char *ap_pid_fname = NULL;
573
574 const char *ap_mpm_set_pidfile(cmd_parms *cmd, void *dummy,
575                                const char *arg)
576 {
577     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
578     if (err != NULL) {
579         return err;
580     }
581
582     if (cmd->server->is_virtual) {
583         return "PidFile directive not allowed in <VirtualHost>";
584     }
585
586     ap_pid_fname = arg;
587     return NULL;
588 }
589 #endif
590
591 #ifdef AP_MPM_WANT_SET_SCOREBOARD
592 const char * ap_mpm_set_scoreboard(cmd_parms *cmd, void *dummy,
593                                    const char *arg)
594 {
595     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
596     if (err != NULL) {
597         return err;
598     }
599
600     ap_scoreboard_fname = arg;
601     return NULL;
602 }
603 #endif
604
605 #ifdef AP_MPM_WANT_SET_LOCKFILE
606 const char *ap_lock_fname = NULL;
607
608 const char *ap_mpm_set_lockfile(cmd_parms *cmd, void *dummy,
609                                 const char *arg)
610 {
611     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
612     if (err != NULL) {
613         return err;
614     }
615
616     ap_lock_fname = arg;
617     return NULL;
618 }
619 #endif
620
621 #ifdef AP_MPM_WANT_SET_MAX_REQUESTS
622 int ap_max_requests_per_child = 0;
623
624 const char *ap_mpm_set_max_requests(cmd_parms *cmd, void *dummy,
625                                     const char *arg)
626 {
627     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
628     if (err != NULL) {
629         return err;
630     }
631
632     ap_max_requests_per_child = atoi(arg);
633
634     return NULL;
635 }
636 #endif
637
638 #ifdef AP_MPM_WANT_SET_COREDUMPDIR
639 char ap_coredump_dir[MAX_STRING_LEN];
640 int ap_coredumpdir_configured;
641
642 const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy,
643                                    const char *arg)
644 {
645     apr_status_t rv;
646     apr_finfo_t finfo;
647     const char *fname;
648     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
649     if (err != NULL) {
650         return err;
651     }
652
653     fname = ap_server_root_relative(cmd->pool, arg);
654     if (!fname) {
655         return apr_pstrcat(cmd->pool, "Invalid CoreDumpDirectory path ", 
656                            arg, NULL);
657     }
658     if ((rv = apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool)) != APR_SUCCESS) {
659         return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
660                            " does not exist", NULL);
661     }
662     if (finfo.filetype != APR_DIR) {
663         return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
664                            " is not a directory", NULL);
665     }
666     apr_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
667     ap_coredumpdir_configured = 1;
668     return NULL;
669 }
670 #endif
671
672 #ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
673 apr_lockmech_e ap_accept_lock_mech = APR_LOCK_DEFAULT;
674
675 const char ap_valid_accept_mutex_string[] =
676     "Valid accept mutexes for this platform and MPM are: default"
677 #if APR_HAS_FLOCK_SERIALIZE
678     ", flock"
679 #endif
680 #if APR_HAS_FCNTL_SERIALIZE
681     ", fcntl"
682 #endif
683 #if APR_HAS_SYSVSEM_SERIALIZE && !defined(PERCHILD_MPM)
684     ", sysvsem"
685 #endif
686 #if APR_HAS_POSIXSEM_SERIALIZE
687     ", posixsem"
688 #endif
689 #if APR_HAS_PROC_PTHREAD_SERIALIZE
690     ", pthread"
691 #endif
692     ".";
693
694 AP_DECLARE(const char *) ap_mpm_set_accept_lock_mech(cmd_parms *cmd,
695                                                      void *dummy,
696                                                      const char *arg)
697 {
698     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
699     if (err != NULL) {
700         return err;
701     }
702
703     if (!strcasecmp(arg, "default")) {
704         ap_accept_lock_mech = APR_LOCK_DEFAULT;
705     }
706 #if APR_HAS_FLOCK_SERIALIZE
707     else if (!strcasecmp(arg, "flock")) {
708         ap_accept_lock_mech = APR_LOCK_FLOCK;
709     }
710 #endif
711 #if APR_HAS_FCNTL_SERIALIZE
712     else if (!strcasecmp(arg, "fcntl")) {
713         ap_accept_lock_mech = APR_LOCK_FCNTL;
714     }
715 #endif
716
717     /* perchild can't use SysV sems because the permissions on the accept
718      * mutex can't be set to allow all processes to use the mutex and
719      * at the same time keep all users from being able to dink with the
720      * mutex
721      */
722 #if APR_HAS_SYSVSEM_SERIALIZE && !defined(PERCHILD_MPM)
723     else if (!strcasecmp(arg, "sysvsem")) {
724         ap_accept_lock_mech = APR_LOCK_SYSVSEM;
725     }
726 #endif
727 #if APR_HAS_POSIXSEM_SERIALIZE
728     else if (!strcasecmp(arg, "posixsem")) {
729         ap_accept_lock_mech = APR_LOCK_POSIXSEM;
730     }
731 #endif
732 #if APR_HAS_PROC_PTHREAD_SERIALIZE
733     else if (!strcasecmp(arg, "pthread")) {
734         ap_accept_lock_mech = APR_LOCK_PROC_PTHREAD;
735     }
736 #endif
737     else {
738         return apr_pstrcat(cmd->pool, arg, " is an invalid mutex mechanism; ",
739                            ap_valid_accept_mutex_string, NULL);
740     }
741     return NULL;
742 }
743
744 #endif
745
746 #ifdef AP_MPM_WANT_SIGNAL_SERVER
747
748 static const char *dash_k_arg;
749
750 static int send_signal(pid_t pid, int sig)
751 {
752     if (kill(pid, sig) < 0) {
753         ap_log_error(APLOG_MARK, APLOG_STARTUP, errno, NULL,
754                      "sending signal to server");
755         return 1;
756     }
757     return 0;
758 }
759
760 int ap_signal_server(int *exit_status, apr_pool_t *pconf)
761 {
762     apr_status_t rv;
763     pid_t otherpid;
764     int running = 0;
765     int have_pid_file = 0;
766     const char *status;
767     
768     *exit_status = 0;
769
770     rv = ap_read_pid(pconf, ap_pid_fname, &otherpid);
771     if (rv != APR_SUCCESS) {
772         if (rv != APR_ENOENT) {
773             ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, NULL,
774                          "Error retrieving pid file %s", ap_pid_fname);
775             *exit_status = 1;
776             return 1;
777         }
778         status = "httpd (no pid file) not running";
779     }
780     else {
781         have_pid_file = 1;
782         if (kill(otherpid, 0) == 0) {
783             running = 1;
784             status = apr_psprintf(pconf, 
785                                   "httpd (pid %" APR_PID_T_FMT ") already "
786                                   "running", otherpid);
787         }
788         else {
789             status = apr_psprintf(pconf,
790                                   "httpd (pid %" APR_PID_T_FMT "?) not running",
791                                   otherpid);
792         }
793     }
794
795     if (!strcmp(dash_k_arg, "start")) {
796         if (running) {
797             printf("%s\n", status);
798             return 1;
799         }
800     }
801
802     if (!strcmp(dash_k_arg, "stop")) {
803         if (!running) {
804             printf("%s\n", status);
805         }
806         else {
807             send_signal(otherpid, SIGTERM);
808         }
809         return 1;
810     }
811
812     if (!strcmp(dash_k_arg, "restart")) {
813         if (!running) {
814             printf("httpd not running, trying to start\n");
815         }
816         else {
817             *exit_status = send_signal(otherpid, SIGHUP);
818             return 1;
819         }
820     }
821
822     if (!strcmp(dash_k_arg, "graceful")) {
823         if (!running) {
824             printf("httpd not running, trying to start\n");
825         }
826         else {
827             *exit_status = send_signal(otherpid, SIGUSR1);
828             return 1;
829         }
830     }
831
832     return 0;
833 }
834
835 void ap_mpm_rewrite_args(process_rec *process)
836 {
837     apr_array_header_t *mpm_new_argv;
838     apr_status_t rv;
839     apr_getopt_t *opt;
840     char optbuf[3];
841     const char *optarg;
842     int fixed_args;
843
844     mpm_new_argv = apr_array_make(process->pool, process->argc,
845                                   sizeof(const char **));
846     *(const char **)apr_array_push(mpm_new_argv) = process->argv[0];
847     fixed_args = mpm_new_argv->nelts;
848     apr_getopt_init(&opt, process->pool, process->argc, process->argv);
849     opt->errfn = NULL;
850     optbuf[0] = '-';
851     /* option char returned by apr_getopt() will be stored in optbuf[1] */
852     optbuf[2] = '\0';
853     while ((rv = apr_getopt(opt, "k:" AP_SERVER_BASEARGS,
854                             optbuf + 1, &optarg)) == APR_SUCCESS) {
855         switch(optbuf[1]) {
856         case 'k':
857             if (!dash_k_arg) {
858                 if (!strcmp(optarg, "start") || !strcmp(optarg, "stop") ||
859                     !strcmp(optarg, "restart") || !strcmp(optarg, "graceful")) {
860                     dash_k_arg = optarg;
861                     break;
862                 }
863             }
864         default:
865             *(const char **)apr_array_push(mpm_new_argv) =
866                 apr_pstrdup(process->pool, optbuf);
867             if (optarg) {
868                 *(const char **)apr_array_push(mpm_new_argv) = optarg;
869             }
870         }
871     }
872
873     /* back up to capture the bad argument */
874     if (rv == APR_BADCH || rv == APR_BADARG) {
875         opt->ind--;
876     }
877
878     while (opt->ind < opt->argc) {
879         *(const char **)apr_array_push(mpm_new_argv) =
880             apr_pstrdup(process->pool, opt->argv[opt->ind++]);
881     }
882
883     process->argc = mpm_new_argv->nelts;
884     process->argv = (const char * const *)mpm_new_argv->elts;
885
886     if (dash_k_arg) {
887         APR_REGISTER_OPTIONAL_FN(ap_signal_server);
888     }
889 }
890
891 #endif /* AP_MPM_WANT_SIGNAL_SERVER */
892
893 #ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
894 apr_uint32_t ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
895
896 const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy,
897                                     const char *arg)
898 {
899     long value;
900     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
901     if (err != NULL) {
902         return err;
903     }
904     
905     value = strtol(arg, NULL, 0);
906     if (value < 0 || errno == ERANGE)
907         return apr_pstrcat(cmd->pool, "Invalid MaxMemFree value: ", 
908                            arg, NULL);
909
910     ap_max_mem_free = (apr_uint32_t)value * 1024;
911
912     return NULL;
913 }
914
915 #endif /* AP_MPM_WANT_SET_MAX_MEM_FREE */
916
917 #ifdef AP_MPM_WANT_FATAL_SIGNAL_HANDLER
918
919 static pid_t parent_pid, my_pid;
920 apr_pool_t *pconf;
921
922 /* handle all varieties of core dumping signals */
923 static void sig_coredump(int sig)
924 {
925     apr_filepath_set(ap_coredump_dir, pconf);
926     apr_signal(sig, SIG_DFL);
927     if (my_pid == parent_pid) {
928         ap_log_error(APLOG_MARK, APLOG_NOTICE,
929                      0, ap_server_conf,
930                      "seg fault or similar nasty error detected "
931                      "in the parent process");
932         /* XXX we can probably add some rudimentary cleanup code here,
933          * like getting rid of the pid file.  If any additional bad stuff
934          * happens, we are protected from recursive errors taking down the
935          * system since this function is no longer the signal handler   GLA
936          */
937     }
938     kill(getpid(), sig);
939     /* At this point we've got sig blocked, because we're still inside
940      * the signal handler.  When we leave the signal handler it will
941      * be unblocked, and we'll take the signal... and coredump or whatever
942      * is appropriate for this particular Unix.  In addition the parent
943      * will see the real signal we received -- whereas if we called
944      * abort() here, the parent would only see SIGABRT.
945      */
946 }
947
948 apr_status_t ap_fatal_signal_child_setup(server_rec *s)
949 {
950     my_pid = getpid();
951     return APR_SUCCESS;
952 }
953
954 apr_status_t ap_fatal_signal_setup(server_rec *s, apr_pool_t *in_pconf)
955 {
956 #ifndef NO_USE_SIGACTION
957     struct sigaction sa;
958
959     sigemptyset(&sa.sa_mask);
960     
961 #if defined(SA_ONESHOT)
962     sa.sa_flags = SA_ONESHOT;
963 #elif defined(SA_RESETHAND)
964     sa.sa_flags = SA_RESETHAND;
965 #else
966     sa.sa_flags = 0;
967 #endif
968
969     sa.sa_handler = sig_coredump;
970     if (sigaction(SIGSEGV, &sa, NULL) < 0)
971         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGSEGV)");
972 #ifdef SIGBUS
973     if (sigaction(SIGBUS, &sa, NULL) < 0)
974         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGBUS)");
975 #endif
976 #ifdef SIGABORT
977     if (sigaction(SIGABORT, &sa, NULL) < 0)
978         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABORT)");
979 #endif
980 #ifdef SIGABRT
981     if (sigaction(SIGABRT, &sa, NULL) < 0)
982         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGABRT)");
983 #endif
984 #ifdef SIGILL
985     if (sigaction(SIGILL, &sa, NULL) < 0)
986         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, s, "sigaction(SIGILL)");
987 #endif
988
989 #else /* NO_USE_SIGACTION */
990     
991     apr_signal(SIGSEGV, sig_coredump);
992 #ifdef SIGBUS
993     apr_signal(SIGBUS, sig_coredump);
994 #endif /* SIGBUS */
995 #ifdef SIGABORT
996     apr_signal(SIGABORT, sig_coredump);
997 #endif /* SIGABORT */
998 #ifdef SIGABRT
999     apr_signal(SIGABRT, sig_coredump);
1000 #endif /* SIGABRT */
1001 #ifdef SIGILL
1002     apr_signal(SIGILL, sig_coredump);
1003 #endif /* SIGILL */
1004
1005 #endif /* NO_USE_SIGACTION */
1006
1007     pconf = in_pconf;
1008     parent_pid = my_pid = getpid();
1009
1010     return APR_SUCCESS;
1011 }
1012
1013 #endif /* AP_MPM_WANT_FATAL_SIGNAL_HANDLER */