@ -49,12 +49,13 @@ impl AsRef<Path> for Process
}
}
#[ cfg(feature= " shutdown " ) ]
fn set_process_group ( to : i32 ) -> impl FnMut ( ) -> io ::Result < ( ) > + Send + Sync + ' static
{
use libc ::{
getpid ,
//getpid, // Not required: `setpgid(0, ...)` works on the current (*child* in this case) process.
setpgid ,
pid_t
//pid_t, // Not required: Result & arguments for `getpgid()`, deduced implicitly.
} ;
move | | {
//let this = unsafe { getpid() };
@ -69,11 +70,61 @@ fn set_process_group(to: i32) -> impl FnMut() -> io::Result<()> + Send + Sync +
}
}
/// Options passed to `contained_spawn()` to control how the process spawning intricicies are handled.
///
/// Usage: `contained_spawn(Some(&config.into()), ...)`
#[ derive(Debug, Clone, PartialEq, Eq, Copy) ]
pub struct SpawnOptions < ' cli > {
cli_flags : & ' cli crate ::arg ::Flags ,
}
impl < ' cli > From < & ' cli crate ::arg ::Config > for SpawnOptions < ' cli >
{
#[ inline ]
fn from ( from : & ' cli crate ::arg ::Config ) -> Self
{
Self ::with_flags ( & from . flags )
}
}
impl < ' cli > From < & ' cli crate ::arg ::Flags > for SpawnOptions < ' cli >
{
#[ inline ]
fn from ( from : & ' cli crate ::arg ::Flags ) -> Self
{
Self ::with_flags ( from )
}
}
impl < ' cli > SpawnOptions < ' cli >
{
/// Create a new `SpawnOptions` instance using the specified command-line flags.
#[ inline(always) ]
pub const fn with_flags ( cli_flags : & ' cli crate ::arg ::Flags ) -> Self
{
Self { cli_flags }
}
/// Create a new `SpawnOptions` instance using the optionally specified command-line flags.
#[ inline(always) ]
pub fn new ( config : Option < & ' cli crate ::arg ::Config > ) -> Option < Self >
{
config . map ( Into ::into )
}
/// Create a new `SpawnOptions` instance using the optionally specified command-line flags.
#[ inline(always) ]
pub fn try_with_flags ( config : Option < & ' cli crate ::arg ::Flags > ) -> Option < Self >
{
config . map ( Self ::with_flags )
}
}
/// Spawn the process, and contain its standard output.
///
/// # Notes
/// Standard error is printed immediately instead.
pub async fn contained_spawn < T , U , V > ( process : T , args : U , mut output_to : mpsc ::Sender < ( bool , String ) > ) -> Result < ( ) , Error >
pub async fn contained_spawn < T , U , V > ( _cli: Option < SpawnOptions < ' _ > > , process: T , args : U , mut output_to : mpsc ::Sender < ( bool , String ) > ) -> Result < ( ) , Error >
where U : IntoIterator < Item = V > ,
V : AsRef < std ::ffi ::OsStr > ,
T : AsRef < Process >
@ -89,6 +140,47 @@ where U: IntoIterator<Item=V>,
let stderr = std ::process ::Stdio ::inherit ( ) ;
}
} ;
/// Retrieve a command-line flag if command-line flags have been provided.
///
/// # Usage
/// Retrieve value-or-default (**eager**): `cli_flag!([ref] name_of_field ?? default_value)`.
/// If `ref` is provided, `default_value` must be able to coerce to *a reference of* the same type as `name_of_field`.
///
/// Retrieve value-or-default lazily: `cli_flag!([ref] name_of_field ?? do default_value_thunk)`.
/// If `ref` is provided, the thunk/function must return a reference pointing to the same type as `name_of_field` with a compatible lifetime.
///
/// Retrieve value inside an `Option`: `cli_flag!([ref] name_of_field ?)`.
/// If `ref` is provided, the returned Option will be `Option<&'_ T>` where `T` is the type of the field, otherwise, it will return `Option<T>`.
#[ allow(unused_macros) ] // NOTE: This macro might not be used if certain feature flags are not set.
macro_rules! cli_flag {
( $name :ident ? ? do $default :expr ) = > {
_cli . as_ref ( ) . map ( | opt | opt . cli_flags . $name ) . unwrap_or_else ( $default )
} ;
( $name :ident ? ? $default :expr ) = > {
_cli . as_ref ( ) . map ( | opt | opt . cli_flags . $name ) . unwrap_or ( From ::from ( $default ) )
} ;
( $name :ident ? ) = > {
_cli . as_ref ( ) . map ( | opt | opt . cli_flags . $name )
} ;
( $name :ident ) = > {
cli_flag ! ( $name ? ? Default ::default ( ) )
} ;
( ref $name :ident ? ? do $default :expr ) = > {
_cli . as_ref ( ) . map ( | opt | & opt . cli_flags . $name ) . unwrap_or_else ( $default )
} ;
( ref $name :ident ? ? $default :expr ) = > {
_cli . as_ref ( ) . map ( | opt | & opt . cli_flags . $name ) . unwrap_or ( $default )
} ;
( ref $name :ident ? ) = > {
_cli . as_ref ( ) . map ( | opt | & opt . cli_flags . $name )
} ;
( ref $name :ident ) = > {
cli_flag ! ( ref $name ? ? Default ::default ( ) )
} ;
}
let mut child = Command ::new ( process ) ;
let child = child
. args ( process_args . iter_cloned ( ) . map ( | x | x . into_owned ( ) ) ) //Do we need `into_owned()` here?
@ -98,10 +190,14 @@ where U: IntoIterator<Item=V>,
. stdin ( std ::process ::Stdio ::null ( ) )
. kill_on_drop ( false ) ;
// SAFETY: This is only calling `setpgid()`.
unsafe {
child
. pre_exec ( set_process_group ( 0 ) ) // NOTE: .process_group() does not exist in this version, so we hack it together outselves and call `setpgid()` directly
#[ cfg(feature= " shutdown " ) ]
if cli_flag ! ( graceful_shutdown ? ? true ) {
// SAFETY: This is only calling the `setpgid()` syscall.
unsafe {
child
. pre_exec ( set_process_group ( 0 ) ) // NOTE: .process_group() does not exist in this version, so we hack it together outselves and call `setpgid()` directly
} ;
} ;
let mut child = match child . spawn ( ) {