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