mod-wsgi-standalone 6.0.0.dev4__tar.gz → 6.0.0.dev5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/PKG-INFO +1 -1
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/PKG-INFO +1 -1
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/diagnostics/environ.py +16 -19
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/apache.py +9 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/options.py +11 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_daemon.c +187 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_interp.c +85 -83
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_interp.h +13 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_logger.c +308 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_logger.h +7 -4
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_metrics.c +123 -3
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_metrics.h +9 -5
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_module.c +12 -3
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_module.h +1 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_server.c +1 -1
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_version.h +1 -1
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/CREDITS.rst +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/LICENSE +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/MANIFEST.in +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/Makefile.in +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/README-express.rst +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/README-standalone.rst +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/README.rst +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/configure +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/configure.ac +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/images/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/images/snake-whiskey.jpg +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/SOURCES.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/dependency_links.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/entry_points.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/not-zip-safe +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/requires.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/mod_wsgi_standalone.egg-info/top_level.txt +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/pyproject.toml +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/setup.cfg +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/setup.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/diagnostics/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/diagnostics/hello.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/cli.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/management/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/management/commands/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/management/commands/runmodwsgi.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/platform.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/reloader.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/runtime.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/scripts.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/express/server.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/management/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/management/commands/__init__.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/management/commands/runmodwsgi.py +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/mod_wsgi.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_adapter.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_adapter.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_apache.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_apache.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_auth.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_auth.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_buckets.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_buckets.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_config.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_config.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_convert.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_convert.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_daemon.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_dispatch.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_dispatch.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_environ.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_environ.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_execute.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_execute.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_gc.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_gc.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_input.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_input.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_memory.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_memory.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_python.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_remote.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_remote.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_restrict.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_restrict.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_server.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_shutdown.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_shutdown.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_signal.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_signal.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_stream.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_stream.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_telemetry.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_telemetry.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_thread.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_thread.h +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_validate.c +0 -0
- {mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/server/wsgi_validate.h +0 -0
{mod_wsgi_standalone-6.0.0.dev4 → mod_wsgi_standalone-6.0.0.dev5}/src/diagnostics/environ.py
RENAMED
|
@@ -15,10 +15,16 @@ def application(environ, start_response):
|
|
|
15
15
|
input = environ["wsgi.input"]
|
|
16
16
|
output = StringIO()
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
if os.name != "nt":
|
|
19
|
+
print(f"PID: {os.getpid()}", file=output)
|
|
20
|
+
print(f"UID: {os.getuid()}", file=output)
|
|
21
|
+
print(f"GID: {os.getgid()}", file=output)
|
|
22
|
+
print(f"CWD: {os.getcwd()}", file=output)
|
|
23
|
+
print(file=output)
|
|
24
|
+
|
|
25
|
+
print(f"STDOUT: {sys.stdout.name}", file=output)
|
|
26
|
+
print(f"STDERR: {sys.stderr.name}", file=output)
|
|
27
|
+
print(f'ERRORS: {environ["wsgi.errors"].name}', file=output)
|
|
22
28
|
print(file=output)
|
|
23
29
|
|
|
24
30
|
print(f"python.version: {sys.version!r}", file=output)
|
|
@@ -36,19 +42,8 @@ def application(environ, start_response):
|
|
|
36
42
|
|
|
37
43
|
print(f"mod_wsgi.maximum_processes: {mod_wsgi.maximum_processes}", file=output)
|
|
38
44
|
print(f"mod_wsgi.threads_per_process: {mod_wsgi.threads_per_process}", file=output)
|
|
39
|
-
print(f"mod_wsgi.process_metrics: {mod_wsgi.process_metrics()}", file=output)
|
|
40
|
-
print(f"mod_wsgi.server_metrics: {mod_wsgi.server_metrics()}", file=output)
|
|
41
45
|
print(file=output)
|
|
42
46
|
|
|
43
|
-
metrics = mod_wsgi.server_metrics()
|
|
44
|
-
|
|
45
|
-
if metrics:
|
|
46
|
-
for process in metrics["processes"]:
|
|
47
|
-
for worker in process["workers"]:
|
|
48
|
-
print(worker["status"], file=output, end="")
|
|
49
|
-
print(file=output)
|
|
50
|
-
print(file=output)
|
|
51
|
-
|
|
52
47
|
print(f"apache.description: {apache.description}", file=output)
|
|
53
48
|
print(f"apache.build_date: {apache.build_date}", file=output)
|
|
54
49
|
print(f"apache.mpm_name: {apache.mpm_name}", file=output)
|
|
@@ -56,16 +51,18 @@ def application(environ, start_response):
|
|
|
56
51
|
print(f"apache.threads_per_process: {apache.threads_per_process}", file=output)
|
|
57
52
|
print(file=output)
|
|
58
53
|
|
|
59
|
-
print(f
|
|
54
|
+
print(f'PATH: {os.environ.get("PATH")}', file=output)
|
|
60
55
|
print(file=output)
|
|
61
56
|
|
|
62
|
-
print(f
|
|
63
|
-
print(f
|
|
57
|
+
print(f'LANG: {os.environ.get("LANG")}', file=output)
|
|
58
|
+
print(f'LC_ALL: {os.environ.get("LC_ALL")}', file=output)
|
|
64
59
|
print(f"sys.getdefaultencoding(): {sys.getdefaultencoding()}", file=output)
|
|
65
60
|
print(f"sys.getfilesystemencoding(): {sys.getfilesystemencoding()}", file=output)
|
|
66
61
|
print(f"locale.getlocale(): {locale.getlocale()}", file=output)
|
|
67
62
|
print(f"locale.getdefaultlocale(): {locale.getdefaultlocale()}", file=output)
|
|
68
|
-
print(
|
|
63
|
+
print(
|
|
64
|
+
f"locale.getpreferredencoding(): {locale.getpreferredencoding()}", file=output
|
|
65
|
+
)
|
|
69
66
|
print(file=output)
|
|
70
67
|
|
|
71
68
|
for key in sorted(environ):
|
|
@@ -816,6 +816,10 @@ APACHE_SETENV_CONFIG = """
|
|
|
816
816
|
SetEnv '%(name)s' '%(value)s'
|
|
817
817
|
"""
|
|
818
818
|
|
|
819
|
+
APACHE_PYTHON_WARNINGS_CONFIG = """
|
|
820
|
+
WSGIPythonWarnings '%(spec)s'
|
|
821
|
+
"""
|
|
822
|
+
|
|
819
823
|
APACHE_PASSENV_CONFIG = """
|
|
820
824
|
PassEnv '%(name)s'
|
|
821
825
|
"""
|
|
@@ -982,6 +986,11 @@ def generate_apache_config(options):
|
|
|
982
986
|
print(APACHE_SETENV_CONFIG % dict(name=name, value=value),
|
|
983
987
|
file=fp)
|
|
984
988
|
|
|
989
|
+
if options['python_warnings']:
|
|
990
|
+
for spec in options['python_warnings']:
|
|
991
|
+
print(APACHE_PYTHON_WARNINGS_CONFIG % dict(spec=spec),
|
|
992
|
+
file=fp)
|
|
993
|
+
|
|
985
994
|
if options['passenv_variables']:
|
|
986
995
|
for name in options['passenv_variables']:
|
|
987
996
|
print(APACHE_PASSENV_CONFIG % dict(name=name), file=fp)
|
|
@@ -751,6 +751,17 @@ add_option('all', '--python-eggs', metavar='DIRECTORY-PATH',
|
|
|
751
751
|
'unpacking of Python eggs. Defaults to a sub directory of '
|
|
752
752
|
'the server root directory.')
|
|
753
753
|
|
|
754
|
+
add_option('all', '--python-warnings', action='append', default=[],
|
|
755
|
+
metavar='SPEC', help='Add an entry to the Python warnings filter '
|
|
756
|
+
'using the standard -W syntax (action:message:category:module:lineno). '
|
|
757
|
+
'May be specified multiple times; each entry is emitted as a '
|
|
758
|
+
'separate WSGIPythonWarnings directive in the generated '
|
|
759
|
+
'configuration. The most common form is just an action: '
|
|
760
|
+
'default, error, always, module, once, or ignore. For example '
|
|
761
|
+
'--python-warnings error converts every warning into an '
|
|
762
|
+
'exception, which is useful for catching deprecation regressions '
|
|
763
|
+
'in CI.')
|
|
764
|
+
|
|
754
765
|
add_option('unix', '--shell-executable', default=SHELL,
|
|
755
766
|
metavar='FILE-PATH', help='Override the path to the shell '
|
|
756
767
|
'used in the \'apachectl\' script. The \'bash\' shell will '
|
|
@@ -886,6 +886,23 @@ const char *wsgi_set_accept_mutex(cmd_parms *cmd, void *mconfig,
|
|
|
886
886
|
static apr_file_t *wsgi_signal_pipe_in = NULL;
|
|
887
887
|
static apr_file_t *wsgi_signal_pipe_out = NULL;
|
|
888
888
|
|
|
889
|
+
/*
|
|
890
|
+
* Dedicated pipe carrying SIGHUP / SIGUSR2 bytes from the signal
|
|
891
|
+
* handler to the signal dispatcher thread. Independent of the
|
|
892
|
+
* shutdown pipe above so that long-running subscriber callbacks
|
|
893
|
+
* cannot delay the daemon main thread's detection of shutdown
|
|
894
|
+
* signals. Write end is set non-blocking after creation so the
|
|
895
|
+
* signal handler can never deadlock on a full pipe buffer; the
|
|
896
|
+
* trade-off is that signals arriving faster than subscribers
|
|
897
|
+
* consume them are silently dropped, which matches the
|
|
898
|
+
* not-refcounted semantics of Unix signals themselves.
|
|
899
|
+
*/
|
|
900
|
+
|
|
901
|
+
static apr_file_t *wsgi_signal_event_pipe_in = NULL;
|
|
902
|
+
static apr_file_t *wsgi_signal_event_pipe_out = NULL;
|
|
903
|
+
|
|
904
|
+
static apr_thread_t *wsgi_signal_dispatcher = NULL;
|
|
905
|
+
|
|
889
906
|
static void wsgi_signal_handler(int signum)
|
|
890
907
|
{
|
|
891
908
|
apr_size_t nbytes = 1;
|
|
@@ -909,6 +926,22 @@ static void wsgi_signal_handler(int signum)
|
|
|
909
926
|
apr_file_write(wsgi_signal_pipe_out, "C", &nbytes);
|
|
910
927
|
apr_file_flush(wsgi_signal_pipe_out);
|
|
911
928
|
}
|
|
929
|
+
else if (signum == SIGHUP)
|
|
930
|
+
{
|
|
931
|
+
if (wsgi_signal_event_pipe_out)
|
|
932
|
+
{
|
|
933
|
+
apr_file_write(wsgi_signal_event_pipe_out, "H", &nbytes);
|
|
934
|
+
apr_file_flush(wsgi_signal_event_pipe_out);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
else if (signum == SIGUSR2)
|
|
938
|
+
{
|
|
939
|
+
if (wsgi_signal_event_pipe_out)
|
|
940
|
+
{
|
|
941
|
+
apr_file_write(wsgi_signal_event_pipe_out, "U", &nbytes);
|
|
942
|
+
apr_file_flush(wsgi_signal_event_pipe_out);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
912
945
|
else
|
|
913
946
|
{
|
|
914
947
|
wsgi_daemon_shutdown++;
|
|
@@ -918,6 +951,69 @@ static void wsgi_signal_handler(int signum)
|
|
|
918
951
|
}
|
|
919
952
|
}
|
|
920
953
|
|
|
954
|
+
static void *wsgi_signal_dispatcher_thread(apr_thread_t *thd, void *data)
|
|
955
|
+
{
|
|
956
|
+
WSGIDaemonProcess *daemon = data;
|
|
957
|
+
|
|
958
|
+
apr_pollfd_t poll_fd;
|
|
959
|
+
apr_int32_t poll_count = 0;
|
|
960
|
+
apr_status_t rv;
|
|
961
|
+
|
|
962
|
+
poll_fd.desc_type = APR_POLL_FILE;
|
|
963
|
+
poll_fd.reqevents = APR_POLLIN;
|
|
964
|
+
poll_fd.desc.f = wsgi_signal_event_pipe_in;
|
|
965
|
+
|
|
966
|
+
while (1)
|
|
967
|
+
{
|
|
968
|
+
char buf[1];
|
|
969
|
+
apr_size_t nbytes = 1;
|
|
970
|
+
int signum;
|
|
971
|
+
const char *signame;
|
|
972
|
+
|
|
973
|
+
rv = apr_poll(&poll_fd, 1, &poll_count, -1);
|
|
974
|
+
if (APR_STATUS_IS_EINTR(rv))
|
|
975
|
+
continue;
|
|
976
|
+
if (rv != APR_SUCCESS)
|
|
977
|
+
break;
|
|
978
|
+
|
|
979
|
+
rv = apr_file_read(wsgi_signal_event_pipe_in, buf, &nbytes);
|
|
980
|
+
|
|
981
|
+
if (APR_STATUS_IS_EOF(rv))
|
|
982
|
+
break;
|
|
983
|
+
|
|
984
|
+
if (rv != APR_SUCCESS || nbytes != 1)
|
|
985
|
+
{
|
|
986
|
+
if (!wsgi_daemon_shutdown)
|
|
987
|
+
{
|
|
988
|
+
wsgi_log_error(APLOG_ALERT, 0, wsgi_server, WSGI_APLOGNO(0208) "Read failed on signal event pipe in "
|
|
989
|
+
"daemon process '%s'; signal dispatcher "
|
|
990
|
+
"exiting.",
|
|
991
|
+
daemon->group->name);
|
|
992
|
+
}
|
|
993
|
+
break;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
switch (buf[0])
|
|
997
|
+
{
|
|
998
|
+
case 'H':
|
|
999
|
+
signum = SIGHUP;
|
|
1000
|
+
signame = "SIGHUP";
|
|
1001
|
+
break;
|
|
1002
|
+
case 'U':
|
|
1003
|
+
signum = SIGUSR2;
|
|
1004
|
+
signame = "SIGUSR2";
|
|
1005
|
+
break;
|
|
1006
|
+
default:
|
|
1007
|
+
/* Unknown tag byte; defensive skip. */
|
|
1008
|
+
continue;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
wsgi_publish_process_signal(signum, signame);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
return NULL;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
921
1017
|
static void wsgi_exit_daemon_process(int status)
|
|
922
1018
|
{
|
|
923
1019
|
if (wsgi_server && wsgi_daemon_group)
|
|
@@ -2991,6 +3087,25 @@ static void wsgi_daemon_main(apr_pool_t *p, WSGIDaemonProcess *daemon)
|
|
|
2991
3087
|
}
|
|
2992
3088
|
}
|
|
2993
3089
|
|
|
3090
|
+
/*
|
|
3091
|
+
* Spawn the signal dispatcher thread that drains the signal
|
|
3092
|
+
* event pipe and publishes process_signal events to registered
|
|
3093
|
+
* Python subscribers. Created unconditionally since subscribers
|
|
3094
|
+
* can register at any point during the daemon's lifetime.
|
|
3095
|
+
*/
|
|
3096
|
+
|
|
3097
|
+
rv = apr_thread_create(&wsgi_signal_dispatcher, thread_attr,
|
|
3098
|
+
wsgi_signal_dispatcher_thread, daemon, p);
|
|
3099
|
+
|
|
3100
|
+
if (rv != APR_SUCCESS)
|
|
3101
|
+
{
|
|
3102
|
+
wsgi_log_error(APLOG_ERR, rv, wsgi_server, WSGI_APLOGNO(0207) "Unable to create signal dispatcher thread in "
|
|
3103
|
+
"daemon process '%s'; SIGHUP and SIGUSR2 signals "
|
|
3104
|
+
"will not be delivered to mod_wsgi.subscribe_signals "
|
|
3105
|
+
"subscribers.",
|
|
3106
|
+
daemon->group->name);
|
|
3107
|
+
}
|
|
3108
|
+
|
|
2994
3109
|
/*
|
|
2995
3110
|
* Start telemetry reporter if configured. Skip for service-script
|
|
2996
3111
|
* daemons (threads=0): they handle no requests, so there is
|
|
@@ -3254,6 +3369,40 @@ static void wsgi_daemon_main(apr_pool_t *p, WSGIDaemonProcess *daemon)
|
|
|
3254
3369
|
}
|
|
3255
3370
|
}
|
|
3256
3371
|
|
|
3372
|
+
/*
|
|
3373
|
+
* Stop the signal dispatcher before publishing process_stopping
|
|
3374
|
+
* so the dispatcher is not still inside a sub-interpreter GIL
|
|
3375
|
+
* acquire when the shutdown publish walks the same interpreter
|
|
3376
|
+
* table. Ignoring SIGHUP/SIGUSR2 first means no new bytes can
|
|
3377
|
+
* appear in the pipe; closing the write end then makes the
|
|
3378
|
+
* dispatcher's pending read return EOF and exit cleanly. The
|
|
3379
|
+
* join is best-effort: if a subscriber callback is wedged the
|
|
3380
|
+
* shutdown-timeout reaper still bounds the total wait via
|
|
3381
|
+
* exit().
|
|
3382
|
+
*/
|
|
3383
|
+
|
|
3384
|
+
apr_signal(SIGHUP, SIG_IGN);
|
|
3385
|
+
apr_signal(SIGUSR2, SIG_IGN);
|
|
3386
|
+
|
|
3387
|
+
if (wsgi_signal_event_pipe_out)
|
|
3388
|
+
{
|
|
3389
|
+
apr_file_close(wsgi_signal_event_pipe_out);
|
|
3390
|
+
wsgi_signal_event_pipe_out = NULL;
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
if (wsgi_signal_dispatcher)
|
|
3394
|
+
{
|
|
3395
|
+
rv = apr_thread_join(&thread_rv, wsgi_signal_dispatcher);
|
|
3396
|
+
if (rv != APR_SUCCESS)
|
|
3397
|
+
{
|
|
3398
|
+
wsgi_log_error(APLOG_WARNING, rv, wsgi_server,
|
|
3399
|
+
"Unable to join with signal dispatcher thread "
|
|
3400
|
+
"in daemon process '%s' during shutdown.",
|
|
3401
|
+
daemon->group->name);
|
|
3402
|
+
}
|
|
3403
|
+
wsgi_signal_dispatcher = NULL;
|
|
3404
|
+
}
|
|
3405
|
+
|
|
3257
3406
|
/*
|
|
3258
3407
|
* If shutting down process due to reaching request time
|
|
3259
3408
|
* limit, then try and dump out stack traces of any threads
|
|
@@ -3546,6 +3695,31 @@ static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon)
|
|
|
3546
3695
|
wsgi_daemon_init_failure_exit();
|
|
3547
3696
|
}
|
|
3548
3697
|
|
|
3698
|
+
/*
|
|
3699
|
+
* Dedicated pipe for the application-facing signal events
|
|
3700
|
+
* (SIGHUP, SIGUSR2). Separate from the shutdown signal pipe
|
|
3701
|
+
* so that the daemon main thread reading shutdown bytes is
|
|
3702
|
+
* never delayed by subscriber callback work. Write end is
|
|
3703
|
+
* set non-blocking so the in-signal-handler write cannot
|
|
3704
|
+
* deadlock if subscribers are slow enough to fill the
|
|
3705
|
+
* kernel pipe buffer.
|
|
3706
|
+
*/
|
|
3707
|
+
|
|
3708
|
+
status = apr_file_pipe_create(&wsgi_signal_event_pipe_in,
|
|
3709
|
+
&wsgi_signal_event_pipe_out, p);
|
|
3710
|
+
|
|
3711
|
+
if (status != APR_SUCCESS)
|
|
3712
|
+
{
|
|
3713
|
+
wsgi_log_error(APLOG_ALERT, status, wsgi_server, WSGI_APLOGNO(0206) "Unable to initialise signal event pipe in "
|
|
3714
|
+
"daemon process '%s'. Daemon process will "
|
|
3715
|
+
"exit and be restarted after a delay.",
|
|
3716
|
+
daemon->group->name);
|
|
3717
|
+
|
|
3718
|
+
wsgi_daemon_init_failure_exit();
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3721
|
+
apr_file_pipe_timeout_set(wsgi_signal_event_pipe_out, 0);
|
|
3722
|
+
|
|
3549
3723
|
wsgi_daemon_shutdown = 0;
|
|
3550
3724
|
|
|
3551
3725
|
wsgi_daemon_pid = getpid();
|
|
@@ -3559,6 +3733,19 @@ static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon)
|
|
|
3559
3733
|
apr_signal(SIGXCPU, wsgi_signal_handler);
|
|
3560
3734
|
#endif
|
|
3561
3735
|
|
|
3736
|
+
/*
|
|
3737
|
+
* Application-facing signal handlers. SIGHUP and SIGUSR2
|
|
3738
|
+
* are not used by Apache for child process management
|
|
3739
|
+
* (Apache uses SIGHUP at the parent only, and translates
|
|
3740
|
+
* parent shutdown to SIGTERM/SIGUSR1 for the daemon); they
|
|
3741
|
+
* are routed through the signal event pipe to the
|
|
3742
|
+
* dispatcher thread which calls registered Python
|
|
3743
|
+
* subscribers via mod_wsgi.subscribe_signals.
|
|
3744
|
+
*/
|
|
3745
|
+
|
|
3746
|
+
apr_signal(SIGHUP, wsgi_signal_handler);
|
|
3747
|
+
apr_signal(SIGUSR2, wsgi_signal_handler);
|
|
3748
|
+
|
|
3562
3749
|
/* Set limits on amount of CPU time that can be used. */
|
|
3563
3750
|
|
|
3564
3751
|
if (daemon->group->cpu_time_limit > 0)
|
|
@@ -1687,8 +1687,6 @@ failure:
|
|
|
1687
1687
|
static void wsgi_destroy_interpreter(InterpreterObject *self)
|
|
1688
1688
|
{
|
|
1689
1689
|
PyThreadState *tstate = NULL;
|
|
1690
|
-
PyObject *module = NULL;
|
|
1691
|
-
PyObject *exitfunc = NULL;
|
|
1692
1690
|
|
|
1693
1691
|
PyThreadState *tstate_enter = NULL;
|
|
1694
1692
|
|
|
@@ -1769,87 +1767,6 @@ static void wsgi_destroy_interpreter(InterpreterObject *self)
|
|
|
1769
1767
|
wsgi_server->process->pool));
|
|
1770
1768
|
}
|
|
1771
1769
|
|
|
1772
|
-
#if 0
|
|
1773
|
-
/*
|
|
1774
|
-
* Disabled: Py_EndInterpreter on Python 3.7+ runs the
|
|
1775
|
-
* threading._shutdown atexit hook itself during sub-interpreter
|
|
1776
|
-
* teardown, so the destroy thread no longer needs to be
|
|
1777
|
-
* pre-registered in threading._active for that hook to find a
|
|
1778
|
-
* Thread handle. Left in #if 0 so the original rationale and
|
|
1779
|
-
* call shape are recoverable if a regression turns up.
|
|
1780
|
-
*/
|
|
1781
|
-
|
|
1782
|
-
module = PyImport_ImportModule("threading");
|
|
1783
|
-
|
|
1784
|
-
if (!module)
|
|
1785
|
-
PyErr_Clear();
|
|
1786
|
-
|
|
1787
|
-
if (module)
|
|
1788
|
-
{
|
|
1789
|
-
PyObject *dict = NULL;
|
|
1790
|
-
PyObject *func = NULL;
|
|
1791
|
-
|
|
1792
|
-
dict = PyModule_GetDict(module);
|
|
1793
|
-
func = PyDict_GetItemString(dict, "current_thread");
|
|
1794
|
-
if (func)
|
|
1795
|
-
{
|
|
1796
|
-
PyObject *res = NULL;
|
|
1797
|
-
Py_INCREF(func);
|
|
1798
|
-
res = PyObject_CallObject(func, (PyObject *)NULL);
|
|
1799
|
-
if (!res)
|
|
1800
|
-
{
|
|
1801
|
-
PyErr_Clear();
|
|
1802
|
-
}
|
|
1803
|
-
Py_XDECREF(res);
|
|
1804
|
-
Py_DECREF(func);
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
|
|
1808
|
-
Py_XDECREF(module);
|
|
1809
|
-
#endif
|
|
1810
|
-
|
|
1811
|
-
#if 0
|
|
1812
|
-
/*
|
|
1813
|
-
* Disabled: Py_EndInterpreter on Python 3.7+ calls
|
|
1814
|
-
* _PyAtExit_Call on the sub-interpreter itself, so driving
|
|
1815
|
-
* atexit._run_exitfuncs here just runs every callback twice
|
|
1816
|
-
* (atexit does not deregister as it calls). Left in #if 0 so
|
|
1817
|
-
* the original call shape is recoverable.
|
|
1818
|
-
*/
|
|
1819
|
-
|
|
1820
|
-
module = NULL;
|
|
1821
|
-
|
|
1822
|
-
if (is_sub)
|
|
1823
|
-
{
|
|
1824
|
-
module = PyImport_ImportModule("atexit");
|
|
1825
|
-
|
|
1826
|
-
if (module)
|
|
1827
|
-
{
|
|
1828
|
-
PyObject *dict = NULL;
|
|
1829
|
-
|
|
1830
|
-
dict = PyModule_GetDict(module);
|
|
1831
|
-
exitfunc = PyDict_GetItemString(dict, "_run_exitfuncs");
|
|
1832
|
-
}
|
|
1833
|
-
else
|
|
1834
|
-
PyErr_Clear();
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
if (exitfunc)
|
|
1838
|
-
{
|
|
1839
|
-
PyObject *res = NULL;
|
|
1840
|
-
Py_INCREF(exitfunc);
|
|
1841
|
-
res = PyObject_CallObject(exitfunc, (PyObject *)NULL);
|
|
1842
|
-
|
|
1843
|
-
if (res == NULL)
|
|
1844
|
-
wsgi_log_python_interp_atexit_error(self->name);
|
|
1845
|
-
|
|
1846
|
-
Py_XDECREF(res);
|
|
1847
|
-
Py_DECREF(exitfunc);
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
Py_XDECREF(module);
|
|
1851
|
-
#endif
|
|
1852
|
-
|
|
1853
1770
|
if (is_sub)
|
|
1854
1771
|
{
|
|
1855
1772
|
/*
|
|
@@ -2883,6 +2800,91 @@ void wsgi_publish_process_stopping(char *reason)
|
|
|
2883
2800
|
}
|
|
2884
2801
|
}
|
|
2885
2802
|
|
|
2803
|
+
void wsgi_publish_process_signal(int signum, const char *signame)
|
|
2804
|
+
{
|
|
2805
|
+
apr_pool_t *pool = NULL;
|
|
2806
|
+
apr_array_header_t *names = NULL;
|
|
2807
|
+
apr_hash_index_t *hi;
|
|
2808
|
+
apr_status_t rv;
|
|
2809
|
+
int i;
|
|
2810
|
+
|
|
2811
|
+
/*
|
|
2812
|
+
* Snapshot the interpreter name table under wsgi_interp_lock.
|
|
2813
|
+
* Worker threads may grow the table concurrently via
|
|
2814
|
+
* wsgi_acquire_interpreter, so iterating it directly is unsafe;
|
|
2815
|
+
* a snapshot lets us release the lock before acquiring any
|
|
2816
|
+
* sub-interpreter GIL (acquire then takes wsgi_interp_lock again
|
|
2817
|
+
* internally, which would deadlock if we still held it here).
|
|
2818
|
+
*/
|
|
2819
|
+
|
|
2820
|
+
rv = apr_pool_create(&pool, NULL);
|
|
2821
|
+
if (rv != APR_SUCCESS)
|
|
2822
|
+
return;
|
|
2823
|
+
|
|
2824
|
+
apr_thread_mutex_lock(wsgi_interp_lock);
|
|
2825
|
+
|
|
2826
|
+
names = apr_array_make(pool, apr_hash_count(wsgi_interpreters),
|
|
2827
|
+
sizeof(const char *));
|
|
2828
|
+
|
|
2829
|
+
for (hi = apr_hash_first(pool, wsgi_interpreters); hi;
|
|
2830
|
+
hi = apr_hash_next(hi))
|
|
2831
|
+
{
|
|
2832
|
+
const void *key;
|
|
2833
|
+
apr_hash_this(hi, &key, NULL, NULL);
|
|
2834
|
+
APR_ARRAY_PUSH(names, const char *) = apr_pstrdup(pool,
|
|
2835
|
+
(const char *)key);
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
apr_thread_mutex_unlock(wsgi_interp_lock);
|
|
2839
|
+
|
|
2840
|
+
for (i = 0; i < names->nelts; i++)
|
|
2841
|
+
{
|
|
2842
|
+
const char *name = APR_ARRAY_IDX(names, i, const char *);
|
|
2843
|
+
InterpreterObject *interp = NULL;
|
|
2844
|
+
PyObject *event = NULL;
|
|
2845
|
+
PyObject *signame_obj = NULL;
|
|
2846
|
+
PyObject *signum_obj = NULL;
|
|
2847
|
+
int ok = 0;
|
|
2848
|
+
|
|
2849
|
+
interp = wsgi_acquire_interpreter((char *)name);
|
|
2850
|
+
|
|
2851
|
+
if (!interp)
|
|
2852
|
+
continue;
|
|
2853
|
+
|
|
2854
|
+
event = PyDict_New();
|
|
2855
|
+
signame_obj = PyUnicode_FromString(signame);
|
|
2856
|
+
signum_obj = PyLong_FromLong(signum);
|
|
2857
|
+
|
|
2858
|
+
if (event && signame_obj && signum_obj &&
|
|
2859
|
+
PyDict_SetItemString(event, "signame", signame_obj) == 0 &&
|
|
2860
|
+
PyDict_SetItemString(event, "signum", signum_obj) == 0)
|
|
2861
|
+
{
|
|
2862
|
+
wsgi_publish_event("process_signal", event);
|
|
2863
|
+
ok = 1;
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
Py_XDECREF(signum_obj);
|
|
2867
|
+
Py_XDECREF(signame_obj);
|
|
2868
|
+
Py_XDECREF(event);
|
|
2869
|
+
|
|
2870
|
+
if (!ok)
|
|
2871
|
+
{
|
|
2872
|
+
wsgi_log_error_locked(APLOG_ERR, 0, wsgi_server,
|
|
2873
|
+
WSGI_APLOGNO(0209) "Unable to publish "
|
|
2874
|
+
"'process_signal' event "
|
|
2875
|
+
"for %s.",
|
|
2876
|
+
wsgi_format_interp_context(
|
|
2877
|
+
wsgi_server->process->pool, NULL,
|
|
2878
|
+
name));
|
|
2879
|
+
PyErr_Clear();
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
wsgi_release_interpreter(interp);
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
apr_pool_destroy(pool);
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2886
2888
|
/* ------------------------------------------------------------------------- */
|
|
2887
2889
|
|
|
2888
2890
|
/*
|
|
@@ -119,6 +119,19 @@ extern int wsgi_interpreter_exists(const char *name);
|
|
|
119
119
|
|
|
120
120
|
extern void wsgi_publish_process_stopping(char *reason);
|
|
121
121
|
|
|
122
|
+
/*
|
|
123
|
+
* Publish a "process_signal" event into every existing sub-interpreter
|
|
124
|
+
* in this process. Snapshots the interpreter name table under
|
|
125
|
+
* wsgi_interp_lock so the walk is safe against concurrent creation by
|
|
126
|
+
* worker threads, then for each name acquires the interpreter (which
|
|
127
|
+
* takes the GIL for that interpreter), constructs an event dict with
|
|
128
|
+
* signame (canonical str like "SIGHUP") and signum (int) payload keys,
|
|
129
|
+
* and dispatches via wsgi_publish_event. Intended to be called only
|
|
130
|
+
* from the signal dispatcher thread in daemon mode.
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
extern void wsgi_publish_process_signal(int signum, const char *signame);
|
|
134
|
+
|
|
122
135
|
extern apr_status_t wsgi_python_child_init(apr_pool_t *p);
|
|
123
136
|
|
|
124
137
|
/* ------------------------------------------------------------------------- */
|