Skip to content

Commit 01ba47a

Browse files
committed
yes: print error when SIGPIPE is trapped
Fixes #7252
1 parent b4423b9 commit 01ba47a

File tree

2 files changed

+20
-9
lines changed

2 files changed

+20
-9
lines changed

src/uu/yes/src/yes.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ use std::ffi::OsString;
1111
use std::io::{self, Write};
1212
use uucore::error::{UResult, USimpleError};
1313
use uucore::format_usage;
14-
#[cfg(unix)]
15-
use uucore::signals::enable_pipe_errors;
1614
use uucore::translate;
1715

1816
// it's possible that using a smaller or larger buffer might provide better performance on some
@@ -29,7 +27,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
2927

3028
match exec(&buffer) {
3129
Ok(()) => Ok(()),
32-
Err(err) if err.kind() == io::ErrorKind::BrokenPipe => Ok(()),
30+
Err(err) if err.kind() == io::ErrorKind::BrokenPipe => {
31+
// When SIGPIPE is trapped (SIG_IGN), write operations return EPIPE.
32+
// GNU coreutils prints an error message in this case.
33+
eprintln!("yes: stdout: Broken pipe");
34+
Ok(())
35+
}
3336
Err(err) => Err(USimpleError::new(
3437
1,
3538
translate!("yes-error-standard-output", "error" => err),
@@ -113,8 +116,16 @@ fn prepare_buffer(buf: &mut Vec<u8>) {
113116
pub fn exec(bytes: &[u8]) -> io::Result<()> {
114117
let stdout = io::stdout();
115118
let mut stdout = stdout.lock();
116-
#[cfg(unix)]
117-
enable_pipe_errors()?;
119+
120+
// SIGPIPE handling:
121+
// Rust ignores SIGPIPE by default (rust-lang/rust#62569). When write() fails
122+
// because the pipe is closed, it returns EPIPE error instead of being killed by
123+
// the signal. We catch this error in uumain() and print a diagnostic message,
124+
// matching GNU coreutils behavior.
125+
//
126+
// Key point: We do NOT restore SIGPIPE to SIG_DFL. This preserves POSIX signal
127+
// inheritance semantics - if the parent set SIGPIPE to SIG_IGN (e.g., `trap '' PIPE`),
128+
// we respect that setting and rely on EPIPE error handling.
118129

119130
loop {
120131
stdout.write_all(bytes)?;

tests/by-util/test_yes.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
use std::ffi::OsStr;
66
use std::process::ExitStatus;
77

8-
#[cfg(unix)]
9-
use std::os::unix::process::ExitStatusExt;
10-
118
use uutests::new_ucmd;
129

1310
#[cfg(unix)]
1411
fn check_termination(result: ExitStatus) {
15-
assert_eq!(result.signal(), Some(libc::SIGPIPE));
12+
// yes should exit successfully (code 0) when the pipe breaks.
13+
// Rust ignores SIGPIPE by default, so write() returns EPIPE error,
14+
// which is caught and handled gracefully. This matches GNU coreutils behavior.
15+
assert!(result.success(), "yes should exit successfully");
1616
}
1717

1818
#[cfg(not(unix))]

0 commit comments

Comments
 (0)