Skip to content
Closed
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
11 changes: 0 additions & 11 deletions src/uu/cat/src/cat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ use std::os::unix::net::UnixStream;
use thiserror::Error;
use uucore::display::Quotable;
use uucore::error::UResult;
#[cfg(not(target_os = "windows"))]
use uucore::libc;
use uucore::translate;
use uucore::{fast_inc::fast_inc_one, format_usage};

Expand Down Expand Up @@ -222,15 +220,6 @@ mod options {

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// When we receive a SIGPIPE signal, we want to terminate the process so
// that we don't print any error messages to stderr. Rust ignores SIGPIPE
// (see https://github.com/rust-lang/rust/issues/62569), so we restore it's
// default action here.
#[cfg(not(target_os = "windows"))]
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}

let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;

let number_mode = if matches.get_flag(options::NUMBER_NONBLANK) {
Expand Down
8 changes: 0 additions & 8 deletions src/uu/env/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ use native_int_str::{
Convert, NCvt, NativeIntStr, NativeIntString, NativeStr, from_native_int_representation_owned,
};
#[cfg(unix)]
use nix::libc;
#[cfg(unix)]
use nix::sys::signal::{SigHandler::SigIgn, Signal, signal};
use std::borrow::Cow;
use std::env;
Expand Down Expand Up @@ -845,12 +843,6 @@ fn ignore_signal(sig: Signal) -> UResult<()> {

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// Rust ignores SIGPIPE (see https://github.com/rust-lang/rust/issues/62569).
// We restore its default action here.
#[cfg(unix)]
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
EnvAppData::default().run_env(args)
}

Expand Down
1 change: 1 addition & 0 deletions src/uu/seq/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ uucore = { workspace = true, features = [
"format",
"parser",
"quoting-style",
"signals",
] }
fluent = { workspace = true }

Expand Down
5 changes: 5 additions & 0 deletions src/uu/seq/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ pub enum SeqError {
translate!("seq-error-format-and-equal-width")
)]
FormatAndEqualWidth,

/// Failed to set signal handler
#[cfg(unix)]
#[error("failed to set signal handler")]
SignalHandler,
}

fn parse_error_type(e: &ParseNumberError) -> String {
Expand Down
10 changes: 10 additions & 0 deletions src/uu/seq/src/seq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ use uucore::format::num_format::FloatVariant;
use uucore::format::{Format, num_format};
use uucore::{fast_inc::fast_inc, format_usage};

#[cfg(unix)]
use uucore::signals::ignore_pipe;

mod error;

// public to allow fuzzing
Expand Down Expand Up @@ -92,6 +95,13 @@ fn select_precision(

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
#[cfg(unix)]
{
// Ignore SIGPIPE so that broken pipes return EPIPE errors instead of
// killing the process. This allows seq to handle broken pipes gracefully
// (printing an error message but exiting with status 0, matching GNU seq behavior).
ignore_pipe().map_err(|_| SeqError::SignalHandler)?;
}
let matches =
uucore::clap_localization::handle_clap_result(uu_app(), split_short_args_with_value(args))?;

Expand Down
2 changes: 1 addition & 1 deletion src/uu/split/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ path = "src/split.rs"
[dependencies]
clap = { workspace = true }
memchr = { workspace = true }
uucore = { workspace = true, features = ["fs", "parser-size"] }
uucore = { workspace = true, features = ["fs", "parser-size", "signals"] }
thiserror = { workspace = true }
fluent = { workspace = true }

Expand Down
12 changes: 12 additions & 0 deletions src/uu/split/src/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ use uucore::parser::parse_size::parse_size_u64;
use uucore::format_usage;
use uucore::uio_error;

#[cfg(unix)]
use uucore::signals::ignore_pipe;

static OPT_BYTES: &str = "bytes";
static OPT_LINE_BYTES: &str = "line-bytes";
static OPT_LINES: &str = "lines";
Expand Down Expand Up @@ -1530,6 +1533,15 @@ where

#[allow(clippy::cognitive_complexity)]
fn split(settings: &Settings) -> UResult<()> {
#[cfg(unix)]
{
// When using --filter, ignore SIGPIPE so that closure of one pipe
// does not terminate the process, as there may still be other streams
// expecting input from us. This matches GNU split behavior.
if settings.filter.is_some() {
ignore_pipe().map_err(|_| USimpleError::new(1, "failed to set signal handler"))?;
}
}
let r_box = if settings.input == "-" {
Box::new(stdin()) as Box<dyn Read>
} else {
Expand Down
9 changes: 0 additions & 9 deletions src/uu/tail/src/tail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,6 @@ use uucore::{show, show_error};

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// When we receive a SIGPIPE signal, we want to terminate the process so
// that we don't print any error messages to stderr. Rust ignores SIGPIPE
// (see https://github.com/rust-lang/rust/issues/62569), so we restore it's
// default action here.
#[cfg(not(target_os = "windows"))]
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}

let settings = parse_args(args)?;

settings.check_warnings();
Expand Down
5 changes: 4 additions & 1 deletion src/uu/tee/src/tee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use uucore::{format_usage, show_error};
// spell-checker:ignore nopipe

#[cfg(unix)]
use uucore::signals::{enable_pipe_errors, ignore_interrupts};
use uucore::signals::{enable_pipe_errors, ignore_interrupts, ignore_pipe};

mod options {
pub const APPEND: &str = "append";
Expand Down Expand Up @@ -164,6 +164,9 @@ fn tee(options: &Options) -> Result<()> {
}
if options.output_error.is_none() {
enable_pipe_errors().map_err(|_| Error::from(ErrorKind::Other))?;
} else {
// When --output-error is set, ignore SIGPIPE to handle broken pipes gracefully
ignore_pipe().map_err(|_| Error::from(ErrorKind::Other))?;
}
}
let mut writers: Vec<NamedWriter> = options
Expand Down
11 changes: 0 additions & 11 deletions src/uu/tr/src/tr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ use std::io::{stdin, stdout};
use uucore::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::fs::is_stdin_directory;
#[cfg(not(target_os = "windows"))]
use uucore::libc;
use uucore::translate;
use uucore::{format_usage, os_str_as_bytes, show};

Expand All @@ -33,15 +31,6 @@ mod options {

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// When we receive a SIGPIPE signal, we want to terminate the process so
// that we don't print any error messages to stderr. Rust ignores SIGPIPE
// (see https://github.com/rust-lang/rust/issues/62569), so we restore it's
// default action here.
#[cfg(not(target_os = "windows"))]
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}

let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;

let delete_flag = matches.get_flag(options::DELETE);
Expand Down
2 changes: 1 addition & 1 deletion src/uu/tty/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ path = "src/tty.rs"
[dependencies]
clap = { workspace = true }
nix = { workspace = true, features = ["term"] }
uucore = { workspace = true, features = ["fs"] }
uucore = { workspace = true, features = ["fs", "signals"] }
fluent = { workspace = true }

[[bin]]
Expand Down
10 changes: 10 additions & 0 deletions src/uu/tty/src/tty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,22 @@ use uucore::format_usage;

use uucore::translate;

#[cfg(unix)]
use uucore::signals::ignore_pipe;

mod options {
pub const SILENT: &str = "silent";
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
#[cfg(unix)]
{
// Ignore SIGPIPE so that broken pipes return EPIPE errors instead of
// killing the process. This allows tty to handle write failures gracefully
// and exit with code 3 when stdout fails, matching GNU tty behavior.
ignore_pipe().ok();
}
let matches = uucore::clap_localization::handle_clap_result_with_exit_code(uu_app(), args, 2)?;

let silent = matches.get_flag(options::SILENT);
Expand Down
8 changes: 8 additions & 0 deletions src/uucore/src/lib/features/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,14 @@ pub fn ignore_interrupts() -> Result<(), Errno> {
unsafe { signal(SIGINT, SigIgn) }.map(|_| ())
}

/// Ignores the SIGPIPE signal.
#[cfg(unix)]
pub fn ignore_pipe() -> Result<(), Errno> {
// We pass the error as is, the return value would just be Ok(SigIgn), so we can safely ignore it.
// SAFETY: this function is safe as long as we do not use a custom SigHandler -- we use the default one.
unsafe { signal(SIGPIPE, SigIgn) }.map(|_| ())
}

#[test]
fn signal_by_value() {
assert_eq!(signal_by_name_or_value("0"), Some(0));
Expand Down
18 changes: 15 additions & 3 deletions src/uucore/src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ pub use crate::features::selinux;
use nix::errno::Errno;
#[cfg(unix)]
use nix::sys::signal::{
SaFlags, SigAction, SigHandler::SigDfl, SigSet, Signal::SIGBUS, Signal::SIGSEGV, sigaction,
SaFlags, SigAction, SigHandler::SigDfl, SigSet, Signal::SIGBUS, Signal::SIGPIPE,
Signal::SIGSEGV, sigaction,
};
use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
Expand All @@ -143,10 +144,12 @@ use std::str;
use std::str::Utf8Chunk;
use std::sync::{LazyLock, atomic::Ordering};

/// Disables the custom signal handlers installed by Rust for stack-overflow handling. With those custom signal handlers processes ignore the first SIGBUS and SIGSEGV signal they receive.
/// See <https://github.com/rust-lang/rust/blob/8ac1525e091d3db28e67adcbbd6db1e1deaa37fb/src/libstd/sys/unix/stack_overflow.rs#L71-L92> for details.
/// Restore standard Unix signal behavior.
#[cfg(unix)]
pub fn disable_rust_signal_handlers() -> Result<(), Errno> {
// Disable custom signal handlers installed by Rust for stack-overflow handling.
// With those custom signal handlers processes ignore the first SIGBUS and SIGSEGV signal they receive.
// See <https://github.com/rust-lang/rust/blob/8ac1525e091d3db28e67adcbbd6db1e1deaa37fb/src/libstd/sys/unix/stack_overflow.rs#L71-L92> for details.
unsafe {
sigaction(
SIGSEGV,
Expand All @@ -159,6 +162,15 @@ pub fn disable_rust_signal_handlers() -> Result<(), Errno> {
&SigAction::new(SigDfl, SaFlags::empty(), SigSet::all()),
)
}?;
// Reset SIGPIPE to default behavior since Rust ignores it by default.
// This ensures Unix utilities behave normally when pipes are broken.
// See https://github.com/rust-lang/rust/issues/62569 for discussion.
unsafe {
sigaction(
SIGPIPE,
&SigAction::new(SigDfl, SaFlags::empty(), SigSet::all()),
)
}?;
Ok(())
}

Expand Down
Loading