/* 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" /* XXX: PL_modglobal thingers might be useful elsewhere */ #define MP_MODGLOBAL_ENT(key) \ {key, "ModPerl::" key, MP_SSTRLEN("ModPerl::") + MP_SSTRLEN(key), 0} static modperl_modglobal_key_t MP_modglobal_keys[] = { MP_MODGLOBAL_ENT("END"), MP_MODGLOBAL_ENT("ANONSUB"), { NULL }, }; void modperl_modglobal_hash_keys(pTHX) { modperl_modglobal_key_t *gkey = MP_modglobal_keys; while (gkey->name) { PERL_HASH(gkey->hash, gkey->val, gkey->len); gkey++; } } modperl_modglobal_key_t *modperl_modglobal_lookup(pTHX_ const char *name) { modperl_modglobal_key_t *gkey = MP_modglobal_keys; while (gkey->name) { if (strEQ(gkey->name, name)) { return gkey; } gkey++; } return NULL; } static void modperl_perl_global_init(pTHX_ modperl_perl_globals_t *globals) { globals->env.gv = PL_envgv; globals->inc.gv = PL_incgv; globals->defout.gv = PL_defoutgv; globals->rs.sv = &PL_rs; globals->end.av = &PL_endav; globals->end.key = MP_MODGLOBAL_END; } /* * if (exists $PL_modglobal{$key}{$package}) { * return $PL_modglobal{$key}{$package}; * } * elsif ($autovivify) { * return $PL_modglobal{$key}{$package} = []; * } * else { * return $Nullav; # a null pointer in C of course :) * } */ static AV *modperl_perl_global_avcv_fetch(pTHX_ modperl_modglobal_key_t *gkey, const char *package, I32 packlen, I32 autovivify) { HE *he = MP_MODGLOBAL_FETCH(gkey); HV *hv; if (!(he && (hv = (HV*)HeVAL(he)))) { if (autovivify) { hv = MP_MODGLOBAL_STORE_HV(gkey); } else { return Nullav; } } if ((he = hv_fetch_he(hv, (char *)package, packlen, 0))) { return (AV*)HeVAL(he); } else { if (autovivify) { return (AV*)*hv_store(hv, package, packlen, (SV*)newAV(), 0); } else { return Nullav; } } } /* autovivify $PL_modglobal{$key}{$package} if it doesn't exist yet, * so that in modperl_perl_global_avcv_set we will know whether to * store blocks in it or keep them in the original list. * * For example in the case of END blocks, if * $PL_modglobal{END}{$package} exists, modperl_perl_global_avcv_set * will push newly encountered END blocks to it, otherwise it'll keep * them in PL_endav. */ void modperl_perl_global_avcv_register(pTHX_ modperl_modglobal_key_t *gkey, const char *package, I32 packlen) { AV *av = modperl_perl_global_avcv_fetch(aTHX_ gkey, package, packlen, TRUE); MP_TRACE_g(MP_FUNC, "register PL_modglobal %s::%s (has %d entries)", package, (char*)gkey->name, av ? 1+av_len(av) : 0); } /* if (exists $PL_modglobal{$key}{$package}) { * for my $cv (@{ $PL_modglobal{$key}{$package} }) { * $cv->(); * } * } */ void modperl_perl_global_avcv_call(pTHX_ modperl_modglobal_key_t *gkey, const char *package, I32 packlen) { AV *av = modperl_perl_global_avcv_fetch(aTHX_ gkey, package, packlen, FALSE); MP_TRACE_g(MP_FUNC, "run PL_modglobal %s::%s (has %d entries)", package, (char*)gkey->name, av ? 1+av_len(av) : 0); if (av) { modperl_perl_call_list(aTHX_ av, gkey->name); } } /* if (exists $PL_modglobal{$key}{$package}) { * @{ $PL_modglobal{$key}{$package} } = (); * } */ void modperl_perl_global_avcv_clear(pTHX_ modperl_modglobal_key_t *gkey, const char *package, I32 packlen) { AV *av = modperl_perl_global_avcv_fetch(aTHX_ gkey, package, packlen, FALSE); MP_TRACE_g(MP_FUNC, "clear PL_modglobal %s::%s (has %d entries)", package, (char*)gkey->name, av ? 1+av_len(av) : 0); if (av) { av_clear(av); } } static int modperl_perl_global_avcv_set(pTHX_ SV *sv, MAGIC *mg) { AV *mav, *av = (AV*)sv; const char *package = HvNAME(PL_curstash); I32 packlen = strlen(package); modperl_modglobal_key_t *gkey = (modperl_modglobal_key_t *)mg->mg_ptr; /* the argument sv, is the original list perl was operating on. * (e.g. PL_endav). So now if we find that we have package/cv name * (e.g. Foo/END) registered for set-aside, we remove the cv that * was just unshifted in and push it into * $PL_modglobal{$key}{$package}. Otherwise we do nothing, which * keeps the unshifted cv (e.g. END block) in its original av * (e.g. PL_endav) */ mav = modperl_perl_global_avcv_fetch(aTHX_ gkey, package, packlen, FALSE); if (!mav) { MP_TRACE_g(MP_FUNC, "%s::%s is not going to PL_modglobal", package, (char*)gkey->name); /* keep it in the tied list (e.g. PL_endav) */ return 1; } MP_TRACE_g(MP_FUNC, "%s::%s is going into PL_modglobal", package, (char*)gkey->name); sv = av_shift(av); /* push @{ $PL_modglobal{$key}{$package} }, $cv */ av_store(mav, AvFILLp(mav)+1, sv); /* print scalar @{ $PL_modglobal{$key}{$package} } */ MP_TRACE_g(MP_FUNC, "%s::%s av now has %d entries", package, (char*)gkey->name, 1+av_len(mav)); return 1; } static MGVTBL modperl_vtbl_global_avcv_t = { 0, MEMBER_TO_FPTR(modperl_perl_global_avcv_set), 0, 0, 0, }; static void modperl_perl_global_avcv_tie(pTHX_ modperl_modglobal_key_e key, AV *av) { if (!SvMAGIC((SV*)av)) { MAGIC *mg; Newz(702, mg, 1, MAGIC); mg->mg_virtual = &modperl_vtbl_global_avcv_t; mg->mg_ptr = (char *)&MP_modglobal_keys[key]; mg->mg_len = -1; /* prevent free() of mg->mg_ptr */ SvMAGIC((SV*)av) = mg; } SvSMAGICAL_on((SV*)av); } static void modperl_perl_global_avcv_untie(pTHX_ AV *av) { SvSMAGICAL_off((SV*)av); } static void modperl_perl_global_avcv_save(pTHX_ modperl_perl_global_avcv_t *avcv) { if (!*avcv->av) { *avcv->av = newAV(); } modperl_perl_global_avcv_tie(aTHX_ avcv->key, *avcv->av); } static void modperl_perl_global_avcv_restore(pTHX_ modperl_perl_global_avcv_t *avcv) { modperl_perl_global_avcv_untie(aTHX_ *avcv->av); } /* * newHVhv is not good enough since it does not copy magic. * XXX: 5.8.0+ newHVhv has some code thats faster than hv_iternext */ static HV *copyENV(pTHX_ HV *ohv) { HE *entry, *hv_eiter; I32 hv_riter; register HV *hv; STRLEN hv_max = HvMAX(ohv); STRLEN hv_fill = HvFILL(ohv); hv = newHV(); while (hv_max && hv_max + 1 >= hv_fill * 2) { hv_max = hv_max / 2; /* Is always 2^n-1 */ } HvMAX(hv) = hv_max; if (!hv_fill) { return hv; } hv_riter = HvRITER(ohv); /* current root of iterator */ hv_eiter = HvEITER(ohv); /* current entry of iterator */ hv_iterinit(ohv); while ((entry = hv_iternext(ohv))) { SV *sv = newSVsv(HeVAL(entry)); modperl_envelem_tie(sv, HeKEY(entry), HeKLEN(entry)); hv_store(hv, HeKEY(entry), HeKLEN(entry), sv, HeHASH(entry)); } HvRITER(ohv) = hv_riter; HvEITER(ohv) = hv_eiter; hv_magic(hv, Nullgv, 'E'); TAINT_NOT; return hv; } static void modperl_perl_global_gvhv_save(pTHX_ modperl_perl_global_gvhv_t *gvhv) { HV *hv = GvHV(gvhv->gv); #if 0 U32 mg_flags; MAGIC *mg = SvMAGIC(hv); /* * there should only be a small number of entries in %ENV * at this point: modperl_env.c:modperl_env_const_vars[], * PerlPassEnv and top-level PerlSetEnv * XXX: still; could have have something faster than newHVhv() * especially if we add another GVHV to the globals table that * might have more entries */ /* makes newHVhv() faster in bleedperl */ MP_magical_untie(hv, mg_flags); gvhv->tmphv = newHVhv(hv); TAINT_NOT; /* reapply magic flags */ MP_magical_tie(hv, mg_flags); MP_magical_tie(gvhv->tmphv, mg_flags); if (mg && mg->mg_type && !SvMAGIC(gvhv->tmphv)) { /* propagate SvMAGIC(hv) to SvMAGIC(gvhv->tmphv) */ /* XXX: maybe newHVhv should do this? */ hv_magic(gvhv->tmphv, Nullgv, mg->mg_type); } #else gvhv->tmphv = copyENV(aTHX_ hv); #endif gvhv->orighv = hv; GvHV(gvhv->gv) = gvhv->tmphv; } static void modperl_perl_global_gvhv_restore(pTHX_ modperl_perl_global_gvhv_t *gvhv) { U32 mg_flags; GvHV(gvhv->gv) = gvhv->orighv; /* loose magic for hv_clear() * e.g. for %ENV don't want to clear environ array */ MP_magical_untie(gvhv->tmphv, mg_flags); SvREFCNT_dec(gvhv->tmphv); } static void modperl_perl_global_gvav_save(pTHX_ modperl_perl_global_gvav_t *gvav) { gvav->origav = GvAV(gvav->gv); gvav->tmpav = newAV(); modperl_perl_av_push_elts_ref(aTHX_ gvav->tmpav, gvav->origav); GvAV(gvav->gv) = gvav->tmpav; } static void modperl_perl_global_gvav_restore(pTHX_ modperl_perl_global_gvav_t *gvav) { GvAV(gvav->gv) = gvav->origav; SvREFCNT_dec(gvav->tmpav); } static void modperl_perl_global_gvio_save(pTHX_ modperl_perl_global_gvio_t *gvio) { gvio->flags = IoFLAGS(GvIOp(gvio->gv)); } static void modperl_perl_global_gvio_restore(pTHX_ modperl_perl_global_gvio_t *gvio) { IoFLAGS(GvIOp(gvio->gv)) = gvio->flags; } static void modperl_perl_global_svpv_save(pTHX_ modperl_perl_global_svpv_t *svpv) { svpv->cur = SvCUR(*svpv->sv); strncpy(svpv->pv, SvPVX(*svpv->sv), sizeof(svpv->pv)); } static void modperl_perl_global_svpv_restore(pTHX_ modperl_perl_global_svpv_t *svpv) { sv_setpvn(*svpv->sv, svpv->pv, svpv->cur); } typedef enum { MP_GLOBAL_AVCV, MP_GLOBAL_GVHV, MP_GLOBAL_GVAV, MP_GLOBAL_GVIO, MP_GLOBAL_SVPV } modperl_perl_global_types_e; typedef struct { char *name; int offset; modperl_perl_global_types_e type; } modperl_perl_global_entry_t; #define MP_GLOBAL_OFFSET(m) \ STRUCT_OFFSET(modperl_perl_globals_t, m) static modperl_perl_global_entry_t MP_perl_global_entries[] = { {"END", MP_GLOBAL_OFFSET(end), MP_GLOBAL_AVCV}, /* END */ {"ENV", MP_GLOBAL_OFFSET(env), MP_GLOBAL_GVHV}, /* %ENV */ {"INC", MP_GLOBAL_OFFSET(inc), MP_GLOBAL_GVAV}, /* @INC */ {"STDOUT", MP_GLOBAL_OFFSET(defout), MP_GLOBAL_GVIO}, /* $| */ {"/", MP_GLOBAL_OFFSET(rs), MP_GLOBAL_SVPV}, /* $/ */ {NULL} }; #define MP_PERL_GLOBAL_SAVE(type, ptr) \ modperl_perl_global_##type##_save( \ aTHX_ (modperl_perl_global_##type##_t *)&(*ptr)) #define MP_PERL_GLOBAL_RESTORE(type, ptr) \ modperl_perl_global_##type##_restore( \ aTHX_ (modperl_perl_global_##type##_t *)&(*ptr)) #define MP_dGLOBAL_PTR(globals, entries) \ apr_uint64_t **ptr = (apr_uint64_t **) \ ((char *)globals + (int)(long)entries->offset) static void modperl_perl_global_save(pTHX_ modperl_perl_globals_t *globals, modperl_perl_global_entry_t *entries) { modperl_perl_global_init(aTHX_ globals); while (entries->name) { MP_dGLOBAL_PTR(globals, entries); switch (entries->type) { case MP_GLOBAL_AVCV: MP_PERL_GLOBAL_SAVE(avcv, ptr); break; case MP_GLOBAL_GVHV: MP_PERL_GLOBAL_SAVE(gvhv, ptr); break; case MP_GLOBAL_GVAV: MP_PERL_GLOBAL_SAVE(gvav, ptr); break; case MP_GLOBAL_GVIO: MP_PERL_GLOBAL_SAVE(gvio, ptr); break; case MP_GLOBAL_SVPV: MP_PERL_GLOBAL_SAVE(svpv, ptr); break; } entries++; } } static void modperl_perl_global_restore(pTHX_ modperl_perl_globals_t *globals, modperl_perl_global_entry_t *entries) { while (entries->name) { MP_dGLOBAL_PTR(globals, entries); switch (entries->type) { case MP_GLOBAL_AVCV: MP_PERL_GLOBAL_RESTORE(avcv, ptr); break; case MP_GLOBAL_GVHV: MP_PERL_GLOBAL_RESTORE(gvhv, ptr); break; case MP_GLOBAL_GVAV: MP_PERL_GLOBAL_RESTORE(gvav, ptr); break; case MP_GLOBAL_GVIO: MP_PERL_GLOBAL_RESTORE(gvio, ptr); break; case MP_GLOBAL_SVPV: MP_PERL_GLOBAL_RESTORE(svpv, ptr); break; } entries++; } } void modperl_perl_global_request_save(pTHX_ request_rec *r) { MP_dRCFG; modperl_perl_global_save(aTHX_ &rcfg->perl_globals, MP_perl_global_entries); } void modperl_perl_global_request_restore(pTHX_ request_rec *r) { MP_dRCFG; modperl_perl_global_restore(aTHX_ &rcfg->perl_globals, MP_perl_global_entries); }