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