diff options
-rw-r--r-- | docs/release-notes/index.rst | 1 | ||||
-rw-r--r-- | docs/release-notes/version-4.3.2.rst | 63 | ||||
-rw-r--r-- | src/server/__init__.py | 13 | ||||
-rw-r--r-- | src/server/mod_wsgi.c | 331 | ||||
-rw-r--r-- | src/server/wsgi_interp.c | 137 | ||||
-rw-r--r-- | src/server/wsgi_version.h | 4 | ||||
-rw-r--r-- | tests/environ.wsgi | 1 |
7 files changed, 262 insertions, 288 deletions
diff --git a/docs/release-notes/index.rst b/docs/release-notes/index.rst index bb98bd2..ed8010a 100644 --- a/docs/release-notes/index.rst +++ b/docs/release-notes/index.rst @@ -5,6 +5,7 @@ Release Notes .. toctree:: :maxdepth: 2 + version-4.3.2.rst version-4.3.1.rst version-4.3.0.rst diff --git a/docs/release-notes/version-4.3.2.rst b/docs/release-notes/version-4.3.2.rst new file mode 100644 index 0000000..2ce46e7 --- /dev/null +++ b/docs/release-notes/version-4.3.2.rst @@ -0,0 +1,63 @@ +============= +Version 4.3.2 +============= + +Version 4.3.2 of mod_wsgi can be obtained from: + + https://github.com/GrahamDumpleton/mod_wsgi/archive/4.3.2.tar.gz + +Known Issues +------------ + +1. The makefiles for building mod_wsgi on Windows are currently broken and +need updating. As most new changes relate to mod_wsgi daemon mode, which is +not supported under Windows, you should keep using the last available +binary for version 3.X on Windows instead. + +Bugs Fixed +---------- + +1. Linux behaviour when using ``connect()`` on a non blocking UNIX socket +and the listener queue is full, is apparently not POSIX compliant and it +returns ``EAGAIN`` instead of ``ECONNREFUSED``. The code handling errors +from the ``connect()`` wasn't accomodating this non standard behaviour +and so would fail immediately rather than retrying. + +2. Only change working directory for mod_wsgi daemon process after having +dropped privileges to target user. This is required where the specified +working directory is on an NFS file system configured so as not to have +root access priviliges. + +3. The workaround for getting pyvenv style virtual environments to work +with Python 3.3+ would break brew Python 2.7 on MacOS X as brew Python +appears to not work in embedded systems which use Py_SetProgramName() +instead of using Py_SetPythonHome(). Now only use Py_SetProgramName() if +detect it is actually a pyvenv style virtual environment. This even appears +to be okay for brew Python 3.4 at least as it does still work with the +Py_SetProgramName() call even if brew Python 2.7 doesn't. + +New Features +------------ + +1. If the ``WSGIPythonHome`` directive or the ``python-home`` option is +used with the ``WSGIDaemonProcess`` directive, the path provided, which is +supposed to be the root directory of the Python installation or virtual +environment, will be checked to see if it is actually accessible and refers +to a directory. If it isn't, a warning message will be logged along with +any details providing an indication of what may be wrong with the supplied +path. + +This is being done to warn when an invalid path has been supplied that +subsequently is likely to be rejected and ignored by the Python +interpreter. In such a situation where an invalid path is supplied the +Python interpreter doesn't actually log anything and will instead silently +fallback to using any Python installation it finds by seaching for +``python`` on the users ``PATH``. This may not be the Python installation +or virtual environment you intended be used. + +2. The Apache configuration snippet generated as an example when running +the ``install-module`` sub command of ``mod_wsgi-express`` to install the +``mod_wsgi.so`` into the Apache installation itself, will now output a +``WSGIPythonHome`` directive for the Python installation or virtual +environment the mod_wsgi module was compiled against so that the correct +Python runtime will be used. diff --git a/src/server/__init__.py b/src/server/__init__.py index fd0b54d..071ccae 100644 --- a/src/server/__init__.py +++ b/src/server/__init__.py @@ -1760,14 +1760,14 @@ def _cmd_setup_server(command, args, options): if options['max_clients'] is not None: max_clients = max(options['max_clients'], max_clients) else: - max_clients = int(1.5 * max_clients) + max_clients = 10 + max(10, int(1.5 * max_clients)) initial_workers = options['initial_workers'] min_spare_workers = options['minimum_spare_workers'] max_spare_workers = options['maximum_spare_workers'] if initial_workers is None: - prefork_initial_workers = 0.02 + prefork_initial_workers = 0.05 else: prefork_initial_workers = initial_workers @@ -1777,7 +1777,7 @@ def _cmd_setup_server(command, args, options): prefork_min_spare_workers = min_spare_workers if max_spare_workers is None: - prefork_max_spare_workers = 0.05 + prefork_max_spare_workers = 0.1 else: prefork_max_spare_workers = max_spare_workers @@ -1807,11 +1807,11 @@ def _cmd_setup_server(command, args, options): options['worker_max_clients'] = max_clients - if max_clients > 25: + if max_clients > 20: options['worker_threads_per_child'] = int(max_clients / - (int(max_clients / 25) + 1)) + (int(max_clients / 20) + 1)) else: - options['worker_threads_per_child'] = max_clients + options['worker_threads_per_child'] = 10 options['worker_thread_limit'] = options['worker_threads_per_child'] @@ -2003,6 +2003,7 @@ def cmd_install_module(params): shutil.copyfile(where(), target) print('LoadModule wsgi_module %s' % target) + print('WSGIPythonHome %s' % os.path.normpath(sys.prefix)) def cmd_module_location(params): formatter = optparse.IndentedHelpFormatter() diff --git a/src/server/mod_wsgi.c b/src/server/mod_wsgi.c index 34bfd88..0e891dd 100644 --- a/src/server/mod_wsgi.c +++ b/src/server/mod_wsgi.c @@ -2509,7 +2509,7 @@ static int Adapter_run(AdapterObject *self, PyObject *object) if (wsgi_newrelic_config_file) { PyObject *module = NULL; - module = PyImport_ImportModule("newrelic.api.web_transaction"); + module = PyImport_ImportModule("newrelic.agent"); if (module) { PyObject *dict; @@ -2524,6 +2524,12 @@ static int Adapter_run(AdapterObject *self, PyObject *object) wrapper = PyObject_CallFunctionObjArgs( factory, object, Py_None, NULL); + if (!wrapper) { + wsgi_log_python_error(self->r, self->log, + self->r->filename); + PyErr_Clear(); + } + Py_DECREF(factory); } @@ -2863,8 +2869,6 @@ static PyObject *wsgi_load_source(apr_pool_t *pool, request_rec *r, PyObject *co = NULL; struct _node *n = NULL; - PyObject *transaction = NULL; - #if defined(WIN32) && defined(APR_HAS_UNICODE_FS) apr_wchar_t wfilename[APR_PATH_MAX]; #endif @@ -2970,85 +2974,6 @@ static PyObject *wsgi_load_source(apr_pool_t *pool, request_rec *r, return NULL; } - if (wsgi_newrelic_config_file) { - PyObject *module = NULL; - - PyObject *application = NULL; - - - module = PyImport_ImportModule("newrelic.api.application"); - - if (module) { - PyObject *dict = NULL; - PyObject *object = NULL; - - dict = PyModule_GetDict(module); - object = PyDict_GetItemString(dict, "application"); - - Py_INCREF(object); - application = PyObject_CallFunctionObjArgs(object, NULL); - Py_DECREF(object); - - Py_DECREF(module); - module = NULL; - - if (!application) - PyErr_Clear(); - } - else - PyErr_Clear(); - - if (application) - module = PyImport_ImportModule("newrelic.api.background_task"); - - if (module) { - PyObject *dict = NULL; - PyObject *object = NULL; - - dict = PyModule_GetDict(module); - object = PyDict_GetItemString(dict, "BackgroundTask"); - - if (object) { - PyObject *args = NULL; - - Py_INCREF(object); - - args = Py_BuildValue("(Oss)", application, filename, - "Script/Import"); - transaction = PyObject_Call(object, args, NULL); - - if (!transaction) - PyErr_WriteUnraisable(object); - - Py_DECREF(args); - Py_DECREF(object); - - if (transaction) { - PyObject *result = NULL; - - object = PyObject_GetAttrString( - transaction, "__enter__"); - args = PyTuple_Pack(0); - result = PyObject_Call(object, args, NULL); - - if (!result) - PyErr_WriteUnraisable(object); - - Py_XDECREF(result); - Py_DECREF(object); - } - } - - Py_DECREF(module); - } - else - PyErr_Print(); - - Py_XDECREF(application); - } - else - PyErr_Clear(); - co = (PyObject *)PyNode_Compile(n, filename); PyNode_Free(n); @@ -3057,67 +2982,6 @@ static PyObject *wsgi_load_source(apr_pool_t *pool, request_rec *r, Py_XDECREF(co); - if (wsgi_newrelic_config_file) { - if (transaction) { - PyObject *object; - - object = PyObject_GetAttrString(transaction, "__exit__"); - - if (m) { - PyObject *args = NULL; - PyObject *result = NULL; - - args = PyTuple_Pack(3, Py_None, Py_None, Py_None); - result = PyObject_Call(object, args, NULL); - - if (!result) - PyErr_WriteUnraisable(object); - else - Py_DECREF(result); - - Py_DECREF(args); - } - else { - PyObject *args = NULL; - PyObject *result = NULL; - - PyObject *type = NULL; - PyObject *value = NULL; - PyObject *traceback = NULL; - - PyErr_Fetch(&type, &value, &traceback); - - if (!value) { - value = Py_None; - Py_INCREF(value); - } - - if (!traceback) { - traceback = Py_None; - Py_INCREF(traceback); - } - - PyErr_NormalizeException(&type, &value, &traceback); - - args = PyTuple_Pack(3, type, value, traceback); - result = PyObject_Call(object, args, NULL); - - if (!result) - PyErr_WriteUnraisable(object); - else - Py_DECREF(result); - - Py_DECREF(args); - - PyErr_Restore(type, value, traceback); - } - - Py_DECREF(object); - - Py_DECREF(transaction); - } - } - if (m) { PyObject *object = NULL; @@ -7196,11 +7060,6 @@ static void wsgi_setup_daemon_name(WSGIDaemonProcess *daemon, apr_pool_t *p) static int wsgi_setup_access(WSGIDaemonProcess *daemon) { - /* Setup the umask for the effective user. */ - - if (daemon->group->umask != -1) - umask(daemon->group->umask); - /* Change to chroot environment. */ if (daemon->group->root) { @@ -7213,35 +7072,86 @@ static int wsgi_setup_access(WSGIDaemonProcess *daemon) } } - /* Setup the working directory.*/ + /* We don't need to switch user/group if not root. */ - if (daemon->group->home) { - if (chdir(daemon->group->home) == -1) { + if (geteuid() == 0) { + /* Setup the daemon process real and effective group. */ + + if (setgid(daemon->group->gid) == -1) { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server, - "mod_wsgi (pid=%d): Unable to change working " - "directory to '%s'.", getpid(), daemon->group->home); + "mod_wsgi (pid=%d): Unable to set group id " + "to gid=%u.", getpid(), + (unsigned)daemon->group->gid); return -1; } - } - else if (geteuid()) { - struct passwd *pwent; + else { + if (daemon->group->groups) { + if (setgroups(daemon->group->groups_count, + daemon->group->groups) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, + wsgi_server, "mod_wsgi (pid=%d): Unable " + "to set supplementary groups for uname=%s " + "of '%s'.", getpid(), daemon->group->user, + daemon->group->groups_list); + + return -1; + } + } + else if (initgroups(daemon->group->user, + daemon->group->gid) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, + wsgi_server, "mod_wsgi (pid=%d): Unable " + "to set groups for uname=%s and gid=%u.", + getpid(), daemon->group->user, + (unsigned)daemon->group->gid); - pwent = getpwuid(geteuid()); + return -1; + } + } - if (pwent) { - if (chdir(pwent->pw_dir) == -1) { - ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server, - "mod_wsgi (pid=%d): Unable to change working " - "directory to '%s'.", getpid(), pwent->pw_dir); + /* Setup the daemon process real and effective user. */ + + if (setuid(daemon->group->uid) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server, + "mod_wsgi (pid=%d): Unable to change to uid=%ld.", + getpid(), (long)daemon->group->uid); + + /* + * On true UNIX systems this should always succeed at + * this point. With certain Linux kernel versions though + * we can get back EAGAIN where the target user had + * reached their process limit. In that case will be left + * running as wrong user. Just exit on all failures to be + * safe. Don't die immediately to avoid a fork bomb. + * + * We could just return -1 here and let the caller do the + * sleep() and exit() but this failure is critical enough + * that we still do it here so it is obvious that the issue + * is being addressed. + */ + + ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server, + "mod_wsgi (pid=%d): Failure to configure the " + "daemon process correctly and process left in " + "unspecified state. Restarting daemon process " + "after delay.", getpid()); + + sleep(20); + + wsgi_exit_daemon_process(-1); return -1; - } } - else { + } + + /* Setup the working directory for the process. */ + + if (daemon->group->home) { + if (chdir(daemon->group->home) == -1) { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server, - "mod_wsgi (pid=%d): Unable to determine home " - "directory for uid=%ld.", getpid(), (long)geteuid()); + "mod_wsgi (pid=%d): Unable to change working " + "directory to '%s'.", getpid(), daemon->group->home); return -1; } @@ -7249,7 +7159,7 @@ static int wsgi_setup_access(WSGIDaemonProcess *daemon) else { struct passwd *pwent; - pwent = getpwuid(daemon->group->uid); + pwent = getpwuid(geteuid()); if (pwent) { if (chdir(pwent->pw_dir) == -1) { @@ -7257,87 +7167,22 @@ static int wsgi_setup_access(WSGIDaemonProcess *daemon) "mod_wsgi (pid=%d): Unable to change working " "directory to '%s'.", getpid(), pwent->pw_dir); - return -1; + return -1; } } else { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server, "mod_wsgi (pid=%d): Unable to determine home " - "directory for uid=%ld.", getpid(), - (long)daemon->group->uid); - - return -1; - } - } - - /* Don't bother switch user/group if not root. */ - - if (geteuid()) - return 0; - - /* Setup the daemon process real and effective group. */ - - if (setgid(daemon->group->gid) == -1) { - ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server, - "mod_wsgi (pid=%d): Unable to set group id to gid=%u.", - getpid(), (unsigned)daemon->group->gid); - - return -1; - } - else { - if (daemon->group->groups) { - if (setgroups(daemon->group->groups_count, - daemon->group->groups) == -1) { - ap_log_error(APLOG_MARK, APLOG_ALERT, errno, - wsgi_server, "mod_wsgi (pid=%d): Unable " - "to set supplementary groups for uname=%s " - "of '%s'.", getpid(), daemon->group->user, - daemon->group->groups_list); - - return -1; - } - } - else if (initgroups(daemon->group->user, daemon->group->gid) == -1) { - ap_log_error(APLOG_MARK, APLOG_ALERT, errno, - wsgi_server, "mod_wsgi (pid=%d): Unable " - "to set groups for uname=%s and gid=%u.", getpid(), - daemon->group->user, (unsigned)daemon->group->gid); + "directory for uid=%ld.", getpid(), (long)geteuid()); return -1; } } - /* Setup the daemon process real and effective user. */ - - if (setuid(daemon->group->uid) == -1) { - ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server, - "mod_wsgi (pid=%d): Unable to change to uid=%ld.", - getpid(), (long)daemon->group->uid); - - /* - * On true UNIX systems this should always succeed at - * this point. With certain Linux kernel versions though - * we can get back EAGAIN where the target user had - * reached their process limit. In that case will be left - * running as wrong user. Just exit on all failures to be - * safe. Don't die immediately to avoid a fork bomb. - * - * We could just return -1 here and let the caller do the - * sleep() and exit() but this failure is critical enough - * that we still do it here so it is obvious that the issue - * is being addressed. - */ - - ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server, - "mod_wsgi (pid=%d): Failure to configure the " - "daemon process correctly and process left in " - "unspecified state. Restarting daemon process " - "after delay.", getpid()); - - sleep(20); + /* Setup the umask for the effective user. */ - wsgi_exit_daemon_process(-1); - } + if (daemon->group->umask != -1) + umask(daemon->group->umask); /* * Linux prevents generation of core dumps after setuid() @@ -7347,6 +7192,7 @@ static int wsgi_setup_access(WSGIDaemonProcess *daemon) #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) /* This applies to Linux 2.4 and later. */ + if (ap_coredumpdir_configured) { if (prctl(PR_SET_DUMPABLE, 1)) { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server, @@ -9496,7 +9342,16 @@ static int wsgi_connect_daemon(request_rec *r, WSGIDaemonSocket *daemon) rv = wsgi_socket_connect_un(daemon->socket, &addr); if (rv != APR_SUCCESS) { - if (APR_STATUS_IS_ECONNREFUSED(rv)) { + /* + * We need to check for both connection refused and + * connection unavailable as Linux systems when + * connecting to a UNIX listener socket in non + * blocking mode, where the listener backlog is full + * will return the error EAGAIN rather than returning + * ECONNREFUSED as is supposedly dictated by POSIX. + */ + + if (APR_STATUS_IS_ECONNREFUSED(rv) || APR_STATUS_IS_EAGAIN(rv)) { if ((apr_time_now()-start_time) < daemon->connect_timeout) { if (wsgi_server_config->verbose_debugging) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, diff --git a/src/server/wsgi_interp.c b/src/server/wsgi_interp.c index 6d8e765..7d0c1a7 100644 --- a/src/server/wsgi_interp.c +++ b/src/server/wsgi_interp.c @@ -1935,6 +1935,8 @@ void wsgi_python_init(apr_pool_t *p) { const char *python_home = 0; + int is_pyvenv = 0; + /* Perform initialisation if required. */ if (!Py_IsInitialized()) { @@ -2003,19 +2005,16 @@ void wsgi_python_init(apr_pool_t *p) python_home = wsgi_server_config->python_home; -#if defined(MOD_WSGI_WITH_DAEMONS) - if (wsgi_daemon_process && wsgi_daemon_process->group->python_home) - python_home = wsgi_daemon_process->group->python_home; -#endif - -#if PY_MAJOR_VERSION >= 3 if (python_home) { - wchar_t *s = NULL; - int len = strlen(python_home)+1; - ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server, "mod_wsgi (pid=%d): Python home %s.", getpid(), python_home); + } + + if (python_home) { +#if PY_MAJOR_VERSION >= 3 + wchar_t *s = NULL; + int len = strlen(python_home)+1; s = (wchar_t *)apr_palloc(p, len*sizeof(wchar_t)); @@ -2025,23 +2024,21 @@ void wsgi_python_init(apr_pool_t *p) mbstowcs(s, python_home, len); #endif Py_SetPythonHome(s); - } #else - if (python_home) { - ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server, - "mod_wsgi (pid=%d): Python home %s.", getpid(), - python_home); - Py_SetPythonHome((char *)python_home); - } #endif + } + #else /* - * Check for Python HOME being overridden. What we are actually - * going to do though is work out where the Python executable - * would be that the designated installation and set the - * location for where it is. This avoids bugs in pyvenv support - * for embedded systems. + * Now for the UNIX version of the code to set the Python HOME. + * For this things are a mess. If using pyvenv with Python 3.3+ + * then setting Python HOME doesn't work. For it we need to use + * Python executable location. Everything else seems to be cool + * with setting Python HOME. We therefore need to detect when we + * have a pyvenv by looking for the presence of pyvenv.cfg file. + * We can simply just set Python executable everywhere as that + * doesn't work with brew Python on MacOS X. */ python_home = wsgi_server_config->python_home; @@ -2051,43 +2048,99 @@ void wsgi_python_init(apr_pool_t *p) python_home = wsgi_daemon_process->group->python_home; #endif -#if PY_MAJOR_VERSION >= 3 if (python_home) { + apr_status_t rv; + apr_finfo_t finfo; + + char *pyvenv_cfg; + const char *python_exe = 0; + +#if PY_MAJOR_VERSION >= 3 wchar_t *s = NULL; int len = 0; +#endif - python_exe = apr_pstrcat(p, python_home, "/bin/python", NULL); - - len = strlen(python_exe)+1; + /* + * Is common to see people set the directory to an incorrect + * location, including to a location within an inaccessible + * user home directory, or to the 'python' executable itself. + * Try and validate that the location is accessible and is a + * directory. + */ ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server, "mod_wsgi (pid=%d): Python home %s.", getpid(), python_home); - s = (wchar_t *)apr_palloc(p, len*sizeof(wchar_t)); + rv = apr_stat(&finfo, python_home, APR_FINFO_NORM, p); -#if defined(WIN32) && defined(APR_HAS_UNICODE_FS) - wsgi_utf8_to_unicode_path(s, len, python_exe); -#else - mbstowcs(s, python_exe, len); -#endif - Py_SetProgramName(s); - } -#else - if (python_home) { - const char *python_exe = 0; + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, wsgi_server, + "mod_wsgi (pid=%d): Unable to stat Python home " + "%s. Python interpreter may not be able to be " + "initialized correctly. Verify the supplied path " + "and access permissions for whole of the path.", + getpid(), python_home); + } + else { + if (finfo.filetype != APR_DIR) { + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, wsgi_server, + "mod_wsgi (pid=%d): Python home %s is not " + "a directory. Python interpreter may not " + "be able to be initialized correctly. " + "Verify the supplied path.", getpid(), + python_home); + } + else if (access(python_home, X_OK) == -1) { + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, wsgi_server, + "mod_wsgi (pid=%d): Python home %s is not " + "accessible. Python interpreter may not " + "be able to be initialized correctly. " + "Verify the supplied path and access " + "permissions on the directory.", getpid(), + python_home); + } + } - python_exe = apr_pstrcat(p, python_home, "/bin/python", NULL); + /* Now detect whether have a pyvenv with Python 3.3+. */ - ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server, - "mod_wsgi (pid=%d): Python home %s.", getpid(), - python_home); + pyvenv_cfg = apr_pstrcat(p, python_home, "/pyvenv.cfg", NULL); - Py_SetProgramName((char *)python_exe); - } + if (access(pyvenv_cfg, R_OK) == 0) + is_pyvenv = 1; + + if (is_pyvenv) { + /* + * Embedded support for pyvenv is broken so need to + * set Python executable location and cannot set the + * Python HOME as is more desirable. + */ + + python_exe = apr_pstrcat(p, python_home, "/bin/python", NULL); +#if PY_MAJOR_VERSION >= 3 + len = strlen(python_exe)+1; + s = (wchar_t *)apr_palloc(p, len*sizeof(wchar_t)); + mbstowcs(s, python_exe, len); + + Py_SetProgramName(s); +#else + Py_SetProgramName((char *)python_exe); +#endif + } + else { +#if PY_MAJOR_VERSION >= 3 + len = strlen(python_home)+1; + s = (wchar_t *)apr_palloc(p, len*sizeof(wchar_t)); + mbstowcs(s, python_home, len); + + Py_SetPythonHome(s); +#else + Py_SetPythonHome((char *)python_home); #endif + } #endif + } /* * Set environment variable PYTHONHASHSEED. We need to diff --git a/src/server/wsgi_version.h b/src/server/wsgi_version.h index c71f4a7..4f91475 100644 --- a/src/server/wsgi_version.h +++ b/src/server/wsgi_version.h @@ -25,8 +25,8 @@ #define MOD_WSGI_MAJORVERSION_NUMBER 4 #define MOD_WSGI_MINORVERSION_NUMBER 3 -#define MOD_WSGI_MICROVERSION_NUMBER 1 -#define MOD_WSGI_VERSION_STRING "4.3.1" +#define MOD_WSGI_MICROVERSION_NUMBER 2 +#define MOD_WSGI_VERSION_STRING "4.3.2" /* ------------------------------------------------------------------------- */ diff --git a/tests/environ.wsgi b/tests/environ.wsgi index c26eae7..3a478ec 100644 --- a/tests/environ.wsgi +++ b/tests/environ.wsgi @@ -22,6 +22,7 @@ def application(environ, start_response): print('PID: %s' % os.getpid(), file=output) print('UID: %s' % os.getuid(), file=output) print('GID: %s' % os.getgid(), file=output) + print('CWD: %s' % os.getcwd(), file=output) print(file=output) print('python.version: %r' % (sys.version,), file=output) |