From f0e5d7cdb2a665b2d08e8d33c2f11afd2dda8896 Mon Sep 17 00:00:00 2001 From: Kevin Burke Date: Thu, 19 Mar 2026 21:26:38 -0700 Subject: [PATCH] uucore: support realtime signals (RTMIN/RTMAX) in signal_by_name_or_value `signal_by_name_or_value()` only looked up signals from the static ALL_SIGNALS array, which doesn't include realtime signals. This caused `timeout -s RTMAX`, `kill -s RTMIN`, and `env --default-signal=RTMAX` to reject valid signal names. Extend the function to fall back to realtime_signal_bounds() for both name lookups and numeric values in the realtime range, matching the behavior already present in signal_list_value_by_name_or_number(). Also fix timeout's report_if_verbose() to use signal_list_name_by_value instead of signal_name_by_value, so verbose output doesn't panic for realtime signal numbers. Fixes GNU test: tests/env/env-signal-handler.sh Co-Authored-By: Claude Opus 4.6 --- .../workspace.wordlist.txt | 4 +++ src/uu/timeout/src/timeout.rs | 4 +-- src/uucore/src/lib/features/signals.rs | 33 +++++++++++++++++-- tests/by-util/test_timeout.rs | 18 ++++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/.vscode/cspell.dictionaries/workspace.wordlist.txt b/.vscode/cspell.dictionaries/workspace.wordlist.txt index 1d3929832a2..d936ee43ab5 100644 --- a/.vscode/cspell.dictionaries/workspace.wordlist.txt +++ b/.vscode/cspell.dictionaries/workspace.wordlist.txt @@ -183,9 +183,13 @@ LINESIZE NAMESIZE RTLD_NEXT RTLD +RTMAX +RTMIN SIGABRT SIGINT SIGKILL +SIGRTMAX +SIGRTMIN SIGSTOP SIGTERM SYS_fdatasync diff --git a/src/uu/timeout/src/timeout.rs b/src/uu/timeout/src/timeout.rs index 6944794f19c..cb483db8fb8 100644 --- a/src/uu/timeout/src/timeout.rs +++ b/src/uu/timeout/src/timeout.rs @@ -22,7 +22,7 @@ use uucore::translate; use uucore::{ format_usage, - signals::{signal_by_name_or_value, signal_name_by_value}, + signals::{signal_by_name_or_value, signal_list_name_by_value}, }; use nix::sys::signal::{SigHandler, Signal, kill}; @@ -227,7 +227,7 @@ fn report_if_verbose(signal: usize, cmd: &str, verbose: bool) { let s = if signal == 0 { "0".to_string() } else { - signal_name_by_value(signal).unwrap().to_string() + signal_list_name_by_value(signal).unwrap() }; let mut stderr = std::io::stderr(); let _ = writeln!( diff --git a/src/uucore/src/lib/features/signals.rs b/src/uucore/src/lib/features/signals.rs index 6b89704fcad..a5141f299e3 100644 --- a/src/uucore/src/lib/features/signals.rs +++ b/src/uucore/src/lib/features/signals.rs @@ -396,11 +396,24 @@ pub static ALL_SIGNALS: [&str; 32] = [ pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option { let signal_name_upcase = signal_name_or_value.to_uppercase(); if let Ok(value) = signal_name_upcase.parse() { - return if is_signal(value) { Some(value) } else { None }; + if is_signal(value) { + return Some(value); + } + return realtime_signal_bounds() + .filter(|&(rtmin, rtmax)| value >= rtmin && value <= rtmax) + .map(|_| value); } let signal_name = signal_name_upcase.trim_start_matches("SIG"); - ALL_SIGNALS.iter().position(|&s| s == signal_name) + if let Some(pos) = ALL_SIGNALS.iter().position(|&s| s == signal_name) { + return Some(pos); + } + + realtime_signal_bounds().and_then(|(rtmin, rtmax)| match signal_name { + "RTMIN" => Some(rtmin), + "RTMAX" => Some(rtmax), + _ => None, + }) } /// Returns true if the given number is a valid signal number. @@ -754,3 +767,19 @@ fn linux_unnamed_signal_numbers_are_valid_for_lists() { assert_eq!(signal_list_value_by_name_or_number("32"), Some(32)); assert_eq!(signal_list_value_by_name_or_number("33"), Some(33)); } + +#[cfg(any(target_os = "linux", target_os = "android"))] +#[test] +fn linux_realtime_signals_resolve_by_name_or_value() { + let (rtmin, rtmax) = realtime_signal_bounds().unwrap(); + + // By name + assert_eq!(signal_by_name_or_value("RTMIN"), Some(rtmin)); + assert_eq!(signal_by_name_or_value("RTMAX"), Some(rtmax)); + assert_eq!(signal_by_name_or_value("SIGRTMIN"), Some(rtmin)); + assert_eq!(signal_by_name_or_value("SIGRTMAX"), Some(rtmax)); + + // By numeric value + assert_eq!(signal_by_name_or_value(&rtmin.to_string()), Some(rtmin)); + assert_eq!(signal_by_name_or_value(&rtmax.to_string()), Some(rtmax)); +} diff --git a/tests/by-util/test_timeout.rs b/tests/by-util/test_timeout.rs index 4bbc532e9cc..bb2ea78b825 100644 --- a/tests/by-util/test_timeout.rs +++ b/tests/by-util/test_timeout.rs @@ -286,3 +286,21 @@ fn test_foreground_signal0_kill_after() { .args(&["--foreground", "-s0", "-k.1", ".1", "sleep", "10"]) .fails_with_code(137); } + +#[test] +#[cfg(any(target_os = "linux", target_os = "android"))] +fn test_realtime_signal_names() { + // timeout should accept RTMIN and RTMAX as valid signal names + new_ucmd!() + .args(&["-v", "-s", "RTMAX", ".1", "sleep", "1"]) + .fails() + .stderr_contains("sending signal RTMAX to command"); + new_ucmd!() + .args(&["-v", "-s", "RTMIN", ".1", "sleep", "1"]) + .fails() + .stderr_contains("sending signal RTMIN to command"); + new_ucmd!() + .args(&["-v", "-s", "SIGRTMAX", ".1", "sleep", "1"]) + .fails() + .stderr_contains("sending signal RTMAX to command"); +}