From 598194d79349f1363bc2f42262ec3931efc4a8b4 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 21 Jan 2022 14:45:47 +0100 Subject: [PATCH] Import sd listen Signed-off-by: Harald Hoyer --- Cargo.lock | 20 ++++-- Cargo.toml | 1 + crates/wasi-common/cap-std-sync/src/lib.rs | 15 ++++- crates/wasi-common/tokio/src/lib.rs | 19 ++++++ src/commands/run.rs | 72 +++++++++++++++++++++- 5 files changed, 121 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cbce8960687..77b4e9067f70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -281,9 +281,9 @@ dependencies = [ [[package]] name = "cap-primitives" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2737eb174c9482c865789a428bfbddfdf284317d466ebbc637eee2e1242bfaa" +checksum = "fa4885ffa2fcd32e04f2e2828537d760cba3aae7f3642e72195272b856ae7d71" dependencies = [ "ambient-authority", "errno", @@ -310,9 +310,9 @@ dependencies = [ [[package]] name = "cap-std" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9ba199282865f3e3c3e7afc6907a3a27896435e24209e035c8a24d4fbcf43a" +checksum = "9d3500cb800c41283d7ba1e541609c041378410494cfdc43232220d63d240e5d" dependencies = [ "cap-primitives", "io-extras", @@ -1563,6 +1563,17 @@ version = "0.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a261afc61b7a5e323933b402ca6a1765183687c614789b1e4db7762ed4230bca" +[[package]] +name = "listenfd" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e514e2cb8a9624701346ea3e694c1766d76778e343e537d873c1c366e79a7" +dependencies = [ + "libc", + "uuid", + "winapi", +] + [[package]] name = "lock_api" version = "0.4.4" @@ -3441,6 +3452,7 @@ dependencies = [ "humantime 2.1.0", "lazy_static", "libc", + "listenfd", "memchr", "more-asserts", "num_cpus", diff --git a/Cargo.toml b/Cargo.toml index 748cb801daa3..c31e395561c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ rayon = "1.5.0" humantime = "2.0.0" wasmparser = "0.81.0" lazy_static = "1.4.0" +listenfd = "0.3.5" [target.'cfg(unix)'.dependencies] rustix = "0.31.0" diff --git a/crates/wasi-common/cap-std-sync/src/lib.rs b/crates/wasi-common/cap-std-sync/src/lib.rs index 96ffebf5b922..dc63f305605a 100644 --- a/crates/wasi-common/cap-std-sync/src/lib.rs +++ b/crates/wasi-common/cap-std-sync/src/lib.rs @@ -43,12 +43,13 @@ pub mod tcpstream; pub use cap_std::ambient_authority; pub use cap_std::fs::Dir; +pub use cap_std::net::TcpListener; pub use clocks::clocks_ctx; pub use sched::sched_ctx; use cap_rand::RngCore; use std::path::Path; -use wasi_common::{table::Table, Error, WasiCtx, WasiFile}; +use wasi_common::{file::FileCaps, table::Table, Error, WasiCtx, WasiFile}; pub struct WasiCtxBuilder(WasiCtx); @@ -122,6 +123,18 @@ impl WasiCtxBuilder { self.0.push_preopened_dir(dir, guest_path)?; Ok(self) } + pub fn preopened_listener(mut self, fd: u32, listener: TcpListener) -> Result { + let file: Box = + Box::new(crate::tcplisten::TcpListener::from_cap_std(listener)); + + let caps = FileCaps::FDSTAT_SET_FLAGS + | FileCaps::FILESTAT_GET + | FileCaps::READ + | FileCaps::POLL_READWRITE; + + self.0.insert_file(fd, file, caps); + Ok(self) + } pub fn build(self) -> WasiCtx { self.0 } diff --git a/crates/wasi-common/tokio/src/lib.rs b/crates/wasi-common/tokio/src/lib.rs index e58d5e2cc062..19f0c191ab54 100644 --- a/crates/wasi-common/tokio/src/lib.rs +++ b/crates/wasi-common/tokio/src/lib.rs @@ -14,6 +14,9 @@ use wasi_common::{Error, Table, WasiCtx, WasiFile}; pub use dir::Dir; pub use file::File; +pub use tcplisten::TcpListener; +pub use tcpstream::TcpStream; +use wasi_common::file::FileCaps; use crate::sched::sched_ctx; @@ -93,6 +96,22 @@ impl WasiCtxBuilder { self.0.push_preopened_dir(dir, guest_path)?; Ok(self) } + pub fn preopened_listener( + mut self, + fd: u32, + listener: cap_std::net::TcpListener, + ) -> Result { + let file: Box = Box::new(TcpListener::from_cap_std(listener)); + + let caps = FileCaps::FDSTAT_SET_FLAGS + | FileCaps::FILESTAT_GET + | FileCaps::READ + | FileCaps::POLL_READWRITE; + + self.0.insert_file(fd, file, caps); + Ok(self) + } + pub fn build(self) -> WasiCtx { self.0 } diff --git a/src/commands/run.rs b/src/commands/run.rs index c7877d10d1e4..bac08f32dbe2 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -13,7 +13,7 @@ use std::{ }; use structopt::{clap::AppSettings, StructOpt}; use wasmtime::{Engine, Func, Linker, Module, Store, Trap, Val, ValType}; -use wasmtime_wasi::sync::{ambient_authority, Dir, WasiCtxBuilder}; +use wasmtime_wasi::sync::{ambient_authority, Dir, TcpListener, WasiCtxBuilder}; #[cfg(feature = "wasi-nn")] use wasmtime_wasi_nn::WasiNnCtx; @@ -91,6 +91,19 @@ pub struct RunCommand { #[structopt(long = "allow-precompiled")] allow_precompiled: bool, + /// Inherit environment variables and file descriptors following the + /// systemd listen fd specification (UNIX only) + #[structopt(long = "listenfd")] + listenfd: bool, + + /// Grant access to the given TCP listen socket + #[structopt( + long = "tcplisten", + number_of_values = 1, + value_name = "SOCKET ADDRESS" + )] + tcplisten: Vec, + /// Grant access to the given host directory #[structopt(long = "dir", number_of_values = 1, value_name = "DIRECTORY")] dirs: Vec, @@ -165,6 +178,8 @@ impl RunCommand { &argv, &self.vars, &self.common.wasi_modules.unwrap_or(WasiModules::default()), + self.listenfd, + &self.tcplisten, )?; // Load the preload wasm modules. @@ -415,6 +430,8 @@ fn populate_with_wasi( argv: &[String], vars: &[(String, String)], wasi_modules: &WasiModules, + listenfd: bool, + tcplisten: &Vec, ) -> Result<()> { if wasi_modules.wasi_common { wasmtime_wasi::add_to_linker(linker, |host| host.wasi.as_mut().unwrap())?; @@ -422,9 +439,30 @@ fn populate_with_wasi( let mut builder = WasiCtxBuilder::new(); builder = builder.inherit_stdio().args(argv)?.envs(vars)?; + let mut num_fd: usize = 3; + + if listenfd { + let (n, b) = ctx_set_listenfd(num_fd, builder)?; + num_fd = n; + builder = b; + } + + for address in tcplisten.iter() { + let stdlistener = std::net::TcpListener::bind(address) + .with_context(|| format!("failed to bind to address '{}'", address))?; + + let _ = stdlistener.set_nonblocking(true)?; + + let listener = TcpListener::from_std(stdlistener); + + builder = builder.preopened_listener(num_fd as _, listener)?; + num_fd += 1; + } + for (name, dir) in preopen_dirs.into_iter() { builder = builder.preopened_dir(dir, name)?; } + store.data_mut().wasi = Some(builder.build()); } @@ -454,3 +492,35 @@ fn populate_with_wasi( Ok(()) } + +#[cfg(not(unix))] +fn ctx_set_listenfd(num_fd: usize, builder: WasiCtxBuilder) -> Result<(usize, WasiCtxBuilder)> { + Ok((num_fd, builder)) +} + +#[cfg(unix)] +fn ctx_set_listenfd(num_fd: usize, builder: WasiCtxBuilder) -> Result<(usize, WasiCtxBuilder)> { + use listenfd::ListenFd; + + let mut builder = builder; + let mut num_fd = num_fd; + + for env in ["LISTEN_FDS", "LISTEN_FDNAMES"] { + if let Ok(val) = std::env::var(env) { + builder = builder.env(env, &val)?; + } + } + + let mut listenfd = ListenFd::from_env(); + + for i in 0..listenfd.len() { + if let Some(stdlistener) = listenfd.take_tcp_listener(i)? { + let _ = stdlistener.set_nonblocking(true)?; + let listener = TcpListener::from_std(stdlistener); + builder = builder.preopened_listener((3 + i) as _, listener)?; + num_fd = 3 + i; + } + } + + Ok((num_fd, builder)) +}