pub mod io;
mod pipe;
mod runc;
use std::{fmt::Debug, io::Result, os::fd::AsRawFd};
use async_trait::async_trait;
use log::debug;
pub use pipe::Pipe;
pub use runc::{DefaultExecutor, Spawner};
use tokio::io::{AsyncRead, AsyncWrite};
use crate::Command;
#[async_trait]
pub trait Io: Debug + Send + Sync {
fn stdin(&self) -> Option<Box<dyn AsyncWrite + Send + Sync + Unpin>> {
None
}
fn stdout(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
None
}
fn stderr(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
None
}
async fn set(&self, cmd: &mut Command) -> Result<()>;
async fn close_after_start(&self);
}
#[derive(Debug)]
pub struct PipedIo {
pub stdin: Option<Pipe>,
pub stdout: Option<Pipe>,
pub stderr: Option<Pipe>,
}
#[async_trait]
impl Io for PipedIo {
fn stdin(&self) -> Option<Box<dyn AsyncWrite + Send + Sync + Unpin>> {
self.stdin.as_ref().and_then(|pipe| {
let fd = pipe.wr.as_raw_fd();
tokio_pipe::PipeWrite::from_raw_fd_checked(fd)
.map(|x| Box::new(x) as Box<dyn AsyncWrite + Send + Sync + Unpin>)
.ok()
})
}
fn stdout(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
self.stdout.as_ref().and_then(|pipe| {
let fd = pipe.rd.as_raw_fd();
tokio_pipe::PipeRead::from_raw_fd_checked(fd)
.map(|x| Box::new(x) as Box<dyn AsyncRead + Send + Sync + Unpin>)
.ok()
})
}
fn stderr(&self) -> Option<Box<dyn AsyncRead + Send + Sync + Unpin>> {
self.stderr.as_ref().and_then(|pipe| {
let fd = pipe.rd.as_raw_fd();
tokio_pipe::PipeRead::from_raw_fd_checked(fd)
.map(|x| Box::new(x) as Box<dyn AsyncRead + Send + Sync + Unpin>)
.ok()
})
}
async fn set(&self, cmd: &mut Command) -> std::io::Result<()> {
if let Some(p) = self.stdin.as_ref() {
let pr = p.rd.try_clone()?;
cmd.stdin(pr);
}
if let Some(p) = self.stdout.as_ref() {
let pw = p.wr.try_clone()?;
cmd.stdout(pw);
}
if let Some(p) = self.stderr.as_ref() {
let pw = p.wr.try_clone()?;
cmd.stdout(pw);
}
Ok(())
}
async fn close_after_start(&self) {
if let Some(p) = self.stdout.as_ref() {
nix::unistd::close(p.wr.as_raw_fd()).unwrap_or_else(|e| debug!("close stdout: {}", e));
}
if let Some(p) = self.stderr.as_ref() {
nix::unistd::close(p.wr.as_raw_fd()).unwrap_or_else(|e| debug!("close stderr: {}", e));
}
}
}