blob: 66030651faec2216e4eb5a61f13dd812f22272f6 [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"
void *modperl_config_dir_create(apr_pool_t *p, char *dir)
{
modperl_config_dir_t *dcfg = modperl_config_dir_new(p);
dcfg->location = dir;
MP_TRACE_d(MP_FUNC, "dir %s", dir);
return dcfg;
}
#define merge_item(item) \
mrg->item = add->item ? add->item : base->item
static apr_table_t *modperl_table_overlap(apr_pool_t *p,
apr_table_t *base,
apr_table_t *add)
{
/* take the base (parent) values, and override with add (child) values,
* generating a new table. entries in add but not in base will be
* added to the new table. all using core apr table routines.
*
* note that this is equivalent to apr_table_overlap except a new
* table is generated, which is required (otherwise we would clobber
* the existing parent or child configurations)
*
* note that this is *not* equivalent to apr_table_overlap, although
* I think it should be, because apr_table_overlap seems to clear
* its first argument when the tables have different pools. I think
* this is wrong -- rici
*/
apr_table_t *merge = apr_table_overlay(p, base, add);
/* compress will squash each key to the last value in the table. this
* is acceptable for all tables that expect only a single value per key
* such as PerlPassEnv and PerlSetEnv. PerlSetVar/PerlAddVar get their
* own, non-standard, merge routines in merge_table_config_vars.
*/
apr_table_compress(merge, APR_OVERLAP_TABLES_SET);
return merge;
}
#define merge_table_overlap_item(item) \
mrg->item = modperl_table_overlap(p, base->item, add->item)
static apr_table_t *merge_config_add_vars(apr_pool_t *p,
const apr_table_t *base,
const apr_table_t *unset,
const apr_table_t *add)
{
apr_table_t *temp = apr_table_copy(p, base);
const apr_array_header_t *arr;
apr_table_entry_t *entries;
int i;
/* for each key in unset do apr_table_unset(temp, key); */
arr = apr_table_elts(unset);
entries = (apr_table_entry_t *)arr->elts;
/* hopefully this is faster than using apr_table_do */
for (i = 0; i < arr->nelts; i++) {
if (entries[i].key) {
apr_table_unset(temp, entries[i].key);
}
}
return apr_table_overlay(p, temp, add);
}
#define merge_handlers(merge_flag, array) \
if (merge_flag(mrg)) { \
mrg->array = modperl_handler_array_merge(p, \
base->array, \
add->array); \
} \
else { \
merge_item(array); \
}
void *modperl_config_dir_merge(apr_pool_t *p, void *basev, void *addv)
{
int i;
modperl_config_dir_t
*base = (modperl_config_dir_t *)basev,
*add = (modperl_config_dir_t *)addv,
*mrg = modperl_config_dir_new(p);
MP_TRACE_d(MP_FUNC, "basev==0x%lx, addv==0x%lx, mrg==0x%lx",
(unsigned long)basev, (unsigned long)addv,
(unsigned long)mrg);
mrg->flags = modperl_options_merge(p, base->flags, add->flags);
merge_item(location);
merge_table_overlap_item(SetEnv);
/* this is where we merge PerlSetVar and PerlAddVar together */
mrg->configvars = merge_config_add_vars(p,
base->configvars,
add->setvars, add->configvars);
merge_table_overlap_item(setvars);
/* XXX: check if Perl*Handler is disabled */
for (i=0; i < MP_HANDLER_NUM_PER_DIR; i++) {
merge_handlers(MpDirMERGE_HANDLERS, handlers_per_dir[i]);
}
return mrg;
}
modperl_config_req_t *modperl_config_req_new(request_rec *r)
{
modperl_config_req_t *rcfg =
(modperl_config_req_t *)apr_pcalloc(r->pool, sizeof(*rcfg));
MP_TRACE_d(MP_FUNC, "0x%lx", (unsigned long)rcfg);
return rcfg;
}
modperl_config_con_t *modperl_config_con_new(conn_rec *c)
{
modperl_config_con_t *ccfg =
(modperl_config_con_t *)apr_pcalloc(c->pool, sizeof(*ccfg));
MP_TRACE_d(MP_FUNC, "0x%lx", (unsigned long)ccfg);
return ccfg;
}
modperl_config_srv_t *modperl_config_srv_new(apr_pool_t *p, server_rec *s)
{
modperl_config_srv_t *scfg = (modperl_config_srv_t *)
apr_pcalloc(p, sizeof(*scfg));
scfg->flags = modperl_options_new(p, MpSrvType);
MpSrvENABLE_On(scfg); /* mod_perl enabled by default */
MpSrvHOOKS_ALL_On(scfg); /* all hooks enabled by default */
scfg->PerlModule = apr_array_make(p, 2, sizeof(char *));
scfg->PerlRequire = apr_array_make(p, 2, sizeof(char *));
scfg->PerlPostConfigRequire =
apr_array_make(p, 1, sizeof(modperl_require_file_t *));
/* 2 arguments + NULL terminator */
scfg->argv = apr_array_make(p, 3, sizeof(char *));
scfg->setvars = apr_table_make(p, 2);
scfg->configvars = apr_table_make(p, 2);
scfg->PassEnv = apr_table_make(p, 2);
scfg->SetEnv = apr_table_make(p, 2);
#ifdef MP_USE_GTOP
scfg->gtop = modperl_gtop_new(p);
#endif
/* make sure httpd's argv[0] is the first argument so $0 is
* correctly connected to the real thing */
modperl_config_srv_argv_push(s->process->argv[0]);
MP_TRACE_d(MP_FUNC, "new scfg: 0x%lx", (unsigned long)scfg);
return scfg;
}
modperl_config_dir_t *modperl_config_dir_new(apr_pool_t *p)
{
modperl_config_dir_t *dcfg = (modperl_config_dir_t *)
apr_pcalloc(p, sizeof(modperl_config_dir_t));
dcfg->flags = modperl_options_new(p, MpDirType);
dcfg->setvars = apr_table_make(p, 2);
dcfg->configvars = apr_table_make(p, 2);
dcfg->SetEnv = apr_table_make(p, 2);
MP_TRACE_d(MP_FUNC, "new dcfg: 0x%lx", (unsigned long)dcfg);
return dcfg;
}
#ifdef MP_TRACE
static void dump_argv(modperl_config_srv_t *scfg)
{
int i;
char **argv = (char **)scfg->argv->elts;
modperl_trace(NULL, "modperl_config_srv_argv_init =>");
for (i=0; i<scfg->argv->nelts; i++) {
modperl_trace(NULL, " %d = %s", i, argv[i]);
}
}
#endif
char **modperl_config_srv_argv_init(modperl_config_srv_t *scfg, int *argc)
{
modperl_config_srv_argv_push("-e;0");
*argc = scfg->argv->nelts;
/* perl_parse() expects a NULL terminated argv array */
modperl_config_srv_argv_push(NULL);
MP_TRACE_g_do(dump_argv(scfg));
return (char **)scfg->argv->elts;
}
void *modperl_config_srv_create(apr_pool_t *p, server_rec *s)
{
modperl_config_srv_t *scfg = modperl_config_srv_new(p, s);
if (!s->is_virtual) {
/* give a chance to MOD_PERL_TRACE env var to set
* PerlTrace. This place is the earliest point in mod_perl
* configuration parsing, when we have the server object
*/
modperl_trace_level_set_apache(s, NULL);
/* Must store the global server record as early as possible,
* because if mod_perl happens to be started from within a
* vhost (e.g., PerlLoadModule) the base server record won't
* be available to vhost and things will blow up
*/
modperl_init_globals(s, p);
}
MP_TRACE_d(MP_FUNC, "p=0x%lx, s=0x%lx, virtual=%d",
p, s, s->is_virtual);
#ifdef USE_ITHREADS
scfg->interp_pool_cfg =
(modperl_tipool_config_t *)
apr_pcalloc(p, sizeof(*scfg->interp_pool_cfg));
/* XXX: determine reasonable defaults */
scfg->interp_pool_cfg->start = 3;
scfg->interp_pool_cfg->max_spare = 3;
scfg->interp_pool_cfg->min_spare = 3;
scfg->interp_pool_cfg->max = 5;
scfg->interp_pool_cfg->max_requests = 2000;
#endif /* USE_ITHREADS */
scfg->server = s;
return scfg;
}
/* XXX: this is not complete */
void *modperl_config_srv_merge(apr_pool_t *p, void *basev, void *addv)
{
int i;
modperl_config_srv_t
*base = (modperl_config_srv_t *)basev,
*add = (modperl_config_srv_t *)addv,
*mrg = modperl_config_srv_new(p, add->server);
MP_TRACE_d(MP_FUNC, "basev==0x%lx, addv==0x%lx, mrg==0x%lx",
(unsigned long)basev, (unsigned long)addv,
(unsigned long)mrg);
merge_item(modules);
merge_item(PerlModule);
merge_item(PerlRequire);
merge_item(PerlPostConfigRequire);
merge_table_overlap_item(SetEnv);
merge_table_overlap_item(PassEnv);
/* this is where we merge PerlSetVar and PerlAddVar together */
mrg->configvars = merge_config_add_vars(p,
base->configvars,
add->setvars, add->configvars);
merge_table_overlap_item(setvars);
merge_item(server);
#ifdef USE_ITHREADS
merge_item(interp_pool_cfg);
#else
merge_item(perl);
#endif
if (MpSrvINHERIT_SWITCHES(add)) {
/* only inherit base PerlSwitches if explicitly told to */
mrg->argv = base->argv;
}
else {
mrg->argv = add->argv;
}
mrg->flags = modperl_options_merge(p, base->flags, add->flags);
/* XXX: check if Perl*Handler is disabled */
for (i=0; i < MP_HANDLER_NUM_PER_SRV; i++) {
merge_handlers(MpSrvMERGE_HANDLERS, handlers_per_srv[i]);
}
for (i=0; i < MP_HANDLER_NUM_FILES; i++) {
merge_handlers(MpSrvMERGE_HANDLERS, handlers_files[i]);
}
for (i=0; i < MP_HANDLER_NUM_PROCESS; i++) {
merge_handlers(MpSrvMERGE_HANDLERS, handlers_process[i]);
}
for (i=0; i < MP_HANDLER_NUM_PRE_CONNECTION; i++) {
merge_handlers(MpSrvMERGE_HANDLERS, handlers_pre_connection[i]);
}
for (i=0; i < MP_HANDLER_NUM_CONNECTION; i++) {
merge_handlers(MpSrvMERGE_HANDLERS, handlers_connection[i]);
}
if (modperl_is_running()) {
if (modperl_init_vhost(mrg->server, p, NULL) != OK) {
exit(1); /*XXX*/
}
}
#ifdef USE_ITHREADS
merge_item(mip);
#endif
return mrg;
}
/* any per-request cleanup goes here */
apr_status_t modperl_config_request_cleanup(pTHX_ request_rec *r)
{
apr_status_t retval;
MP_dRCFG;
retval = modperl_callback_per_dir(MP_CLEANUP_HANDLER, r, MP_HOOK_RUN_ALL);
/* undo changes to %ENV caused by +SetupEnv, perl-script, or
* $r->subprocess_env, so the values won't persist */
if (MpReqSETUP_ENV(rcfg)) {
modperl_env_request_unpopulate(aTHX_ r);
}
return retval;
}
apr_status_t modperl_config_req_cleanup(void *data)
{
request_rec *r = (request_rec *)data;
apr_status_t rc;
MP_dINTERPa(r, NULL, NULL);
rc = modperl_config_request_cleanup(aTHX_ r);
MP_INTERP_PUTBACK(interp, aTHX);
return rc;
}
void *modperl_get_perl_module_config(ap_conf_vector_t *cv)
{
return ap_get_module_config(cv, &perl_module);
}
void modperl_set_perl_module_config(ap_conf_vector_t *cv, void *cfg)
{
ap_set_module_config(cv, &perl_module, cfg);
}
int modperl_config_apply_PerlModule(server_rec *s,
modperl_config_srv_t *scfg,
PerlInterpreter *perl, apr_pool_t *p)
{
char **entries;
int i;
dTHXa(perl);
entries = (char **)scfg->PerlModule->elts;
for (i = 0; i < scfg->PerlModule->nelts; i++){
if (modperl_require_module(aTHX_ entries[i], TRUE)){
MP_TRACE_d(MP_FUNC, "loaded Perl module %s for server %s",
entries[i], modperl_server_desc(s,p));
}
else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"Can't load Perl module %s for server %s, exiting...",
entries[i], modperl_server_desc(s,p));
return FALSE;
}
}
return TRUE;
}
int modperl_config_apply_PerlRequire(server_rec *s,
modperl_config_srv_t *scfg,
PerlInterpreter *perl, apr_pool_t *p)
{
char **entries;
int i;
dTHXa(perl);
entries = (char **)scfg->PerlRequire->elts;
for (i = 0; i < scfg->PerlRequire->nelts; i++){
if (modperl_require_file(aTHX_ entries[i], TRUE)){
MP_TRACE_d(MP_FUNC, "loaded Perl file: %s for server %s",
entries[i], modperl_server_desc(s,p));
}
else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"Can't load Perl file: %s for server %s, exiting...",
entries[i], modperl_server_desc(s,p));
return FALSE;
}
}
return TRUE;
}
int modperl_config_apply_PerlPostConfigRequire(server_rec *s,
modperl_config_srv_t *scfg,
apr_pool_t *p)
{
modperl_require_file_t **requires;
int i;
MP_PERL_CONTEXT_DECLARE;
requires = (modperl_require_file_t **)scfg->PerlPostConfigRequire->elts;
for (i = 0; i < scfg->PerlPostConfigRequire->nelts; i++){
int retval;
MP_PERL_CONTEXT_STORE_OVERRIDE(scfg->mip->parent->perl);
retval = modperl_require_file(aTHX_ requires[i]->file, TRUE);
modperl_env_sync_srv_env_hash2table(aTHX_ p, scfg);
modperl_env_sync_dir_env_hash2table(aTHX_ p, requires[i]->dcfg);
MP_PERL_CONTEXT_RESTORE;
if (retval) {
MP_TRACE_d(MP_FUNC, "loaded Perl file: %s for server %s",
requires[i]->file, modperl_server_desc(s, p));
}
else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"Can't load Perl file: %s for server %s, exiting...",
requires[i]->file, modperl_server_desc(s, p));
return FALSE;
}
}
return TRUE;
}
typedef struct {
AV *av;
I32 ix;
PerlInterpreter *perl;
} svav_param_t;
static
#if AP_MODULE_MAGIC_AT_LEAST(20110329,0)
apr_status_t
#else
void *
#endif
svav_getstr(void *buf, size_t bufsiz, void *param)
{
svav_param_t *svav_param = (svav_param_t *)param;
dTHXa(svav_param->perl);
AV *av = svav_param->av;
SV *sv;
STRLEN n_a;
if (svav_param->ix > AvFILL(av)) {
#if AP_MODULE_MAGIC_AT_LEAST(20110329,0)
return APR_EOF;
#else
return NULL;
#endif
}
sv = AvARRAY(av)[svav_param->ix++];
SvPV_force(sv, n_a);
apr_cpystrn(buf, SvPVX(sv), bufsiz);
#if AP_MODULE_MAGIC_AT_LEAST(20110329,0)
return APR_SUCCESS;
#else
return buf;
#endif
}
const char *modperl_config_insert(pTHX_ server_rec *s,
apr_pool_t *p,
apr_pool_t *ptmp,
int override,
char *path,
int override_options,
ap_conf_vector_t *conf,
SV *lines)
{
const char *errmsg;
cmd_parms parms;
svav_param_t svav_parms;
ap_directive_t *conftree = NULL;
memset(&parms, '\0', sizeof(parms));
parms.limited = -1;
parms.server = s;
parms.override = override;
parms.path = apr_pstrdup(p, path);
parms.pool = p;
#ifdef MP_HTTPD_HAS_OVERRIDE_OPTS
if (override_options == MP_HTTPD_OVERRIDE_OPTS_UNSET) {
parms.override_opts = MP_HTTPD_OVERRIDE_OPTS_DEFAULT;
}
else {
parms.override_opts = override_options;
}
#endif
if (ptmp) {
parms.temp_pool = ptmp;
}
else {
apr_pool_create(&parms.temp_pool, p);
}
if (!(SvROK(lines) && (SvTYPE(SvRV(lines)) == SVt_PVAV))) {
return "not an array reference";
}
svav_parms.av = (AV*)SvRV(lines);
svav_parms.ix = 0;
#ifdef USE_ITHREADS
svav_parms.perl = aTHX;
#endif
parms.config_file = ap_pcfg_open_custom(p, "mod_perl",
&svav_parms, NULL,
svav_getstr, NULL);
errmsg = ap_build_config(&parms, p, parms.temp_pool, &conftree);
if (!errmsg) {
errmsg = ap_walk_config(conftree, &parms, conf);
}
ap_cfg_closefile(parms.config_file);
if (ptmp != parms.temp_pool) {
apr_pool_destroy(parms.temp_pool);
}
return errmsg;
}
const char *modperl_config_insert_parms(pTHX_ cmd_parms *parms,
SV *lines)
{
return modperl_config_insert(aTHX_
parms->server,
parms->pool,
parms->temp_pool,
parms->override,
parms->path,
#ifdef MP_HTTPD_HAS_OVERRIDE_OPTS
parms->override_opts,
#else
MP_HTTPD_OVERRIDE_OPTS_UNSET,
#endif
parms->context,
lines);
}
const char *modperl_config_insert_server(pTHX_ server_rec *s, SV *lines)
{
int override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT);
apr_pool_t *p = s->process->pconf;
return modperl_config_insert(aTHX_ s, p, NULL, override, NULL,
MP_HTTPD_OVERRIDE_OPTS_UNSET,
s->lookup_defaults, lines);
}
const char *modperl_config_insert_request(pTHX_
request_rec *r,
SV *lines,
int override,
char *path,
int override_options)
{
const char *errmsg;
ap_conf_vector_t *dconf = ap_create_per_dir_config(r->pool);
if (!path) {
/* pass a non-NULL path if nothing else given and for compatibility */
path = "/";
}
errmsg = modperl_config_insert(aTHX_
r->server, r->pool, r->pool,
override, path, override_options,
dconf, lines);
if (errmsg) {
return errmsg;
}
r->per_dir_config =
ap_merge_per_dir_configs(r->pool,
r->per_dir_config,
dconf);
return NULL;
}
/* if r!=NULL check for dir PerlOptions, otherwise check for server
* PerlOptions, (s must be always set)
*/
int modperl_config_is_perl_option_enabled(pTHX_ request_rec *r,
server_rec *s, const char *name)
{
U32 flag;
/* XXX: should we test whether perl is disabled for this server? */
/* if (!MpSrvENABLE(scfg)) { */
/* return 0; */
/* } */
if (r) {
if ((flag = modperl_flags_lookup_dir(name)) != -1) {
MP_dDCFG;
return MpDirFLAGS(dcfg) & flag ? 1 : 0;
}
else {
Perl_croak(aTHX_ "PerlOptions %s is not a directory option", name);
}
}
else {
if ((flag = modperl_flags_lookup_srv(name)) != -1) {
MP_dSCFG(s);
return MpSrvFLAGS(scfg) & flag ? 1 : 0;
}
else {
Perl_croak(aTHX_ "PerlOptions %s is not a server option", name);
}
}
}
/*
* Local Variables:
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/