blob: fe935d78574cc95e5ebc724543ac282cc806ab74 [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"
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; i<base_a->nelts; i++) {
for (j=0; j<add_a->nelts; 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:
*/