1
1
/* vim: set sw=4 expandtab : */
4
* Copyright 2007-2008 GRAHAM DUMPLETON
4
* Copyright 2007-2009 GRAHAM DUMPLETON
6
6
* Licensed under the Apache License, Version 2.0 (the "License");
7
7
* you may not use this file except in compliance with the License.
245
245
/* Version and module information. */
247
247
#define MOD_WSGI_MAJORVERSION_NUMBER 2
248
#define MOD_WSGI_MINORVERSION_NUMBER 2
249
#define MOD_WSGI_VERSION_STRING "2.3"
248
#define MOD_WSGI_MINORVERSION_NUMBER 4
249
#define MOD_WSGI_VERSION_STRING "2.4"
251
251
#if AP_SERVER_MAJORVERSION_NUMBER < 2
252
252
module MODULE_VAR_EXPORT wsgi_module;
1055
1055
PyObject_Del(self);
1058
static PyObject *Log_close(LogObject *self, PyObject *args)
1060
if (self->expired) {
1061
PyErr_SetString(PyExc_RuntimeError, "log object has expired");
1065
if (!PyArg_ParseTuple(args, ":close"))
1068
PyErr_SetString(PyExc_RuntimeError, "log object cannot be closed");
1058
1072
static PyObject *Log_flush(LogObject *self, PyObject *args)
1060
1074
if (self->expired) {
1247
1261
return Py_None;
1264
static PyObject *Log_closed(LogObject *self, void *closure)
1266
Py_INCREF(Py_False);
1270
static PyObject *Log_isatty(LogObject *self, void *closure)
1272
Py_INCREF(Py_False);
1250
1276
static PyMethodDef Log_methods[] = {
1251
{ "flush", (PyCFunction)Log_flush, METH_VARARGS, 0},
1252
{ "write", (PyCFunction)Log_write, METH_VARARGS, 0},
1253
{ "writelines", (PyCFunction)Log_writelines, METH_VARARGS, 0},
1277
{ "close", (PyCFunction)Log_close, METH_VARARGS, 0 },
1278
{ "flush", (PyCFunction)Log_flush, METH_VARARGS, 0 },
1279
{ "write", (PyCFunction)Log_write, METH_VARARGS, 0 },
1280
{ "writelines", (PyCFunction)Log_writelines, METH_VARARGS, 0 },
1284
static PyGetSetDef Log_getset[] = {
1285
{ "closed", (getter)Log_closed, NULL, 0 },
1286
{ "isatty", (getter)Log_isatty, NULL, 0 },
1257
1290
static PyTypeObject Log_Type = {
1258
1291
/* The ob_type field must be initialized in the module init function
1259
1292
* to be portable to Windows without using C++. */
1686
1719
while (!self->done) {
1687
/* Increase the size of the string by 25%. */
1689
size = size + (size >> 2);
1691
if (_PyString_Resize(&result, size))
1694
buffer = PyString_AS_STRING((PyStringObject *)result);
1720
if (length == size) {
1721
/* Increase the size of the string by 25%. */
1723
size = size + (size >> 2);
1725
if (_PyString_Resize(&result, size))
1728
buffer = PyString_AS_STRING((PyStringObject *)result);
1696
1731
/* Now make succesive attempt at reading data. */
1992
2027
memcpy(self->buffer, p, self->size);
1995
if (buffer[length-1] != '\n') {
2030
if (buffer[length-1] != '\n' && length == size) {
1996
2031
/* Increase size of string and keep going. */
1998
2033
size = size + (size >> 2);
2084
2119
static PyMethodDef Input_methods[] = {
2085
{ "close", (PyCFunction)Input_close, METH_VARARGS, 0},
2086
{ "read", (PyCFunction)Input_read, METH_VARARGS, 0},
2087
{ "readline", (PyCFunction)Input_readline, METH_VARARGS, 0},
2088
{ "readlines", (PyCFunction)Input_readlines, METH_VARARGS, 0},
2120
{ "close", (PyCFunction)Input_close, METH_VARARGS, 0 },
2121
{ "read", (PyCFunction)Input_read, METH_VARARGS, 0 },
2122
{ "readline", (PyCFunction)Input_readline, METH_VARARGS, 0 },
2123
{ "readlines", (PyCFunction)Input_readlines, METH_VARARGS, 0 },
2460
if (strchr(name, '\n') != 0 || strchr(value, '\n') != 0) {
2461
PyErr_Format(PyExc_ValueError, "embedded newline in "
2462
"response header with name '%s' and value '%s'",
2425
2467
if (!strcasecmp(name, "Content-Type")) {
2426
2468
#if AP_SERVER_MAJORVERSION_NUMBER < 2
2427
2469
r->content_type = apr_pstrdup(r->pool, value);
2686
2728
APR_BRIGADE_INSERT_TAIL(bb, b);
2730
b = apr_bucket_flush_create(r->connection->bucket_alloc);
2731
APR_BRIGADE_INSERT_TAIL(bb, b);
2688
2733
b = apr_bucket_eos_create(r->connection->bucket_alloc);
2689
2734
APR_BRIGADE_INSERT_TAIL(bb, b);
2801
2846
PyDict_SetItemString(vars, "wsgi.file_wrapper", object);
2802
2847
Py_DECREF(object);
2849
/* Add mod_wsgi version information. */
2851
object = Py_BuildValue("(ii)", MOD_WSGI_MAJORVERSION_NUMBER,
2852
MOD_WSGI_MINORVERSION_NUMBER);
2853
PyDict_SetItemString(vars, "mod_wsgi.version", object);
2805
2857
* If Apache extensions are enabled and running in embedded
2806
2858
* mode add a CObject reference to the Apache request_rec
3207
3259
static PyMethodDef Adapter_methods[] = {
3208
{ "start_response", (PyCFunction)Adapter_start_response, METH_VARARGS, 0},
3209
{ "write", (PyCFunction)Adapter_write, METH_VARARGS, 0},
3210
{ "file_wrapper", (PyCFunction)Adapter_file_wrapper, METH_VARARGS, 0},
3260
{ "start_response", (PyCFunction)Adapter_start_response, METH_VARARGS, 0 },
3261
{ "write", (PyCFunction)Adapter_write, METH_VARARGS, 0 },
3262
{ "file_wrapper", (PyCFunction)Adapter_file_wrapper, METH_VARARGS, 0 },
3368
3420
static PyMethodDef Stream_methods[] = {
3369
{ "close", (PyCFunction)Stream_close, METH_VARARGS, 0},
3421
{ "close", (PyCFunction)Stream_close, METH_VARARGS, 0 },
3753
* If running in daemon process, override as appropriate
3754
* the USER, USERNAME or LOGNAME environment variables
3755
* so that they match the user that the process is running
3756
* as. Need to do this else we inherit the value from the
3757
* Apache parent process which is likely wrong as will be
3758
* root or the user than ran sudo when Apache started.
3759
* Can't update these for normal Apache child processes
3760
* as that would change the expected environment of other
3765
if (wsgi_daemon_pool) {
3766
module = PyImport_ImportModule("os");
3769
PyObject *dict = NULL;
3770
PyObject *key = NULL;
3771
PyObject *value = NULL;
3773
dict = PyModule_GetDict(module);
3774
object = PyDict_GetItemString(dict, "environ");
3777
struct passwd *pwent;
3779
pwent = getpwuid(geteuid());
3781
if (getenv("USER")) {
3782
key = PyString_FromString("USER");
3783
value = PyString_FromString(pwent->pw_name);
3785
PyObject_SetItem(object, key, value);
3791
if (getenv("USERNAME")) {
3792
key = PyString_FromString("USERNAME");
3793
value = PyString_FromString(pwent->pw_name);
3795
PyObject_SetItem(object, key, value);
3801
if (getenv("LOGNAME")) {
3802
key = PyString_FromString("LOGNAME");
3803
value = PyString_FromString(pwent->pw_name);
3805
PyObject_SetItem(object, key, value);
3701
3818
* If running in daemon process, override HOME environment
3702
3819
* variable so that is matches the home directory of the
3703
3820
* user that the process is running as. Need to do this as
3779
3896
* added using site.addsitedir() so that any Python .pth
3780
3897
* files are opened and additional directories so defined
3781
3898
* are added to default Python search path as well. This
3782
* allows virtual Python environments to work.
3899
* allows virtual Python environments to work. Note that
3900
* site.addsitedir() adds new directories at the end of
3901
* sys.path when they really need to be added in order at
3902
* the start. We therefore need to do a fiddle and shift
3903
* any newly added directories to the start of sys.path.
3785
3906
if (!wsgi_daemon_pool)
3786
3907
wsgi_python_path = wsgi_server_config->python_path;
3788
3909
if (wsgi_python_path) {
3910
PyObject *path = NULL;
3789
3912
module = PyImport_ImportModule("site");
3913
path = PySys_GetObject("path");
3915
if (module && path) {
3792
3916
PyObject *dict = NULL;
3918
PyObject *old = NULL;
3919
PyObject *new = NULL;
3920
PyObject *tmp = NULL;
3922
PyObject *item = NULL;
3926
old = PyList_New(0);
3927
new = PyList_New(0);
3928
tmp = PyList_New(0);
3930
for (i=0; i<PyList_Size(path); i++)
3931
PyList_Append(old, PyList_GetItem(path, i));
3794
3933
dict = PyModule_GetDict(module);
3795
3934
object = PyDict_GetItemString(dict, "addsitedir");
3903
4042
Py_END_ALLOW_THREADS
4045
for (i=0; i<PyList_Size(path); i++)
4046
PyList_Append(tmp, PyList_GetItem(path, i));
4048
for (i=0; i<PyList_Size(tmp); i++) {
4049
item = PyList_GetItem(tmp, i);
4050
if (!PySequence_Contains(old, item)) {
4051
int index = PySequence_Index(path, item);
4052
PyList_Append(new, item);
4054
PySequence_DelItem(path, index);
4058
PyList_SetSlice(path, 0, 0, new);
3906
4064
Py_DECREF(module);
3909
Py_BEGIN_ALLOW_THREADS
3910
ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
3911
"mod_wsgi (pid=%d): Unable to import 'site' "
3912
"module.", getpid());
3913
Py_END_ALLOW_THREADS
4068
Py_BEGIN_ALLOW_THREADS
4069
ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
4070
"mod_wsgi (pid=%d): Unable to import 'site' "
4071
"module.", getpid());
4072
Py_END_ALLOW_THREADS
4076
Py_BEGIN_ALLOW_THREADS
4077
ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
4078
"mod_wsgi (pid=%d): Lookup for 'sys.path' "
4079
"failed.", getpid());
4080
Py_END_ALLOW_THREADS
3962
4130
MOD_WSGI_MAJORVERSION_NUMBER,
3963
4131
MOD_WSGI_MINORVERSION_NUMBER));
4134
* Add information about process group and application
4135
* group to the Python 'mod_wsgi' module.
4138
PyModule_AddObject(module, "process_group",
4139
PyString_FromString(wsgi_daemon_group));
4140
PyModule_AddObject(module, "application_group",
4141
PyString_FromString(name));
3965
4143
Py_DECREF(module);
4448
4626
return APR_SUCCESS;
4629
#if AP_SERVER_MAJORVERSION_NUMBER < 2
4630
static void wsgi_python_parent_cleanup(void *data)
4632
static apr_status_t wsgi_python_parent_cleanup(void *data)
4635
if (wsgi_parent_pid == getpid()) {
4637
* Destroy Python itself including the main
4638
* interpreter. If mod_python is being loaded it
4639
* is left to mod_python to destroy Python,
4640
* although it currently doesn't do so.
4643
if (wsgi_python_initialized)
4647
#if AP_SERVER_MAJORVERSION_NUMBER >= 2
4451
4653
static void wsgi_python_init(apr_pool_t *p)
4453
4655
#if defined(DARWIN) && (AP_SERVER_MAJORVERSION_NUMBER < 2)
4483
4685
/* Initialise Python. */
4485
4687
ap_log_error(APLOG_MARK, WSGI_LOG_INFO(0), wsgi_server,
4486
"mod_wsgi: Initializing Python.");
4688
"mod_wsgi (pid=%d): Initializing Python.", getpid());
4488
4690
initialized = 1;
4513
4715
PyEval_ReleaseLock();
4515
4717
wsgi_python_initialized = 1;
4720
* Register cleanups to be performed on parent restart
4721
* or shutdown. This will destroy Python itself.
4724
#if AP_SERVER_MAJORVERSION_NUMBER < 2
4725
ap_register_cleanup(p, NULL, wsgi_python_parent_cleanup,
4728
apr_pool_cleanup_register(p, NULL, wsgi_python_parent_cleanup,
4729
apr_pool_cleanup_null);
4755
4970
apr_finfo_t finfo;
4756
if (apr_stat(&finfo, filename, APR_FINFO_SIZE,
4971
if (apr_stat(&finfo, filename, APR_FINFO_NORM,
4757
4972
pool) != APR_SUCCESS) {
4758
4973
object = PyLong_FromLongLong(0);
4821
5036
apr_finfo_t finfo;
4822
if (apr_stat(&finfo, filename, APR_FINFO_SIZE,
5037
if (apr_stat(&finfo, filename, APR_FINFO_NORM,
4823
5038
pool) != APR_SUCCESS) {
5167
* Apache child process initialision and cleanup. Initialise
5382
* Apache child process initialisation and cleanup. Initialise
5168
5383
* global table containing Python interpreter instances and
5169
5384
* cache reference to main interpreter. Also register cleanup
5170
5385
* function to delete interpreter on process shutdown.
5225
5440
* Destroy Python itself including the main interpreter.
5226
5441
* If mod_python is being loaded it is left to mod_python to
5227
* destroy mod_python, although it currently doesn't do so.
5442
* destroy Python, although it currently doesn't do so.
5230
5445
if (wsgi_python_initialized)
5231
wsgi_python_term(0);
5233
5448
#if AP_SERVER_MAJORVERSION_NUMBER >= 2
5234
5449
return APR_SUCCESS;
5655
5873
while (*args) {
5656
5874
char const *option;
5658
option = ap_getword_conf(cmd->temp_pool, &args);
5876
option = ap_getword_conf(cmd->pool, &args);
5660
5878
if (!strcmp(option, "%{GLOBAL}"))
5745
5963
object = (WSGIScriptFile *)apr_array_push(sconfig->import_list);
5747
object->handler_script = ap_getword_conf(cmd->temp_pool, &args);
5965
object->handler_script = ap_getword_conf(cmd->pool, &args);
5748
5966
object->process_group = NULL;
5749
5967
object->application_group = NULL;
5752
5970
return "Location of import script not supplied.";
5754
5972
while (*args) {
5755
option = ap_getword_conf(cmd->temp_pool, &args);
5973
option = ap_getword_conf(cmd->pool, &args);
5757
5975
if (strstr(option, "application-group=") == option) {
5758
5976
value = option + 18;
5804
6022
object = newWSGIScriptFile(cmd->pool);
5806
object->handler_script = ap_getword_conf(cmd->temp_pool, &args);
6024
object->handler_script = ap_getword_conf(cmd->pool, &args);
5808
6026
if (!object->handler_script || !*object->handler_script)
5809
6027
return "Location of dispatch script not supplied.";
5811
6029
while (*args) {
5812
option = ap_getword_conf(cmd->temp_pool, &args);
6030
option = ap_getword_conf(cmd->pool, &args);
5814
6032
if (strstr(option, "application-group=") == option) {
5815
6033
value = option + 18;
5973
6191
object = newWSGIScriptFile(cmd->pool);
5975
object->handler_script = ap_getword_conf(cmd->temp_pool, &args);
6193
object->handler_script = ap_getword_conf(cmd->pool, &args);
5977
6195
if (!object->handler_script || !*object->handler_script)
5978
6196
return "Location of access script not supplied.";
5980
6198
while (*args) {
5981
option = ap_getword_conf(cmd->temp_pool, &args);
6199
option = ap_getword_conf(cmd->pool, &args);
5983
6201
if (strstr(option, "application-group=") == option) {
5984
6202
value = option + 18;
6009
6227
object = newWSGIScriptFile(cmd->pool);
6011
object->handler_script = ap_getword_conf(cmd->temp_pool, &args);
6229
object->handler_script = ap_getword_conf(cmd->pool, &args);
6013
6231
if (!object->handler_script || !*object->handler_script)
6014
6232
return "Location of auth user script not supplied.";
6016
6234
while (*args) {
6017
option = ap_getword_conf(cmd->temp_pool, &args);
6235
option = ap_getword_conf(cmd->pool, &args);
6019
6237
if (strstr(option, "application-group=") == option) {
6020
6238
value = option + 18;
6045
6263
object = newWSGIScriptFile(cmd->pool);
6047
object->handler_script = ap_getword_conf(cmd->temp_pool, &args);
6265
object->handler_script = ap_getword_conf(cmd->pool, &args);
6049
6267
if (!object->handler_script || !*object->handler_script)
6050
6268
return "Location of auth group script not supplied.";
6052
6270
while (*args) {
6053
option = ap_getword_conf(cmd->temp_pool, &args);
6271
option = ap_getword_conf(cmd->pool, &args);
6055
6273
if (strstr(option, "application-group=") == option) {
6056
6274
value = option + 18;
6226
6444
n = r->filename;
6228
6446
message = apr_psprintf(r->pool, "%s: %s", e, n);
6229
apr_table_set(r->notes, "error-notes", message);
6231
ap_log_rerror(APLOG_MARK, WSGI_LOG_ERR(0), r, message);
6448
ap_log_rerror(APLOG_MARK, WSGI_LOG_ERR(0), r, "%s", message);
6234
6451
static void wsgi_build_environment(request_rec *r)
7309
7526
/* Now parse options for directive. */
7311
name = ap_getword_conf(cmd->temp_pool, &args);
7528
name = ap_getword_conf(cmd->pool, &args);
7313
7530
if (!name || !*name)
7314
7531
return "Name of WSGI daemon process not supplied.";
7316
7533
while (*args) {
7317
option = ap_getword_conf(cmd->temp_pool, &args);
7534
option = ap_getword_conf(cmd->pool, &args);
7319
7536
if (strstr(option, "user=") == option) {
7320
7537
value = option + 5;
7837
static apr_file_t *wsgi_signal_pipe_in = NULL;
7838
static apr_file_t *wsgi_signal_pipe_out = NULL;
7620
7840
static void wsgi_signal_handler(int signum)
7842
apr_size_t nbytes = 1;
7844
apr_file_write(wsgi_signal_pipe_out, "X", &nbytes);
7845
apr_file_flush(wsgi_signal_pipe_out);
7622
7847
wsgi_daemon_shutdown++;
7625
static int wsgi_check_signal(int signum)
7627
if (signum == SIGINT || signum == SIGTERM) {
7628
wsgi_daemon_shutdown++;
7636
7850
static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon);
7638
7852
static void wsgi_manage_process(int reason, void *data, apr_wait_t status)
8380
8594
apr_status_t rv;
8381
8595
apr_status_t thread_rv;
8383
/* Block all signals from being received. */
8385
rv = apr_setup_signal_thread();
8597
apr_pollfd_t poll_fd;
8598
apr_int32_t poll_count = 0;
8601
* Create pipe by which signal handler can notify the main
8602
* thread that signal has arrived indicating that process
8603
* needs to shutdown.
8606
rv = apr_file_pipe_create(&wsgi_signal_pipe_in, &wsgi_signal_pipe_out, p);
8386
8608
if (rv != APR_SUCCESS) {
8387
8609
ap_log_error(APLOG_MARK, WSGI_LOG_EMERG(rv), wsgi_server,
8388
8610
"mod_wsgi (pid=%d): Couldn't initialise signal "
8389
"thread in daemon process '%s'.", getpid(),
8611
"pipe in daemon process '%s'.", getpid(),
8390
8612
daemon->group->name);
8618
poll_fd.desc_type = APR_POLL_FILE;
8619
poll_fd.reqevents = APR_POLLIN;
8620
poll_fd.desc.f = wsgi_signal_pipe_in;
8396
8622
/* Initialise maximum request count for daemon. */
8398
8624
if (daemon->group->maximum_requests)
8481
8707
/* Block until we get a process shutdown signal. */
8483
apr_signal_thread(wsgi_check_signal);
8710
rv = apr_poll(&poll_fd, 1, &poll_count, -1);
8711
} while (APR_STATUS_IS_EINTR(rv));
8485
8713
ap_log_error(APLOG_MARK, WSGI_LOG_INFO(0), wsgi_server,
8486
8714
"mod_wsgi (pid=%d): Shutdown requested '%s'.",
8665
8893
ap_close_listeners();
8896
* Cleanup the Apache scoreboard to ensure that any
8897
* shared memory segments or memory mapped files not
8898
* available to code in daemon processes.
8901
ap_cleanup_scoreboard(0);
8668
8904
* Wipe out random value used in magic token so that not
8669
8905
* possible for user code running in daemon process to
8670
8906
* discover this value for other daemon process groups.
8705
8941
wsgi_daemon_shutdown = 0;
8707
if (daemon->group->threads == 1) {
8708
apr_signal(SIGINT, wsgi_signal_handler);
8709
apr_signal(SIGTERM, wsgi_signal_handler);
8943
apr_signal(SIGINT, wsgi_signal_handler);
8944
apr_signal(SIGTERM, wsgi_signal_handler);
8713
8947
* Flag whether multiple daemon processes or denoted
8854
9088
entry = &entries[i];
9091
* Check for whether the daemon process user and
9092
* group are the default Apache values. If they are
9093
* then reset them to the current values configured for
9094
* Apache. This is to work around where the User/Group
9095
* directives had not been set before the WSGIDaemonProcess
9096
* directive was used in configuration file. In this case,
9097
* where no 'user' and 'group' options were provided,
9098
* the default values would have been used, but these
9099
* were later overridden thus why we need to update it.
9102
if (entry->uid == ap_uname2id(DEFAULT_USER)) {
9103
entry->uid = unixd_config.user_id;
9104
entry->user = unixd_config.user_name;
9106
ap_log_error(APLOG_MARK, WSGI_LOG_DEBUG(0), wsgi_server,
9107
"mod_wsgi (pid=%d): Reset default user for "
9108
"daemon process group '%s' to uid=%ld.",
9109
getpid(), entry->name, (long)entry->uid);
9112
if (entry->gid == ap_gname2id(DEFAULT_GROUP)) {
9113
entry->gid = unixd_config.group_id;
9115
ap_log_error(APLOG_MARK, WSGI_LOG_DEBUG(0), wsgi_server,
9116
"mod_wsgi (pid=%d): Reset default group for "
9117
"daemon process group '%s' to gid=%ld.",
9118
getpid(), entry->name, (long)entry->gid);
8857
9122
* Calculate path for socket to accept requests on and
8858
9123
* create the socket.
9649
* Need to reset request status value to HTTP_OK else it
9650
* screws up HTTP input filter when processing a POST
9651
* request with 100-continue requirement.
9654
r->status = HTTP_OK;
9383
9656
/* Transfer any request content which was provided. */
9806
10079
r->filename = (char *)apr_table_get(r->subprocess_env, "SCRIPT_FILENAME");
9808
if ((rv = apr_stat(&r->finfo, r->filename, APR_FINFO_SIZE,
10081
if ((rv = apr_stat(&r->finfo, r->filename, APR_FINFO_NORM,
9809
10082
r->pool)) != APR_SUCCESS) {
9811
10084
* Don't fail at this point. Allow the lack of file to
10037
10310
static void wsgi_hook_child_init(apr_pool_t *p, server_rec *s)
10312
#if defined(MOD_WSGI_WITH_DAEMONS)
10039
10313
WSGIProcessGroup *entries = NULL;
10040
10314
WSGIProcessGroup *entry = NULL;