diff --git a/src/spawner/args.rs b/src/spawner/args.rs index 655052c..b3f4527 100644 --- a/src/spawner/args.rs +++ b/src/spawner/args.rs @@ -1,5 +1,6 @@ use super::{Spawner, TriggerData}; use crate::specification::{Arg, FileSocket, Pipe}; +use crate::void::VoidBuilder; use crate::{Error, Result}; use std::ffi::CString; @@ -7,36 +8,42 @@ use std::fs::File; use std::net::TcpListener; use std::os::unix::io::IntoRawFd; -/** - * perform initial processing with ambient authority - * for things like network sockets. - */ pub struct PreparedArgs(Vec); impl PreparedArgs { - pub fn prepare_ambient(args: &[Arg]) -> Result { + /** + * perform initial processing with ambient authority + * for things like network sockets. update the builder + * with newly passed fds. the mutable call is allowed + * to use up things such as pipes and sockets. + */ + pub fn prepare_ambient_mut( + spawner: &mut Spawner, + builder: &mut VoidBuilder, + args: &[Arg], + ) -> Result { let mut v = Vec::with_capacity(args.len()); for arg in args { - v.push(PreparedArg::prepare_ambient(arg)?); + v.push(PreparedArg::prepare_ambient_mut(spawner, builder, arg)?); } Ok(PreparedArgs(v)) } - pub(super) fn prepare_void_mut( - self, - spawner: &mut Spawner, - entrypoint: &str, - trigger: &mut TriggerData, - ) -> Result> { - let mut v = Vec::new(); + /** + * perform initial processing with ambient authority + * for things like network sockets. update the builder + * with newly passed fds. + */ + pub fn prepare_ambient(builder: &mut VoidBuilder, args: &[Arg]) -> Result { + let mut v = Vec::with_capacity(args.len()); - for arg in self.0 { - v.extend(arg.prepare_void_mut(spawner, entrypoint, trigger)?) + for arg in args { + v.push(PreparedArg::prepare_ambient(builder, arg)?); } - Ok(v) + Ok(PreparedArgs(v)) } pub(super) fn prepare_void( @@ -65,10 +72,10 @@ enum PreparedArg { File(File), /// A chosen end of a named pipe - Pipe(Pipe), + Pipe(File), /// File socket - FileSocket(FileSocket), + FileSocket(File), /// A value specified by the trigger /// NOTE: Only valid if the trigger is of type Pipe(...) or FileSocket(...) @@ -89,57 +96,60 @@ impl PreparedArg { * Leave the remainder untouched so they can be processed in parallel * (in the child process) and to reduce authority */ - fn prepare_ambient(arg: &Arg) -> Result { + fn prepare_ambient_mut( + spawner: &mut Spawner, + builder: &mut VoidBuilder, + arg: &Arg, + ) -> Result { Ok(match arg { - Arg::File(path) => PreparedArg::File(File::open(path)?), + Arg::Pipe(p) => { + let pipe = match p { + Pipe::Rx(s) => spawner.pipes.get_mut(s).unwrap().take_read(), + Pipe::Tx(s) => spawner.pipes.get_mut(s).unwrap().take_write(), + }?; - Arg::TcpListener { addr } => PreparedArg::TcpListener { - socket: TcpListener::bind(addr)?, - }, + builder.keep_fd(&pipe); + PreparedArg::Pipe(pipe) + } - Arg::BinaryName => PreparedArg::BinaryName, - Arg::Entrypoint => PreparedArg::Entrypoint, - Arg::Pipe(p) => PreparedArg::Pipe(p.clone()), - Arg::FileSocket(s) => PreparedArg::FileSocket(s.clone()), - Arg::Trigger => PreparedArg::Trigger, - Arg::Trailing => PreparedArg::Trailing, + Arg::FileSocket(s) => { + let socket = match s { + FileSocket::Rx(s) => spawner.sockets.get_mut(s).unwrap().take_read(), + FileSocket::Tx(s) => spawner.sockets.get_mut(s).unwrap().take_write(), + }?; + + builder.keep_fd(&socket); + PreparedArg::FileSocket(socket) + } + + arg => Self::prepare_ambient(builder, arg)?, }) } - /** - * Complete argument preparation in the void - */ - fn prepare_void_mut( - self, - spawner: &mut Spawner, - entrypoint: &str, - trigger: &mut TriggerData, - ) -> Result> { - match self { - PreparedArg::Pipe(p) => match p { - Pipe::Rx(s) => { - let pipe = spawner.pipes.get_mut(&s).unwrap().take_read()?; - Ok(vec![CString::new(pipe.into_raw_fd().to_string()).unwrap()]) - } - Pipe::Tx(s) => { - let pipe = spawner.pipes.get_mut(&s).unwrap().take_write()?; - Ok(vec![CString::new(pipe.into_raw_fd().to_string()).unwrap()]) - } - }, + fn prepare_ambient(builder: &mut VoidBuilder, arg: &Arg) -> Result { + Ok(match arg { + Arg::Pipe(p) => return Err(Error::BadPipe(p.get_name().to_string())), + Arg::FileSocket(s) => return Err(Error::BadFileSocket(s.get_name().to_string())), - PreparedArg::FileSocket(s) => match s { - FileSocket::Rx(s) => { - let pipe = spawner.sockets.get_mut(&s).unwrap().take_read()?; - Ok(vec![CString::new(pipe.into_raw_fd().to_string()).unwrap()]) - } - FileSocket::Tx(s) => { - let pipe = spawner.sockets.get_mut(&s).unwrap().take_write()?; - Ok(vec![CString::new(pipe.into_raw_fd().to_string()).unwrap()]) - } - }, + Arg::File(path) => { + let fd = File::open(path)?; + builder.keep_fd(&fd); - arg => arg.prepare_void(spawner, entrypoint, trigger), - } + PreparedArg::File(fd) + } + + Arg::TcpListener { addr } => { + let socket = TcpListener::bind(addr)?; + builder.keep_fd(&socket); + + PreparedArg::TcpListener { socket } + } + + Arg::BinaryName => PreparedArg::BinaryName, + Arg::Entrypoint => PreparedArg::Entrypoint, + Arg::Trigger => PreparedArg::Trigger, + Arg::Trailing => PreparedArg::Trailing, + }) } /** @@ -155,8 +165,10 @@ impl PreparedArg { PreparedArg::BinaryName => Ok(vec![CString::new(spawner.binary).unwrap()]), PreparedArg::Entrypoint => Ok(vec![CString::new(entrypoint).unwrap()]), - PreparedArg::Pipe(p) => Err(Error::BadPipe(p.get_name().to_string())), - PreparedArg::FileSocket(s) => Err(Error::BadFileSocket(s.get_name().to_string())), + PreparedArg::Pipe(p) => Ok(vec![CString::new(p.into_raw_fd().to_string()).unwrap()]), + PreparedArg::FileSocket(s) => { + Ok(vec![CString::new(s.into_raw_fd().to_string()).unwrap()]) + } PreparedArg::File(f) => Ok(vec![CString::new(f.into_raw_fd().to_string()).unwrap()]), diff --git a/src/spawner/mod.rs b/src/spawner/mod.rs index 67e948d..5232266 100644 --- a/src/spawner/mod.rs +++ b/src/spawner/mod.rs @@ -68,11 +68,12 @@ impl<'a> Spawner<'a> { self.prepare_env(&mut builder, &entrypoint.environment); - let args = PreparedArgs::prepare_ambient(&entrypoint.args)?; + let args = + PreparedArgs::prepare_ambient_mut(self, &mut builder, &entrypoint.args)?; let closure = || { let args = args - .prepare_void_mut(self, name, &mut TriggerData::None) + .prepare_void(self, name, &mut TriggerData::None) .unwrap(); if let Err(e) = unistd::execv(&CString::new("/entrypoint").unwrap(), &args) @@ -162,7 +163,7 @@ impl<'a> Spawner<'a> { } } - let args = PreparedArgs::prepare_ambient(&spec.args)?; + let args = PreparedArgs::prepare_ambient(&mut builder, &spec.args)?; let closure = || { @@ -226,7 +227,7 @@ impl<'a> Spawner<'a> { } } - let args = PreparedArgs::prepare_ambient(&spec.args)?; + let args = PreparedArgs::prepare_ambient(&mut builder, &spec.args)?; let closure = || { let args = args diff --git a/src/void.rs b/src/void.rs index 1a67333..d2f2967 100644 --- a/src/void.rs +++ b/src/void.rs @@ -8,6 +8,7 @@ use std::fs; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::{Path, PathBuf}; +use nix::fcntl::{FcntlArg, FdFlag}; use nix::mount::{mount, umount2, MntFlags, MsFlags}; use nix::sched::unshare; use nix::sys::signal::{signal, SigHandler, Signal}; @@ -103,6 +104,22 @@ impl VoidBuilder { closer.closefrom(3); } + 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 { + msg: "fcntl", + src: e, + })?, + ); + + flags.remove(FdFlag::FD_CLOEXEC); + + nix::fcntl::fcntl(*fd, FcntlArg::F_SETFD(flags)).map_err(|e| Error::Nix { + msg: "fcntl", + src: e, + })?; + } + Ok(()) }