/* 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" /* * mgv = ModPerl Glob Value || Mostly Glob Value * as close to GV as we can get without actually using a GV * need config structures to be free of Perl structures */ #define modperl_mgv_new_w_name(mgv, p, n, copy) \ mgv = modperl_mgv_new(p); \ mgv->len = strlen(n); \ mgv->name = (copy ? apr_pstrndup(p, n, mgv->len) : n) #define modperl_mgv_new_name(mgv, p, n) \ modperl_mgv_new_w_name(mgv, p, n, 1) #define modperl_mgv_new_namen(mgv, p, n) \ modperl_mgv_new_w_name(mgv, p, n, 0) int modperl_mgv_equal(modperl_mgv_t *mgv1, modperl_mgv_t *mgv2) { for (; mgv1 && mgv2; mgv1=mgv1->next, mgv2=mgv2->next) { if (mgv1->hash != mgv2->hash) { return FALSE; } if (mgv1->len != mgv2->len) { return FALSE; } if (memNE(mgv1->name, mgv2->name, mgv1->len)) { return FALSE; } } return TRUE; } modperl_mgv_t *modperl_mgv_new(apr_pool_t *p) { return (modperl_mgv_t *)apr_pcalloc(p, sizeof(modperl_mgv_t)); } #define modperl_mgv_get_next(mgv) \ if (mgv->name) { \ mgv->next = modperl_mgv_new(p); \ mgv = mgv->next; \ } #define modperl_mgv_hash(mgv) \ PERL_HASH(mgv->hash, mgv->name, mgv->len) /* MP_TRACE_h(MP_FUNC, "%s...hash=%ld", mgv->name, mgv->hash) */ modperl_mgv_t *modperl_mgv_compile(pTHX_ apr_pool_t *p, register const char *name) { register const char *namend; I32 len; modperl_mgv_t *symbol = modperl_mgv_new(p); modperl_mgv_t *mgv = symbol; /* @mgv = split '::', $name */ for (namend = name; *namend; namend++) { if (*namend == ':' && namend[1] == ':') { if ((len = (namend - name)) > 0) { modperl_mgv_get_next(mgv); mgv->name = apr_palloc(p, len+3); Copy(name, mgv->name, len, char); mgv->name[len++] = ':'; mgv->name[len++] = ':'; mgv->name[len] = '\0'; mgv->len = len; modperl_mgv_hash(mgv); } name = namend + 2; } } modperl_mgv_get_next(mgv); mgv->len = namend - name; mgv->name = apr_pstrndup(p, name, mgv->len); modperl_mgv_hash(mgv); return symbol; } void modperl_mgv_append(pTHX_ apr_pool_t *p, modperl_mgv_t *symbol, const char *name) { modperl_mgv_t *mgv = symbol; while (mgv->next) { mgv = mgv->next; } mgv->name = apr_pstrcat(p, mgv->name, "::", NULL); mgv->len += 2; modperl_mgv_hash(mgv); mgv->next = modperl_mgv_compile(aTHX_ p, name); } /* faster replacement for gv_fetchpv() */ GV *modperl_mgv_lookup(pTHX_ modperl_mgv_t *symbol) { HV *stash = PL_defstash; modperl_mgv_t *mgv; if (!symbol->hash) { /* special case for MyClass->handler */ return (GV*)sv_2mortal(newSVpvn(symbol->name, symbol->len)); } for (mgv = symbol; mgv; mgv = mgv->next) { HE *he = hv_fetch_he(stash, mgv->name, mgv->len, mgv->hash); if (he) { if (mgv->next) { stash = GvHV((GV *)HeVAL(he)); } else { return (GV *)HeVAL(he); } } else { return Nullgv; } } return Nullgv; } #ifdef USE_ITHREADS MP_INLINE GV *modperl_mgv_lookup_autoload(pTHX_ modperl_mgv_t *symbol, server_rec *s, apr_pool_t *p) { MP_dSCFG(s); GV *gv = modperl_mgv_lookup(aTHX_ symbol); if (gv || !MpSrvPARENT(scfg)) { return gv; } /* * this VirtualHost has its own parent interpreter * must require the module again with this server's THX */ modperl_mgv_require_module(aTHX_ symbol, s, p); return modperl_mgv_lookup(aTHX_ symbol); } #else MP_INLINE GV *modperl_mgv_lookup_autoload(pTHX_ modperl_mgv_t *symbol, server_rec *s, apr_pool_t *p) { return modperl_mgv_lookup(aTHX_ symbol); } #endif /* currently used for complex filters attributes parsing */ /* XXX: may want to generalize it for any handlers */ #define MODPERL_MGV_DEEP_RESOLVE(handler, p) \ if (handler->attrs & MP_FILTER_HAS_INIT_HANDLER) { \ modperl_filter_resolve_init_handler(aTHX_ handler, p); \ } int modperl_mgv_resolve(pTHX_ modperl_handler_t *handler, apr_pool_t *p, const char *name, int logfailure) { CV *cv; GV *gv; HV *stash = Nullhv; char *handler_name = "handler"; char *tmp; if (MpHandlerANON(handler)) { /* already resolved anonymous handler */ return 1; } if (strnEQ(name, "sub ", 4)) { SV *sv; CV *cv; MpHandlerPARSED_On(handler); MpHandlerANON_On(handler); ENTER;SAVETMPS; sv = eval_pv(name, TRUE); if (!(SvROK(sv) && (cv = (CV*)SvRV(sv)) && (CvFLAGS(cv) & CVf_ANON))) { Perl_croak(aTHX_ "expected anonymous sub, got '%s'\n", name); } #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); MP_TRACE_h(MP_FUNC, "[%s] new anon handler", modperl_pid_tid(p)); #else SvREFCNT_inc(cv); handler->cv = cv; handler->name = NULL; MP_TRACE_h(MP_FUNC, "[%s] new cached-cv anon handler", modperl_pid_tid(p)); #endif FREETMPS;LEAVE; return 1; } if ((tmp = strstr((char *)name, "->"))) { int package_len = strlen(name) - strlen(tmp); char *package = apr_pstrndup(p, name, package_len); name = package; handler_name = &tmp[2]; MpHandlerMETHOD_On(handler); if (*package == '$') { GV *gv; SV *obj; handler->mgv_obj = modperl_mgv_compile(aTHX_ p, package + 1); gv = modperl_mgv_lookup(aTHX_ handler->mgv_obj); obj = gv ? GvSV(gv) : Nullsv; if (SvTRUE(obj)) { if (SvROK(obj) && sv_isobject(obj)) { stash = SvSTASH(SvRV(obj)); MpHandlerOBJECT_On(handler); MP_TRACE_h(MP_FUNC, "handler object %s isa %s", package, HvNAME(stash)); } else { MP_TRACE_h(MP_FUNC, "%s is not an object, pv=%s", package, SvPV_nolen(obj)); return 0; } } else { MP_TRACE_h(MP_FUNC, "failed to thaw %s", package); return 0; } } if (!stash) { if ((stash = gv_stashpvn(package, package_len, FALSE))) { MP_TRACE_h(MP_FUNC, "handler method %s isa %s", name, HvNAME(stash)); } } } else { if ((cv = get_cv(name, FALSE))) { handler->attrs = *modperl_code_attrs(aTHX_ cv); handler->mgv_cv = modperl_mgv_compile(aTHX_ p, HvNAME(GvSTASH(CvGV(cv)))); modperl_mgv_append(aTHX_ p, handler->mgv_cv, GvNAME(CvGV(cv))); MpHandlerPARSED_On(handler); MODPERL_MGV_DEEP_RESOLVE(handler, p); return 1; } } if (!stash && MpHandlerAUTOLOAD(handler)) { if (!modperl_perl_module_loaded(aTHX_ name)) { /* not in %INC */ MP_TRACE_h(MP_FUNC, "package %s not in %INC, attempting to load it", name); if (modperl_require_module(aTHX_ name, logfailure)) { MP_TRACE_h(MP_FUNC, "loaded %s package", name); } else { if (logfailure) { /* the caller doesn't handle the error checking */ Perl_croak(aTHX_ "failed to load %s package\n", name); } else { /* the caller handles the error checking */ MP_TRACE_h(MP_FUNC, "failed to load %s package", name); return 0; } } } else { MP_TRACE_h(MP_FUNC, "package %s seems to be loaded", name); } } /* try to lookup the stash only after loading the module, to avoid * the case where a stash is autovivified by a user before the * module was loaded, preventing from loading the module */ if (!(stash || (stash = gv_stashpv(name, FALSE)))) { MP_TRACE_h(MP_FUNC, "%s's stash is not found", name); return 0; } if ((gv = gv_fetchmethod(stash, handler_name)) && (cv = GvCV(gv))) { if (CvFLAGS(cv) & CVf_METHOD) { /* sub foo : method {}; */ MpHandlerMETHOD_On(handler); } if (!stash) { return 0; } if (MpHandlerMETHOD(handler) && !handler->mgv_obj) { char *name = HvNAME(stash); if (!name) { name = ""; } modperl_mgv_new_name(handler->mgv_obj, p, name); } handler->attrs = *modperl_code_attrs(aTHX_ cv); /* note: this is the real function after @ISA lookup */ handler->mgv_cv = modperl_mgv_compile(aTHX_ p, HvNAME(GvSTASH(gv))); modperl_mgv_append(aTHX_ p, handler->mgv_cv, handler_name); MpHandlerPARSED_On(handler); MP_TRACE_h(MP_FUNC, "[%s] found `%s' in class `%s' as a %s", modperl_pid_tid(p), handler_name, HvNAME(stash), MpHandlerMETHOD(handler) ? "method" : "function"); MODPERL_MGV_DEEP_RESOLVE(handler, p); return 1; } /* at least modperl_hash_handlers needs to verify that an * autoloaded-marked handler needs to be loaded, since it doesn't * check success failure, and handlers marked to be autoloaded are * the same as PerlModule and the failure should be fatal */ if (MpHandlerAUTOLOAD(handler)) { Perl_croak(aTHX_ "failed to resolve handler %s\n", name); } #ifdef MP_TRACE /* complain only if the class was actually loaded/created */ if (stash) { MP_TRACE_h(MP_FUNC, "`%s' not found in class `%s'", handler_name, name); } #endif return 0; } modperl_mgv_t *modperl_mgv_last(modperl_mgv_t *symbol) { while (symbol->next) { symbol = symbol->next; } return symbol; } char *modperl_mgv_last_name(modperl_mgv_t *symbol) { symbol = modperl_mgv_last(symbol); return symbol->name; } char *modperl_mgv_as_string(pTHX_ modperl_mgv_t *symbol, apr_pool_t *p, int package) { char *string, *ptr; modperl_mgv_t *mgv; int len = 0; for (mgv = symbol; (package ? mgv->next : mgv); mgv = mgv->next) { len += mgv->len; } ptr = string = apr_palloc(p, len+1); for (mgv = symbol; (package ? mgv->next : mgv); mgv = mgv->next) { Copy(mgv->name, ptr, mgv->len, char); ptr += mgv->len; } if (package) { *(ptr-2) = '\0'; /* trim trailing :: */ } else { *ptr = '\0'; } return string; } #ifdef USE_ITHREADS int modperl_mgv_require_module(pTHX_ modperl_mgv_t *symbol, server_rec *s, apr_pool_t *p) { char *package = modperl_mgv_as_string(aTHX_ symbol, p, 1); if (modperl_require_module(aTHX_ package, TRUE)) { MP_TRACE_h(MP_FUNC, "reloaded %s for server %s", package, modperl_server_desc(s, p)); return TRUE; } return FALSE; } #endif /* precompute the hash(es) for handler names, preload handlers * configured to be autoloaded */ static void modperl_hash_handlers(pTHX_ apr_pool_t *p, server_rec *s, MpAV *entry, void *data) { MP_dSCFG(s); int i; modperl_handler_t **handlers; if (!entry) { return; } handlers = (modperl_handler_t **)entry->elts; for (i=0; i < entry->nelts; i++) { modperl_handler_t *handler = handlers[i]; if (MpHandlerFAKE(handler)) { /* do nothing with fake handlers */ } else if (MpHandlerPARSED(handler)) { #ifdef USE_ITHREADS if ((MpSrvPARENT(scfg) && MpSrvAUTOLOAD(scfg)) && !modperl_mgv_lookup(aTHX_ handler->mgv_cv)) { /* * this VirtualHost has its own parent interpreter * must require the module again with this server's THX */ modperl_mgv_require_module(aTHX_ handler->mgv_cv, s, p); } #endif MP_TRACE_h(MP_FUNC, "%s already resolved in server %s", modperl_handler_name(handler), modperl_server_desc(s, p)); } else { if (MpSrvAUTOLOAD(scfg)) { MpHandlerAUTOLOAD_On(handler); } modperl_mgv_resolve(aTHX_ handler, p, handler->name, TRUE); } } } static int modperl_hash_handlers_dir(apr_pool_t *p, server_rec *s, void *cfg, char *d, void *data) { int i; modperl_config_dir_t *dir_cfg = (modperl_config_dir_t *)cfg; #ifdef USE_ITHREADS MP_dSCFG(s); MP_dSCFG_dTHX; #endif if (!dir_cfg) { return 1; } for (i=0; i < MP_HANDLER_NUM_PER_DIR; i++) { modperl_hash_handlers(aTHX_ p, s, dir_cfg->handlers_per_dir[i], data); } return 1; } static int modperl_hash_handlers_srv(apr_pool_t *p, server_rec *s, void *cfg, void *data) { int i; modperl_config_srv_t *scfg = (modperl_config_srv_t *)cfg; MP_dSCFG_dTHX; for (i=0; i < MP_HANDLER_NUM_PER_SRV; i++) { modperl_hash_handlers(aTHX_ p, s, scfg->handlers_per_srv[i], data); } for (i=0; i < MP_HANDLER_NUM_PROCESS; i++) { modperl_hash_handlers(aTHX_ p, s, scfg->handlers_process[i], data); } for (i=0; i < MP_HANDLER_NUM_CONNECTION; i++) { modperl_hash_handlers(aTHX_ p, s, scfg->handlers_connection[i], data); } for (i=0; i < MP_HANDLER_NUM_FILES; i++) { modperl_hash_handlers(aTHX_ p, s, scfg->handlers_files[i], data); } return 1; } void modperl_mgv_hash_handlers(apr_pool_t *p, server_rec *s) { ap_pcw_walk_config(p, s, &perl_module, NULL, modperl_hash_handlers_dir, modperl_hash_handlers_srv); }