use std::fs::File;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::os::unix::process::CommandExt;
use std::process::Command;
const DIAG_FD: RawFd = 3;
pub struct DiagPipe {
pub read: File,
pub write: OwnedFd,
}
pub fn prepare_diag_pipe(cmd: &mut Command) -> std::io::Result<DiagPipe> {
let (read, write) = make_pipe(libc::O_CLOEXEC)?;
let write_raw = write.as_raw_fd();
unsafe {
cmd.pre_exec(move || dup_fd(write_raw, DIAG_FD));
}
cmd.env("BALLS_DIAG_FD", DIAG_FD.to_string());
Ok(DiagPipe { read, write })
}
fn make_pipe(flags: libc::c_int) -> std::io::Result<(File, OwnedFd)> {
let mut fds = [0 as libc::c_int; 2];
if unsafe { libc::pipe2(fds.as_mut_ptr(), flags) } != 0 {
return Err(std::io::Error::last_os_error());
}
Ok(unsafe { (File::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])) })
}
fn dup_fd(from: RawFd, to: RawFd) -> std::io::Result<()> {
if unsafe { libc::dup2(from, to) } == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Read, Write};
#[test]
fn make_pipe_success_returns_usable_endpoints() {
let (mut r, w) = make_pipe(libc::O_CLOEXEC).expect("real pipe");
let w_raw = w.as_raw_fd();
let mut w_file = unsafe { File::from_raw_fd(w_raw) };
std::mem::forget(w);
w_file.write_all(b"ping").unwrap();
drop(w_file);
let mut buf = Vec::new();
r.read_to_end(&mut buf).unwrap();
assert_eq!(buf, b"ping");
}
#[test]
fn make_pipe_error_branch_triggers_on_bad_flags() {
let err = make_pipe(-1).expect_err("bad flags must error");
assert!(err.raw_os_error().is_some());
}
#[test]
fn dup_fd_success_duplicates_descriptor() {
const TARGET: RawFd = 900;
dup_fd(0, TARGET).expect("dup2 to unused fd");
unsafe {
libc::close(TARGET);
}
}
#[test]
fn dup_fd_error_branch_triggers_on_bad_source() {
let err = dup_fd(-1, 901).expect_err("bad source fd must error");
assert!(err.raw_os_error().is_some());
}
}