Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 49 additions & 32 deletions src/uu/env/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@
#[cfg(unix)]
use nix::libc;
#[cfg(unix)]
use nix::sys::signal::{
SigHandler::{SigDfl, SigIgn},
SigSet, SigmaskHow, Signal, signal, sigprocmask,
};
use nix::sys::signal::{sigprocmask, SigSet, SigmaskHow};
#[cfg(unix)]
use nix::unistd::execvp;
use std::borrow::Cow;
Expand All @@ -36,6 +33,7 @@
use std::io;
use std::io::Write as _;
use std::io::stderr;
use std::mem::zeroed;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;

Expand Down Expand Up @@ -294,19 +292,6 @@
Ok(request)
}

#[cfg(unix)]
fn signal_from_value(sig_value: usize) -> UResult<Signal> {
Signal::try_from(sig_value as i32).map_err(|_| {
USimpleError::new(
125,
translate!(
"env-error-invalid-signal",
"signal" => sig_value.to_string().quote()
),
)
})
}

fn load_config_file(opts: &mut Options) -> UResult<()> {
// NOTE: config files are parsed using an INI parser b/c it's available and compatible with ".env"-style files
// ... * but support for actual INI files, although working, is not intended, nor claimed
Expand Down Expand Up @@ -1072,15 +1057,10 @@
signal_fn: F,
) -> UResult<()>
where
F: Fn(Signal) -> UResult<()>,
F: Fn(usize) -> UResult<()>,
{
request.for_each_signal(|sig_value, explicit| {
// On some platforms ALL_SIGNALS may contain values that are not valid in libc.
// Skip those invalid ones and continue (GNU env also ignores undefined signals).
let Ok(sig) = signal_from_value(sig_value) else {
return Ok(());
};
signal_fn(sig)?;
signal_fn(sig_value)?;
log.record(sig_value, action_kind, explicit);

// Set environment variable to communicate to Rust child processes
Expand All @@ -1096,9 +1076,14 @@
}

#[cfg(unix)]
fn ignore_signal(sig: Signal) -> UResult<()> {
fn ignore_signal(sig: usize) -> UResult<()> {
// SAFETY: This is safe because we write the handler for each signal only once, and therefore "the current handler is the default", as the documentation requires it.
let result = unsafe { signal(sig, SigIgn) };
// nix::sys::signal::Signal does not cover real-time signals, so we need to call
// libc::signal directly.
let result = unsafe {
let res = libc::signal(sig as libc::c_int, libc::SIG_IGN);
nix::errno::Errno::result(res)
};
if let Err(err) = result {
return Err(USimpleError::new(
125,
Expand All @@ -1109,8 +1094,13 @@
}

#[cfg(unix)]
fn reset_signal(sig: Signal) -> UResult<()> {
let result = unsafe { signal(sig, SigDfl) };
fn reset_signal(sig: usize) -> UResult<()> {
// nix::sys::signal::Signal does not cover real-time signals, so we need to call
// libc::signal directly.
let result = unsafe {
let res = libc::signal(sig as libc::c_int, libc::SIG_DFL);
nix::errno::Errno::result(res)
};
if let Err(err) = result {
return Err(USimpleError::new(
125,
Expand All @@ -1121,9 +1111,36 @@
}

#[cfg(unix)]
fn block_signal(sig: Signal) -> UResult<()> {
let mut set = SigSet::empty();
set.add(sig);
fn block_signal(sig: usize) -> UResult<()> {
// nix::sys::signal::Signal does not cover real time signals, so we need to build
// sigset_t manually using libc.

Check failure on line 1116 in src/uu/env/src/env.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: `cspell`: Unknown word 'sigset' (file:'src/uu/env/src/env.rs', line:1116)
let set = unsafe {
let mut sigset: libc::sigset_t = zeroed();

Check failure on line 1118 in src/uu/env/src/env.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: `cspell`: Unknown word 'sigset' (file:'src/uu/env/src/env.rs', line:1118)

Check failure on line 1118 in src/uu/env/src/env.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: `cspell`: Unknown word 'sigset' (file:'src/uu/env/src/env.rs', line:1118)

if let Err(err) = nix::errno::Errno::result(libc::sigemptyset(&mut sigset)) {

Check failure on line 1120 in src/uu/env/src/env.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: `cspell`: Unknown word 'sigset' (file:'src/uu/env/src/env.rs', line:1120)

Check failure on line 1120 in src/uu/env/src/env.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: `cspell`: Unknown word 'sigemptyset' (file:'src/uu/env/src/env.rs', line:1120)
return Err(USimpleError::new(
125,
translate!(
"env-error-failed-set-signal-action",
"signal" => (sig as i32),
"error" => err.desc()
),
));
}
if let Err(err) = nix::errno::Errno::result(libc::sigaddset(&mut sigset, sig as libc::c_int))

Check failure on line 1130 in src/uu/env/src/env.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: `cspell`: Unknown word 'sigset' (file:'src/uu/env/src/env.rs', line:1130)

Check failure on line 1130 in src/uu/env/src/env.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: `cspell`: Unknown word 'sigaddset' (file:'src/uu/env/src/env.rs', line:1130)
{
return Err(USimpleError::new(
125,
translate!(
"env-error-failed-set-signal-action",
"signal" => (sig as i32),
"error" => err.desc()
),
));
}
SigSet::from_sigset_t_unchecked(sigset)
};

if let Err(err) = sigprocmask(SigmaskHow::SIG_BLOCK, Some(&set), None) {
return Err(USimpleError::new(
125,
Expand All @@ -1148,7 +1165,7 @@
SignalActionKind::Ignore => "IGNORE",
SignalActionKind::Block => "BLOCK",
};
let signal_name = signal_name_by_value(sig_value).unwrap_or("?");
let signal_name = signal_name_by_value(sig_value).unwrap_or("?".to_string());
eprintln!("{signal_name:<10} ({}): {action}", sig_value as i32);
}
}
Expand Down
27 changes: 10 additions & 17 deletions src/uu/kill/src/kill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
// spell-checker:ignore (ToDO) signalname pids killpg

use clap::{Arg, ArgAction, Command};
use nix::sys::signal::{self, Signal};
use nix::unistd::Pid;
use nix::libc;
use nix::errno::Errno;
use std::io::Error;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
use uucore::translate;

use uucore::signals::{
signal_by_name_or_value, signal_list_name_by_value, signal_list_value_by_name_or_number,
signal_name_by_value, signal_number_upper_bound,
signal_number_upper_bound,
};
use uucore::{format_usage, show};

Expand Down Expand Up @@ -68,18 +68,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
15_usize //SIGTERM
};

let sig_name = signal_name_by_value(sig);
// Signal does not support converting from EXIT
// Instead, nix::signal::kill expects Option::None to properly handle EXIT
let sig: Option<Signal> = if sig_name.is_some_and(|name| name == "EXIT") {
None
} else {
let sig = (sig as i32)
.try_into()
.map_err(|e| Error::from_raw_os_error(e as i32))?;
Some(sig)
};

let pids = parse_pids(&pids_or_signals)?;
if pids.is_empty() {
Err(USimpleError::new(1, translate!("kill-error-no-process-id")))
Expand Down Expand Up @@ -250,9 +238,14 @@ fn parse_pids(pids: &[String]) -> UResult<Vec<i32>> {
.collect()
}

fn kill(sig: Option<Signal>, pids: &[i32]) {
fn kill(sig: usize, pids: &[i32]) {
for &pid in pids {
if let Err(e) = signal::kill(Pid::from_raw(pid), sig) {
let result = unsafe {
let res = libc::kill(pid, sig as libc::c_int);
Errno::result(res)
};

if let Err(e) = result {
show!(
Error::from_raw_os_error(e as i32)
.map_err_context(|| { translate!("kill-error-sending-signal", "pid" => pid) })
Expand Down
84 changes: 72 additions & 12 deletions src/uucore/src/lib/features/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,31 +399,67 @@ pub fn signal_by_name_or_value(signal_name_or_value: &str) -> Option<usize> {
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");

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,
realtime_signal_bounds().and_then(|(rtmin, rtmax)| {
if signal_name.starts_with("RTMIN+") {
if let Ok(n) = signal_name.trim_start_matches("RTMIN+").parse::<usize>() {
let value = rtmin + n;
return (value >= rtmin && value <= rtmax).then_some(value);
}
}

if signal_name.starts_with("RTMAX-") {
if let Ok(n) = signal_name.trim_start_matches("RTMAX-").parse::<usize>() {
let value = rtmax - n;
return (value >= rtmin && value <= rtmax).then_some(value);
}
}

match signal_name {
"RTMIN" => Some(rtmin),
"RTMAX" => Some(rtmax),
_ => None,
}
})
}

/// Returns true if the given number is a valid signal number.
pub fn is_signal(num: usize) -> bool {
num < ALL_SIGNALS.len()
if num < ALL_SIGNALS.len() {
return true;
}

if let Some((rtmin, rtmax)) = realtime_signal_bounds() {
return num >= rtmin && num <= rtmax;
}

false
}

/// Returns the signal name for a given signal value.
pub fn signal_name_by_value(signal_value: usize) -> Option<&'static str> {
ALL_SIGNALS.get(signal_value).copied()
pub fn signal_name_by_value(signal_value: usize) -> Option<String> {
if let Some(name) = ALL_SIGNALS.get(signal_value).copied() {
return Some(name.to_string());
}

realtime_signal_bounds().and_then(|(rtmin, rtmax)| {
if signal_value == rtmin {
Some("RTMIN".to_string())
} else if signal_value == rtmax {
Some("RTMAX".to_string())
} else if signal_value > rtmin && signal_value < rtmax {
let n = signal_value - rtmin;
Some(format!("RTMIN+{n}"))
} else {
None
}
})
}

#[cfg(any(target_os = "linux", target_os = "android"))]
Expand All @@ -449,7 +485,7 @@ pub fn signal_number_upper_bound() -> usize {
/// Returns the signal name for list-style interfaces.
pub fn signal_list_name_by_value(signal_value: usize) -> Option<String> {
if let Some(signal_name) = signal_name_by_value(signal_value) {
return Some(signal_name.to_string());
return Some(signal_name);
}

realtime_signal_bounds().and_then(|(rtmin, rtmax)| {
Expand Down Expand Up @@ -719,7 +755,7 @@ fn signal_by_long_name() {
#[test]
fn name() {
for (value, signal) in ALL_SIGNALS.iter().enumerate() {
assert_eq!(signal_name_by_value(value), Some(*signal));
assert_eq!(signal_name_by_value(value), Some(signal.to_string()));
}
}

Expand Down Expand Up @@ -783,11 +819,35 @@ fn linux_realtime_signals_resolve_by_name_or_value() {

// By name
assert_eq!(signal_by_name_or_value("RTMIN"), Some(rtmin));
for i in 1..rtmax - rtmin - 1 {
assert_eq!(
signal_by_name_or_value(&format!("RTMIN+{i}")),
Some(rtmin + i)
);
assert_eq!(
signal_by_name_or_value(&format!("RTMAX-{i}")),
Some(rtmax - i)
);
}
assert_eq!(signal_by_name_or_value("RTMAX"), Some(rtmax));
assert_eq!(signal_by_name_or_value("SIGRTMIN"), Some(rtmin));
for i in 1..rtmax - rtmin - 1 {
assert_eq!(
signal_by_name_or_value(&format!("SIGRTMIN+{i}")),
Some(rtmin + i)
);
assert_eq!(
signal_by_name_or_value(&format!("SIGRTMAX-{i}")),
Some(rtmax - i)
);
}
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));
for i in 1..rtmax - rtmin - 1 {
let value = rtmin + i;
assert_eq!(signal_by_name_or_value(&value.to_string()), Some(value));
}
assert_eq!(signal_by_name_or_value(&rtmax.to_string()), Some(rtmax));
}
Loading