1 |
commit: 0d18c696230bcabe007e35194e4b97a38f3b69f5 |
2 |
Author: Zac Medico <zmedico <AT> gentoo <DOT> org> |
3 |
AuthorDate: Sun Jan 20 04:02:35 2019 +0000 |
4 |
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> |
5 |
CommitDate: Sun Jan 20 23:31:41 2019 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=0d18c696 |
7 |
|
8 |
pid-ns-init: fix child process signal disposition (bug 675828) |
9 |
|
10 |
Use subprocess.Popen to correctly configure the signal disposition |
11 |
of the child process, since os.fork leaves the signal disposition |
12 |
in a state which may be inappropriate for various signals including |
13 |
SIGPIPE, SIGQUIT, SIGTERM, and SIGINT. For python implementations |
14 |
other that CPython >= 3, use preexec_fn to manually configure the |
15 |
signal disposition (I have found that this is necessary for CPython |
16 |
2.7 and all PyPy versions tested, including PyPy3). |
17 |
|
18 |
Bug: https://bugs.gentoo.org/675828 |
19 |
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org> |
20 |
|
21 |
bin/pid-ns-init | 39 +++++++++++++++++++++++++++++++++------ |
22 |
lib/portage/process.py | 1 + |
23 |
2 files changed, 34 insertions(+), 6 deletions(-) |
24 |
|
25 |
diff --git a/bin/pid-ns-init b/bin/pid-ns-init |
26 |
index 182d00a43..f9b8cc4f3 100644 |
27 |
--- a/bin/pid-ns-init |
28 |
+++ b/bin/pid-ns-init |
29 |
@@ -2,25 +2,44 @@ |
30 |
# Copyright 2018-2019 Gentoo Authors |
31 |
# Distributed under the terms of the GNU General Public License v2 |
32 |
|
33 |
+import errno |
34 |
import functools |
35 |
import os |
36 |
+import platform |
37 |
import signal |
38 |
+import subprocess |
39 |
import sys |
40 |
|
41 |
|
42 |
+if sys.version_info.major < 3 or platform.python_implementation() != 'CPython': |
43 |
+ def signal_disposition_preexec(): |
44 |
+ for signum in ( |
45 |
+ signal.SIGHUP, |
46 |
+ signal.SIGINT, |
47 |
+ signal.SIGPIPE, |
48 |
+ signal.SIGQUIT, |
49 |
+ signal.SIGTERM, |
50 |
+ ): |
51 |
+ signal.signal(signum, signal.SIG_DFL) |
52 |
+else: |
53 |
+ # CPython >= 3 subprocess.Popen handles this internally. |
54 |
+ signal_disposition_preexec = None |
55 |
+ |
56 |
+ |
57 |
KILL_SIGNALS = ( |
58 |
signal.SIGINT, |
59 |
signal.SIGTERM, |
60 |
signal.SIGHUP, |
61 |
) |
62 |
|
63 |
+ |
64 |
def forward_kill_signal(main_child_pid, signum, frame): |
65 |
os.kill(main_child_pid, signum) |
66 |
|
67 |
|
68 |
def main(argv): |
69 |
if len(argv) < 2: |
70 |
- return 'Usage: {} <main-child-pid> or <binary> <argv0> [arg]..'.format(argv[0]) |
71 |
+ return 'Usage: {} <main-child-pid> or <pass_fds> <binary> <argv0> [arg]..'.format(argv[0]) |
72 |
|
73 |
if len(argv) == 2: |
74 |
# The child process is init (pid 1) in a child pid namespace, and |
75 |
@@ -28,14 +47,17 @@ def main(argv): |
76 |
# (forwarding signals to init and forwarding exit status to the parent |
77 |
# process). |
78 |
main_child_pid = int(argv[1]) |
79 |
+ proc = None |
80 |
else: |
81 |
# The current process is init (pid 1) in a child pid namespace. |
82 |
- binary = argv[1] |
83 |
- args = argv[2:] |
84 |
+ pass_fds, binary, args = tuple(int(fd) for fd in argv[1].split(',')), argv[2], argv[3:] |
85 |
|
86 |
- main_child_pid = os.fork() |
87 |
- if main_child_pid == 0: |
88 |
- os.execv(binary, args) |
89 |
+ popen_kwargs = {} |
90 |
+ if sys.version_info.major > 2: |
91 |
+ popen_kwargs['pass_fds'] = pass_fds |
92 |
+ proc = subprocess.Popen(args, executable=binary, |
93 |
+ preexec_fn=signal_disposition_preexec, **popen_kwargs) |
94 |
+ main_child_pid = proc.pid |
95 |
|
96 |
sig_handler = functools.partial(forward_kill_signal, main_child_pid) |
97 |
for signum in KILL_SIGNALS: |
98 |
@@ -50,6 +72,11 @@ def main(argv): |
99 |
continue |
100 |
raise |
101 |
if pid == main_child_pid: |
102 |
+ if proc is not None: |
103 |
+ # Suppress warning messages like this: |
104 |
+ # ResourceWarning: subprocess 1234 is still running |
105 |
+ proc.returncode = 0 |
106 |
+ |
107 |
if os.WIFEXITED(status): |
108 |
return os.WEXITSTATUS(status) |
109 |
elif os.WIFSIGNALED(status): |
110 |
|
111 |
diff --git a/lib/portage/process.py b/lib/portage/process.py |
112 |
index 6af3ac37d..dd3d58ddc 100644 |
113 |
--- a/lib/portage/process.py |
114 |
+++ b/lib/portage/process.py |
115 |
@@ -571,6 +571,7 @@ def _exec(binary, mycommand, opt_name, fd_pipes, |
116 |
portage._python_interpreter, |
117 |
os.path.join(portage._bin_path, |
118 |
'pid-ns-init'), |
119 |
+ _unicode_encode(','.join(str(fd) for fd in fd_pipes)), |
120 |
binary] + myargs |
121 |
else: |
122 |
# Execute a supervisor process which will forward |