bunpen: pasta setup (ip ...): never outlive parent bunpen instance

This commit is contained in:
2024-12-19 08:06:11 +00:00
parent 27e67748d4
commit 01c7bae542
3 changed files with 49 additions and 48 deletions

View File

@@ -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.

View File

@@ -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",
[

View File

@@ -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 = {