Skip to content

Commit 87b6902

Browse files
committed
Forward additional signals to the child process in uv run
1 parent 2803e6f commit 87b6902

File tree

1 file changed

+136
-1
lines changed

1 file changed

+136
-1
lines changed

crates/uv/src/commands/run.rs

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,24 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
4242
use nix::sys::signal;
4343
use nix::unistd::{getpgid, Pid};
4444
use tokio::select;
45-
use tokio::signal::unix::{signal as handle_signal, SignalKind};
45+
use tokio::signal::unix::{signal as handle_signal, Signal, SignalKind};
46+
47+
/// A wrapper for a signal handler that is not available on all platforms, `recv` never
48+
/// returns on unsupported platforms.
49+
#[allow(dead_code)]
50+
enum PlatformSpecificSignal {
51+
Signal(Signal),
52+
Dummy,
53+
}
54+
55+
impl PlatformSpecificSignal {
56+
async fn recv(&mut self) -> Option<()> {
57+
match self {
58+
PlatformSpecificSignal::Signal(ref mut signal) => signal.recv().await,
59+
PlatformSpecificSignal::Dummy => std::future::pending().await,
60+
}
61+
}
62+
}
4663

4764
/// Simple new type for `Pid` allowing construction from [`Child`].
4865
///
@@ -77,6 +94,44 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
7794
let mut sigint_handle = handle_signal(SignalKind::interrupt())?;
7895
let mut sigint_count = 0;
7996

97+
// The following signals are terminal by default, but can be have user defined handlers.
98+
// Forward them to the child process for handling.
99+
let mut sigusr1_handle = handle_signal(SignalKind::user_defined1())?;
100+
let mut sigusr2_handle = handle_signal(SignalKind::user_defined2())?;
101+
let mut sighup_handle = handle_signal(SignalKind::hangup())?;
102+
let mut sigalrm_handle = handle_signal(SignalKind::alarm())?;
103+
let mut sigquit_handle = handle_signal(SignalKind::quit())?;
104+
105+
// The following signals are ignored by default, but can be have user defined handlers.
106+
// Forward them to the child process for handling.
107+
let mut sigwinch_handle = handle_signal(SignalKind::window_change())?;
108+
let mut sigpipe_handle = handle_signal(SignalKind::pipe())?;
109+
110+
// This signal is only available on some platforms, copied from `tokio::signal::unix`
111+
#[cfg(any(
112+
target_os = "dragonfly",
113+
target_os = "freebsd",
114+
target_os = "macos",
115+
target_os = "netbsd",
116+
target_os = "openbsd",
117+
target_os = "illumos",
118+
))]
119+
let mut siginfo_handle = PlatformSpecificSignal::Signal(handle_signal(SignalKind::info())?);
120+
121+
#[cfg(not(any(
122+
target_os = "dragonfly",
123+
target_os = "freebsd",
124+
target_os = "macos",
125+
target_os = "netbsd",
126+
target_os = "openbsd",
127+
target_os = "illumos",
128+
)))]
129+
let mut siginfo_handle = PlatformSpecificSignal::Dummy;
130+
131+
// Notably, we do not handle `SIGCHLD` (sent when a child process status changes) and
132+
// `SIGIO` or `SIGPOLL` (sent when a file descriptor is ready for io) as they don't make
133+
// sense for this parent / child relationship.
134+
80135
loop {
81136
select! {
82137
result = handle.wait() => {
@@ -120,6 +175,86 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
120175
debug!("Received SIGTERM, forwarding to child at {child_pid}");
121176
let _ = signal::kill(child_pid, signal::Signal::SIGTERM);
122177
}
178+
_ = sigusr1_handle.recv() => {
179+
let Some(child_pid) = *ChildPid::from(&handle) else {
180+
debug!("Received SIGUSR1, but the child has already exited");
181+
continue;
182+
};
183+
184+
// We unconditionally forward SIGUSR1 to the child process.
185+
debug!("Received SIGUSR1, forwarding to child at {child_pid}");
186+
let _ = signal::kill(child_pid, signal::Signal::SIGUSR1);
187+
}
188+
_ = sigusr2_handle.recv() => {
189+
let Some(child_pid) = *ChildPid::from(&handle) else {
190+
debug!("Received SIGUSR2, but the child has already exited");
191+
continue;
192+
};
193+
194+
// We unconditionally forward SIGUSR2 to the child process.
195+
debug!("Received SIGUSR2, forwarding to child at {child_pid}");
196+
let _ = signal::kill(child_pid, signal::Signal::SIGUSR2);
197+
}
198+
_ = sighup_handle.recv() => {
199+
let Some(child_pid) = *ChildPid::from(&handle) else {
200+
debug!("Received SIGHUP, but the child has already exited");
201+
continue;
202+
};
203+
204+
// We unconditionally forward SIGHUP to the child process.
205+
debug!("Received SIGHUP, forwarding to child at {child_pid}");
206+
let _ = signal::kill(child_pid, signal::Signal::SIGHUP);
207+
}
208+
_ = sigalrm_handle.recv() => {
209+
let Some(child_pid) = *ChildPid::from(&handle) else {
210+
debug!("Received SIGALRM, but the child has already exited");
211+
continue;
212+
};
213+
214+
// We unconditionally forward SIGALRM to the child process.
215+
debug!("Received SIGALRM, forwarding to child at {child_pid}");
216+
let _ = signal::kill(child_pid, signal::Signal::SIGALRM);
217+
}
218+
_ = sigquit_handle.recv() => {
219+
let Some(child_pid) = *ChildPid::from(&handle) else {
220+
debug!("Received SIGQUIT, but the child has already exited");
221+
continue;
222+
};
223+
224+
// We unconditionally forward SIGQUIT to the child process.
225+
debug!("Received SIGQUIT, forwarding to child at {child_pid}");
226+
let _ = signal::kill(child_pid, signal::Signal::SIGQUIT);
227+
}
228+
_ = sigwinch_handle.recv() => {
229+
let Some(child_pid) = *ChildPid::from(&handle) else {
230+
debug!("Received SIGWINCH, but the child has already exited");
231+
continue;
232+
};
233+
234+
// We unconditionally forward SIGWINCH to the child process.
235+
debug!("Received SIGWINCH, forwarding to child at {child_pid}");
236+
let _ = signal::kill(child_pid, signal::Signal::SIGWINCH);
237+
}
238+
_ = sigpipe_handle.recv() => {
239+
let Some(child_pid) = *ChildPid::from(&handle) else {
240+
debug!("Received SIGPIPE, but the child has already exited");
241+
continue;
242+
};
243+
244+
// We unconditionally forward SIGPIPE to the child process.
245+
debug!("Received SIGPIPE, forwarding to child at {child_pid}");
246+
let _ = signal::kill(child_pid, signal::Signal::SIGPIPE);
247+
}
248+
_ = siginfo_handle.recv() => {
249+
let Some(child_pid) = *ChildPid::from(&handle) else {
250+
debug!("Received SIGINFO, but the child has already exited");
251+
continue;
252+
};
253+
254+
// We unconditionally forward SIGINFO to the child process.
255+
debug!("Received SIGINFO, forwarding to child at {child_pid}");
256+
let _ = signal::kill(child_pid, signal::Signal::SIGINFO);
257+
}
123258
};
124259
}
125260
}?;

0 commit comments

Comments
 (0)