Skip to content

Commit

Permalink
libcontainer: support set stdio for container
Browse files Browse the repository at this point in the history
currently the container can only inherit stdios from it's parent
process, the one who create container with libcontainer can not set
stdios to a different file.

Signed-off-by: Abel Feng <fshb1988@gmail.com>
  • Loading branch information
abel-von committed Oct 29, 2024
1 parent 6717cbc commit 7da767b
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 1 deletion.
112 changes: 112 additions & 0 deletions crates/libcontainer/src/container/builder.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::os::fd::OwnedFd;
use std::path::PathBuf;

use super::init_builder::InitContainerBuilder;
Expand All @@ -24,6 +25,12 @@ 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>,
// RawFd set to stdin of the container init process.
pub stdin: Option<OwnedFd>,
// RawFd set to stdout of the container init process.
pub stdout: Option<OwnedFd>,
// RawFd set to stderr of the container init process.
pub stderr: Option<OwnedFd>,
}

/// Builder that can be used to configure the common properties of
Expand Down Expand Up @@ -70,6 +77,9 @@ impl ContainerBuilder {
console_socket: None,
preserve_fds: 0,
executor: workload::default::get_executor(),
stdin: None,
stdout: None,
stderr: None,
}
}

Expand Down Expand Up @@ -257,13 +267,84 @@ impl ContainerBuilder {
self.executor = Box::new(executor);
self
}

/// Sets the stdin of the container, for those who use libcontainer as an library,
/// the container stdin may not have to be set to an opened file descriptor
/// rather than the stdin of the current process.
/// # Example
///
/// ```no_run
/// # use libcontainer::container::builder::ContainerBuilder;
/// # use libcontainer::syscall::syscall::SyscallType;
/// # use libcontainer::workload::default::DefaultExecutor;
/// # use nix::unistd::pipe;
///
/// let (r, _w) = pipe().unwrap();
/// ContainerBuilder::new(
/// "74f1a4cb3801".to_owned(),
/// SyscallType::default(),
/// )
/// .with_stdin(r);
/// ```
pub fn with_stdin(mut self, stdin: OwnedFd) -> Self {
self.stdin = Some(stdin);
self
}

/// Sets the stdout of the container, for those who use libcontainer as an library,
/// the container stdout may not have to be set to an opened file descriptor
/// rather than the stdout of the current process.
/// # Example
///
/// ```no_run
/// # use libcontainer::container::builder::ContainerBuilder;
/// # use libcontainer::syscall::syscall::SyscallType;
/// # use libcontainer::workload::default::DefaultExecutor;
/// # use nix::unistd::pipe;
///
/// let (_r, w) = pipe().unwrap();
/// ContainerBuilder::new(
/// "74f1a4cb3801".to_owned(),
/// SyscallType::default(),
/// )
/// .with_stdout(w);
/// ```
pub fn with_stdout(mut self, stdout: OwnedFd) -> Self {
self.stdout = Some(stdout);
self
}

/// Sets the stderr of the container, for those who use libcontainer as an library,
/// the container stderr may not have to be set to an opened file descriptor
/// rather than the stderr of the current process.
/// # Example
///
/// ```no_run
/// # use libcontainer::container::builder::ContainerBuilder;
/// # use libcontainer::syscall::syscall::SyscallType;
/// # use libcontainer::workload::default::DefaultExecutor;
/// # use nix::unistd::pipe;
///
/// let (_r, w) = pipe().unwrap();
/// ContainerBuilder::new(
/// "74f1a4cb3801".to_owned(),
/// SyscallType::default(),
/// )
/// .with_stderr(w);
/// ```
pub fn with_stderr(mut self, stderr: OwnedFd) -> Self {
self.stderr = Some(stderr);
self
}
}

#[cfg(test)]
mod tests {
use std::os::fd::AsRawFd;
use std::path::PathBuf;

use anyhow::{Context, Result};
use nix::unistd::pipe;

use crate::container::builder::ContainerBuilder;
use crate::syscall::syscall::SyscallType;
Expand Down Expand Up @@ -334,4 +415,35 @@ mod tests {
assert!(result.is_ok());
Ok(())
}

#[test]
fn test_stdios() -> Result<()> {
let (r, _w) = pipe()?;
let stdin_raw = r.as_raw_fd();
let builder =
ContainerBuilder::new("74f1a4cb3801".to_owned(), SyscallType::default()).with_stdin(r);
assert_eq!(
builder.stdin.as_ref().map(|o| o.as_raw_fd()),
Some(stdin_raw)
);

let (_r, w) = pipe()?;
let stdout_raw = w.as_raw_fd();
let builder =
ContainerBuilder::new("74f1a4cb3801".to_owned(), SyscallType::default()).with_stdout(w);
assert_eq!(
builder.stdout.as_ref().map(|o| o.as_raw_fd()),
Some(stdout_raw)
);

let (_r, w) = pipe()?;
let stderr_raw = w.as_raw_fd();
let builder =
ContainerBuilder::new("74f1a4cb3801".to_owned(), SyscallType::default()).with_stderr(w);
assert_eq!(
builder.stderr.as_ref().map(|o| o.as_raw_fd()),
Some(stderr_raw)
);
Ok(())
}
}
10 changes: 10 additions & 0 deletions crates/libcontainer/src/container/builder_impl.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::fs;
use std::io::Write;
use std::os::fd::{AsRawFd, OwnedFd};
use std::os::unix::prelude::RawFd;
use std::path::PathBuf;
use std::rc::Rc;
Expand Down Expand Up @@ -51,6 +52,12 @@ pub(super) struct ContainerBuilderImpl {
pub executor: Box<dyn Executor>,
/// If do not use pivot root to jail process inside rootfs
pub no_pivot: bool,
// RawFd set to stdin of the container init process.
pub stdin: Option<OwnedFd>,
// RawFd set to stdout of the container init process.
pub stdout: Option<OwnedFd>,
// RawFd set to stderr of the container init process.
pub stderr: Option<OwnedFd>,
}

impl ContainerBuilderImpl {
Expand Down Expand Up @@ -157,6 +164,9 @@ impl ContainerBuilderImpl {
detached: self.detached,
executor: self.executor.clone(),
no_pivot: self.no_pivot,
stdin: self.stdin.as_ref().map(|x| x.as_raw_fd()),
stdout: self.stdout.as_ref().map(|x| x.as_raw_fd()),
stderr: self.stderr.as_ref().map(|x| x.as_raw_fd()),
};

let (init_pid, need_to_clean_up_intel_rdt_dir) =
Expand Down
3 changes: 3 additions & 0 deletions crates/libcontainer/src/container/init_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ impl InitContainerBuilder {
detached: self.detached,
executor: self.base.executor,
no_pivot: self.no_pivot,
stdin: self.base.stdin,
stdout: self.base.stdout,
stderr: self.base.stderr,
};

builder_impl.create()?;
Expand Down
3 changes: 3 additions & 0 deletions crates/libcontainer/src/container/tenant_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ impl TenantContainerBuilder {
detached: self.detached,
executor: self.base.executor,
no_pivot: false,
stdin: self.base.stdin,
stdout: self.base.stdout,
stderr: self.base.stderr,
};

let pid = builder_impl.create()?;
Expand Down
6 changes: 6 additions & 0 deletions crates/libcontainer/src/process/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,10 @@ pub struct ContainerArgs {
pub executor: Box<dyn Executor>,
/// If do not use pivot root to jail process inside rootfs
pub no_pivot: bool,
// RawFd set to stdin of the container init process.
pub stdin: Option<RawFd>,
// RawFd set to stdout of the container init process.
pub stdout: Option<RawFd>,
// RawFd set to stderr of the container init process.
pub stderr: Option<RawFd>,
}
15 changes: 14 additions & 1 deletion crates/libcontainer/src/process/container_init_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use nc;
use nix::mount::{MntFlags, MsFlags};
use nix::sched::CloneFlags;
use nix::sys::stat::Mode;
use nix::unistd::{self, setsid, Gid, Uid};
use nix::unistd::{self, close, dup2, setsid, Gid, Uid};
use oci_spec::runtime::{
IOPriorityClass, LinuxIOPriority, LinuxNamespaceType, LinuxSchedulerFlag, LinuxSchedulerPolicy,
Scheduler, Spec, User,
Expand Down Expand Up @@ -374,6 +374,19 @@ pub fn container_init_process(
tracing::error!(?err, "failed to set up tty");
InitProcessError::Tty(err)
})?;
} else {
if let Some(stdin) = args.stdin {
dup2(stdin, 0).map_err(InitProcessError::NixOther)?;
close(stdin).map_err(InitProcessError::NixOther)?;
}
if let Some(stdout) = args.stdout {
dup2(stdout, 1).map_err(InitProcessError::NixOther)?;
close(stdout).map_err(InitProcessError::NixOther)?;
}
if let Some(stderr) = args.stderr {
dup2(stderr, 2).map_err(InitProcessError::NixOther)?;
close(stderr).map_err(InitProcessError::NixOther)?;
}
}

apply_rest_namespaces(&namespaces, spec, syscall.as_ref())?;
Expand Down

0 comments on commit 7da767b

Please sign in to comment.