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