use std::ffi::OsStr;
use std::io::{Read, Write};
use std::mem;
#[cfg(unix)]
use std::os::unix::process::CommandExt;
use std::process::{Command, Stdio};
use std::ptr;
#[doc(hidden)]
pub trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}
macro_rules! impl_is_minus_one {
($($t:ident)*) => ($(impl IsMinusOne for $t {
fn is_minus_one(&self) -> bool {
*self == -1
}
})*)
}
impl_is_minus_one! { i8 i16 i32 i64 isize }
pub fn cvt<T: IsMinusOne>(t: T) -> std::io::Result<T> {
if t.is_minus_one() {
Err(std::io::Error::last_os_error())
} else {
Ok(t)
}
}
pub fn cvt_r<T, F>(mut f: F) -> std::io::Result<T>
where
T: IsMinusOne,
F: FnMut() -> T,
{
loop {
match cvt(f()) {
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
other => return other,
}
}
}
#[allow(dead_code)] pub fn cvt_nz(error: libc::c_int) -> std::io::Result<()> {
if error == 0 {
Ok(())
} else {
Err(std::io::Error::from_raw_os_error(error))
}
}
macro_rules! t {
($e:expr) => {
match $e {
Ok(t) => t,
Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
}
};
}
#[test]
#[cfg_attr(
any(
// See #14232 for more information, but it appears that signal delivery to a
// newly spawned process may just be raced in the macOS, so to prevent this
// test from being flaky we ignore it on macOS.
target_os = "macos",
// When run under our current QEMU emulation test suite this test fails,
// although the reason isn't very clear as to why. For now this test is
// ignored there.
target_arch = "arm",
target_arch = "aarch64",
target_arch = "riscv64",
),
ignore
)]
fn test_process_mask() {
fn test_inner(mut cmd: Command) {
unsafe {
let mut set = mem::MaybeUninit::<libc::sigset_t>::uninit();
let mut old_set = mem::MaybeUninit::<libc::sigset_t>::uninit();
t!(cvt(libc::sigemptyset(set.as_mut_ptr())));
t!(cvt(libc::sigaddset(set.as_mut_ptr(), libc::SIGINT)));
t!(cvt_nz(libc::pthread_sigmask(
libc::SIG_SETMASK,
set.as_ptr(),
old_set.as_mut_ptr()
)));
cmd.stdin(Stdio::piped());
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::null());
let mut child = t!(cmd.spawn());
let mut stdin_write = child.stdin.take().unwrap();
let mut stdout_read = child.stdout.take().unwrap();
t!(cvt_nz(libc::pthread_sigmask(
libc::SIG_SETMASK,
old_set.as_ptr(),
ptr::null_mut()
)));
t!(cvt(libc::kill(child.id() as libc::pid_t, libc::SIGINT)));
let _ = stdin_write.write(b"Hello");
drop(stdin_write);
let mut buf = [0; 5];
let ret = t!(stdout_read.read(&mut buf));
assert_eq!(ret, 5);
assert_eq!(&buf, b"Hello");
t!(child.wait());
}
}
let cmd = Command::new(OsStr::new("cat"));
test_inner(cmd);
let mut cmd = Command::new(OsStr::new("cat"));
unsafe { cmd.pre_exec(Box::new(|| Ok(()))) };
test_inner(cmd);
}
#[test]
#[cfg_attr(
any(
// See test_process_mask
target_os = "macos",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "riscv64",
),
ignore
)]
fn test_process_group_posix_spawn() {
unsafe {
let mut cmd = Command::new(OsStr::new("cat"));
cmd.process_group(0);
cmd.stdin(Stdio::piped());
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::null());
let mut child = t!(cmd.spawn());
t!(cvt(libc::kill(-(child.id() as libc::pid_t), libc::SIGINT)));
t!(child.wait());
}
}
#[test]
#[cfg_attr(
any(
// See test_process_mask
target_os = "macos",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "riscv64",
),
ignore
)]
fn test_process_group_no_posix_spawn() {
unsafe {
let mut cmd = Command::new(OsStr::new("cat"));
cmd.process_group(0);
cmd.pre_exec(Box::new(|| Ok(()))); cmd.stdin(Stdio::piped());
cmd.stdout(Stdio::piped());
cmd.stderr(Stdio::null());
let mut child = t!(cmd.spawn());
t!(cvt(libc::kill(-(child.id() as libc::pid_t), libc::SIGINT)));
t!(child.wait());
}
}