split arg processing with some before void
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing

This commit is contained in:
Jake Hillion 2022-04-26 17:33:57 +01:00
parent ed6827b74e
commit 398c604850
3 changed files with 199 additions and 115 deletions

176
src/spawner/args.rs Normal file
View File

@ -0,0 +1,176 @@
use super::{Spawner, TriggerData};
use crate::specification::{Arg, FileSocket, Pipe};
use crate::{Error, Result};
use std::ffi::CString;
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<PreparedArg>);
impl PreparedArgs {
pub fn prepare_ambient(args: &[Arg]) -> Result<Self> {
let mut v = Vec::with_capacity(args.len());
for arg in args {
v.push(PreparedArg::prepare_ambient(arg)?);
}
Ok(PreparedArgs(v))
}
pub(super) fn prepare_void_mut(
self,
spawner: &mut Spawner,
entrypoint: &str,
trigger: &mut TriggerData,
) -> Result<Vec<CString>> {
let mut v = Vec::new();
for arg in self.0 {
v.extend(arg.prepare_void_mut(spawner, entrypoint, trigger)?)
}
Ok(v)
}
pub(super) fn prepare_void(
self,
spawner: &Spawner,
entrypoint: &str,
trigger: &mut TriggerData,
) -> Result<Vec<CString>> {
let mut v = Vec::new();
for arg in self.0 {
v.extend(arg.prepare_void(spawner, entrypoint, trigger)?)
}
Ok(v)
}
}
enum PreparedArg {
/// The binary name, or argv[0], of the original program start
BinaryName,
/// The name of this entrypoint
Entrypoint,
/// A file descriptor for a file on the filesystem in the launching namespace
File(File),
/// A chosen end of a named pipe
Pipe(Pipe),
/// File socket
FileSocket(FileSocket),
/// A value specified by the trigger
/// NOTE: Only valid if the trigger is of type Pipe(...) or FileSocket(...)
Trigger,
/// A TCP Listener
TcpListener { socket: TcpListener },
/// The rest of argv[1..], 0 or more arguments
Trailing,
}
impl PreparedArg {
/**
* Process the parts of the argument which must be processed
* with ambient authority
*
* 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<Self> {
Ok(match arg {
Arg::File(path) => PreparedArg::File(File::open(path)?),
Arg::TcpListener { addr } => PreparedArg::TcpListener {
socket: TcpListener::bind(addr)?,
},
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,
})
}
/**
* Complete argument preparation in the void
*/
fn prepare_void_mut(
self,
spawner: &mut Spawner,
entrypoint: &str,
trigger: &mut TriggerData,
) -> Result<Vec<CString>> {
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()])
}
},
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 => arg.prepare_void(spawner, entrypoint, trigger),
}
}
/**
* Complete argument preparation in the void
*/
fn prepare_void(
self,
spawner: &Spawner,
entrypoint: &str,
trigger: &mut TriggerData,
) -> Result<Vec<CString>> {
match self {
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::File(f) => Ok(vec![CString::new(f.into_raw_fd().to_string()).unwrap()]),
PreparedArg::Trigger => Ok(trigger.args()),
PreparedArg::TcpListener { socket } => {
Ok(vec![CString::new(socket.into_raw_fd().to_string()).unwrap()])
}
PreparedArg::Trailing => Ok(spawner
.trailing
.iter()
.map(|s| CString::new(*s).unwrap())
.collect()),
}
}
}

View File

@ -1,17 +1,18 @@
use log::{debug, error, info}; use log::{debug, error, info};
use super::specification::{ mod args;
Arg, Entrypoint, Environment, FileSocket, Pipe, Specification, Trigger,
}; use args::PreparedArgs;
use super::{PipePair, SocketPair};
use crate::specification::{Entrypoint, Environment, Specification, Trigger};
use crate::void::VoidBuilder; use crate::void::VoidBuilder;
use crate::{Error, Result}; use crate::{Error, Result};
use crate::{PipePair, SocketPair};
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::CString; use std::ffi::CString;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::net::TcpListener;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
use std::path::PathBuf; use std::path::PathBuf;
@ -60,16 +61,18 @@ impl<'a> Spawner<'a> {
match &entrypoint.trigger { match &entrypoint.trigger {
Trigger::Startup => { Trigger::Startup => {
let binary = PathBuf::from(self.binary).canonicalize()?;
let mut builder = VoidBuilder::new(); let mut builder = VoidBuilder::new();
let binary = PathBuf::from(self.binary).canonicalize()?;
builder.mount(binary, "/entrypoint"); builder.mount(binary, "/entrypoint");
self.prepare_env(&mut builder, &entrypoint.environment); self.prepare_env(&mut builder, &entrypoint.environment);
let args = PreparedArgs::prepare_ambient(&entrypoint.args)?;
let closure = || { let closure = || {
let args = self let args = args
.prepare_args(name, &entrypoint.args, &mut TriggerData::None) .prepare_void_mut(self, name, &mut TriggerData::None)
.unwrap(); .unwrap();
if let Err(e) = unistd::execv(&CString::new("/entrypoint").unwrap(), &args) if let Err(e) = unistd::execv(&CString::new("/entrypoint").unwrap(), &args)
@ -159,11 +162,14 @@ impl<'a> Spawner<'a> {
} }
} }
let args = PreparedArgs::prepare_ambient(&spec.args)?;
let closure = let closure =
|| { || {
let pipe_trigger = std::str::from_utf8(&buf[0..read_bytes]).unwrap(); let pipe_trigger = std::str::from_utf8(&buf[0..read_bytes]).unwrap();
let args = self
.prepare_args_ref(name, &spec.args, &mut TriggerData::Pipe(pipe_trigger)) let args = args
.prepare_void(self, name, &mut TriggerData::Pipe(pipe_trigger))
.unwrap(); .unwrap();
if let Err(e) = unistd::execv(&CString::new("/entrypoint").unwrap(), &args) if let Err(e) = unistd::execv(&CString::new("/entrypoint").unwrap(), &args)
@ -220,13 +226,11 @@ impl<'a> Spawner<'a> {
} }
} }
let args = PreparedArgs::prepare_ambient(&spec.args)?;
let closure = || { let closure = || {
let args = self let args = args
.prepare_args_ref( .prepare_void(self, name, &mut TriggerData::FileSocket(fds))
name,
&spec.args,
&mut TriggerData::FileSocket(fds),
)
.unwrap(); .unwrap();
if let Err(e) = if let Err(e) =
@ -268,100 +272,4 @@ impl<'a> Spawner<'a> {
} }
} }
} }
fn prepare_args(
&mut self,
entrypoint: &str,
args: &[Arg],
trigger: &mut TriggerData,
) -> Result<Vec<CString>> {
let mut out = Vec::new();
for arg in args {
out.extend(self.prepare_arg(entrypoint, arg, trigger)?);
}
Ok(out)
}
fn prepare_args_ref(
&self,
entrypoint: &str,
args: &[Arg],
trigger: &mut TriggerData,
) -> Result<Vec<CString>> {
let mut out = Vec::new();
for arg in args {
out.extend(self.prepare_arg_ref(entrypoint, arg, trigger)?);
}
Ok(out)
}
fn prepare_arg(
&mut self,
entrypoint: &str,
arg: &Arg,
trigger: &mut TriggerData,
) -> Result<Vec<CString>> {
match arg {
Arg::Pipe(p) => match p {
Pipe::Rx(s) => {
let pipe = self.pipes.get_mut(s).unwrap().take_read()?;
Ok(vec![CString::new(pipe.into_raw_fd().to_string()).unwrap()])
}
Pipe::Tx(s) => {
let pipe = self.pipes.get_mut(s).unwrap().take_write()?;
Ok(vec![CString::new(pipe.into_raw_fd().to_string()).unwrap()])
}
},
Arg::FileSocket(s) => match s {
FileSocket::Rx(s) => {
let pipe = self.sockets.get_mut(s).unwrap().take_read()?;
Ok(vec![CString::new(pipe.into_raw_fd().to_string()).unwrap()])
}
FileSocket::Tx(s) => {
let pipe = self.sockets.get_mut(s).unwrap().take_write()?;
Ok(vec![CString::new(pipe.into_raw_fd().to_string()).unwrap()])
}
},
a => self.prepare_arg_ref(entrypoint, a, trigger),
}
}
fn prepare_arg_ref(
&self,
entrypoint: &str,
arg: &Arg,
trigger: &mut TriggerData,
) -> Result<Vec<CString>> {
match arg {
Arg::BinaryName => Ok(vec![CString::new(self.binary).unwrap()]),
Arg::Entrypoint => Ok(vec![CString::new(entrypoint).unwrap()]),
Arg::Pipe(p) => Err(Error::BadPipe(p.get_name().to_string())),
Arg::FileSocket(s) => Err(Error::BadFileSocket(s.get_name().to_string())),
Arg::File(p) => {
let f = File::open(p)?.into_raw_fd();
Ok(vec![CString::new(f.to_string()).unwrap()])
}
Arg::Trigger => Ok(trigger.args()),
Arg::TcpListener { addr } => {
let listener = TcpListener::bind(addr)?;
let listener = listener.into_raw_fd();
Ok(vec![CString::new(listener.to_string()).unwrap()])
}
Arg::Trailing => Ok(self
.trailing
.iter()
.map(|s| CString::new(*s).unwrap())
.collect()),
}
}
} }

View File

@ -78,7 +78,7 @@ impl Arg {
} }
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub enum Pipe { pub enum Pipe {
Rx(String), Rx(String),
Tx(String), Tx(String),
@ -93,7 +93,7 @@ impl Pipe {
} }
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub enum FileSocket { pub enum FileSocket {
Rx(String), Rx(String),
Tx(String), Tx(String),