/*
* Copyright (C) 2000, 2001, 2013, 2024 Gregory Trubetskoy
* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation
*
* Licensed 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.
*
* Originally developed by Gregory Trubetskoy.
*
*
* mod_python.c
*
*
* See accompanying documentation and source code comments
* for details.
*
*/
#include "mod_python.h"
static PyThreadState* global_tstate;
/* Server object for main server as supplied to python_init(). */
static server_rec *main_server = NULL;
/* List of available Python obCallBacks/Interpreters */
static apr_hash_t *interpreters = NULL;
static apr_pool_t *interp_pool = NULL;
static void release_interpreter(interpreterdata *idata);
apr_pool_t *child_init_pool = NULL;
/* Optional functions imported from mod_include when loaded: */
static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *optfn_register_include_handler;
static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *optfn_ssi_get_tag_and_value;
static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *optfn_ssi_parse_string;
/*
* Python3 uses wchar_t * for strings; Python2 uses char *.
* So for printing we need a different format string.
*/
#if PY_MAJOR_VERSION < 3
# define PRIs "%s"
#else
# define PRIs "%ls"
#endif
/**
** make_obcallback
**
* This function instantiates an obCallBack object.
* NOTE: The obCallBack object is instantiated by Python
* code. This C module calls into Python code which returns
* the reference to obCallBack.
*/
static PyObject * make_obcallback(const char *name)
{
PyObject *m = NULL;
PyObject *obCallBack = NULL;
/* Now execute the equivalent of
* >>> import
* >>>
* in the __main__ module to start up Python.
*/
m = PyImport_ImportModule("mod_python.apache");
if (!m) {
PyObject *path;
char *c_path;
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"make_obcallback: could not import mod_python.apache.\n");
PyErr_Print();
fflush(stderr);
path = PyObject_Repr(PySys_GetObject("path"));
MP_ANYSTR_AS_STR(c_path, path, 0);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"make_obcallback: Python path being used \"%s\".",
c_path);
Py_DECREF(path);
return NULL;
} else {
/* Make sure that C and Python code have the same version */
const char *mp_dynamic_version = "";
PyObject *mp = PyImport_ImportModule("mod_python");
if (mp) {
PyObject *d = PyModule_GetDict(mp);
PyObject *o = PyDict_GetItemString(d, "mp_version");
PyObject *f = PyDict_GetItemString(d, "__file__");
MP_ANYSTR_AS_STR(mp_dynamic_version, o, 1);
if (!mp_dynamic_version) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"make_obcallback: fatal: mp_dynamic_version is NULL.");
Py_DECREF(o);
Py_DECREF(mp);
return NULL;
}
if (strcmp(mp_version_string, mp_dynamic_version) != 0) {
char *c_f;
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server,
"WARNING: mod_python version mismatch, expected '%s', found '%s'.",
mp_version_string, mp_dynamic_version);
MP_ANYSTR_AS_STR(c_f, f, 1);
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server,
"WARNING: mod_python modules location '%s'.", c_f);
Py_DECREF(f); /* MP_ANYSTR_AS_STR */
}
Py_DECREF(o); /* MP_ANYSTR_AS_STR */
Py_XDECREF(mp);
/* call init to get obCallBack */
if (! (obCallBack = PyObject_CallMethod(
m, "init", "sO", name, MpServer_FromServer(main_server)))) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"make_obcallback: could not call init().");
PyErr_Print();
fflush(stderr);
}
} else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"make_obcallback: could not import mod_python");
}
}
Py_XDECREF(m);
return obCallBack;
}
/**
** save_interpreter
**
* Save the interpreter. The argument is a PyThreadState
* belonging to (i.e. tstate->interp) the interpreter we are
* saving. Creates a new interpreterdata and returns a pointer to
* it.
*/
static interpreterdata *save_interpreter(const char *name, PyThreadState *tstate)
{
interpreterdata *idata = NULL;
idata = (interpreterdata *)malloc(sizeof(interpreterdata));
if (!idata)
return NULL;
idata->tstates = apr_array_make(interp_pool, 128, sizeof(interpreterdata *));
idata->interp = tstate->interp;
idata->obcallback = NULL;
apr_hash_set(interpreters, name, APR_HASH_KEY_STRING, idata);
return idata;
}
/*
* python_interpreter_name
*
* Get name of current interpreter. Must be called while lock is held.
* This is effectively a shortcut for accessing "apache.interpreter".
*/
PyObject *python_interpreter_name()
{
PyObject *m = NULL;
PyObject *d = NULL;
PyObject *o = NULL;
m = PyImport_ImportModule("mod_python.apache");
if (m) {
d = PyModule_GetDict(m);
o = PyDict_GetItemString(d, "interpreter");
if (o) {
Py_INCREF(o);
Py_DECREF(m);
return o;
}
}
return 0;
}
/**
** get_interpreter
**
* Get interpreter given its name.
*
* A thread state never outlives a handler; when a phase of a handler
* is done, there is no use for this thread state. But there is no
* need to malloc() a new thread state at every request, so when a
* handler is done, in release_interpreter() we push the tstate into
* an array of available tstates for this interpreter. When we need
* another tstate, we pop from this array, and only if that returns
* nothing do we create a new tstate.
*
*/
static interpreterdata *get_interpreter(const char *name)
{
PyThreadState *tstate;
interpreterdata *idata = NULL;
if (! name)
name = MAIN_INTERPRETER;
/* Py_NewInterpreter requires the GIL held, this is one way to have it so */
PyEval_RestoreThread(global_tstate);
idata = apr_hash_get(interpreters, name, APR_HASH_KEY_STRING);
if (!idata) {
tstate = Py_NewInterpreter();
if (!tstate) {
/* couldn't create an interpreter, this is bad */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"get_interpreter: Py_NewInterpreter() returned NULL. No more memory?");
return NULL;
}
idata = save_interpreter(name, tstate);
if (!idata) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"get_interpreter: save_interpreter() returned NULL. No more memory?");
return NULL;
}
}
else {
#ifdef WITH_THREAD
/* use an available tstate, or create a new one */
PyThreadState ** tstate_pp;
tstate_pp = (PyThreadState **)apr_array_pop(idata->tstates);
if (!tstate_pp)
tstate = PyThreadState_New(idata->interp);
else
tstate = *tstate_pp;
#else
/* always use the first (and only) tstate */
tstate = idata->interp->tstate_head;
#endif
PyThreadState_Swap(tstate);
}
/* At this point GIL is held and tstate is set, we're ready to run */
if (!idata->obcallback) {
idata->obcallback = make_obcallback(name);
if (!idata->obcallback) {
release_interpreter(idata);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"get_interpreter: no interpreter callback found.");
return NULL;
}
}
return idata;
}
/**
** release_interpreter
**
* Release interpreter.
*/
static void release_interpreter(interpreterdata *idata)
{
PyThreadState *tstate = PyThreadState_Get();
#ifdef WITH_THREAD
#if PY_MAJOR_VERSION <= 3 && PY_MINOR_VERSION < 9
PyThreadState_Clear(tstate);
#endif
if (idata)
APR_ARRAY_PUSH(idata->tstates, PyThreadState *) = tstate;
else
PyThreadState_Delete(tstate);
PyEval_ReleaseThread(tstate);
#else
if (!idata) PyThreadState_Delete(tstate);
#endif
}
/**
** pytho_cleanup
**
* This function gets called for clean ups registered
* with register_cleanup(). Clean ups registered via
* PythonCleanupHandler run in python_cleanup_handler()
* below.
*/
apr_status_t python_cleanup(void *data)
{
interpreterdata *idata;
cleanup_info *ci = (cleanup_info *)data;
/* get/create interpreter */
idata = get_interpreter(ci->interpreter);
if (!idata) {
Py_DECREF(ci->handler);
Py_XDECREF(ci->data);
free((void *)ci->interpreter);
free(ci);
return APR_SUCCESS; /* this is ignored anyway */
}
/*
* Call the cleanup function.
*/
if (! PyObject_CallFunction(ci->handler, "O", ci->data)) {
PyObject *ptype;
PyObject *pvalue;
PyObject *ptb;
PyObject *handler;
PyObject *stype;
PyObject *svalue;
char *c_handler, *c_svalue, *c_stype;
PyErr_Fetch(&ptype, &pvalue, &ptb);
handler = PyObject_Str(ci->handler);
stype = PyObject_Str(ptype);
svalue = PyObject_Str(pvalue);
Py_XDECREF(ptype);
Py_XDECREF(pvalue);
Py_XDECREF(ptb);
MP_ANYSTR_AS_STR(c_handler, handler, 0);
if (!c_handler) c_handler = "";
MP_ANYSTR_AS_STR(c_svalue, svalue, 0);
if (!c_svalue) c_svalue = "";
MP_ANYSTR_AS_STR(c_stype, stype, 0);
if (!c_stype) c_stype = "";
if (ci->request_rec) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0,
ci->request_rec,
"python_cleanup: Error calling cleanup object %s",
c_handler);
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0,
ci->request_rec,
" %s: %s", c_stype, c_svalue);
}
else {
ap_log_error(APLOG_MARK, APLOG_ERR, 0,
ci->server_rec,
"python_cleanup: Error calling cleanup object %s",
c_handler);
ap_log_error(APLOG_MARK, APLOG_ERR, 0,
ci->server_rec,
" %s: %s", c_stype, c_svalue);
}
Py_DECREF(handler);
Py_DECREF(stype);
Py_DECREF(svalue);
}
Py_DECREF(ci->handler);
Py_DECREF(ci->data);
free((void *)ci->interpreter);
free(ci);
release_interpreter(idata);
return APR_SUCCESS;
}
static apr_status_t init_mutexes(server_rec *s, apr_pool_t *p, py_global_config *glb)
{
int max_threads = 0;
int max_procs = 0;
int is_threaded = 0;
int is_forked = 0;
int max_clients;
int locks;
int n;
const char *val;
const char *mutex_dir;
py_config *conf;
conf = (py_config *) ap_get_module_config(s->module_config,
&python_module);
/* figure out maximum possible concurrent connections */
/* MAX_DAEMON_USED seems to account for MaxClients, as opposed to
MAX_DAEMONS, which is ServerLimit
*/
ap_mpm_query(AP_MPMQ_IS_THREADED, &is_threaded);
if (is_threaded != AP_MPMQ_NOT_SUPPORTED) {
ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads);
}
ap_mpm_query(AP_MPMQ_IS_FORKED, &is_forked);
if (is_forked != AP_MPMQ_NOT_SUPPORTED) {
/* XXX This looks strange, and it is. prefork.c seems to use
MAX_DAEMON_USED the same way that worker.c uses
MAX_DAEMONS (prefork is wrong IMO) */
ap_mpm_query(AP_MPMQ_MAX_DAEMON_USED, &max_procs);
if (max_procs == -1) {
ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_procs);
}
}
max_clients = (((max_threads <= 0) ? 1 : max_threads) *
((max_procs <= 0) ? 1 : max_procs));
/* On some systems the locking mechanism chosen uses valuable
system resources, notably on RH 8 it will use sysv ipc for
which Linux by default provides only 128 semaphores
system-wide, and on many other systems flock is used, which
results in a relatively large number of open files.
The maximum number of locks can be specified at
compile time using "./configure --with-max-locks value" or
at run time with "PythonOption mod_python.mutex_locks value".
If the PythonOption directive is used, it must be in a
server config context, otherwise it will be ignored.
The optimal number of necessary locks is not clear, perhaps a
small number is more than sufficient - if someone took the
time to run some research on this, that'd be most welcome!
*/
val = apr_table_get(conf->options, "mod_python.mutex_locks");
if (val) {
locks = atoi(val);
} else {
locks = MAX_LOCKS;
}
locks = (max_clients > locks) ? locks : max_clients;
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
"mod_python: Creating %d session mutexes based "
"on %d max processes and %d max threads.",
locks, max_procs, max_threads);
glb->g_locks = (apr_global_mutex_t **)
apr_palloc(p, locks * sizeof(apr_global_mutex_t *));
glb->nlocks = locks;
glb->parent_pid = getpid();
#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
/* On some sytems a directory for the mutex lock files is required.
This mutex directory can be specifed at compile time using
"./configure --with-mutex-dir value" or at run time with
"PythonOption mod_python.mutex_directory value".
If the PythonOption directive is used, it must be in a
server config context, otherwise it will be ignored.
XXX Should we check if mutex_dir exists and has the correct
permissions?
*/
mutex_dir = apr_table_get(conf->options, "mod_python.mutex_directory");
if (!mutex_dir)
mutex_dir = MUTEX_DIR;
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
"mod_python: using mutex_directory %s ",
mutex_dir);
#endif
for (n=0; ng_locks;
#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
char fname[255];
/* XXX What happens if len(mutex_dir) > 255 - len(mpmtx%d%d)? */
snprintf(fname, 255, "%s/mpmtx%d%d", mutex_dir, glb->parent_pid, n);
#else
char *fname = NULL;
#endif
rc = apr_global_mutex_create(&mutex[n], fname, APR_LOCK_DEFAULT,
p);
if (rc != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rc, s,
"mod_python: Failed to create global mutex %d of %d (%s).",
n, locks, (!fname) ? "" : fname);
if (n > 1) {
/* we were able to create at least two, so lets just print a
warning/hint and proceed
*/
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"mod_python: We can probably continue, but with diminished ability "
"to process session locks.");
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"mod_python: Hint: On Linux, the problem may be the number of "
"available semaphores, check 'sysctl kernel.sem'");
/* now free two locks so that if there is another
module or two that wants a lock, it will be ok */
apr_global_mutex_destroy(mutex[n-1]);
glb->nlocks = n-1;
if (n > 2) {
apr_global_mutex_destroy(mutex[n-2]);
glb->nlocks = n-2;
}
break;
}
else {
return rc;
}
}
else {
#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
#if AP_MODULE_MAGIC_AT_LEAST(20081201,0)
ap_unixd_set_global_mutex_perms(mutex[n]);
#else
if (!geteuid()) {
chown(fname, unixd_config.user_id, -1);
unixd_set_global_mutex_perms(mutex[n]);
}
#endif
#endif
}
}
return APR_SUCCESS;
}
static apr_status_t reinit_mutexes(server_rec *s, apr_pool_t *p, py_global_config *glb)
{
int n;
#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
/* Determine the directory to use for mutex lock files.
See init_mutexes function for more details.
*/
const char *mutex_dir;
py_config *conf;
conf = (py_config *) ap_get_module_config(s->module_config,
&python_module);
mutex_dir = apr_table_get(conf->options, "mod_python.mutex_directory");
if (!mutex_dir)
mutex_dir = MUTEX_DIR;
#endif
for (n=0; n< glb->nlocks; n++) {
apr_status_t rc;
apr_global_mutex_t **mutex = glb->g_locks;
#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
char fname[255];
snprintf(fname, 255, "%s/mpmtx%d%d", mutex_dir, glb->parent_pid, n);
#else
char *fname = NULL;
#endif
rc = apr_global_mutex_child_init(&mutex[n], fname, p);
if (rc != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, s,
"mod_python: Failed to reinit global mutex %s.",
(!fname) ? "" : fname);
return rc;
}
}
return APR_SUCCESS;
}
/**
** python_create_global_config
**
* This creates the part of config that survives
* server restarts
*
*/
static py_global_config *python_create_global_config(server_rec *s)
{
apr_pool_t *pool = s->process->pool;
py_global_config *glb;
/* do we already have it in s->process->pool? */
apr_pool_userdata_get((void **)&glb, MP_CONFIG_KEY, pool);
if (glb) {
return glb;
}
/* otherwise, create it */
glb = (py_global_config *)apr_palloc(pool, sizeof(*glb));
apr_pool_userdata_set(glb, MP_CONFIG_KEY,
apr_pool_cleanup_null,
pool);
return glb;
}
/*
* mp_acquire_interpreter()
*
* Exported function for acquiring named interpreter.
*/
PyInterpreterState *mp_acquire_interpreter(const char *name)
{
interpreterdata *idata;
idata = get_interpreter(name);
return idata->interp;
}
/*
* mp_release_interpreter()
*
* Exported function for releasing acquired interpreter.
*
*/
void mp_release_interpreter(void)
{
release_interpreter(NULL);
}
/*
* mp_get_request_object(request_rec *req)
*
* Exported function for obtaining wrapper for request object.
*
*/
PyObject *mp_get_request_object(request_rec *req)
{
requestobject *request_obj;
request_obj = python_get_request_object(req, 0);
return (PyObject *)request_obj;
}
/*
* mp_get_server_object(server_rec *srv)
*
* Exported function for obtaining wrapper for server object.
*
*/
PyObject *mp_get_server_object(server_rec *srv)
{
return (PyObject *)MpServer_FromServer(srv);
}
/*
* mp_get_connection_object(conn_rec *conn)
*
* Exported function for obtaining wrapper for connection object.
*
*/
PyObject *mp_get_connection_object(conn_rec *conn)
{
return (PyObject *)MpConn_FromConn(conn);
}
/**
** python_init()
**
* Called by Apache at mod_python initialization time.
*/
static int python_init(apr_pool_t *p, apr_pool_t *ptemp,
apr_pool_t *plog, server_rec *s)
{
char buff[255];
void *data;
py_global_config *glb;
const char *userdata_key = "python_init";
apr_status_t rc;
const char *py_dynamic_version = 0;
/* The "initialized" flag is a fudge for Mac OS X. It
* addresses two issues. The first is that when an Apache
* "restart" is performed, Apache will unload the mod_python
* shared object, but not the Python framework. This means
* that when "python_init()" is called after the restart,
* the mod_python initialization will not run if only the
* initialized state of Python is checked, because Python
* is already initialized. The second problem is that for
* some older revisions of Mac OS X, even on the initial
* startup of Apache, the "Py_IsInitialized()" function
* would return true and mod_python wouldn't initialize
* itself correctly and would crash.
*/
static int initialized = 0;
#ifdef WIN32
/* No need to run python_init() in Win32 parent processes as
* the lack of fork on Win32 means we get no benefit as far as
* inheriting a preinitialized Python interpreter. Further,
* upon a restart on Win32 platform the python_init() function
* will be called again in the parent process but without some
* resources allocated by the previous call having being
* released properly, resulting in memory and Win32 resource
* leaks.
*/
if (!getenv("AP_PARENT_PID"))
return OK;
#endif /* WIN32 */
apr_pool_userdata_get(&data, userdata_key, s->process->pool);
if (!data) {
apr_pool_userdata_set((const void *)1, userdata_key,
apr_pool_cleanup_null, s->process->pool);
return OK;
}
/* mod_python version */
ap_add_version_component(p, mp_version_component);
py_dynamic_version = strtok(apr_pstrdup(p, Py_GetVersion()), " ");
/* Python version */
sprintf(buff, "Python/%.200s", py_dynamic_version);
ap_add_version_component(p, buff);
/* cache main server */
main_server = s;
/* global config */
glb = python_create_global_config(s);
if ((rc = init_mutexes(s, p, glb)) != APR_SUCCESS) {
return rc;
}
/* initialize global Python interpreter if necessary */
if (initialized == 0 || !Py_IsInitialized())
{
initialized = 1;
#if PY_VERSION_HEX < 0x030C0000
/* disable user site directories */
Py_NoUserSiteDirectory = 1;
#endif
/* Initialze the main interpreter. */
#if PY_MAJOR_VERSION == 2 && \
(PY_MINOR_VERSION < 7 || (PY_MINOR_VERSION == 7 && PY_MICRO_VERSION < 14))
/*
* We do not want site.py to
* be imported because as of Python 2.7.9 it would cause a
* circular dependency related to _locale which breaks
* graceful restart so we set Py_NoSiteFlag to 1 just for this
* one time. (https://github.com/grisha/mod_python/issues/46)
*/
Py_NoSiteFlag = 1;
#endif
#if PY_MAJOR_VERSION == 2
PyMODINIT_FUNC init_apache(void);
PyImport_AppendInittab("_apache", &init_apache);
#else
PyMODINIT_FUNC PyInit_apache(void);
PyImport_AppendInittab("_apache", &PyInit_apache);
#endif
Py_Initialize();
#if PY_MAJOR_VERSION == 2 && \
(PY_MINOR_VERSION < 7 || (PY_MINOR_VERSION == 7 && PY_MICRO_VERSION < 14))
Py_NoSiteFlag = 0;
#endif
/* see https://docs.python.org/3/c-api/init.html#c.PyEval_InitThreads */
#if defined (WITH_THREAD) && PY_VERSION_HEX < 0x03070000
/* create and acquire the interpreter lock */
PyEval_InitThreads();
#endif
/* create the obCallBack dictionary */
interpreters = apr_hash_make(p);
interp_pool = p;
if (! interpreters) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"python_init: apr_hash_make() failed! No more memory?");
exit(1);
}
/* save the global_tstate */
global_tstate = PyEval_SaveThread();
}
APR_REGISTER_OPTIONAL_FN(mp_acquire_interpreter);
APR_REGISTER_OPTIONAL_FN(mp_release_interpreter);
APR_REGISTER_OPTIONAL_FN(mp_get_request_object);
APR_REGISTER_OPTIONAL_FN(mp_get_server_object);
APR_REGISTER_OPTIONAL_FN(mp_get_connection_object);
return OK;
}
/**
** python_create_config
**
* Called by create_dir_config and create_srv_config
*/
static py_config *python_create_config(apr_pool_t *p)
{
py_config *conf =
(py_config *) apr_pcalloc(p, sizeof(py_config));
conf->authoritative = 1;
conf->d_is_location = 0;
conf->options = apr_table_make(p, 4);
conf->directives = apr_table_make(p, 4);
conf->hlists = apr_hash_make(p);
conf->in_filters = apr_hash_make(p);
conf->out_filters = apr_hash_make(p);
return conf;
}
/**
** python_create_dir_config
**
* Allocate memory and initialize the strucure that will
* hold configuration parametes.
*
* This function is called on every hit it seems.
*/
static void *python_create_dir_config(apr_pool_t *p, char *dir)
{
py_config *conf = python_create_config(p);
conf->config_dir = dir;
return conf;
}
/**
** python_create_srv_config
**
* Allocate memory and initialize the strucure that will
* hold configuration parametes.
*/
static void *python_create_srv_config(apr_pool_t *p, server_rec *srv)
{
py_config *conf = python_create_config(p);
return conf;
}
/**
** modpython_table_overlap
**
* Replaces the apr_table_overlap() function using a specific pool
* for the resulting table.
*/
static apr_table_t *modpython_table_overlap(apr_pool_t *p,
apr_table_t *current_table,
apr_table_t *new_table)
{
apr_table_t *merge = apr_table_overlay(p, current_table, new_table);
apr_table_compress(merge, APR_OVERLAP_TABLES_SET);
return merge;
}
/**
** python_merge_dir_config
**
*/
static void *python_merge_config(apr_pool_t *p, void *current_conf,
void *new_conf)
{
py_config *merged_conf =
(py_config *) apr_pcalloc(p, sizeof(py_config));
py_config *cc = (py_config *) current_conf;
py_config *nc = (py_config *) new_conf;
apr_hash_index_t *hi;
char *key;
apr_ssize_t klen;
hl_entry *hle;
py_handler *fh;
/* we basically allow the local configuration to override global,
* by first copying current values and then new values on top
*/
/** create **/
merged_conf->hlists = apr_hash_make(p);
merged_conf->in_filters = apr_hash_make(p);
merged_conf->out_filters = apr_hash_make(p);
/** merge directives and options **/
merged_conf->directives = modpython_table_overlap(p, cc->directives,
nc->directives);
merged_conf->options = modpython_table_overlap(p, cc->options,
nc->options);
/** copy current **/
merged_conf->authoritative = cc->authoritative;
merged_conf->config_dir = apr_pstrdup(p, cc->config_dir);
merged_conf->d_is_location = cc->d_is_location;
for (hi = apr_hash_first(p, cc->hlists); hi; hi=apr_hash_next(hi)) {
apr_hash_this(hi, (const void **)&key, &klen, (void **)&hle);
apr_hash_set(merged_conf->hlists, key, klen, (void *)hle);
}
for (hi = apr_hash_first(p, cc->in_filters); hi; hi=apr_hash_next(hi)) {
apr_hash_this(hi, (const void **)&key, &klen, (void **)&fh);
apr_hash_set(merged_conf->in_filters, key, klen, (void *)fh);
}
for (hi = apr_hash_first(p, cc->out_filters); hi; hi=apr_hash_next(hi)) {
apr_hash_this(hi, (const void **)&key, &klen, (void **)&fh);
apr_hash_set(merged_conf->out_filters, key, klen, (void *)fh);
}
/** copy new **/
if (nc->authoritative != merged_conf->authoritative)
merged_conf->authoritative = nc->authoritative;
if (nc->config_dir) {
merged_conf->config_dir = apr_pstrdup(p, nc->config_dir);
merged_conf->d_is_location = nc->d_is_location;
}
for (hi = apr_hash_first(p, nc->hlists); hi; hi=apr_hash_next(hi)) {
apr_hash_this(hi, (const void**)&key, &klen, (void **)&hle);
apr_hash_set(merged_conf->hlists, key, klen, (void *)hle);
}
for (hi = apr_hash_first(p, nc->in_filters); hi; hi=apr_hash_next(hi)) {
apr_hash_this(hi, (const void**)&key, &klen, (void **)&fh);
apr_hash_set(merged_conf->in_filters, key, klen, (void *)fh);
}
for (hi = apr_hash_first(p, nc->out_filters); hi; hi=apr_hash_next(hi)) {
apr_hash_this(hi, (const void**)&key, &klen, (void **)&fh);
apr_hash_set(merged_conf->out_filters, key, klen, (void *)fh);
}
return (void *) merged_conf;
}
/**
** python_directive
**
* Called by non-handler directives
*
*/
static const char *python_directive(cmd_parms *cmd, void * mconfig,
char *key, const char *val)
{
py_config *conf;
conf = (py_config *) mconfig;
apr_table_set(conf->directives, key, val);
return NULL;
}
/* returns a parent if it matches the given directive */
static const ap_directive_t * find_parent(const ap_directive_t *dirp,
const char *what)
{
while (dirp->parent != NULL) {
dirp = dirp->parent;
if (strcasecmp(dirp->directive, what) == 0)
return dirp;
}
return NULL;
}
#ifdef WIN32
#define USE_ICASE AP_REG_ICASE
#else
#define USE_ICASE 0
#endif
static void determine_context(apr_pool_t *p, const cmd_parms* cmd,
char **dp, char *dx, char *dl,
ap_regex_t **rx)
{
const ap_directive_t *context = NULL;
const ap_directive_t *directive = NULL;
const char *endp, *arg;
char *directory = NULL;
char d_is_fnmatch = 0;
char d_is_location = 0;
ap_regex_t *regex = NULL;
directive = cmd->directive;
/* Skip any enclosing File directive if one exists */
if ((context = find_parent(directive, "args;
endp = ap_strrchr_c(arg, '>');
arg = apr_pstrndup(p, arg, endp - arg);
directory = ap_getword_conf(p, &arg);
d_is_location = 1;
if (!strcmp(directory, "~")) {
directory = ap_getword_conf(p, &arg);
regex = ap_pregcomp(p, cmd->path, AP_REG_EXTENDED|USE_ICASE);
} else if (apr_fnmatch_test(directory)) {
d_is_fnmatch = 1;
}
} else if ((context = find_parent(directive, "args;
endp = ap_strrchr_c(arg, '>');
arg = apr_pstrndup(p, arg, endp - arg);
directory = ap_getword_conf(p, &arg);
d_is_location = 1;
regex = ap_pregcomp(p, directory, AP_REG_EXTENDED|USE_ICASE);
} else if ((context = find_parent(directive, "args;
endp = ap_strrchr_c(arg, '>');
arg = apr_pstrndup(p, arg, endp - arg);
directory = ap_getword_conf(p, &arg);
if (!strcmp(directory, "~")) {
directory = ap_getword_conf(p, &arg);
regex = ap_pregcomp(p, cmd->path, AP_REG_EXTENDED|USE_ICASE);
} else if (apr_fnmatch_test(directory)) {
d_is_fnmatch = 1;
}
} else if ((context = find_parent(directive, "args;
endp = ap_strrchr_c(arg, '>');
arg = apr_pstrndup(p, arg, endp - arg);
directory = ap_getword_conf(p, &arg);
regex = ap_pregcomp(p, directory, AP_REG_EXTENDED|USE_ICASE);
}
else if (cmd->config_file != NULL) {
/* cmd->config_file is NULL when in main Apache
* configuration file as the file is completely
* read in before the directive is processed as
* EXEC_ON_READ is not set in req_override field
* of command_struct table entry. Thus know then
* we are being used in a .htaccess file. */
directory = ap_make_dirstr_parent(p, directive->filename);
}
/* Only canonicalize path and add trailing slash at
* this point if no pattern matching to be done at
* a later time. */
if (directory && !d_is_fnmatch && !regex && !d_is_location) {
char *newpath = NULL;
apr_status_t rv;
/* NB: if directory is not absolute, CWD will be prepended to
* it. If directory is NULL (we make sure it is not NULL
* above), then newpath will become CWD */
rv = apr_filepath_merge(&newpath, NULL, directory,
APR_FILEPATH_TRUENAME, p);
if (rv == APR_SUCCESS || rv == APR_EPATHWILD) {
/*
* We are appending a trailing slash here to be consistent
* with what httpd's core.c does in dirsection(). I [GT]
* am not very clear why core.c does it, especially given
* that it appends the trailing slash even to things that
* aren't really directories, e.g. when directory is an
* fnmatch pattern such as "/foo/bar*"
*/
directory = newpath;
if (directory[strlen(directory) - 1] != '/') {
directory = apr_pstrcat(p, directory, "/", NULL);
}
}
}
*dp = directory;
*dx = d_is_fnmatch;
*dl = d_is_location;
*rx = regex;
}
static void python_directive_hl_add(apr_pool_t *p, apr_hash_t *hlists,
const char *phase, const char *handler,
const cmd_parms* cmd, char *directory,
char d_is_fnmatch, char d_is_location,
ap_regex_t* regex, const char silent)
{
hl_entry *head;
char *h;
head = (hl_entry *)apr_hash_get(hlists, phase, APR_HASH_KEY_STRING);
/* it's possible that handler is multiple handlers separated
by white space */
while (*(h = ap_getword_white(p, &handler)) != '\0') {
if (!head) {
head = hlist_new(p, h, directory, d_is_fnmatch, d_is_location, regex, silent);
apr_hash_set(hlists, phase, APR_HASH_KEY_STRING, head);
}
else {
hlist_append(p, head, h, directory, d_is_fnmatch, d_is_location, regex, silent);
}
}
}
/**
** python_directive_handler
**
* Called by Python*Handler directives.
*
* When used within the same directory, this will have a
* cumulative, rather than overriding effect - i.e. values
* from same directives specified multiple times will be appended.
*
*/
static const char *python_directive_handler(cmd_parms *cmd, py_config* conf,
char *key, const char *val, int silent)
{
char *directory = NULL;
char d_is_fnmatch = 0;
char d_is_location = 0;
ap_regex_t *regex = NULL;
determine_context(cmd->pool, cmd, &directory, &d_is_fnmatch, &d_is_location, ®ex);
/* d_is_location is used by the map_to_storage handler - there is no need for it to
run, if we are inside a Location. We also do not prepend a Location to sys.path. */
conf->d_is_location = d_is_location;
/* a handler may be restricted to certain file type by
* extention using the "| .ext1 .ext2" syntax. When this
* is the case, we will end up with a directive concatenated
* with the extension, one per, e.g.
* "PythonHandler foo | .ext1 .ext2" will result in
* PythonHandler.ext1 foo
* PythonHandler.ext2 foo
*/
const char *exts = val;
val = ap_getword(cmd->pool, &exts, '|');
if (*exts == '\0') {
python_directive_hl_add(cmd->pool, conf->hlists, key, val,
cmd, directory, d_is_fnmatch, d_is_location, regex, silent);
}
else {
char *ext;
/* skip blanks */
while (apr_isspace(*exts)) exts++;
/* repeat for every extension */
while (*(ext = ap_getword_white(cmd->pool, &exts)) != '\0') {
char *s;
/* append extention to the directive name */
s = apr_pstrcat(cmd->pool, key, ext, NULL);
python_directive_hl_add(cmd->pool, conf->hlists, s, val,
cmd, directory, d_is_fnmatch, d_is_location, regex, silent);
}
}
return NULL;
}
/**
** python_directive_flag
**
* Called for FLAG directives.
*
*/
static const char *python_directive_flag(void * mconfig, char *key, int val)
{
py_config *conf;
conf = (py_config *) mconfig;
if (val) {
apr_table_set(conf->directives, key, "1");
}
else {
apr_table_set(conf->directives, key, "0");
}
return NULL;
}
static apr_status_t python_cleanup_handler(void *data);
/**
** python_get_request_object
**
* This creates or retrieves a previously created request object.
* The pointer to request object is stored in req->request_config.
*/
requestobject *python_get_request_object(request_rec *req, const char *phase)
{
py_req_config *req_config;
requestobject *request_obj = NULL;
/* see if there is a request object already */
req_config = (py_req_config *) ap_get_module_config(req->request_config,
&python_module);
if (req_config) {
request_obj = req_config->request_obj;
}
else {
request_obj = (requestobject *)MpRequest_FromRequest(req);
if (!request_obj) return NULL;
/* store the pointer to this object in request_config */
req_config = apr_pcalloc(req->pool, sizeof(py_req_config));
req_config->request_obj = request_obj;
req_config->dynhls = apr_hash_make(req->pool);
req_config->in_filters = apr_hash_make(req->pool);
req_config->out_filters = apr_hash_make(req->pool);
ap_set_module_config(req->request_config, &python_module, req_config);
/* register the clean up directive handler */
apr_pool_cleanup_register(req->pool, (void *)req,
python_cleanup_handler,
apr_pool_cleanup_null);
}
/* make a note of which phase we are in right now */
if (phase)
{
Py_XDECREF(request_obj->phase);
request_obj->phase = MpBytesOrUnicode_FromString(phase);
}
return request_obj;
}
/**
** resolve_directory
**
* resolve any directory match returning the matched directory
*/
static const char *resolve_directory(request_rec *req, const char *directory,
char d_is_fnmatch, ap_regex_t *regex)
{
char *prefix;
int len, dirs, i;
if (!req || !req->filename || (!d_is_fnmatch && !regex))
return directory;
dirs = ap_count_dirs(req->filename) + 1;
len = strlen(req->filename);
prefix = (char*)apr_palloc(req->pool, len+1);
for (i=0; i<=dirs; i++) {
ap_make_dirstr_prefix(prefix, req->filename, i);
#ifdef WIN32
if (d_is_fnmatch && apr_fnmatch(directory, prefix,
APR_FNM_PATHNAME|APR_FNM_CASE_BLIND) == 0) {
#else
if (d_is_fnmatch && apr_fnmatch(directory, prefix,
APR_FNM_PATHNAME) == 0) {
#endif
return prefix;
}
else if (regex && ap_regexec(regex, prefix, 0, NULL, 0) == 0) {
return prefix;
}
if (strcmp(prefix, "/") != 0) {
prefix[strlen(prefix)-1] = '\0';
#ifdef WIN32
if (d_is_fnmatch && apr_fnmatch(directory, prefix,
APR_FNM_PATHNAME|APR_FNM_CASE_BLIND) == 0) {
#else
if (d_is_fnmatch && apr_fnmatch(directory, prefix,
APR_FNM_PATHNAME) == 0) {
#endif
prefix[strlen(prefix)] = '/';
return prefix;
}
else if (regex && ap_regexec(regex, prefix, 0, NULL, 0) == 0) {
prefix[strlen(prefix)] = '/';
return prefix;
}
}
}
return directory;
}
/**
** select_interp_name
**
* (internal)
* figure out the name of the interpreter we should be using
* If this is for a handler, then hle is required. If this is
* for a filter, then fname and is_input are required. If con
* is specified, then its a connection handler.
*/
static const char *select_interp_name(request_rec *req, conn_rec *con,
py_config *conf, hl_entry *hle,
py_handler *fh)
{
const char *s = NULL;
if ((s = apr_table_get(conf->directives, "PythonInterpreter"))) {
/* forced by configuration */
return s;
}
else {
if ((s = apr_table_get(conf->directives, "PythonInterpPerDirectory")) && (strcmp(s, "1") == 0)) {
/* base interpreter on directory where the file is found */
if (req && ap_is_directory(req->pool, req->filename)) {
/*
* We are appending a trailing slash here to be
* consistent with what httpd's core.c does in
* dirsection(), or else we will end up with a
* different interpreter named without the slash.
*/
if (req->filename[strlen(req->filename)-1]=='/')
return ap_make_dirstr_parent(req->pool, req->filename);
else
return ap_make_dirstr_parent(req->pool,
apr_pstrcat(req->pool, req->filename,
"/", NULL ));
} else {
if (req && req->filename)
return ap_make_dirstr_parent(req->pool, req->filename);
else
/*
* In early phases of the request, req->filename is not known,
* so this would have to run in the global interpreter.
*/
return NULL;
}
}
else if ((s = apr_table_get(conf->directives, "PythonInterpPerDirective")) && (strcmp(s, "1") == 0)) {
/*
* base interpreter name on directory where the handler directive
* was last found. If it was in http.conf, then we will use the
* global interpreter.
*/
if (fh) {
s = fh->directory;
}
else if (hle) {
s = hle->directory;
}
else {
return NULL;
}
if (s && (s[0] == '\0'))
return NULL;
else
return s;
}
else {
/* - default: per server - */
if (con)
return con->base_server->server_hostname;
else
return req->server->server_hostname;
}
}
}
/**
** python_handler
**
* A generic python handler. Most handlers should use this.
*/
static int python_handler(request_rec *req, char *phase)
{
PyObject *resultobject = NULL;
interpreterdata *idata;
requestobject *request_obj;
py_config * conf;
int result;
const char *interp_name = NULL;
char *ext = NULL;
hl_entry *hle = NULL;
hl_entry *dynhle = NULL;
hl_entry *hlohle = NULL;
py_req_config *req_conf;
/* get configuration */
conf = (py_config *) ap_get_module_config(req->per_dir_config,
&python_module);
/* get file extension */
if (req->filename) { /* filename is null until after transhandler */
/* get rid of preceeding path */
if ((ext = (char *)ap_strrchr_c(req->filename, '/')) == NULL)
ext = req->filename;
else
++ext;
/* get extension */
ap_getword(req->pool, (const char **)&ext, '.');
if (*ext != '\0')
ext = apr_pstrcat(req->pool, ".", ext, NULL);
}
/* is there an hlist entry, i.e. a handler? */
/* try with extension */
if (ext) {
hle = (hl_entry *)apr_hash_get(conf->hlists,
apr_pstrcat(req->pool, phase, ext, NULL),
APR_HASH_KEY_STRING);
}
/* try without extension if we don't match */
if (!hle) {
hle = (hl_entry *)apr_hash_get(conf->hlists, phase,
APR_HASH_KEY_STRING);
/* also blank out ext since we didn't succeed with it. this is tested
further below */
ext = NULL;
}
req_conf = (py_req_config *) ap_get_module_config(req->request_config,
&python_module);
if (req_conf) {
dynhle = (hl_entry *)apr_hash_get(req_conf->dynhls, phase,
APR_HASH_KEY_STRING);
}
if (! (hle || dynhle)) {
/* nothing to do here */
return DECLINED;
}
/* construct list for the handler list object */
if (!hle) {
hlohle = hlist_copy(req->pool, dynhle);
}
else {
hlohle = hlist_copy(req->pool, hle);
if (dynhle)
hlist_extend(req->pool, hlohle, dynhle);
}
/* resolve wildcard or regex directory patterns. we do not resolve
* Location patterns because there is no use case for it so far */
hle = hlohle;
while (hle) {
if (!hle->d_is_location && (hle->d_is_fnmatch || hle->regex)) {
hle->directory = resolve_directory(req, hle->directory,
hle->d_is_fnmatch, hle->regex);
hle->d_is_fnmatch = 0;
hle->regex = NULL;
}
hle = hle->next;
}
/* determine interpreter to use */
interp_name = select_interp_name(req, NULL, conf, hlohle, NULL);
/* get/create interpreter */
idata = get_interpreter(interp_name);
if (!idata) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"python_handler: Can't get/create interpreter.");
return HTTP_INTERNAL_SERVER_ERROR;
}
/* create/acquire request object */
request_obj = python_get_request_object(req, phase);
/* remember the extension if any. used by publisher */
if (ext)
request_obj->extension = apr_pstrdup(req->pool, ext);
/* construct a new handler list object */
Py_XDECREF(request_obj->hlo);
request_obj->hlo = (hlistobject *)MpHList_FromHLEntry(hlohle);
/*
* Here is where we call into Python!
* This is the C equivalent of
* >>> resultobject = obCallBack.HandlerDispatch(request_object)
*/
resultobject = PyObject_CallMethod(idata->obcallback, "HandlerDispatch",
"O", request_obj);
/* clear phase from request object */
Py_XDECREF(request_obj->phase);
request_obj->phase = NULL;
/* release the lock and destroy tstate */
release_interpreter(idata);
if (! resultobject) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"python_handler: (%s) HandlerDispatch() returned nothing.", phase);
return HTTP_INTERNAL_SERVER_ERROR;
}
else {
/* Attempt to analyze the result as a string indicating which
result to return */
#if PY_MAJOR_VERSION < 3
if (! PyInt_Check(resultobject)) {
#else
if (! PyLong_Check(resultobject)) {
#endif
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"python_handler: (%s) HandlerDispatch() returned non-integer.", phase);
return HTTP_INTERNAL_SERVER_ERROR;
}
else {
#if PY_MAJOR_VERSION < 3
result = PyInt_AsLong(resultobject);
#else
result = PyLong_AsLong(resultobject);
#endif
/* authen handlers need one more thing
* if authentication failed and this handler is not
* authoritative, let the others handle it
*/
if (strcmp(phase, "PythonAuthenHandler") == 0) {
/* This is a prevention measure for what is likely a bug
in mod_auth.c whereby r->user is used even if null.
XXX Remove in the future
*/
if (result == OK && !req->user) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"python_handler: After PythonAuthenHandler req->user is NULL. "
"Assign something to req.user if returning OK to avoid this error.");
return HTTP_INTERNAL_SERVER_ERROR;
}
if (result == HTTP_UNAUTHORIZED)
{
if (! conf->authoritative)
result = DECLINED;
else {
/*
* This will insert a WWW-Authenticate header
* to let the browser know that we are using
* Basic authentication. This function does check
* to make sure that auth is indeed Basic, no
* need to do that here.
*/
ap_note_basic_auth_failure(req);
}
}
}
}
}
/* When the script sets an error status by using req.status,
* it can then either provide its own HTML error message or have
* Apache provide one. To have Apache provide one, you need to send
* no output and return the error from the handler function. However,
* if the script is providing HTML, then the return value of the
* handler should be OK, else the user will get both the script
* output and the Apache output.
*/
/* Another note on status. req->status is used to build req->status_line
* unless status_line is not NULL. req->status has no effect on how the
* server will behave. The error behaviour is dictated by the return
* value of this handler. When the handler returns anything other than OK,
* the server will display the error that matches req->status, unless it is
* 200 (HTTP_OK), in which case it will just show the error matching the return
* value. If the req->status and the return of the handle do not match,
* then the server will first show what req->status shows, then it will
* print "Additionally, X error was recieved", where X is the return code
* of the handle. If the req->status or return code is a weird number that the
* server doesn't know, it will default to 500 Internal Server Error.
*/
/* clean up */
Py_XDECREF(resultobject);
/* return the translated result (or default result) to the Server. */
return result;
}
/**
** python_cleanup_handler
**
* Runs handler registered via PythonCleanupHandler. Clean ups
* registered via register_cleanup() run in python_cleanup() above.
*/
static apr_status_t python_cleanup_handler(void *data)
{
apr_status_t rc;
py_req_config *req_config;
request_rec *req = (request_rec *)data;
rc = python_handler((request_rec *)data, "PythonCleanupHandler");
req_config = (py_req_config *) ap_get_module_config(req->request_config,
&python_module);
if (req_config && req_config->request_obj) {
interpreterdata *idata;
requestobject *request_obj = req_config->request_obj;
/* get interpreter */
idata = get_interpreter(NULL);
if (!idata)
return APR_SUCCESS; /* this return code is ignored by httpd anyway */
Py_XDECREF(request_obj);
/* release interpreter */
release_interpreter(idata);
}
return rc;
}
/**
** python_connection
**
* connection handler
*/
static apr_status_t python_connection(conn_rec *con)
{
PyObject *resultobject = NULL;
interpreterdata *idata;
connobject *conn_obj;
py_config * conf;
int result;
const char *interp_name = NULL;
hl_entry *hle = NULL;
/* get configuration */
conf = (py_config *) ap_get_module_config(con->base_server->module_config,
&python_module);
/* is there a handler? */
hle = (hl_entry *)apr_hash_get(conf->hlists, "PythonConnectionHandler",
APR_HASH_KEY_STRING);
if (! hle) {
/* nothing to do here */
return DECLINED;
}
/* determine interpreter to use */
interp_name = select_interp_name(NULL, con, conf, hle, NULL);
/* get/create interpreter */
idata = get_interpreter(interp_name);
if (!idata) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, con->base_server,
"python_connection: Can't get/create interpreter.");
return HTTP_INTERNAL_SERVER_ERROR;
}
/* create connection object */
conn_obj = (connobject*) MpConn_FromConn(con);
/* create a handler list object */
conn_obj->hlo = (hlistobject *)MpHList_FromHLEntry(hle);
/*
* Here is where we call into Python!
* This is the C equivalent of
* >>> resultobject = obCallBack.ConnectionDispatch(request_object)
*/
resultobject = PyObject_CallMethod(idata->obcallback, "ConnectionDispatch",
"O", conn_obj);
/* release the lock and destroy tstate*/
release_interpreter(idata);
if (! resultobject) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, con->base_server,
"python_connection: ConnectionDispatch() returned nothing.");
return HTTP_INTERNAL_SERVER_ERROR;
}
else {
/* Attempt to analyze the result as a string indicating which
result to return */
#if PY_MAJOR_VERSION < 3
if (! PyInt_Check(resultobject)) {
#else
if (! PyLong_Check(resultobject)) {
#endif
ap_log_error(APLOG_MARK, APLOG_ERR, 0, con->base_server,
"python_connection: ConnectionDispatch() returned non-integer.");
return HTTP_INTERNAL_SERVER_ERROR;
}
else
#if PY_MAJOR_VERSION < 3
result = PyInt_AsLong(resultobject);
#else
result = PyLong_AsLong(resultobject);
#endif
}
/* clean up */
Py_XDECREF(resultobject);
/* return the translated result (or default result) to the Server. */
return result;
}
/**
** python_filter
**
* filter
*/
static apr_status_t python_filter(int is_input, ap_filter_t *f,
apr_bucket_brigade *bb,
ap_input_mode_t mode,
apr_read_type_e block,
apr_size_t readbytes) {
PyObject *resultobject = NULL;
interpreterdata *idata;
requestobject *request_obj;
py_config * conf;
py_req_config * req_config;
const char * interp_name = NULL;
request_rec *req;
filterobject *filter;
python_filter_ctx *ctx;
py_handler *fh;
/* we only allow request level filters so far */
req = f->r;
/* create ctx if not there yet */
if (!f->ctx) {
ctx = (python_filter_ctx *) apr_pcalloc(req->pool, sizeof(python_filter_ctx));
f->ctx = (void *)ctx;
}
else {
ctx = (python_filter_ctx *) f->ctx;
}
/* are we in transparent mode? transparent mode is on after an error,
so a filter can spit out an error without causing infinite loop */
if (ctx->transparent) {
if (is_input)
return ap_get_brigade(f->next, bb, mode, block, readbytes);
else
return ap_pass_brigade(f->next, bb);
}
/* get configuration */
conf = (py_config *) ap_get_module_config(req->per_dir_config,
&python_module);
req_config = (py_req_config *) ap_get_module_config(req->request_config,
&python_module);
/* the name of python function to call */
if (ctx->name) {
if (is_input)
fh = apr_hash_get(req_config->in_filters, ctx->name, APR_HASH_KEY_STRING);
else
fh = apr_hash_get(req_config->out_filters, ctx->name, APR_HASH_KEY_STRING);
} else {
if (is_input)
fh = apr_hash_get(conf->in_filters, f->frec->name, APR_HASH_KEY_STRING);
else
fh = apr_hash_get(conf->out_filters, f->frec->name, APR_HASH_KEY_STRING);
}
if (!fh) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"python_filter: Could not find registered filter.");
return HTTP_INTERNAL_SERVER_ERROR;
}
/* determine interpreter to use */
interp_name = select_interp_name(req, NULL, conf, NULL, fh);
/* get/create interpreter */
idata = get_interpreter(interp_name);
if (!idata) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"python_filter: Can't get/create interpreter.");
return HTTP_INTERNAL_SERVER_ERROR;
}
/* create/acquire request object */
request_obj = python_get_request_object(req, 0);
/* create filter */
filter = (filterobject *)MpFilter_FromFilter(f, bb, is_input, mode, readbytes,
fh->handler, fh->directory);
Py_INCREF(request_obj);
filter->request_obj = request_obj;
/*
* Here is where we call into Python!
* This is the C equivalent of
* >>> resultobject = obCallBack.FilterDispatch(filter_object)
*/
resultobject = PyObject_CallMethod(idata->obcallback, "FilterDispatch", "O",
filter);
/* clean up */
Py_XDECREF(resultobject);
/* release interpreter */
release_interpreter(idata);
return filter->rc;
}
/**
** python_input_filter
**
* input filter
*/
static apr_status_t python_input_filter(ap_filter_t *f,
apr_bucket_brigade *bb,
ap_input_mode_t mode,
apr_read_type_e block,
apr_off_t readbytes)
{
return python_filter(1, f, bb, mode, block, readbytes);
}
/**
** python_output_filter
**
* output filter
*/
static apr_status_t python_output_filter(ap_filter_t *f,
apr_bucket_brigade *bb)
{
return python_filter(0, f, bb, 0, 0, 0);
}
/**
** handle_python
**
* handler function for mod_include tag
*
* The mod_include tag handler interface changed at:
*
* 20030821 (2.1.0-dev) bumped mod_include's entire API
*
* Provide a completely separate implementation for now until
* it is determined whether the new SSI_CREATE_ERROR_BUCKET
* macro can simply be copied to allow backward compatibility.
*/
#if AP_MODULE_MAGIC_AT_LEAST(20030821,0)
static apr_status_t handle_python(include_ctx_t *ctx,
ap_filter_t *f,
apr_bucket_brigade *bb) {
py_config *conf;
const char *interp_name = NULL;
interpreterdata *idata;
requestobject *request_obj;
PyObject *resultobject = NULL;
filterobject *filter;
apr_bucket *tmp_buck;
char *file = f->r->filename;
char *tag = NULL;
char *tag_val = NULL;
PyObject *tagobject = NULL;
PyObject *codeobject = NULL;
request_rec *req = f->r;
if (!(ctx->flags & SSI_FLAG_PRINTING)) {
return APR_SUCCESS;
}
if (ctx->flags & SSI_FLAG_NO_EXEC) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"#python used but not allowed in %s", file);
SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
return APR_SUCCESS;
}
/* get configuration */
conf = (py_config *) ap_get_module_config(req->per_dir_config,
&python_module);
/* determine interpreter to use */
interp_name = select_interp_name(req, NULL, conf, NULL, NULL);
/* get/create interpreter */
idata = get_interpreter(interp_name);
if (!idata) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"handle_python: Can't get/create interpreter.");
Py_XDECREF(tagobject);
Py_XDECREF(codeobject);
return HTTP_INTERNAL_SERVER_ERROR;
}
/* process tags */
while (1) {
optfn_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
if (!tag || !tag_val)
break;
if (!strlen(tag_val)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"empty value for '%s' parameter to tag 'python' in %s",
tag, file);
SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
Py_XDECREF(tagobject);
Py_XDECREF(codeobject);
return APR_SUCCESS;
}
if (!strcmp(tag, "eval") || !strcmp(tag, "exec")) {
if (tagobject) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"multiple 'eval/exec' parameters to tag 'python' in %s",
file);
SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
Py_XDECREF(tagobject);
Py_XDECREF(codeobject);
return APR_SUCCESS;
}
tagobject = MpBytesOrUnicode_FromString(tag);
codeobject = MpBytesOrUnicode_FromString(tag_val);
} else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"unexpected '%s' parameter to tag 'python' in %s",
tag, file);
SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
Py_XDECREF(tagobject);
Py_XDECREF(codeobject);
return APR_SUCCESS;
}
}
if (!tagobject) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"missing 'eval/exec' parameter to tag 'python' in %s",
file);
SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
return APR_SUCCESS;
}
/* create/acquire request object */
request_obj = python_get_request_object(req, 0);
/* create filter */
filter = (filterobject *)MpFilter_FromFilter(f, bb, 0, 0, 0, 0, 0);
Py_INCREF(request_obj);
filter->request_obj = request_obj;
/*
* Here is where we call into Python!
*/
resultobject = PyObject_CallMethod(idata->obcallback,
"IncludeDispatch", "OOO", filter, tagobject, codeobject);
if (!resultobject)
{
SSI_CREATE_ERROR_BUCKET(ctx, f, bb);
release_interpreter(idata);
return APR_SUCCESS;
}
/* clean up */
Py_XDECREF(resultobject);
/* release interpreter */
release_interpreter(idata);
return filter->rc;
}
#else
static apr_status_t handle_python(include_ctx_t *ctx,
apr_bucket_brigade **bb,
request_rec *r_bogus,
ap_filter_t *f,
apr_bucket *head_ptr,
apr_bucket **inserted_head) {
py_config *conf;
const char *interp_name = NULL;
interpreterdata *idata;
requestobject *request_obj;
PyObject *resultobject = NULL;
filterobject *filter;
apr_bucket *tmp_buck;
char *file = f->r->filename;
char *tag = NULL;
char *tag_val = NULL;
PyObject *tagobject = NULL;
PyObject *codeobject = NULL;
request_rec *req = f->r;
if (!(ctx->flags & FLAG_PRINTING)) {
return APR_SUCCESS;
}
if (ctx->flags & FLAG_NO_EXEC) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"#python used but not allowed in %s", file);
CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
return APR_SUCCESS;
}
/* process tags */
while (1) {
optfn_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
if (!tag || !tag_val)
break;
if (!strlen(tag_val)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"empty value for '%s' parameter to tag 'python' in %s",
tag, file);
CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
Py_XDECREF(tagobject);
Py_XDECREF(codeobject);
return APR_SUCCESS;
}
if (!strcmp(tag, "eval") || !strcmp(tag, "exec")) {
if (tagobject) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"multiple 'eval/exec' parameters to tag 'python' in %s",
file);
CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
Py_XDECREF(tagobject);
Py_XDECREF(codeobject);
return APR_SUCCESS;
}
tagobject = MpBytesOrUnicode_FromString(tag);
codeobject = MpBytesOrUnicode_FromString(tag_val);
} else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"unexpected '%s' parameter to tag 'python' in %s",
tag, file);
CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
Py_XDECREF(tagobject);
Py_XDECREF(codeobject);
return APR_SUCCESS;
}
}
if (!tagobject) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"missing 'eval/exec' parameter to tag 'python' in %s",
file);
CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
return APR_SUCCESS;
}
/* get configuration */
conf = (py_config *) ap_get_module_config(req->per_dir_config,
&python_module);
/* determine interpreter to use */
interp_name = select_interp_name(req, NULL, conf, NULL, NULL);
/* get/create interpreter */
idata = get_interpreter(interp_name);
if (!idata) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
"handle_python: Can't get/create interpreter.");
Py_XDECREF(tagobject);
Py_XDECREF(codeobject);
return HTTP_INTERNAL_SERVER_ERROR;
}
/* create/acquire request object */
request_obj = python_get_request_object(req, 0);
/* create filter */
filter = (filterobject *)MpFilter_FromFilter(f, *bb, 0, 0, 0, 0, 0);
Py_INCREF(request_obj);
filter->request_obj = request_obj;
/*
* Here is where we call into Python!
*/
resultobject = PyObject_CallMethod(idata->obcallback,
"IncludeDispatch", "OOO", filter, tagobject, codeobject);
if (!resultobject)
{
CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
release_interpreter(idata);
return APR_SUCCESS;
}
/* clean up */
Py_XDECREF(resultobject);
/* release interpreter */
release_interpreter(idata);
return filter->rc;
}
#endif
/**
** directive_PythonImport
**
* This function called whenever PythonImport directive
* is encountered. Note that this function does not actually
* import anything, it just remembers what needs to be imported.
* The actual importing is done later
* in the ChildInitHandler. This is because this function here
* is called before the python_init and before the suid and fork.
*
*/
static const char *directive_PythonImport(cmd_parms *cmd, void *mconfig,
const char *module, const char *interp_name)
{
py_config *conf = ap_get_module_config(cmd->server->module_config,
&python_module);
if (!conf->imports)
conf->imports = apr_table_make(cmd->pool, 4);
apr_table_add(conf->imports, interp_name, module);
return NULL;
}
/**
** directive_PythonPath
**
* This function called whenever PythonPath directive
* is encountered.
*/
static const char *directive_PythonPath(cmd_parms *cmd, void *mconfig,
const char *val) {
const char *rc = python_directive(cmd, mconfig, "PythonPath", val);
if (!cmd->path) {
py_config *conf = ap_get_module_config(cmd->server->module_config,
&python_module);
return python_directive(cmd, conf, "PythonPath", val);
}
return rc;
}
/**
** directive_PythonInterpreter
**
* This function called whenever PythonInterpreter directive
* is encountered.
*/
static const char *directive_PythonInterpreter(cmd_parms *cmd, void *mconfig,
const char *val) {
const char *rc = python_directive(cmd, mconfig, "PythonInterpreter", val);
if (!cmd->path) {
py_config *conf = ap_get_module_config(cmd->server->module_config,
&python_module);
return python_directive(cmd, conf, "PythonInterpreter", val);
}
return rc;
}
/**
** directive_PythonDebug
**
* This function called whenever PythonDebug directive
* is encountered.
*/
static const char *directive_PythonDebug(cmd_parms *cmd, void *mconfig,
int val) {
const char *rc = python_directive_flag(mconfig, "PythonDebug", val);
if (!cmd->path) {
py_config *conf = ap_get_module_config(cmd->server->module_config,
&python_module);
return python_directive_flag(conf, "PythonDebug", val);
}
return rc;
}
/**
** directive_PythonEnablePdb
**
* This function called whenever PythonEnablePdb directive
* is encountered.
*/
static const char *directive_PythonEnablePdb(cmd_parms *cmd, void *mconfig,
int val) {
const char *rc = python_directive_flag(mconfig, "PythonEnablePdb", val);
if (!cmd->path) {
py_config *conf = ap_get_module_config(cmd->server->module_config,
&python_module);
return python_directive_flag(conf, "PythonEnablePdb", val);
}
return rc;
}
/**
** directive_PythonInterpPerDirective
**
* This function called whenever PythonInterpPerDirective directive
* is encountered.
*/
static const char *directive_PythonInterpPerDirective(cmd_parms *cmd,
void *mconfig, int val) {
const char *rc = python_directive_flag(mconfig, "PythonInterpPerDirective", val);
if (!cmd->path) {
py_config *conf = ap_get_module_config(cmd->server->module_config,
&python_module);
return python_directive_flag(conf, "PythonInterpPerDirective", val);
}
return rc;
}
/**
** directive_PythonInterpPerDirectory
**
* This function called whenever PythonInterpPerDirectory directive
* is encountered.
*/
static const char *directive_PythonInterpPerDirectory(cmd_parms *cmd,
void *mconfig, int val) {
return python_directive_flag(mconfig, "PythonInterpPerDirectory", val);
}
/**
** directive_PythonAutoReload
**
* This function called whenever PythonAutoReload directive
* is encountered.
*/
static const char *directive_PythonAutoReload(cmd_parms *cmd,
void *mconfig, int val) {
const char *rc = python_directive_flag(mconfig, "PythonAutoReload", val);
if (!cmd->path) {
py_config *conf = ap_get_module_config(cmd->server->module_config,
&python_module);
return python_directive_flag(conf, "PythonAutoReload", val);
}
return rc;
}
/**
** directive_PythonOption
**
* This function is called every time PythonOption directive
* is encountered. It sticks the option into a table containing
* a list of options. This table is part of the local config structure.
*/
static const char *directive_PythonOption(cmd_parms *cmd, void * mconfig,
const char *key, const char *val)
{
py_config *conf;
conf = (py_config *) mconfig;
if(val!=NULL) {
apr_table_set(conf->options, key, val);
if (!cmd->path) {
conf = ap_get_module_config(cmd->server->module_config,
&python_module);
apr_table_set(conf->options, key, val);
}
}
else {
/** We don't remove the value, but set it
to an empty string. There is no possibility
of colliding with an actual value, since
an entry string precisely means 'remove the value' */
apr_table_set(conf->options, key, "");
if (!cmd->path) {
conf = ap_get_module_config(cmd->server->module_config,
&python_module);
apr_table_set(conf->options, key, "");
}
}
return NULL;
}
/**
** directive_PythonOptimize
**
* This function called whenever PythonOptimize directive
* is encountered.
*/
static const char *directive_PythonOptimize(cmd_parms *cmd, void *mconfig,
int val) {
#if PY_VERSION_HEX < 0x030C0000
if ((val) && (Py_OptimizeFlag != 2))
Py_OptimizeFlag = 2;
#endif
return NULL;
}
/**
** Python*Handler directives
**
*/
static const char *directive_PythonAccessHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonAccessHandler", val, NOTSILENT);
}
static const char *directive_PythonAuthenHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonAuthenHandler", val, NOTSILENT);
}
static const char *directive_PythonAuthzHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonAuthzHandler", val, NOTSILENT);
}
static const char *directive_PythonCleanupHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonCleanupHandler", val, NOTSILENT);
}
static const char *directive_PythonConnectionHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
py_config *conf = ap_get_module_config(cmd->server->module_config,
&python_module);
return python_directive_handler(cmd, conf, "PythonConnectionHandler", val, NOTSILENT);
}
static const char *directive_PythonFixupHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonFixupHandler", val, NOTSILENT);
}
static const char *directive_PythonHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonHandler", val, NOTSILENT);
}
static const char *directive_PythonHeaderParserHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonHeaderParserHandler", val, NOTSILENT);
}
static const char *directive_PythonInitHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonInitHandler", val, NOTSILENT);
}
static const char *directive_PythonHandlerModule(cmd_parms *cmd, void *mconfig,
const char *val) {
/*
* This handler explodes into all other handlers, but their absense will be
* silently ignored.
*/
/*
* XXX Not used at present. See problems noted against connection
* handler below.
*
py_config *srv_conf = ap_get_module_config(cmd->server->module_config,
&python_module);
*/
python_directive_handler(cmd, mconfig, "PythonPostReadRequestHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonTransHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonHeaderParserHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonAccessHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonAuthenHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonAuthzHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonTypeHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonFixupHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonInitHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonLogHandler", val, SILENT);
python_directive_handler(cmd, mconfig, "PythonCleanupHandler", val, SILENT);
/*
* XXX There is a bug here with PythonConnectionHandler which can
* cause an infinite loop when the handler is added to the handler
* list. Cause is unknown so simply disable it for now. If someone
* really needs a connection handler, they can use the directive
* PythonConnectionHandler explicitly still and not rely on the
* PythonHandlerModule directive doing it automatically.
*
python_directive_handler(cmd, srv_conf, "PythonConnectionHandler", val, SILENT);
*/
return NULL;
}
static const char *directive_PythonPostReadRequestHandler(cmd_parms *cmd,
void * mconfig,
const char *val) {
if (strchr((char *)val, '|'))
return "PythonPostReadRequestHandler does not accept \"| .ext\" syntax.";
return python_directive_handler(cmd, mconfig, "PythonPostReadRequestHandler", val,NOTSILENT);
}
static const char *directive_PythonTransHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
if (strchr((char *)val, '|'))
return "PythonTransHandler does not accept \"| .ext\" syntax.";
return python_directive_handler(cmd, mconfig, "PythonTransHandler", val, NOTSILENT);
}
static const char *directive_PythonTypeHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonTypeHandler", val, NOTSILENT);
}
static const char *directive_PythonLogHandler(cmd_parms *cmd, void *mconfig,
const char *val) {
return python_directive_handler(cmd, mconfig, "PythonLogHandler", val, NOTSILENT);
}
static const char *directive_PythonInputFilter(cmd_parms *cmd, void *mconfig,
const char *handler, const char *name) {
py_config *conf;
py_handler *fh;
ap_filter_rec_t *frec;
char *directory = NULL;
char d_is_fnmatch = 0;
char d_is_location = 0;
ap_regex_t *regex = NULL;
if (!name)
name = apr_pstrdup(cmd->pool, handler);
/* register the filter NOTE - this only works so long as the
directive is only allowed in the main config. For .htaccess we
would have to make sure not to duplicate this */
frec = ap_register_input_filter(name, python_input_filter, NULL, AP_FTYPE_RESOURCE);
conf = (py_config *) mconfig;
determine_context(cmd->pool, cmd, &directory, &d_is_fnmatch, &d_is_location, ®ex);
fh = (py_handler *) apr_pcalloc(cmd->pool, sizeof(py_handler));
fh->handler = (char *)handler;
fh->directory = directory;
fh->d_is_fnmatch = d_is_fnmatch;
fh->d_is_location = d_is_location;
fh->regex = regex;
apr_hash_set(conf->in_filters, frec->name, APR_HASH_KEY_STRING, fh);
return NULL;
}
static const char *directive_PythonOutputFilter(cmd_parms *cmd, void *mconfig,
const char *handler, const char *name) {
py_config *conf;
py_handler *fh;
ap_filter_rec_t *frec;
char *directory = NULL;
char d_is_fnmatch = 0;
char d_is_location = 0;
ap_regex_t *regex = NULL;
if (!name)
name = apr_pstrdup(cmd->pool, handler);
/* register the filter NOTE - this only works so long as the
directive is only allowed in the main config. For .htaccess we
would have to make sure not to duplicate this */
frec = ap_register_output_filter(name, python_output_filter, NULL, AP_FTYPE_RESOURCE);
determine_context(cmd->pool, cmd, &directory, &d_is_fnmatch, &d_is_location, ®ex);
conf = (py_config *) mconfig;
fh = (py_handler *) apr_pcalloc(cmd->pool, sizeof(py_handler));
fh->handler = (char *)handler;
fh->directory = directory;
fh->d_is_fnmatch = d_is_fnmatch;
fh->d_is_location = d_is_location;
fh->regex = regex;
apr_hash_set(conf->out_filters, frec->name, APR_HASH_KEY_STRING, fh);
return NULL;
}
/**
** python_finalize
**
* We create a thread state just so we can run Py_Finalize()
*/
static apr_status_t python_finalize(void *data)
{
interpreterdata *idata;
idata = get_interpreter(NULL);
if (idata) {
Py_Finalize();
PyEval_SaveThread();
}
return APR_SUCCESS;
}
/**
** Handlers
**
*/
static void PythonChildInitHandler(apr_pool_t *p, server_rec *s)
{
const apr_array_header_t *ah;
apr_table_entry_t *elts;
int i;
py_config *conf = ap_get_module_config(s->module_config, &python_module);
py_global_config *glb;
PyObject *resultobject = NULL;
/* accordig Py C Docs we must do this after forking */
PyEval_RestoreThread(global_tstate);
#if PY_VERSION_HEX < 0x03070000
PyOS_AfterFork();
#elif PY_VERSION_HEX < 0x030D0000
PyOS_AfterFork_Child();
#endif
interpreterdata *idata = save_interpreter(MAIN_INTERPRETER, PyThreadState_Get());
if (!idata)
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"PythonChildInitHandler: save_interpreter() returned NULL. No more memory?");
if (PyEval_SaveThread() != global_tstate) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
"PythonChildInitHandler: not in global thread state, aborting.");
return;
}
/*
* Cleanups registered first will be called last. This will
* end the Python interpreter *after* all other cleanups.
*/
/*
* XXX Trying to cleanup Python on process shutdown causes
* problems. This seems to mainly be an issue where there
* are user created threads which are running in parallel as
* the environment they are running in will be destroyed
* from underneath them potentially resulting in crashes,
* process hangs or simply Python errors. There is also a
* small chance that finalization code can be called within
* a signal handler in some configurations which could cause
* problems as well. Thus disable cleanup of Python when
* child processes are being shutdown. (MODPYTHON-109)
*
* apr_pool_cleanup_register(p, NULL, python_finalize, apr_pool_cleanup_null);
*/
/*
* Reinit mutexes
*/
/* this will return it if it already exists */
glb = python_create_global_config(s);
reinit_mutexes(s, p, glb);
/*
* remember the pool in a global var. we may use it
* later in server.register_cleanup()
*/
child_init_pool = p;
/* register python handler for mod_include. could probably
* also have done this in python_init() instead */
optfn_register_include_handler = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
optfn_ssi_get_tag_and_value = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value);
optfn_ssi_parse_string = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string);
if (optfn_register_include_handler && optfn_ssi_get_tag_and_value &&
optfn_ssi_parse_string) {
optfn_register_include_handler("python", handle_python);
}
/*
* Now run PythonImports
*/
if (conf->imports) {
ah = apr_table_elts (conf->imports);
elts = (apr_table_entry_t *) ah->elts;
i = ah->nelts;
while (i--) {
if (elts[i].key) {
interpreterdata *idata;
const char *interp_name = elts[i].key;
const char *module_name = elts[i].val;
/* get interpreter */
idata = get_interpreter(interp_name);
if (!idata)
return;
/*
* Call into Python to do import.
* This is the C equivalent of
* >>> resultobject = obCallBack.ImportDispatch(module_name)
*/
resultobject = PyObject_CallMethod(idata->obcallback,
"ImportDispatch", "s", module_name);
if (!resultobject) {
if (PyErr_Occurred()) {
PyErr_Print();
fflush(stderr);
}
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"directive_PythonImport: error importing %s",
(!module_name) ? "" : module_name);
}
/* clean up */
Py_XDECREF(resultobject);
/* release interpreter */
release_interpreter(idata);
}
}
}
}
static int PythonConnectionHandler(conn_rec *con) {
return python_connection(con);
}
static int PythonAccessHandler(request_rec *req) {
return python_handler(req, "PythonAccessHandler");
}
static int PythonAuthenHandler(request_rec *req) {
return python_handler(req, "PythonAuthenHandler");
}
static int PythonAuthzHandler(request_rec *req) {
return python_handler(req, "PythonAuthzHandler");
}
static int PythonFixupHandler(request_rec *req) {
return python_handler(req, "PythonFixupHandler");
}
static int PythonHandler(request_rec *req) {
/*
* In Apache 2.0, all handlers receive a request and have
* a chance to process them. Therefore, we need to only
* handle those that we explicitly agreed to handle (see
* above).
*/
if (!req->handler || (strcmp(req->handler, "mod_python") &&
strcmp(req->handler, "python-program")))
return DECLINED;
return python_handler(req, "PythonHandler");
}
static int PythonHeaderParserHandler(request_rec *req) {
int rc;
/* run PythonInitHandler, if not already */
if (! apr_table_get(req->notes, "python_init_ran")) {
rc = python_handler(req, "PythonInitHandler");
if ((rc != OK) && (rc != DECLINED))
return rc;
}
return python_handler(req, "PythonHeaderParserHandler");
}
static int PythonLogHandler(request_rec *req) {
return python_handler(req, "PythonLogHandler");
}
static int PythonPostReadRequestHandler(request_rec *req) {
int rc;
/* run PythonInitHandler */
rc = python_handler(req, "PythonInitHandler");
apr_table_set(req->notes, "python_init_ran", "1");
if ((rc != OK) && (rc != DECLINED))
return rc;
return python_handler(req, "PythonPostReadRequestHandler");
}
static int PythonTransHandler(request_rec *req) {
return python_handler(req, "PythonTransHandler");
}
static int PythonMapToStorageHandler(request_rec *req) {
py_config *conf = (py_config *) ap_get_module_config(req->per_dir_config,
&python_module);
if (conf->d_is_location) {
/* there is no storage, no need to hit the slow filesystem to
* map to storage */
return OK;
}
return DECLINED;
}
static int PythonTypeHandler(request_rec *req) {
return python_handler(req, "PythonTypeHandler");
}
static void python_register_hooks(apr_pool_t *p)
{
/* module initializer */
ap_hook_post_config(python_init,
NULL, NULL, APR_HOOK_MIDDLE);
/* [0] raw connection handling */
ap_hook_process_connection(PythonConnectionHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [1] post read_request handling */
ap_hook_post_read_request(PythonPostReadRequestHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [2] filename-to-URI translation */
ap_hook_translate_name(PythonTransHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [2.5] storage mapping (TODO add me as a valid handler?) */
ap_hook_translate_name(PythonMapToStorageHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [3] header parser */
ap_hook_header_parser(PythonHeaderParserHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [4] check access by host address */
ap_hook_access_checker(PythonAccessHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [5] check/validate user_id */
ap_hook_check_user_id(PythonAuthenHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [6] check user_id is valid *here* */
ap_hook_auth_checker(PythonAuthzHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [7] MIME type checker/setter */
ap_hook_type_checker(PythonTypeHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [8] fixups */
ap_hook_fixups(PythonFixupHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* [9] filter insert opportunity */
/* ap_hook_insert_filter(PythonInsertFilter,
NULL, NULL, APR_HOOK_MIDDLE); */
/* [10] is for the handlers; see below */
ap_hook_handler(PythonHandler, NULL, NULL, APR_HOOK_MIDDLE);
/* [11] logger */
ap_hook_log_transaction(PythonLogHandler,
NULL, NULL, APR_HOOK_MIDDLE);
/* dynamic input/output filter entry points */
ap_register_input_filter(FILTER_NAME, python_input_filter, NULL,
AP_FTYPE_RESOURCE);
ap_register_output_filter(FILTER_NAME, python_output_filter, NULL,
AP_FTYPE_RESOURCE);
/* process initializer */
ap_hook_child_init(PythonChildInitHandler,
NULL, NULL, APR_HOOK_MIDDLE);
}
/* command table */
command_rec python_commands[] =
{
AP_INIT_RAW_ARGS(
"PythonAccessHandler", directive_PythonAccessHandler, NULL, OR_ALL,
"Python access by host address handlers."),
AP_INIT_RAW_ARGS(
"PythonAuthenHandler", directive_PythonAuthenHandler, NULL, OR_ALL,
"Python authentication handlers."),
AP_INIT_FLAG(
"PythonAutoReload", directive_PythonAutoReload, NULL, OR_ALL,
"Set to Off if you don't want changed modules to reload."),
AP_INIT_RAW_ARGS(
"PythonAuthzHandler", directive_PythonAuthzHandler, NULL, OR_ALL,
"Python authorization handlers."),
AP_INIT_RAW_ARGS(
"PythonCleanupHandler", directive_PythonCleanupHandler, NULL, OR_ALL,
"Python clean up handlers."),
AP_INIT_RAW_ARGS(
"PythonConnectionHandler", directive_PythonConnectionHandler, NULL, RSRC_CONF,
"Python connection handlers."),
AP_INIT_FLAG(
"PythonDebug", directive_PythonDebug, NULL, OR_ALL,
"Send (most) Python error output to the client rather than logfile."),
AP_INIT_FLAG(
"PythonEnablePdb", directive_PythonEnablePdb, NULL, OR_ALL,
"Run handlers in PDB (Python Debugger). Use with -DONE_PROCESS."),
AP_INIT_RAW_ARGS(
"PythonFixupHandler", directive_PythonFixupHandler, NULL, OR_ALL,
"Python fixups handlers."),
AP_INIT_RAW_ARGS(
"PythonHandler", directive_PythonHandler, NULL, OR_ALL,
"Python request handlers."),
AP_INIT_RAW_ARGS(
"PythonHeaderParserHandler", directive_PythonHeaderParserHandler, NULL, OR_ALL,
"Python header parser handlers."),
AP_INIT_TAKE2(
"PythonImport", directive_PythonImport, NULL, RSRC_CONF,
"Module and interpreter name to be imported at server/child init time."),
AP_INIT_RAW_ARGS(
"PythonInitHandler", directive_PythonInitHandler, NULL, OR_ALL,
"Python request initialization handler."),
AP_INIT_FLAG(
"PythonInterpPerDirective", directive_PythonInterpPerDirective, NULL, OR_ALL,
"Create subinterpreters per directive."),
AP_INIT_FLAG(
"PythonInterpPerDirectory", directive_PythonInterpPerDirectory, NULL, OR_ALL,
"Create subinterpreters per directory."),
AP_INIT_TAKE1(
"PythonInterpreter", directive_PythonInterpreter, NULL, OR_ALL,
"Forces a specific Python interpreter name to be used here."),
AP_INIT_RAW_ARGS(
"PythonLogHandler", directive_PythonLogHandler, NULL, OR_ALL,
"Python logger handlers."),
AP_INIT_RAW_ARGS(
"PythonHandlerModule", directive_PythonHandlerModule, NULL, OR_ALL,
"A Python module containing handlers to be executed."),
AP_INIT_FLAG(
"PythonOptimize", directive_PythonOptimize, NULL, RSRC_CONF,
"Set the equivalent of the -O command-line flag on the interpreter."),
AP_INIT_TAKE12(
"PythonOption", directive_PythonOption, NULL, OR_ALL,
"Useful to pass custom configuration information to scripts."),
AP_INIT_TAKE1(
"PythonPath", directive_PythonPath, NULL, OR_ALL,
"Python path, specified in Python list syntax."),
AP_INIT_RAW_ARGS(
"PythonPostReadRequestHandler", directive_PythonPostReadRequestHandler,
NULL, RSRC_CONF,
"Python post read-request handlers."),
AP_INIT_RAW_ARGS(
"PythonTransHandler", directive_PythonTransHandler, NULL, RSRC_CONF,
"Python filename to URI translation handlers."),
AP_INIT_RAW_ARGS(
"PythonTypeHandler", directive_PythonTypeHandler, NULL, OR_ALL,
"Python MIME type checker/setter handlers."),
AP_INIT_TAKE12(
"PythonInputFilter", directive_PythonInputFilter, NULL, RSRC_CONF|ACCESS_CONF,
"Python input filter."),
AP_INIT_TAKE12(
"PythonOutputFilter", directive_PythonOutputFilter, NULL, RSRC_CONF|ACCESS_CONF,
"Python output filter."),
{NULL}
};
module python_module =
{
STANDARD20_MODULE_STUFF,
python_create_dir_config, /* per-directory config creator */
python_merge_config, /* dir config merger */
python_create_srv_config, /* server config creator */
python_merge_config, /* server config merger */
python_commands, /* command table */
python_register_hooks /* register hooks */
};