Skip to content

Commit

Permalink
Vendor dependency os_pipe (#822)
Browse files Browse the repository at this point in the history
Co-authored-by: Josh Triplett <josh@joshtriplett.org>
  • Loading branch information
NobodyXu and joshtriplett authored Jul 20, 2023
1 parent 163eb8e commit b030a29
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 16 deletions.
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ edition = "2018"

[dependencies]
jobserver = { version = "0.1.16", optional = true }
os_pipe = "1"

[target.'cfg(unix)'.dependencies]
libc = "0.2.62"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.48.0", features = ["Win32_Foundation", "Win32_System_Pipes", "Win32_Security"] }

[features]
parallel = ["jobserver"]
Expand Down
24 changes: 9 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,16 @@ use std::collections::{hash_map, HashMap};
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt::{self, Display, Formatter};
use std::fs;
use std::fs::{self, File};
use std::hash::Hasher;
use std::io::{self, BufRead, BufReader, Read, Write};
use std::path::{Component, Path, PathBuf};
use std::process::{Child, Command, Stdio};
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};

mod os_pipe;

// These modules are all glue to support reading the MSVC version from
// the registry and from COM interfaces
#[cfg(windows)]
Expand Down Expand Up @@ -3494,11 +3496,7 @@ fn wait_on_child(cmd: &Command, program: &str, child: &mut Child) -> Result<(),
}
}

fn run_inner(
cmd: &mut Command,
program: &str,
pipe_writer: os_pipe::PipeWriter,
) -> Result<(), Error> {
fn run_inner(cmd: &mut Command, program: &str, pipe_writer: File) -> Result<(), Error> {
let mut child = spawn(cmd, program, pipe_writer)?;
wait_on_child(cmd, program, &mut child)
}
Expand Down Expand Up @@ -3529,11 +3527,7 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {
Ok(stdout)
}

fn spawn(
cmd: &mut Command,
program: &str,
pipe_writer: os_pipe::PipeWriter,
) -> Result<Child, Error> {
fn spawn(cmd: &mut Command, program: &str, pipe_writer: File) -> Result<Child, Error> {
struct ResetStderr<'cmd>(&'cmd mut Command);

impl Drop for ResetStderr<'_> {
Expand Down Expand Up @@ -3773,7 +3767,7 @@ impl AsmFileExt {

struct PrintThread {
handle: Option<JoinHandle<()>>,
pipe_writer: Option<os_pipe::PipeWriter>,
pipe_writer: Option<File>,
}

impl PrintThread {
Expand Down Expand Up @@ -3804,14 +3798,14 @@ impl PrintThread {
})
}

fn pipe_writer(&mut self) -> &mut Option<os_pipe::PipeWriter> {
fn pipe_writer(&mut self) -> &mut Option<File> {
&mut self.pipe_writer
}

fn pipe_writer_cloned(&self) -> Result<Option<os_pipe::PipeWriter>, Error> {
fn pipe_writer_cloned(&self) -> Result<Option<File>, Error> {
self.pipe_writer
.as_ref()
.map(os_pipe::PipeWriter::try_clone)
.map(File::try_clone)
.transpose()
.map_err(From::from)
}
Expand Down
28 changes: 28 additions & 0 deletions src/os_pipe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! Adapted from:
//! - https://doc.rust-lang.org/src/std/sys/unix/pipe.rs.html
//! - https://doc.rust-lang.org/src/std/sys/unix/fd.rs.html#385
//! - https://github.com/rust-lang/rust/blob/master/library/std/src/sys/mod.rs#L57
//! - https://github.com/oconnor663/os_pipe.rs
use std::fs::File;

/// Open a new pipe and return a pair of [`File`] objects for the reader and writer.
///
/// This corresponds to the `pipe2` library call on Posix and the
/// `CreatePipe` library call on Windows (though these implementation
/// details might change). These pipes are non-inheritable, so new child
/// processes won't receive a copy of them unless they're explicitly
/// passed as stdin/stdout/stderr.
pub fn pipe() -> std::io::Result<(File, File)> {
sys::pipe()
}

#[cfg(unix)]
#[path = "os_pipe/unix.rs"]
mod sys;

#[cfg(windows)]
#[path = "os_pipe/windows.rs"]
mod sys;

#[cfg(all(not(unix), not(windows)))]
compile_error!("Only unix and windows support os_pipe!");
121 changes: 121 additions & 0 deletions src/os_pipe/unix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
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<c_int> {
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(())
}
}
24 changes: 24 additions & 0 deletions src/os_pipe/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::{fs::File, io, os::windows::prelude::*, ptr};
use windows_sys::Win32::{Foundation::INVALID_HANDLE_VALUE, System::Pipes::CreatePipe};

/// NOTE: These pipes do not support IOCP.
///
/// If IOCP is needed, then you might want to emulate
/// anonymous pipes with CreateNamedPipe, as Rust's stdlib does.
pub(super) fn pipe() -> io::Result<(File, File)> {
let mut read_pipe = INVALID_HANDLE_VALUE;
let mut write_pipe = INVALID_HANDLE_VALUE;

let ret = unsafe { CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) };

if ret == 0 {
Err(io::Error::last_os_error())
} else {
unsafe {
Ok((
File::from_raw_handle(read_pipe as RawHandle),
File::from_raw_handle(write_pipe as RawHandle),
))
}
}
}

0 comments on commit b030a29

Please sign in to comment.