Skip to content

Commit a58d74b

Browse files
committed
fix(stat): use USR1 for termination
1 parent 167b5fb commit a58d74b

File tree

2 files changed

+51
-35
lines changed

2 files changed

+51
-35
lines changed

src/uu/timeout/src/timeout.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ use uucore::process::{ChildExt, CommandExt, SelfPipe, WaitOrTimeoutRet};
2020
use uucore::translate;
2121

2222
#[cfg(unix)]
23-
use uucore::signals::{enable_pipe_errors, is_ignored};
23+
use ::{
24+
nix::sys::signal::{SigSet, SigmaskHow, pthread_sigmask},
25+
uucore::signals::{enable_pipe_errors, is_ignored},
26+
};
2427

2528
use uucore::{
2629
format_usage, show_error,
@@ -233,7 +236,7 @@ fn wait_or_kill_process(
233236
self_pipe: &mut SelfPipe,
234237
) -> std::io::Result<i32> {
235238
// ignore `SIGTERM` here
236-
self_pipe.unset_other()?;
239+
self_pipe.unset_other(Signal::SIGTERM)?;
237240

238241
match process.wait_or_timeout(duration, self_pipe) {
239242
Ok(WaitOrTimeoutRet::InTime(status)) => {
@@ -250,7 +253,8 @@ fn wait_or_kill_process(
250253
process.wait()?;
251254
Ok(ExitStatus::SignalSent(signal).into())
252255
}
253-
Ok(WaitOrTimeoutRet::CustomSignaled) | Err(_) => Ok(ExitStatus::TimeoutFailed.into()),
256+
Ok(WaitOrTimeoutRet::CustomSignaled(n)) => Ok(ExitStatus::SignalSent(n as _).into()),
257+
Err(_) => Ok(ExitStatus::TimeoutFailed.into()),
254258
}
255259
}
256260

@@ -334,7 +338,10 @@ fn timeout(
334338
.stderr(Stdio::inherit());
335339
#[cfg(unix)]
336340
block_ignored_signals()?;
337-
let mut self_pipe = command.set_up_timeout(Some(Signal::SIGTERM))?;
341+
let mut set = SigSet::empty();
342+
set.add(Signal::SIGTERM);
343+
set.add(Signal::SIGUSR1);
344+
let mut self_pipe = command.set_up_timeout(set)?;
338345
let process = &mut command.spawn().map_err(|err| {
339346
let status_code = match err.kind() {
340347
ErrorKind::NotFound => ExitStatus::CommandNotFound.into(),
@@ -356,11 +363,15 @@ fn timeout(
356363
.code()
357364
.unwrap_or_else(|| preserve_signal_info(status.signal().unwrap()))
358365
.into()),
359-
Ok(WaitOrTimeoutRet::CustomSignaled) => {
366+
Ok(WaitOrTimeoutRet::CustomSignaled(n)) => {
360367
report_if_verbose(signal, &cmd[0], verbose);
361368
send_signal(process, signal, foreground);
362369
process.wait()?;
363-
Err(ExitStatus::Terminated.into())
370+
if n == Signal::SIGTERM as i32 {
371+
Err(ExitStatus::Terminated.into())
372+
} else {
373+
Err(ExitStatus::SignalSent(n as _).into())
374+
}
364375
}
365376
Ok(WaitOrTimeoutRet::TimedOut) => {
366377
report_if_verbose(signal, &cmd[0], verbose);

src/uucore/src/lib/features/process.rs

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::pipes::pipe;
1616
use ::{
1717
nix::sys::select::FdSet,
1818
nix::sys::select::select,
19-
nix::sys::signal::{self, signal},
19+
nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, sigaction},
2020
nix::sys::time::TimeVal,
2121
std::fs::File,
2222
std::io::{Read, Write},
@@ -126,19 +126,19 @@ pub trait ChildExt {
126126
}
127127

128128
#[cfg(feature = "pipes")]
129-
pub struct SelfPipe(File, Option<Signal>, PhantomData<*mut ()>);
129+
pub struct SelfPipe(File, SigSet, PhantomData<*mut ()>);
130130

131131
#[cfg(feature = "pipes")]
132132
pub trait CommandExt {
133-
fn set_up_timeout(&mut self, other: Option<Signal>) -> io::Result<SelfPipe>;
133+
fn set_up_timeout(&mut self, others: SigSet) -> io::Result<SelfPipe>;
134134
}
135135

136136
/// Concise enum of [`ChildExt::wait_or_timeout`] possible returns.
137137
#[derive(Debug)]
138138
#[cfg(feature = "pipes")]
139139
pub enum WaitOrTimeoutRet {
140140
InTime(ExitStatus),
141-
CustomSignaled,
141+
CustomSignaled(i32),
142142
TimedOut,
143143
}
144144

@@ -197,24 +197,24 @@ impl ChildExt for Child {
197197
// if empty, we'd stall on the read. However, this may
198198
// happen spuriously, so we try to select again.
199199
if fd_set.contains(self_pipe.0.as_fd()) {
200-
let mut buf = [0];
200+
let mut buf = [0; std::mem::size_of::<Signal>()];
201201
self_pipe.0.read_exact(&mut buf)?;
202-
return match buf[0] {
202+
let sig = i32::from_ne_bytes(buf);
203+
return match sig {
203204
// SIGCHLD
204-
1 => match self.try_wait()? {
205+
libc::SIGCHLD => match self.try_wait()? {
205206
Some(e) => Ok(WaitOrTimeoutRet::InTime(e)),
206207
None => Ok(WaitOrTimeoutRet::InTime(ExitStatus::default())),
207208
},
208209
// Received SIGALRM externally, for compat with
209210
// GNU timeout we act as if it had timed out.
210-
2 => Ok(WaitOrTimeoutRet::TimedOut),
211+
libc::SIGALRM => Ok(WaitOrTimeoutRet::TimedOut),
211212
// Custom signals on zero timeout still succeed.
212-
3 if timeout.is_zero() => {
213+
_ if timeout.is_zero() => {
213214
Ok(WaitOrTimeoutRet::InTime(ExitStatus::default()))
214215
}
215216
// We received a custom signal and fail.
216-
3 => Ok(WaitOrTimeoutRet::CustomSignaled),
217-
_ => unreachable!(),
217+
x => Ok(WaitOrTimeoutRet::CustomSignaled(x)),
218218
};
219219
}
220220
}
@@ -250,7 +250,7 @@ fn duration_to_timeval_elapsed(time: Duration, start: Instant) -> Option<TimeVal
250250

251251
#[cfg(feature = "pipes")]
252252
impl CommandExt for Command {
253-
fn set_up_timeout(&mut self, other: Option<Signal>) -> io::Result<SelfPipe> {
253+
fn set_up_timeout(&mut self, others: SigSet) -> io::Result<SelfPipe> {
254254
static SELF_PIPE_W: Mutex<Option<File>> = Mutex::new(None);
255255
let (r, w) = pipe()?;
256256
*SELF_PIPE_W.lock().unwrap() = Some(w);
@@ -259,31 +259,33 @@ impl CommandExt for Command {
259259
let Ok(&mut Some(ref mut writer)) = lock.as_deref_mut() else {
260260
return;
261261
};
262-
if signal == Signal::SIGCHLD as c_int {
263-
let _ = writer.write(&[1]);
264-
} else if signal == Signal::SIGALRM as c_int {
265-
let _ = writer.write(&[2]);
266-
} else {
267-
let _ = writer.write(&[3]);
268-
}
262+
let _ = writer.write(&signal.to_ne_bytes());
269263
}
264+
let action = SigAction::new(
265+
SigHandler::Handler(sig_handler),
266+
SaFlags::SA_NOCLDSTOP,
267+
SigSet::all(),
268+
);
270269
unsafe {
271-
signal(Signal::SIGCHLD, signal::SigHandler::Handler(sig_handler))?;
272-
signal(Signal::SIGALRM, signal::SigHandler::Handler(sig_handler))?;
273-
if let Some(other) = other {
274-
signal(other, signal::SigHandler::Handler(sig_handler))?;
270+
sigaction(Signal::SIGCHLD, &action)?;
271+
sigaction(Signal::SIGALRM, &action)?;
272+
for signal in &others {
273+
sigaction(signal, &action)?;
275274
}
276275
};
277-
Ok(SelfPipe(r, other, PhantomData))
276+
Ok(SelfPipe(r, others, PhantomData))
278277
}
279278
}
280279

281280
#[cfg(feature = "pipes")]
282281
impl SelfPipe {
283-
pub fn unset_other(&self) -> io::Result<()> {
284-
if let Some(other) = self.1 {
282+
pub fn unset_other(&self, signal: Signal) -> io::Result<()> {
283+
if self.1.contains(signal) {
285284
unsafe {
286-
signal(other, signal::SigHandler::SigDfl)?;
285+
sigaction(
286+
signal,
287+
&SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty()),
288+
)?;
287289
}
288290
}
289291
Ok(())
@@ -293,8 +295,11 @@ impl SelfPipe {
293295
#[cfg(feature = "pipes")]
294296
impl Drop for SelfPipe {
295297
fn drop(&mut self) {
296-
let _ = unsafe { signal(Signal::SIGCHLD, signal::SigHandler::SigDfl) };
297-
let _ = self.unset_other();
298+
let action = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
299+
let _ = unsafe { sigaction(Signal::SIGCHLD, &action) };
300+
for signal in &self.1 {
301+
let _ = unsafe { sigaction(signal, &action) };
302+
}
298303
}
299304
}
300305

0 commit comments

Comments
 (0)