Bring native process bindings up to date
Move the tests into libstd, use the `iotest!` macro to test both native and uv bindings, and use the cloexec trick to figure out when the child process fails in exec.
This commit is contained in:
@@ -37,7 +37,7 @@ fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 {
|
|||||||
let mut ret;
|
let mut ret;
|
||||||
loop {
|
loop {
|
||||||
ret = f(data, amt);
|
ret = f(data, amt);
|
||||||
if cfg!(not(windows)) { break } // windows has no eintr
|
if cfg!(windows) { break } // windows has no eintr
|
||||||
// if we get an eintr, then try again
|
// if we get an eintr, then try again
|
||||||
if ret != -1 || os::errno() as int != eintr { break }
|
if ret != -1 || os::errno() as int != eintr { break }
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,10 @@ impl FileDesc {
|
|||||||
FileDesc { fd: fd, close_on_drop: close_on_drop }
|
FileDesc { fd: fd, close_on_drop: close_on_drop }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inner_read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
|
// FIXME(#10465) these functions should not be public, but anything in
|
||||||
|
// native::io wanting to use them is forced to have all the
|
||||||
|
// rtio traits in scope
|
||||||
|
pub fn inner_read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
|
||||||
#[cfg(windows)] type rlen = libc::c_uint;
|
#[cfg(windows)] type rlen = libc::c_uint;
|
||||||
#[cfg(not(windows))] type rlen = libc::size_t;
|
#[cfg(not(windows))] type rlen = libc::size_t;
|
||||||
let ret = keep_going(buf, |buf, len| {
|
let ret = keep_going(buf, |buf, len| {
|
||||||
@@ -899,7 +902,7 @@ pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{CFile, FileDesc};
|
use super::{CFile, FileDesc, CloseFd};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::libc;
|
use std::libc;
|
||||||
use std::os;
|
use std::os;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ fn unimpl() -> IoError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_error() -> IoError {
|
fn translate_error(errno: i32, detail: bool) -> IoError {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) {
|
fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) {
|
||||||
match errno {
|
match errno {
|
||||||
@@ -79,14 +79,16 @@ fn last_error() -> IoError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (kind, desc) = get_err(os::errno() as i32);
|
let (kind, desc) = get_err(errno);
|
||||||
IoError {
|
IoError {
|
||||||
kind: kind,
|
kind: kind,
|
||||||
desc: desc,
|
desc: desc,
|
||||||
detail: Some(os::last_os_error())
|
detail: if detail {Some(os::last_os_error())} else {None},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn last_error() -> IoError { translate_error(os::errno() as i32, true) }
|
||||||
|
|
||||||
// unix has nonzero values as errors
|
// unix has nonzero values as errors
|
||||||
fn mkerr_libc(ret: libc::c_int) -> IoResult<()> {
|
fn mkerr_libc(ret: libc::c_int) -> IoResult<()> {
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
@@ -106,6 +108,17 @@ fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn retry(f: || -> libc::c_int) -> IoResult<libc::c_int> {
|
||||||
|
loop {
|
||||||
|
match f() {
|
||||||
|
-1 if os::errno() as int == libc::EINTR as int => {}
|
||||||
|
-1 => return Err(last_error()),
|
||||||
|
n => return Ok(n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Implementation of rt::rtio's IoFactory trait to generate handles to the
|
/// Implementation of rt::rtio's IoFactory trait to generate handles to the
|
||||||
/// native I/O functionality.
|
/// native I/O functionality.
|
||||||
pub struct IoFactory;
|
pub struct IoFactory;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use p = std::io::process;
|
|||||||
|
|
||||||
#[cfg(windows)] use std::cast;
|
#[cfg(windows)] use std::cast;
|
||||||
|
|
||||||
|
use super::IoResult;
|
||||||
use super::file;
|
use super::file;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +38,7 @@ pub struct Process {
|
|||||||
priv handle: *(),
|
priv handle: *(),
|
||||||
|
|
||||||
/// None until finish() is called.
|
/// None until finish() is called.
|
||||||
priv exit_code: Option<int>,
|
priv exit_code: Option<p::ProcessExit>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
@@ -105,7 +106,13 @@ impl Process {
|
|||||||
for pipe in err_pipe.iter() { libc::close(pipe.out); }
|
for pipe in err_pipe.iter() { libc::close(pipe.out); }
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((Process { pid: res.pid, handle: res.handle, exit_code: None }, ret_io))
|
match res {
|
||||||
|
Ok(res) => {
|
||||||
|
Ok((Process { pid: res.pid, handle: res.handle, exit_code: None },
|
||||||
|
ret_io))
|
||||||
|
}
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,15 +120,14 @@ impl rtio::RtioProcess for Process {
|
|||||||
fn id(&self) -> pid_t { self.pid }
|
fn id(&self) -> pid_t { self.pid }
|
||||||
|
|
||||||
fn wait(&mut self) -> p::ProcessExit {
|
fn wait(&mut self) -> p::ProcessExit {
|
||||||
let code = match self.exit_code {
|
match self.exit_code {
|
||||||
Some(code) => code,
|
Some(code) => code,
|
||||||
None => {
|
None => {
|
||||||
let code = waitpid(self.pid);
|
let code = waitpid(self.pid);
|
||||||
self.exit_code = Some(code);
|
self.exit_code = Some(code);
|
||||||
code
|
code
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
return p::ExitStatus(code); // XXX: this is wrong
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kill(&mut self, signum: int) -> Result<(), io::IoError> {
|
fn kill(&mut self, signum: int) -> Result<(), io::IoError> {
|
||||||
@@ -177,7 +183,8 @@ struct SpawnProcessResult {
|
|||||||
fn spawn_process_os(prog: &str, args: &[~str],
|
fn spawn_process_os(prog: &str, args: &[~str],
|
||||||
env: Option<~[(~str, ~str)]>,
|
env: Option<~[(~str, ~str)]>,
|
||||||
dir: Option<&Path>,
|
dir: Option<&Path>,
|
||||||
in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult {
|
in_fd: c_int, out_fd: c_int,
|
||||||
|
err_fd: c_int) -> IoResult<SpawnProcessResult> {
|
||||||
use std::libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
|
use std::libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
|
||||||
use std::libc::consts::os::extra::{
|
use std::libc::consts::os::extra::{
|
||||||
TRUE, FALSE,
|
TRUE, FALSE,
|
||||||
@@ -241,7 +248,7 @@ fn spawn_process_os(prog: &str, args: &[~str],
|
|||||||
ptr::mut_null(), ptr::mut_null(), TRUE,
|
ptr::mut_null(), ptr::mut_null(), TRUE,
|
||||||
0, envp, dirp, &mut si, &mut pi);
|
0, envp, dirp, &mut si, &mut pi);
|
||||||
if created == FALSE {
|
if created == FALSE {
|
||||||
create_err = Some(os::last_os_error());
|
create_err = Some(super::last_error());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -251,21 +258,22 @@ fn spawn_process_os(prog: &str, args: &[~str],
|
|||||||
CloseHandle(si.hStdOutput);
|
CloseHandle(si.hStdOutput);
|
||||||
CloseHandle(si.hStdError);
|
CloseHandle(si.hStdError);
|
||||||
|
|
||||||
for msg in create_err.iter() {
|
match create_err {
|
||||||
fail!("failure in CreateProcess: {}", *msg);
|
Some(err) => return Err(err),
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We close the thread handle because std::we don't care about keeping the
|
// We close the thread handle because we don't care about keeping the
|
||||||
// thread id valid, and we aren't keeping the thread handle around to be
|
// thread id valid, and we aren't keeping the thread handle around to be
|
||||||
// able to close it later. We don't close the process handle however
|
// able to close it later. We don't close the process handle however
|
||||||
// because std::we want the process id to stay valid at least until the
|
// because std::we want the process id to stay valid at least until the
|
||||||
// calling code closes the process handle.
|
// calling code closes the process handle.
|
||||||
CloseHandle(pi.hThread);
|
CloseHandle(pi.hThread);
|
||||||
|
|
||||||
SpawnProcessResult {
|
Ok(SpawnProcessResult {
|
||||||
pid: pi.dwProcessId as pid_t,
|
pid: pi.dwProcessId as pid_t,
|
||||||
handle: pi.hProcess as *()
|
handle: pi.hProcess as *()
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,9 +311,8 @@ fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this is only pub so it can be tested (see issue #4536)
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn make_command_line(prog: &str, args: &[~str]) -> ~str {
|
fn make_command_line(prog: &str, args: &[~str]) -> ~str {
|
||||||
let mut cmd = ~"";
|
let mut cmd = ~"";
|
||||||
append_arg(&mut cmd, prog);
|
append_arg(&mut cmd, prog);
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
@@ -360,9 +367,12 @@ pub fn make_command_line(prog: &str, args: &[~str]) -> ~str {
|
|||||||
fn spawn_process_os(prog: &str, args: &[~str],
|
fn spawn_process_os(prog: &str, args: &[~str],
|
||||||
env: Option<~[(~str, ~str)]>,
|
env: Option<~[(~str, ~str)]>,
|
||||||
dir: Option<&Path>,
|
dir: Option<&Path>,
|
||||||
in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult {
|
in_fd: c_int, out_fd: c_int,
|
||||||
|
err_fd: c_int) -> IoResult<SpawnProcessResult> {
|
||||||
use std::libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
|
use std::libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
|
||||||
use std::libc::funcs::bsd44::getdtablesize;
|
use std::libc::funcs::bsd44::getdtablesize;
|
||||||
|
use std::libc::c_ulong;
|
||||||
|
use std::unstable::intrinsics;
|
||||||
|
|
||||||
mod rustrt {
|
mod rustrt {
|
||||||
extern {
|
extern {
|
||||||
@@ -370,46 +380,90 @@ fn spawn_process_os(prog: &str, args: &[~str],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
unsafe fn set_environ(_envp: *c_void) {}
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
unsafe fn set_environ(envp: *c_void) {
|
unsafe fn set_environ(envp: *c_void) {
|
||||||
extern { fn _NSGetEnviron() -> *mut *c_void; }
|
extern { fn _NSGetEnviron() -> *mut *c_void; }
|
||||||
|
|
||||||
*_NSGetEnviron() = envp;
|
*_NSGetEnviron() = envp;
|
||||||
}
|
}
|
||||||
#[cfg(not(target_os = "macos"), not(windows))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
unsafe fn set_environ(envp: *c_void) {
|
unsafe fn set_environ(envp: *c_void) {
|
||||||
extern {
|
extern { static mut environ: *c_void; }
|
||||||
static mut environ: *c_void;
|
|
||||||
}
|
|
||||||
environ = envp;
|
environ = envp;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe fn set_cloexec(fd: c_int) {
|
||||||
|
extern { fn ioctl(fd: c_int, req: c_ulong) -> c_int; }
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
static FIOCLEX: c_ulong = 0x20006601;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
static FIOCLEX: c_ulong = 0x5451;
|
||||||
|
|
||||||
|
let ret = ioctl(fd, FIOCLEX);
|
||||||
|
assert_eq!(ret, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pipe = os::pipe();
|
||||||
|
let mut input = file::FileDesc::new(pipe.input, file::CloseFd);
|
||||||
|
let mut output = file::FileDesc::new(pipe.out, file::CloseFd);
|
||||||
|
|
||||||
|
unsafe { set_cloexec(output.fd()) };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
let pid = fork();
|
let pid = fork();
|
||||||
if pid < 0 {
|
if pid < 0 {
|
||||||
fail!("failure in fork: {}", os::last_os_error());
|
fail!("failure in fork: {}", os::last_os_error());
|
||||||
} else if pid > 0 {
|
} else if pid > 0 {
|
||||||
return SpawnProcessResult {pid: pid, handle: ptr::null()};
|
drop(output);
|
||||||
|
let mut bytes = [0, ..4];
|
||||||
|
return match input.inner_read(bytes) {
|
||||||
|
Ok(4) => {
|
||||||
|
let errno = (bytes[0] << 24) as i32 |
|
||||||
|
(bytes[1] << 16) as i32 |
|
||||||
|
(bytes[2] << 8) as i32 |
|
||||||
|
(bytes[3] << 0) as i32;
|
||||||
|
Err(super::translate_error(errno, false))
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
assert!(e.kind == io::BrokenPipe ||
|
||||||
|
e.kind == io::EndOfFile,
|
||||||
|
"unexpected error: {:?}", e);
|
||||||
|
Ok(SpawnProcessResult {
|
||||||
|
pid: pid,
|
||||||
|
handle: ptr::null()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ok(..) => fail!("short read on the cloexec pipe"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
drop(input);
|
||||||
|
|
||||||
rustrt::rust_unset_sigprocmask();
|
rustrt::rust_unset_sigprocmask();
|
||||||
|
|
||||||
if dup2(in_fd, 0) == -1 {
|
if in_fd == -1 {
|
||||||
|
libc::close(libc::STDIN_FILENO);
|
||||||
|
} else if dup2(in_fd, 0) == -1 {
|
||||||
fail!("failure in dup2(in_fd, 0): {}", os::last_os_error());
|
fail!("failure in dup2(in_fd, 0): {}", os::last_os_error());
|
||||||
}
|
}
|
||||||
if dup2(out_fd, 1) == -1 {
|
if out_fd == -1 {
|
||||||
|
libc::close(libc::STDOUT_FILENO);
|
||||||
|
} else if dup2(out_fd, 1) == -1 {
|
||||||
fail!("failure in dup2(out_fd, 1): {}", os::last_os_error());
|
fail!("failure in dup2(out_fd, 1): {}", os::last_os_error());
|
||||||
}
|
}
|
||||||
if dup2(err_fd, 2) == -1 {
|
if err_fd == -1 {
|
||||||
|
libc::close(libc::STDERR_FILENO);
|
||||||
|
} else if dup2(err_fd, 2) == -1 {
|
||||||
fail!("failure in dup3(err_fd, 2): {}", os::last_os_error());
|
fail!("failure in dup3(err_fd, 2): {}", os::last_os_error());
|
||||||
}
|
}
|
||||||
// close all other fds
|
// close all other fds
|
||||||
for fd in range(3, getdtablesize()).invert() {
|
for fd in range(3, getdtablesize()).invert() {
|
||||||
|
if fd != output.fd() {
|
||||||
close(fd as c_int);
|
close(fd as c_int);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
with_dirp(dir, |dirp| {
|
with_dirp(dir, |dirp| {
|
||||||
if !dirp.is_null() && chdir(dirp) == -1 {
|
if !dirp.is_null() && chdir(dirp) == -1 {
|
||||||
@@ -421,11 +475,18 @@ fn spawn_process_os(prog: &str, args: &[~str],
|
|||||||
if !envp.is_null() {
|
if !envp.is_null() {
|
||||||
set_environ(envp);
|
set_environ(envp);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
with_argv(prog, args, |argv| {
|
with_argv(prog, args, |argv| {
|
||||||
execvp(*argv, argv);
|
execvp(*argv, argv);
|
||||||
// execvp only returns if an error occurred
|
let errno = os::errno();
|
||||||
fail!("failure in execvp: {}", os::last_os_error());
|
let bytes = [
|
||||||
})
|
(errno << 24) as u8,
|
||||||
|
(errno << 16) as u8,
|
||||||
|
(errno << 8) as u8,
|
||||||
|
(errno << 0) as u8,
|
||||||
|
];
|
||||||
|
output.inner_write(bytes);
|
||||||
|
intrinsics::abort();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -534,11 +595,11 @@ fn free_handle(_handle: *()) {
|
|||||||
* operate on a none-existent process or, even worse, on a newer process
|
* operate on a none-existent process or, even worse, on a newer process
|
||||||
* with the same id.
|
* with the same id.
|
||||||
*/
|
*/
|
||||||
fn waitpid(pid: pid_t) -> int {
|
fn waitpid(pid: pid_t) -> p::ProcessExit {
|
||||||
return waitpid_os(pid);
|
return waitpid_os(pid);
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn waitpid_os(pid: pid_t) -> int {
|
fn waitpid_os(pid: pid_t) -> p::ProcessExit {
|
||||||
use std::libc::types::os::arch::extra::DWORD;
|
use std::libc::types::os::arch::extra::DWORD;
|
||||||
use std::libc::consts::os::extra::{
|
use std::libc::consts::os::extra::{
|
||||||
SYNCHRONIZE,
|
SYNCHRONIZE,
|
||||||
@@ -572,7 +633,7 @@ fn waitpid(pid: pid_t) -> int {
|
|||||||
}
|
}
|
||||||
if status != STILL_ACTIVE {
|
if status != STILL_ACTIVE {
|
||||||
CloseHandle(process);
|
CloseHandle(process);
|
||||||
return status as int;
|
return p::ExitStatus(status as int);
|
||||||
}
|
}
|
||||||
if WaitForSingleObject(process, INFINITE) == WAIT_FAILED {
|
if WaitForSingleObject(process, INFINITE) == WAIT_FAILED {
|
||||||
CloseHandle(process);
|
CloseHandle(process);
|
||||||
@@ -583,43 +644,36 @@ fn waitpid(pid: pid_t) -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn waitpid_os(pid: pid_t) -> int {
|
fn waitpid_os(pid: pid_t) -> p::ProcessExit {
|
||||||
use std::libc::funcs::posix01::wait;
|
use std::libc::funcs::posix01::wait;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
fn WIFEXITED(status: i32) -> bool {
|
mod imp {
|
||||||
(status & 0xffi32) == 0i32
|
pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
|
||||||
|
pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
|
||||||
|
pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
#[cfg(target_os = "freebsd")]
|
#[cfg(target_os = "freebsd")]
|
||||||
fn WIFEXITED(status: i32) -> bool {
|
mod imp {
|
||||||
(status & 0x7fi32) == 0i32
|
pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
|
||||||
}
|
pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
|
||||||
|
pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
fn WEXITSTATUS(status: i32) -> i32 {
|
|
||||||
(status >> 8i32) & 0xffi32
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
#[cfg(target_os = "freebsd")]
|
|
||||||
fn WEXITSTATUS(status: i32) -> i32 {
|
|
||||||
status >> 8i32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut status = 0 as c_int;
|
let mut status = 0 as c_int;
|
||||||
if unsafe { wait::waitpid(pid, &mut status, 0) } == -1 {
|
match super::retry(|| unsafe { wait::waitpid(pid, &mut status, 0) }) {
|
||||||
fail!("failure in waitpid: {}", os::last_os_error());
|
Err(e) => fail!("unknown waitpid error: {:?}", e),
|
||||||
}
|
Ok(_ret) => {
|
||||||
|
if imp::WIFEXITED(status) {
|
||||||
return if WIFEXITED(status) {
|
p::ExitStatus(imp::WEXITSTATUS(status) as int)
|
||||||
WEXITSTATUS(status) as int
|
|
||||||
} else {
|
} else {
|
||||||
1
|
p::ExitSignal(imp::WTERMSIG(status) as int)
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,8 +700,4 @@ mod tests {
|
|||||||
~"echo \"a b c\""
|
~"echo \"a b c\""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently most of the tests of this functionality live inside std::run,
|
|
||||||
// but they may move here eventually as a non-blocking backend is added to
|
|
||||||
// std::run
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ mod test {
|
|||||||
use prelude::*;
|
use prelude::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
use io;
|
use io;
|
||||||
use comm;
|
|
||||||
use task;
|
use task;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -134,9 +134,8 @@ impl Acceptor<TcpStream> for TcpAcceptor {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use io::net::ip::{Ipv4Addr, SocketAddr};
|
use io::net::ip::SocketAddr;
|
||||||
use io::*;
|
use io::*;
|
||||||
use io::test::{next_test_ip4, next_test_ip6};
|
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
#[test] #[ignore]
|
#[test] #[ignore]
|
||||||
|
|||||||
@@ -169,5 +169,152 @@ impl Drop for Process {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests for this module can be found in the rtio-processes run-pass test, along
|
#[cfg(test)]
|
||||||
// with the justification for why it's not located here.
|
mod tests {
|
||||||
|
use io::process::{ProcessConfig, Process};
|
||||||
|
use prelude::*;
|
||||||
|
use str;
|
||||||
|
|
||||||
|
// FIXME(#10380)
|
||||||
|
#[cfg(unix, not(target_os="android"))]
|
||||||
|
iotest!(fn smoke() {
|
||||||
|
let io = ~[];
|
||||||
|
let args = ProcessConfig {
|
||||||
|
program: "/bin/sh",
|
||||||
|
args: [~"-c", ~"true"],
|
||||||
|
env: None,
|
||||||
|
cwd: None,
|
||||||
|
io: io,
|
||||||
|
};
|
||||||
|
let p = Process::new(args);
|
||||||
|
assert!(p.is_some());
|
||||||
|
let mut p = p.unwrap();
|
||||||
|
assert!(p.wait().success());
|
||||||
|
})
|
||||||
|
|
||||||
|
// FIXME(#10380)
|
||||||
|
#[cfg(unix, not(target_os="android"))]
|
||||||
|
iotest!(fn smoke_failure() {
|
||||||
|
let io = ~[];
|
||||||
|
let args = ProcessConfig {
|
||||||
|
program: "if-this-is-a-binary-then-the-world-has-ended",
|
||||||
|
args: [],
|
||||||
|
env: None,
|
||||||
|
cwd: None,
|
||||||
|
io: io,
|
||||||
|
};
|
||||||
|
match io::result(|| Process::new(args)) {
|
||||||
|
Ok(..) => fail!(),
|
||||||
|
Err(..) => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// FIXME(#10380)
|
||||||
|
#[cfg(unix, not(target_os="android"))]
|
||||||
|
iotest!(fn exit_reported_right() {
|
||||||
|
let io = ~[];
|
||||||
|
let args = ProcessConfig {
|
||||||
|
program: "/bin/sh",
|
||||||
|
args: [~"-c", ~"exit 1"],
|
||||||
|
env: None,
|
||||||
|
cwd: None,
|
||||||
|
io: io,
|
||||||
|
};
|
||||||
|
let p = Process::new(args);
|
||||||
|
assert!(p.is_some());
|
||||||
|
let mut p = p.unwrap();
|
||||||
|
assert!(p.wait().matches_exit_status(1));
|
||||||
|
})
|
||||||
|
|
||||||
|
#[cfg(unix, not(target_os="android"))]
|
||||||
|
iotest!(fn signal_reported_right() {
|
||||||
|
let io = ~[];
|
||||||
|
let args = ProcessConfig {
|
||||||
|
program: "/bin/sh",
|
||||||
|
args: [~"-c", ~"kill -1 $$"],
|
||||||
|
env: None,
|
||||||
|
cwd: None,
|
||||||
|
io: io,
|
||||||
|
};
|
||||||
|
let p = Process::new(args);
|
||||||
|
assert!(p.is_some());
|
||||||
|
let mut p = p.unwrap();
|
||||||
|
match p.wait() {
|
||||||
|
process::ExitSignal(1) => {},
|
||||||
|
result => fail!("not terminated by signal 1 (instead, {})", result),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
pub fn read_all(input: &mut Reader) -> ~str {
|
||||||
|
let mut ret = ~"";
|
||||||
|
let mut buf = [0, ..1024];
|
||||||
|
loop {
|
||||||
|
match input.read(buf) {
|
||||||
|
None => { break }
|
||||||
|
Some(n) => { ret.push_str(str::from_utf8(buf.slice_to(n))); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_output(args: ProcessConfig) -> ~str {
|
||||||
|
let p = Process::new(args);
|
||||||
|
assert!(p.is_some());
|
||||||
|
let mut p = p.unwrap();
|
||||||
|
assert!(p.io[0].is_none());
|
||||||
|
assert!(p.io[1].is_some());
|
||||||
|
let ret = read_all(p.io[1].get_mut_ref() as &mut Reader);
|
||||||
|
assert!(p.wait().success());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(#10380)
|
||||||
|
#[cfg(unix, not(target_os="android"))]
|
||||||
|
iotest!(fn stdout_works() {
|
||||||
|
let io = ~[Ignored, CreatePipe(false, true)];
|
||||||
|
let args = ProcessConfig {
|
||||||
|
program: "/bin/sh",
|
||||||
|
args: [~"-c", ~"echo foobar"],
|
||||||
|
env: None,
|
||||||
|
cwd: None,
|
||||||
|
io: io,
|
||||||
|
};
|
||||||
|
assert_eq!(run_output(args), ~"foobar\n");
|
||||||
|
})
|
||||||
|
|
||||||
|
// FIXME(#10380)
|
||||||
|
#[cfg(unix, not(target_os="android"))]
|
||||||
|
iotest!(fn set_cwd_works() {
|
||||||
|
let io = ~[Ignored, CreatePipe(false, true)];
|
||||||
|
let cwd = Some("/");
|
||||||
|
let args = ProcessConfig {
|
||||||
|
program: "/bin/sh",
|
||||||
|
args: [~"-c", ~"pwd"],
|
||||||
|
env: None,
|
||||||
|
cwd: cwd,
|
||||||
|
io: io,
|
||||||
|
};
|
||||||
|
assert_eq!(run_output(args), ~"/\n");
|
||||||
|
})
|
||||||
|
|
||||||
|
// FIXME(#10380)
|
||||||
|
#[cfg(unix, not(target_os="android"))]
|
||||||
|
iotest!(fn stdin_works() {
|
||||||
|
let io = ~[CreatePipe(true, false),
|
||||||
|
CreatePipe(false, true)];
|
||||||
|
let args = ProcessConfig {
|
||||||
|
program: "/bin/sh",
|
||||||
|
args: [~"-c", ~"read line; echo $line"],
|
||||||
|
env: None,
|
||||||
|
cwd: None,
|
||||||
|
io: io,
|
||||||
|
};
|
||||||
|
let mut p = Process::new(args).expect("didn't create a proces?!");
|
||||||
|
p.io[0].get_mut_ref().write("foobar".as_bytes());
|
||||||
|
p.io[0] = None; // close stdin;
|
||||||
|
let out = read_all(p.io[1].get_mut_ref() as &mut Reader);
|
||||||
|
assert!(p.wait().success());
|
||||||
|
assert_eq!(out, ~"foobar\n");
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ macro_rules! iotest (
|
|||||||
use io::net::udp::*;
|
use io::net::udp::*;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use io::net::unix::*;
|
use io::net::unix::*;
|
||||||
|
use io::process::*;
|
||||||
use str;
|
use str;
|
||||||
use util;
|
use util;
|
||||||
|
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ mod tests {
|
|||||||
use task::spawn;
|
use task::spawn;
|
||||||
use unstable::running_on_valgrind;
|
use unstable::running_on_valgrind;
|
||||||
use io::pipe::PipeStream;
|
use io::pipe::PipeStream;
|
||||||
use io::{Writer, Reader, io_error, FileNotFound, OtherIoError};
|
use io::{Writer, Reader, io_error, FileNotFound};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(target_os="android"))] // FIXME(#10380)
|
#[cfg(not(target_os="android"))] // FIXME(#10380)
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// compile-flags: --test
|
|
||||||
// xfail-fast
|
|
||||||
|
|
||||||
// In the current state of affairs, libuv registers a SIGCHLD handler when a
|
|
||||||
// process is spawned through it. This is not done with a SA_RESTART flag,
|
|
||||||
// meaning that all of our syscalls run the risk of returning EINTR. This error
|
|
||||||
// is not correctly handled in the majority of std::io, so these can't run with
|
|
||||||
// the main body of tests there.
|
|
||||||
//
|
|
||||||
// That being said, libuv correctly handles EINTR completely, so these tests
|
|
||||||
// themselves are safe against that. Currently the test runner may run into this
|
|
||||||
// problem, but it's less likely than a whole suite of tests...
|
|
||||||
//
|
|
||||||
// See #9341
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::io::process;
|
|
||||||
use std::io::process::{Process, ProcessConfig, CreatePipe, Ignored};
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
// FIXME(#10380)
|
|
||||||
#[cfg(unix, not(target_os="android"))]
|
|
||||||
fn smoke() {
|
|
||||||
let io = ~[];
|
|
||||||
let args = ProcessConfig {
|
|
||||||
program: "/bin/sh",
|
|
||||||
args: [~"-c", ~"true"],
|
|
||||||
env: None,
|
|
||||||
cwd: None,
|
|
||||||
io: io,
|
|
||||||
};
|
|
||||||
let p = Process::new(args);
|
|
||||||
assert!(p.is_some());
|
|
||||||
let mut p = p.unwrap();
|
|
||||||
assert!(p.wait().success());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
// FIXME(#10380)
|
|
||||||
#[cfg(unix, not(target_os="android"))]
|
|
||||||
fn smoke_failure() {
|
|
||||||
let io = ~[];
|
|
||||||
let args = ProcessConfig {
|
|
||||||
program: "if-this-is-a-binary-then-the-world-has-ended",
|
|
||||||
args: [],
|
|
||||||
env: None,
|
|
||||||
cwd: None,
|
|
||||||
io: io,
|
|
||||||
};
|
|
||||||
match io::result(|| Process::new(args)) {
|
|
||||||
Ok(..) => fail!(),
|
|
||||||
Err(..) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
// FIXME(#10380)
|
|
||||||
#[cfg(unix, not(target_os="android"))]
|
|
||||||
fn exit_reported_right() {
|
|
||||||
let io = ~[];
|
|
||||||
let args = ProcessConfig {
|
|
||||||
program: "/bin/sh",
|
|
||||||
args: [~"-c", ~"exit 1"],
|
|
||||||
env: None,
|
|
||||||
cwd: None,
|
|
||||||
io: io,
|
|
||||||
};
|
|
||||||
let p = Process::new(args);
|
|
||||||
assert!(p.is_some());
|
|
||||||
let mut p = p.unwrap();
|
|
||||||
assert!(p.wait().matches_exit_status(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(unix, not(target_os="android"))]
|
|
||||||
fn signal_reported_right() {
|
|
||||||
let io = ~[];
|
|
||||||
let args = ProcessConfig {
|
|
||||||
program: "/bin/sh",
|
|
||||||
args: [~"-c", ~"kill -1 $$"],
|
|
||||||
env: None,
|
|
||||||
cwd: None,
|
|
||||||
io: io,
|
|
||||||
};
|
|
||||||
let p = Process::new(args);
|
|
||||||
assert!(p.is_some());
|
|
||||||
let mut p = p.unwrap();
|
|
||||||
match p.wait() {
|
|
||||||
process::ExitSignal(1) => {},
|
|
||||||
result => fail!("not terminated by signal 1 (instead, {})", result),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_all(input: &mut Reader) -> ~str {
|
|
||||||
let mut ret = ~"";
|
|
||||||
let mut buf = [0, ..1024];
|
|
||||||
loop {
|
|
||||||
match input.read(buf) {
|
|
||||||
None => { break }
|
|
||||||
Some(n) => { ret.push_str(str::from_utf8(buf.slice_to(n))); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_output(args: ProcessConfig) -> ~str {
|
|
||||||
let p = Process::new(args);
|
|
||||||
assert!(p.is_some());
|
|
||||||
let mut p = p.unwrap();
|
|
||||||
assert!(p.io[0].is_none());
|
|
||||||
assert!(p.io[1].is_some());
|
|
||||||
let ret = read_all(p.io[1].get_mut_ref() as &mut Reader);
|
|
||||||
assert!(p.wait().success());
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
// FIXME(#10380)
|
|
||||||
#[cfg(unix, not(target_os="android"))]
|
|
||||||
fn stdout_works() {
|
|
||||||
let io = ~[Ignored, CreatePipe(false, true)];
|
|
||||||
let args = ProcessConfig {
|
|
||||||
program: "/bin/sh",
|
|
||||||
args: [~"-c", ~"echo foobar"],
|
|
||||||
env: None,
|
|
||||||
cwd: None,
|
|
||||||
io: io,
|
|
||||||
};
|
|
||||||
assert_eq!(run_output(args), ~"foobar\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
// FIXME(#10380)
|
|
||||||
#[cfg(unix, not(target_os="android"))]
|
|
||||||
fn set_cwd_works() {
|
|
||||||
let io = ~[Ignored, CreatePipe(false, true)];
|
|
||||||
let cwd = Some("/");
|
|
||||||
let args = ProcessConfig {
|
|
||||||
program: "/bin/sh",
|
|
||||||
args: [~"-c", ~"pwd"],
|
|
||||||
env: None,
|
|
||||||
cwd: cwd,
|
|
||||||
io: io,
|
|
||||||
};
|
|
||||||
assert_eq!(run_output(args), ~"/\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
// FIXME(#10380)
|
|
||||||
#[cfg(unix, not(target_os="android"))]
|
|
||||||
fn stdin_works() {
|
|
||||||
let io = ~[CreatePipe(true, false),
|
|
||||||
CreatePipe(false, true)];
|
|
||||||
let args = ProcessConfig {
|
|
||||||
program: "/bin/sh",
|
|
||||||
args: [~"-c", ~"read line; echo $line"],
|
|
||||||
env: None,
|
|
||||||
cwd: None,
|
|
||||||
io: io,
|
|
||||||
};
|
|
||||||
let mut p = Process::new(args).expect("didn't create a proces?!");
|
|
||||||
p.io[0].get_mut_ref().write("foobar".as_bytes());
|
|
||||||
p.io[0] = None; // close stdin;
|
|
||||||
let out = read_all(p.io[1].get_mut_ref() as &mut Reader);
|
|
||||||
assert!(p.wait().success());
|
|
||||||
assert_eq!(out, ~"foobar\n");
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user