diff --git a/src/lib.rs b/src/lib.rs index 47d03f3..0440203 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ mod void; use error::{Error, Result}; use spawner::Spawner; -use specification::Specification; +use specification::{Environment, Specification}; use std::collections::HashMap; use std::fs::File; @@ -25,13 +25,16 @@ pub struct RunArgs<'a> { pub debug: bool, pub daemon: bool, + pub stdout: bool, + pub stderr: bool, + pub binary: &'a Path, pub binary_args: Vec<&'a str>, } pub fn run(args: &RunArgs) -> Result { // parse the specification - let spec: Specification = if let Some(m) = args.spec { + let mut spec: Specification = if let Some(m) = args.spec { if m.extension().map(|e| e == "json") == Some(true) { let f = std::fs::File::open(m)?; Ok(serde_json::from_reader(f)?) @@ -45,6 +48,20 @@ pub fn run(args: &RunArgs) -> Result { debug!("specification read: {:?}", &spec); spec.validate()?; + if args.stdout { + debug!("forwarding stdout"); + for entrypoint in &mut spec.entrypoints.values_mut() { + entrypoint.environment.insert(Environment::Stdout); + } + } + + if args.stderr { + debug!("forwarding stderr"); + for entrypoint in &mut spec.entrypoints.values_mut() { + entrypoint.environment.insert(Environment::Stderr); + } + } + // create all the pipes let (pipes, _) = spec.pipes(); let pipes = create_pipes(pipes)?; diff --git a/src/main.rs b/src/main.rs index 07f7404..5dbb0ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,6 +41,18 @@ fn main() { .help("Detach the shim from all child processes and exit immediately.") .takes_value(false), ) + .arg( + Arg::new("stdout") + .long("stdout") + .help("Allow all spawned processes access to stdout (useful for debugging).") + .takes_value(false), + ) + .arg( + Arg::new("stderr") + .long("stderr") + .help("Allow all spawned processes access to stderr (useful for debugging).") + .takes_value(false), + ) .arg( Arg::new("binary") .index(1) @@ -77,6 +89,10 @@ fn main() { spec: matches.value_of("spec").map(Path::new), debug: matches.is_present("debug"), daemon: matches.is_present("daemon"), + + stdout: matches.is_present("stdout"), + stderr: matches.is_present("stderr"), + binary, binary_args, }; diff --git a/src/spawner/mod.rs b/src/spawner/mod.rs index 26517a7..aa3688e 100644 --- a/src/spawner/mod.rs +++ b/src/spawner/mod.rs @@ -349,6 +349,16 @@ impl<'a> Spawner<'a> { Environment::Procfs => { builder.mount("/proc", "/proc").remount_proc(); } + + Environment::Stdin => { + builder.keep_fd(&0); + } + Environment::Stdout => { + builder.keep_fd(&1); + } + Environment::Stderr => { + builder.keep_fd(&2); + } } } } diff --git a/src/specification.rs b/src/specification.rs index 8006e46..4a0ab19 100644 --- a/src/specification.rs +++ b/src/specification.rs @@ -119,6 +119,10 @@ pub enum Environment { DomainName(String), Procfs, + + Stdin, + Stdout, + Stderr, } #[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug)] diff --git a/src/void.rs b/src/void.rs index 130148d..10da7e6 100644 --- a/src/void.rs +++ b/src/void.rs @@ -6,7 +6,7 @@ use crate::{Error, Result}; use std::collections::{HashMap, HashSet}; use std::env; use std::fmt; -use std::fs; +use std::fs::{self, File}; use std::io::Write; use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; use std::path::{Path, PathBuf}; @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; use nix::fcntl::{FcntlArg, FdFlag}; use nix::mount::{mount, umount2, MntFlags, MsFlags}; use nix::sys::signal::{signal, SigHandler, Signal}; -use nix::unistd::{close, getgid, getuid, pivot_root, sethostname, Gid, Pid, Uid}; +use nix::unistd::{close, dup2, getgid, getuid, pivot_root, sethostname, Gid, Pid, Uid}; use close_fds::CloseFdsBuilder; @@ -392,6 +392,26 @@ impl VoidBuilder { closer.closefrom(3); } + // overwrite stdin/stdout/stderr without closing + { + let mut nullfd: Option = None; + for stdfd in &[0, 1, 2] { + if !keep.contains(stdfd) { + let fd = nullfd + .take() + .map(Ok) + .unwrap_or_else(|| File::open("/dev/null"))?; + + dup2(fd.as_raw_fd(), *stdfd).map_err(|e| Error::Nix { + msg: "dup2", + src: e, + })?; + + nullfd = Some(fd); + } + } + } + for fd in keep.as_ref() { let mut flags = FdFlag::from_bits_truncate( nix::fcntl::fcntl(*fd, FcntlArg::F_GETFD).map_err(|e| Error::Nix {