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
105 changes: 74 additions & 31 deletions src/uu/env/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) chdir progname subcommand subcommands unsets setenv putenv spawnp SIGSEGV SIGBUS sigaction Sigmask sigprocmask elidable
// spell-checker:ignore (ToDO) chdir progname subcommand subcommands unsets setenv putenv spawnp SIGSEGV SIGBUS sigaction Sigmask sigprocmask elidable sigset sigaddset sigemptyset

pub mod native_int_str;
pub mod split_iterator;
Expand All @@ -20,10 +20,7 @@ use native_int_str::{
#[cfg(unix)]
use nix::libc;
#[cfg(unix)]
use nix::sys::signal::{
SigHandler::{SigDfl, SigIgn},
SigSet, SigmaskHow, Signal, signal, sigprocmask,
};
use nix::sys::signal::{SigSet, SigmaskHow, Signal, sigprocmask};
#[cfg(unix)]
use nix::unistd::execvp;
use std::borrow::Cow;
Expand All @@ -37,13 +34,18 @@ use std::io;
use std::io::Write as _;
use std::io::stderr;
#[cfg(unix)]
use std::mem::zeroed;
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;

use uucore::display::{Quotable, print_all_env_vars};
use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError};
use uucore::line_ending::LineEnding;
#[cfg(unix)]
use uucore::signals::{signal_by_name_or_value, signal_name_by_value, signal_number_upper_bound};
use uucore::signals::{
realtime_signal_bounds, signal_by_name_or_value, signal_name_by_value,
signal_number_upper_bound,
};
use uucore::translate;
use uucore::{format_usage, show_warning};

Expand Down Expand Up @@ -294,19 +296,6 @@ fn build_signal_request(
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 @@ -1064,6 +1053,21 @@ fn apply_specified_env_vars(opts: &Options<'_>) {
}
}

#[cfg(unix)]
fn signal_is_valid(sig: usize) -> bool {
if Signal::try_from(sig as i32).is_err() {
// nix::sys::signal does not know about real-time signals, so check that
// ourselves.
if let Some((rtmin, rtmax)) = realtime_signal_bounds() {
return sig >= rtmin && sig <= rtmax;
}

return false;
}

true
}

#[cfg(unix)]
fn apply_signal_action<F>(
request: &SignalRequest,
Expand All @@ -1072,15 +1076,16 @@ fn apply_signal_action<F>(
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 {
if !signal_is_valid(sig_value) {
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 +1101,14 @@ where
}

#[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 +1119,13 @@ fn ignore_signal(sig: Signal) -> UResult<()> {
}

#[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 +1136,37 @@ fn reset_signal(sig: Signal) -> UResult<()> {
}

#[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.
let set = unsafe {
let mut sigset: libc::sigset_t = zeroed();

if let Err(err) = nix::errno::Errno::result(libc::sigemptyset(&raw mut sigset)) {
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(&raw mut sigset, sig as libc::c_int))
{
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 +1191,7 @@ fn list_signal_handling(log: &SignalActionLog) {
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::errno::Errno;
use nix::libc;
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
90 changes: 76 additions & 14 deletions src/uucore/src/lib/features/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,43 +399,81 @@ 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
}
})
}

/// Returns the values of SIGRTMIN and SIGRTMAX if defined on this platform.
#[cfg(any(target_os = "linux", target_os = "android"))]
fn realtime_signal_bounds() -> Option<(usize, usize)> {
pub fn realtime_signal_bounds() -> Option<(usize, usize)> {
let rtmin = libc::SIGRTMIN();
let rtmax = libc::SIGRTMAX();

(0 < rtmin && rtmin <= rtmax).then_some((rtmin as usize, rtmax as usize))
}

/// Returns the values of SIGRTMIN and SIGRTMAX if defined on this platform.
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn realtime_signal_bounds() -> Option<(usize, usize)> {
pub fn realtime_signal_bounds() -> Option<(usize, usize)> {
None
}

Expand All @@ -449,7 +487,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 +757,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 +821,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
Loading