/* 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" modperl_handler_t *modperl_handler_new(apr_pool_t *p, const char *name) { modperl_handler_t *handler = (modperl_handler_t *)apr_pcalloc(p, sizeof(*handler)); switch (*name) { case '+': ++name; MpHandlerAUTOLOAD_On(handler); break; case '-': ++name; /* XXX: currently a noop; should disable autoload of given handler * if PerlOptions +AutoLoad is configured * see: modperl_hash_handlers in modperl_mgv.c */ MpHandlerAUTOLOAD_Off(handler); break; } /* not necessary due to apr_pcalloc */ /* handler->cv = NULL; */ handler->name = name; MP_TRACE_h(MP_FUNC, "new handler %s", handler->name); return handler; } /* How anon-subs are handled: * We have two ways anon-subs can be registered * A) at startup from httpd.conf: * PerlTransHandler 'sub { ... }' * B) run-time perl code * $r->push_handlers(PerlTransHandler => sub { .... }); * $s->push_handlers(PerlTransHandler => sub { .... }); * * In the case of non-threaded perl, we just compile A or grab B and * store it in the mod_perl struct and call it when it's used. No * problems here * * In the case of threads, things get more complicated. we no longer * can store the CV value of the compiled anon-sub, since when * perl_clone is called each interpreter will have a different CV * value. since we need to be able to have 1 entry for each anon-sub * across all interpreters a different solution is needed. to remind * in the case of named subs, we just store the name of the sub and * look its corresponding CV when we need it. * * The used solution: each process has a global counter, which always * grows. Every time a new anon-sub is encountered, a new ID is * allocated from that process-global counter and that ID is stored in * the mod_perl struct. The compiled CV is stored as * $PL_modglobal{ANONSUB}{$id} = CV; * when perl_clone is called, each clone will clone that CV value, but * we will still be able to find it, since we stored it in the * hash. so we retrieve the CV value, whatever it is and we run it. * * that explanation can be written and run in perl: * * use threads; * our %h; * $h{x} = eval 'sub { print qq[this is sub @_\n] }'; * $h{x}->("main"); * threads->new(sub { $h{x}->(threads->self->tid)}); * * XXX: more nuances will follow */ void modperl_handler_anon_init(pTHX_ apr_pool_t *p) { modperl_modglobal_key_t *gkey = modperl_modglobal_lookup(aTHX_ "ANONSUB"); MP_TRACE_h(MP_FUNC, "init $PL_modglobal{ANONSUB} = []"); (void)MP_MODGLOBAL_STORE_HV(gkey); } /* allocate and populate the anon handler sub-struct */ MP_INLINE modperl_mgv_t *modperl_handler_anon_next(pTHX_ apr_pool_t *p) { /* re-use modperl_mgv_t entry which is otherwise is not used * by anon handlers */ modperl_mgv_t *anon = (modperl_mgv_t *)apr_pcalloc(p, sizeof(*anon)); anon->name = apr_psprintf(p, "anon%d", modperl_global_anon_cnt_next()); anon->len = strlen(anon->name); PERL_HASH(anon->hash, anon->name, anon->len); MP_TRACE_h(MP_FUNC, "new anon handler: '%s'", anon->name); return anon; } MP_INLINE void modperl_handler_anon_add(pTHX_ modperl_mgv_t *anon, CV *cv) { modperl_modglobal_key_t *gkey = modperl_modglobal_lookup(aTHX_ "ANONSUB"); HE *he = MP_MODGLOBAL_FETCH(gkey); HV *hv; if (!(he && (hv = (HV*)HeVAL(he)))) { Perl_croak(aTHX_ "modperl_handler_anon_add: " "can't find ANONSUB top entry (get)"); } SvREFCNT_inc(cv); if (!(*hv_store(hv, anon->name, anon->len, (SV*)cv, anon->hash))) { SvREFCNT_dec(cv); Perl_croak(aTHX_ "hv_store of anonsub '%s' has failed!", anon->name); } MP_TRACE_h(MP_FUNC, "anonsub '%s' added", anon->name); } MP_INLINE CV *modperl_handler_anon_get(pTHX_ modperl_mgv_t *anon) { modperl_modglobal_key_t *gkey = modperl_modglobal_lookup(aTHX_ "ANONSUB"); HE *he = MP_MODGLOBAL_FETCH(gkey); HV *hv; SV *sv; if (!(he && (hv = (HV*)HeVAL(he)))) { Perl_croak(aTHX_ "modperl_handler_anon_get: " "can't find ANONSUB top entry (get)"); } if ((he = hv_fetch_he(hv, anon->name, anon->len, anon->hash))) { sv = HeVAL(he); MP_TRACE_h(MP_FUNC, "anonsub gets name '%s'", anon->name); } else { Perl_croak(aTHX_ "can't find ANONSUB's '%s' entry", anon->name); } return (CV*)sv; } static modperl_handler_t *modperl_handler_new_anon(pTHX_ apr_pool_t *p, CV *cv) { modperl_handler_t *handler = (modperl_handler_t *)apr_pcalloc(p, sizeof(*handler)); MpHandlerPARSED_On(handler); MpHandlerANON_On(handler); #ifdef USE_ITHREADS handler->cv = NULL; handler->name = NULL; handler->mgv_obj = modperl_handler_anon_next(aTHX_ p); modperl_handler_anon_add(aTHX_ handler->mgv_obj, cv); #else /* it's safe to cache and later use the cv, since the same perl * interpeter is always used */ SvREFCNT_inc((SV*)cv); handler->cv = cv; handler->name = NULL; MP_TRACE_h(MP_FUNC, "new cached cv anon handler"); #endif return handler; } MP_INLINE const char *modperl_handler_name(modperl_handler_t *handler) { /* a handler containing an anonymous sub doesn't have a normal sub * name */ if (handler->name) { return handler->name; } else { /* anon sub stores the internal modperl name in mgv_obj */ return handler->mgv_obj ? handler->mgv_obj->name : "anonsub"; } } int modperl_handler_resolve(pTHX_ modperl_handler_t **handp, apr_pool_t *p, server_rec *s) { int duped=0; modperl_handler_t *handler = *handp; #ifdef USE_ITHREADS if (modperl_threaded_mpm() && p && !MpHandlerPARSED(handler) && !MpHandlerDYNAMIC(handler)) { /* * under threaded mpm we cannot update the handler structure * at request time without locking, so just copy it */ handler = *handp = modperl_handler_dup(p, handler); duped = 1; } #endif MP_TRACE_h_do(MpHandler_dump_flags(handler, modperl_handler_name(handler))); if (!MpHandlerPARSED(handler)) { apr_pool_t *rp = duped ? p : s->process->pconf; MpHandlerAUTOLOAD_On(handler); MP_TRACE_h(MP_FUNC, "[%s] handler %s hasn't yet been resolved, " "attempting to resolve using %s pool 0x%lx", modperl_server_desc(s, p), modperl_handler_name(handler), duped ? "current" : "server conf", (unsigned long)rp); if (!modperl_mgv_resolve(aTHX_ handler, rp, handler->name, FALSE)) { modperl_errsv_prepend(aTHX_ "failed to resolve handler `%s': ", handler->name); return HTTP_INTERNAL_SERVER_ERROR; } } return OK; } modperl_handler_t *modperl_handler_dup(apr_pool_t *p, modperl_handler_t *h) { MP_TRACE_h(MP_FUNC, "dup handler %s", modperl_handler_name(h)); return modperl_handler_new(p, h->name); } int modperl_handler_equal(modperl_handler_t *h1, modperl_handler_t *h2) { if (h1->mgv_cv && h2->mgv_cv) { return modperl_mgv_equal(h1->mgv_cv, h2->mgv_cv); } return strEQ(h1->name, h2->name); } MpAV *modperl_handler_array_merge(apr_pool_t *p, MpAV *base_a, MpAV *add_a) { int i, j; modperl_handler_t **base_h, **add_h; MpAV *mrg_a; if (!add_a) { return base_a; } if (!base_a) { return add_a; } mrg_a = apr_array_copy(p, base_a); base_h = (modperl_handler_t **)base_a->elts; add_h = (modperl_handler_t **)add_a->elts; for (i=0; inelts; i++) { for (j=0; jnelts; j++) { if (modperl_handler_equal(base_h[i], add_h[j])) { MP_TRACE_d(MP_FUNC, "both base and new config contain %s", add_h[j]->name); } else { modperl_handler_array_push(mrg_a, add_h[j]); MP_TRACE_d(MP_FUNC, "base does not contain %s", add_h[j]->name); } } } return mrg_a; } void modperl_handler_make_args(pTHX_ AV **avp, ...) { va_list args; if (!*avp) { *avp = newAV(); /* XXX: cache an intialized AV* per-request */ } va_start(args, avp); for (;;) { char *classname = va_arg(args, char *); void *ptr; SV *sv; if (classname == NULL) { break; } ptr = va_arg(args, void *); switch (*classname) { case 'A': if (strEQ(classname, "APR::Table")) { sv = modperl_hash_tie(aTHX_ classname, (SV *)NULL, ptr); break; } case 'I': if (strEQ(classname, "IV")) { sv = ptr ? newSViv(PTR2IV(ptr)) : &PL_sv_undef; break; } case 'P': if (strEQ(classname, "PV")) { sv = ptr ? newSVpv((char *)ptr, 0) : &PL_sv_undef; break; } case 'H': if (strEQ(classname, "HV")) { sv = newRV_noinc((SV*)ptr); break; } default: sv = modperl_ptr2obj(aTHX_ classname, ptr); break; } av_push(*avp, sv); } va_end(args); } #define set_desc(dtype) \ if (desc) *desc = modperl_handler_desc_##dtype(idx) /* We should be able to use PERL_GET_CONTEXT here. The rcfg condition * makes sure there is a request being processed. The action > GET part * means it is a $r->set_handlers or $r->push_handlers operation. This * can only happen if called by perl code. */ #define check_modify(dtype) \ if ((action > MP_HANDLER_ACTION_GET) && rcfg) { \ dTHXa(PERL_GET_CONTEXT); \ MP_ASSERT(aTHX+0); \ Perl_croak(aTHX_ "too late to modify %s handlers", \ modperl_handler_desc_##dtype(idx)); \ } /* * generic function to lookup handlers for use in modperl_callback(), * $r->{push,set,get}_handlers, $s->{push,set,get}_handlers * $s->push/set at startup time are the same as configuring Perl*Handlers * $r->push/set at request time will create entries in r->request_config * push will first merge with configured handlers, unless an entry * in r->request_config already exists. in this case, push or set has * already been called for the given handler, * r->request_config entries then override those in r->per_dir_config */ MpAV **modperl_handler_lookup_handlers(modperl_config_dir_t *dcfg, modperl_config_srv_t *scfg, modperl_config_req_t *rcfg, apr_pool_t *p, int type, int idx, modperl_handler_action_e action, const char **desc) { MpAV **avp = NULL, **ravp = NULL; switch (type) { case MP_HANDLER_TYPE_PER_DIR: avp = &dcfg->handlers_per_dir[idx]; if (rcfg) { ravp = &rcfg->handlers_per_dir[idx]; } set_desc(per_dir); break; case MP_HANDLER_TYPE_PER_SRV: avp = &scfg->handlers_per_srv[idx]; if (rcfg) { ravp = &rcfg->handlers_per_srv[idx]; } set_desc(per_srv); break; case MP_HANDLER_TYPE_PRE_CONNECTION: avp = &scfg->handlers_pre_connection[idx]; check_modify(pre_connection); set_desc(pre_connection); break; case MP_HANDLER_TYPE_CONNECTION: avp = &scfg->handlers_connection[idx]; check_modify(connection); set_desc(connection); break; case MP_HANDLER_TYPE_FILES: avp = &scfg->handlers_files[idx]; check_modify(files); set_desc(files); break; case MP_HANDLER_TYPE_PROCESS: avp = &scfg->handlers_process[idx]; check_modify(files); set_desc(process); break; }; if (!avp) { /* should never happen */ #if 0 fprintf(stderr, "PANIC: no such handler type: %d\n", type); #endif return NULL; } switch (action) { case MP_HANDLER_ACTION_GET: /* just a lookup */ break; case MP_HANDLER_ACTION_PUSH: if (ravp) { if (!*ravp) { if (*avp) { /* merge with existing configured handlers */ *ravp = apr_array_copy(p, *avp); } else { /* no request handlers have been previously pushed or set */ *ravp = modperl_handler_array_new(p); } } } else if (!*avp) { /* directly modify the configuration at startup time */ *avp = modperl_handler_array_new(p); } break; case MP_HANDLER_ACTION_SET: if (ravp) { if (*ravp) { /* wipe out existing pushed/set request handlers */ (*ravp)->nelts = 0; } else { /* no request handlers have been previously pushed or set */ *ravp = modperl_handler_array_new(p); } } else if (*avp) { /* wipe out existing configuration, only at startup time */ (*avp)->nelts = 0; } else { /* no configured handlers for this phase */ *avp = modperl_handler_array_new(p); } break; } return (ravp && *ravp) ? ravp : avp; } MpAV **modperl_handler_get_handlers(request_rec *r, conn_rec *c, server_rec *s, apr_pool_t *p, const char *name, modperl_handler_action_e action) { MP_dSCFG(s); MP_dDCFG; MP_dRCFG; int idx, type; if (!r) { /* so $s->{push,set}_handlers can configured request-time handlers */ dcfg = modperl_config_dir_get_defaults(s); } if ((idx = modperl_handler_lookup(name, &type)) == DECLINED) { return FALSE; } return modperl_handler_lookup_handlers(dcfg, scfg, rcfg, p, type, idx, action, NULL); } modperl_handler_t *modperl_handler_new_from_sv(pTHX_ apr_pool_t *p, SV *sv) { char *name = NULL; GV *gv; if (SvROK(sv)) { sv = SvRV(sv); } switch (SvTYPE(sv)) { case SVt_PV: name = SvPVX(sv); return modperl_handler_new(p, apr_pstrdup(p, name)); break; case SVt_PVCV: if (CvANON((CV*)sv)) { return modperl_handler_new_anon(aTHX_ p, (CV*)sv); } if (!(gv = CvGV((CV*)sv))) { Perl_croak(aTHX_ "can't resolve the code reference"); } name = apr_pstrcat(p, HvNAME(GvSTASH(gv)), "::", GvNAME(gv), NULL); return modperl_handler_new(p, name); default: break; }; return NULL; } int modperl_handler_push_handlers(pTHX_ apr_pool_t *p, MpAV *handlers, SV *sv) { modperl_handler_t *handler = modperl_handler_new_from_sv(aTHX_ p, sv); if (handler) { modperl_handler_array_push(handlers, handler); return TRUE; } MP_TRACE_h(MP_FUNC, "unable to push_handler 0x%lx", (unsigned long)sv); return FALSE; } /* convert array header of modperl_handlers_t's to AV ref of CV refs */ SV *modperl_handler_perl_get_handlers(pTHX_ MpAV **handp, apr_pool_t *p) { AV *av = newAV(); int i; modperl_handler_t **handlers; if (!(handp && *handp)) { return &PL_sv_undef; } av_extend(av, (*handp)->nelts - 1); handlers = (modperl_handler_t **)(*handp)->elts; for (i=0; i<(*handp)->nelts; i++) { modperl_handler_t *handler = NULL; GV *gv; if (MpHandlerPARSED(handlers[i])) { handler = handlers[i]; } else { #ifdef USE_ITHREADS if (!MpHandlerDYNAMIC(handlers[i])) { handler = modperl_handler_dup(p, handlers[i]); } #endif if (!handler) { handler = handlers[i]; } if (!modperl_mgv_resolve(aTHX_ handler, p, handler->name, TRUE)) { MP_TRACE_h(MP_FUNC, "failed to resolve handler %s", handler->name); } } if (handler->mgv_cv) { if ((gv = modperl_mgv_lookup(aTHX_ handler->mgv_cv))) { CV *cv = modperl_mgv_cv(gv); av_push(av, newRV_inc((SV*)cv)); } } else { av_push(av, newSVpv(handler->name, 0)); } } return newRV_noinc((SV*)av); } #define push_sv_handler \ if ((modperl_handler_push_handlers(aTHX_ p, *handlers, sv))) { \ MpHandlerDYNAMIC_On(modperl_handler_array_last(*handlers)); \ } /* allow push/set of single cv ref or array ref of cv refs */ int modperl_handler_perl_add_handlers(pTHX_ request_rec *r, conn_rec *c, server_rec *s, apr_pool_t *p, const char *name, SV *sv, modperl_handler_action_e action) { I32 i; AV *av = (AV *)NULL; MpAV **handlers = modperl_handler_get_handlers(r, c, s, p, name, action); if (!(handlers && *handlers)) { return FALSE; } if (SvROK(sv) && (SvTYPE(SvRV(sv)) == SVt_PVAV)) { av = (AV*)SvRV(sv); for (i=0; i <= AvFILL(av); i++) { sv = *av_fetch(av, i, FALSE); push_sv_handler; } } else { push_sv_handler; } return TRUE; } /* * Local Variables: * c-basic-offset: 4 * indent-tabs-mode: nil * End: */