bunpen: pasta: wait for pasta to be ready before executing the user program
This commit is contained in:
@@ -19,32 +19,37 @@ fn setup_pasta(net: restrict::net_subset) void = {
|
|||||||
// ordering:
|
// ordering:
|
||||||
// 1. fork into (P, C1)
|
// 1. fork into (P, C1)
|
||||||
// 2. child C1: enter netns, signal parent P.
|
// 2. child C1: enter netns, signal parent P.
|
||||||
// 3. parent P: fork and spawn `pasta --pid /proc/fd/$N` (C2)
|
// 3. parent P: fork and spawn `pasta --pid /dev/fd/$N` (C2)
|
||||||
// then dumbly wait on child (C2) until completion
|
// then dumbly wait on child (C2) until completion
|
||||||
// 4. child C1:: wait for `pasta` to signal readiness on fd `$N`
|
// 4. child C1:: wait for `pasta` to signal readiness on fd `$N`
|
||||||
//
|
//
|
||||||
// after that, C1 can continue on & exec into the user code
|
// after that, C1 can continue on & exec into the user code
|
||||||
//
|
|
||||||
// TODO: this currently lacks the synchronization described above.
|
|
||||||
// it still generally _works_, but possible that some applications are flaky
|
|
||||||
|
|
||||||
let (pipe_parent_rd, pipe_child_wr) = unix::pipe()!;
|
let (pipe_parent_rd, pipe_child_wr) = unix::pipe()!;
|
||||||
|
let (pipe_child_rd, pipe_parent_wr) = unix::pipe()!;
|
||||||
log::printfln("[namespace/pasta]: forking: parent will launch pasta while child will exec user code");
|
log::printfln("[namespace/pasta]: forking: parent will launch pasta while child will exec user code");
|
||||||
match (fork_and_die_with_parent()) {
|
match (fork_and_die_with_parent()) {
|
||||||
case let child_pid: os::exec::process =>
|
case let child_pid: os::exec::process =>
|
||||||
// close the side of the pipe that's not ours:
|
// close the pipe ends which aren't ours
|
||||||
io::close(pipe_child_wr)!;
|
io::close(pipe_child_wr)!;
|
||||||
|
io::close(pipe_child_rd)!;
|
||||||
|
|
||||||
// wait for the child to signal that it's ready for us to attach pasta.
|
// wait for the child to signal that it's ready for us to attach pasta.
|
||||||
io::readall(pipe_parent_rd, &[0u8])!;
|
io::readall(pipe_parent_rd, &[0u8])!;
|
||||||
errors::ext::check("setup_pasta: attach", attach_pasta(net, child_pid));
|
errors::ext::check("setup_pasta: attach", attach_pasta(net, child_pid));
|
||||||
|
|
||||||
|
// let the child know its environment is configured
|
||||||
|
io::write(pipe_parent_wr, [1])!;
|
||||||
|
|
||||||
errors::ext::check("setup_pasta: wait", wait_and_propagate(child_pid));
|
errors::ext::check("setup_pasta: wait", wait_and_propagate(child_pid));
|
||||||
|
|
||||||
// cleanup: we're done with the pipe
|
// cleanup: we're done with the pipes
|
||||||
|
io::close(pipe_parent_wr)!;
|
||||||
io::close(pipe_parent_rd)!;
|
io::close(pipe_parent_rd)!;
|
||||||
|
|
||||||
case void =>
|
case void =>
|
||||||
// close the side of the pipe that's not ours:
|
// close the pipe ends which aren't ours
|
||||||
|
io::close(pipe_parent_wr)!;
|
||||||
io::close(pipe_parent_rd)!;
|
io::close(pipe_parent_rd)!;
|
||||||
|
|
||||||
errors::ext::check("namespace: unshare net", rt::ext::unshare(rt::ext::clone_flag::NEWNET));
|
errors::ext::check("namespace: unshare net", rt::ext::unshare(rt::ext::clone_flag::NEWNET));
|
||||||
@@ -59,11 +64,12 @@ fn setup_pasta(net: restrict::net_subset) void = {
|
|||||||
// let the parent know we're ready for pasta to attach to us
|
// let the parent know we're ready for pasta to attach to us
|
||||||
io::write(pipe_child_wr, [1])!;
|
io::write(pipe_child_wr, [1])!;
|
||||||
|
|
||||||
// TODO: race condition here, where the child immediately continues on even
|
// wait for the parent to attach pasta
|
||||||
// though pasta hasn't created the device.
|
io::readall(pipe_child_rd, &[0u8])!;
|
||||||
|
|
||||||
// cleanup: we're done with the pipe
|
// cleanup: we're done with the pipes
|
||||||
io::close(pipe_child_wr)!;
|
io::close(pipe_child_wr)!;
|
||||||
|
io::close(pipe_child_rd)!;
|
||||||
|
|
||||||
case let e: (os::exec::error | rt::errno) =>
|
case let e: (os::exec::error | rt::errno) =>
|
||||||
errors::ext::check("setup_pasta: fork", e);
|
errors::ext::check("setup_pasta: fork", e);
|
||||||
@@ -72,9 +78,39 @@ fn setup_pasta(net: restrict::net_subset) void = {
|
|||||||
|
|
||||||
// spawn pasta as a separate process, and have it attach to the netns of the given pid.
|
// spawn pasta as a separate process, and have it attach to the netns of the given pid.
|
||||||
fn attach_pasta(net: restrict::net_subset, child: os::exec::process) (void | os::exec::error | rt::errno) = {
|
fn attach_pasta(net: restrict::net_subset, child: os::exec::process) (void | os::exec::error | rt::errno) = {
|
||||||
|
// pipe is used by child pasta process to notify parent process once ready
|
||||||
|
let (pipe_parent_rd, pipe_child_wr) = unix::pipe(unix::pipe_flag::NOCLOEXEC)!;
|
||||||
return match (fork_and_die_with_parent()?) {
|
return match (fork_and_die_with_parent()?) {
|
||||||
case let pasta_pid: os::exec::process => yield void;
|
case let pasta_pid: os::exec::process =>
|
||||||
|
// close the side of the pipe that's not ours:
|
||||||
|
io::close(pipe_child_wr)!;
|
||||||
|
|
||||||
|
// wait for pasta to signal readiness.
|
||||||
|
// it does this by writing its pid (followed by newline) to a file.
|
||||||
|
let pasta_pid: [32]u8 = [0...];
|
||||||
|
io::read(pipe_parent_rd, &pasta_pid)!;
|
||||||
|
|
||||||
|
log::printfln(
|
||||||
|
"[namespace/pasta] pasta signalled readiness as pid {}: continuing",
|
||||||
|
strings::fromutf8_unsafe(pasta_pid),
|
||||||
|
);
|
||||||
|
|
||||||
|
// cleanup: we're done with the pipe
|
||||||
|
io::close(pipe_parent_rd)!;
|
||||||
|
|
||||||
|
yield void;
|
||||||
case void =>
|
case void =>
|
||||||
|
// close the side of the pipe that's not ours:
|
||||||
|
io::close(pipe_parent_rd)!;
|
||||||
|
|
||||||
|
// move the notification pipe to be one of the special fd's which *don't*
|
||||||
|
// get closed on exec (e.g. /dev/fd/0, stdin).
|
||||||
|
// this *shouldn't* be necessary: the pipe was created NOCLOEXEC;
|
||||||
|
// but maybe pasta is doing some security measure like clearing all open fd's on exec.
|
||||||
|
rt::dup2(pipe_child_wr, 0)!;
|
||||||
|
io::close(pipe_child_wr)!;
|
||||||
|
let notify_fd_path = "/dev/fd/0";
|
||||||
|
|
||||||
// pasta needs permissions to create a device in the netns (it apparently
|
// pasta needs permissions to create a device in the netns (it apparently
|
||||||
// won't raise those caps itself). TODO: reduce these resources!
|
// won't raise those caps itself). TODO: reduce these resources!
|
||||||
let res = restrict::resources {
|
let res = restrict::resources {
|
||||||
@@ -85,13 +121,9 @@ fn attach_pasta(net: restrict::net_subset, child: os::exec::process) (void | os:
|
|||||||
let netns_path = fmt::asprintf("/proc/{}/ns/net", child: int);
|
let netns_path = fmt::asprintf("/proc/{}/ns/net", child: int);
|
||||||
defer free(netns_path);
|
defer free(netns_path);
|
||||||
|
|
||||||
// let notify_fd_path = fmt::asprintf("/proc/self/fd/{}", notify_fd);
|
|
||||||
// log::printfln("[namespace/pasta] notify {}", notify_fd_path);
|
|
||||||
// defer free(notify_fd_path);
|
|
||||||
|
|
||||||
// TODO: append dns argument to `pasta`.
|
|
||||||
let pasta_args = [
|
let pasta_args = [
|
||||||
"pasta",
|
"pasta",
|
||||||
|
"--quiet", //< don't print MAC/DHCP/DNS values (TODO: enable these for BUNPEN_DEBUG>=1)
|
||||||
"--ipv4-only",
|
"--ipv4-only",
|
||||||
// pasta `up`s `lo` regardless of this flag; `--config-net` just tells
|
// pasta `up`s `lo` regardless of this flag; `--config-net` just tells
|
||||||
// it to assign an IP and routes to the new device it creates
|
// it to assign an IP and routes to the new device it creates
|
||||||
@@ -108,7 +140,7 @@ fn attach_pasta(net: restrict::net_subset, child: os::exec::process) (void | os:
|
|||||||
"--netns-only",
|
"--netns-only",
|
||||||
// pidstr,
|
// pidstr,
|
||||||
"--netns", netns_path,
|
"--netns", netns_path,
|
||||||
// "--pid", notify_fd_path,
|
"--pid", notify_fd_path,
|
||||||
];
|
];
|
||||||
log::printfln("[namespace/pasta]: invoking pasta: {}", strings::join(" ", pasta_args...));
|
log::printfln("[namespace/pasta]: invoking pasta: {}", strings::join(" ", pasta_args...));
|
||||||
yield rt::ext::execvpe_or_default(
|
yield rt::ext::execvpe_or_default(
|
||||||
|
Reference in New Issue
Block a user