bunpen: reap child processes when acting as PID1
This commit is contained in:
@@ -103,6 +103,17 @@ test_14_keep_net() {
|
|||||||
test -z "$(bunpen --bunpen-path / ip link show lo up)"
|
test -z "$(bunpen --bunpen-path / ip link show lo up)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_15_reap_children() {
|
||||||
|
# in a PID namespace, PID 1 needs to reap children.
|
||||||
|
# that is, any processes which `fork` away from the main program being sandboxed,
|
||||||
|
# and then exit, become zombies: PID 1 needs to `wait` on them to properly dispose of the processes.
|
||||||
|
bunpen --bunpen-path / bash -c "setsid -f true ; sleep 2" &
|
||||||
|
sleep 0.5
|
||||||
|
# check for a line like: `225215 ? Zs 0:00 [true] <defunct>`
|
||||||
|
# if this line exists, then we failed to reap
|
||||||
|
ps x | grep -E 'Zs +[0-9]+:[0-9]+ \[true\] <defunct>' && return 1 || return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
tested=
|
tested=
|
||||||
rc=0
|
rc=0
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
hareHook,
|
hareHook,
|
||||||
iproute2,
|
iproute2,
|
||||||
|
procps,
|
||||||
stdenv,
|
stdenv,
|
||||||
|
util-linux,
|
||||||
which,
|
which,
|
||||||
}: stdenv.mkDerivation {
|
}: stdenv.mkDerivation {
|
||||||
pname = "bunpen";
|
pname = "bunpen";
|
||||||
@@ -13,6 +15,8 @@
|
|||||||
|
|
||||||
nativeCheckInputs = [
|
nativeCheckInputs = [
|
||||||
iproute2
|
iproute2
|
||||||
|
procps # for `ps`
|
||||||
|
util-linux # for `setsid`
|
||||||
which
|
which
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -188,18 +188,29 @@ fn wait_and_propagate(child_pid: os::exec::process) (void | os::exec::error) = {
|
|||||||
os::exit(rc); // propagate exit code
|
os::exit(rc); // propagate exit code
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// block until the provided child exits, and then return its exit status.
|
||||||
|
// this function actually waits on *any* child, in a loop, but only returns the
|
||||||
|
// exit code for the specific child of interest.
|
||||||
|
// this way, we're able to act as a child reaper (as expected of PID 1).
|
||||||
fn wait_child(child_pid: os::exec::process) (os::exec::status | os::exec::error) = {
|
fn wait_child(child_pid: os::exec::process) (os::exec::status | os::exec::error) = {
|
||||||
for (true) {
|
for (true) {
|
||||||
match (os::exec::wait(&child_pid)) {
|
match (os::exec::waitany()) {
|
||||||
case let e: os::exec::error => match (e) {
|
case let e: os::exec::error => match (e) {
|
||||||
case errors::interrupted =>
|
case errors::interrupted =>
|
||||||
// i guess before the days of `poll`, `wait` had to wait on either the
|
// i guess before the days of `poll`, `wait` had to wait on either the
|
||||||
// child OR a signal sent to this pid; so we need to retry if the
|
// child OR a signal sent to this pid; so we need to retry if the
|
||||||
// reason we woke isn't because the child died...
|
// reason we woke isn't because the child died...
|
||||||
log::printfln("[namespace/fork] wait(child) interrupted (signal?)... will retry");
|
log::printfln("[namespace/fork] waitany() interrupted (signal?)... will retry");
|
||||||
case => return e;
|
case => return e;
|
||||||
};
|
};
|
||||||
case let status: os::exec::status => return status;
|
case let proc_and_status: (os::exec::process, os::exec::status) => {
|
||||||
|
let (proc, status) = proc_and_status;
|
||||||
|
if (proc == child_pid) {
|
||||||
|
return status;
|
||||||
|
};
|
||||||
|
// else we've just reaped a zombie child which had forked away from the
|
||||||
|
// main program
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user