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: Tue, 15 Jan 2019 06:22:56
Message-Id: 1547531155.fb406579b1d13c1ba23b28e0bb794c22878a58c0.zmedico@gentoo
1 commit: fb406579b1d13c1ba23b28e0bb794c22878a58c0
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Sun Jan 13 23:06:35 2019 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Tue Jan 15 05:45:55 2019 +0000
6 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=fb406579
7
8 pid-sandbox: execute pid-ns-init as pid 1 (bug 675312)
9
10 Execute pid-ns-init as the first fork after unshare, as
11 required for it to have pid 1 and become the default reaper
12 of orphaned descendant processes. In _exec, exec a separate
13 pid-ns-init process to behave as a supervisor which will
14 forward signals to init and forward exit status to the parent
15 process.
16
17 Fixes: a75d5546e3a4 ("Introduce a tiny init replacement for inside pid namespace")
18 Bug: https://bugs.gentoo.org/675312
19 Reviewed-by: Brian Dolbec <dolsen <AT> gentoo.org>
20 Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>
21
22 bin/pid-ns-init | 44 ++++++++++++++++++++++++++++++++++++++++----
23 lib/portage/process.py | 25 +++++++++++++++++++------
24 2 files changed, 59 insertions(+), 10 deletions(-)
25
26 diff --git a/bin/pid-ns-init b/bin/pid-ns-init
27 index 843257b70..182d00a43 100644
28 --- a/bin/pid-ns-init
29 +++ b/bin/pid-ns-init
30 @@ -1,23 +1,59 @@
31 #!/usr/bin/env python
32 -# Copyright 2018 Gentoo Authors
33 +# Copyright 2018-2019 Gentoo Authors
34 # Distributed under the terms of the GNU General Public License v2
35
36 +import functools
37 import os
38 +import signal
39 import sys
40
41
42 +KILL_SIGNALS = (
43 + signal.SIGINT,
44 + signal.SIGTERM,
45 + signal.SIGHUP,
46 +)
47 +
48 +def forward_kill_signal(main_child_pid, signum, frame):
49 + os.kill(main_child_pid, signum)
50 +
51 +
52 def main(argv):
53 if len(argv) < 2:
54 - return 'Usage: {} <main-child-pid>'.format(argv[0])
55 - main_child_pid = int(argv[1])
56 + return 'Usage: {} <main-child-pid> or <binary> <argv0> [arg]..'.format(argv[0])
57 +
58 + if len(argv) == 2:
59 + # The child process is init (pid 1) in a child pid namespace, and
60 + # the current process supervises from within the global pid namespace
61 + # (forwarding signals to init and forwarding exit status to the parent
62 + # process).
63 + main_child_pid = int(argv[1])
64 + else:
65 + # The current process is init (pid 1) in a child pid namespace.
66 + binary = argv[1]
67 + args = argv[2:]
68 +
69 + main_child_pid = os.fork()
70 + if main_child_pid == 0:
71 + os.execv(binary, args)
72 +
73 + sig_handler = functools.partial(forward_kill_signal, main_child_pid)
74 + for signum in KILL_SIGNALS:
75 + signal.signal(signum, sig_handler)
76
77 # wait for child processes
78 while True:
79 - pid, status = os.wait()
80 + try:
81 + pid, status = os.wait()
82 + except OSError as e:
83 + if e.errno == errno.EINTR:
84 + continue
85 + raise
86 if pid == main_child_pid:
87 if os.WIFEXITED(status):
88 return os.WEXITSTATUS(status)
89 elif os.WIFSIGNALED(status):
90 + signal.signal(os.WTERMSIG(status), signal.SIG_DFL)
91 os.kill(os.getpid(), os.WTERMSIG(status))
92 # go to the unreachable place
93 break
94
95 diff --git a/lib/portage/process.py b/lib/portage/process.py
96 index 7103b6b31..6af3ac37d 100644
97 --- a/lib/portage/process.py
98 +++ b/lib/portage/process.py
99 @@ -564,15 +564,28 @@ def _exec(binary, mycommand, opt_name, fd_pipes,
100 noiselevel=-1)
101 else:
102 if unshare_pid:
103 - # pid namespace requires us to become init
104 - fork_ret = os.fork()
105 - if fork_ret != 0:
106 - os.execv(portage._python_interpreter, [
107 + main_child_pid = os.fork()
108 + if main_child_pid == 0:
109 + # pid namespace requires us to become init
110 + binary, myargs = portage._python_interpreter, [
111 portage._python_interpreter,
112 os.path.join(portage._bin_path,
113 'pid-ns-init'),
114 - '%s' % fork_ret,
115 - ])
116 + binary] + myargs
117 + else:
118 + # Execute a supervisor process which will forward
119 + # signals to init and forward exit status to the
120 + # parent process. The supervisor process runs in
121 + # the global pid namespace, so skip /proc remount
122 + # and other setup that's intended only for the
123 + # init process.
124 + binary, myargs = portage._python_interpreter, [
125 + portage._python_interpreter,
126 + os.path.join(portage._bin_path,
127 + 'pid-ns-init'), str(main_child_pid)]
128 +
129 + os.execve(binary, myargs, env)
130 +
131 if unshare_mount:
132 # mark the whole filesystem as slave to avoid
133 # mounts escaping the namespace