Skip to content

Commit a9cade6

Browse files
committed
lib.rs: restore default SIGPIPE signal handler
Rust's start-up code sets the SIGPIPE signal handler to ignored. However the vast majority of the utilities should use the default signal handler for SIGPIPE. Instead of restoring the default signaler handler in individual utilities, do it in lib.rs and add some logic in the utilities which can't use the default SIGPIPE signal handler (cat, env, seq, split, tail, tee, tr, tty). Signed-off-by: Etienne Cordonnier <[email protected]>
1 parent 13c1624 commit a9cade6

File tree

15 files changed

+76
-47
lines changed

15 files changed

+76
-47
lines changed

src/uu/cat/src/cat.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ use std::os::unix::net::UnixStream;
2525
use thiserror::Error;
2626
use uucore::display::Quotable;
2727
use uucore::error::UResult;
28-
#[cfg(not(target_os = "windows"))]
29-
use uucore::libc;
3028
use uucore::translate;
3129
use uucore::{fast_inc::fast_inc_one, format_usage};
3230

@@ -222,15 +220,6 @@ mod options {
222220

223221
#[uucore::main]
224222
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
225-
// When we receive a SIGPIPE signal, we want to terminate the process so
226-
// that we don't print any error messages to stderr. Rust ignores SIGPIPE
227-
// (see https://github.com/rust-lang/rust/issues/62569), so we restore it's
228-
// default action here.
229-
#[cfg(not(target_os = "windows"))]
230-
unsafe {
231-
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
232-
}
233-
234223
let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;
235224

236225
let number_mode = if matches.get_flag(options::NUMBER_NONBLANK) {

src/uu/env/src/env.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ use native_int_str::{
1818
Convert, NCvt, NativeIntStr, NativeIntString, NativeStr, from_native_int_representation_owned,
1919
};
2020
#[cfg(unix)]
21-
use nix::libc;
22-
#[cfg(unix)]
2321
use nix::sys::signal::{SigHandler::SigIgn, Signal, signal};
2422
use std::borrow::Cow;
2523
use std::env;
@@ -845,12 +843,6 @@ fn ignore_signal(sig: Signal) -> UResult<()> {
845843

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

src/uu/seq/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ uucore = { workspace = true, features = [
3030
"format",
3131
"parser",
3232
"quoting-style",
33+
"signals",
3334
] }
3435
fluent = { workspace = true }
3536

src/uu/seq/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ pub enum SeqError {
3636
translate!("seq-error-format-and-equal-width")
3737
)]
3838
FormatAndEqualWidth,
39+
40+
/// Failed to set signal handler
41+
#[cfg(unix)]
42+
#[error("failed to set signal handler")]
43+
SignalHandler,
3944
}
4045

4146
fn parse_error_type(e: &ParseNumberError) -> String {

src/uu/seq/src/seq.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ use uucore::format::num_format::FloatVariant;
1717
use uucore::format::{Format, num_format};
1818
use uucore::{fast_inc::fast_inc, format_usage};
1919

20+
#[cfg(unix)]
21+
use uucore::signals::ignore_pipe;
22+
2023
mod error;
2124

2225
// public to allow fuzzing
@@ -92,6 +95,13 @@ fn select_precision(
9295

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

src/uu/split/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ path = "src/split.rs"
2020
[dependencies]
2121
clap = { workspace = true }
2222
memchr = { workspace = true }
23-
uucore = { workspace = true, features = ["fs", "parser-size"] }
23+
uucore = { workspace = true, features = ["fs", "parser-size", "signals"] }
2424
thiserror = { workspace = true }
2525
fluent = { workspace = true }
2626

src/uu/split/src/split.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ use uucore::parser::parse_size::parse_size_u64;
2929
use uucore::format_usage;
3030
use uucore::uio_error;
3131

32+
#[cfg(unix)]
33+
use uucore::signals::ignore_pipe;
34+
3235
static OPT_BYTES: &str = "bytes";
3336
static OPT_LINE_BYTES: &str = "line-bytes";
3437
static OPT_LINES: &str = "lines";
@@ -1530,6 +1533,15 @@ where
15301533

15311534
#[allow(clippy::cognitive_complexity)]
15321535
fn split(settings: &Settings) -> UResult<()> {
1536+
#[cfg(unix)]
1537+
{
1538+
// When using --filter, ignore SIGPIPE so that closure of one pipe
1539+
// does not terminate the process, as there may still be other streams
1540+
// expecting input from us. This matches GNU split behavior.
1541+
if settings.filter.is_some() {
1542+
ignore_pipe().map_err(|_| USimpleError::new(1, "failed to set signal handler"))?;
1543+
}
1544+
}
15331545
let r_box = if settings.input == "-" {
15341546
Box::new(stdin()) as Box<dyn Read>
15351547
} else {

src/uu/tail/src/tail.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,6 @@ use uucore::{show, show_error};
4040

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

5445
settings.check_warnings();

src/uu/tee/src/tee.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use uucore::{format_usage, show_error};
1919
// spell-checker:ignore nopipe
2020

2121
#[cfg(unix)]
22-
use uucore::signals::{enable_pipe_errors, ignore_interrupts};
22+
use uucore::signals::{enable_pipe_errors, ignore_interrupts, ignore_pipe};
2323

2424
mod options {
2525
pub const APPEND: &str = "append";
@@ -164,6 +164,9 @@ fn tee(options: &Options) -> Result<()> {
164164
}
165165
if options.output_error.is_none() {
166166
enable_pipe_errors().map_err(|_| Error::from(ErrorKind::Other))?;
167+
} else {
168+
// When --output-error is set, ignore SIGPIPE to handle broken pipes gracefully
169+
ignore_pipe().map_err(|_| Error::from(ErrorKind::Other))?;
167170
}
168171
}
169172
let mut writers: Vec<NamedWriter> = options

src/uu/tr/src/tr.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ use std::io::{stdin, stdout};
1818
use uucore::display::Quotable;
1919
use uucore::error::{UResult, USimpleError, UUsageError};
2020
use uucore::fs::is_stdin_directory;
21-
#[cfg(not(target_os = "windows"))]
22-
use uucore::libc;
2321
use uucore::translate;
2422
use uucore::{format_usage, os_str_as_bytes, show};
2523

@@ -33,15 +31,6 @@ mod options {
3331

3432
#[uucore::main]
3533
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
36-
// When we receive a SIGPIPE signal, we want to terminate the process so
37-
// that we don't print any error messages to stderr. Rust ignores SIGPIPE
38-
// (see https://github.com/rust-lang/rust/issues/62569), so we restore it's
39-
// default action here.
40-
#[cfg(not(target_os = "windows"))]
41-
unsafe {
42-
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
43-
}
44-
4534
let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;
4635

4736
let delete_flag = matches.get_flag(options::DELETE);

0 commit comments

Comments
 (0)