diff options
Diffstat (limited to 'src')
-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 |
4 files changed, 197 insertions, 288 deletions
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" /* ------------------------------------------------------------------------- */ |