Skip to content

Commit f04ed45

Browse files
committed
tail: handle broken pipe gracefully
1 parent a060344 commit f04ed45

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
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");

0 commit comments

Comments
 (0)