From 01c7bae542b71f842d36577dafae0af1d996ef4a Mon Sep 17 00:00:00 2001 From: Colin Date: Thu, 19 Dec 2024 08:06:11 +0000 Subject: [PATCH] bunpen: pasta setup (ip ...): never outlive parent bunpen instance --- .../bunpen/restrict/ns/multiprocess.ha | 46 +++++++++++++++++++ pkgs/by-name/bunpen/restrict/ns/pasta.ha | 6 +-- pkgs/by-name/bunpen/rt/ext/exec.ha | 45 ------------------ 3 files changed, 49 insertions(+), 48 deletions(-) diff --git a/pkgs/by-name/bunpen/restrict/ns/multiprocess.ha b/pkgs/by-name/bunpen/restrict/ns/multiprocess.ha index da22bd007..847b520f6 100644 --- a/pkgs/by-name/bunpen/restrict/ns/multiprocess.ha +++ b/pkgs/by-name/bunpen/restrict/ns/multiprocess.ha @@ -8,6 +8,7 @@ use log; use os; use os::exec; use rt; +use rt::ext; use unix::signal; // fork and: @@ -65,6 +66,51 @@ fn wait_and_propagate(child_pid: os::exec::process) (void | os::exec::error) = { os::exit(rc); // propagate exit code }; +// run the provided command in a new process, and wait for its return. +// under the hood: +// 1. fork +// 2. in child: execvpe +// 3. in parent: wait for child +export fn shellvpe(name: str, argv: []str, envp: []str = []) (os::exec::status | os::exec::error | rt::errno) = { + return match (fork_and_die_with_parent()?) { + case void => + errors::ext::check("shellvpe", rt::ext::execvpe(name, argv, envp)); + assert(false, "unreachable"); + return os::exec::status { ... }; + case let child_pid: os::exec::process => + yield wait_child(child_pid); + }; +}; + +// same as `shellvpe(name, argv, envp)` but fallback to `name=default` if `name` +// isn't found on PATH +export fn shellvpe_or_default(default: str, name: str, argv: []str, envp: []str = []) (os::exec::status | os::exec::error | rt::errno) = { + return match (fork_and_die_with_parent()?) { + case void => + errors::ext::check("shellvpe", rt::ext::execvpe_or_default(default, name, argv, envp)); + assert(false, "unreachable"); + return os::exec::status { ... }; + case let child_pid: os::exec::process => + yield wait_child(child_pid); + }; +}; + +// fn wait_child(child_pid: os::exec::process) (os::exec::status | os::exec::error) = { +// for (true) { +// match (os::exec::wait(&child_pid)) { +// case let e: os::exec::error => match (e) { +// case errors::interrupted => +// // 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 +// // reason we woke isn't because the child died... +// void; +// case => return e; +// }; +// case let status: os::exec::status => return status; +// }; +// }; +// }; + // 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. diff --git a/pkgs/by-name/bunpen/restrict/ns/pasta.ha b/pkgs/by-name/bunpen/restrict/ns/pasta.ha index 4bdfefb62..0825d7b67 100644 --- a/pkgs/by-name/bunpen/restrict/ns/pasta.ha +++ b/pkgs/by-name/bunpen/restrict/ns/pasta.ha @@ -123,7 +123,7 @@ fn config_routing_in_ns(net: restrict::net_subset) (void | os::exec::error | rt: // forward dns to the desired endpoint let dnsdest = fmt::asprintf("{}:53", net.dns); defer free(dnsdest); - let rc = rt::ext::shellvpe_or_default( + let rc = shellvpe_or_default( config::IPTABLES, "iptables", [ @@ -147,7 +147,7 @@ fn config_routing_in_ns(net: restrict::net_subset) (void | os::exec::error | rt: // therefore, set it up *now*, and delete the addrs, and then since it's // already up they won't re-appear when we exec pasta. - let rc = rt::ext::shellvpe_or_default( + let rc = shellvpe_or_default( config::IP, "ip", [ @@ -159,7 +159,7 @@ fn config_routing_in_ns(net: restrict::net_subset) (void | os::exec::error | rt: )?; log::printfln("[namespace/pasta] 'ip link set lo up' exited {}", rc.status); - let rc = rt::ext::shellvpe_or_default( + let rc = shellvpe_or_default( config::IP, "ip", [ diff --git a/pkgs/by-name/bunpen/rt/ext/exec.ha b/pkgs/by-name/bunpen/rt/ext/exec.ha index cc71a6b4e..ab0c32011 100644 --- a/pkgs/by-name/bunpen/rt/ext/exec.ha +++ b/pkgs/by-name/bunpen/rt/ext/exec.ha @@ -47,51 +47,6 @@ export fn execvpe_or_default(default: str, name: str, argv: []str, envp: []str = }; }; -// run the provided command in a new process, and wait for its return. -// under the hood: -// 1. fork -// 2. in child: execvpe -// 3. in parent: wait for child -export fn shellvpe(name: str, argv: []str, envp: []str = []) (os::exec::status | os::exec::error) = { - return match (os::exec::fork()) { - case void => - errors::ext::check("shellvpe", execvpe(name, argv, envp)); - assert(false, "unreachable"); - return os::exec::status { ... }; - case let child_pid: os::exec::process => - yield wait_child(child_pid); - }; -}; - -// same as `shellvpe(name, argv, envp)` but fallback to `name=default` if `name` -// isn't found on PATH -export fn shellvpe_or_default(default: str, name: str, argv: []str, envp: []str = []) (os::exec::status | os::exec::error) = { - return match (os::exec::fork()) { - case void => - errors::ext::check("shellvpe", execvpe_or_default(default, name, argv, envp)); - assert(false, "unreachable"); - return os::exec::status { ... }; - case let child_pid: os::exec::process => - yield wait_child(child_pid); - }; -}; - -fn wait_child(child_pid: os::exec::process) (os::exec::status | os::exec::error) = { - for (true) { - match (os::exec::wait(&child_pid)) { - case let e: os::exec::error => match (e) { - case errors::interrupted => - // 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 - // reason we woke isn't because the child died... - void; - case => return e; - }; - case let status: os::exec::status => return status; - }; - }; -}; - // allocate and return a NULL-terminated array of pointers to c strings. // caller is responsible for free'ing the resulting array AND its strings. fn to_cstr_array(strs: []str) []nullable *const c::char = {