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 <noreply@anthropic.com>
This commit is contained in:
Kevin Burke
2026-03-19 21:26:38 -07:00
committed by Daniel Hofstetter
parent f6451c7d20
commit 6e18efb218
4 changed files with 55 additions and 4 deletions
+4
View File
@@ -185,9 +185,13 @@ LINESIZE
NAMESIZE
RTLD_NEXT
RTLD
RTMAX
RTMIN
SIGABRT
SIGINT
SIGKILL
SIGRTMAX
SIGRTMIN
SIGSTOP
SIGTERM
SYS_fdatasync
+2 -2
View File
@@ -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!(
+31 -2
View File
@@ -396,11 +396,24 @@ pub static ALL_SIGNALS: [&str; 32] = [
pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option<usize> {
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));
}
+18
View File
@@ -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");
}