mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
cat: handle broken pipe gracefully
This commit is contained in:
+27
-8
@@ -8,8 +8,10 @@
|
||||
mod platform;
|
||||
|
||||
use crate::platform::is_unsafe_overwrite;
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use memchr::memchr2;
|
||||
use std::fs::{File, metadata};
|
||||
use std::io::{self, BufWriter, IsTerminal, Read, Write};
|
||||
use std::io::{self, BufWriter, ErrorKind, IsTerminal, Read, Write};
|
||||
/// Unix domain socket support
|
||||
#[cfg(unix)]
|
||||
use std::net::Shutdown;
|
||||
@@ -19,12 +21,11 @@ use std::os::fd::AsFd;
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use memchr::memchr2;
|
||||
use thiserror::Error;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::UResult;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use uucore::libc;
|
||||
use uucore::locale::get_message;
|
||||
use uucore::{fast_inc::fast_inc_one, format_usage};
|
||||
|
||||
@@ -220,6 +221,15 @@ mod options {
|
||||
|
||||
#[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 matches = uu_app().try_get_matches_from(args)?;
|
||||
|
||||
let number_mode = if matches.get_flag(options::NUMBER_NONBLANK) {
|
||||
@@ -502,7 +512,9 @@ fn write_fast<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
stdout_lock.write_all(&buf[..n])?;
|
||||
stdout_lock
|
||||
.write_all(&buf[..n])
|
||||
.inspect_err(handle_broken_pipe)?;
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
@@ -513,7 +525,7 @@ fn write_fast<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
|
||||
// that will succeed, data pushed through splice will be output before
|
||||
// the data buffered in stdout.lock. Therefore additional explicit flush
|
||||
// is required here.
|
||||
stdout_lock.flush()?;
|
||||
stdout_lock.flush().inspect_err(handle_broken_pipe)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -584,7 +596,7 @@ fn write_lines<R: FdReadable>(
|
||||
// and not be buffered internally to the `cat` process.
|
||||
// Hence it's necessary to flush our buffer before every time we could potentially block
|
||||
// on a `std::io::Read::read` call.
|
||||
writer.flush()?;
|
||||
writer.flush().inspect_err(handle_broken_pipe)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -704,11 +716,18 @@ fn write_end_of_line<W: Write>(
|
||||
) -> CatResult<()> {
|
||||
writer.write_all(end_of_line)?;
|
||||
if is_interactive {
|
||||
writer.flush()?;
|
||||
writer.flush().inspect_err(handle_broken_pipe)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_broken_pipe(error: &io::Error) {
|
||||
// SIGPIPE is not available on Windows.
|
||||
if cfg!(target_os = "windows") && error.kind() == ErrorKind::BrokenPipe {
|
||||
std::process::exit(13);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::{BufWriter, stdout};
|
||||
|
||||
@@ -119,6 +119,19 @@ fn test_closes_file_descriptors() {
|
||||
.succeeds();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_broken_pipe() {
|
||||
let mut cmd = new_ucmd!();
|
||||
let mut child = cmd
|
||||
.args(&["alpha.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]
|
||||
#[cfg(unix)]
|
||||
fn test_piped_to_regular_file() {
|
||||
|
||||
Reference in New Issue
Block a user