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