use std::{ fs::File, io, os::{raw::c_int, unix::io::FromRawFd}, }; pub(super) fn pipe() -> io::Result<(File, File)> { let mut fds = [0; 2]; // The only known way right now to create atomically set the CLOEXEC flag is // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9 // and musl 0.9.3, and some other targets also have it. #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", target_os = "redox" ))] { unsafe { cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?; } } #[cfg(not(any( target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", target_os = "redox" )))] { unsafe { cvt(libc::pipe(fds.as_mut_ptr()))?; } cloexec::set_cloexec(fds[0])?; cloexec::set_cloexec(fds[1])?; } unsafe { Ok((File::from_raw_fd(fds[0]), File::from_raw_fd(fds[1]))) } } fn cvt(t: c_int) -> io::Result { if t == -1 { Err(io::Error::last_os_error()) } else { Ok(t) } } #[cfg(not(any( target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", target_os = "redox" )))] mod cloexec { use super::{c_int, cvt, io}; #[cfg(not(any( target_env = "newlib", target_os = "solaris", target_os = "illumos", target_os = "emscripten", target_os = "fuchsia", target_os = "l4re", target_os = "linux", target_os = "haiku", target_os = "redox", target_os = "vxworks", target_os = "nto", )))] pub(super) fn set_cloexec(fd: c_int) -> io::Result<()> { unsafe { cvt(libc::ioctl(fd, libc::FIOCLEX))?; } Ok(()) } #[cfg(any( all( target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon")) ), target_os = "solaris", target_os = "illumos", target_os = "emscripten", target_os = "fuchsia", target_os = "l4re", target_os = "linux", target_os = "haiku", target_os = "redox", target_os = "vxworks", target_os = "nto", ))] pub(super) fn set_cloexec(fd: c_int) -> io::Result<()> { unsafe { let previous = cvt(libc::fcntl(fd, libc::F_GETFD))?; let new = previous | libc::FD_CLOEXEC; if new != previous { cvt(libc::fcntl(fd, libc::F_SETFD, new))?; } } Ok(()) } // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to, // because neither supports spawning processes. #[cfg(any(target_os = "espidf", target_os = "horizon"))] pub(super) fn set_cloexec(_fd: c_int) -> io::Result<()> { Ok(()) } }