-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
541 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#[doc(inline)] | ||
pub use core::{StdChildWrapper, StdCommandWrap, StdCommandWrapper}; | ||
#[cfg(all(windows, feature = "creation-flags"))] | ||
#[doc(inline)] | ||
pub use creation_flags::CreationFlags; | ||
#[cfg(all(windows, feature = "job-object"))] | ||
#[doc(inline)] | ||
pub use job_object::JobObject; | ||
#[cfg(feature = "kill-on-drop")] | ||
#[doc(inline)] | ||
pub use kill_on_drop::KillOnDrop; | ||
#[cfg(all(unix, feature = "process-group"))] | ||
#[doc(inline)] | ||
pub use process_group::{ProcessGroup, ProcessGroupChild}; | ||
#[cfg(all(unix, feature = "process-session"))] | ||
#[doc(inline)] | ||
pub use process_session::ProcessSession; | ||
|
||
mod core; | ||
#[cfg(all(windows, feature = "creation-flags"))] | ||
mod creation_flags; | ||
#[cfg(all(windows, feature = "job-object"))] | ||
mod job_object; | ||
#[cfg(feature = "kill-on-drop")] | ||
mod kill_on_drop; | ||
#[cfg(all(unix, feature = "process-group"))] | ||
mod process_group; | ||
#[cfg(all(unix, feature = "process-session"))] | ||
mod process_session; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
use std::{ | ||
io::Result, | ||
process::{Child, ChildStderr, ChildStdin, ChildStdout, Command, ExitStatus, Output}, | ||
}; | ||
|
||
#[cfg(unix)] | ||
use nix::{ | ||
sys::signal::{kill, Signal}, | ||
unistd::Pid, | ||
}; | ||
|
||
crate::generic_wrap::Wrap!(StdCommandWrap, Command, StdCommandWrapper, StdChildWrapper); | ||
|
||
pub trait StdCommandWrapper: std::fmt::Debug { | ||
// process-wrap guarantees that `other` will be of the same type as `self` | ||
// note that other crates that may use this trait should guarantee this, but | ||
// that cannot be enforced by the type system, so you should still panic if | ||
// downcasting fails, instead of potentially causing UB | ||
fn extend(&mut self, _other: Box<dyn StdCommandWrapper>) {} | ||
|
||
fn pre_spawn(&mut self, _command: &mut Command, _core: &StdCommandWrap) -> Result<()> { | ||
Ok(()) | ||
} | ||
|
||
fn post_spawn(&mut self, _child: &mut Child, _core: &StdCommandWrap) -> Result<()> { | ||
Ok(()) | ||
} | ||
|
||
fn wrap_child( | ||
&mut self, | ||
child: Box<dyn StdChildWrapper>, | ||
_core: &StdCommandWrap, | ||
) -> Result<Box<dyn StdChildWrapper>> { | ||
Ok(child) | ||
} | ||
} | ||
|
||
pub trait StdChildWrapper: std::fmt::Debug + Send + Sync { | ||
fn inner(&self) -> &Child; | ||
fn inner_mut(&mut self) -> &mut Child; | ||
fn into_inner(self: Box<Self>) -> Child; | ||
|
||
fn stdin(&mut self) -> &mut Option<ChildStdin> { | ||
&mut self.inner_mut().stdin | ||
} | ||
|
||
fn stdout(&mut self) -> &mut Option<ChildStdout> { | ||
&mut self.inner_mut().stdout | ||
} | ||
|
||
fn stderr(&mut self) -> &mut Option<ChildStderr> { | ||
&mut self.inner_mut().stderr | ||
} | ||
|
||
fn id(&self) -> u32 { | ||
self.inner().id() | ||
} | ||
|
||
fn kill(&mut self) -> Result<()> { | ||
self.start_kill()?; | ||
self.wait()?; | ||
Ok(()) | ||
} | ||
|
||
fn start_kill(&mut self) -> Result<()> { | ||
self.inner_mut().start_kill() | ||
} | ||
|
||
fn try_wait(&mut self) -> Result<Option<ExitStatus>> { | ||
self.inner_mut().try_wait() | ||
} | ||
|
||
fn wait(&mut self) -> Result<ExitStatus> { | ||
self.inner_mut().wait() | ||
} | ||
|
||
fn wait_with_output(self: Box<Self>) -> Result<Output> | ||
where | ||
Self: 'static, | ||
{ | ||
todo!() | ||
} | ||
|
||
#[cfg(unix)] | ||
fn signal(&self, sig: Signal) -> Result<()> { | ||
kill( | ||
Pid::from_raw(i32::try_from(self.id()).map_err(std::io::Error::other)?), | ||
sig, | ||
) | ||
.map_err(std::io::Error::from) | ||
} | ||
} | ||
|
||
impl StdChildWrapper for Child { | ||
fn inner(&self) -> &Child { | ||
self | ||
} | ||
fn inner_mut(&mut self) -> &mut Child { | ||
self | ||
} | ||
fn into_inner(self: Box<Self>) -> Child { | ||
*self | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use std::{io::Result, os::windows::process::CommandExt, process::Command}; | ||
|
||
use windows::Win32::System::Threading::PROCESS_CREATION_FLAGS; | ||
|
||
use super::{StdCommandWrap, StdCommandWrapper}; | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct CreationFlags(pub PROCESS_CREATION_FLAGS); | ||
|
||
impl StdCommandWrapper for CreationFlags { | ||
fn pre_spawn(&mut self, command: &mut Command, _core: &StdCommandWrap) -> Result<()> { | ||
command.creation_flags((self.0).0); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
use std::{ | ||
io::Result, | ||
os::windows::{io::AsRawHandle, process::CommandExt}, | ||
process::{Child, Command, ExitStatus}, | ||
time::Duration, | ||
}; | ||
|
||
use tracing::{debug, instrument}; | ||
use windows::Win32::{ | ||
Foundation::{CloseHandle, HANDLE}, | ||
System::Threading::CREATE_SUSPENDED, | ||
}; | ||
|
||
use crate::{ | ||
windows::{make_job_object, resume_threads, terminate_job, wait_on_job, JobPort}, | ||
ChildExitStatus, | ||
}; | ||
|
||
#[cfg(feature = "creation-flags")] | ||
use super::CreationFlags; | ||
#[cfg(feature = "kill-on-drop")] | ||
use super::KillOnDrop; | ||
use super::{StdChildWrapper, StdCommandWrap, StdCommandWrapper}; | ||
|
||
#[derive(Debug)] | ||
pub struct JobObject; | ||
|
||
impl StdCommandWrapper for JobObject { | ||
#[instrument(level = "debug", skip(self))] | ||
fn pre_spawn(&mut self, command: &mut Command, core: &StdCommandWrap) -> Result<()> { | ||
let mut flags = CREATE_SUSPENDED; | ||
#[cfg(feature = "creation-flags")] | ||
if let Some(CreationFlags(user_flags)) = core.get_wrap::<CreationFlags>() { | ||
flags |= *user_flags; | ||
} | ||
|
||
command.creation_flags(flags.0); | ||
Ok(()) | ||
} | ||
|
||
#[instrument(level = "debug", skip(self))] | ||
fn wrap_child( | ||
&mut self, | ||
inner: Box<dyn StdChildWrapper>, | ||
core: &StdCommandWrap, | ||
) -> Result<Box<dyn StdChildWrapper>> { | ||
#[cfg(feature = "kill-on-drop")] | ||
let kill_on_drop = core.has_wrap::<KillOnDrop>(); | ||
#[cfg(not(feature = "kill-on-drop"))] | ||
let kill_on_drop = false; | ||
|
||
#[cfg(feature = "creation-flags")] | ||
let create_suspended = core | ||
.get_wrap::<CreationFlags>() | ||
.map_or(false, |flags| flags.0.contains(CREATE_SUSPENDED)); | ||
#[cfg(not(feature = "creation-flags"))] | ||
let create_suspended = false; | ||
|
||
debug!( | ||
?kill_on_drop, | ||
?create_suspended, | ||
"options from other wrappers" | ||
); | ||
|
||
let handle = HANDLE(inner.inner().as_raw_handle() as _); | ||
|
||
let job_port = make_job_object(handle, kill_on_drop)?; | ||
|
||
// only resume if the user didn't specify CREATE_SUSPENDED | ||
if !create_suspended { | ||
resume_threads(handle)?; | ||
} | ||
|
||
Ok(Box::new(JobObjectChild::new(inner, job_port))) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct JobObjectChild { | ||
inner: Box<dyn StdChildWrapper>, | ||
exit_status: ChildExitStatus, | ||
job_port: JobPort, | ||
} | ||
|
||
impl JobObjectChild { | ||
#[instrument(level = "debug", skip(job_port))] | ||
pub(crate) fn new(inner: Box<dyn StdChildWrapper>, job_port: JobPort) -> Self { | ||
Self { | ||
inner, | ||
exit_status: ChildExitStatus::Running, | ||
job_port, | ||
} | ||
} | ||
} | ||
|
||
impl StdChildWrapper for JobObjectChild { | ||
fn inner(&self) -> &Child { | ||
self.inner.inner() | ||
} | ||
fn inner_mut(&mut self) -> &mut Child { | ||
self.inner.inner_mut() | ||
} | ||
fn into_inner(self: Box<Self>) -> Child { | ||
// manually drop the completion port | ||
let its = std::mem::ManuallyDrop::new(self.job_port); | ||
unsafe { CloseHandle(its.completion_port) }.ok(); | ||
// we leave the job handle unclosed, otherwise the Child is useless | ||
// (as closing it will terminate the job) | ||
|
||
self.inner.into_inner() | ||
} | ||
|
||
#[instrument(level = "debug", skip(self))] | ||
fn start_kill(&mut self) -> Result<()> { | ||
terminate_job(self.job_port.job, 1) | ||
} | ||
|
||
#[instrument(level = "debug", skip(self))] | ||
fn wait(&mut self) -> Result<ExitStatus> { | ||
if let ChildExitStatus::Exited(status) = &self.exit_status { | ||
return Ok(*status); | ||
} | ||
|
||
// always wait for parent to exit first, as by the time it does, | ||
// it's likely that all its children have already exited. | ||
let status = self.inner.wait()?; | ||
self.exit_status = ChildExitStatus::Exited(status); | ||
|
||
// nevertheless, now wait and make sure we reap all children. | ||
let JobPort { | ||
completion_port, .. | ||
} = self.job_port; | ||
wait_on_job(completion_port, None)?; | ||
Ok(status) | ||
} | ||
|
||
#[instrument(level = "debug", skip(self))] | ||
fn try_wait(&mut self) -> Result<Option<ExitStatus>> { | ||
wait_on_job(self.job_port.completion_port, Some(Duration::ZERO))?; | ||
self.inner.try_wait() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
use std::{io::Result, process::Command}; | ||
|
||
use super::{StdCommandWrap, StdCommandWrapper}; | ||
|
||
#[derive(Debug)] | ||
pub struct KillOnDrop; | ||
|
||
impl StdCommandWrapper for KillOnDrop { | ||
fn pre_spawn(&mut self, command: &mut Command, _core: &StdCommandWrap) -> Result<()> { | ||
command.kill_on_drop(true); | ||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.