-
Notifications
You must be signed in to change notification settings - Fork 97
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Jorge Prendes <[email protected]>
- Loading branch information
Showing
3 changed files
with
7 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)?) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters