Implemented `-exec/{}` implementation functionality.

Added file sealing for making `-exec/{}` better (esp. for memfile mode.)

Fixed `errors` being shit.

Removed `memfile-preallocate` from `mode-memfile` feature: It is unneeded.

Fortune for collect's current commit: Middle blessing − 中吉
exec
Avril 2 years ago
parent 715fa4d5a8
commit 79721444ba
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -24,7 +24,7 @@ default = ["mode-memfile", "logging"]
# Mode: default # Mode: default
# Use physical-memory backed kernel file-descriptors. (see feature `memfile`.) # Use physical-memory backed kernel file-descriptors. (see feature `memfile`.)
mode-memfile = ["memfile-preallocate"] #, "tracing/release_max_level_warn"] mode-memfile = ["memfile"] #, "tracing/release_max_level_warn"]
# Mode: alternative # Mode: alternative
# Use non-physical memory allocated buffers. # Use non-physical memory allocated buffers.

@ -1,16 +1,19 @@
//! Errors and helpers for errors. //! Errors and helpers for errors.
//TODO: Comment how this works (controllably optional simple or complex `main()` error messages.)
use super::*; use super::*;
use std::{ use std::{
fmt, fmt,
error, error,
}; };
use std::os::unix::prelude::*;
pub const DEFAULT_USE_ENV: bool = std::option_env!("NO_RT_ERROR_CTL").is_none(); pub const DEFAULT_USE_ENV: bool = std::option_env!("NO_RT_ERROR_CTL").is_none();
pub type DispersedResult<T, const USE_ENV: bool = DEFAULT_USE_ENV> = Result<T, Dispersed<USE_ENV>>; pub type DispersedResult<T, const USE_ENV: bool = DEFAULT_USE_ENV> = Result<T, Dispersed<USE_ENV>>;
pub const ENV_NAME: &'static str = "RUST_VERBOSE"; pub const ENV_NAME: &'static str = "RUST_VERBOSE";
const DEFAULT_ENV_VERBOSE: DispersedVerbosity = match std::option_env!("DEFAULT_ERROR") { lazy_static!{
static ref DEFAULT_ENV_VERBOSE: DispersedVerbosity = match std::option_env!("DEFAULT_ERROR") {
Some("1") | Some("1") |
Some("V") | Some("V") |
Some("verbose") | Some("verbose") |
@ -18,7 +21,8 @@ const DEFAULT_ENV_VERBOSE: DispersedVerbosity = match std::option_env!("DEFAULT_
Some("v") => DispersedVerbosity::Verbose, Some("v") => DispersedVerbosity::Verbose,
Some("0") | Some("0") |
_ => DispersedVerbosity::static_default(), _ => DispersedVerbosity::static_default(),
}; };
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
#[repr(u8)] #[repr(u8)]
@ -43,7 +47,7 @@ impl Default for DispersedVerbosity
#[inline] #[inline]
fn default() -> Self fn default() -> Self
{ {
DEFAULT_ENV_VERBOSE *DEFAULT_ENV_VERBOSE
} }
} }
@ -68,13 +72,13 @@ fn get_env_value() -> DispersedVerbosity
match std::env::var_os(ENV_NAME) { match std::env::var_os(ENV_NAME) {
Some(mut value) => { Some(mut value) => {
value.make_ascii_lowercase(); value.make_ascii_lowercase();
match value { match value.as_bytes() {
"1" | b"1" |
"v" | b"v" |
"verbose" => DispersedVerbosity::Verbose, b"verbose" => DispersedVerbosity::Verbose,
"0" | b"0" |
"s" | b"s" |
"simple" => DispersedVerbosity::Simple, b"simple" => DispersedVerbosity::Simple,
_ => DispersedVerbosity::default(), _ => DispersedVerbosity::default(),
} }
}, },
@ -115,7 +119,7 @@ impl<const E: bool> Dispersed<E>
impl Dispersed<false> impl Dispersed<false>
{ {
#[inline(always)] #[inline(always)]
pub const fn obey_env(self) -> Dispersed<true> pub fn obey_env(self) -> Dispersed<true>
{ {
Dispersed(self.0) Dispersed(self.0)
} }
@ -124,16 +128,16 @@ impl Dispersed<false>
impl Dispersed<true> impl Dispersed<true>
{ {
#[inline(always)] #[inline(always)]
pub const fn ignore_env(self) -> Dispersed<false> pub fn ignore_env(self) -> Dispersed<false>
{ {
Dispersed(self.1) Dispersed(self.0)
} }
} }
impl<const E: bool> Dispersed<E> impl<const E: bool> Dispersed<E>
{ {
#[inline(always)] #[inline(always)]
pub const fn set_env<const To: bool>(self) -> Dispersed<To> pub fn set_env<const To: bool>(self) -> Dispersed<To>
{ {
Dispersed(self.0) Dispersed(self.0)
} }
@ -143,14 +147,14 @@ impl error::Error for Dispersed<true>
{ {
#[inline] #[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> { fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.0 self.0.source()
} }
} }
impl error::Error for Dispersed<false> impl error::Error for Dispersed<false>
{ {
#[inline] #[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> { fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.0 self.0.source()
} }
} }

@ -39,17 +39,17 @@ fn dup_file<F: ?Sized + AsRawFd>(file: &F) -> io::Result<memfile::RawFile>
Ok(memfile::RawFile::take_ownership_of_unchecked(fd)) Ok(memfile::RawFile::take_ownership_of_unchecked(fd))
} }
fn run_stdin<I>(file: impl Into<fs::File>, filename: impl AsRef<OsStr>, args: I) -> io::Result<process::Child> fn run_stdin<I>(file: Option<impl Into<fs::File>>, filename: impl AsRef<OsStr>, args: I) -> io::Result<process::Child>
where I: IntoIterator<Item = OsString>, where I: IntoIterator<Item = OsString>,
{ {
let file = { let file = {
let mut file: fs::File = file.into(); let mut file: Option<fs::File> = file.map(Into::into);
//TODO: Do we need to fcntl() this to make it (the fd) RW? //TODO: Do we need to fcntl() this to make it (the fd) RW?
file file
}; };
let child = process::Command::new(filename) let child = process::Command::new(filename)
.args(args) .args(args)
.stdin(process::Stdio::from(file)) .stdin(file.map(|file| process::Stdio::from(file)).unwrap_or_else(|| process::Stdio::inherit()))
.stdout(process::Stdio::inherit()) .stdout(process::Stdio::inherit())
.spawn()?; .spawn()?;
@ -71,10 +71,10 @@ pub fn run_single<F: ?Sized + AsRawFd>(file: &F, opt: args::ExecMode) -> io::Res
match opt { match opt {
args::ExecMode::Positional { command, args } => { args::ExecMode::Positional { command, args } => {
todo!("implement this with `proc_file(&file)`") run_stdin(None::<fs::File>, command, args.into_iter().map(move |x| x.unwrap_or_else(|| proc_file(&input).into())))
}, },
args::ExecMode::Stdin { command, args } => { args::ExecMode::Stdin { command, args } => {
run_stdin(input, command, args) run_stdin(Some(input), command, args)
} }
} }
} }
@ -83,13 +83,29 @@ pub fn run_single<F: ?Sized + AsRawFd>(file: &F, opt: args::ExecMode) -> io::Res
/// ///
/// # Returns /// # Returns
/// An iterator of each (possibly running) spawned child, or the error that occoured when trying to spawn that child from the `exec` option in `opt`. /// An iterator of each (possibly running) spawned child, or the error that occoured when trying to spawn that child from the `exec` option in `opt`.
pub fn spawn_from<F: ?Sized + AsRawFd>(file: &F, opt: Options) -> io::Result<impl IntoIterator<Item = io::Result<process::Child> + 'static>> pub fn spawn_from<'a, F: ?Sized + AsRawFd>(file: &'a F, opt: Options) -> impl IntoIterator<Item = io::Result<process::Child>> + 'a
{ {
todo!("Loop through `opt.into_exec()`, map the call to `|x| run_single(file, x)`, and return that iterator") opt.into_opt_exec().map(|x| run_single(file, x))
//todo!("Loop through `opt.into_exec()`, map the call to `|x| run_single(file, x)`, and return that iterator")
} }
/// Spawn all `-exec/{}` commands and wait for all children to complete.
///
/// # Returns
/// An iterator of the result of spawning each child and its exit status.
#[inline] #[inline]
pub fn spawn_from<F: ?Sized + AsRawFd>(file: &F, opt: Options) -> io::Result<impl IntoIterator<Item = io::Result<i32> + 'static>> pub fn spawn_from_sync<'a, F: ?Sized + AsRawFd>(file: &'a F, opt: Options) -> impl IntoIterator<Item = io::Result<i32>> + 'a
{ {
todo!("Map `spawn_from(...)` and wait for each child to terminate concurrently. Then return an iterator or the return codes or spawning errors for that now terminated child.") spawn_from(file, opt).into_iter().map(move |child| -> io::Result<_> {
match child {
Ok(mut child) => {
Ok(child.wait()?.code().unwrap_or(-1))
},
Err(err) => {
if_trace!(error!("Failed to spawn child: {err}"));
Err(err)
}
}
})
//todo!("Map `spawn_from(...)` and wait for each child to terminate concurrently. Then return an iterator or the return codes or spawning errors for that now terminated child.")
} }

@ -348,3 +348,72 @@ where F: FnOnce() -> T
} }
} }
} }
#[inline(always)]
pub(crate) fn map_bool<T>(ok: bool, value: T) -> T
where T: Default
{
if ok {
value
} else {
T::default()
}
}
pub trait SealExt
{
fn try_seal(&self, shrink: bool, grow: bool, write: bool) -> io::Result<()>;
#[inline]
fn sealed(self, shrink: bool, grow: bool, write: bool) -> Self
where Self: Sized {
if let Err(e) = self.try_seal(shrink, grow, write) {
panic!("Failed to apply seals: {}", io::Error::last_os_error())
}
self
}
}
#[cfg(any(feature="memfile", feature="exec"))]
const _: () = {
impl<T: AsRawFd + ?Sized> SealExt for T
{
#[cfg_attr(feature="logging", instrument(skip(self)))]
fn sealed(self, shrink: bool, grow: bool, write: bool) -> Self
where Self: Sized {
use libc::{
F_SEAL_GROW, F_SEAL_SHRINK, F_SEAL_WRITE,
F_ADD_SEALS,
fcntl
};
let fd = self.as_raw_fd();
if unsafe {
fcntl(fd, F_ADD_SEALS
, map_bool(shrink, F_SEAL_SHRINK)
| map_bool(grow, F_SEAL_GROW)
| map_bool(write, F_SEAL_WRITE))
} < 0 {
panic!("Failed to apply seals to file descriptor {fd}: {}", io::Error::last_os_error())
}
self
}
#[cfg_attr(feature="logging", instrument(skip(self), err))]
fn try_seal(&self, shrink: bool, grow: bool, write: bool) -> io::Result<()> {
use libc::{
F_SEAL_GROW, F_SEAL_SHRINK, F_SEAL_WRITE,
F_ADD_SEALS,
fcntl
};
let fd = self.as_raw_fd();
if unsafe {
fcntl(fd, F_ADD_SEALS
, map_bool(shrink, F_SEAL_SHRINK)
| map_bool(grow, F_SEAL_GROW)
| map_bool(write, F_SEAL_WRITE))
} < 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
}
};

@ -1,3 +1,4 @@
//#![feature(const_trait_impl)]
#[macro_use] extern crate cfg_if; #[macro_use] extern crate cfg_if;
#[cfg(feature="logging")] #[cfg(feature="logging")]
@ -170,6 +171,25 @@ fn feature_check() -> eyre::Result<()>
Ok(()) Ok(())
} }
#[inline]
fn try_seal_size<F: AsRawFd + ?Sized>(file: &F) -> eyre::Result<()>
{
//if cfg!(feature="exec") {
if let Err(err) = file.try_seal(true,true,false)
.with_section(|| format!("Raw file descriptor: {}", file.as_raw_fd()).header("Attempted seal was on"))
.with_warning(|| "This may cause consumers of -exec{} to misbehave") {
let fd = file.as_raw_fd();
if_trace!{{
warn!("Failed to seal file descriptor {fd}: {err}");
eprintln!("\t{err:?}");
}}
Err(err).wrap_err("Failed to seal file's length")
} else {
Ok(())
}
//}
}
mod work { mod work {
use super::*; use super::*;
#[cfg_attr(feature="logging", instrument(err))] #[cfg_attr(feature="logging", instrument(err))]
@ -467,6 +487,9 @@ mod work {
}; };
if_trace!(info!("collected {} from stdin. starting write.", read)); if_trace!(info!("collected {} from stdin. starting write.", read));
// Seal memfile
let _ = try_seal_size(&file);
// TODO: XXX: Currently causes crash. But if we can get this to work, leaving this in is definitely safe (as opposed to the pre-setting (see above.)) // TODO: XXX: Currently causes crash. But if we can get this to work, leaving this in is definitely safe (as opposed to the pre-setting (see above.))
set_stdout_len(read) set_stdout_len(read)
.wrap_err(eyre!("Failed to `ftruncate()` stdout after collection of {read} bytes")) .wrap_err(eyre!("Failed to `ftruncate()` stdout after collection of {read} bytes"))

Loading…
Cancel
Save