]> granicus.if.org Git - apache/blob - server/mpm_common.c
toss the signal description stuff from unixd.[ch], beosd.h, and spmt_os2.
[apache] / server / mpm_common.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 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
74 #include "httpd.h"
75 #include "http_config.h"
76 #include "http_log.h"
77 #include "http_main.h"
78 #include "mpm.h"
79 #include "mpm_common.h"
80
81 #ifdef HAVE_PWD_H
82 #include <pwd.h>
83 #endif
84 #ifdef HAVE_GRP_H
85 #include <grp.h>
86 #endif
87
88 #ifdef MPM_NEEDS_RECLAIM_CHILD_PROCESSES
89 void ap_reclaim_child_processes(int terminate)
90 {
91     int i;
92     long int waittime = 1024 * 16;      /* in usecs */
93     apr_status_t waitret;
94     int tries;
95     int not_dead_yet;
96     int max_daemons = ap_get_max_daemons();
97
98     MPM_SYNC_CHILD_TABLE();
99
100     for (tries = terminate ? 4 : 1; tries <= 9; ++tries) {
101         /* don't want to hold up progress any more than
102          * necessary, but we need to allow children a few moments to exit.
103          * Set delay with an exponential backoff.
104          */
105         waittime = waittime * 4;
106         apr_sleep(waittime);
107
108         /* now see who is done */
109         not_dead_yet = 0;
110         for (i = 0; i < max_daemons; ++i) {
111             pid_t pid = MPM_CHILD_PID(i);
112             apr_proc_t proc;
113
114             if (pid == 0)
115                 continue;
116
117             proc.pid = pid;
118             waitret = apr_proc_wait(&proc, APR_NOWAIT);
119             if (waitret != APR_CHILD_NOTDONE) {
120                 MPM_NOTE_CHILD_KILLED(i);
121                 continue;
122             }
123             ++not_dead_yet;
124             switch (tries) {
125             case 1:     /*  16ms */
126             case 2:     /*  82ms */
127                 break;
128             case 3:     /* 344ms */
129             case 4:     /*  16ms */
130             case 5:     /*  82ms */
131             case 6:     /* 344ms */
132             case 7:     /* 1.4sec */
133                 /* ok, now it's being annoying */
134                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING,
135                              0, ap_server_conf,
136                    "child process %ld still did not exit, sending a SIGTERM",
137                              (long)pid);
138                 kill(pid, SIGTERM);
139                 break;
140             case 8:     /*  6 sec */
141                 /* die child scum */
142                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
143                              0, ap_server_conf,
144                    "child process %ld still did not exit, sending a SIGKILL",
145                              (long)pid);
146 #ifndef BEOS
147                 kill(pid, SIGKILL);
148 #else
149                 /* sending a SIGKILL kills the entire team on BeOS, and as
150                  * httpd thread is part of that team it removes any chance
151                  * of ever doing a restart.  To counter this I'm changing to
152                  * use a kinder, gentler way of killing a specific thread
153                  * that is just as effective.
154                  */
155                 kill_thread(pid);
156 #endif
157                 break;
158             case 9:     /* 14 sec */
159                 /* gave it our best shot, but alas...  If this really
160                  * is a child we are trying to kill and it really hasn't
161                  * exited, we will likely fail to bind to the port
162                  * after the restart.
163                  */
164                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
165                              0, ap_server_conf,
166                              "could not make child process %ld exit, "
167                              "attempting to continue anyway", (long)pid);
168                 break;
169             }
170         }
171         apr_proc_other_child_check();
172         if (!not_dead_yet) {
173             /* nothing left to wait for */
174             break;
175         }
176     }
177 }
178 #endif /* NEED_RECLAIM_CHILD_PROCESSES */
179
180 /* number of calls to wait_or_timeout between writable probes */
181 #ifndef INTERVAL_OF_WRITABLE_PROBES
182 #define INTERVAL_OF_WRITABLE_PROBES 10
183 #endif
184 static int wait_or_timeout_counter;
185
186 void ap_wait_or_timeout(apr_wait_t *status, apr_proc_t *ret, apr_pool_t *p)
187 {
188     apr_status_t rv;
189
190     ++wait_or_timeout_counter;
191     if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
192         wait_or_timeout_counter = 0;
193 #if APR_HAS_OTHER_CHILD
194         apr_proc_probe_writable_fds();
195 #endif
196     }
197     rv = apr_proc_wait_all_procs(ret, status, APR_NOWAIT, p);
198     if (APR_STATUS_IS_EINTR(rv)) {
199         ret->pid = -1;
200         return;
201     }
202     if (APR_STATUS_IS_CHILD_DONE(rv)) {
203         return;
204     }
205 #ifdef NEED_WAITPID
206     if ((ret = reap_children(status)) > 0) {
207         return;
208     }
209 #endif
210     apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
211     ret->pid = -1;
212     return;
213 }
214
215 void ap_process_child_status(apr_proc_t *pid, apr_wait_t status)
216 {
217     int signum = WTERMSIG(status);
218     const char *sigdesc = apr_signal_get_description(signum);
219
220     /* Child died... if it died due to a fatal error,
221         * we should simply bail out.
222         */
223     if ((WIFEXITED(status)) &&
224         WEXITSTATUS(status) == APEXIT_CHILDFATAL) {
225         ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, 0, ap_server_conf,
226                         "Child %ld returned a Fatal error..." APR_EOL_STR
227                         "Apache is exiting!",
228                         (long)pid->pid);
229         exit(APEXIT_CHILDFATAL);
230     }
231
232     if (WIFSIGNALED(status)) {
233         switch (signum) {
234         case SIGTERM:
235         case SIGHUP:
236         case SIGWINCH:
237         case SIGKILL:
238             break;
239         default:
240 #ifdef WCOREDUMP
241             if (WCOREDUMP(status)) {
242                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
243                              0, ap_server_conf,
244                              "child pid %ld exit signal %s (%d), "
245                              "possible coredump in %s",
246                              (long)pid->pid, sigdesc, signum,
247                              ap_coredump_dir);
248             }
249             else
250 #endif
251             {
252                 ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
253                              0, ap_server_conf,
254                              "child pid %ld exit signal %s (%d)",
255                              (long)pid->pid, sigdesc, signum);
256             }
257         }
258     }
259 }
260
261 #if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF)
262 void ap_sock_disable_nagle(apr_socket_t *s)
263 {
264     /* The Nagle algorithm says that we should delay sending partial
265      * packets in hopes of getting more data.  We don't want to do
266      * this; we are not telnet.  There are bad interactions between
267      * persistent connections and Nagle's algorithm that have very severe
268      * performance penalties.  (Failing to disable Nagle is not much of a
269      * problem with simple HTTP.)
270      *
271      * In spite of these problems, failure here is not a shooting offense.
272      */
273     apr_status_t status = apr_setsocketopt(s, APR_TCP_NODELAY, 1);
274
275     if (status != APR_SUCCESS) {
276         ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf,
277                     "setsockopt: (TCP_NODELAY)");
278     }
279 }
280 #endif
281
282 AP_DECLARE(uid_t) ap_uname2id(const char *name)
283 {
284     struct passwd *ent;
285
286     if (name[0] == '#')
287         return (atoi(&name[1]));
288
289     if (!(ent = getpwnam(name))) {                                                      ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s: bad user name %s", ap_server_argv0, name);
290         exit(1);
291     }
292     return (ent->pw_uid);
293 }
294
295 AP_DECLARE(gid_t) ap_gname2id(const char *name)
296 {
297     struct group *ent;
298
299     if (name[0] == '#')
300         return (atoi(&name[1]));
301
302     if (!(ent = getgrnam(name))) {
303         ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s: bad group name %s", ap_server_argv0, name);                                               exit(1);
304     }
305     return (ent->gr_gid);
306 }
307
308 #ifndef HAVE_INITGROUPS
309 int initgroups(const char *name, gid_t basegid)
310 {
311 #if defined(QNX) || defined(MPE) || defined(BEOS) || defined(_OSD_POSIX) || defined(TPF) || defined(__TANDEM) || defined(OS2) || defined(WIN32)
312 /* QNX, MPE and BeOS do not appear to support supplementary groups. */
313     return 0;
314 #else /* ndef QNX */
315     gid_t groups[NGROUPS_MAX];
316     struct group *g;
317     int index = 0;
318
319     setgrent();
320
321     groups[index++] = basegid;
322
323     while (index < NGROUPS_MAX && ((g = getgrent()) != NULL))
324         if (g->gr_gid != basegid) {
325             char **names;
326
327             for (names = g->gr_mem; *names != NULL; ++names)
328                 if (!strcmp(*names, name))
329                     groups[index++] = g->gr_gid;
330         }
331
332     endgrent();
333
334     return setgroups(index, groups);
335 #endif /* def QNX */
336 }
337 #endif /* def NEED_INITGROUPS */
338
339