rust/tests/ui/process/no-stdio.rs

140 lines
3.6 KiB
Rust

//@ run-pass
//@ ignore-android
//@ ignore-wasm32 no processes
//@ ignore-sgx no processes
#![feature(rustc_private)]
#[cfg(unix)]
extern crate libc;
use std::env;
use std::ffi::c_int;
use std::io::{self, Read, Write};
use std::process::{Command, Stdio};
#[cfg(unix)]
unsafe fn without_stdio<R, F: FnOnce() -> R>(f: F) -> R {
let doit = |a| {
let r = libc::dup(a);
assert!(r >= 0);
return r
};
let a = doit(0);
let b = doit(1);
let c = doit(2);
assert!(libc::close(0) >= 0);
assert!(libc::close(1) >= 0);
assert!(libc::close(2) >= 0);
let r = f();
assert!(libc::dup2(a, 0) >= 0);
assert!(libc::dup2(b, 1) >= 0);
assert!(libc::dup2(c, 2) >= 0);
return r
}
#[cfg(unix)]
fn assert_fd_is_valid(fd: c_int) {
if unsafe { libc::fcntl(fd, libc::F_GETFD) == -1 } {
panic!("file descriptor {} is not valid: {}", fd, io::Error::last_os_error());
}
}
#[cfg(windows)]
fn assert_fd_is_valid(_fd: c_int) {}
#[cfg(windows)]
unsafe fn without_stdio<R, F: FnOnce() -> R>(f: F) -> R {
type DWORD = u32;
type HANDLE = *mut u8;
type BOOL = i32;
const STD_INPUT_HANDLE: DWORD = -10i32 as DWORD;
const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
const INVALID_HANDLE_VALUE: HANDLE = !0 as HANDLE;
extern "system" {
fn GetStdHandle(which: DWORD) -> HANDLE;
fn SetStdHandle(which: DWORD, handle: HANDLE) -> BOOL;
}
let doit = |id| {
let handle = GetStdHandle(id);
assert!(handle != INVALID_HANDLE_VALUE);
assert!(SetStdHandle(id, INVALID_HANDLE_VALUE) != 0);
return handle
};
let a = doit(STD_INPUT_HANDLE);
let b = doit(STD_OUTPUT_HANDLE);
let c = doit(STD_ERROR_HANDLE);
let r = f();
let doit = |id, handle| {
assert!(SetStdHandle(id, handle) != 0);
};
doit(STD_INPUT_HANDLE, a);
doit(STD_OUTPUT_HANDLE, b);
doit(STD_ERROR_HANDLE, c);
return r
}
fn main() {
if env::args().len() > 1 {
// Writing to stdout & stderr should not panic.
println!("test");
assert!(io::stdout().write(b"test\n").is_ok());
assert!(io::stderr().write(b"test\n").is_ok());
// Stdin should be at EOF.
assert_eq!(io::stdin().read(&mut [0; 10]).unwrap(), 0);
// Standard file descriptors should be valid on UNIX:
assert_fd_is_valid(0);
assert_fd_is_valid(1);
assert_fd_is_valid(2);
return
}
// First, make sure reads/writes without stdio work if stdio itself is
// missing.
let (a, b, c) = unsafe {
without_stdio(|| {
let a = io::stdout().write(b"test\n");
let b = io::stderr().write(b"test\n");
let c = io::stdin().read(&mut [0; 10]);
(a, b, c)
})
};
assert_eq!(a.unwrap(), 5);
assert_eq!(b.unwrap(), 5);
assert_eq!(c.unwrap(), 0);
// Second, spawn a child and do some work with "null" descriptors to make
// sure it's ok
let me = env::current_exe().unwrap();
let status = Command::new(&me)
.arg("next")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status().unwrap();
assert!(status.success(), "{} isn't a success", status);
// Finally, close everything then spawn a child to make sure everything is
// *still* ok.
let status = unsafe {
without_stdio(|| Command::new(&me).arg("next").status())
}.unwrap();
assert!(status.success(), "{} isn't a success", status);
}