eska_tool_runtime/
process.rs1use crate::{Component, Error, resolve};
4use std::{
5 ffi::OsString,
6 io,
7 process::{Child, Command, ExitStatus},
8 sync::{Arc, atomic::AtomicUsize},
9};
10#[cfg(windows)]
11use std::{
12 path::Path,
13 process::Stdio,
14 sync::atomic::Ordering,
15 thread,
16 time::{Duration, Instant},
17};
18
19pub fn delegate(component: Component, args: &[OsString]) -> Result<ExitStatus, Error> {
24 let binary = resolve(component)?;
25 #[cfg(unix)]
26 {
27 use std::os::unix::process::CommandExt;
28 Err(Error::Process {
29 component,
30 source: Command::new(binary).args(args).exec(),
31 })
32 }
33 #[cfg(windows)]
34 {
35 let signal = SignalState::new().map_err(Error::Signal)?;
36 execute(&binary, args, &signal).map_err(|source| Error::Process { component, source })
37 }
38}
39
40#[cfg(windows)]
42fn execute(binary: &Path, args: &[OsString], signal: &SignalState) -> io::Result<ExitStatus> {
43 let child = Command::new(binary)
44 .args(args)
45 .stdin(Stdio::inherit())
46 .stdout(Stdio::inherit())
47 .stderr(Stdio::inherit())
48 .spawn()?;
49 let mut child = OwnedChild(child);
50 let mut cancellation = None;
51 loop {
52 if let Some(status) = child.0.try_wait()? {
53 return Ok(status);
54 }
55 let received = signal.received.load(Ordering::Acquire);
56 if received != 0 && cancellation.is_none() {
57 cancellation = Some(Instant::now());
58 }
59 if cancellation.is_some_and(|started| started.elapsed() >= Duration::from_secs(10)) {
60 child.0.kill()?;
61 return child.0.wait();
62 }
63 thread::sleep(Duration::from_millis(10));
64 }
65}
66
67pub struct OwnedChild(pub Child);
68
69impl Drop for OwnedChild {
70 fn drop(&mut self) {
72 if !matches!(self.0.try_wait(), Ok(Some(_))) {
73 let _ = self.0.kill();
74 let _ = self.0.wait();
75 }
76 }
77}
78
79pub struct SignalState {
80 pub received: Arc<AtomicUsize>,
81 #[cfg(unix)]
82 handlers: Vec<signal_hook::SigId>,
83}
84
85impl SignalState {
86 #[cfg(unix)]
88 pub fn new() -> io::Result<Self> {
89 use signal_hook::consts::signal::{SIGHUP, SIGINT, SIGTERM};
90 let mut state = Self {
91 received: Arc::new(AtomicUsize::new(0)),
92 handlers: Vec::new(),
93 };
94 for signal in [SIGINT, SIGTERM, SIGHUP] {
95 state.handlers.push(signal_hook::flag::register_usize(
96 signal,
97 Arc::clone(&state.received),
98 usize::try_from(signal).map_err(io::Error::other)?,
99 )?);
100 }
101 Ok(state)
102 }
103
104 #[cfg(windows)]
106 pub fn new() -> io::Result<Self> {
107 let received = Arc::new(AtomicUsize::new(0));
108 let flag = Arc::clone(&received);
109 ctrlc::set_handler(move || flag.store(2, Ordering::Release)).map_err(io::Error::other)?;
110 Ok(Self { received })
111 }
112}
113
114#[cfg(unix)]
115impl Drop for SignalState {
116 fn drop(&mut self) {
118 for handler in self.handlers.drain(..) {
119 signal_hook::low_level::unregister(handler);
120 }
121 }
122}