aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2019-01-19 20:02:35 -0800
committerZac Medico <zmedico@gentoo.org>2019-01-20 15:31:41 -0800
commit0d18c696230bcabe007e35194e4b97a38f3b69f5 (patch)
tree8c409700f223e119c10fd0b16e09abda7d47985d
parentINSTALL_MASK: index patterns anchored with leading slash (bug 675826) (diff)
downloadportage-0d18c696.tar.gz
portage-0d18c696.tar.bz2
portage-0d18c696.zip
pid-ns-init: fix child process signal disposition (bug 675828)
Use subprocess.Popen to correctly configure the signal disposition of the child process, since os.fork leaves the signal disposition in a state which may be inappropriate for various signals including SIGPIPE, SIGQUIT, SIGTERM, and SIGINT. For python implementations other that CPython >= 3, use preexec_fn to manually configure the signal disposition (I have found that this is necessary for CPython 2.7 and all PyPy versions tested, including PyPy3). Bug: https://bugs.gentoo.org/675828 Signed-off-by: Zac Medico <zmedico@gentoo.org>
-rw-r--r--bin/pid-ns-init39
-rw-r--r--lib/portage/process.py1
2 files changed, 34 insertions, 6 deletions
diff --git a/bin/pid-ns-init b/bin/pid-ns-init
index 182d00a43..f9b8cc4f3 100644
--- a/bin/pid-ns-init
+++ b/bin/pid-ns-init
@@ -2,25 +2,44 @@
# Copyright 2018-2019 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
+import errno
import functools
import os
+import platform
import signal
+import subprocess
import sys
+if sys.version_info.major < 3 or platform.python_implementation() != 'CPython':
+ def signal_disposition_preexec():
+ for signum in (
+ signal.SIGHUP,
+ signal.SIGINT,
+ signal.SIGPIPE,
+ signal.SIGQUIT,
+ signal.SIGTERM,
+ ):
+ signal.signal(signum, signal.SIG_DFL)
+else:
+ # CPython >= 3 subprocess.Popen handles this internally.
+ signal_disposition_preexec = None
+
+
KILL_SIGNALS = (
signal.SIGINT,
signal.SIGTERM,
signal.SIGHUP,
)
+
def forward_kill_signal(main_child_pid, signum, frame):
os.kill(main_child_pid, signum)
def main(argv):
if len(argv) < 2:
- return 'Usage: {} <main-child-pid> or <binary> <argv0> [arg]..'.format(argv[0])
+ return 'Usage: {} <main-child-pid> or <pass_fds> <binary> <argv0> [arg]..'.format(argv[0])
if len(argv) == 2:
# The child process is init (pid 1) in a child pid namespace, and
@@ -28,14 +47,17 @@ def main(argv):
# (forwarding signals to init and forwarding exit status to the parent
# process).
main_child_pid = int(argv[1])
+ proc = None
else:
# The current process is init (pid 1) in a child pid namespace.
- binary = argv[1]
- args = argv[2:]
+ pass_fds, binary, args = tuple(int(fd) for fd in argv[1].split(',')), argv[2], argv[3:]
- main_child_pid = os.fork()
- if main_child_pid == 0:
- os.execv(binary, args)
+ popen_kwargs = {}
+ if sys.version_info.major > 2:
+ popen_kwargs['pass_fds'] = pass_fds
+ proc = subprocess.Popen(args, executable=binary,
+ preexec_fn=signal_disposition_preexec, **popen_kwargs)
+ main_child_pid = proc.pid
sig_handler = functools.partial(forward_kill_signal, main_child_pid)
for signum in KILL_SIGNALS:
@@ -50,6 +72,11 @@ def main(argv):
continue
raise
if pid == main_child_pid:
+ if proc is not None:
+ # Suppress warning messages like this:
+ # ResourceWarning: subprocess 1234 is still running
+ proc.returncode = 0
+
if os.WIFEXITED(status):
return os.WEXITSTATUS(status)
elif os.WIFSIGNALED(status):
diff --git a/lib/portage/process.py b/lib/portage/process.py
index 6af3ac37d..dd3d58ddc 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -571,6 +571,7 @@ def _exec(binary, mycommand, opt_name, fd_pipes,
portage._python_interpreter,
os.path.join(portage._bin_path,
'pid-ns-init'),
+ _unicode_encode(','.join(str(fd) for fd in fd_pipes)),
binary] + myargs
else:
# Execute a supervisor process which will forward