From 7da767b213d65c42489e98587ffa8717bccce8b0 Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Fri, 25 Oct 2024 14:38:14 +0800 Subject: [PATCH] libcontainer: support set stdio for container 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 --- crates/libcontainer/src/container/builder.rs | 112 ++++++++++++++++++ .../src/container/builder_impl.rs | 10 ++ .../src/container/init_builder.rs | 3 + .../src/container/tenant_builder.rs | 3 + crates/libcontainer/src/process/args.rs | 6 + .../src/process/container_init_process.rs | 15 ++- 6 files changed, 148 insertions(+), 1 deletion(-) diff --git a/crates/libcontainer/src/container/builder.rs b/crates/libcontainer/src/container/builder.rs index 5aa1d39c5..23ddaafe9 100644 --- a/crates/libcontainer/src/container/builder.rs +++ b/crates/libcontainer/src/container/builder.rs @@ -1,3 +1,4 @@ +use std::os::fd::OwnedFd; use std::path::PathBuf; use super::init_builder::InitContainerBuilder; @@ -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, + // RawFd set to stdin of the container init process. + pub stdin: Option, + // RawFd set to stdout of the container init process. + pub stdout: Option, + // RawFd set to stderr of the container init process. + pub stderr: Option, } /// Builder that can be used to configure the common properties of @@ -70,6 +77,9 @@ impl ContainerBuilder { console_socket: None, preserve_fds: 0, executor: workload::default::get_executor(), + stdin: None, + stdout: None, + stderr: None, } } @@ -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; @@ -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(()) + } } diff --git a/crates/libcontainer/src/container/builder_impl.rs b/crates/libcontainer/src/container/builder_impl.rs index 0a9e43f52..32d5c6e86 100644 --- a/crates/libcontainer/src/container/builder_impl.rs +++ b/crates/libcontainer/src/container/builder_impl.rs @@ -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; @@ -51,6 +52,12 @@ pub(super) struct ContainerBuilderImpl { pub executor: Box, /// 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 set to stdout of the container init process. + pub stdout: Option, + // RawFd set to stderr of the container init process. + pub stderr: Option, } impl ContainerBuilderImpl { @@ -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) = diff --git a/crates/libcontainer/src/container/init_builder.rs b/crates/libcontainer/src/container/init_builder.rs index 4ac2104de..4ff2094ed 100644 --- a/crates/libcontainer/src/container/init_builder.rs +++ b/crates/libcontainer/src/container/init_builder.rs @@ -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()?; diff --git a/crates/libcontainer/src/container/tenant_builder.rs b/crates/libcontainer/src/container/tenant_builder.rs index e54a22cca..458a1f308 100644 --- a/crates/libcontainer/src/container/tenant_builder.rs +++ b/crates/libcontainer/src/container/tenant_builder.rs @@ -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()?; diff --git a/crates/libcontainer/src/process/args.rs b/crates/libcontainer/src/process/args.rs index 1c7d0c395..2ea0dc974 100644 --- a/crates/libcontainer/src/process/args.rs +++ b/crates/libcontainer/src/process/args.rs @@ -44,4 +44,10 @@ pub struct ContainerArgs { pub executor: Box, /// 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 set to stdout of the container init process. + pub stdout: Option, + // RawFd set to stderr of the container init process. + pub stderr: Option, } diff --git a/crates/libcontainer/src/process/container_init_process.rs b/crates/libcontainer/src/process/container_init_process.rs index 8d30b74b4..714b5b314 100644 --- a/crates/libcontainer/src/process/container_init_process.rs +++ b/crates/libcontainer/src/process/container_init_process.rs @@ -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, @@ -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())?;