Skip to content

Commit

Permalink
remove stdio functionalities
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Prendes <[email protected]>
  • Loading branch information
jprendes committed Jan 2, 2025
1 parent 3268568 commit 9a5be0c
Show file tree
Hide file tree
Showing 3 changed files with 7 additions and 240 deletions.
138 changes: 5 additions & 133 deletions crates/containerd-shim-wasm/src/sandbox/stdio.rs
Original file line number Diff line number Diff line change
@@ -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<Stdio> = 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<impl Send + Sync + Clone>) -> Result<Self> {
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<const FD: StdioRawFd>(Arc<StdioOwnedFd>);

impl<const FD: StdioRawFd> StdioStream<FD> {
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<Self> {
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<const FD: StdioRawFd> StdioStream<FD> {
fn try_from_path(path: impl AsRef<Path>) -> Result<Self> {
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<STDIN_FILENO>;
pub type Stdout = StdioStream<STDOUT_FILENO>;
pub type Stderr = StdioStream<STDERR_FILENO>;

#[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>(_: T) -> Result<Self> {
Ok(Self)
}
}
48 changes: 0 additions & 48 deletions crates/containerd-shim-wasm/src/sys/unix/stdio.rs
Original file line number Diff line number Diff line change
@@ -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<StdioRawFd>);

pub fn open(path: impl AsRef<Path>) -> Result<File> {
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<OwnedFd>) -> Result<Self> {
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<StdioRawFd> {
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<Path>) -> Result<Self> {
Self::try_from(open(path)?)
}
}
61 changes: 2 additions & 59 deletions crates/containerd-shim-wasm/src/sys/windows/stdio.rs
Original file line number Diff line number Diff line change
@@ -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<StdioRawFd>);

pub fn open(path: impl AsRef<Path>) -> Result<File> {
// 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();
Expand All @@ -26,48 +14,3 @@ pub fn open(path: impl AsRef<Path>) -> Result<File> {
}
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<OwnedHandle>) -> Result<Self> {
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<StdioRawFd> {
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<Path>) -> Result<Self> {
Self::try_from(open(path)?)
}
}

0 comments on commit 9a5be0c

Please sign in to comment.