/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mod_perl.h" /* make sure that mod_perl won't try to start itself, while it's * already starting. If the flag's value is 1 * it's still starting, * when it's 2 it is running */ static int MP_init_status = 0; #define MP_IS_NOT_RUNNING (MP_init_status == 0 ? 1 : 0) #define MP_IS_STARTING (MP_init_status == 1 ? 1 : 0) #define MP_IS_RUNNING (MP_init_status == 2 ? 1 : 0) /* false while there is only the parent process and may be child * processes, but no threads around, useful for allowing things that * don't require locking and won't affect other threads. It should * become true just before the child_init phase */ static int MP_threads_started = 0; int modperl_threads_started(void) { return MP_threads_started; } static int MP_threaded_mpm = 0; int modperl_threaded_mpm(void) { return MP_threaded_mpm; } /* sometimes non-threaded mpm also needs to know whether it's still * starting up or after post_config) */ static int MP_post_post_config_phase = 0; int modperl_post_post_config_phase(void) { return MP_post_post_config_phase; } #ifndef USE_ITHREADS static apr_status_t modperl_shutdown(void *data) { modperl_cleanup_data_t *cdata = (modperl_cleanup_data_t *)data; PerlInterpreter *perl = (PerlInterpreter *)cdata->data; void **handles; handles = modperl_xs_dl_handles_get(aTHX); MP_TRACE_i(MP_FUNC, "destroying interpreter=0x%lx", (unsigned long)perl); modperl_perl_destruct(perl); modperl_xs_dl_handles_close(handles); return APR_SUCCESS; } #endif static const char *MP_xs_loaders[] = { "Apache2", "APR", NULL, }; #define MP_xs_loader_name "%s::XSLoader::BOOTSTRAP" /* ugly hack to have access to startup pool and server during xs_init */ static struct { apr_pool_t *p; server_rec *s; } MP_boot_data = {NULL,NULL}; #define MP_boot_data_set(pool, server) \ MP_boot_data.p = pool; \ MP_boot_data.s = server #define MP_dBOOT_DATA \ apr_pool_t *p = MP_boot_data.p; \ server_rec *s = MP_boot_data.s static void modperl_boot(pTHX_ void *data) { MP_dBOOT_DATA; int i; modperl_env_clear(aTHX); modperl_env_default_populate(aTHX); modperl_env_configure_server(aTHX_ p, s); modperl_perl_core_global_init(aTHX); for (i=0; MP_xs_loaders[i]; i++) { char *name = Perl_form(aTHX_ MP_xs_loader_name, MP_xs_loaders[i]); newCONSTSUB(PL_defstash, name, newSViv(1)); } /* outside mod_perl this is done by ModPerl::Const.xs */ newXS("ModPerl::Const::compile", XS_modperl_const_compile, __FILE__); /* make sure DynaLoader is loaded before XSLoader * - to workaround bug in 5.6.1 that can trigger a segv * when using modperl as a dso * - also needed when sections are loaded from +Parent vhost */ modperl_require_module(aTHX_ "DynaLoader", FALSE); IoFLUSH_on(PL_stderrgv); /* unbuffer STDERR */ } static void modperl_xs_init(pTHX) { xs_init(aTHX); /* see modperl_xsinit.c */ /* XXX: in 5.7.2+ we can call the body of modperl_boot here * but in 5.6.1 the Perl runtime is not properly setup yet * so we have to pull this stunt to delay */ SAVEDESTRUCTOR_X(modperl_boot, 0); } /* * the "server_pool" is a subpool of the parent pool (aka "pconf") * this is where we register the cleanups that teardown the interpreter. * the parent process will run the cleanups since server_pool is a subpool * of pconf. we manually clear the server_pool to run cleanups in the * child processes * * the "server_user_pool" is a subpool of the "server_pool", this is * the pool which is exposed to users, so that they can register * cleanup callbacks. This is needed so that the perl cleanups won't * be run before user cleanups are executed. * */ static apr_pool_t *server_pool = NULL; static apr_pool_t *server_user_pool = NULL; apr_pool_t *modperl_server_pool(void) { return server_pool; } apr_pool_t *modperl_server_user_pool(void) { return server_user_pool; } static void set_taint_var(PerlInterpreter *perl) { dTHXa(perl); /* 5.7.3+ has a built-in special ${^TAINT}, backport it to 5.6.0+ */ #if MP_PERL_VERSION_AT_MOST(5, 7, 2) { GV *gv = gv_fetchpv("\024AINT", GV_ADDMULTI, SVt_IV); sv_setiv(GvSV(gv), PL_tainting); SvREADONLY_on(GvSV(gv)); } #endif /* perl v < 5.7.3 */ #ifdef MP_COMPAT_1X { GV *gv = gv_fetchpv("Apache2::__T", GV_ADDMULTI, SVt_PV); sv_setiv(GvSV(gv), PL_tainting); SvREADONLY_on(GvSV(gv)); } #endif /* MP_COMPAT_1X */ } PerlInterpreter *modperl_startup(server_rec *s, apr_pool_t *p) { AV *endav; dTHXa(NULL); MP_dSCFG(s); PerlInterpreter *perl; int status; char **argv; int argc; #ifndef USE_ITHREADS modperl_cleanup_data_t *cdata; #endif /* ensure that we start the base server's perl, before vhost's * one, if modperl_startup was called by vhost before the former * was started */ if (MP_init_status != 2) { server_rec *base_server = modperl_global_get_server_rec(); PerlInterpreter *base_perl; MP_init_status = 2; /* calls itself, so set the flag early */ base_perl = modperl_startup(base_server, p); if (base_server == s ) { return base_perl; } } #ifdef MP_TRACE { server_rec *base_server = modperl_global_get_server_rec(); const char *desc = modperl_server_desc(s, p); if (base_server == s) { MP_TRACE_i(MP_FUNC, "starting the parent perl for the base server", desc); } else { MP_TRACE_i(MP_FUNC, "starting the parent perl for vhost %s", desc); } } #endif #ifdef MP_USE_GTOP MP_TRACE_m_do( modperl_gtop_do_proc_mem_before(MP_FUNC, "perl_parse"); ); #endif argv = modperl_config_srv_argv_init(scfg, &argc); if (!(perl = perl_alloc())) { perror("perl_alloc"); exit(1); } #ifdef USE_ITHREADS aTHX = perl; #endif perl_construct(perl); modperl_hash_seed_set(aTHX); modperl_io_apache_init(aTHX); PL_perl_destruct_level = 2; MP_boot_data_set(p, s); status = perl_parse(perl, modperl_xs_init, argc, argv, NULL); MP_boot_data_set(NULL, NULL); if (status) { perror("perl_parse"); exit(1); } /* suspend END blocks to be run at server shutdown */ endav = PL_endav; PL_endav = Nullav; /* This was fixed in 5.9.0/5.8.1 (17775), but won't compile after 19122 */ #if MP_PERL_VERSION(5, 8, 0) && \ defined(USE_REENTRANT_API) && defined(HAS_CRYPT_R) && defined(__GLIBC__) /* workaround perl5.8.0/glibc bug */ PL_reentrant_buffer->_crypt_struct.current_saltbits = 0; #endif /* We need to reset $0 to argv[0] (httpd) since perl_parse() will * have set it to '-e'. Being magic-aware ensures that some * OS-specific magic will happen (i.e. setproctitle() on *BSDs) */ PL_origalen = strlen(argv[0]) + 1; sv_setpv_mg(get_sv("0",0), argv[0]); perl_run(perl); #ifdef USE_ITHREADS /* base server / virtual host w/ +Parent gets its own mip */ modperl_interp_init(s, p, perl); /* mark the parent perl to be destroyed */ MpInterpBASE_On(scfg->mip->parent); #endif PL_endav = endav; set_taint_var(perl); MP_TRACE_i(MP_FUNC, "constructed interpreter=0x%lx", (unsigned long)perl); #ifdef MP_USE_GTOP MP_TRACE_m_do( modperl_gtop_do_proc_mem_after(MP_FUNC, "perl_parse"); ); #endif #ifdef MP_COMPAT_1X { char *path, *path1; apr_finfo_t finfo; /* 1) push @INC, $ServerRoot */ av_push(GvAV(PL_incgv), newSVpv(ap_server_root, 0)); /* 2) push @INC, $ServerRoot/lib/perl only if it exists */ apr_filepath_merge(&path, ap_server_root, "lib", APR_FILEPATH_NATIVE, p); apr_filepath_merge(&path1, path, "perl", APR_FILEPATH_NATIVE, p); if (APR_SUCCESS == apr_stat(&finfo, path1, APR_FINFO_TYPE, p)) { if (finfo.filetype == APR_DIR) { av_push(GvAV(PL_incgv), newSVpv(path1, 0)); } } } #endif /* MP_COMPAT_1X */ /* base perl and each vhost +Parent should have this init'ed */ modperl_handler_anon_init(aTHX_ p); if (!modperl_config_apply_PerlRequire(s, scfg, perl, p)) { exit(1); } if (!modperl_config_apply_PerlModule(s, scfg, perl, p)) { exit(1); } #ifndef USE_ITHREADS cdata = modperl_cleanup_data_new(server_pool, (void*)perl); apr_pool_cleanup_register(server_pool, cdata, modperl_shutdown, apr_pool_cleanup_null); #endif return perl; } int modperl_init_vhost(server_rec *s, apr_pool_t *p, server_rec *base_server) { MP_dSCFG(s); modperl_config_srv_t *base_scfg; PerlInterpreter *base_perl; PerlInterpreter *perl; const char *vhost = modperl_server_desc(s, p); if (!scfg) { MP_TRACE_i(MP_FUNC, "server %s has no mod_perl config", vhost); return OK; } if (base_server == NULL) { base_server = modperl_global_get_server_rec(); } MP_TRACE_i(MP_FUNC, "Init vhost %s: s=0x%lx, base_s=0x%lx", vhost, s, base_server); if (base_server == s) { MP_TRACE_i(MP_FUNC, "base server is not vhost, skipping %s", vhost); return OK; } base_scfg = modperl_config_srv_get(base_server); #ifdef USE_ITHREADS perl = base_perl = base_scfg->mip->parent->perl; #else perl = base_perl = base_scfg->perl; #endif /* USE_ITHREADS */ #ifdef USE_ITHREADS if (scfg->mip) { MP_TRACE_i(MP_FUNC, "server %s already initialized", vhost); return OK; } /* the base server could have mod_perl callbacks disabled, but it * still needs perl to drive the vhosts */ if (!MpSrvENABLE(scfg) && s->is_virtual) { MP_TRACE_i(MP_FUNC, "mod_perl disabled for server %s", vhost); scfg->mip = NULL; return OK; } PERL_SET_CONTEXT(perl); #endif /* USE_ITHREADS */ MP_TRACE_d_do(MpSrv_dump_flags(scfg, s->server_hostname)); /* if alloc flags is On, virtual host gets its own parent perl */ if (MpSrvPARENT(scfg)) { perl = modperl_startup(s, p); MP_TRACE_i(MP_FUNC, "created parent interpreter for VirtualHost %s", modperl_server_desc(s, p)); } else { #ifdef USE_ITHREADS /* virtual host w/ +Clone gets its own mip */ if (MpSrvCLONE(scfg)) { modperl_interp_init(s, p, perl); } #endif if (!modperl_config_apply_PerlRequire(s, scfg, perl, p)) { return HTTP_INTERNAL_SERVER_ERROR; } if (!modperl_config_apply_PerlModule(s, scfg, perl, p)) { return HTTP_INTERNAL_SERVER_ERROR; } } #ifdef USE_ITHREADS if (!scfg->mip) { /* since mips are created after merge_server_configs() * need to point to the base mip here if this vhost * doesn't have its own */ MP_TRACE_i(MP_FUNC, "%s mip inherited from %s", vhost, modperl_server_desc(base_server, p)); scfg->mip = base_scfg->mip; } #endif /* USE_ITHREADS */ return OK; } void modperl_init(server_rec *base_server, apr_pool_t *p) { server_rec *s; modperl_config_srv_t *base_scfg; PerlInterpreter *base_perl; base_scfg = modperl_config_srv_get(base_server); MP_TRACE_d_do(MpSrv_dump_flags(base_scfg, base_server->server_hostname)); #ifndef USE_ITHREADS if (modperl_threaded_mpm()) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server, "cannot use threaded MPM without ithreads enabled Perl"); exit(1); } #endif base_perl = modperl_startup(base_server, p); for (s=base_server->next; s; s=s->next) { if (modperl_init_vhost(s, p, base_server) != OK) { exit(1); /*XXX*/ } } #ifdef USE_ITHREADS /* after other parent perls were started in vhosts, make sure that * the context is set to the base_perl */ PERL_SET_CONTEXT(base_perl); #endif } static int modperl_post_config_require(server_rec *s, apr_pool_t *p) { for (; s; s=s->next) { MP_dSCFG(s); if (!modperl_config_apply_PerlPostConfigRequire(s, scfg, p)) { return FALSE; } } return TRUE; } #ifdef USE_ITHREADS static void modperl_init_clones(server_rec *s, apr_pool_t *p) { #ifdef MP_TRACE modperl_config_srv_t *base_scfg = modperl_config_srv_get(s); char *base_name = modperl_server_desc(s, p); #endif /* MP_TRACE */ if (!modperl_threaded_mpm()) { MP_TRACE_i(MP_FUNC, "no clones created for non-threaded mpm"); return; } for (; s; s=s->next) { MP_dSCFG(s); #ifdef MP_TRACE char *name = modperl_server_desc(s, p); MP_TRACE_i(MP_FUNC, "PerlInterpScope set to %s for %s", modperl_interp_scope_desc(scfg->interp_scope), name); #else char *name = NULL; #endif /* MP_TRACE */ if (scfg->mip->tipool->idle) { #ifdef MP_TRACE if (scfg->mip == base_scfg->mip) { MP_TRACE_i(MP_FUNC, "%s interp pool inherited from %s", name, base_name); } else { MP_TRACE_i(MP_FUNC, "%s interp pool already initialized", name); } #endif /* MP_TRACE */ } else { MP_TRACE_i(MP_FUNC, "initializing interp pool for %s", name); modperl_tipool_init(scfg->mip->tipool); } } } #endif /* USE_ITHREADS */ void modperl_init_globals(server_rec *s, apr_pool_t *pconf) { ap_mpm_query(AP_MPMQ_IS_THREADED, &MP_threaded_mpm); MP_TRACE_g(MP_FUNC, "mod_perl globals are configured"); modperl_global_init_pconf(pconf, pconf); modperl_global_init_server_rec(pconf, s); modperl_tls_create_request_rec(pconf); /* init the counter to 0 */ modperl_global_anon_cnt_init(pconf); } /* * modperl_sys_{init,term} are things that happen * once per-parent process, not per-interpreter */ static apr_status_t modperl_sys_init(void) { int argc = 0; char **argv = NULL, **env = NULL; MP_TRACE_i(MP_FUNC, "mod_perl sys init"); /* not every OS uses those vars in PERL_SYS_INIT3 macro */ argc = argc; argv = argv; env = env; PERL_SYS_INIT3(&argc, &argv, &env); #if 0 /*XXX*/ #ifdef PTHREAD_ATFORK if (!ap_exists_config_define("PERL_PTHREAD_ATFORK_DONE")) { PTHREAD_ATFORK(Perl_atfork_lock, Perl_atfork_unlock, Perl_atfork_unlock); *(char **)apr_array_push(ap_server_config_defines) = "PERL_PTHREAD_ATFORK_DONE"; } #endif #endif /* modifies PL_ppaddr */ modperl_perl_pp_set_all(); /* modifies PL_vtbl_env{elem} */ modperl_env_init(); return APR_SUCCESS; } static apr_status_t modperl_sys_term(void *data) { /* PERL_SYS_TERM() needs 'my_perl' as of 5.9.5 */ #if MP_PERL_VERSION_AT_LEAST(5, 9, 5) && defined(USE_ITHREADS) modperl_cleanup_data_t *cdata = (modperl_cleanup_data_t *)data; PerlInterpreter *my_perl = cdata == NULL ? NULL : (PerlInterpreter *)cdata->data; #endif MP_init_status = 0; MP_threads_started = 0; MP_post_post_config_phase = 0; MP_PERL_FREE_THREAD_KEY_WORKAROUND; MP_TRACE_i(MP_FUNC, "mod_perl sys term"); modperl_env_unload(); modperl_perl_pp_unset_all(); PERL_SYS_TERM(); return APR_SUCCESS; } int modperl_hook_init(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { if (MP_IS_STARTING || MP_IS_RUNNING) { return OK; } MP_TRACE_i(MP_FUNC, "mod_perl hook init"); MP_init_status = 1; /* now starting */ modperl_restart_count_inc(s); apr_pool_create(&server_pool, pconf); apr_pool_tag(server_pool, "mod_perl server pool"); apr_pool_create(&server_user_pool, pconf); apr_pool_tag(server_user_pool, "mod_perl server user pool"); modperl_sys_init(); apr_pool_cleanup_register(server_pool, NULL, modperl_sys_term, apr_pool_cleanup_null); modperl_init(s, pconf); return OK; } /* * if we need to init earlier than post_config, * e.g. sections or directive handlers. */ int modperl_run(void) { return modperl_hook_init(modperl_global_get_pconf(), NULL, NULL, modperl_global_get_server_rec()); } int modperl_is_running(void) { return MP_IS_RUNNING; } int modperl_hook_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) { /* we can't have PerlPreConfigHandler without first configuring mod_perl */ /* perl 5.8.1+ */ modperl_hash_seed_init(p); return OK; } static int modperl_hook_pre_connection(conn_rec *c, void *csd) { modperl_input_filter_add_connection(c); modperl_output_filter_add_connection(c); return OK; } static int modperl_hook_post_config_last(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { /* in the threaded environment, no server_rec/process_rec * modifications should be done beyond this point */ #ifdef USE_ITHREADS MP_dSCFG(s); dTHXa(scfg->mip->parent->perl); #endif if (!modperl_post_config_require(s, pconf)) { exit(1); } if (modperl_threaded_mpm()) { MP_threads_started = 1; } MP_post_post_config_phase = 1; #ifdef MP_TRACE /* httpd core open_logs handler re-opens s->error_log, which might * change, even though it still points to the same physical file * (.e.g on win32 the filehandle will be different. Therefore * reset the tracing logfile setting here, since this is the * earliest place, happening after the open_logs phase. * * Moreover, we need to dup the filehandle so that when the server * shuts down, we will be able to log to error_log after Apache * has closed it (which happens too early for our likening). */ { apr_file_t *dup; MP_RUN_CROAK(apr_file_dup(&dup, s->error_log, pconf), "mod_perl core post_config"); modperl_trace_logfile_set(dup); } #endif #if MP_PERL_VERSION_AT_LEAST(5, 9, 0) #define MP_PERL_VERSION_STAMP "Perl/%" SVf #else #define MP_PERL_VERSION_STAMP "Perl/v%vd" #endif ap_add_version_component(pconf, MP_VERSION_STRING); ap_add_version_component(pconf, Perl_form(aTHX_ MP_PERL_VERSION_STAMP, PL_patchlevel)); modperl_mgv_hash_handlers(pconf, s); modperl_modglobal_hash_keys(aTHX); modperl_env_hash_keys(aTHX); #ifdef USE_ITHREADS modperl_init_clones(s, pconf); #endif #ifdef MP_NEED_HASH_SEED_FIXUP ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "mod_perl: using Perl HASH_SEED: %"UVuf, MP_init_hash_seed); #endif return OK; } static int modperl_hook_create_request(request_rec *r) { MP_dRCFG; modperl_config_req_init(r, rcfg); /* set the default for cgi header parsing On as early as possible * so $r->content_type in any phase after header_parser could turn * it off. wb->header_parse will be set to 1 only if this flag * wasn't turned off and MpDirPARSE_HEADERS is on */ MpReqPARSE_HEADERS_On(rcfg); return OK; } static int modperl_hook_post_read_request(request_rec *r) { /* if 'PerlOptions +GlobalRequest' is outside a container */ modperl_global_request_cfg_set(r); return OK; } static int modperl_hook_header_parser(request_rec *r) { /* if 'PerlOptions +GlobalRequest' is inside a container */ modperl_global_request_cfg_set(r); return OK; } static int modperl_destruct_level = 2; /* default is full tear down */ int modperl_perl_destruct_level(void) { return modperl_destruct_level; } #ifdef USE_ITHREADS static apr_status_t modperl_perl_call_endav_mip(pTHX_ modperl_interp_pool_t *mip, void *data) { modperl_perl_call_endav(aTHX); return APR_SUCCESS; } #endif /* USE_ITHREADS */ static apr_status_t modperl_child_exit(void *data) { char *level = NULL; server_rec *s = (server_rec *)data; modperl_callback_process(MP_CHILD_EXIT_HANDLER, server_pool, s, MP_HOOK_VOID); if ((level = getenv("PERL_DESTRUCT_LEVEL"))) { modperl_destruct_level = atoi(level); } else { /* default to no teardown in the children */ modperl_destruct_level = 0; } if (modperl_destruct_level) { apr_pool_clear(server_pool); } else { /* run the END blocks of this child process if * modperl_perl_destruct is not called for this process */ #ifdef USE_ITHREADS modperl_interp_mip_walk_servers(NULL, s, modperl_perl_call_endav_mip, (void*)NULL); #else modperl_perl_call_endav(aTHX); #endif } server_pool = NULL; return APR_SUCCESS; } static void modperl_hook_child_init(apr_pool_t *p, server_rec *s) { modperl_perl_init_ids_server(s); apr_pool_cleanup_register(p, (void *)s, modperl_child_exit, apr_pool_cleanup_null); } #define MP_FILTER_HANDLER(f) f, NULL void modperl_register_hooks(apr_pool_t *p) { #ifdef USE_ITHREADS APR_REGISTER_OPTIONAL_FN(modperl_interp_unselect); APR_REGISTER_OPTIONAL_FN(modperl_thx_interp_get); #endif /* for and Apache2->define("MODPERL2") */ *(char **)apr_array_push(ap_server_config_defines) = apr_pstrdup(p, "MODPERL2"); ap_hook_pre_config(modperl_hook_pre_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_open_logs(modperl_hook_init, NULL, NULL, APR_HOOK_FIRST); ap_hook_post_config(modperl_hook_post_config_last, NULL, NULL, APR_HOOK_REALLY_LAST); ap_hook_handler(modperl_response_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(modperl_response_handler_cgi, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_insert_filter(modperl_output_filter_add_request, NULL, NULL, APR_HOOK_LAST); ap_hook_insert_filter(modperl_input_filter_add_request, NULL, NULL, APR_HOOK_LAST); ap_register_output_filter(MP_FILTER_REQUEST_OUTPUT_NAME, MP_FILTER_HANDLER(modperl_output_filter_handler), AP_FTYPE_RESOURCE); ap_register_input_filter(MP_FILTER_REQUEST_INPUT_NAME, MP_FILTER_HANDLER(modperl_input_filter_handler), AP_FTYPE_RESOURCE); ap_register_output_filter(MP_FILTER_CONNECTION_OUTPUT_NAME, MP_FILTER_HANDLER(modperl_output_filter_handler), AP_FTYPE_CONNECTION); ap_register_input_filter(MP_FILTER_CONNECTION_INPUT_NAME, MP_FILTER_HANDLER(modperl_input_filter_handler), AP_FTYPE_CONNECTION); ap_hook_pre_connection(modperl_hook_pre_connection, NULL, NULL, APR_HOOK_FIRST); ap_hook_create_request(modperl_hook_create_request, NULL, NULL, APR_HOOK_MIDDLE); /* both of these hooks need to run really, really first. * otherwise, the global request_rec will be set up _after_ some * Perl handlers run. */ ap_hook_post_read_request(modperl_hook_post_read_request, NULL, NULL, MODPERL_HOOK_REALLY_REALLY_FIRST); ap_hook_header_parser(modperl_hook_header_parser, NULL, NULL, MODPERL_HOOK_REALLY_REALLY_FIRST); ap_hook_child_init(modperl_hook_child_init, NULL, NULL, APR_HOOK_FIRST); modperl_register_handler_hooks(); } static const command_rec modperl_cmds[] = { MP_CMD_SRV_ITERATE("PerlSwitches", switches, "Perl Switches"), MP_CMD_DIR_ITERATE("PerlModule", modules, "PerlModule"), MP_CMD_DIR_ITERATE("PerlRequire", requires, "PerlRequire"), MP_CMD_SRV_ITERATE("PerlConfigRequire", config_requires, "PerlConfigRequire"), MP_CMD_SRV_ITERATE("PerlPostConfigRequire", post_config_requires, "PerlPostConfigRequire"), MP_CMD_DIR_ITERATE("PerlOptions", options, "Perl Options"), MP_CMD_DIR_ITERATE("PerlInitHandler", init_handlers, "Subroutine name"), MP_CMD_DIR_TAKE2("PerlSetVar", set_var, "PerlSetVar"), MP_CMD_DIR_ITERATE2("PerlAddVar", add_var, "PerlAddVar"), MP_CMD_DIR_TAKE2("PerlSetEnv", set_env, "PerlSetEnv"), MP_CMD_SRV_TAKE1("PerlPassEnv", pass_env, "PerlPassEnv"), MP_CMD_DIR_RAW_ARGS_ON_READ("wbucket) { rcfg->wbucket = (modperl_wbucket_t *)apr_palloc(r->pool, sizeof(*rcfg->wbucket)); } wb = rcfg->wbucket; /* setup buffer for output */ wb->pool = r->pool; wb->filters = &r->output_filters; wb->outcnt = 0; wb->header_parse = MpDirPARSE_HEADERS(dcfg) && MpReqPARSE_HEADERS(rcfg) ? 1 : 0; wb->r = r; } apr_status_t modperl_response_finish(request_rec *r) { MP_dRCFG; /* flush output buffer */ return modperl_wbucket_flush(rcfg->wbucket, FALSE); } static int modperl_response_handler_run(request_rec *r, int finish) { int retval; modperl_response_init(r); retval = modperl_callback_per_dir(MP_RESPONSE_HANDLER, r, MP_HOOK_RUN_FIRST); if ((retval == DECLINED) && r->content_type) { r->handler = r->content_type; /* let http_core or whatever try */ } if (finish) { apr_status_t rc = modperl_response_finish(r); if (rc != APR_SUCCESS) { retval = rc; } } return retval; } int modperl_response_handler(request_rec *r) { MP_dDCFG; MP_dRCFG; apr_status_t retval; #ifdef USE_ITHREADS pTHX; modperl_interp_t *interp; #endif if (!strEQ(r->handler, "modperl")) { return DECLINED; } #ifdef USE_ITHREADS interp = modperl_interp_select(r, r->connection, r->server); aTHX = interp->perl; if (MpInterpPUTBACK(interp)) { rcfg->interp = interp; } #endif /* default is -SetupEnv, add if PerlOption +SetupEnv */ if (MpDirSETUP_ENV(dcfg)) { modperl_env_request_populate(aTHX_ r); } retval = modperl_response_handler_run(r, TRUE); #ifdef USE_ITHREADS if (MpInterpPUTBACK(interp)) { /* PerlInterpScope handler */ rcfg->interp = NULL; modperl_interp_unselect(interp); } #endif return retval; } int modperl_response_handler_cgi(request_rec *r) { MP_dDCFG; GV *h_stdin, *h_stdout; apr_status_t retval, rc; MP_dRCFG; #ifdef USE_ITHREADS pTHX; modperl_interp_t *interp; #endif if (!strEQ(r->handler, "perl-script")) { return DECLINED; } #ifdef USE_ITHREADS interp = modperl_interp_select(r, r->connection, r->server); aTHX = interp->perl; if (MpInterpPUTBACK(interp)) { rcfg->interp = interp; } #endif modperl_perl_global_request_save(aTHX_ r); /* default is +SetupEnv, skip if PerlOption -SetupEnv */ if (MpDirSETUP_ENV(dcfg) || !MpDirSeenSETUP_ENV(dcfg)) { modperl_env_request_populate(aTHX_ r); } /* default is +GlobalRequest, skip if PerlOption -GlobalRequest */ if (MpDirGLOBAL_REQUEST(dcfg) || !MpDirSeenGLOBAL_REQUEST(dcfg)) { modperl_global_request_set(r); } /* need to create a block around the IO setup so the temp vars * will be automatically cleaned up when we are done with IO */ ENTER;SAVETMPS; h_stdin = modperl_io_override_stdin(aTHX_ r); h_stdout = modperl_io_override_stdout(aTHX_ r); modperl_env_request_tie(aTHX_ r); retval = modperl_response_handler_run(r, FALSE); modperl_env_request_untie(aTHX_ r); modperl_perl_global_request_restore(aTHX_ r); modperl_io_restore_stdin(aTHX_ h_stdin); modperl_io_restore_stdout(aTHX_ h_stdout); FREETMPS;LEAVE; #ifdef USE_ITHREADS if (MpInterpPUTBACK(interp)) { /* PerlInterpScope handler */ modperl_interp_unselect(interp); rcfg->interp = NULL; } #endif /* flush output buffer after interpreter is putback */ rc = modperl_response_finish(r); if (rc != APR_SUCCESS) { retval = rc; } switch (rcfg->status) { case HTTP_MOVED_TEMPORARILY: /* set by modperl_cgi_header_parse */ retval = HTTP_MOVED_TEMPORARILY; break; } return retval; } /* This ugly hack pulls in any function listed in * modperl_exports.c. Otherwise, the over-zealous * linker would remove unused api functions */ const void *modperl_suck_in_ugly_hack(void); const void *modperl_suck_in_ugly_hack(void) { extern const void *modperl_ugly_hack; return modperl_ugly_hack; } module AP_MODULE_DECLARE_DATA perl_module = { STANDARD20_MODULE_STUFF, modperl_config_dir_create, /* dir config creater */ modperl_config_dir_merge, /* dir merger --- default is to override */ modperl_config_srv_create, /* server config */ modperl_config_srv_merge, /* merge server config */ modperl_cmds, /* table of config file commands */ modperl_register_hooks, /* register hooks */ };