diff --git a/crates/containerd-shim-wasm/src/sandbox/stdio.rs b/crates/containerd-shim-wasm/src/sandbox/stdio.rs index afa31220f..d0363cd70 100644 --- a/crates/containerd-shim-wasm/src/sandbox/stdio.rs +++ b/crates/containerd-shim-wasm/src/sandbox/stdio.rs @@ -1,146 +1,18 @@ -use std::io::ErrorKind::NotFound; -use std::io::{Error, Result}; -use std::path::Path; -use std::sync::{Arc, OnceLock}; - -use super::InstanceConfig; -use crate::sys::stdio::*; +use std::io::Result; #[derive(Default, Clone)] -pub struct Stdio { - pub stdin: Stdin, - pub stdout: Stdout, - pub stderr: Stderr, -} - -static INITIAL_STDIO: OnceLock = OnceLock::new(); +pub struct Stdio; impl Stdio { pub fn redirect(self) -> Result<()> { - self.stdin.redirect()?; - self.stdout.redirect()?; - self.stderr.redirect()?; Ok(()) } pub fn take(&self) -> Self { - Self { - stdin: self.stdin.take(), - stdout: self.stdout.take(), - stderr: self.stderr.take(), - } - } - - pub fn init_from_cfg(cfg: &InstanceConfig) -> Result { - Ok(Self { - stdin: StdioStream::try_from_path(cfg.get_stdin())?, - stdout: StdioStream::try_from_path(cfg.get_stdout())?, - stderr: StdioStream::try_from_path(cfg.get_stderr())?, - }) - } - - pub fn init_from_std() -> Self { - Self { - stdin: Stdin::try_from_std().unwrap_or_default(), - stdout: Stdout::try_from_std().unwrap_or_default(), - stderr: Stderr::try_from_std().unwrap_or_default(), - } - } - - pub fn guard(self) -> impl Drop { - StdioGuard(self) - } -} - -struct StdioGuard(Stdio); - -impl Drop for StdioGuard { - fn drop(&mut self) { - let _ = self.0.take().redirect(); - } -} - -#[derive(Clone, Default)] -pub struct StdioStream(Arc); - -impl StdioStream { - pub fn redirect(self) -> Result<()> { - if let Some(fd) = self.0.as_raw_fd() { - // Before any redirection we try to keep a copy of the original stdio - // to make sure the streams stay open - INITIAL_STDIO.get_or_init(Stdio::init_from_std); - - if unsafe { libc::dup2(fd, FD) } == -1 { - return Err(Error::last_os_error()); - } - } - Ok(()) + Self } - pub fn take(&self) -> Self { - Self(Arc::new(self.0.take())) - } - - pub fn try_from_std() -> Result { - let fd: i32 = unsafe { libc::dup(FD) }; - if fd == -1 { - return Err(Error::last_os_error()); - } - Ok(Self(Arc::new(unsafe { StdioOwnedFd::from_raw_fd(fd) }))) - } -} - -impl StdioStream { - fn try_from_path(path: impl AsRef) -> Result { - let path = path.as_ref(); - if path.as_os_str().is_empty() { - return Ok(Self(Arc::default())); - } - - let fd = match StdioOwnedFd::try_from_path(path) { - Err(err) if err.kind() == NotFound => Default::default(), - Err(err) => return Err(err), - Ok(fd) => fd, - }; - - Ok(Self(Arc::new(fd))) - } -} - -pub type Stdin = StdioStream; -pub type Stdout = StdioStream; -pub type Stderr = StdioStream; - -#[cfg(test)] -mod test { - use std::fs::File; - - use tempfile::tempdir; - - use super::*; - - /// containerd can send an empty path or a non-existent path - /// In both these cases we should just assume that the stdio stream was not setup (intentionally) - /// Any other error is a real error. - #[test] - fn test_maybe_open_stdio() -> anyhow::Result<()> { - // empty path - let s = Stdout::try_from_path("")?; - assert!(s.0.take().as_raw_fd().is_none()); - - // nonexistent path - let s = Stdout::try_from_path("/some/nonexistent/path")?; - assert!(s.0.take().as_raw_fd().is_none()); - - // valid path - let dir = tempdir()?; - let path = dir.path().join("testfile"); - let temp = File::create(&path)?; - drop(temp); - - // a valid path should not fail - let s = Stdout::try_from_path(path)?; - assert!(s.0.take().as_raw_fd().is_some()); - Ok(()) + pub fn init_from_cfg(_: T) -> Result { + Ok(Self) } } diff --git a/crates/containerd-shim-wasm/src/sys/unix/stdio.rs b/crates/containerd-shim-wasm/src/sys/unix/stdio.rs index 33b7a9945..5784035a0 100644 --- a/crates/containerd-shim-wasm/src/sys/unix/stdio.rs +++ b/crates/containerd-shim-wasm/src/sys/unix/stdio.rs @@ -1,55 +1,7 @@ use std::fs::{File, OpenOptions}; use std::io::Result; -use std::os::fd::{IntoRawFd, OwnedFd, RawFd}; use std::path::Path; -use crossbeam::atomic::AtomicCell; -pub use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; - -pub type StdioRawFd = RawFd; - -pub struct StdioOwnedFd(AtomicCell); - pub fn open(path: impl AsRef) -> Result { OpenOptions::new().read(true).write(true).open(path) } - -impl Drop for StdioOwnedFd { - fn drop(&mut self) { - let fd = self.0.swap(-1); - if fd >= 0 { - unsafe { libc::close(fd) }; - } - } -} - -impl Default for StdioOwnedFd { - fn default() -> Self { - Self(AtomicCell::new(-1)) - } -} - -impl StdioOwnedFd { - pub fn try_from(f: impl Into) -> Result { - let fd = f.into().into_raw_fd(); - Ok(unsafe { Self::from_raw_fd(fd) }) - } - - pub unsafe fn from_raw_fd(fd: StdioRawFd) -> Self { - Self(AtomicCell::new(fd)) - } - - pub fn as_raw_fd(&self) -> Option { - let fd = self.0.load(); - (fd >= 0).then_some(fd) - } - - pub fn take(&self) -> Self { - let fd = self.0.swap(-1); - unsafe { Self::from_raw_fd(fd) } - } - - pub fn try_from_path(path: impl AsRef) -> Result { - Self::try_from(open(path)?) - } -} diff --git a/crates/containerd-shim-wasm/src/sys/windows/stdio.rs b/crates/containerd-shim-wasm/src/sys/windows/stdio.rs index dbe5adad2..69e1e4ba4 100644 --- a/crates/containerd-shim-wasm/src/sys/windows/stdio.rs +++ b/crates/containerd-shim-wasm/src/sys/windows/stdio.rs @@ -1,22 +1,10 @@ use std::fs::{File, OpenOptions}; -use std::io::ErrorKind::Other; -use std::io::{Error, Result}; -use std::os::windows::fs::OpenOptionsExt; -use std::os::windows::prelude::{AsRawHandle, IntoRawHandle, OwnedHandle}; +use std::io::Result; +use std::os::windows::fs::OpenOptionsExt as _; use std::path::Path; -use crossbeam::atomic::AtomicCell; -use libc::{intptr_t, open_osfhandle, O_APPEND}; use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OVERLAPPED; -pub type StdioRawFd = libc::c_int; - -pub const STDIN_FILENO: StdioRawFd = 0; -pub const STDOUT_FILENO: StdioRawFd = 1; -pub const STDERR_FILENO: StdioRawFd = 2; - -pub struct StdioOwnedFd(AtomicCell); - pub fn open(path: impl AsRef) -> Result { // Containerd always passes a named pipe for stdin, stdout, and stderr so we can check if it is a pipe and open with overlapped IO let mut options = OpenOptions::new(); @@ -26,48 +14,3 @@ pub fn open(path: impl AsRef) -> Result { } options.open(path) } - -impl Drop for StdioOwnedFd { - fn drop(&mut self) { - let fd = self.0.swap(-1); - if fd >= 0 { - unsafe { libc::close(fd) }; - } - } -} - -impl Default for StdioOwnedFd { - fn default() -> Self { - Self(AtomicCell::new(-1)) - } -} - -impl StdioOwnedFd { - pub fn try_from(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(unsafe { Self::from_raw_fd(fd) }) - } - - pub unsafe fn from_raw_fd(fd: StdioRawFd) -> Self { - Self(AtomicCell::new(fd)) - } - - pub fn as_raw_fd(&self) -> Option { - let fd = self.0.load(); - (fd >= 0).then_some(fd) - } - - pub fn take(&self) -> Self { - let fd = self.0.swap(-1); - unsafe { Self::from_raw_fd(fd) } - } - - pub fn try_from_path(path: impl AsRef) -> Result { - Self::try_from(open(path)?) - } -}