Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vendor dependency os_pipe #822

Merged
merged 4 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:/rust-lang/rust/blob/master/library/std/src/sys/mod.rs#L57
//! - https:/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),
))
}
}
}