Skip to content

Commit 685969e

Browse files
authored
Merge pull request #8327 from tgrez/main
tail: handle broken pipe gracefully
2 parents 2440278 + f04ed45 commit 685969e

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

src/uu/tail/src/tail.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ 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+
4352
let settings = parse_args(args)?;
4453

4554
settings.check_warnings();
@@ -541,7 +550,16 @@ fn unbounded_tail<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UR
541550
}
542551
_ => {}
543552
}
553+
#[cfg(not(target_os = "windows"))]
544554
writer.flush()?;
555+
556+
// SIGPIPE is not available on Windows.
557+
#[cfg(target_os = "windows")]
558+
writer.flush().inspect_err(|err| {
559+
if err.kind() == ErrorKind::BrokenPipe {
560+
std::process::exit(13);
561+
}
562+
})?;
545563
Ok(())
546564
}
547565

tests/by-util/test_tail.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4897,6 +4897,19 @@ fn test_when_piped_input_then_no_broken_pipe() {
48974897
}
48984898
}
48994899

4900+
#[test]
4901+
fn test_when_output_closed_then_no_broken_pie() {
4902+
let mut cmd = new_ucmd!();
4903+
let mut child = cmd
4904+
.args(&[FOOBAR_TXT])
4905+
.set_stdout(Stdio::piped())
4906+
.run_no_wait();
4907+
// Dropping the stdout should not lead to an error.
4908+
// The "Broken pipe" error should be silently ignored.
4909+
child.close_stdout();
4910+
child.wait().unwrap().fails_silently();
4911+
}
4912+
49004913
#[test]
49014914
fn test_child_when_run_with_stderr_to_stdout() {
49024915
let ts = TestScenario::new("tail");

tests/uutests/src/lib/util.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,11 @@ impl CmdResult {
695695
#[track_caller]
696696
pub fn fails_silently(&self) -> &Self {
697697
assert!(!self.succeeded());
698-
assert!(self.stderr.is_empty());
698+
assert!(
699+
self.stderr.is_empty(),
700+
"Expected stderr to be empty, but it's:\n{}",
701+
self.stderr_str()
702+
);
699703
self
700704
}
701705

0 commit comments

Comments
 (0)