From 563fe4df74227cc5bc3dbb49a74aeeb030ea8e21 Mon Sep 17 00:00:00 2001 From: gabrielhnf Date: Fri, 22 May 2026 21:22:26 -0300 Subject: [PATCH] timeout: replace nix with rustix --- Cargo.lock | 2 +- src/uu/dd/src/progress.rs | 5 +- src/uu/timeout/Cargo.toml | 2 +- src/uu/timeout/src/timeout.rs | 75 ++++++++++++++++---------- src/uucore/src/lib/features/signals.rs | 5 +- 5 files changed, 55 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e7501c18..e6e827def 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4261,7 +4261,7 @@ dependencies = [ "codspeed-divan-compat", "fluent", "libc", - "nix", + "rustix", "uucore", ] diff --git a/src/uu/dd/src/progress.rs b/src/uu/dd/src/progress.rs index cc5527f55..0177f2918 100644 --- a/src/uu/dd/src/progress.rs +++ b/src/uu/dd/src/progress.rs @@ -460,7 +460,10 @@ extern "C" fn sigusr1_handler(_: std::os::raw::c_int) { #[cfg(target_os = "linux")] pub(crate) fn install_sigusr1_handler() -> Result<(), nix::errno::Errno> { - uucore::signals::install_signal_handler(nix::sys::signal::Signal::SIGUSR1, sigusr1_handler) + uucore::signals::install_signal_handler( + nix::sys::signal::Signal::SIGUSR1 as libc::c_int, + sigusr1_handler, + ) } /// Return a closure that can be used in its own thread to print progress info. diff --git a/src/uu/timeout/Cargo.toml b/src/uu/timeout/Cargo.toml index 24e16f95a..a4cd53a0d 100644 --- a/src/uu/timeout/Cargo.toml +++ b/src/uu/timeout/Cargo.toml @@ -26,7 +26,7 @@ uucore = { workspace = true, features = ["parser", "process", "signals"] } fluent = { workspace = true } [target.'cfg(unix)'.dependencies] -nix = { workspace = true, features = ["signal"] } +rustix = { workspace = true, features = ["process"] } [[bin]] name = "timeout" diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index 25590ebd6..6646540d8 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (ToDO) tstr sigstr cmdname setpgid sigchld getpid +// spell-checker:ignore (ToDO) tstr sigstr cmdname setpgid sigchld getpid TTIN TTOU mod status; @@ -18,18 +18,17 @@ use uucore::display::Quotable; use uucore::error::{UResult, USimpleError, UUsageError}; use uucore::parser::parse_time; use uucore::process::ChildExt; +use uucore::signals::install_signal_handler; use uucore::translate; +use rustix::process::{Pid, Signal, getpid, kill_process, setpgid}; +#[cfg(unix)] +use std::os::unix::process::CommandExt; use uucore::{ format_usage, signals::{signal_by_name_or_value, signal_list_name_by_value}, }; -use nix::sys::signal::{SigHandler, Signal, kill}; -use nix::unistd::{Pid, getpid, setpgid}; -#[cfg(unix)] -use std::os::unix::process::CommandExt; - pub mod options { pub static FOREGROUND: &str = "foreground"; pub static KILL_AFTER: &str = "kill-after"; @@ -182,7 +181,7 @@ pub fn uu_app() -> Command { /// Install SIGCHLD handler to ensure waiting for child works even if parent ignored SIGCHLD. fn install_sigchld() { extern "C" fn chld(_: libc::c_int) {} - let _ = unsafe { nix::sys::signal::signal(Signal::SIGCHLD, SigHandler::Handler(chld)) }; + let _ = install_signal_handler(Signal::as_raw(Signal::CHILD), chld); } /// We should terminate child process when receiving termination signals. @@ -197,27 +196,26 @@ fn install_signal_handlers(term_signal: usize) { RECEIVED_SIGNAL.store(sig, atomic::Ordering::Relaxed); } - let handler = SigHandler::Handler(handle_signal); let sigpipe_ignored = uucore::signals::sigpipe_was_ignored(); for sig in [ - Signal::SIGALRM, - Signal::SIGINT, - Signal::SIGQUIT, - Signal::SIGHUP, - Signal::SIGTERM, - Signal::SIGPIPE, - Signal::SIGUSR1, - Signal::SIGUSR2, + Signal::ALARM, + Signal::INT, + Signal::QUIT, + Signal::HUP, + Signal::TERM, + Signal::PIPE, + Signal::USR1, + Signal::USR2, ] { - if sig == Signal::SIGPIPE && sigpipe_ignored { + if sig == Signal::PIPE && sigpipe_ignored { continue; // Skip SIGPIPE if it was ignored by parent } - let _ = unsafe { nix::sys::signal::signal(sig, handler) }; + let _ = install_signal_handler(Signal::as_raw(sig), handle_signal); } - if let Ok(sig) = Signal::try_from(term_signal as i32) { - let _ = unsafe { nix::sys::signal::signal(sig, handler) }; + if let Some(sig) = signal_from_raw(term_signal as i32) { + let _ = install_signal_handler(Signal::as_raw(sig), handle_signal); } } @@ -239,6 +237,27 @@ fn report_if_verbose(signal: usize, cmd: &str, verbose: bool) { } } +fn signal_from_raw(sig: i32) -> Option { + if sig <= 0 { + return None; + } + // Fast path: standard named signals (SIGHUP, SIGTERM, SIGKILL, etc.) + if let Some(s) = Signal::from_named_raw(sig) { + return Some(s); + } + // Slow path: realtime signals (SIGRTMIN..=SIGRTMAX). + #[cfg(target_os = "linux")] + { + let rtmin = libc::SIGRTMIN(); + let rtmax = libc::SIGRTMAX(); + if sig >= rtmin && sig <= rtmax { + return Some(unsafe { Signal::from_raw_unchecked(sig) }); + } + } + + None +} + fn send_signal(process: &mut Child, signal: usize, foreground: bool) { // NOTE: GNU timeout doesn't check for errors of signal. // The subprocess might have exited just after the timeout. @@ -324,8 +343,8 @@ fn preserve_signal_info(signal: libc::c_int) -> libc::c_int { // The easiest way to preserve the latter seems to be to kill // ourselves with whatever signal our child exited with, which is // what the following is intended to accomplish. - if let Ok(sig) = Signal::try_from(signal) { - let _ = kill(getpid(), Some(sig)); + if let Some(sig) = signal_from_raw(signal) { + let _ = kill_process(getpid(), sig); } signal } @@ -359,27 +378,25 @@ fn timeout( #[cfg(unix)] { #[cfg(target_os = "linux")] - let death_sig = Signal::try_from(signal as i32).ok(); + let death_sig = signal_from_raw(signal as i32); let sigpipe_was_ignored = uucore::signals::sigpipe_was_ignored(); let stdin_was_closed = uucore::signals::stdin_was_closed(); unsafe { cmd_builder.pre_exec(move || { // Reset terminal signals to default - let _ = nix::sys::signal::signal(Signal::SIGTTIN, SigHandler::SigDfl); - let _ = nix::sys::signal::signal(Signal::SIGTTOU, SigHandler::SigDfl); + let _ = libc::signal(Signal::as_raw(Signal::TTIN), libc::SIG_DFL); + let _ = libc::signal(Signal::as_raw(Signal::TTOU), libc::SIG_DFL); // Preserve SIGPIPE ignore status if parent had it ignored if sigpipe_was_ignored { - let _ = nix::sys::signal::signal(Signal::SIGPIPE, SigHandler::SigIgn); + let _ = libc::signal(Signal::as_raw(Signal::PIPE), libc::SIG_IGN); } // If stdin was closed before Rust reopened it as /dev/null, close it in child if stdin_was_closed { libc::close(libc::STDIN_FILENO); } #[cfg(target_os = "linux")] - if let Some(sig) = death_sig { - let _ = nix::sys::prctl::set_pdeathsig(sig); - } + let _ = rustix::process::set_parent_process_death_signal(death_sig); Ok(()) }); } diff --git a/src/uucore/src/lib/features/signals.rs b/src/uucore/src/lib/features/signals.rs index 275f7acd2..0d423a6e5 100644 --- a/src/uucore/src/lib/features/signals.rs +++ b/src/uucore/src/lib/features/signals.rs @@ -511,15 +511,16 @@ pub fn ignore_interrupts() -> Result<(), Errno> { /// Installs a signal handler. The handler must be async-signal-safe. #[cfg(unix)] pub fn install_signal_handler( - sig: Signal, + sig: i32, handler: extern "C" fn(std::os::raw::c_int), ) -> Result<(), Errno> { + let signal = Signal::try_from(sig).map_err(|_| Errno::EINVAL)?; let action = SigAction::new( SigHandler::Handler(handler), SaFlags::SA_RESTART, SigSet::empty(), ); - unsafe { sigaction(sig, &action) }?; + unsafe { sigaction(signal, &action) }?; Ok(()) }