#[cfg(unix)]
pub(crate) fn ignore_sigpipe() {
let prev = unsafe { libc::signal(libc::SIGPIPE, libc::SIG_IGN) };
debug_assert_ne!(
prev,
libc::SIG_ERR,
"failed to set SIGPIPE to SIG_IGN: {}",
std::io::Error::last_os_error()
);
}
#[cfg(not(unix))]
pub(crate) fn ignore_sigpipe() {}
#[cfg(all(test, unix))]
mod tests {
use super::*;
use std::sync::Mutex;
static SIGPIPE_LOCK: Mutex<()> = Mutex::new(());
fn current_sigpipe_handler() -> libc::sighandler_t {
let mut oldact: libc::sigaction = unsafe { std::mem::zeroed() };
let rc = unsafe { libc::sigaction(libc::SIGPIPE, std::ptr::null(), &mut oldact) };
assert_eq!(
rc,
0,
"sigaction query failed: {}",
std::io::Error::last_os_error()
);
oldact.sa_sigaction
}
struct RestoreSigpipe(libc::sighandler_t);
impl RestoreSigpipe {
fn capture() -> Self {
Self(current_sigpipe_handler())
}
}
impl Drop for RestoreSigpipe {
fn drop(&mut self) {
unsafe {
libc::signal(libc::SIGPIPE, self.0);
}
}
}
#[test]
fn write_to_broken_pipe_returns_epipe_not_signal() {
let _lock = SIGPIPE_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let _restore = RestoreSigpipe::capture();
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
ignore_sigpipe();
let mut fds = [0 as libc::c_int; 2];
assert_eq!(
unsafe { libc::pipe(fds.as_mut_ptr()) },
0,
"pipe() failed: {}",
std::io::Error::last_os_error()
);
let (read_fd, write_fd) = (fds[0], fds[1]);
assert_eq!(unsafe { libc::close(read_fd) }, 0);
let buf = [0u8; 16];
let n = unsafe { libc::write(write_fd, buf.as_ptr() as *const libc::c_void, buf.len()) };
let err = std::io::Error::last_os_error();
unsafe {
libc::close(write_fd);
}
assert_eq!(n, -1, "write to broken pipe should fail");
assert_eq!(
err.raw_os_error(),
Some(libc::EPIPE),
"broken-pipe write should report EPIPE"
);
}
#[test]
fn ignore_sigpipe_installs_sig_ign_over_sig_dfl() {
let _lock = SIGPIPE_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let _restore = RestoreSigpipe::capture();
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
}
assert_eq!(current_sigpipe_handler(), libc::SIG_DFL);
ignore_sigpipe();
assert_eq!(current_sigpipe_handler(), libc::SIG_IGN);
}
}