dd: remove signal-hook dependency, use raw sigaction handler

This commit is contained in:
Christopher Dryden
2026-02-06 20:34:06 +00:00
parent ba1afb0b9e
commit d5c9c233cc
6 changed files with 42 additions and 75 deletions
Generated
+2 -13
View File
@@ -755,7 +755,7 @@ dependencies = [
"mio",
"parking_lot",
"rustix",
"signal-hook 0.3.18",
"signal-hook",
"signal-hook-mio",
"winapi",
]
@@ -2765,16 +2765,6 @@ dependencies = [
"signal-hook-registry",
]
[[package]]
name = "signal-hook"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b57709da74f9ff9f4a27dce9526eec25ca8407c45a7887243b031a58935fb8e"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.5"
@@ -2783,7 +2773,7 @@ checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
dependencies = [
"libc",
"mio",
"signal-hook 0.3.18",
"signal-hook",
]
[[package]]
@@ -3410,7 +3400,6 @@ dependencies = [
"gcd",
"libc",
"nix",
"signal-hook 0.4.3",
"tempfile",
"thiserror 2.0.18",
"uucore",
-1
View File
@@ -377,7 +377,6 @@ self_cell = "1.0.4"
# FIXME we use the exact version because the new 0.5.3 requires an MSRV of 1.88
selinux = "=0.5.2"
string-interner = "0.19.0"
signal-hook = "0.4.1"
tempfile = "3.15.0"
terminal_size = "0.4.0"
textwrap = { version = "0.16.1", features = ["terminal_size"] }
+1 -2
View File
@@ -32,8 +32,7 @@ thiserror = { workspace = true }
fluent = { workspace = true }
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
nix = { workspace = true, features = ["fs"] }
signal-hook = { workspace = true }
nix = { workspace = true, features = ["fs", "signal"] }
[[bin]]
name = "dd"
+11 -19
View File
@@ -23,6 +23,8 @@ use nix::fcntl::OFlag;
use parseargs::Parser;
use progress::ProgUpdateType;
use progress::{ProgUpdate, ReadStat, StatusLevel, WriteStat, gen_prog_updater};
#[cfg(target_os = "linux")]
use progress::{check_and_reset_sigusr1, install_sigusr1_handler};
use uucore::io::OwnedFileDescriptorOrHandle;
use uucore::translate;
@@ -122,18 +124,9 @@ impl Alarm {
Self { interval, trigger }
}
/// Returns a closure that allows to manually trigger the alarm
///
/// This is useful for cases where more than one alarm even source exists
/// In case of `dd` there is the SIGUSR1/SIGINFO case where we want to
/// trigger an manual progress report.
pub fn manual_trigger_fn(&self) -> Box<dyn Send + Sync + Fn()> {
let weak_trigger = Arc::downgrade(&self.trigger);
Box::new(move || {
if let Some(trigger) = weak_trigger.upgrade() {
trigger.store(ALARM_TRIGGER_SIGNAL, Relaxed);
}
})
/// Manually trigger the alarm as a signal event
pub fn manual_trigger(&self) {
self.trigger.store(ALARM_TRIGGER_SIGNAL, Relaxed);
}
/// Use this function to poll for any pending alarm event
@@ -1183,14 +1176,9 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
// This avoids the need to query the OS monotonic clock for every block.
let alarm = Alarm::with_interval(Duration::from_secs(1));
// The signal handler spawns an own thread that waits for signals.
// When the signal is received, it calls a handler function.
// We inject a handler function that manually triggers the alarm.
#[cfg(target_os = "linux")]
let signal_handler = progress::SignalHandler::install_signal_handler(alarm.manual_trigger_fn());
#[cfg(target_os = "linux")]
if let Err(e) = &signal_handler {
if Some(StatusLevel::None) != i.settings.status {
if let Err(e) = install_sigusr1_handler() {
if i.settings.status != Some(StatusLevel::None) {
eprintln!("{}\n\t{e}", translate!("dd-warning-signal-handler"));
}
}
@@ -1270,6 +1258,10 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
// error.
rstat += rstat_update;
wstat += wstat_update;
#[cfg(target_os = "linux")]
if check_and_reset_sigusr1() {
alarm.manual_trigger();
}
match alarm.get_trigger() {
ALARM_TRIGGER_NONE => {}
t @ (ALARM_TRIGGER_TIMER | ALARM_TRIGGER_SIGNAL) => {
+11 -39
View File
@@ -10,13 +10,10 @@
//! [`gen_prog_updater`] function can be used to implement a progress
//! updater that runs in its own thread.
use std::io::Write;
#[cfg(target_os = "linux")]
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc;
#[cfg(target_os = "linux")]
use std::thread::JoinHandle;
use std::time::Duration;
#[cfg(target_os = "linux")]
use signal_hook::iterator::Handle;
use uucore::{
error::{UResult, set_exit_code},
format::num_format::{FloatVariant, Formatter},
@@ -448,47 +445,22 @@ pub(crate) fn gen_prog_updater(
}
}
/// signal handler listens for SIGUSR1 signal and runs provided closure.
#[cfg(target_os = "linux")]
pub(crate) struct SignalHandler {
handle: Handle,
thread: Option<JoinHandle<()>>,
static SIGUSR1_RECEIVED: AtomicBool = AtomicBool::new(false);
#[cfg(target_os = "linux")]
pub(crate) fn check_and_reset_sigusr1() -> bool {
SIGUSR1_RECEIVED.swap(false, Ordering::Relaxed)
}
#[cfg(target_os = "linux")]
impl SignalHandler {
pub(crate) fn install_signal_handler(
f: Box<dyn Send + Sync + Fn()>,
) -> Result<Self, std::io::Error> {
use signal_hook::consts::signal::SIGUSR1;
use signal_hook::iterator::Signals;
let mut signals = Signals::new([SIGUSR1])?;
let handle = signals.handle();
let thread = std::thread::spawn(move || {
for signal in &mut signals {
match signal {
SIGUSR1 => (*f)(),
_ => unreachable!(),
}
}
});
Ok(Self {
handle,
thread: Some(thread),
})
}
extern "C" fn sigusr1_handler(_: std::os::raw::c_int) {
SIGUSR1_RECEIVED.store(true, Ordering::Relaxed);
}
#[cfg(target_os = "linux")]
impl Drop for SignalHandler {
fn drop(&mut self) {
self.handle.close();
if let Some(thread) = std::mem::take(&mut self.thread) {
thread.join().unwrap();
}
}
pub(crate) fn install_sigusr1_handler() -> Result<(), nix::errno::Errno> {
uucore::signals::install_signal_handler(nix::sys::signal::Signal::SIGUSR1, sigusr1_handler)
}
/// Return a closure that can be used in its own thread to print progress info.
+17 -1
View File
@@ -14,7 +14,8 @@
use nix::errno::Errno;
#[cfg(unix)]
use nix::sys::signal::{
SigHandler::SigDfl, SigHandler::SigIgn, Signal::SIGINT, Signal::SIGPIPE, signal,
SaFlags, SigAction, SigHandler, SigHandler::SigDfl, SigHandler::SigIgn, SigSet, Signal,
Signal::SIGINT, Signal::SIGPIPE, sigaction, signal,
};
/// The default signal value.
@@ -435,6 +436,21 @@ pub fn ignore_interrupts() -> Result<(), Errno> {
unsafe { signal(SIGINT, SigIgn) }.map(|_| ())
}
/// Installs a signal handler. The handler must be async-signal-safe.
#[cfg(unix)]
pub fn install_signal_handler(
sig: Signal,
handler: extern "C" fn(std::os::raw::c_int),
) -> Result<(), Errno> {
let action = SigAction::new(
SigHandler::Handler(handler),
SaFlags::SA_RESTART,
SigSet::empty(),
);
unsafe { sigaction(sig, &action) }?;
Ok(())
}
// Detect closed stdin/stdout before Rust reopens them as /dev/null (see issue #2873)
#[cfg(unix)]
use std::sync::atomic::{AtomicBool, Ordering};