diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index 444f4d42fb1..c8946d98c1e 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -40,6 +40,15 @@ 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(); @@ -541,7 +550,16 @@ fn unbounded_tail(reader: &mut BufReader, settings: &Settings) -> UR } _ => {} } + #[cfg(not(target_os = "windows"))] writer.flush()?; + + // SIGPIPE is not available on Windows. + #[cfg(target_os = "windows")] + writer.flush().inspect_err(|err| { + if err.kind() == ErrorKind::BrokenPipe { + std::process::exit(13); + } + })?; Ok(()) } diff --git a/tests/by-util/test_tail.rs b/tests/by-util/test_tail.rs index 11f20ec1e49..ec4f46401d2 100644 --- a/tests/by-util/test_tail.rs +++ b/tests/by-util/test_tail.rs @@ -4897,6 +4897,19 @@ fn test_when_piped_input_then_no_broken_pipe() { } } +#[test] +fn test_when_output_closed_then_no_broken_pie() { + let mut cmd = new_ucmd!(); + let mut child = cmd + .args(&[FOOBAR_TXT]) + .set_stdout(Stdio::piped()) + .run_no_wait(); + // Dropping the stdout should not lead to an error. + // The "Broken pipe" error should be silently ignored. + child.close_stdout(); + child.wait().unwrap().fails_silently(); +} + #[test] fn test_child_when_run_with_stderr_to_stdout() { let ts = TestScenario::new("tail"); diff --git a/tests/uutests/src/lib/util.rs b/tests/uutests/src/lib/util.rs index 981bce8e282..3fa3ed47755 100644 --- a/tests/uutests/src/lib/util.rs +++ b/tests/uutests/src/lib/util.rs @@ -695,7 +695,11 @@ impl CmdResult { #[track_caller] pub fn fails_silently(&self) -> &Self { assert!(!self.succeeded()); - assert!(self.stderr.is_empty()); + assert!( + self.stderr.is_empty(), + "Expected stderr to be empty, but it's:\n{}", + self.stderr_str() + ); self }