diff --git a/crates/containerd-shim-wasm/src/lib.rs b/crates/containerd-shim-wasm/src/lib.rs index 84b78b09f..f2bbf7aba 100644 --- a/crates/containerd-shim-wasm/src/lib.rs +++ b/crates/containerd-shim-wasm/src/lib.rs @@ -5,9 +5,9 @@ pub mod sandbox; pub mod services; -#[cfg_attr(unix, path = "sys/unix.rs")] -#[cfg_attr(windows, path = "sys/windows.rs")] -pub mod sys; +#[cfg_attr(unix, path = "sys/unix/mod.rs")] +#[cfg_attr(windows, path = "sys/windows/mod.rs")] +pub(crate) mod sys; #[cfg(all(feature = "libcontainer", not(target_os = "windows")))] pub mod libcontainer_instance; diff --git a/crates/containerd-shim-wasm/src/libcontainer_instance/container_executor.rs b/crates/containerd-shim-wasm/src/libcontainer_instance/container_executor.rs index 840416d8e..47afd5e7d 100644 --- a/crates/containerd-shim-wasm/src/libcontainer_instance/container_executor.rs +++ b/crates/containerd-shim-wasm/src/libcontainer_instance/container_executor.rs @@ -1,30 +1,23 @@ use std::fs::OpenOptions; use std::io::Read; -use std::os::fd::RawFd; use std::path::PathBuf; -use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use libcontainer::workload::default::DefaultExecutor; use libcontainer::workload::{Executor, ExecutorError}; -use nix::unistd::{dup, dup2}; use oci_spec::runtime::Spec; -use crate::sandbox::oci; +use crate::sandbox::{oci, Stdio}; #[derive(Default)] pub struct LinuxContainerExecutor { - stdin: Option, - stdout: Option, - stderr: Option, + stdio: Stdio, default_executor: DefaultExecutor, } impl LinuxContainerExecutor { - pub fn new(stdin: Option, stdout: Option, stderr: Option) -> Self { + pub fn new(stdio: Stdio) -> Self { Self { - stdin, - stdout, - stderr, + stdio, ..Default::default() } } @@ -32,7 +25,7 @@ impl LinuxContainerExecutor { impl Executor for LinuxContainerExecutor { fn exec(&self, spec: &Spec) -> Result<(), ExecutorError> { - redirect_io(self.stdin, self.stdout, self.stderr).map_err(|err| { + self.stdio.redirect().map_err(|err| { log::error!("failed to redirect io: {}", err); ExecutorError::Other(format!("failed to redirect io: {}", err)) })?; @@ -118,19 +111,3 @@ impl Executor for LinuxContainerExecutor { self.default_executor.name() } } - -fn redirect_io(stdin: Option, stdout: Option, stderr: Option) -> anyhow::Result<()> { - if let Some(stdin) = stdin { - dup(STDIN_FILENO)?; - dup2(stdin, STDIN_FILENO)?; - } - if let Some(stdout) = stdout { - dup(STDOUT_FILENO)?; - dup2(stdout, STDOUT_FILENO)?; - } - if let Some(stderr) = stderr { - dup(STDERR_FILENO)?; - dup2(stderr, STDERR_FILENO)?; - } - Ok(()) -} diff --git a/crates/containerd-shim-wasm/src/sandbox/manager.rs b/crates/containerd-shim-wasm/src/sandbox/manager.rs index 5f5097f9d..10fd5a423 100644 --- a/crates/containerd-shim-wasm/src/sandbox/manager.rs +++ b/crates/containerd-shim-wasm/src/sandbox/manager.rs @@ -23,7 +23,7 @@ use super::error::Error; use super::instance::Instance; use super::{oci, sandbox}; use crate::services::sandbox_ttrpc::{Manager, ManagerClient}; -use crate::sys::setup_namespaces; +use crate::sys::networking::setup_namespaces; /// Sandbox wraps an Instance and is used with the `Service` to manage multiple instances. pub trait Sandbox: Task + Send + Sync { diff --git a/crates/containerd-shim-wasm/src/sandbox/mod.rs b/crates/containerd-shim-wasm/src/sandbox/mod.rs index c5b79537c..fd3802c87 100644 --- a/crates/containerd-shim-wasm/src/sandbox/mod.rs +++ b/crates/containerd-shim-wasm/src/sandbox/mod.rs @@ -7,11 +7,13 @@ pub mod instance; pub mod instance_utils; pub mod manager; pub mod shim; +pub mod stdio; pub use error::{Error, Result}; pub use instance::{Instance, InstanceConfig}; pub use manager::{Sandbox as SandboxService, Service as ManagerService}; pub use shim::{Cli as ShimCli, Local}; +pub use stdio::Stdio; pub mod oci; diff --git a/crates/containerd-shim-wasm/src/sandbox/shim.rs b/crates/containerd-shim-wasm/src/sandbox/shim.rs index 5713098a6..9c29a992a 100644 --- a/crates/containerd-shim-wasm/src/sandbox/shim.rs +++ b/crates/containerd-shim-wasm/src/sandbox/shim.rs @@ -36,7 +36,8 @@ use ttrpc::context::Context; use super::instance::{Instance, InstanceConfig, Nop, Wait}; use super::{oci, Error, SandboxService}; -use crate::sys::{get_metrics, setup_namespaces}; +use crate::sys::metrics::get_metrics; +use crate::sys::networking::setup_namespaces; type InstanceDataStatus = (Mutex)>>, Condvar); diff --git a/crates/containerd-shim-wasm/src/sandbox/stdio.rs b/crates/containerd-shim-wasm/src/sandbox/stdio.rs new file mode 100644 index 000000000..50a005121 --- /dev/null +++ b/crates/containerd-shim-wasm/src/sandbox/stdio.rs @@ -0,0 +1,66 @@ +use std::fs::{File, OpenOptions}; +use std::io::ErrorKind::NotFound; +use std::io::{Error, Result}; +use std::path::Path; +use std::sync::{Arc, Mutex}; + +use crate::sys::stdio::*; + +#[derive(Default, Clone)] +pub struct Stdio { + pub stdin: Stdin, + pub stdout: Stdout, + pub stderr: Stderr, +} + +impl Stdio { + pub fn redirect(&self) -> Result<()> { + self.stdin.redirect()?; + self.stdout.redirect()?; + self.stderr.redirect()?; + Ok(()) + } +} + +macro_rules! stdio_impl { + ( $stdio_type:ident, $fd:expr ) => { + #[derive(Default, Clone)] + pub struct $stdio_type(Arc>>); + + impl> TryFrom> for $stdio_type { + type Error = std::io::Error; + fn try_from(path: Option

) -> Result { + path.and_then(|path| match path.as_ref() { + path if path.as_os_str().is_empty() => None, + path => Some(path.to_owned()), + }) + .map( + |path| match OpenOptions::new().read(true).write(true).open(path) { + Err(err) if err.kind() == NotFound => Ok(None), + Ok(f) => Ok(Some(f)), + Err(err) => Err(err), + }, + ) + .transpose() + .map(|opt| Self(Arc::new(Mutex::new(opt.flatten())))) + } + } + + impl $stdio_type { + pub fn redirect(&self) -> Result<()> { + if let Some(f) = self.0.try_lock().ok().and_then(|mut f| f.take()) { + let f = try_into_fd(f)?; + let _ = unsafe { libc::dup($fd) }; + if unsafe { libc::dup2(f.as_raw_fd(), $fd) } == -1 { + return Err(Error::last_os_error()); + } + } + Ok(()) + } + } + }; +} + +stdio_impl!(Stdin, STDIN_FILENO); +stdio_impl!(Stdout, STDOUT_FILENO); +stdio_impl!(Stderr, STDERR_FILENO); diff --git a/crates/containerd-shim-wasm/src/sys/unix.rs b/crates/containerd-shim-wasm/src/sys/unix/metrics.rs similarity index 76% rename from crates/containerd-shim-wasm/src/sys/unix.rs rename to crates/containerd-shim-wasm/src/sys/unix/metrics.rs index db3694a4a..8bc3aa718 100644 --- a/crates/containerd-shim-wasm/src/sys/unix.rs +++ b/crates/containerd-shim-wasm/src/sys/unix/metrics.rs @@ -1,15 +1,11 @@ -use std::fs::{self, File}; -use std::os::unix::io::AsRawFd; +use std::fs; use std::path::Path; use anyhow::Result; use cgroups_rs::cgroup::get_cgroups_relative_paths_by_pid; use cgroups_rs::hierarchies::{self}; use cgroups_rs::{Cgroup, Subsystem}; -use containerd_shim::error::Error as ShimError; use containerd_shim::{self as shim}; -use nix::sched::{setns, unshare, CloneFlags}; -use oci_spec::runtime; use protobuf::well_known_types::any::Any; use shim::protos::cgroups::metrics::{ CPUStat, CPUUsage, MemoryEntry, MemoryStat, Metrics, PidsStat, Throttle, @@ -118,39 +114,3 @@ pub fn get_metrics(pid: u32) -> Result { let metrics = convert_to_any(Box::new(metrics)).map_err(|e| Error::Others(e.to_string()))?; Ok(metrics) } - -pub fn setup_namespaces(spec: &runtime::Spec) -> Result<()> { - let namespaces = spec - .linux() - .as_ref() - .unwrap() - .namespaces() - .as_ref() - .unwrap(); - for ns in namespaces { - if ns.typ() == runtime::LinuxNamespaceType::Network { - if let Some(p) = ns.path() { - let f = File::open(p).map_err(|err| { - ShimError::Other(format!( - "could not open network namespace {}: {}", - p.display(), - err - )) - })?; - setns(f.as_raw_fd(), CloneFlags::CLONE_NEWNET).map_err(|err| { - ShimError::Other(format!("could not set network namespace: {0}", err)) - })?; - } else { - unshare(CloneFlags::CLONE_NEWNET).map_err(|err| { - ShimError::Other(format!("could not unshare network namespace: {0}", err)) - })?; - } - } - } - - // Keep all mounts changes (such as for the rootfs) private to the shim - // This way mounts will automatically be cleaned up when the shim exits. - unshare(CloneFlags::CLONE_NEWNS) - .map_err(|err| shim::Error::Other(format!("failed to unshare mount namespace: {}", err)))?; - Ok(()) -} diff --git a/crates/containerd-shim-wasm/src/sys/unix/mod.rs b/crates/containerd-shim-wasm/src/sys/unix/mod.rs new file mode 100644 index 000000000..968fb40cd --- /dev/null +++ b/crates/containerd-shim-wasm/src/sys/unix/mod.rs @@ -0,0 +1,3 @@ +pub mod metrics; +pub mod networking; +pub mod stdio; diff --git a/crates/containerd-shim-wasm/src/sys/unix/networking.rs b/crates/containerd-shim-wasm/src/sys/unix/networking.rs new file mode 100644 index 000000000..5f1d15fcb --- /dev/null +++ b/crates/containerd-shim-wasm/src/sys/unix/networking.rs @@ -0,0 +1,44 @@ +use std::fs::File; +use std::os::unix::io::AsRawFd; + +use anyhow::Result; +use containerd_shim::error::Error as ShimError; +use containerd_shim::{self as shim}; +use nix::sched::{setns, unshare, CloneFlags}; +use oci_spec::runtime; + +pub fn setup_namespaces(spec: &runtime::Spec) -> Result<()> { + let namespaces = spec + .linux() + .as_ref() + .unwrap() + .namespaces() + .as_ref() + .unwrap(); + for ns in namespaces { + if ns.typ() == runtime::LinuxNamespaceType::Network { + if let Some(p) = ns.path() { + let f = File::open(p).map_err(|err| { + ShimError::Other(format!( + "could not open network namespace {}: {}", + p.display(), + err + )) + })?; + setns(f.as_raw_fd(), CloneFlags::CLONE_NEWNET).map_err(|err| { + ShimError::Other(format!("could not set network namespace: {0}", err)) + })?; + } else { + unshare(CloneFlags::CLONE_NEWNET).map_err(|err| { + ShimError::Other(format!("could not unshare network namespace: {0}", err)) + })?; + } + } + } + + // Keep all mounts changes (such as for the rootfs) private to the shim + // This way mounts will automatically be cleaned up when the shim exits. + unshare(CloneFlags::CLONE_NEWNS) + .map_err(|err| shim::Error::Other(format!("failed to unshare mount namespace: {}", err)))?; + Ok(()) +} diff --git a/crates/containerd-shim-wasm/src/sys/unix/stdio.rs b/crates/containerd-shim-wasm/src/sys/unix/stdio.rs new file mode 100644 index 000000000..16a652f10 --- /dev/null +++ b/crates/containerd-shim-wasm/src/sys/unix/stdio.rs @@ -0,0 +1,9 @@ +use std::io::Result; +pub use std::os::fd::AsRawFd as StdioAsRawFd; +use std::os::fd::OwnedFd; + +pub use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; + +pub fn try_into_fd(f: impl Into) -> Result { + Ok(f.into()) +} diff --git a/crates/containerd-shim-wasm/src/sys/windows.rs b/crates/containerd-shim-wasm/src/sys/windows/metrics.rs similarity index 83% rename from crates/containerd-shim-wasm/src/sys/windows.rs rename to crates/containerd-shim-wasm/src/sys/windows/metrics.rs index b0fede726..2a8161b8b 100644 --- a/crates/containerd-shim-wasm/src/sys/windows.rs +++ b/crates/containerd-shim-wasm/src/sys/windows/metrics.rs @@ -14,8 +14,3 @@ pub fn get_metrics(pid: u32) -> Result { let metrics = convert_to_any(Box::new(m)).map_err(|e| Error::Others(e.to_string()))?; Ok(metrics) } - -pub fn setup_namespaces(spec: &runtime::Spec) -> Result<()> { - // noop for now - Ok(()) -} diff --git a/crates/containerd-shim-wasm/src/sys/windows/mod.rs b/crates/containerd-shim-wasm/src/sys/windows/mod.rs new file mode 100644 index 000000000..968fb40cd --- /dev/null +++ b/crates/containerd-shim-wasm/src/sys/windows/mod.rs @@ -0,0 +1,3 @@ +pub mod metrics; +pub mod networking; +pub mod stdio; diff --git a/crates/containerd-shim-wasm/src/sys/windows/networking.rs b/crates/containerd-shim-wasm/src/sys/windows/networking.rs new file mode 100644 index 000000000..349f4fb55 --- /dev/null +++ b/crates/containerd-shim-wasm/src/sys/windows/networking.rs @@ -0,0 +1,4 @@ +pub fn setup_namespaces(spec: &oci_spec::runtime::Spec) -> anyhow::Result<()> { + // noop for now + Ok(()) +} diff --git a/crates/containerd-shim-wasm/src/sys/windows/stdio.rs b/crates/containerd-shim-wasm/src/sys/windows/stdio.rs new file mode 100644 index 000000000..a0c60f7bf --- /dev/null +++ b/crates/containerd-shim-wasm/src/sys/windows/stdio.rs @@ -0,0 +1,39 @@ +use std::io::ErrorKind::Other; +use std::io::{Error, Result}; +use std::os::windows::prelude::{AsRawHandle, IntoRawHandle, OwnedHandle}; + +use libc::{c_int, close, intptr_t, open_osfhandle, O_APPEND}; + +type StdioRawFd = libc::c_int; + +pub static STDIN_FILENO: StdioRawFd = 0; +pub static STDOUT_FILENO: StdioRawFd = 1; +pub static STDERR_FILENO: StdioRawFd = 2; + +struct StdioOwnedFd(c_int); + +pub fn try_into_fd(f: impl Into) -> Result { + let handle = f.into(); + let fd = unsafe { open_osfhandle(handle.as_raw_handle() as intptr_t, O_APPEND) }; + if fd == -1 { + return Err(Error::new(Other, "Failed to open file descriptor")); + } + let _ = handle.into_raw_handle(); // drop ownership of the handle, it's managed by fd now + Ok(StdioOwnedFd(fd)) +} + +pub trait StdioAsRawFd { + fn as_raw_fd(&self) -> c_int; +} + +impl StdioAsRawFd for StdioOwnedFd { + fn as_raw_fd(&self) -> c_int { + self.0 + } +} + +impl Drop for StdioOwnedFd { + fn drop(&mut self) { + unsafe { close(self.0) }; + } +} diff --git a/crates/containerd-shim-wasmedge/src/executor.rs b/crates/containerd-shim-wasmedge/src/executor.rs index 1f09d39cc..983bf12e4 100644 --- a/crates/containerd-shim-wasmedge/src/executor.rs +++ b/crates/containerd-shim-wasmedge/src/executor.rs @@ -1,12 +1,9 @@ -use std::os::unix::io::RawFd; use std::path::PathBuf; use anyhow::Result; -use containerd_shim_wasm::sandbox::oci; -use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; +use containerd_shim_wasm::sandbox::{oci, Stdio}; use libcontainer::workload::{Executor, ExecutorError}; use log::debug; -use nix::unistd::{dup, dup2}; use oci_spec::runtime::Spec; use wasmedge_sdk::config::{CommonConfigOptions, ConfigBuilder, HostRegistrationConfigOptions}; use wasmedge_sdk::{params, VmBuilder}; @@ -14,18 +11,12 @@ use wasmedge_sdk::{params, VmBuilder}; const EXECUTOR_NAME: &str = "wasmedge"; pub struct WasmEdgeExecutor { - stdin: Option, - stdout: Option, - stderr: Option, + stdio: Stdio, } impl WasmEdgeExecutor { - pub fn new(stdin: Option, stdout: Option, stderr: Option) -> Self { - Self { - stdin, - stdout, - stderr, - } + pub fn new(stdio: Stdio) -> Self { + Self { stdio } } } @@ -102,18 +93,9 @@ impl WasmEdgeExecutor { let vm = vm .register_module_from_file("main", module_name) .map_err(|err| ExecutorError::Execution(err))?; - if let Some(stdin) = self.stdin { - dup(STDIN_FILENO)?; - dup2(stdin, STDIN_FILENO)?; - } - if let Some(stdout) = self.stdout { - dup(STDOUT_FILENO)?; - dup2(stdout, STDOUT_FILENO)?; - } - if let Some(stderr) = self.stderr { - dup(STDERR_FILENO)?; - dup2(stderr, STDERR_FILENO)?; - } + + self.stdio.redirect()?; + Ok(vm) } } diff --git a/crates/containerd-shim-wasmedge/src/instance/instance_linux.rs b/crates/containerd-shim-wasmedge/src/instance/instance_linux.rs index 9be246f25..8778ad887 100644 --- a/crates/containerd-shim-wasmedge/src/instance/instance_linux.rs +++ b/crates/containerd-shim-wasmedge/src/instance/instance_linux.rs @@ -1,18 +1,18 @@ use std::fs; -use std::os::fd::IntoRawFd; -use std::path::PathBuf; + + + +use std::path::{PathBuf}; use std::sync::{Arc, Condvar, Mutex}; -use anyhow::Context; use containerd_shim_wasm::libcontainer_instance::{LibcontainerInstance, LinuxContainerExecutor}; use containerd_shim_wasm::sandbox::error::Error; use containerd_shim_wasm::sandbox::instance::ExitCode; -use containerd_shim_wasm::sandbox::instance_utils::{determine_rootdir, maybe_open_stdio}; -use containerd_shim_wasm::sandbox::InstanceConfig; +use containerd_shim_wasm::sandbox::instance_utils::determine_rootdir; +use containerd_shim_wasm::sandbox::{InstanceConfig, Stdio}; use libcontainer::container::builder::ContainerBuilder; use libcontainer::container::Container; use libcontainer::syscall::syscall::create_syscall; -use nix::unistd::close; use crate::executor::WasmEdgeExecutor; @@ -23,9 +23,7 @@ pub struct Wasi { exit_code: ExitCode, - stdin: String, - stdout: String, - stderr: String, + stdio: Stdio, bundle: String, rootdir: PathBuf, @@ -47,9 +45,11 @@ impl LibcontainerInstance for Wasi { ) .unwrap(), exit_code: Arc::new((Mutex::new(None), Condvar::new())), - stdin: cfg.get_stdin().unwrap_or_default(), - stdout: cfg.get_stdout().unwrap_or_default(), - stderr: cfg.get_stderr().unwrap_or_default(), + stdio: Stdio { + stdin: cfg.get_stdin().try_into().unwrap(), + stdout: cfg.get_stdout().try_into().unwrap(), + stderr: cfg.get_stderr().try_into().unwrap(), + }, bundle, } } @@ -69,20 +69,10 @@ impl LibcontainerInstance for Wasi { fn build_container(&self) -> std::result::Result { fs::create_dir_all(&self.rootdir)?; - let stdin = maybe_open_stdio(self.stdin.as_str()) - .context("could not open stdin")? - .map(|f| f.into_raw_fd()); - let stdout = maybe_open_stdio(self.stdout.as_str()) - .context("could not open stdout")? - .map(|f| f.into_raw_fd()); - let stderr = maybe_open_stdio(self.stderr.as_str()) - .context("could not open stderr")? - .map(|f| f.into_raw_fd()); - let syscall = create_syscall(); let err_others = |err| Error::Others(format!("failed to create container: {}", err)); - let default_executor = Box::new(LinuxContainerExecutor::new(stdin, stdout, stderr)); - let wasmedge_executor = Box::new(WasmEdgeExecutor::new(stdin, stdout, stderr)); + let default_executor = Box::new(LinuxContainerExecutor::new(self.stdio.clone())); + let wasmedge_executor = Box::new(WasmEdgeExecutor::new(self.stdio.clone())); let container = ContainerBuilder::new(self.id.clone(), syscall.as_ref()) .with_executor(vec![default_executor, wasmedge_executor]) @@ -93,11 +83,6 @@ impl LibcontainerInstance for Wasi { .with_systemd(false) .build() .map_err(err_others)?; - // Close the fds now that they have been passed to the container process - // so that we don't leak them. - stdin.map(close); - stdout.map(close); - stderr.map(close); Ok(container) } diff --git a/crates/containerd-shim-wasmtime/src/executor.rs b/crates/containerd-shim-wasmtime/src/executor.rs index 6185fd835..f5652a9e6 100644 --- a/crates/containerd-shim-wasmtime/src/executor.rs +++ b/crates/containerd-shim-wasmtime/src/executor.rs @@ -1,12 +1,9 @@ use std::fs::OpenOptions; -use std::os::fd::RawFd; use std::path::PathBuf; use anyhow::{anyhow, Result}; -use containerd_shim_wasm::sandbox::oci; -use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; +use containerd_shim_wasm::sandbox::{oci, Stdio}; use libcontainer::workload::{Executor, ExecutorError}; -use nix::unistd::{dup, dup2}; use oci_spec::runtime::Spec; use wasmtime::{Engine, Linker, Module, Store}; use wasmtime_wasi::WasiCtxBuilder; @@ -16,25 +13,13 @@ use crate::oci_wasmtime::{self, wasi_dir}; const EXECUTOR_NAME: &str = "wasmtime"; pub struct WasmtimeExecutor { - stdin: Option, - stdout: Option, - stderr: Option, + stdio: Stdio, engine: Engine, } impl WasmtimeExecutor { - pub fn new( - stdin: Option, - stdout: Option, - stderr: Option, - engine: Engine, - ) -> Self { - Self { - stdin, - stdout, - stderr, - engine, - } + pub fn new(stdio: Stdio, engine: Engine) -> Self { + Self { stdio, engine } } } @@ -101,18 +86,7 @@ impl WasmtimeExecutor { .inherit_stdio() .preopened_dir(path, "/")?; - if let Some(stdin) = self.stdin { - dup(STDIN_FILENO)?; - dup2(stdin, STDIN_FILENO)?; - } - if let Some(stdout) = self.stdout { - dup(STDOUT_FILENO)?; - dup2(stdout, STDOUT_FILENO)?; - } - if let Some(stderr) = self.stderr { - dup(STDERR_FILENO)?; - dup2(stderr, STDERR_FILENO)?; - } + self.stdio.redirect()?; log::info!("building wasi context"); let wctx = wasi_builder.build(); diff --git a/crates/containerd-shim-wasmtime/src/instance/instance_linux.rs b/crates/containerd-shim-wasmtime/src/instance/instance_linux.rs index 0fdb20c6d..7bfb9cee2 100644 --- a/crates/containerd-shim-wasmtime/src/instance/instance_linux.rs +++ b/crates/containerd-shim-wasmtime/src/instance/instance_linux.rs @@ -1,17 +1,19 @@ -use std::os::fd::IntoRawFd; -use std::path::PathBuf; + + +use std::path::{PathBuf}; use std::sync::{Arc, Condvar, Mutex}; -use anyhow::Context; + use containerd_shim_wasm::libcontainer_instance::{LibcontainerInstance, LinuxContainerExecutor}; use containerd_shim_wasm::sandbox::error::Error; use containerd_shim_wasm::sandbox::instance::ExitCode; -use containerd_shim_wasm::sandbox::instance_utils::{determine_rootdir, maybe_open_stdio}; +use containerd_shim_wasm::sandbox::instance_utils::determine_rootdir; +use containerd_shim_wasm::sandbox::stdio::Stdio; use containerd_shim_wasm::sandbox::InstanceConfig; use libcontainer::container::builder::ContainerBuilder; use libcontainer::container::Container; use libcontainer::syscall::syscall::create_syscall; -use nix::unistd::close; + use crate::executor::WasmtimeExecutor; @@ -20,9 +22,7 @@ static DEFAULT_CONTAINER_ROOT_DIR: &str = "/run/containerd/wasmtime"; pub struct Wasi { exit_code: ExitCode, engine: wasmtime::Engine, - stdin: String, - stdout: String, - stderr: String, + stdio: Stdio, bundle: String, rootdir: PathBuf, id: String, @@ -47,9 +47,11 @@ impl LibcontainerInstance for Wasi { id, exit_code: Arc::new((Mutex::new(None), Condvar::new())), engine: cfg.get_engine(), - stdin: cfg.get_stdin().unwrap_or_default(), - stdout: cfg.get_stdout().unwrap_or_default(), - stderr: cfg.get_stderr().unwrap_or_default(), + stdio: Stdio { + stdin: cfg.get_stdin().try_into().unwrap(), + stdout: cfg.get_stdout().try_into().unwrap(), + stderr: cfg.get_stderr().try_into().unwrap(), + }, bundle, rootdir, } @@ -70,19 +72,11 @@ impl LibcontainerInstance for Wasi { fn build_container(&self) -> std::result::Result { let engine = self.engine.clone(); let syscall = create_syscall(); - let stdin = maybe_open_stdio(&self.stdin) - .context("could not open stdin")? - .map(|f| f.into_raw_fd()); - let stdout = maybe_open_stdio(&self.stdout) - .context("could not open stdout")? - .map(|f| f.into_raw_fd()); - let stderr = maybe_open_stdio(&self.stderr) - .context("could not open stderr")? - .map(|f| f.into_raw_fd()); + self.stdio.redirect()?; let err_others = |err| Error::Others(format!("failed to create container: {}", err)); - let wasmtime_executor = Box::new(WasmtimeExecutor::new(stdin, stdout, stderr, engine)); - let default_executor = Box::new(LinuxContainerExecutor::new(stdin, stdout, stderr)); + let wasmtime_executor = Box::new(WasmtimeExecutor::new(self.stdio.clone(), engine)); + let default_executor = Box::new(LinuxContainerExecutor::new(self.stdio.clone())); let container = ContainerBuilder::new(self.id.clone(), syscall.as_ref()) .with_executor(vec![default_executor, wasmtime_executor]) @@ -94,12 +88,6 @@ impl LibcontainerInstance for Wasi { .build() .map_err(err_others)?; - // Close the fds now that they have been passed to the container process - // so that we don't leak them. - stdin.map(close); - stdout.map(close); - stderr.map(close); - Ok(container) } }