|
1 | 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | 2 |
|
3 | 3 | use anyhow::Result; |
4 | | -use nix::libc::c_int; |
5 | | -#[cfg(unix)] |
6 | | -use nix::sys::signal::{kill, Signal}; |
7 | | -#[cfg(unix)] |
8 | | -use nix::unistd::Pid; |
9 | 4 | use std::process::{Command, ExitStatus}; |
10 | | -use std::sync::atomic::{AtomicBool, Ordering}; |
| 5 | +use std::sync::atomic::{AtomicUsize, Ordering}; |
11 | 6 | use std::sync::Arc; |
12 | 7 | use std::thread; |
13 | | -use std::time::Duration; |
14 | | -#[cfg(windows)] |
15 | | -use winapi::shared::minwindef::FALSE; |
16 | | -#[cfg(windows)] |
17 | | -use winapi::um::processthreadsapi::{OpenProcess, TerminateProcess}; |
18 | | -#[cfg(windows)] |
19 | | -use winapi::um::winnt::{PROCESS_TERMINATE, SYNCHRONIZE}; |
| 8 | +use std::time; |
20 | 9 |
|
21 | 10 | /// This method supervises the execution of a command. |
22 | 11 | /// |
23 | 12 | /// It starts the command and waits for its completion. It also forwards |
24 | 13 | /// signals to the child process. The method returns the exit status of the |
25 | 14 | /// child process. |
26 | 15 | pub fn supervise(command: &mut Command) -> Result<ExitStatus> { |
27 | | - let mut child = command.spawn()?; |
28 | | - |
29 | | - let child_pid = child.id(); |
30 | | - let running = Arc::new(AtomicBool::new(true)); |
31 | | - let running_in_thread = running.clone(); |
32 | | - |
33 | | - let mut signals = signal_hook::iterator::Signals::new([ |
34 | | - signal_hook::consts::SIGINT, |
35 | | - signal_hook::consts::SIGTERM, |
36 | | - ])?; |
37 | | - |
38 | | - #[cfg(unix)] |
39 | | - { |
40 | | - signals.add_signal(signal_hook::consts::SIGHUP)?; |
41 | | - signals.add_signal(signal_hook::consts::SIGQUIT)?; |
42 | | - signals.add_signal(signal_hook::consts::SIGALRM)?; |
43 | | - signals.add_signal(signal_hook::consts::SIGUSR1)?; |
44 | | - signals.add_signal(signal_hook::consts::SIGUSR2)?; |
45 | | - signals.add_signal(signal_hook::consts::SIGCONT)?; |
46 | | - signals.add_signal(signal_hook::consts::SIGSTOP)?; |
| 16 | + let signaled = Arc::new(AtomicUsize::new(0)); |
| 17 | + for signal in signal_hook::consts::TERM_SIGNALS { |
| 18 | + signal_hook::flag::register_usize(*signal, Arc::clone(&signaled), *signal as usize)?; |
47 | 19 | } |
48 | 20 |
|
49 | | - let handler = thread::spawn(move || { |
50 | | - for signal in signals.forever() { |
51 | | - log::debug!("Received signal: {:?}", signal); |
52 | | - if forward_signal(signal, child_pid) { |
53 | | - // If the signal caused termination, we should stop the process. |
54 | | - running_in_thread.store(false, Ordering::SeqCst); |
55 | | - break; |
56 | | - } |
| 21 | + let mut child = command.spawn()?; |
| 22 | + loop { |
| 23 | + // Forward signals to the child process, but don't exit the loop while it is running |
| 24 | + if signaled.swap(0usize, Ordering::SeqCst) != 0 { |
| 25 | + log::debug!("Received signal, forwarding to child process"); |
| 26 | + child.kill()?; |
57 | 27 | } |
58 | | - }); |
59 | | - |
60 | | - while running.load(Ordering::SeqCst) { |
61 | | - thread::sleep(Duration::from_millis(100)); |
62 | | - } |
63 | | - handler.join().unwrap(); |
64 | | - |
65 | | - let exit_status = child.wait()?; |
66 | | - |
67 | | - Ok(exit_status) |
68 | | -} |
69 | | - |
70 | | -#[cfg(windows)] |
71 | | -fn forward_signal(_: c_int, child_pid: u32) -> bool { |
72 | | - let process_handle = unsafe { OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, child_pid) }; |
73 | | - if process_handle.is_null() { |
74 | | - let err = unsafe { winapi::um::errhandling::GetLastError() }; |
75 | | - log::error!("Failed to open process: {}", err); |
76 | | - // If the process handle is not valid, presume the process is not running anymore. |
77 | | - return true; |
78 | | - } |
79 | 28 |
|
80 | | - let terminated = unsafe { TerminateProcess(process_handle, 1) }; |
81 | | - if terminated == FALSE { |
82 | | - let err = unsafe { winapi::um::errhandling::GetLastError() }; |
83 | | - log::error!("Failed to terminate process: {}", err); |
84 | | - } |
85 | | - |
86 | | - // Ensure proper handle closure |
87 | | - unsafe { winapi::um::handleapi::CloseHandle(process_handle) }; |
88 | | - |
89 | | - // Return true if the process was terminated. |
90 | | - terminated == TRUE |
91 | | -} |
92 | | - |
93 | | -#[cfg(unix)] |
94 | | -fn forward_signal(signal: c_int, child_pid: u32) -> bool { |
95 | | - // Forward the signal to the child process |
96 | | - if let Err(e) = kill( |
97 | | - Pid::from_raw(child_pid as i32), |
98 | | - Signal::try_from(signal).ok(), |
99 | | - ) { |
100 | | - log::error!("Error forwarding signal: {}", e); |
101 | | - } |
102 | | - |
103 | | - // Return true if the process was terminated. |
104 | | - match kill(Pid::from_raw(child_pid as i32), None) { |
105 | | - Ok(_) => { |
106 | | - log::debug!("Checking if the process is still running... yes"); |
107 | | - false |
108 | | - } |
109 | | - Err(nix::Error::ESRCH) => { |
110 | | - log::debug!("Checking if the process is still running... no"); |
111 | | - true |
112 | | - } |
113 | | - Err(_) => { |
114 | | - log::debug!("Checking if the process is still running... presume dead"); |
115 | | - true |
| 29 | + // Check if the child process has exited |
| 30 | + match child.try_wait() { |
| 31 | + Ok(Some(exit_status)) => { |
| 32 | + log::debug!("Child process exited"); |
| 33 | + return Ok(exit_status); |
| 34 | + } |
| 35 | + Ok(None) => { |
| 36 | + thread::sleep(time::Duration::from_millis(100)); |
| 37 | + } |
| 38 | + Err(e) => { |
| 39 | + log::error!("Error waiting for child process: {}", e); |
| 40 | + return Err(e.into()); |
| 41 | + } |
116 | 42 | } |
117 | 43 | } |
118 | 44 | } |
0 commit comments