Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: bin/, lib/portage/
Date: Mon, 21 Jan 2019 00:02:28
Message-Id: 1548027101.0d18c696230bcabe007e35194e4b97a38f3b69f5.zmedico@gentoo
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