Skip to content

Commit

Permalink
support setting stdin, stdout and stderr
Browse files Browse the repository at this point in the history
Signed-off-by: Jerome Gravel-Niquet <jeromegn@gmail.com>
  • Loading branch information
jeromegn committed Apr 8, 2024
1 parent 08d1db5 commit 51b6ac3
Show file tree
Hide file tree
Showing 15 changed files with 502 additions and 61 deletions.
24 changes: 24 additions & 0 deletions crates/libcontainer/src/container/builder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::error::{ErrInvalidID, LibcontainerError};
use crate::stdio::{Fd, Stdio};
use crate::syscall::syscall::SyscallType;
use crate::utils::PathBufExt;
use crate::workload::{self, Executor};
Expand All @@ -23,6 +24,8 @@ pub struct ContainerBuilder {
/// The function that actually runs on the container init process. Default
/// is to execute the specified command in the oci spec.
pub(super) executor: Box<dyn Executor>,
/// Stdio file descriptors to dup inside the container's namespace
pub(super) fds: [Fd; 3],
}

/// Builder that can be used to configure the common properties of
Expand Down Expand Up @@ -69,6 +72,8 @@ impl ContainerBuilder {
console_socket: None,
preserve_fds: 0,
executor: workload::default::get_executor(),
// by default, inherit stdio
fds: [Fd::Inherit, Fd::Inherit, Fd::Inherit],
}
}

Expand Down Expand Up @@ -237,6 +242,25 @@ impl ContainerBuilder {
self.preserve_fds = preserved_fds;
self
}

/// Sets STDIN within the container
pub fn with_stdin(mut self, stdio: Stdio) -> Self {
self.fds[0] = stdio.to_fd(false);
self
}

/// Sets STDOUT within the container
pub fn with_stdout(mut self, stdio: Stdio) -> Self {
self.fds[1] = stdio.to_fd(true);
self
}

/// Sets STDERR within the container
pub fn with_stderr(mut self, stdio: Stdio) -> Self {
self.fds[2] = stdio.to_fd(true);
self
}

/// Sets the number of additional file descriptors which will be passed into
/// the container process.
/// # Example
Expand Down
122 changes: 115 additions & 7 deletions crates/libcontainer/src/container/builder_impl.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
use super::{Container, ContainerStatus};
use super::{stdio::StdioFds, Container, ContainerStatus};
use crate::{
error::{LibcontainerError, MissingSpecError},
hooks,
notify_socket::NotifyListener,
pipe::{Pipe, PipeError, PipeHolder},
process::{
self,
args::{ContainerArgs, ContainerType},
intel_rdt::delete_resctrl_subdirectory,
},
stdio::{Closing, Fd},
syscall::syscall::SyscallType,
user_ns::UserNamespaceConfig,
utils,
workload::Executor,
};
use libcgroups::common::CgroupManager;
use nix::unistd::Pid;
use nix::{
fcntl::{fcntl, FcntlArg, OFlag},
sys::stat::Mode,
unistd::Pid,
};
use oci_spec::runtime::Spec;
use std::{fs, io::Write, os::unix::prelude::RawFd, path::PathBuf, rc::Rc};
use std::{
collections::HashMap,
fs,
io::Write,
os::{fd::AsRawFd, unix::prelude::RawFd},
path::{Path, PathBuf},
rc::Rc,
};

pub(super) struct ContainerBuilderImpl {
/// Flag indicating if an init or a tenant container should be created
Expand Down Expand Up @@ -48,12 +61,14 @@ pub(super) struct ContainerBuilderImpl {
pub detached: bool,
/// Default executes the specified execution of a generic command
pub executor: Box<dyn Executor>,
/// Stdio file descriptors to dup inside the container's namespace
pub fds: [Fd; 3],
}

impl ContainerBuilderImpl {
pub(super) fn create(&mut self) -> Result<Pid, LibcontainerError> {
pub(super) fn create(&mut self) -> Result<(Pid, StdioFds), LibcontainerError> {
match self.run_container() {
Ok(pid) => Ok(pid),
Ok(ret) => Ok(ret),
Err(outer) => {
// Only the init container should be cleaned up in the case of
// an error.
Expand All @@ -66,7 +81,7 @@ impl ContainerBuilderImpl {
}
}

fn run_container(&mut self) -> Result<Pid, LibcontainerError> {
fn run_container(&mut self) -> Result<(Pid, StdioFds), LibcontainerError> {
let linux = self.spec.linux().as_ref().ok_or(MissingSpecError::Linux)?;
let cgroups_path = utils::get_cgroup_path(
linux.cgroups_path(),
Expand Down Expand Up @@ -137,6 +152,12 @@ impl ContainerBuilderImpl {
})?;
}

// Prepare the stdio file descriptors for `dup`-ing inside the container
// namespace. Determines which ones needs closing on drop.
let mut stdio_descs = prepare_stdio_descriptors(&self.fds)?;
// Extract `StdioFds` from the prepared fds, for use by client
let stdio_fds = (&mut stdio_descs).into();

// This container_args will be passed to the container processes,
// therefore we will have to move all the variable by value. Since self
// is a shared reference, we have to clone these variables here.
Expand All @@ -153,6 +174,7 @@ impl ContainerBuilderImpl {
cgroup_config,
detached: self.detached,
executor: self.executor.clone(),
fds: stdio_descs.inner,
};

let (init_pid, need_to_clean_up_intel_rdt_dir) =
Expand Down Expand Up @@ -181,7 +203,7 @@ impl ContainerBuilderImpl {
.save()?;
}

Ok(init_pid)
Ok((init_pid, stdio_fds))
}

fn cleanup_container(&self) -> Result<(), LibcontainerError> {
Expand Down Expand Up @@ -231,3 +253,89 @@ impl ContainerBuilderImpl {
Ok(())
}
}

struct StdioDescriptors {
inner: HashMap<RawFd, RawFd>,
outer: HashMap<RawFd, PipeHolder>,
_guards: Vec<Closing>,
}

impl From<&mut StdioDescriptors> for StdioFds {
fn from(value: &mut StdioDescriptors) -> Self {
StdioFds {
stdin: value.outer.remove(&0).and_then(|x| match x {
PipeHolder::Writer(x) => Some(x),
_ => None,
}),
stdout: value.outer.remove(&1).and_then(|x| match x {
PipeHolder::Reader(x) => Some(x),
_ => None,
}),
stderr: value.outer.remove(&2).and_then(|x| match x {
PipeHolder::Reader(x) => Some(x),
_ => None,
}),
}
}
}

fn prepare_stdio_descriptors(fds: &[Fd; 3]) -> Result<StdioDescriptors, LibcontainerError> {
let mut inner = HashMap::new();
let mut outer = HashMap::new();
let mut guards = Vec::new();
for (idx, fdkind) in fds.iter().enumerate() {
let dest_fd = idx as i32;
let mut fd = match fdkind {
Fd::ReadPipe => {
let (rd, wr) = Pipe::new()?.split();
let fd = rd.into_fd();
guards.push(Closing::new(fd));
outer.insert(dest_fd, PipeHolder::Writer(wr));
fd
}
Fd::WritePipe => {
let (rd, wr) = Pipe::new()?.split();
let fd = wr.into_fd();
guards.push(Closing::new(fd));
outer.insert(dest_fd, PipeHolder::Reader(rd));
fd
}
Fd::ReadNull => {
// Need to keep fd with cloexec, until we are in child
let fd = nix::fcntl::open(
Path::new("/dev/null"),
OFlag::O_CLOEXEC | OFlag::O_RDONLY,
Mode::empty(),
)
.map_err(PipeError::Create)?;
guards.push(Closing::new(fd));
fd
}
Fd::WriteNull => {
// Need to keep fd with cloexec, until we are in child
let fd = nix::fcntl::open(
Path::new("/dev/null"),
OFlag::O_CLOEXEC | OFlag::O_WRONLY,
Mode::empty(),
)
.map_err(PipeError::Create)?;
guards.push(Closing::new(fd));
fd
}
Fd::Inherit => dest_fd,
Fd::Fd(ref x) => x.as_raw_fd(),
};
// The descriptor must not clobber the descriptors that are passed to
// a child
while fd != dest_fd {
fd = fcntl(fd, FcntlArg::F_DUPFD_CLOEXEC(3)).map_err(PipeError::Create)?;
guards.push(Closing::new(fd));
}
inner.insert(dest_fd, fd);
}
Ok(StdioDescriptors {
inner,
outer,
_guards: guards,
})
}
10 changes: 6 additions & 4 deletions crates/libcontainer/src/container/init_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use crate::{
};

use super::{
builder::ContainerBuilder, builder_impl::ContainerBuilderImpl, Container, ContainerStatus,
builder::ContainerBuilder, builder_impl::ContainerBuilderImpl, stdio::StdioFds, Container,
ContainerStatus,
};

// Builder that can be used to configure the properties of a new container
Expand Down Expand Up @@ -52,7 +53,7 @@ impl InitContainerBuilder {
}

/// Creates a new container
pub fn build(self) -> Result<Container, LibcontainerError> {
pub fn build(self) -> Result<(Container, StdioFds), LibcontainerError> {
let spec = self.load_spec()?;
let container_dir = self.create_container_dir()?;

Expand Down Expand Up @@ -109,13 +110,14 @@ impl InitContainerBuilder {
preserve_fds: self.base.preserve_fds,
detached: self.detached,
executor: self.base.executor,
fds: self.base.fds,
};

builder_impl.create()?;
let (_, stdio_fds) = builder_impl.create()?;

container.refresh_state()?;

Ok(container)
Ok((container, stdio_fds))
}

fn create_container_dir(&self) -> Result<PathBuf, LibcontainerError> {
Expand Down
1 change: 1 addition & 0 deletions crates/libcontainer/src/container/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod container_resume;
mod container_start;
pub mod init_builder;
pub mod state;
pub mod stdio;
pub mod tenant_builder;
pub use container::CheckpointOptions;
pub use container::Container;
Expand Down
7 changes: 7 additions & 0 deletions crates/libcontainer/src/container/stdio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use crate::pipe::{PipeReader, PipeWriter};

pub struct StdioFds {
pub stdin: Option<PipeWriter>,
pub stdout: Option<PipeReader>,
pub stderr: Option<PipeReader>,
}
4 changes: 3 additions & 1 deletion crates/libcontainer/src/container/tenant_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::process::args::ContainerType;
use crate::{capabilities::CapabilityExt, container::builder_impl::ContainerBuilderImpl};
use crate::{notify_socket::NotifySocket, tty, user_ns::UserNamespaceConfig, utils};

use super::stdio::StdioFds;
use super::{builder::ContainerBuilder, Container};

const NAMESPACE_TYPES: &[&str] = &["ipc", "uts", "net", "pid", "mnt", "cgroup"];
Expand Down Expand Up @@ -100,7 +101,7 @@ impl TenantContainerBuilder {
}

/// Joins an existing container
pub fn build(self) -> Result<Pid, LibcontainerError> {
pub fn build(self) -> Result<(Pid, StdioFds), LibcontainerError> {
let container_dir = self.lookup_container_dir()?;
let container = self.load_container_state(container_dir.clone())?;
let mut spec = self.load_init_spec(&container)?;
Expand Down Expand Up @@ -141,6 +142,7 @@ impl TenantContainerBuilder {
preserve_fds: self.base.preserve_fds,
detached: self.detached,
executor: self.base.executor,
fds: self.base.fds,
};

let pid = builder_impl.create()?;
Expand Down
2 changes: 2 additions & 0 deletions crates/libcontainer/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum LibcontainerError {
#[error(transparent)]
Tty(#[from] crate::tty::TTYError),
#[error(transparent)]
Pipe(#[from] crate::pipe::PipeError),
#[error(transparent)]
UserNamespace(#[from] crate::user_ns::UserNamespaceError),
#[error(transparent)]
NotifyListener(#[from] crate::notify_socket::NotifyListenerError),
Expand Down
2 changes: 2 additions & 0 deletions crates/libcontainer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ pub mod error;
pub mod hooks;
pub mod namespaces;
pub mod notify_socket;
pub mod pipe;
pub mod process;
pub mod rootfs;
#[cfg(feature = "libseccomp")]
pub mod seccomp;
pub mod signal;
pub mod stdio;
pub mod syscall;
pub mod test_utils;
pub mod tty;
Expand Down
Loading

0 comments on commit 51b6ac3

Please sign in to comment.