/* 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" /* this module contains mod_perl small tweaks to the Perl runtime * others (larger tweaks) are in their own modules, e.g. modperl_env.c */ typedef struct { const char *name; const char *sub_name; const char *core_name; } modperl_perl_core_global_t; #define MP_PERL_CORE_GLOBAL_ENT(name) \ { name, "ModPerl::Util::" name, "CORE::GLOBAL::" name } static modperl_perl_core_global_t MP_perl_core_global_entries[] = { MP_PERL_CORE_GLOBAL_ENT("exit"), { NULL }, }; XS(XS_ModPerl__Util_exit); /* prototype to pass -Wmissing-prototypes */ XS(XS_ModPerl__Util_exit) { dXSARGS; int status; if (items < 0 || items > 1) { Perl_croak(aTHX_ "Usage: ModPerl::Util::exit(status=0)"); } /* default: 0 */ status = items < 1 ? 0 : (int)SvIV(ST(0)); modperl_perl_exit(aTHX_ status); XSRETURN_EMPTY; } void modperl_perl_core_global_init(pTHX) { modperl_perl_core_global_t *cglobals = MP_perl_core_global_entries; while (cglobals->name) { GV *gv = gv_fetchpv(cglobals->core_name, TRUE, SVt_PVCV); GvCV(gv) = get_cv(cglobals->sub_name, TRUE); GvIMPORTED_CV_on(gv); cglobals++; } newXS("ModPerl::Util::exit", XS_ModPerl__Util_exit, __FILE__); } static void modperl_perl_ids_get(modperl_perl_ids_t *ids) { ids->pid = (I32)getpid(); #ifdef MP_MAINTAIN_PPID ids->ppid = (I32)getppid(); #endif #ifndef WIN32 ids->uid = getuid(); ids->euid = geteuid(); ids->gid = getgid(); ids->egid = getegid(); MP_TRACE_r(MP_FUNC, "pid=%d, " #ifdef MP_MAINTAIN_PPID "ppid=%d, " #endif "uid=%" Uid_t_f ", euid=%" Uid_t_f ", " "gid=%" Gid_t_f ", egid=%" Gid_t_f, (int)ids->pid, #ifdef MP_MAINTAIN_PPID (int)ids->ppid, #endif ids->uid, ids->euid, ids->gid, ids->egid); #endif /* #ifndef WIN32 */ } static void modperl_perl_init_ids(pTHX_ modperl_perl_ids_t *ids) { sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), ids->pid); #ifndef WIN32 PL_uid = ids->uid; PL_euid = ids->euid; PL_gid = ids->gid; PL_egid = ids->egid; #endif #ifdef MP_MAINTAIN_PPID PL_ppid = ids->ppid; #endif } #ifdef USE_ITHREADS static apr_status_t modperl_perl_init_ids_mip(pTHX_ modperl_interp_pool_t *mip, void *data) { modperl_perl_init_ids(aTHX_ (modperl_perl_ids_t *)data); return APR_SUCCESS; } #endif /* USE_ITHREADS */ void modperl_perl_init_ids_server(server_rec *s) { modperl_perl_ids_t ids; modperl_perl_ids_get(&ids); #ifdef USE_ITHREADS modperl_interp_mip_walk_servers(NULL, s, modperl_perl_init_ids_mip, (void*)&ids); #else modperl_perl_init_ids(aTHX_ &ids); #endif } void modperl_perl_destruct(PerlInterpreter *perl) { char **orig_environ = NULL; PTR_TBL_t *module_commands; dTHXa(perl); PERL_SET_CONTEXT(perl); modperl_perl_call_endav(aTHX); PL_perl_destruct_level = modperl_perl_destruct_level(); #ifdef USE_ENVIRON_ARRAY /* XXX: otherwise Perl may try to free() environ multiple times * but it wasn't Perl that modified environ * at least, not if modperl is doing things right * this is a bug in Perl. */ # ifdef WIN32 /* * PL_origenviron = environ; doesn't work under win32 service. * we pull a different stunt here that has the same effect of * tricking perl into _not_ freeing the real 'environ' array. * instead temporarily swap with a dummy array we malloc * here which is ok to let perl free. */ orig_environ = environ; environ = safemalloc(2 * sizeof(char *)); environ[0] = NULL; # else PL_origenviron = environ; # endif #endif { dTHXa(perl); if ((module_commands = modperl_module_config_table_get(aTHX_ FALSE))) { modperl_svptr_table_destroy(aTHX_ module_commands); } } perl_destruct(perl); /* XXX: big bug in 5.6.1 fixed in 5.7.2+ * XXX: try to find a workaround for 5.6.1 */ #if defined(WIN32) && !defined(CLONEf_CLONE_HOST) # define MP_NO_PERL_FREE #endif #ifndef MP_NO_PERL_FREE perl_free(perl); #endif #ifdef USE_ENVIRON_ARRAY if (orig_environ) { environ = orig_environ; } #endif } void modperl_perl_call_endav(pTHX) { if (PL_endav) { modperl_perl_call_list(aTHX_ PL_endav, "END"); } } #if !(MP_PERL_VERSION_AT_MOST(5, 8, 0)) && \ (defined(USE_HASH_SEED) || defined(USE_HASH_SEED_EXPLICIT)) #define MP_NEED_HASH_SEED_FIXUP #endif #ifdef MP_NEED_HASH_SEED_FIXUP static UV MP_init_hash_seed = 0; static bool MP_init_hash_seed_set = FALSE; #endif /* see modperl_hash_seed_set() */ void modperl_hash_seed_init(apr_pool_t *p) { #ifdef MP_NEED_HASH_SEED_FIXUP char *s; /* check if there is a specific hash seed passed via the env var * and if that's the case -- use it */ apr_status_t rv = apr_env_get(&s, "PERL_HASH_SEED", p); if (rv == APR_SUCCESS) { if (s) { while (isSPACE(*s)) s++; } if (s && isDIGIT(*s)) { MP_init_hash_seed = (UV)Atol(s); /* XXX: Atoul()? */ MP_init_hash_seed_set = TRUE; } } /* calculate our own random hash seed */ if (!MP_init_hash_seed_set) { apr_uuid_t *uuid = (apr_uuid_t *)apr_palloc(p, sizeof(apr_uuid_t)); char buf[APR_UUID_FORMATTED_LENGTH + 1]; int i; apr_initialize(); apr_uuid_get(uuid); apr_uuid_format(buf, uuid); /* fprintf(stderr, "UUID: %s\n", buf); */ /* XXX: need a better alg to convert uuid string into a seed */ for (i=0; buf[i]; i++){ MP_init_hash_seed += (UV)(i+1)*(buf[i]+MP_init_hash_seed); } MP_init_hash_seed_set = TRUE; } #endif } /* before 5.8.1, perl was using HASH_SEED=0, starting from 5.8.1 * it randomizes if perl was compiled with ccflags -DUSE_HASH_SEED * or -DUSE_HASH_SEED_EXPLICIT, in which case we need to tell perl * to use the same seed everywhere */ void modperl_hash_seed_set(pTHX) { #ifdef MP_NEED_HASH_SEED_FIXUP if (MP_init_hash_seed_set) { #if MP_PERL_VERSION(5, 8, 1) PL_hash_seed = MP_init_hash_seed; PL_hash_seed_set = MP_init_hash_seed_set; #else PL_rehash_seed = MP_init_hash_seed; PL_rehash_seed_set = MP_init_hash_seed_set; #endif } #endif }