Skip to content

Commit d5c9c23

Browse files
committed
dd: remove signal-hook dependency, use raw sigaction handler
1 parent ba1afb0 commit d5c9c23

File tree

6 files changed

+42
-75
lines changed

6 files changed

+42
-75
lines changed

Cargo.lock

Lines changed: 2 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,6 @@ self_cell = "1.0.4"
377377
# FIXME we use the exact version because the new 0.5.3 requires an MSRV of 1.88
378378
selinux = "=0.5.2"
379379
string-interner = "0.19.0"
380-
signal-hook = "0.4.1"
381380
tempfile = "3.15.0"
382381
terminal_size = "0.4.0"
383382
textwrap = { version = "0.16.1", features = ["terminal_size"] }

src/uu/dd/Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ thiserror = { workspace = true }
3232
fluent = { workspace = true }
3333

3434
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
35-
nix = { workspace = true, features = ["fs"] }
36-
signal-hook = { workspace = true }
35+
nix = { workspace = true, features = ["fs", "signal"] }
3736

3837
[[bin]]
3938
name = "dd"

src/uu/dd/src/dd.rs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ use nix::fcntl::OFlag;
2323
use parseargs::Parser;
2424
use progress::ProgUpdateType;
2525
use progress::{ProgUpdate, ReadStat, StatusLevel, WriteStat, gen_prog_updater};
26+
#[cfg(target_os = "linux")]
27+
use progress::{check_and_reset_sigusr1, install_sigusr1_handler};
2628
use uucore::io::OwnedFileDescriptorOrHandle;
2729
use uucore::translate;
2830

@@ -122,18 +124,9 @@ impl Alarm {
122124
Self { interval, trigger }
123125
}
124126

125-
/// Returns a closure that allows to manually trigger the alarm
126-
///
127-
/// This is useful for cases where more than one alarm even source exists
128-
/// In case of `dd` there is the SIGUSR1/SIGINFO case where we want to
129-
/// trigger an manual progress report.
130-
pub fn manual_trigger_fn(&self) -> Box<dyn Send + Sync + Fn()> {
131-
let weak_trigger = Arc::downgrade(&self.trigger);
132-
Box::new(move || {
133-
if let Some(trigger) = weak_trigger.upgrade() {
134-
trigger.store(ALARM_TRIGGER_SIGNAL, Relaxed);
135-
}
136-
})
127+
/// Manually trigger the alarm as a signal event
128+
pub fn manual_trigger(&self) {
129+
self.trigger.store(ALARM_TRIGGER_SIGNAL, Relaxed);
137130
}
138131

139132
/// Use this function to poll for any pending alarm event
@@ -1183,14 +1176,9 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
11831176
// This avoids the need to query the OS monotonic clock for every block.
11841177
let alarm = Alarm::with_interval(Duration::from_secs(1));
11851178

1186-
// The signal handler spawns an own thread that waits for signals.
1187-
// When the signal is received, it calls a handler function.
1188-
// We inject a handler function that manually triggers the alarm.
11891179
#[cfg(target_os = "linux")]
1190-
let signal_handler = progress::SignalHandler::install_signal_handler(alarm.manual_trigger_fn());
1191-
#[cfg(target_os = "linux")]
1192-
if let Err(e) = &signal_handler {
1193-
if Some(StatusLevel::None) != i.settings.status {
1180+
if let Err(e) = install_sigusr1_handler() {
1181+
if i.settings.status != Some(StatusLevel::None) {
11941182
eprintln!("{}\n\t{e}", translate!("dd-warning-signal-handler"));
11951183
}
11961184
}
@@ -1270,6 +1258,10 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
12701258
// error.
12711259
rstat += rstat_update;
12721260
wstat += wstat_update;
1261+
#[cfg(target_os = "linux")]
1262+
if check_and_reset_sigusr1() {
1263+
alarm.manual_trigger();
1264+
}
12731265
match alarm.get_trigger() {
12741266
ALARM_TRIGGER_NONE => {}
12751267
t @ (ALARM_TRIGGER_TIMER | ALARM_TRIGGER_SIGNAL) => {

src/uu/dd/src/progress.rs

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@
1010
//! [`gen_prog_updater`] function can be used to implement a progress
1111
//! updater that runs in its own thread.
1212
use std::io::Write;
13-
use std::sync::mpsc;
1413
#[cfg(target_os = "linux")]
15-
use std::thread::JoinHandle;
14+
use std::sync::atomic::{AtomicBool, Ordering};
15+
use std::sync::mpsc;
1616
use std::time::Duration;
17-
18-
#[cfg(target_os = "linux")]
19-
use signal_hook::iterator::Handle;
2017
use uucore::{
2118
error::{UResult, set_exit_code},
2219
format::num_format::{FloatVariant, Formatter},
@@ -448,47 +445,22 @@ pub(crate) fn gen_prog_updater(
448445
}
449446
}
450447

451-
/// signal handler listens for SIGUSR1 signal and runs provided closure.
452448
#[cfg(target_os = "linux")]
453-
pub(crate) struct SignalHandler {
454-
handle: Handle,
455-
thread: Option<JoinHandle<()>>,
456-
}
449+
static SIGUSR1_RECEIVED: AtomicBool = AtomicBool::new(false);
457450

458451
#[cfg(target_os = "linux")]
459-
impl SignalHandler {
460-
pub(crate) fn install_signal_handler(
461-
f: Box<dyn Send + Sync + Fn()>,
462-
) -> Result<Self, std::io::Error> {
463-
use signal_hook::consts::signal::SIGUSR1;
464-
use signal_hook::iterator::Signals;
465-
466-
let mut signals = Signals::new([SIGUSR1])?;
467-
let handle = signals.handle();
468-
let thread = std::thread::spawn(move || {
469-
for signal in &mut signals {
470-
match signal {
471-
SIGUSR1 => (*f)(),
472-
_ => unreachable!(),
473-
}
474-
}
475-
});
452+
pub(crate) fn check_and_reset_sigusr1() -> bool {
453+
SIGUSR1_RECEIVED.swap(false, Ordering::Relaxed)
454+
}
476455

477-
Ok(Self {
478-
handle,
479-
thread: Some(thread),
480-
})
481-
}
456+
#[cfg(target_os = "linux")]
457+
extern "C" fn sigusr1_handler(_: std::os::raw::c_int) {
458+
SIGUSR1_RECEIVED.store(true, Ordering::Relaxed);
482459
}
483460

484461
#[cfg(target_os = "linux")]
485-
impl Drop for SignalHandler {
486-
fn drop(&mut self) {
487-
self.handle.close();
488-
if let Some(thread) = std::mem::take(&mut self.thread) {
489-
thread.join().unwrap();
490-
}
491-
}
462+
pub(crate) fn install_sigusr1_handler() -> Result<(), nix::errno::Errno> {
463+
uucore::signals::install_signal_handler(nix::sys::signal::Signal::SIGUSR1, sigusr1_handler)
492464
}
493465

494466
/// Return a closure that can be used in its own thread to print progress info.

src/uucore/src/lib/features/signals.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
use nix::errno::Errno;
1515
#[cfg(unix)]
1616
use nix::sys::signal::{
17-
SigHandler::SigDfl, SigHandler::SigIgn, Signal::SIGINT, Signal::SIGPIPE, signal,
17+
SaFlags, SigAction, SigHandler, SigHandler::SigDfl, SigHandler::SigIgn, SigSet, Signal,
18+
Signal::SIGINT, Signal::SIGPIPE, sigaction, signal,
1819
};
1920

2021
/// The default signal value.
@@ -435,6 +436,21 @@ pub fn ignore_interrupts() -> Result<(), Errno> {
435436
unsafe { signal(SIGINT, SigIgn) }.map(|_| ())
436437
}
437438

439+
/// Installs a signal handler. The handler must be async-signal-safe.
440+
#[cfg(unix)]
441+
pub fn install_signal_handler(
442+
sig: Signal,
443+
handler: extern "C" fn(std::os::raw::c_int),
444+
) -> Result<(), Errno> {
445+
let action = SigAction::new(
446+
SigHandler::Handler(handler),
447+
SaFlags::SA_RESTART,
448+
SigSet::empty(),
449+
);
450+
unsafe { sigaction(sig, &action) }?;
451+
Ok(())
452+
}
453+
438454
// Detect closed stdin/stdout before Rust reopens them as /dev/null (see issue #2873)
439455
#[cfg(unix)]
440456
use std::sync::atomic::{AtomicBool, Ordering};

0 commit comments

Comments
 (0)