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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/uu/env/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ uucore = { workspace = true, features = ["signals"] }
fluent = { workspace = true }

[target.'cfg(unix)'.dependencies]
nix = { workspace = true, features = ["signal"] }
libc = { workspace = true }

[[bin]]
name = "env"
Expand Down
118 changes: 48 additions & 70 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 sigemptyset sigaddset sighandler ptrs

pub mod native_int_str;
pub mod split_iterator;
Expand All @@ -18,15 +18,6 @@ use native_int_str::{
Convert, NCvt, NativeIntStr, NativeIntString, NativeStr, from_native_int_representation,
from_native_int_representation_owned, get_single_native_int_value,
};
#[cfg(unix)]
use nix::libc;
#[cfg(unix)]
use nix::sys::signal::{
SigHandler::{SigDfl, SigIgn},
SigSet, SigmaskHow, Signal, signal, sigprocmask,
};
#[cfg(unix)]
use nix::unistd::execvp;
use std::borrow::Cow;
#[cfg(unix)]
use std::collections::{BTreeMap, BTreeSet};
Expand All @@ -44,7 +35,9 @@ 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::{
signal_by_name_or_value, signal_list_name_by_value, signal_number_upper_bound,
};
use uucore::translate;
use uucore::{format_usage, show_warning};

Expand Down Expand Up @@ -293,19 +286,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 @@ -786,13 +766,13 @@ impl EnvAppData {
&opts.default_signal,
&mut signal_action_log,
SignalActionKind::Default,
reset_signal,
|sig| set_signal_disposition(sig, libc::SIG_DFL),
)?;
apply_signal_action(
&opts.ignore_signal,
&mut signal_action_log,
SignalActionKind::Ignore,
ignore_signal,
|sig| set_signal_disposition(sig, libc::SIG_IGN),
)?;
apply_signal_action(
&opts.block_signal,
Expand Down Expand Up @@ -891,12 +871,21 @@ impl EnvAppData {
argv.push(arg_cstring);
}

// Execute the program using execvp. this replaces the current
// process. The execvp function takes care of appending a NULL
// argument to the argument list so that we don't have to.
match execvp(&prog_cstring, &argv) {
Err(nix::errno::Errno::ENOENT) => Err(self.make_error_no_such_file_or_dir(&prog)),
Err(nix::errno::Errno::EACCES) => {
// Execute the program using execvp(3). This replaces the current
// process and only returns on error. Build the NULL-terminated argv
// array of pointers that libc expects.
let mut argv_ptrs: Vec<*const libc::c_char> =
argv.iter().map(|arg| arg.as_ptr()).collect();
argv_ptrs.push(std::ptr::null());

// SAFETY: `prog_cstring` and every `argv` entry are valid NUL-terminated
// C strings that outlive the call, and `argv_ptrs` is NULL-terminated.
unsafe { libc::execvp(prog_cstring.as_ptr(), argv_ptrs.as_ptr()) };

// execvp only returns on failure; errno tells us why.
match io::Error::last_os_error().raw_os_error() {
Some(libc::ENOENT) => Err(self.make_error_no_such_file_or_dir(&prog)),
Some(libc::EACCES) => {
uucore::show_error!(
"{}",
translate!(
Expand All @@ -906,7 +895,7 @@ impl EnvAppData {
);
Err(126.into())
}
Err(_) => {
_ => {
uucore::show_error!(
"{}",
translate!(
Expand All @@ -916,9 +905,6 @@ impl EnvAppData {
);
Err(126.into())
}
Ok(_) => {
unreachable!("execvp should never return on success")
}
}
}

Expand Down Expand Up @@ -1128,12 +1114,12 @@ fn apply_signal_action<F>(
signal_fn: F,
) -> UResult<()>
where
F: Fn(Signal) -> UResult<()>,
F: Fn(i32) -> 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 {
let Some(sig) = uucore::signals::signal_from_raw(sig_value) else {
return Ok(());
};
signal_fn(sig)?;
Expand All @@ -1151,46 +1137,36 @@ where
})
}

/// Set `sig`'s disposition to `SIG_IGN`/`SIG_DFL`, wrapping failures in env's
/// translated error. The disposition is applied via [`uucore::signals::set_disposition`],
/// which handles real-time signals too.
#[cfg(unix)]
fn ignore_signal(sig: Signal) -> 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) };
if let Err(err) = result {
return Err(USimpleError::new(
125,
translate!("env-error-failed-set-signal-action", "signal" => (sig as i32), "error" => err.desc()),
));
}
Ok(())
fn set_signal_disposition(sig: i32, handler: libc::sighandler_t) -> UResult<()> {
uucore::signals::set_disposition(sig, handler).map_err(|_| signal_action_error(sig))
}

#[cfg(unix)]
fn reset_signal(sig: Signal) -> UResult<()> {
let result = unsafe { signal(sig, SigDfl) };
if let Err(err) = result {
return Err(USimpleError::new(
125,
translate!("env-error-failed-set-signal-action", "signal" => (sig as i32), "error" => err.desc()),
));
fn block_signal(sig: i32) -> UResult<()> {
// SAFETY: build a set containing only `sig` and add it to the process mask.
let mut set: libc::sigset_t = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&raw mut set) };
unsafe { libc::sigaddset(&raw mut set, sig) };
if unsafe { libc::sigprocmask(libc::SIG_BLOCK, &raw const set, std::ptr::null_mut()) } == -1 {
return Err(signal_action_error(sig));
}
Ok(())
}

#[cfg(unix)]
fn block_signal(sig: Signal) -> UResult<()> {
let mut set = SigSet::empty();
set.add(sig);
if let Err(err) = sigprocmask(SigmaskHow::SIG_BLOCK, Some(&set), None) {
return Err(USimpleError::new(
125,
translate!(
"env-error-failed-set-signal-action",
"signal" => (sig as i32),
"error" => err.desc()
),
));
}
Ok(())
fn signal_action_error(sig: i32) -> Box<dyn UError> {
USimpleError::new(
125,
translate!(
"env-error-failed-set-signal-action",
"signal" => sig,
"error" => uucore::error::strip_errno(&io::Error::last_os_error())
),
)
}

#[cfg(unix)]
Expand All @@ -1204,7 +1180,9 @@ fn list_signal_handling(log: &SignalActionLog) {
SignalActionKind::Ignore => "IGNORE",
SignalActionKind::Block => "BLOCK",
};
let signal_name = signal_name_by_value(sig_value).unwrap_or("?");
// Use the list-style lookup so real-time signals (SIGRTMIN..=SIGRTMAX)
// print as "RTMIN"/"RTMAX" rather than "?", matching GNU env.
let signal_name = signal_list_name_by_value(sig_value).unwrap_or_else(|| "?".to_string());
eprintln!("{signal_name:<10} ({}): {action}", sig_value as i32);
}
}
Expand Down
11 changes: 8 additions & 3 deletions src/uucore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ jiff = { workspace = true, optional = true, features = [
"tzdb-concatenated",
] }
rustc-hash = { workspace = true }
rustix = { workspace = true, features = ["fs", "net", "pipe", "process"] }
rustix = { workspace = true, features = [
"event",
"fs",
"net",
"pipe",
"process",
] }
time = { workspace = true, optional = true, features = [
"formatting",
"local-offset",
Expand Down Expand Up @@ -102,7 +108,6 @@ selinux = { workspace = true, optional = true }
nix = { workspace = true, features = [
"dir",
"fs",
"poll",
"signal",
"uio",
"user",
Expand Down Expand Up @@ -183,7 +188,7 @@ safe-copy = []
safe-traversal = ["libc"]
selinux = ["dep:selinux"]
smack = ["xattr"]
signals = []
signals = ["libc"]
sum = [
"digest",
"hex",
Expand Down
Loading
Loading