Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reset stdio in tests #299

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 48 additions & 20 deletions crates/containerd-shim-wasm/src/sandbox/stdio.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use std::fs::File;
use std::io::ErrorKind::NotFound;
use std::io::{Error, Result};
use std::path::Path;
use std::sync::Arc;

use crossbeam::atomic::AtomicCell;
use std::sync::{Arc, OnceLock};

use super::InstanceConfig;
use crate::sys::stdio::*;
Expand All @@ -16,6 +13,8 @@ pub struct Stdio {
pub stderr: Stderr,
}

static INITIAL_STDIO: OnceLock<Stdio> = OnceLock::new();

impl Stdio {
pub fn redirect(self) -> Result<()> {
self.stdin.redirect()?;
Expand All @@ -39,45 +38,74 @@ impl Stdio {
stderr: cfg.get_stderr().try_into()?,
})
}

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<AtomicCell<Option<File>>>);
pub struct StdioStream<const FD: StdioRawFd>(Arc<StdioOwnedFd>);

impl<const FD: StdioRawFd> StdioStream<FD> {
pub fn redirect(self) -> Result<()> {
if let Some(f) = self.0.take() {
let f = try_into_fd(f)?;
let _ = unsafe { libc::dup(FD) };
if unsafe { libc::dup2(f.as_raw_fd(), FD) } == -1 {
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(())
}

pub fn take(&self) -> Self {
Self(Arc::new(AtomicCell::new(self.0.take())))
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<P: AsRef<Path>, const FD: StdioRawFd> TryFrom<Option<P>> for StdioStream<FD> {
type Error = Error;
fn try_from(path: Option<P>) -> Result<Self> {
let file = path
let fd = 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 open_file(path) {
Err(err) if err.kind() == NotFound => Ok(None),
Ok(f) => Ok(Some(f)),
Err(err) => Err(err),
path => Some(StdioOwnedFd::try_from_path(path)),
})
.transpose()?
.flatten();
.transpose()
.or_else(|err| match err.kind() {
NotFound => Ok(None),
_ => Err(err),
})?
.unwrap_or_default();

Ok(Self(Arc::new(AtomicCell::new(file))))
Ok(Self(Arc::new(fd)))
}
}

Expand Down
50 changes: 43 additions & 7 deletions crates/containerd-shim-wasm/src/sys/unix/stdio.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,51 @@
use std::fs::{File, OpenOptions};
use std::fs::OpenOptions;
use std::io::Result;
use std::os::fd::OwnedFd;
pub use std::os::fd::{AsRawFd as StdioAsRawFd, RawFd as StdioRawFd};
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 fn try_into_fd(f: impl Into<OwnedFd>) -> Result<impl StdioAsRawFd> {
Ok(f.into())
pub type StdioRawFd = RawFd;

pub struct StdioOwnedFd(AtomicCell<StdioRawFd>);

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))
}
}

pub fn open_file<P: AsRef<Path>>(path: P) -> Result<File> {
OpenOptions::new().read(true).write(true).open(path)
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(OpenOptions::new().read(true).write(true).open(path)?)
}
}
71 changes: 43 additions & 28 deletions crates/containerd-shim-wasm/src/sys/windows/stdio.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::fs::{File, OpenOptions};
use std::fs::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::path::Path;

use libc::{c_int, close, intptr_t, open_osfhandle, O_APPEND};
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;
Expand All @@ -14,41 +15,55 @@ pub const STDIN_FILENO: StdioRawFd = 0;
pub const STDOUT_FILENO: StdioRawFd = 1;
pub const STDERR_FILENO: StdioRawFd = 2;

struct StdioOwnedFd(c_int);
pub struct StdioOwnedFd(AtomicCell<StdioRawFd>);

pub fn try_into_fd(f: impl Into<OwnedHandle>) -> Result<impl StdioAsRawFd> {
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"));
impl Drop for StdioOwnedFd {
fn drop(&mut self) {
let fd = self.0.swap(-1);
if fd >= 0 {
unsafe { libc::close(fd) };
}
}
let _ = handle.into_raw_handle(); // drop ownership of the handle, it's managed by fd now
Ok(StdioOwnedFd(fd))
}

pub fn open_file<P: AsRef<Path>>(path: P) -> 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();
options.read(true).write(true);
if path.as_ref().starts_with("\\\\.\\pipe\\") {
options.custom_flags(FILE_FLAG_OVERLAPPED);
impl Default for StdioOwnedFd {
fn default() -> Self {
Self(AtomicCell::new(-1))
}

options.open(path)
}

pub trait StdioAsRawFd {
fn as_raw_fd(&self) -> c_int;
}
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) })
}

impl StdioAsRawFd for StdioOwnedFd {
fn as_raw_fd(&self) -> c_int {
self.0
pub unsafe fn from_raw_fd(fd: StdioRawFd) -> Self {
Self(AtomicCell::new(fd))
}
}

impl Drop for StdioOwnedFd {
fn drop(&mut self) {
unsafe { close(self.0) };
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> {
// 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();
options.read(true).write(true);
if path.as_ref().starts_with(r"\\.\pipe\") {
options.custom_flags(FILE_FLAG_OVERLAPPED);
}
Self::try_from(options.open(path)?)
}
}
6 changes: 3 additions & 3 deletions crates/containerd-shim-wasmedge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ anyhow = { workspace = true }
oci-spec = { workspace = true, features = ["runtime"] }
thiserror = { workspace = true }
serde_json = { workspace = true }
libc = { workspace = true }

[target.'cfg(unix)'.dependencies]
libcontainer = { workspace = true }
dbus = { version = "*", optional = true }
nix = { workspace = true }

[dev-dependencies]
tempfile = "3.7"
tempfile = "3.8"
serial_test = "*"
env_logger = "0.10"
libc = { workspace = true }

[features]
default = ["standalone", "static"]
Expand Down
Loading