]> granicus.if.org Git - apache/blob - server/mpm_common.c
Add child_status hook for tracking creation/termination of MPM child
[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_common.h"
43 #include "ap_mpm.h"
44 #include "ap_listen.h"
45 #include "util_mutex.h"
46
47 #include "scoreboard.h"
48
49 #ifdef HAVE_PWD_H
50 #include <pwd.h>
51 #endif
52 #ifdef HAVE_GRP_H
53 #include <grp.h>
54 #endif
55 #if APR_HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
58
59 APLOG_USE_MODULE(core);
60
61 #if AP_ENABLE_EXCEPTION_HOOK
62 APR_HOOK_STRUCT(
63     APR_HOOK_LINK(fatal_exception)
64     APR_HOOK_LINK(monitor)
65     APR_HOOK_LINK(drop_privileges)
66     APR_HOOK_LINK(mpm)
67     APR_HOOK_LINK(mpm_query)
68     APR_HOOK_LINK(mpm_register_timed_callback)
69     APR_HOOK_LINK(mpm_get_name)
70     APR_HOOK_LINK(end_generation)
71     APR_HOOK_LINK(child_status)
72 )
73 AP_IMPLEMENT_HOOK_RUN_ALL(int, fatal_exception,
74                           (ap_exception_info_t *ei), (ei), OK, DECLINED)
75 #else
76 APR_HOOK_STRUCT(
77     APR_HOOK_LINK(monitor)
78     APR_HOOK_LINK(drop_privileges)
79     APR_HOOK_LINK(mpm)
80     APR_HOOK_LINK(mpm_query)
81     APR_HOOK_LINK(mpm_register_timed_callback)
82     APR_HOOK_LINK(mpm_get_name)
83     APR_HOOK_LINK(end_generation)
84     APR_HOOK_LINK(child_status)
85 )
86 #endif
87 AP_IMPLEMENT_HOOK_RUN_ALL(int, monitor,
88                           (apr_pool_t *p, server_rec *s), (p, s), OK, DECLINED)
89 AP_IMPLEMENT_HOOK_RUN_ALL(int, drop_privileges,
90                           (apr_pool_t * pchild, server_rec * s),
91                           (pchild, s), OK, DECLINED)
92 AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm,
93                             (apr_pool_t *pconf, apr_pool_t *plog, server_rec *s),
94                             (pconf, plog, s), DECLINED)
95 AP_IMPLEMENT_HOOK_RUN_FIRST(int, mpm_query,
96                             (int query_code, int *result, apr_status_t *_rv),
97                             (query_code, result, _rv), DECLINED)
98 AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, mpm_register_timed_callback,
99                             (apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton),
100                             (t, cbfn, baton), APR_ENOTIMPL)
101 AP_IMPLEMENT_HOOK_RUN_FIRST(const char *, mpm_get_name,
102                             (void),
103                             (), NULL)
104 AP_IMPLEMENT_HOOK_VOID(end_generation,
105                        (server_rec *s, ap_generation_t gen),
106                        (s, gen))
107 AP_IMPLEMENT_HOOK_VOID(child_status,
108                        (server_rec *s, pid_t pid, ap_generation_t gen, int slot, mpm_child_status status),
109                        (s,pid,gen,slot,status))
110
111 typedef struct mpm_gen_info_t {
112     APR_RING_ENTRY(mpm_gen_info_t) link;
113     int gen;          /* which gen? */
114     int active;       /* number of active processes */
115 } mpm_gen_info_t;
116
117 APR_RING_HEAD(mpm_gen_info_head_t, mpm_gen_info_t);
118 static struct mpm_gen_info_head_t geninfo, unused_geninfo;
119 static int gen_head_init; /* yuck */
120
121 /* variables representing config directives implemented here */
122 const char *ap_pid_fname;
123 int ap_max_requests_per_child;
124 char ap_coredump_dir[MAX_STRING_LEN];
125 int ap_coredumpdir_configured;
126 int ap_graceful_shutdown_timeout;
127 apr_uint32_t ap_max_mem_free;
128 apr_size_t ap_thread_stacksize;
129
130 /* Set defaults for config directives implemented here.  This is
131  * called from core's pre-config hook, so MPMs which need to override
132  * one of these should run their pre-config hook after that of core.
133  */
134 void mpm_common_pre_config(apr_pool_t *pconf)
135 {
136     ap_pid_fname = DEFAULT_PIDLOG;
137     ap_max_requests_per_child = 0; /* unlimited */
138     apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
139     ap_coredumpdir_configured = 0;
140     ap_graceful_shutdown_timeout = 0; /* unlimited */
141     ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
142     ap_thread_stacksize = 0; /* use system default */
143 }
144
145 /* number of calls to wait_or_timeout between writable probes */
146 #ifndef INTERVAL_OF_WRITABLE_PROBES
147 #define INTERVAL_OF_WRITABLE_PROBES 10
148 #endif
149 static int wait_or_timeout_counter;
150
151 void ap_wait_or_timeout(apr_exit_why_e *status, int *exitcode, apr_proc_t *ret,
152                         apr_pool_t *p, server_rec *s)
153 {
154     apr_status_t rv;
155
156     ++wait_or_timeout_counter;
157     if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
158         wait_or_timeout_counter = 0;
159         ap_run_monitor(p, s);
160     }
161
162     rv = apr_proc_wait_all_procs(ret, exitcode, status, APR_NOWAIT, p);
163     if (APR_STATUS_IS_EINTR(rv)) {
164         ret->pid = -1;
165         return;
166     }
167
168     if (APR_STATUS_IS_CHILD_DONE(rv)) {
169         return;
170     }
171
172     apr_sleep(apr_time_from_sec(1));
173     ret->pid = -1;
174     return;
175 }
176
177 #if defined(TCP_NODELAY)
178 void ap_sock_disable_nagle(apr_socket_t *s)
179 {
180     /* The Nagle algorithm says that we should delay sending partial
181      * packets in hopes of getting more data.  We don't want to do
182      * this; we are not telnet.  There are bad interactions between
183      * persistent connections and Nagle's algorithm that have very severe
184      * performance penalties.  (Failing to disable Nagle is not much of a
185      * problem with simple HTTP.)
186      *
187      * In spite of these problems, failure here is not a shooting offense.
188      */
189     apr_status_t status = apr_socket_opt_set(s, APR_TCP_NODELAY, 1);
190
191     if (status != APR_SUCCESS) {
192         ap_log_error(APLOG_MARK, APLOG_WARNING, status, ap_server_conf,
193                      "apr_socket_opt_set: (TCP_NODELAY)");
194     }
195 }
196 #endif
197
198 #ifdef HAVE_GETPWNAM
199 AP_DECLARE(uid_t) ap_uname2id(const char *name)
200 {
201     struct passwd *ent;
202
203     if (name[0] == '#')
204         return (atoi(&name[1]));
205
206     if (!(ent = getpwnam(name))) {
207         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
208                      "%s: bad user name %s", ap_server_argv0, name);
209         exit(1);
210     }
211
212     return (ent->pw_uid);
213 }
214 #endif
215
216 #ifdef HAVE_GETGRNAM
217 AP_DECLARE(gid_t) ap_gname2id(const char *name)
218 {
219     struct group *ent;
220
221     if (name[0] == '#')
222         return (atoi(&name[1]));
223
224     if (!(ent = getgrnam(name))) {
225         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
226                      "%s: bad group name %s", ap_server_argv0, name);
227         exit(1);
228     }
229
230     return (ent->gr_gid);
231 }
232 #endif
233
234 #ifndef HAVE_INITGROUPS
235 int initgroups(const char *name, gid_t basegid)
236 {
237 #if defined(_OSD_POSIX) || defined(OS2) || defined(WIN32) || defined(NETWARE)
238     return 0;
239 #else
240     gid_t groups[NGROUPS_MAX];
241     struct group *g;
242     int index = 0;
243
244     setgrent();
245
246     groups[index++] = basegid;
247
248     while (index < NGROUPS_MAX && ((g = getgrent()) != NULL)) {
249         if (g->gr_gid != basegid) {
250             char **names;
251
252             for (names = g->gr_mem; *names != NULL; ++names) {
253                 if (!strcmp(*names, name))
254                     groups[index++] = g->gr_gid;
255             }
256         }
257     }
258
259     endgrent();
260
261     return setgroups(index, groups);
262 #endif
263 }
264 #endif /* def HAVE_INITGROUPS */
265
266 /* standard mpm configuration handling */
267
268 const char *ap_mpm_set_pidfile(cmd_parms *cmd, void *dummy,
269                                const char *arg)
270 {
271     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
272     if (err != NULL) {
273         return err;
274     }
275
276     if (cmd->server->is_virtual) {
277         return "PidFile directive not allowed in <VirtualHost>";
278     }
279
280     ap_pid_fname = arg;
281     return NULL;
282 }
283
284 const char *ap_mpm_set_max_requests(cmd_parms *cmd, void *dummy,
285                                     const char *arg)
286 {
287     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
288     if (err != NULL) {
289         return err;
290     }
291
292     if (!strcasecmp(cmd->cmd->name, "MaxRequestsPerChild")) {
293         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
294                      "MaxRequestsPerChild is deprecated, use "
295                      "MaxConnectionsPerChild instead.");
296     }
297
298     ap_max_requests_per_child = atoi(arg);
299
300     return NULL;
301 }
302
303 const char *ap_mpm_set_coredumpdir(cmd_parms *cmd, void *dummy,
304                                    const char *arg)
305 {
306     apr_finfo_t finfo;
307     const char *fname;
308     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
309     if (err != NULL) {
310         return err;
311     }
312
313     fname = ap_server_root_relative(cmd->pool, arg);
314     if (!fname) {
315         return apr_pstrcat(cmd->pool, "Invalid CoreDumpDirectory path ",
316                            arg, NULL);
317     }
318     if (apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool) != APR_SUCCESS) {
319         return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
320                            " does not exist", NULL);
321     }
322     if (finfo.filetype != APR_DIR) {
323         return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname,
324                            " is not a directory", NULL);
325     }
326     apr_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir));
327     ap_coredumpdir_configured = 1;
328     return NULL;
329 }
330
331 const char * ap_mpm_set_graceful_shutdown(cmd_parms *cmd, void *dummy,
332                                           const char *arg)
333 {
334     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
335     if (err != NULL) {
336         return err;
337     }
338     ap_graceful_shutdown_timeout = atoi(arg);
339     return NULL;
340 }
341
342 const char *ap_mpm_set_max_mem_free(cmd_parms *cmd, void *dummy,
343                                     const char *arg)
344 {
345     long value;
346     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
347     if (err != NULL) {
348         return err;
349     }
350
351     value = strtol(arg, NULL, 0);
352     if (value < 0 || errno == ERANGE)
353         return apr_pstrcat(cmd->pool, "Invalid MaxMemFree value: ",
354                            arg, NULL);
355
356     ap_max_mem_free = (apr_uint32_t)value * 1024;
357
358     return NULL;
359 }
360
361 const char *ap_mpm_set_thread_stacksize(cmd_parms *cmd, void *dummy,
362                                         const char *arg)
363 {
364     long value;
365     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
366     if (err != NULL) {
367         return err;
368     }
369
370     value = strtol(arg, NULL, 0);
371     if (value < 0 || errno == ERANGE)
372         return apr_pstrcat(cmd->pool, "Invalid ThreadStackSize value: ",
373                            arg, NULL);
374
375     ap_thread_stacksize = (apr_size_t)value;
376
377     return NULL;
378 }
379
380 AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
381 {
382     apr_status_t rv;
383
384     if (ap_run_mpm_query(query_code, result, &rv) == DECLINED) {
385         rv = APR_EGENERAL;
386     }
387
388     return rv;
389 }
390
391 /* core's child-status hook
392  * tracks number of remaining children per generation and
393  * runs the end-generation hook when a generation finishes
394  */
395 void ap_core_child_status(server_rec *s, pid_t pid,
396                           ap_generation_t gen, int slot,
397                           mpm_child_status status)
398 {
399     mpm_gen_info_t *cur;
400     const char *status_msg = "unknown status";
401
402     if (!gen_head_init) { /* where to run this? */
403         gen_head_init = 1;
404         APR_RING_INIT(&geninfo, mpm_gen_info_t, link);
405         APR_RING_INIT(&unused_geninfo, mpm_gen_info_t, link);
406     }
407
408     cur = APR_RING_FIRST(&geninfo);
409     while (cur != APR_RING_SENTINEL(&geninfo, mpm_gen_info_t, link) &&
410            cur->gen != gen) {
411         cur = APR_RING_NEXT(cur, link);
412     }
413
414     switch(status) {
415     case MPM_CHILD_STARTED:
416         status_msg = "started";
417         if (cur == APR_RING_SENTINEL(&geninfo, mpm_gen_info_t, link)) {
418             /* first child for this generation */
419             if (!APR_RING_EMPTY(&unused_geninfo, mpm_gen_info_t, link)) {
420                 cur = APR_RING_FIRST(&unused_geninfo);
421                 APR_RING_REMOVE(cur, link);
422             }
423             else {
424                 cur = apr_pcalloc(s->process->pool, sizeof *cur);
425             }
426             cur->gen = gen;
427             APR_RING_ELEM_INIT(cur, link);
428             APR_RING_INSERT_HEAD(&geninfo, cur, mpm_gen_info_t, link);
429         }
430         ++cur->active;
431         break;
432     case MPM_CHILD_EXITED:
433         status_msg = "exited";
434         if (cur == APR_RING_SENTINEL(&geninfo, mpm_gen_info_t, link)) {
435             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
436                          "no record of generation %d of exiting child %" APR_PID_T_FMT,
437                          gen, pid);
438         }
439         else {
440             --cur->active;
441             if (!cur->active) {
442                 ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf,
443                              "end of generation %d", gen);
444                 ap_run_end_generation(ap_server_conf, gen);
445                 APR_RING_REMOVE(cur, link);
446                 APR_RING_INSERT_HEAD(&unused_geninfo, cur, mpm_gen_info_t, link);
447             }
448         }
449         break;
450     case MPM_CHILD_LOST_SLOT:
451         status_msg = "lost slot";
452         /* we don't track by slot, so it doesn't matter */
453         break;
454     }
455     ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s,
456                  "mpm child %" APR_PID_T_FMT " (gen %d/slot %d) %s",
457                  pid, gen, slot, status_msg);
458 }
459
460 AP_DECLARE(apr_status_t) ap_mpm_register_timed_callback(apr_time_t t, ap_mpm_callback_fn_t *cbfn, void *baton)
461 {
462     return ap_run_mpm_register_timed_callback(t, cbfn, baton);
463 }
464
465 AP_DECLARE(const char *)ap_show_mpm(void)
466 {
467     const char *name = ap_run_mpm_get_name();
468
469     if (!name) {
470         name = "";
471     }
472
473     return name;
474 }
475
476 AP_DECLARE(const char *)ap_check_mpm(void)
477 {
478     static const char *last_mpm_name = NULL;
479
480     if (!_hooks.link_mpm || _hooks.link_mpm->nelts == 0)
481         return "No MPM loaded.";
482     else if (_hooks.link_mpm->nelts > 1)
483         return "More than one MPM loaded.";
484
485     if (last_mpm_name) {
486         if (strcmp(last_mpm_name, ap_show_mpm())) {
487             return "The MPM cannot be changed during restart.";
488         }
489     }
490     else {
491         last_mpm_name = apr_pstrdup(ap_pglobal, ap_show_mpm());
492     }
493
494     return NULL;
495 }