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 #include "mod_perl.h"
18 
19 /* make sure that mod_perl won't try to start itself, while it's
20  * already starting. If the flag's value is 1 * it's still starting,
21  * when it's 2 it is running */
22 static int MP_init_status = 0;
23 
24 #define MP_IS_NOT_RUNNING (MP_init_status == 0 ? 1 : 0)
25 #define MP_IS_STARTING    (MP_init_status == 1 ? 1 : 0)
26 #define MP_IS_RUNNING     (MP_init_status == 2 ? 1 : 0)
27 
28 /* false while there is only the parent process and may be child
29  * processes, but no threads around, useful for allowing things that
30  * don't require locking and won't affect other threads. It should
31  * become true just before the child_init phase  */
32 static int MP_threads_started = 0;
33 
modperl_threads_started(void)34 int modperl_threads_started(void)
35 {
36     return MP_threads_started;
37 }
38 
39 static int MP_threaded_mpm = 0;
40 
modperl_threaded_mpm(void)41 int modperl_threaded_mpm(void)
42 {
43     return MP_threaded_mpm;
44 }
45 
46 /* sometimes non-threaded mpm also needs to know whether it's still
47  * starting up or after post_config) */
48 static int MP_post_post_config_phase = 0;
49 
modperl_post_post_config_phase(void)50 int modperl_post_post_config_phase(void)
51 {
52     return MP_post_post_config_phase;
53 }
54 
55 #ifndef USE_ITHREADS
modperl_shutdown(void * data)56 static apr_status_t modperl_shutdown(void *data)
57 {
58     modperl_cleanup_data_t *cdata = (modperl_cleanup_data_t *)data;
59     PerlInterpreter *perl = (PerlInterpreter *)cdata->data;
60     void **handles;
61 
62     handles = modperl_xs_dl_handles_get(aTHX);
63 
64     MP_TRACE_i(MP_FUNC, "destroying interpreter=0x%lx",
65                (unsigned long)perl);
66 
67     modperl_perl_destruct(perl);
68 
69     modperl_xs_dl_handles_close(handles);
70 
71     return APR_SUCCESS;
72 }
73 #endif
74 
75 static const char *MP_xs_loaders[] = {
76     "Apache2", "APR", NULL,
77 };
78 
79 #define MP_xs_loader_name "%s::XSLoader::BOOTSTRAP"
80 
81 /* ugly hack to have access to startup pool and server during xs_init */
82 static struct {
83     apr_pool_t *p;
84     server_rec *s;
85 } MP_boot_data = {NULL,NULL};
86 
87 #define MP_boot_data_set(pool, server) \
88     MP_boot_data.p = pool; \
89     MP_boot_data.s = server
90 
91 #define MP_dBOOT_DATA \
92     apr_pool_t *p = MP_boot_data.p; \
93     server_rec *s = MP_boot_data.s
94 
modperl_boot(pTHX_ void * data)95 static void modperl_boot(pTHX_ void *data)
96 {
97     MP_dBOOT_DATA;
98     int i;
99 
100     modperl_env_clear(aTHX);
101 
102     modperl_env_default_populate(aTHX);
103 
104     modperl_env_configure_server(aTHX_ p, s);
105 
106     modperl_perl_core_global_init(aTHX);
107 
108     for (i=0; MP_xs_loaders[i]; i++) {
109         char *name = Perl_form(aTHX_ MP_xs_loader_name, MP_xs_loaders[i]);
110         newCONSTSUB(PL_defstash, name, newSViv(1));
111     }
112 
113     /* outside mod_perl this is done by ModPerl::Const.xs */
114     newXS("ModPerl::Const::compile", XS_modperl_const_compile, __FILE__);
115 
116     /* make sure DynaLoader is loaded before XSLoader
117      * - to workaround bug in 5.6.1 that can trigger a segv
118      * when using modperl as a dso
119      * - also needed when <Perl> sections are loaded from +Parent vhost
120      */
121     modperl_require_module(aTHX_ "DynaLoader", FALSE);
122 
123     IoFLUSH_on(PL_stderrgv); /* unbuffer STDERR */
124 }
125 
modperl_xs_init(pTHX)126 static void modperl_xs_init(pTHX)
127 {
128     xs_init(aTHX); /* see modperl_xsinit.c */
129 
130     /* XXX: in 5.7.2+ we can call the body of modperl_boot here
131      * but in 5.6.1 the Perl runtime is not properly setup yet
132      * so we have to pull this stunt to delay
133      */
134     SAVEDESTRUCTOR_X(modperl_boot, 0);
135 }
136 
137 /*
138  * the "server_pool" is a subpool of the parent pool (aka "pconf")
139  * this is where we register the cleanups that teardown the interpreter.
140  * the parent process will run the cleanups since server_pool is a subpool
141  * of pconf.  we manually clear the server_pool to run cleanups in the
142  * child processes
143  *
144  * the "server_user_pool" is a subpool of the "server_pool", this is
145  * the pool which is exposed to users, so that they can register
146  * cleanup callbacks. This is needed so that the perl cleanups won't
147  * be run before user cleanups are executed.
148  *
149  */
150 static apr_pool_t *server_pool = NULL;
151 static apr_pool_t *server_user_pool = NULL;
152 
modperl_server_pool(void)153 apr_pool_t *modperl_server_pool(void)
154 {
155     return server_pool;
156 }
157 
modperl_server_user_pool(void)158 apr_pool_t *modperl_server_user_pool(void)
159 {
160     return server_user_pool;
161 }
162 
set_taint_var(PerlInterpreter * perl)163 static void set_taint_var(PerlInterpreter *perl)
164 {
165     dTHXa(perl);
166 
167 /* 5.7.3+ has a built-in special ${^TAINT}, backport it to 5.6.0+ */
168 #if MP_PERL_VERSION_AT_MOST(5, 7, 2)
169     {
170         GV *gv = gv_fetchpv("\024AINT", GV_ADDMULTI, SVt_IV);
171         sv_setiv(GvSV(gv), PL_tainting);
172         SvREADONLY_on(GvSV(gv));
173     }
174 #endif /* perl v < 5.7.3 */
175 
176 #ifdef MP_COMPAT_1X
177     {
178         GV *gv = gv_fetchpv("Apache2::__T", GV_ADDMULTI, SVt_PV);
179         sv_setiv(GvSV(gv), PL_tainting);
180         SvREADONLY_on(GvSV(gv));
181     }
182 #endif /* MP_COMPAT_1X */
183 
184 }
185 
modperl_startup(server_rec * s,apr_pool_t * p)186 PerlInterpreter *modperl_startup(server_rec *s, apr_pool_t *p)
187 {
188     AV *endav;
189     dTHXa(NULL);
190     MP_dSCFG(s);
191     PerlInterpreter *perl;
192     int status;
193     char **argv;
194     int argc;
195 #ifndef USE_ITHREADS
196     modperl_cleanup_data_t *cdata;
197 #endif
198 
199     /* ensure that we start the base server's perl, before vhost's
200      * one, if modperl_startup was called by vhost before the former
201      * was started */
202     if (MP_init_status != 2) {
203         server_rec *base_server = modperl_global_get_server_rec();
204         PerlInterpreter *base_perl;
205 
206         MP_init_status = 2; /* calls itself, so set the flag early */
207         base_perl = modperl_startup(base_server, p);
208 
209         if (base_server == s ) {
210             return base_perl;
211         }
212     }
213 
214 #ifdef MP_TRACE
215     {
216         server_rec *base_server = modperl_global_get_server_rec();
217         const char *desc = modperl_server_desc(s, p);
218         if (base_server == s) {
219             MP_init_status = 1; /* temporarily reset MP_init_status */
220             MP_TRACE_i(MP_FUNC,
221                        "starting the parent perl for the base server", desc);
222             MP_init_status = 2;
223         }
224         else {
225             MP_TRACE_i(MP_FUNC,
226                        "starting the parent perl for vhost %s", desc);
227         }
228     }
229 #endif
230 
231 #ifdef MP_USE_GTOP
232     MP_TRACE_m_do(
233         modperl_gtop_do_proc_mem_before(MP_FUNC, "perl_parse");
234     );
235 #endif
236 
237     argv = modperl_config_srv_argv_init(scfg, &argc);
238 
239     if (!(perl = perl_alloc())) {
240         perror("perl_alloc");
241         exit(1);
242     }
243 
244 #ifdef USE_ITHREADS
245     aTHX = perl;
246 #endif
247 
248     perl_construct(perl);
249 
250     modperl_hash_seed_set(aTHX);
251 
252     modperl_io_apache_init(aTHX);
253 
254     PL_perl_destruct_level = 2;
255 
256     MP_boot_data_set(p, s);
257     status = perl_parse(perl, modperl_xs_init, argc, argv, NULL);
258     MP_boot_data_set(NULL, NULL);
259 
260     if (status) {
261         perror("perl_parse");
262         exit(1);
263     }
264 
265     modperl_env_init(aTHX);
266 
267     /* suspend END blocks to be run at server shutdown */
268     endav = PL_endav;
269     PL_endav = (AV *)NULL;
270 
271 /* This was fixed in 5.9.0/5.8.1 (17775), but won't compile after 19122 */
272 #if MP_PERL_VERSION(5, 8, 0) && \
273     defined(USE_REENTRANT_API) && defined(HAS_CRYPT_R) && defined(__GLIBC__)
274     /* workaround perl5.8.0/glibc bug */
275     PL_reentrant_buffer->_crypt_struct.current_saltbits = 0;
276 #endif
277 
278     /* We need to reset $0 to argv[0] (httpd) since perl_parse() will
279      * have set it to '-e'. Being magic-aware ensures that some
280      * OS-specific magic will happen (i.e. setproctitle() on *BSDs)
281      */
282     PL_origalen = strlen(argv[0]) + 1;
283     sv_setpv_mg(get_sv("0",0), argv[0]);
284 
285     perl_run(perl);
286 
287 #ifdef USE_ITHREADS
288     /* base server / virtual host w/ +Parent gets its own mip */
289     modperl_interp_init(s, p, perl);
290 
291     /* mark the parent perl to be destroyed */
292     MpInterpBASE_On(scfg->mip->parent);
293 #endif
294 
295     PL_endav = endav;
296 
297     set_taint_var(perl);
298 
299     MP_TRACE_i(MP_FUNC, "constructed interpreter=0x%lx",
300                (unsigned long)perl);
301 
302 #ifdef MP_USE_GTOP
303     MP_TRACE_m_do(
304         modperl_gtop_do_proc_mem_after(MP_FUNC, "perl_parse");
305     );
306 #endif
307 
308 #ifdef MP_COMPAT_1X
309     {
310         char *path, *path1;
311         apr_finfo_t finfo;
312         /* 1) push @INC, $ServerRoot */
313         av_push(GvAV(PL_incgv), newSVpv(ap_server_root, 0));
314 
315         /* 2) push @INC, $ServerRoot/lib/perl only if it exists */
316         apr_filepath_merge(&path, ap_server_root, "lib",
317                            APR_FILEPATH_NATIVE, p);
318         apr_filepath_merge(&path1, path, "perl",
319                            APR_FILEPATH_NATIVE, p);
320         if (APR_SUCCESS == apr_stat(&finfo, path1, APR_FINFO_TYPE, p)) {
321             if (finfo.filetype == APR_DIR) {
322                 av_push(GvAV(PL_incgv), newSVpv(path1, 0));
323             }
324         }
325     }
326 #endif /* MP_COMPAT_1X */
327 
328     /* base perl and each vhost +Parent should have this init'ed */
329     modperl_handler_anon_init(aTHX_ p);
330 
331     if (!modperl_config_apply_PerlRequire(s, scfg, perl, p)) {
332         exit(1);
333     }
334 
335     if (!modperl_config_apply_PerlModule(s, scfg, perl, p)) {
336         exit(1);
337     }
338 
339 #ifndef USE_ITHREADS
340     cdata = modperl_cleanup_data_new(server_pool, (void*)perl);
341     apr_pool_cleanup_register(server_pool, cdata,
342                               modperl_shutdown, apr_pool_cleanup_null);
343 #endif
344 
345     return perl;
346 }
347 
modperl_init_vhost(server_rec * s,apr_pool_t * p,server_rec * base_server)348 int modperl_init_vhost(server_rec *s, apr_pool_t *p,
349                        server_rec *base_server)
350 {
351     MP_dSCFG(s);
352     modperl_config_srv_t *base_scfg;
353     PerlInterpreter *base_perl;
354     PerlInterpreter *perl;
355     const char *vhost = modperl_server_desc(s, p);
356 
357     if (!scfg) {
358         MP_TRACE_i(MP_FUNC, "server %s has no mod_perl config", vhost);
359         return OK;
360     }
361 
362     if (base_server == NULL) {
363         base_server = modperl_global_get_server_rec();
364     }
365 
366     MP_TRACE_i(MP_FUNC, "Init vhost %s: s=0x%lx, base_s=0x%lx",
367                vhost, s, base_server);
368 
369     if (base_server == s) {
370         MP_TRACE_i(MP_FUNC, "base server is not vhost, skipping %s",
371                    vhost);
372         return OK;
373     }
374 
375     base_scfg = modperl_config_srv_get(base_server);
376 
377 #ifdef USE_ITHREADS
378     perl = base_perl = base_scfg->mip->parent->perl;
379 #else
380     perl = base_perl = base_scfg->perl;
381 #endif /* USE_ITHREADS */
382 
383 #ifdef USE_ITHREADS
384 
385     if (scfg->mip) {
386         MP_TRACE_i(MP_FUNC, "server %s already initialized", vhost);
387         return OK;
388     }
389 
390     /* the base server could have mod_perl callbacks disabled, but it
391      * still needs perl to drive the vhosts */
392     if (!MpSrvENABLE(scfg) && s->is_virtual) {
393         MP_TRACE_i(MP_FUNC, "mod_perl disabled for server %s", vhost);
394         scfg->mip = NULL;
395         return OK;
396     }
397 
398     PERL_SET_CONTEXT(perl);
399     modperl_thx_interp_set(perl, base_scfg->mip->parent);
400 
401 #endif /* USE_ITHREADS */
402 
403     MP_TRACE_d_do(MpSrv_dump_flags(scfg, s->server_hostname));
404 
405     /* if alloc flags is On, virtual host gets its own parent perl */
406     if (MpSrvPARENT(scfg)) {
407         perl = modperl_startup(s, p);
408         MP_TRACE_i(MP_FUNC,
409                    "created parent interpreter for VirtualHost %s",
410                    modperl_server_desc(s, p));
411     }
412     else {
413 #ifdef USE_ITHREADS
414         /* virtual host w/ +Clone gets its own mip */
415         if (MpSrvCLONE(scfg)) {
416             modperl_interp_init(s, p, perl);
417         }
418 #endif
419 
420         if (!modperl_config_apply_PerlRequire(s, scfg, perl, p)) {
421             return HTTP_INTERNAL_SERVER_ERROR;
422         }
423 
424         if (!modperl_config_apply_PerlModule(s, scfg, perl, p)) {
425             return HTTP_INTERNAL_SERVER_ERROR;
426         }
427     }
428 
429 #ifdef USE_ITHREADS
430     if (!scfg->mip) {
431         /* since mips are created after merge_server_configs()
432          * need to point to the base mip here if this vhost
433          * doesn't have its own
434          */
435         MP_TRACE_i(MP_FUNC, "%s mip inherited from %s",
436                    vhost, modperl_server_desc(base_server, p));
437         scfg->mip = base_scfg->mip;
438     }
439 #endif  /* USE_ITHREADS */
440 
441     return OK;
442 }
443 
modperl_init(server_rec * base_server,apr_pool_t * p)444 void modperl_init(server_rec *base_server, apr_pool_t *p)
445 {
446     server_rec *s;
447     PerlInterpreter *base_perl;
448 #if defined(MP_TRACE) || defined(USE_ITHREADS)
449     modperl_config_srv_t *base_scfg = modperl_config_srv_get(base_server);
450 #endif
451 
452     MP_TRACE_d_do(MpSrv_dump_flags(base_scfg,
453                                    base_server->server_hostname));
454 
455 #ifndef USE_ITHREADS
456     if (modperl_threaded_mpm()) {
457         ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
458                      "cannot use threaded MPM without ithreads enabled Perl");
459         exit(1);
460     }
461 #endif
462 
463     base_perl = modperl_startup(base_server, p);
464 
465     for (s=base_server->next; s; s=s->next) {
466         if (modperl_init_vhost(s, p, base_server) != OK) {
467             exit(1); /*XXX*/
468         }
469     }
470 
471 #ifdef USE_ITHREADS
472     /* after other parent perls were started in vhosts, make sure that
473      * the context is set to the base_perl */
474     PERL_SET_CONTEXT(base_perl);
475     modperl_thx_interp_set(base_perl, base_scfg->mip->parent);
476 #endif
477 
478 }
479 
modperl_post_config_require(server_rec * s,apr_pool_t * p)480 static int modperl_post_config_require(server_rec *s, apr_pool_t *p)
481 {
482     for (; s; s=s->next) {
483         MP_dSCFG(s);
484         if (!modperl_config_apply_PerlPostConfigRequire(s, scfg, p)) {
485             return FALSE;
486         }
487     }
488     return TRUE;
489 }
490 
491 #ifdef USE_ITHREADS
modperl_init_clones(server_rec * s,apr_pool_t * p)492 static void modperl_init_clones(server_rec *s, apr_pool_t *p)
493 {
494 #ifdef MP_TRACE
495     modperl_config_srv_t *base_scfg = modperl_config_srv_get(s);
496     char *base_name = modperl_server_desc(s, p);
497 #endif /* MP_TRACE */
498 
499     if (!modperl_threaded_mpm()) {
500         MP_TRACE_i(MP_FUNC, "no clones created for non-threaded mpm");
501         return;
502     }
503 
504     for (; s; s=s->next) {
505         MP_dSCFG(s);
506 #ifdef MP_TRACE
507         char *name = modperl_server_desc(s, p);
508 #else
509         char *name = NULL;
510 #endif /* MP_TRACE */
511 
512         if (scfg->mip->tipool->idle) {
513 #ifdef MP_TRACE
514             if (scfg->mip == base_scfg->mip) {
515                 MP_TRACE_i(MP_FUNC,
516                            "%s interp pool inherited from %s",
517                            name, base_name);
518             }
519             else {
520                 MP_TRACE_i(MP_FUNC,
521                            "%s interp pool already initialized",
522                            name);
523             }
524 #endif /* MP_TRACE */
525         }
526         else {
527             MP_TRACE_i(MP_FUNC, "initializing interp pool for %s",
528                        name);
529             modperl_tipool_init(scfg->mip->tipool);
530         }
531     }
532 }
533 #endif /* USE_ITHREADS */
534 
modperl_init_globals(server_rec * s,apr_pool_t * pconf)535 void modperl_init_globals(server_rec *s, apr_pool_t *pconf)
536 {
537     ap_mpm_query(AP_MPMQ_IS_THREADED, &MP_threaded_mpm);
538 
539     MP_TRACE_g(MP_FUNC, "mod_perl globals are configured");
540 
541     modperl_global_init_pconf(pconf, pconf);
542     modperl_global_init_server_rec(pconf, s);
543 
544     modperl_tls_create_request_rec(pconf);
545 
546     /* init the counter to 0 */
547     modperl_global_anon_cnt_init(pconf);
548 }
549 
550 /*
551  * modperl_sys_{init,term} are things that happen
552  * once per-parent process, not per-interpreter
553  */
modperl_sys_init(void)554 static apr_status_t modperl_sys_init(void)
555 {
556     int argc = 0;
557     char **argv = NULL, **env = NULL;
558 
559     MP_TRACE_i(MP_FUNC, "mod_perl sys init");
560 
561     /* not every OS uses those vars in PERL_SYS_INIT3 macro */
562     argc = argc; argv = argv; env = env;
563 
564     PERL_SYS_INIT3(&argc, &argv, &env);
565 
566 #if 0 /*XXX*/
567 #ifdef PTHREAD_ATFORK
568     if (!ap_exists_config_define("PERL_PTHREAD_ATFORK_DONE")) {
569         PTHREAD_ATFORK(Perl_atfork_lock,
570                        Perl_atfork_unlock,
571                        Perl_atfork_unlock);
572         *(char **)apr_array_push(ap_server_config_defines) =
573             "PERL_PTHREAD_ATFORK_DONE";
574     }
575 #endif
576 #endif
577 
578     /* modifies PL_ppaddr */
579     modperl_perl_pp_set_all();
580 
581     return APR_SUCCESS;
582 }
583 
modperl_sys_term(void * data)584 static apr_status_t modperl_sys_term(void *data)
585 {
586     /* PERL_SYS_TERM() needs 'my_perl' as of 5.9.5 */
587 #if MP_PERL_VERSION_AT_LEAST(5, 9, 5) && defined(USE_ITHREADS)
588     modperl_cleanup_data_t *cdata = (modperl_cleanup_data_t *)data;
589     PERL_UNUSED_DECL PerlInterpreter *my_perl = cdata == NULL ? NULL : (PerlInterpreter *)cdata->data;
590 #endif
591     MP_init_status = 0;
592     MP_threads_started = 0;
593     MP_post_post_config_phase = 0;
594 
595     MP_PERL_FREE_THREAD_KEY_WORKAROUND;
596 
597     MP_TRACE_i(MP_FUNC, "mod_perl sys term");
598 
599     modperl_perl_pp_unset_all();
600 
601     PERL_SYS_TERM();
602 
603     return APR_SUCCESS;
604 }
605 
modperl_hook_init(apr_pool_t * pconf,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)606 int modperl_hook_init(apr_pool_t *pconf, apr_pool_t *plog,
607                       apr_pool_t *ptemp, server_rec *s)
608 {
609     if (MP_IS_STARTING || MP_IS_RUNNING) {
610         return OK;
611     }
612 
613     MP_init_status = 1; /* now starting */
614 
615     modperl_restart_count_inc(s);
616 
617     apr_pool_create(&server_pool, pconf);
618     apr_pool_tag(server_pool, "mod_perl server pool");
619 
620     apr_pool_create(&server_user_pool, pconf);
621     apr_pool_tag(server_user_pool, "mod_perl server user pool");
622 
623     modperl_sys_init();
624     apr_pool_cleanup_register(server_pool, NULL,
625                               modperl_sys_term, apr_pool_cleanup_null);
626 
627     modperl_init(s, pconf);
628 
629     return OK;
630 }
631 
632 /*
633  * if we need to init earlier than post_config,
634  * e.g. <Perl> sections or directive handlers.
635  */
modperl_run(void)636 int modperl_run(void)
637 {
638     return modperl_hook_init(modperl_global_get_pconf(),
639                              NULL,
640                              NULL,
641                              modperl_global_get_server_rec());
642 }
643 
modperl_is_running(void)644 int modperl_is_running(void)
645 {
646     return MP_IS_RUNNING;
647 }
648 
modperl_hook_pre_config(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp)649 int modperl_hook_pre_config(apr_pool_t *p, apr_pool_t *plog,
650                             apr_pool_t *ptemp)
651 {
652 #if AP_MODULE_MAGIC_AT_LEAST(20110329,0)
653     ap_reserve_module_slots_directive("PerlLoadModule");
654 #endif
655 
656     /* we can't have PerlPreConfigHandler without first configuring mod_perl */
657 
658     /* perl 5.8.1+ */
659     modperl_hash_seed_init(p);
660 
661     return OK;
662 }
663 
modperl_hook_pre_connection(conn_rec * c,void * csd)664 static int modperl_hook_pre_connection(conn_rec *c, void *csd)
665 {
666     modperl_input_filter_add_connection(c);
667     modperl_output_filter_add_connection(c);
668     return OK;
669 }
670 
modperl_hook_post_config_last(apr_pool_t * pconf,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)671 static int modperl_hook_post_config_last(apr_pool_t *pconf, apr_pool_t *plog,
672                                          apr_pool_t *ptemp, server_rec *s)
673 {
674     /* in the threaded environment, no server_rec/process_rec
675      * modifications should be done beyond this point */
676 #ifdef USE_ITHREADS
677     MP_dSCFG(s);
678     dTHXa(scfg->mip->parent->perl);
679 #endif
680 
681     if (!modperl_post_config_require(s, pconf)) {
682         exit(1);
683     }
684 
685     if (modperl_threaded_mpm()) {
686         MP_threads_started = 1;
687     }
688 
689     MP_post_post_config_phase = 1;
690 
691 #ifdef MP_TRACE
692     /* httpd core open_logs handler re-opens s->error_log, which might
693      * change, even though it still points to the same physical file
694      * (.e.g on win32 the filehandle will be different. Therefore
695      * reset the tracing logfile setting here, since this is the
696      * earliest place, happening after the open_logs phase.
697      *
698      * Moreover, we need to dup the filehandle so that when the server
699      * shuts down, we will be able to log to error_log after Apache
700      * has closed it (which happens too early for our likening).
701      */
702     {
703         apr_file_t *dup;
704         MP_RUN_CROAK(apr_file_dup(&dup, s->error_log, pconf),
705                      "mod_perl core post_config");
706         modperl_trace_logfile_set(dup);
707     }
708 #endif
709 
710 #if MP_PERL_VERSION_AT_LEAST(5, 9, 0)
711 #define MP_PERL_VERSION_STAMP "Perl/%" SVf
712 #else
713 #define MP_PERL_VERSION_STAMP "Perl/v%vd"
714 #endif
715 
716     ap_add_version_component(pconf, MP_VERSION_STRING);
717     ap_add_version_component(pconf,
718                              Perl_form(aTHX_ MP_PERL_VERSION_STAMP,
719                                        PL_patchlevel));
720 
721     modperl_mgv_hash_handlers(pconf, s);
722     modperl_modglobal_hash_keys(aTHX);
723     modperl_env_hash_keys(aTHX);
724 #ifdef USE_ITHREADS
725     modperl_init_clones(s, pconf);
726 #endif
727 
728 #ifdef MP_NEED_HASH_SEED_FIXUP
729     ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
730                  "mod_perl: using Perl HASH_SEED: %"UVuf, MP_init_hash_seed);
731 #endif
732 
733     return OK;
734 }
735 
modperl_hook_create_request(request_rec * r)736 static int modperl_hook_create_request(request_rec *r)
737 {
738     MP_dRCFG;
739 
740 #ifdef USE_ITHREADS
741     /* XXX: this is necessary to make modperl_interp_pool_select() work
742      * which is used at runtime only to merge dir-configs by
743      * modperl_module_config_merge().
744      *
745      * Since most requests won't need it it would be good to add some logic
746      * (cheaper logic in terms of CPU cycles) to identify those cases and
747      * avoid the hash operation.
748      */
749     MP_TRACE_i(MP_FUNC, "setting userdata MODPERL_R in pool %#lx to %lx",
750                (unsigned long)r->pool, (unsigned long)r);
751     (void)apr_pool_userdata_set((void *)r, "MODPERL_R", NULL, r->pool);
752 #endif
753 
754     modperl_config_req_init(r, rcfg);
755     modperl_config_req_cleanup_register(r, rcfg);
756 
757     /* set the default for cgi header parsing On as early as possible
758      * so $r->content_type in any phase after header_parser could turn
759      * it off. wb->header_parse will be set to 1 only if this flag
760      * wasn't turned off and MpDirPARSE_HEADERS is on
761      */
762     MpReqPARSE_HEADERS_On(rcfg);
763 
764     return OK;
765 }
766 
modperl_hook_post_read_request(request_rec * r)767 static int modperl_hook_post_read_request(request_rec *r)
768 {
769 #ifdef USE_ITHREADS
770     MP_TRACE_i(MP_FUNC, "%s %s:%d%s",
771                r->method, r->connection->local_addr->hostname,
772                r->connection->local_addr->port, r->unparsed_uri);
773 #endif
774 
775     /* if 'PerlOptions +GlobalRequest' is outside a container */
776     modperl_global_request_cfg_set(r);
777 
778     return OK;
779 }
780 
modperl_hook_header_parser(request_rec * r)781 static int modperl_hook_header_parser(request_rec *r)
782 {
783     /* if 'PerlOptions +GlobalRequest' is inside a container */
784     modperl_global_request_cfg_set(r);
785 
786     return OK;
787 }
788 
789 static int modperl_destruct_level = 2; /* default is full tear down */
790 
modperl_perl_destruct_level(void)791 int modperl_perl_destruct_level(void)
792 {
793     return modperl_destruct_level;
794 }
795 
796 #ifdef USE_ITHREADS
797 
798 static apr_status_t
modperl_perl_call_endav_mip(pTHX_ modperl_interp_pool_t * mip,void * data)799 modperl_perl_call_endav_mip(pTHX_ modperl_interp_pool_t *mip,
800                             void *data)
801 {
802     modperl_perl_call_endav(aTHX);
803     return APR_SUCCESS;
804 }
805 
806 #endif /* USE_ITHREADS */
807 
modperl_child_exit(void * data)808 static apr_status_t modperl_child_exit(void *data)
809 {
810     char *level = NULL;
811     server_rec *s = (server_rec *)data;
812 
813     modperl_callback_process(MP_CHILD_EXIT_HANDLER, server_pool, s,
814                              MP_HOOK_VOID);
815 
816     if ((level = getenv("PERL_DESTRUCT_LEVEL"))) {
817         modperl_destruct_level = atoi(level);
818     }
819     else {
820         /* default to no teardown in the children */
821         modperl_destruct_level = 0;
822     }
823 
824     if (modperl_destruct_level) {
825         apr_pool_clear(server_pool);
826     }
827     else {
828         /* run the END blocks of this child process if
829          * modperl_perl_destruct is not called for this process */
830 #ifdef USE_ITHREADS
831         modperl_interp_mip_walk_servers(NULL, s,
832                                         modperl_perl_call_endav_mip,
833                                         (void*)NULL);
834 #else
835         modperl_perl_call_endav(aTHX);
836 #endif
837     }
838 
839     server_pool = NULL;
840 
841     return APR_SUCCESS;
842 }
843 
modperl_hook_child_init(apr_pool_t * p,server_rec * s)844 static void modperl_hook_child_init(apr_pool_t *p, server_rec *s)
845 {
846     modperl_perl_init_ids_server(s);
847 
848     apr_pool_cleanup_register(p, (void *)s, modperl_child_exit,
849                               apr_pool_cleanup_null);
850 }
851 
852 #define MP_FILTER_HANDLER(f) f, NULL
853 
modperl_register_hooks(apr_pool_t * p)854 void modperl_register_hooks(apr_pool_t *p)
855 {
856 
857 #ifdef USE_ITHREADS
858     APR_REGISTER_OPTIONAL_FN(modperl_interp_unselect);
859     APR_REGISTER_OPTIONAL_FN(modperl_thx_interp_get);
860 #endif
861 
862     /* for <IfDefine MODPERL2> and Apache2->define("MODPERL2") */
863     *(char **)apr_array_push(ap_server_config_defines) =
864         apr_pstrdup(ap_server_config_defines->pool, "MODPERL2");
865 
866     ap_hook_pre_config(modperl_hook_pre_config,
867                        NULL, NULL, APR_HOOK_MIDDLE);
868 
869     ap_hook_open_logs(modperl_hook_init,
870                       NULL, NULL, APR_HOOK_FIRST);
871 
872     ap_hook_post_config(modperl_hook_post_config_last,
873                         NULL, NULL, APR_HOOK_REALLY_LAST);
874 
875     ap_hook_handler(modperl_response_handler,
876                     NULL, NULL, APR_HOOK_MIDDLE);
877 
878     ap_hook_handler(modperl_response_handler_cgi,
879                     NULL, NULL, APR_HOOK_MIDDLE);
880 
881     ap_hook_insert_filter(modperl_output_filter_add_request,
882                           NULL, NULL, APR_HOOK_LAST);
883 
884     ap_hook_insert_filter(modperl_input_filter_add_request,
885                           NULL, NULL, APR_HOOK_LAST);
886 
887     ap_register_output_filter(MP_FILTER_REQUEST_OUTPUT_NAME,
888                               MP_FILTER_HANDLER(modperl_output_filter_handler),
889                               AP_FTYPE_RESOURCE);
890 
891     ap_register_input_filter(MP_FILTER_REQUEST_INPUT_NAME,
892                              MP_FILTER_HANDLER(modperl_input_filter_handler),
893                              AP_FTYPE_RESOURCE);
894 
895     ap_register_output_filter(MP_FILTER_CONNECTION_OUTPUT_NAME,
896                               MP_FILTER_HANDLER(modperl_output_filter_handler),
897                               AP_FTYPE_CONNECTION);
898 
899     ap_register_input_filter(MP_FILTER_CONNECTION_INPUT_NAME,
900                              MP_FILTER_HANDLER(modperl_input_filter_handler),
901                              AP_FTYPE_CONNECTION);
902 
903     ap_hook_pre_connection(modperl_hook_pre_connection,
904                            NULL, NULL, APR_HOOK_FIRST);
905 
906     ap_hook_create_request(modperl_hook_create_request,
907                            NULL, NULL, APR_HOOK_MIDDLE);
908 
909     /* both of these hooks need to run really, really first.
910      * otherwise, the global request_rec will be set up _after_ some
911      * Perl handlers run.
912      */
913     ap_hook_post_read_request(modperl_hook_post_read_request,
914                               NULL, NULL, MODPERL_HOOK_REALLY_REALLY_FIRST);
915 
916     ap_hook_header_parser(modperl_hook_header_parser,
917                           NULL, NULL, MODPERL_HOOK_REALLY_REALLY_FIRST);
918 
919     ap_hook_child_init(modperl_hook_child_init,
920                        NULL, NULL, MODPERL_HOOK_REALLY_REALLY_FIRST);
921 
922     modperl_register_handler_hooks();
923 }
924 
925 static const command_rec modperl_cmds[] = {
926     MP_CMD_SRV_ITERATE("PerlSwitches", switches, "Perl Switches"),
927     MP_CMD_DIR_ITERATE("PerlModule", modules, "PerlModule"),
928     MP_CMD_DIR_ITERATE("PerlRequire", requires, "PerlRequire"),
929     MP_CMD_SRV_ITERATE("PerlConfigRequire", config_requires, "PerlConfigRequire"),
930     MP_CMD_SRV_ITERATE("PerlPostConfigRequire", post_config_requires, "PerlPostConfigRequire"),
931 #if AP_SERVER_MAJORVERSION_NUMBER>2 || \
932     (AP_SERVER_MAJORVERSION_NUMBER == 2 && AP_SERVER_MINORVERSION_NUMBER>=3)
933     MP_CMD_SRV_TAKE2("PerlAddAuthzProvider", authz_provider, "PerlAddAuthzProvider"),
934     MP_CMD_SRV_TAKE2("PerlAddAuthnProvider", authn_provider, "PerlAddAuthnProvider"),
935 #endif
936     MP_CMD_DIR_ITERATE("PerlOptions", options, "Perl Options"),
937     MP_CMD_DIR_ITERATE("PerlInitHandler", init_handlers, "Subroutine name"),
938     MP_CMD_DIR_TAKE2("PerlSetVar", set_var, "PerlSetVar"),
939     MP_CMD_DIR_ITERATE2("PerlAddVar", add_var, "PerlAddVar"),
940     MP_CMD_DIR_TAKE2("PerlSetEnv", set_env, "PerlSetEnv"),
941     MP_CMD_SRV_TAKE1("PerlPassEnv", pass_env, "PerlPassEnv"),
942     MP_CMD_SRV_RAW_ARGS_ON_READ("<Perl", perl, "Perl Code"),
943     MP_CMD_SRV_RAW_ARGS("Perl", perldo, "Perl Code"),
944 
945     MP_CMD_DIR_TAKE1("PerlSetInputFilter", set_input_filter,
946                      "filter[;filter]"),
947     MP_CMD_DIR_TAKE1("PerlSetOutputFilter", set_output_filter,
948                      "filter[;filter]"),
949 
950     MP_CMD_SRV_RAW_ARGS_ON_READ("=pod", pod, "Start of POD"),
951     MP_CMD_SRV_RAW_ARGS_ON_READ("=back", pod, "End of =over"),
952     MP_CMD_SRV_RAW_ARGS_ON_READ("=cut", pod_cut, "End of POD"),
953     MP_CMD_SRV_RAW_ARGS_ON_READ("__END__", END, "Stop reading config"),
954 
955     MP_CMD_SRV_RAW_ARGS("PerlLoadModule", load_module, "A Perl module"),
956 #ifdef MP_TRACE
957     MP_CMD_SRV_TAKE1("PerlTrace", trace, "Trace level"),
958 #endif
959 #ifdef USE_ITHREADS
960     MP_CMD_SRV_TAKE1("PerlInterpStart", interp_start,
961                      "Number of Perl interpreters to start"),
962     MP_CMD_SRV_TAKE1("PerlInterpMax", interp_max,
963                      "Max number of running Perl interpreters"),
964     MP_CMD_SRV_TAKE1("PerlInterpMaxSpare", interp_max_spare,
965                      "Max number of spare Perl interpreters"),
966     MP_CMD_SRV_TAKE1("PerlInterpMinSpare", interp_min_spare,
967                      "Min number of spare Perl interpreters"),
968     MP_CMD_SRV_TAKE1("PerlInterpMaxRequests", interp_max_requests,
969                      "Max number of requests per Perl interpreters"),
970 #endif
971 #ifdef MP_COMPAT_1X
972     MP_CMD_DIR_FLAG("PerlSendHeader", send_header,
973                     "Tell mod_perl to scan output for HTTP headers"),
974     MP_CMD_DIR_FLAG("PerlSetupEnv", setup_env,
975                     "Turn setup of %ENV On or Off"),
976     MP_CMD_DIR_ITERATE("PerlHandler", response_handlers,
977                        "Subroutine name"),
978     MP_CMD_SRV_FLAG("PerlTaintCheck", taint_check,
979                     "Turn on -T switch"),
980     MP_CMD_SRV_FLAG("PerlWarn", warn,
981                     "Turn on -w switch"),
982 #endif
983     MP_CMD_ENTRIES,
984     { NULL },
985 };
986 
modperl_response_init(request_rec * r)987 void modperl_response_init(request_rec *r)
988 {
989     MP_dRCFG;
990     MP_dDCFG;
991     modperl_wbucket_t *wb;
992 
993     if (!rcfg->wbucket) {
994         rcfg->wbucket =
995             (modperl_wbucket_t *)apr_palloc(r->pool,
996                                             sizeof(*rcfg->wbucket));
997     }
998 
999     wb = rcfg->wbucket;
1000 
1001     /* setup buffer for output */
1002     wb->pool = r->pool;
1003     wb->filters = &r->output_filters;
1004     wb->outcnt = 0;
1005     wb->header_parse = MpDirPARSE_HEADERS(dcfg) && MpReqPARSE_HEADERS(rcfg)
1006         ? 1 : 0;
1007     wb->r = r;
1008 }
1009 
modperl_response_finish(request_rec * r)1010 apr_status_t modperl_response_finish(request_rec *r)
1011 {
1012     MP_dRCFG;
1013 
1014     /* flush output buffer */
1015     return modperl_wbucket_flush(rcfg->wbucket, FALSE);
1016 }
1017 
modperl_response_handler_run(request_rec * r)1018 static int modperl_response_handler_run(request_rec *r)
1019 {
1020     int retval;
1021 
1022     modperl_response_init(r);
1023 
1024     retval = modperl_callback_per_dir(MP_RESPONSE_HANDLER, r, MP_HOOK_RUN_FIRST);
1025 
1026     if ((retval == DECLINED) && r->content_type) {
1027         r->handler = r->content_type; /* let http_core or whatever try */
1028     }
1029 
1030     return retval;
1031 }
1032 
modperl_response_handler(request_rec * r)1033 int modperl_response_handler(request_rec *r)
1034 {
1035     MP_dDCFG;
1036     apr_status_t retval, rc;
1037     MP_dINTERP;
1038 
1039     if (!strEQ(r->handler, "modperl")) {
1040         return DECLINED;
1041     }
1042 
1043     MP_INTERPa(r, r->connection, r->server);
1044 
1045     /* default is -SetupEnv, add if PerlOption +SetupEnv */
1046     if (MpDirSETUP_ENV(dcfg)) {
1047         modperl_env_request_populate(aTHX_ r);
1048     }
1049 
1050     retval = modperl_response_handler_run(r);
1051     rc = modperl_response_finish(r);
1052     if (rc != APR_SUCCESS) {
1053         retval = rc;
1054     }
1055 
1056     MP_INTERP_PUTBACK(interp, aTHX);
1057 
1058     return retval;
1059 }
1060 
modperl_response_handler_cgi(request_rec * r)1061 int modperl_response_handler_cgi(request_rec *r)
1062 {
1063     MP_dDCFG;
1064     GV *h_stdin, *h_stdout;
1065     apr_status_t retval, rc;
1066     MP_dRCFG;
1067     MP_dINTERP;
1068 
1069     if (!strEQ(r->handler, "perl-script")) {
1070         return DECLINED;
1071     }
1072 
1073     MP_INTERPa(r, r->connection, r->server);
1074 
1075     modperl_perl_global_request_save(aTHX_ r);
1076 
1077     /* default is +SetupEnv, skip if PerlOption -SetupEnv */
1078     if (MpDirSETUP_ENV(dcfg) || !MpDirSeenSETUP_ENV(dcfg)) {
1079         modperl_env_request_populate(aTHX_ r);
1080     }
1081 
1082     /* default is +GlobalRequest, skip if PerlOption -GlobalRequest */
1083     if (MpDirGLOBAL_REQUEST(dcfg) || !MpDirSeenGLOBAL_REQUEST(dcfg)) {
1084         modperl_global_request_set(r);
1085     }
1086 
1087     /* need to create a block around the IO setup so the temp vars
1088      * will be automatically cleaned up when we are done with IO */
1089     ENTER;SAVETMPS;
1090     h_stdin  = modperl_io_override_stdin(aTHX_ r);
1091     h_stdout = modperl_io_override_stdout(aTHX_ r);
1092 
1093     modperl_env_request_tie(aTHX_ r);
1094 
1095     retval = modperl_response_handler_run(r);
1096 
1097     modperl_env_request_untie(aTHX_ r);
1098 
1099     modperl_perl_global_request_restore(aTHX_ r);
1100 
1101     modperl_io_restore_stdin(aTHX_ h_stdin);
1102     modperl_io_restore_stdout(aTHX_ h_stdout);
1103     FREETMPS;LEAVE;
1104 
1105     MP_INTERP_PUTBACK(interp, aTHX);
1106 
1107     /* flush output buffer after interpreter is putback */
1108     rc = modperl_response_finish(r);
1109     if (rc != APR_SUCCESS) {
1110         retval = rc;
1111     }
1112 
1113     switch (rcfg->status) {
1114       case HTTP_MOVED_TEMPORARILY:
1115         /* set by modperl_cgi_header_parse */
1116         retval = HTTP_MOVED_TEMPORARILY;
1117         break;
1118     }
1119 
1120     return retval;
1121 }
1122 
1123 /* This ugly hack pulls in any function listed in
1124  * modperl_exports.c. Otherwise, the over-zealous
1125  * linker would remove unused api functions
1126  */
1127 const void *modperl_suck_in_ugly_hack(void);
modperl_suck_in_ugly_hack(void)1128 const void *modperl_suck_in_ugly_hack(void)
1129 {
1130     extern const void *modperl_ugly_hack;
1131     return modperl_ugly_hack;
1132 }
1133 
1134 module AP_MODULE_DECLARE_DATA perl_module = {
1135     STANDARD20_MODULE_STUFF,
1136     modperl_config_dir_create, /* dir config creater */
1137     modperl_config_dir_merge,  /* dir merger --- default is to override */
1138     modperl_config_srv_create, /* server config */
1139     modperl_config_srv_merge,  /* merge server config */
1140     modperl_cmds,              /* table of config file commands       */
1141     modperl_register_hooks,    /* register hooks */
1142 };
1143 
1144 /*
1145  * Local Variables:
1146  * c-basic-offset: 4
1147  * indent-tabs-mode: nil
1148  * End:
1149  */
1150