blob: c6286f01883030e6db4bdbc1e9f55ba5d4e369fa [file] [log] [blame]
/* 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 (GV *)NULL;
}
}
return (GV *)NULL;
}
#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 = (HV *)NULL;
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, "new anon handler");
#else
SvREFCNT_inc(cv);
handler->cv = cv;
handler->name = NULL;
MP_TRACE_h(MP_FUNC, "new cached-cv anon handler");
#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) : (SV *)NULL;
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, "found `%s' in class `%s' as a %s",
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;
dTHXa(data);
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;
dTHXa(data);
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)
{
MP_dINTERPa(NULL, NULL, s);
ap_pcw_walk_config(p, s, &perl_module,
#ifdef USE_ITHREADS
aTHX,
#else
NULL,
#endif
modperl_hash_handlers_dir,
modperl_hash_handlers_srv);
MP_INTERP_PUTBACK(interp, aTHX);
}
/*
* Local Variables:
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/