Skip to content
Merged
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
7 changes: 0 additions & 7 deletions src/uu/cat/src/cat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,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"))]
let _ = uucore::signals::enable_pipe_errors();

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

let number_mode = if matches.get_flag(options::NUMBER_NONBLANK) {
Expand Down
3 changes: 0 additions & 3 deletions src/uu/dd/src/dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@

// spell-checker:ignore fname, ftype, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rremain, rsofar, rstat, sigusr, wlen, wstat seekable oconv canonicalized fadvise Fadvise FADV DONTNEED ESPIPE bufferedoutput, SETFL

#[cfg(unix)]
uucore::init_startup_state_capture!();

mod blocks;
mod bufferedoutput;
mod conversion_tables;
Expand Down
4 changes: 0 additions & 4 deletions src/uu/env/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1095,10 +1095,6 @@ fn list_signal_handling(log: &SignalActionLog) {

#[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)]
let _ = uucore::signals::enable_pipe_errors();
EnvAppData::default().run_env(args)
}

Expand Down
14 changes: 0 additions & 14 deletions src/uu/seq/src/seq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,22 +92,8 @@ fn select_precision(
}
}

// Initialize SIGPIPE state capture at process startup (Unix only)
#[cfg(unix)]
uucore::init_startup_state_capture!();

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// Restore SIGPIPE to default if it wasn't explicitly ignored by parent.
// The Rust runtime ignores SIGPIPE, but we need to respect the parent's
// signal disposition for proper pipeline behavior (GNU compatibility).
#[cfg(unix)]
if !signals::sigpipe_was_ignored() {
// Ignore the return value: if setting signal handler fails, we continue anyway.
// The worst case is we don't get proper SIGPIPE behavior, but seq will still work.
let _ = signals::enable_pipe_errors();
}

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

Expand Down
11 changes: 10 additions & 1 deletion src/uu/split/src/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;

match Settings::from(&matches, obs_lines.as_deref()) {
Ok(settings) => split(&settings),
Ok(settings) => {
// When using --filter, we write to a child process's stdin which may
// close early. Disable SIGPIPE so we get EPIPE errors instead of
// being terminated, allowing graceful handling of broken pipes.
#[cfg(unix)]
if settings.filter.is_some() {
let _ = uucore::signals::disable_pipe_errors();
}
split(&settings)
}
Err(e) if e.requires_usage() => Err(UUsageError::new(1, format!("{e}"))),
Err(e) => Err(USimpleError::new(1, format!("{e}"))),
}
Expand Down
2 changes: 0 additions & 2 deletions src/uu/tac/src/tac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) sbytes slen dlen memmem memmap Mmap mmap SIGBUS
#[cfg(unix)]
uucore::init_startup_state_capture!();

mod error;

Expand Down
10 changes: 0 additions & 10 deletions src/uu/tail/src/tail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,8 @@ use uucore::translate;

use uucore::{show, show_error};

#[cfg(unix)]
uucore::init_startup_state_capture!();

#[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"))]
let _ = uucore::signals::enable_pipe_errors();

let settings = parse_args(args)?;

settings.check_warnings();
Expand Down
6 changes: 3 additions & 3 deletions 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};
#[cfg(target_os = "linux")]
use uucore::signals::ensure_stdout_not_broken;
#[cfg(unix)]
use uucore::signals::{enable_pipe_errors, ignore_interrupts};
use uucore::signals::{disable_pipe_errors, ignore_interrupts};

mod options {
pub const APPEND: &str = "append";
Expand Down Expand Up @@ -163,8 +163,8 @@ fn tee(options: &Options) -> Result<()> {
if options.ignore_interrupts {
ignore_interrupts().map_err(|_| Error::from(ErrorKind::Other))?;
}
if options.output_error.is_none() {
enable_pipe_errors().map_err(|_| Error::from(ErrorKind::Other))?;
if options.output_error.is_some() {
disable_pipe_errors().map_err(|_| Error::from(ErrorKind::Other))?;
}
}
let mut writers: Vec<NamedWriter> = options
Expand Down
7 changes: 0 additions & 7 deletions src/uu/timeout/src/timeout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) tstr sigstr cmdname setpgid sigchld getpid
#[cfg(unix)]
uucore::init_startup_state_capture!();

mod status;

Expand All @@ -22,9 +20,6 @@ use uucore::parser::parse_time;
use uucore::process::ChildExt;
use uucore::translate;

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

use uucore::{
format_usage, show_error,
signals::{signal_by_name_or_value, signal_name_by_value},
Expand Down Expand Up @@ -334,8 +329,6 @@ fn timeout(
if !foreground {
let _ = setpgid(Pid::from_raw(0), Pid::from_raw(0));
}
#[cfg(unix)]
enable_pipe_errors()?;

let mut command = process::Command::new(&cmd[0]);
command
Expand Down
7 changes: 0 additions & 7 deletions src/uu/tr/src/tr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +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"))]
let _ = uucore::signals::enable_pipe_errors();

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

let delete_flag = matches.get_flag(options::DELETE);
Expand Down
5 changes: 5 additions & 0 deletions src/uu/tty/src/tty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ mod options {

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
// Disable SIGPIPE so we can handle broken pipe errors gracefully
// and exit with code 3 instead of being killed by the signal.
#[cfg(unix)]
let _ = uucore::signals::disable_pipe_errors();

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

let silent = matches.get_flag(options::SILENT);
Expand Down
4 changes: 0 additions & 4 deletions src/uu/yes/src/yes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ use std::ffi::OsString;
use std::io::{self, Write};
use uucore::error::{UResult, USimpleError};
use uucore::format_usage;
#[cfg(unix)]
use uucore::signals::enable_pipe_errors;
use uucore::translate;

// it's possible that using a smaller or larger buffer might provide better performance on some
Expand Down Expand Up @@ -113,8 +111,6 @@ fn prepare_buffer(buf: &mut Vec<u8>) {
pub fn exec(bytes: &[u8]) -> io::Result<()> {
let stdout = io::stdout();
let mut stdout = stdout.lock();
#[cfg(unix)]
enable_pipe_errors()?;

loop {
stdout.write_all(bytes)?;
Expand Down
2 changes: 1 addition & 1 deletion src/uucore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ windows-sys = { workspace = true, optional = true, default-features = false, fea
utmp-classic = { workspace = true, optional = true }

[features]
default = []
default = ["signals"]
# * non-default features
backup-control = []
colors = []
Expand Down
11 changes: 10 additions & 1 deletion src/uucore/src/lib/features/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,14 +410,23 @@ pub fn signal_name_by_value(signal_value: usize) -> Option<&'static str> {
ALL_SIGNALS.get(signal_value).copied()
}

/// Returns the default signal value.
/// Restores SIGPIPE to default behavior (process terminates on broken pipe).
#[cfg(unix)]
pub fn enable_pipe_errors() -> Result<(), Errno> {
// We pass the error as is, the return value would just be Ok(SigDfl), 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, SigDfl) }.map(|_| ())
}

/// Ignores SIGPIPE signal (broken pipe errors are returned instead of terminating).
/// Use this to override the default SIGPIPE handling when you need to handle
/// broken pipe errors gracefully (e.g., tee with --output-error).
#[cfg(unix)]
pub fn disable_pipe_errors() -> Result<(), Errno> {
// 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(|_| ())
}

/// Ignores the SIGINT signal.
#[cfg(unix)]
pub fn ignore_interrupts() -> Result<(), Errno> {
Expand Down
20 changes: 20 additions & 0 deletions src/uucore_procs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,34 @@ use quote::quote;
//* ref: [path construction from LitStr](https://oschwald.github.io/maxminddb-rust/syn/struct.LitStr.html) @@ <http://archive.is/8YDua>

/// A procedural macro to define the main function of a uutils binary.
///
/// This macro handles:
/// - SIGPIPE state capture at process startup (before Rust runtime overrides it)
/// - SIGPIPE restoration to default if parent didn't explicitly ignore it
/// - Disabling Rust signal handlers for proper core dumps
/// - Error handling and exit code management
#[proc_macro_attribute]
pub fn main(_args: TokenStream, stream: TokenStream) -> TokenStream {
let stream = proc_macro2::TokenStream::from(stream);

let new = quote!(
// Initialize SIGPIPE state capture at process startup (Unix only).
// This must be at module level to set up the .init_array static that runs
// before main() to capture whether SIGPIPE was ignored by the parent process.
#[cfg(unix)]
uucore::init_startup_state_capture!();

pub fn uumain(args: impl uucore::Args) -> i32 {
#stream

// Restore SIGPIPE to default if it wasn't explicitly ignored by parent.
// The Rust runtime ignores SIGPIPE, but we need to respect the parent's
// signal disposition for proper pipeline behavior (GNU compatibility).
#[cfg(unix)]
if !uucore::signals::sigpipe_was_ignored() {
let _ = uucore::signals::enable_pipe_errors();
}

// disable rust signal handlers (otherwise processes don't dump core after e.g. one SIGSEGV)
#[cfg(unix)]
uucore::disable_rust_signal_handlers().expect("Disabling rust signal handlers failed");
Expand Down
Loading