From f910c40bd081c45ce69141c80562103d5e5dcdd8 Mon Sep 17 00:00:00 2001 From: Ed Smith Date: Sat, 25 Jun 2022 23:16:49 +0100 Subject: [PATCH] Do not trap pipe errors in yes This is part of fixing the tee tests. 'yes' is used by the GNU test suite to identify what the SIGPIPE exit code is on the target platform. By trapping SIGPIPE, it creates a requirement that other utilities also trap SIGPIPE (and exit 0 after SIGPIPE). This is sometimes at odds with their desired behaviour. --- Cargo.lock | 1 + src/uu/yes/Cargo.toml | 1 + src/uu/yes/src/yes.rs | 19 ++++++++++++++++++- tests/by-util/test_yes.rs | 16 +++++++++++++++- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d52f9b1d98..ee8a6232aef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3096,6 +3096,7 @@ name = "uu_yes" version = "0.0.14" dependencies = [ "clap 3.1.18", + "libc", "nix", "uucore", ] diff --git a/src/uu/yes/Cargo.toml b/src/uu/yes/Cargo.toml index e2c6d8450c8..6aaa83371a5 100644 --- a/src/uu/yes/Cargo.toml +++ b/src/uu/yes/Cargo.toml @@ -16,6 +16,7 @@ path = "src/yes.rs" [dependencies] clap = { version = "3.1", features = ["wrap_help", "cargo"] } +libc = "0.2.126" uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["pipes"] } [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] diff --git a/src/uu/yes/src/yes.rs b/src/uu/yes/src/yes.rs index 879f60579ca..f78ddbfb61d 100644 --- a/src/uu/yes/src/yes.rs +++ b/src/uu/yes/src/yes.rs @@ -8,7 +8,7 @@ /* last synced with: yes (GNU coreutils) 8.13 */ use std::borrow::Cow; -use std::io::{self, Write}; +use std::io::{self, Result, Write}; #[macro_use] extern crate clap; @@ -70,10 +70,27 @@ fn prepare_buffer<'a>(input: &'a str, buffer: &'a mut [u8; BUF_SIZE]) -> &'a [u8 } } +#[cfg(unix)] +fn enable_pipe_errors() -> Result<()> { + let ret = unsafe { libc::signal(libc::SIGPIPE, libc::SIG_DFL) }; + if ret == libc::SIG_ERR { + return Err(io::Error::new(io::ErrorKind::Other, "")); + } + Ok(()) +} + +#[cfg(not(unix))] +fn enable_pipe_errors() -> Result<()> { + // Do nothing. + Ok(()) +} + pub fn exec(bytes: &[u8]) -> io::Result<()> { let stdout = io::stdout(); let mut stdout = stdout.lock(); + enable_pipe_errors()?; + #[cfg(any(target_os = "linux", target_os = "android"))] { match splice::splice_data(bytes, &stdout) { diff --git a/tests/by-util/test_yes.rs b/tests/by-util/test_yes.rs index 7325cb4bcf0..20561ced667 100644 --- a/tests/by-util/test_yes.rs +++ b/tests/by-util/test_yes.rs @@ -1,7 +1,21 @@ use std::io::Read; +use std::process::ExitStatus; + +#[cfg(unix)] +use std::os::unix::process::ExitStatusExt; use crate::common::util::*; +#[cfg(unix)] +fn check_termination(result: &ExitStatus) { + assert_eq!(result.signal(), Some(libc::SIGPIPE as i32)); +} + +#[cfg(not(unix))] +fn check_termination(result: &ExitStatus) { + assert!(result.success(), "yes did not exit successfully"); +} + /// Run `yes`, capture some of the output, close the pipe, and verify it. fn run(args: &[&str], expected: &[u8]) { let mut cmd = new_ucmd!(); @@ -10,7 +24,7 @@ fn run(args: &[&str], expected: &[u8]) { let mut buf = vec![0; expected.len()]; stdout.read_exact(&mut buf).unwrap(); drop(stdout); - assert!(child.wait().unwrap().success()); + check_termination(&child.wait().unwrap()); assert_eq!(buf.as_slice(), expected); }