auto merge of #11159 : alexcrichton/rust/native-io, r=pcwalton

The old `rtio-processes` run-pass test is now moved into libstd's `io::process` module, and all process and TCP tests are now run with `iotest!` (both a native and a green version are tested).

All TCP networking on windows is provided by `ws2_32` which is apparently very similar to unix networking (hurray!).
This commit is contained in:
bors 2013-12-28 10:36:54 -08:00
commit 200c52a34e
13 changed files with 1109 additions and 342 deletions

View file

@ -26,7 +26,7 @@ use super::IoResult;
#[cfg(windows)] use std::ptr;
#[cfg(windows)] use std::str;
fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 {
pub fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 {
#[cfg(windows)] static eintr: int = 0; // doesn't matter
#[cfg(not(windows))] static eintr: int = libc::EINTR as int;
@ -37,7 +37,7 @@ fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 {
let mut ret;
loop {
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 ret != -1 || os::errno() as int != eintr { break }
}
@ -73,7 +73,10 @@ impl FileDesc {
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(not(windows))] type rlen = libc::size_t;
let ret = keep_going(buf, |buf, len| {
@ -89,7 +92,7 @@ impl FileDesc {
Ok(ret as uint)
}
}
fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> {
pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> {
#[cfg(windows)] type wlen = libc::c_uint;
#[cfg(not(windows))] type wlen = libc::size_t;
let ret = keep_going(buf, |buf, len| {
@ -103,6 +106,8 @@ impl FileDesc {
Ok(())
}
}
pub fn fd(&self) -> fd_t { self.fd }
}
impl io::Reader for FileDesc {

View file

@ -44,6 +44,7 @@ pub use self::process::Process;
// Native I/O implementations
pub mod file;
pub mod process;
pub mod net;
type IoResult<T> = Result<T, IoError>;
@ -55,12 +56,25 @@ fn unimpl() -> IoError {
}
}
fn last_error() -> IoError {
fn translate_error(errno: i32, detail: bool) -> IoError {
#[cfg(windows)]
fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) {
match errno {
libc::EOF => (io::EndOfFile, "end of file"),
_ => (io::OtherIoError, "unknown error"),
libc::WSAECONNREFUSED => (io::ConnectionRefused, "connection refused"),
libc::WSAECONNRESET => (io::ConnectionReset, "connection reset"),
libc::WSAEACCES => (io::PermissionDenied, "permission denied"),
libc::WSAEWOULDBLOCK =>
(io::ResourceUnavailable, "resource temporarily unavailable"),
libc::WSAENOTCONN => (io::NotConnected, "not connected"),
libc::WSAECONNABORTED => (io::ConnectionAborted, "connection aborted"),
libc::WSAEADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"),
x => {
debug!("ignoring {}: {}", x, os::last_os_error());
(io::OtherIoError, "unknown error")
}
}
}
@ -69,24 +83,38 @@ fn last_error() -> IoError {
// XXX: this should probably be a bit more descriptive...
match errno {
libc::EOF => (io::EndOfFile, "end of file"),
libc::ECONNREFUSED => (io::ConnectionRefused, "connection refused"),
libc::ECONNRESET => (io::ConnectionReset, "connection reset"),
libc::EPERM | libc::EACCES =>
(io::PermissionDenied, "permission denied"),
libc::EPIPE => (io::BrokenPipe, "broken pipe"),
libc::ENOTCONN => (io::NotConnected, "not connected"),
libc::ECONNABORTED => (io::ConnectionAborted, "connection aborted"),
libc::EADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
libc::EADDRINUSE => (io::ConnectionRefused, "address in use"),
// These two constants can have the same value on some systems, but
// different values on others, so we can't use a match clause
x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
(io::ResourceUnavailable, "resource temporarily unavailable"),
_ => (io::OtherIoError, "unknown error"),
x => {
debug!("ignoring {}: {}", x, os::last_os_error());
(io::OtherIoError, "unknown error")
}
}
}
let (kind, desc) = get_err(os::errno() as i32);
let (kind, desc) = get_err(errno);
IoError {
kind: kind,
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
fn mkerr_libc(ret: libc::c_int) -> IoResult<()> {
if ret != 0 {
@ -106,17 +134,37 @@ 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
/// native I/O functionality.
pub struct IoFactory;
pub struct IoFactory {
priv cannot_construct_outside_of_this_module: ()
}
impl IoFactory {
pub fn new() -> IoFactory {
net::init();
IoFactory { cannot_construct_outside_of_this_module: () }
}
}
impl rtio::IoFactory for IoFactory {
// networking
fn tcp_connect(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpStream> {
Err(unimpl())
fn tcp_connect(&mut self, addr: SocketAddr) -> IoResult<~RtioTcpStream> {
net::TcpStream::connect(addr).map(|s| ~s as ~RtioTcpStream)
}
fn tcp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpListener> {
Err(unimpl())
fn tcp_bind(&mut self, addr: SocketAddr) -> IoResult<~RtioTcpListener> {
net::TcpListener::bind(addr).map(|s| ~s as ~RtioTcpListener)
}
fn udp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioUdpSocket> {
Err(unimpl())
@ -204,9 +252,7 @@ impl rtio::IoFactory for IoFactory {
}
fn tty_open(&mut self, fd: c_int, _readable: bool) -> IoResult<~RtioTTY> {
if unsafe { libc::isatty(fd) } != 0 {
// Don't ever close the stdio file descriptors, nothing good really
// comes of that.
Ok(~file::FileDesc::new(fd, fd > libc::STDERR_FILENO) as ~RtioTTY)
Ok(~file::FileDesc::new(fd, true) as ~RtioTTY)
} else {
Err(IoError {
kind: io::MismatchedFileTypeForOperation,

412
src/libnative/io/net.rs Normal file
View file

@ -0,0 +1,412 @@
// 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.
use std::cast;
use std::io::net::ip;
use std::io;
use std::libc;
use std::mem;
use std::rt::rtio;
use std::unstable::intrinsics;
use super::IoResult;
use super::file::keep_going;
#[cfg(windows)] pub type sock_t = libc::SOCKET;
#[cfg(unix)] pub type sock_t = super::file::fd_t;
pub struct TcpStream {
priv fd: sock_t,
}
#[cfg(target_endian = "big")] pub fn htons(x: u16) -> u16 { x }
#[cfg(target_endian = "big")] pub fn ntohs(x: u16) -> u16 { x }
#[cfg(target_endian = "little")]
pub fn htons(u: u16) -> u16 {
unsafe { intrinsics::bswap16(u as i16) as u16 }
}
#[cfg(target_endian = "little")]
pub fn ntohs(u: u16) -> u16 {
unsafe { intrinsics::bswap16(u as i16) as u16 }
}
fn addr_to_sockaddr(addr: ip::SocketAddr) -> (libc::sockaddr_storage, uint) {
unsafe {
let storage: libc::sockaddr_storage = intrinsics::init();
let len = match addr.ip {
ip::Ipv4Addr(a, b, c, d) => {
let storage: *mut libc::sockaddr_in = cast::transmute(&storage);
(*storage).sin_family = libc::AF_INET as libc::sa_family_t;
(*storage).sin_port = htons(addr.port);
(*storage).sin_addr.s_addr = (d as u32 << 24) |
(c as u32 << 16) |
(b as u32 << 8) |
(a as u32 << 0);
mem::size_of::<libc::sockaddr_in>()
}
ip::Ipv6Addr(a, b, c, d, e, f, g, h) => {
let storage: *mut libc::sockaddr_in6 = cast::transmute(&storage);
(*storage).sin6_family = libc::AF_INET6 as libc::sa_family_t;
(*storage).sin6_port = htons(addr.port);
(*storage).sin6_addr.s6_addr[0] = htons(a);
(*storage).sin6_addr.s6_addr[1] = htons(b);
(*storage).sin6_addr.s6_addr[2] = htons(c);
(*storage).sin6_addr.s6_addr[3] = htons(d);
(*storage).sin6_addr.s6_addr[4] = htons(e);
(*storage).sin6_addr.s6_addr[5] = htons(f);
(*storage).sin6_addr.s6_addr[6] = htons(g);
(*storage).sin6_addr.s6_addr[7] = htons(h);
mem::size_of::<libc::sockaddr_in6>()
}
};
return (storage, len);
}
}
fn socket(addr: ip::SocketAddr) -> IoResult<sock_t> {
unsafe {
let fam = match addr.ip {
ip::Ipv4Addr(..) => libc::AF_INET,
ip::Ipv6Addr(..) => libc::AF_INET6,
};
match libc::socket(fam, libc::SOCK_STREAM, 0) {
-1 => Err(super::last_error()),
fd => Ok(fd),
}
}
}
fn sockname(fd: sock_t,
f: extern "system" unsafe fn(sock_t, *mut libc::sockaddr,
*mut libc::socklen_t) -> libc::c_int)
-> IoResult<ip::SocketAddr>
{
let mut storage: libc::sockaddr_storage = unsafe { intrinsics::init() };
let mut len = mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
unsafe {
let storage = &mut storage as *mut libc::sockaddr_storage;
let ret = f(fd,
storage as *mut libc::sockaddr,
&mut len as *mut libc::socklen_t);
if ret != 0 {
return Err(super::last_error())
}
}
match storage.ss_family as libc::c_int {
libc::AF_INET => {
assert!(len as uint >= mem::size_of::<libc::sockaddr_in>());
let storage: &mut libc::sockaddr_in = unsafe {
cast::transmute(&mut storage)
};
let addr = storage.sin_addr.s_addr as u32;
let a = (addr >> 0) as u8;
let b = (addr >> 8) as u8;
let c = (addr >> 16) as u8;
let d = (addr >> 24) as u8;
Ok(ip::SocketAddr {
ip: ip::Ipv4Addr(a, b, c, d),
port: ntohs(storage.sin_port),
})
}
libc::AF_INET6 => {
assert!(len as uint >= mem::size_of::<libc::sockaddr_in6>());
let storage: &mut libc::sockaddr_in6 = unsafe {
cast::transmute(&mut storage)
};
let a = ntohs(storage.sin6_addr.s6_addr[0]);
let b = ntohs(storage.sin6_addr.s6_addr[1]);
let c = ntohs(storage.sin6_addr.s6_addr[2]);
let d = ntohs(storage.sin6_addr.s6_addr[3]);
let e = ntohs(storage.sin6_addr.s6_addr[4]);
let f = ntohs(storage.sin6_addr.s6_addr[5]);
let g = ntohs(storage.sin6_addr.s6_addr[6]);
let h = ntohs(storage.sin6_addr.s6_addr[7]);
Ok(ip::SocketAddr {
ip: ip::Ipv6Addr(a, b, c, d, e, f, g, h),
port: ntohs(storage.sin6_port),
})
}
_ => {
Err(io::standard_error(io::OtherIoError))
}
}
}
#[cfg(unix)]
pub fn init() {}
#[cfg(windows)]
pub fn init() {
static WSADESCRIPTION_LEN: uint = 256;
static WSASYS_STATUS_LEN: uint = 128;
struct WSADATA {
wVersion: libc::WORD,
wHighVersion: libc::WORD,
szDescription: [u8, ..WSADESCRIPTION_LEN + 1],
szSystemStatus: [u8, ..WSASYS_STATUS_LEN + 1],
iMaxSockets: u16,
iMaxUdpDg: u16,
lpVendorInfo: *u8,
}
type LPWSADATA = *mut WSADATA;
#[link(name = "ws2_32")]
extern "system" {
fn WSAStartup(wVersionRequested: libc::WORD,
lpWSAData: LPWSADATA) -> libc::c_int;
}
unsafe {
use std::unstable::mutex::{Mutex, MUTEX_INIT};
static mut LOCK: Mutex = MUTEX_INIT;
static mut INITIALIZED: bool = false;
if INITIALIZED { return }
LOCK.lock();
if !INITIALIZED {
let mut data: WSADATA = intrinsics::init();
let ret = WSAStartup(0x202, // version 2.2
&mut data);
assert_eq!(ret, 0);
INITIALIZED = true;
}
LOCK.unlock();
}
}
impl TcpStream {
pub fn connect(addr: ip::SocketAddr) -> IoResult<TcpStream> {
unsafe {
socket(addr).and_then(|fd| {
let (addr, len) = addr_to_sockaddr(addr);
let addrp = &addr as *libc::sockaddr_storage;
let ret = TcpStream { fd: fd };
match libc::connect(fd, addrp as *libc::sockaddr,
len as libc::socklen_t) {
-1 => Err(super::last_error()),
_ => Ok(ret),
}
})
}
}
pub fn fd(&self) -> sock_t { self.fd }
fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> {
unsafe {
let on = nodelay as libc::c_int;
let on = &on as *libc::c_int;
super::mkerr_libc(libc::setsockopt(self.fd,
libc::IPPROTO_TCP,
libc::TCP_NODELAY,
on as *libc::c_void,
mem::size_of::<libc::c_void>()
as libc::socklen_t))
}
}
fn set_keepalive(&mut self, seconds: Option<uint>) -> IoResult<()> {
unsafe {
let on = seconds.is_some() as libc::c_int;
let on = &on as *libc::c_int;
let ret = libc::setsockopt(self.fd,
libc::SOL_SOCKET,
libc::SO_KEEPALIVE,
on as *libc::c_void,
mem::size_of::<libc::c_void>()
as libc::socklen_t);
if ret != 0 { return Err(super::last_error()) }
match seconds {
Some(n) => self.set_tcp_keepalive(n),
None => Ok(())
}
}
}
#[cfg(target_os = "macos")]
unsafe fn set_tcp_keepalive(&mut self, seconds: uint) -> IoResult<()> {
let delay = seconds as libc::c_uint;
let delay = &delay as *libc::c_uint;
let ret = libc::setsockopt(self.fd,
libc::IPPROTO_TCP,
libc::TCP_KEEPALIVE,
delay as *libc::c_void,
mem::size_of::<libc::c_uint>()
as libc::socklen_t);
super::mkerr_libc(ret)
}
#[cfg(target_os = "freebsd")]
unsafe fn set_tcp_keepalive(&mut self, seconds: uint) -> IoResult<()> {
let delay = seconds as libc::c_uint;
let delay = &delay as *libc::c_uint;
let ret = libc::setsockopt(self.fd,
libc::IPPROTO_TCP,
libc::TCP_KEEPIDLE,
delay as *libc::c_void,
mem::size_of::<libc::c_uint>()
as libc::socklen_t);
super::mkerr_libc(ret)
}
#[cfg(not(target_os = "macos"), not(target_os = "freebsd"))]
unsafe fn set_tcp_keepalive(&mut self, _seconds: uint) -> IoResult<()> {
Ok(())
}
}
#[cfg(windows)] type wrlen = libc::c_int;
#[cfg(not(windows))] type wrlen = libc::size_t;
impl rtio::RtioTcpStream for TcpStream {
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
let ret = keep_going(buf, |buf, len| {
unsafe {
libc::recv(self.fd,
buf as *mut libc::c_void,
len as wrlen,
0) as i64
}
});
if ret == 0 {
Err(io::standard_error(io::EndOfFile))
} else if ret < 0 {
Err(super::last_error())
} else {
Ok(ret as uint)
}
}
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
let ret = keep_going(buf, |buf, len| {
unsafe {
libc::send(self.fd,
buf as *mut libc::c_void,
len as wrlen,
0) as i64
}
});
if ret < 0 {
Err(super::last_error())
} else {
Ok(())
}
}
fn peer_name(&mut self) -> IoResult<ip::SocketAddr> {
sockname(self.fd, libc::getpeername)
}
fn control_congestion(&mut self) -> IoResult<()> {
self.set_nodelay(false)
}
fn nodelay(&mut self) -> IoResult<()> {
self.set_nodelay(true)
}
fn keepalive(&mut self, delay_in_seconds: uint) -> IoResult<()> {
self.set_keepalive(Some(delay_in_seconds))
}
fn letdie(&mut self) -> IoResult<()> {
self.set_keepalive(None)
}
}
impl rtio::RtioSocket for TcpStream {
fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
sockname(self.fd, libc::getsockname)
}
}
impl Drop for TcpStream {
#[cfg(unix)]
fn drop(&mut self) {
unsafe { libc::close(self.fd); }
}
#[cfg(windows)]
fn drop(&mut self) {
unsafe { libc::closesocket(self.fd); }
}
}
pub struct TcpListener {
priv fd: sock_t,
}
impl TcpListener {
pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> {
unsafe {
socket(addr).and_then(|fd| {
let (addr, len) = addr_to_sockaddr(addr);
let addrp = &addr as *libc::sockaddr_storage;
let ret = TcpListener { fd: fd };
match libc::bind(fd, addrp as *libc::sockaddr,
len as libc::socklen_t) {
-1 => Err(super::last_error()),
_ => Ok(ret),
}
})
}
}
pub fn fd(&self) -> sock_t { self.fd }
pub fn native_listen(self, backlog: int) -> IoResult<TcpAcceptor> {
match unsafe { libc::listen(self.fd, backlog as libc::c_int) } {
-1 => Err(super::last_error()),
_ => Ok(TcpAcceptor { fd: self.fd })
}
}
}
impl rtio::RtioTcpListener for TcpListener {
fn listen(~self) -> IoResult<~rtio::RtioTcpAcceptor> {
self.native_listen(128).map(|a| ~a as ~rtio::RtioTcpAcceptor)
}
}
impl rtio::RtioSocket for TcpListener {
fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
sockname(self.fd, libc::getsockname)
}
}
pub struct TcpAcceptor {
priv fd: sock_t,
}
impl TcpAcceptor {
pub fn fd(&self) -> sock_t { self.fd }
pub fn native_accept(&mut self) -> IoResult<TcpStream> {
unsafe {
let mut storage: libc::sockaddr_storage = intrinsics::init();
let storagep = &mut storage as *mut libc::sockaddr_storage;
let size = mem::size_of::<libc::sockaddr_storage>();
let mut size = size as libc::socklen_t;
match libc::accept(self.fd,
storagep as *mut libc::sockaddr,
&mut size as *mut libc::socklen_t) {
-1 => Err(super::last_error()),
fd => Ok(TcpStream { fd: fd })
}
}
}
}
impl rtio::RtioSocket for TcpAcceptor {
fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
sockname(self.fd, libc::getsockname)
}
}
impl rtio::RtioTcpAcceptor for TcpAcceptor {
fn accept(&mut self) -> IoResult<~rtio::RtioTcpStream> {
self.native_accept().map(|s| ~s as ~rtio::RtioTcpStream)
}
fn accept_simultaneously(&mut self) -> IoResult<()> { Ok(()) }
fn dont_accept_simultaneously(&mut self) -> IoResult<()> { Ok(()) }
}

View file

@ -18,6 +18,7 @@ use p = std::io::process;
#[cfg(windows)] use std::cast;
use super::IoResult;
use super::file;
/**
@ -37,7 +38,7 @@ pub struct Process {
priv handle: *(),
/// None until finish() is called.
priv exit_code: Option<int>,
priv exit_code: Option<p::ProcessExit>,
}
impl Process {
@ -105,7 +106,13 @@ impl Process {
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 wait(&mut self) -> p::ProcessExit {
let code = match self.exit_code {
match self.exit_code {
Some(code) => code,
None => {
let code = waitpid(self.pid);
self.exit_code = Some(code);
code
}
};
return p::ExitStatus(code); // XXX: this is wrong
}
}
fn kill(&mut self, signum: int) -> Result<(), io::IoError> {
@ -177,7 +183,8 @@ struct SpawnProcessResult {
fn spawn_process_os(prog: &str, args: &[~str],
env: Option<~[(~str, ~str)]>,
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::consts::os::extra::{
TRUE, FALSE,
@ -241,7 +248,7 @@ fn spawn_process_os(prog: &str, args: &[~str],
ptr::mut_null(), ptr::mut_null(), TRUE,
0, envp, dirp, &mut si, &mut pi);
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.hStdError);
for msg in create_err.iter() {
fail!("failure in CreateProcess: {}", *msg);
match create_err {
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
// 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
// calling code closes the process handle.
CloseHandle(pi.hThread);
SpawnProcessResult {
Ok(SpawnProcessResult {
pid: pi.dwProcessId as pid_t,
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)]
pub fn make_command_line(prog: &str, args: &[~str]) -> ~str {
fn make_command_line(prog: &str, args: &[~str]) -> ~str {
let mut cmd = ~"";
append_arg(&mut cmd, prog);
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],
env: Option<~[(~str, ~str)]>,
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::bsd44::getdtablesize;
use std::libc::c_ulong;
use std::unstable::intrinsics;
mod rustrt {
extern {
@ -370,45 +380,89 @@ fn spawn_process_os(prog: &str, args: &[~str],
}
}
#[cfg(windows)]
unsafe fn set_environ(_envp: *c_void) {}
#[cfg(target_os = "macos")]
unsafe fn set_environ(envp: *c_void) {
extern { fn _NSGetEnviron() -> *mut *c_void; }
*_NSGetEnviron() = envp;
}
#[cfg(not(target_os = "macos"), not(windows))]
#[cfg(not(target_os = "macos"))]
unsafe fn set_environ(envp: *c_void) {
extern {
static mut environ: *c_void;
}
extern { static mut environ: *c_void; }
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, true);
let mut output = file::FileDesc::new(pipe.out, true);
unsafe { set_cloexec(output.fd()) };
unsafe {
let pid = fork();
if pid < 0 {
fail!("failure in fork: {}", os::last_os_error());
} 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();
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());
}
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());
}
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());
}
// close all other fds
for fd in range(3, getdtablesize()).invert() {
close(fd as c_int);
if fd != output.fd() {
close(fd as c_int);
}
}
with_dirp(dir, |dirp| {
@ -421,11 +475,18 @@ fn spawn_process_os(prog: &str, args: &[~str],
if !envp.is_null() {
set_environ(envp);
}
with_argv(prog, args, |argv| {
execvp(*argv, argv);
// execvp only returns if an error occurred
fail!("failure in execvp: {}", os::last_os_error());
})
});
with_argv(prog, args, |argv| {
execvp(*argv, argv);
let errno = os::errno();
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
* with the same id.
*/
fn waitpid(pid: pid_t) -> int {
fn waitpid(pid: pid_t) -> p::ProcessExit {
return waitpid_os(pid);
#[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::consts::os::extra::{
SYNCHRONIZE,
@ -572,7 +633,7 @@ fn waitpid(pid: pid_t) -> int {
}
if status != STILL_ACTIVE {
CloseHandle(process);
return status as int;
return p::ExitStatus(status as int);
}
if WaitForSingleObject(process, INFINITE) == WAIT_FAILED {
CloseHandle(process);
@ -583,43 +644,36 @@ fn waitpid(pid: pid_t) -> int {
}
#[cfg(unix)]
fn waitpid_os(pid: pid_t) -> int {
fn waitpid_os(pid: pid_t) -> p::ProcessExit {
use std::libc::funcs::posix01::wait;
#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
fn WIFEXITED(status: i32) -> bool {
(status & 0xffi32) == 0i32
mod imp {
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 = "freebsd")]
fn WIFEXITED(status: i32) -> bool {
(status & 0x7fi32) == 0i32
}
#[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
mod imp {
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 }
}
let mut status = 0 as c_int;
if unsafe { wait::waitpid(pid, &mut status, 0) } == -1 {
fail!("failure in waitpid: {}", os::last_os_error());
match super::retry(|| unsafe { wait::waitpid(pid, &mut status, 0) }) {
Err(e) => fail!("unknown waitpid error: {:?}", e),
Ok(_ret) => {
if imp::WIFEXITED(status) {
p::ExitStatus(imp::WEXITSTATUS(status) as int)
} else {
p::ExitSignal(imp::WTERMSIG(status) as int)
}
}
}
return if WIFEXITED(status) {
WEXITSTATUS(status) as int
} else {
1
};
}
}
@ -646,8 +700,4 @@ mod tests {
~"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
}

View file

@ -34,6 +34,7 @@ pub fn new() -> ~Task {
task.put_runtime(~Ops {
lock: unsafe { Mutex::new() },
awoken: false,
io: io::IoFactory::new(),
} as ~rt::Runtime);
return task;
}
@ -86,8 +87,9 @@ pub fn spawn_opts(opts: TaskOpts, f: proc()) {
// This structure is the glue between channels and the 1:1 scheduling mode. This
// structure is allocated once per task.
struct Ops {
lock: Mutex, // native synchronization
awoken: bool, // used to prevent spurious wakeups
lock: Mutex, // native synchronization
awoken: bool, // used to prevent spurious wakeups
io: io::IoFactory, // local I/O factory
}
impl rt::Runtime for Ops {
@ -217,11 +219,7 @@ impl rt::Runtime for Ops {
}
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> {
static mut io: io::IoFactory = io::IoFactory;
// Unsafety is from accessing `io`, which is guaranteed to be safe
// because you can't do anything usable with this statically initialized
// unit struct.
Some(unsafe { rtio::LocalIo::new(&mut io as &mut rtio::IoFactory) })
Some(rtio::LocalIo::new(&mut self.io as &mut rtio::IoFactory))
}
}

View file

@ -345,6 +345,7 @@ pub fn uv_error_to_io_error(uverr: UvError) -> IoError {
uvll::ENOENT => io::FileNotFound,
uvll::EPIPE => io::BrokenPipe,
uvll::ECONNABORTED => io::ConnectionAborted,
uvll::EADDRNOTAVAIL => io::ConnectionRefused,
err => {
uvdebug!("uverr.code {}", err as int);
// XXX: Need to map remaining uv error types

View file

@ -38,7 +38,7 @@ use std::libc;
use std::libc::uintptr_t;
pub use self::errors::{EACCES, ECONNREFUSED, ECONNRESET, EPIPE, ECONNABORTED,
ECANCELED, EBADF, ENOTCONN, ENOENT};
ECANCELED, EBADF, ENOTCONN, ENOENT, EADDRNOTAVAIL};
pub static OK: c_int = 0;
pub static EOF: c_int = -4095;
@ -60,6 +60,7 @@ pub mod errors {
pub static ECONNABORTED: c_int = -4079;
pub static ECANCELED: c_int = -4081;
pub static EBADF: c_int = -4083;
pub static EADDRNOTAVAIL: c_int = -4090;
}
#[cfg(not(windows))]
pub mod errors {
@ -75,6 +76,7 @@ pub mod errors {
pub static ECONNABORTED: c_int = -libc::ECONNABORTED;
pub static ECANCELED : c_int = -libc::ECANCELED;
pub static EBADF : c_int = -libc::EBADF;
pub static EADDRNOTAVAIL : c_int = -libc::EADDRNOTAVAIL;
}
pub static PROCESS_SETUID: c_int = 1 << 0;

View file

@ -134,13 +134,11 @@ impl Acceptor<TcpStream> for TcpAcceptor {
#[cfg(test)]
mod test {
use super::*;
use io::net::ip::{Ipv4Addr, SocketAddr};
use io::net::ip::SocketAddr;
use io::*;
use io::test::{next_test_ip4, next_test_ip6};
use prelude::*;
#[test] #[ignore]
fn bind_error() {
iotest!(fn bind_error() {
let mut called = false;
io_error::cond.trap(|e| {
assert!(e.kind == PermissionDenied);
@ -151,19 +149,12 @@ mod test {
assert!(listener.is_none());
});
assert!(called);
}
} #[ignore(cfg(windows))])
#[test]
fn connect_error() {
iotest!(fn connect_error() {
let mut called = false;
io_error::cond.trap(|e| {
let expected_error = if cfg!(unix) {
ConnectionRefused
} else {
// On Win32, opening port 1 gives WSAEADDRNOTAVAIL error.
OtherIoError
};
assert_eq!(e.kind, expected_error);
assert_eq!(e.kind, ConnectionRefused);
called = true;
}).inside(|| {
let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 1 };
@ -171,10 +162,9 @@ mod test {
assert!(stream.is_none());
});
assert!(called);
}
})
#[test]
fn smoke_test_ip4() {
iotest!(fn smoke_test_ip4() {
let addr = next_test_ip4();
let (port, chan) = Chan::new();
@ -190,10 +180,9 @@ mod test {
let mut buf = [0];
stream.read(buf);
assert!(buf[0] == 99);
}
})
#[test]
fn smoke_test_ip6() {
iotest!(fn smoke_test_ip6() {
let addr = next_test_ip6();
let (port, chan) = Chan::new();
@ -209,10 +198,9 @@ mod test {
let mut buf = [0];
stream.read(buf);
assert!(buf[0] == 99);
}
})
#[test]
fn read_eof_ip4() {
iotest!(fn read_eof_ip4() {
let addr = next_test_ip4();
let (port, chan) = Chan::new();
@ -228,10 +216,9 @@ mod test {
let mut buf = [0];
let nread = stream.read(buf);
assert!(nread.is_none());
}
})
#[test]
fn read_eof_ip6() {
iotest!(fn read_eof_ip6() {
let addr = next_test_ip6();
let (port, chan) = Chan::new();
@ -247,10 +234,9 @@ mod test {
let mut buf = [0];
let nread = stream.read(buf);
assert!(nread.is_none());
}
})
#[test]
fn read_eof_twice_ip4() {
iotest!(fn read_eof_twice_ip4() {
let addr = next_test_ip4();
let (port, chan) = Chan::new();
@ -276,10 +262,9 @@ mod test {
let nread = stream.read(buf);
assert!(nread.is_none());
})
}
})
#[test]
fn read_eof_twice_ip6() {
iotest!(fn read_eof_twice_ip6() {
let addr = next_test_ip6();
let (port, chan) = Chan::new();
@ -305,10 +290,9 @@ mod test {
let nread = stream.read(buf);
assert!(nread.is_none());
})
}
})
#[test]
fn write_close_ip4() {
iotest!(fn write_close_ip4() {
let addr = next_test_ip4();
let (port, chan) = Chan::new();
@ -337,10 +321,9 @@ mod test {
});
if stop { break }
}
}
})
#[test]
fn write_close_ip6() {
iotest!(fn write_close_ip6() {
let addr = next_test_ip6();
let (port, chan) = Chan::new();
@ -369,10 +352,9 @@ mod test {
});
if stop { break }
}
}
})
#[test]
fn multiple_connect_serial_ip4() {
iotest!(fn multiple_connect_serial_ip4() {
let addr = next_test_ip4();
let max = 10;
let (port, chan) = Chan::new();
@ -392,10 +374,9 @@ mod test {
stream.read(buf);
assert_eq!(buf[0], 99);
}
}
})
#[test]
fn multiple_connect_serial_ip6() {
iotest!(fn multiple_connect_serial_ip6() {
let addr = next_test_ip6();
let max = 10;
let (port, chan) = Chan::new();
@ -415,10 +396,9 @@ mod test {
stream.read(buf);
assert_eq!(buf[0], 99);
}
}
})
#[test]
fn multiple_connect_interleaved_greedy_schedule_ip4() {
iotest!(fn multiple_connect_interleaved_greedy_schedule_ip4() {
let addr = next_test_ip4();
static MAX: int = 10;
let (port, chan) = Chan::new();
@ -453,10 +433,9 @@ mod test {
stream.write([i as u8]);
}
}
}
})
#[test]
fn multiple_connect_interleaved_greedy_schedule_ip6() {
iotest!(fn multiple_connect_interleaved_greedy_schedule_ip6() {
let addr = next_test_ip6();
static MAX: int = 10;
let (port, chan) = Chan::<()>::new();
@ -491,10 +470,9 @@ mod test {
stream.write([i as u8]);
}
}
}
})
#[test]
fn multiple_connect_interleaved_lazy_schedule_ip4() {
iotest!(fn multiple_connect_interleaved_lazy_schedule_ip4() {
let addr = next_test_ip4();
static MAX: int = 10;
let (port, chan) = Chan::new();
@ -529,9 +507,9 @@ mod test {
stream.write([99]);
}
}
}
#[test]
fn multiple_connect_interleaved_lazy_schedule_ip6() {
})
iotest!(fn multiple_connect_interleaved_lazy_schedule_ip6() {
let addr = next_test_ip6();
static MAX: int = 10;
let (port, chan) = Chan::new();
@ -566,10 +544,9 @@ mod test {
stream.write([99]);
}
}
}
})
#[cfg(test)]
fn socket_name(addr: SocketAddr) {
pub fn socket_name(addr: SocketAddr) {
let mut listener = TcpListener::bind(addr).unwrap();
// Make sure socket_name gives
@ -579,8 +556,7 @@ mod test {
assert_eq!(addr, so_name.unwrap());
}
#[cfg(test)]
fn peer_name(addr: SocketAddr) {
pub fn peer_name(addr: SocketAddr) {
let (port, chan) = Chan::new();
do spawn {
@ -603,16 +579,14 @@ mod test {
assert_eq!(addr, peer_name.unwrap());
}
#[test]
fn socket_and_peer_name_ip4() {
iotest!(fn socket_and_peer_name_ip4() {
peer_name(next_test_ip4());
socket_name(next_test_ip4());
}
})
#[test]
fn socket_and_peer_name_ip6() {
iotest!(fn socket_and_peer_name_ip6() {
// XXX: peer name is not consistent
//peer_name(next_test_ip6());
socket_name(next_test_ip6());
}
})
}

View file

@ -169,5 +169,152 @@ impl Drop for Process {
}
}
// Tests for this module can be found in the rtio-processes run-pass test, along
// with the justification for why it's not located here.
#[cfg(test)]
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");
})
}

View file

@ -18,7 +18,7 @@ use std::io::net::ip::*;
use sync::atomics::{AtomicUint, INIT_ATOMIC_UINT, Relaxed};
macro_rules! iotest (
{ fn $name:ident() $b:block } => (
{ fn $name:ident() $b:block $($a:attr)* } => (
mod $name {
#[allow(unused_imports)];
@ -28,18 +28,20 @@ macro_rules! iotest (
use prelude::*;
use io::*;
use io::fs::*;
use io::test::*;
use io::net::tcp::*;
use io::net::ip::*;
use io::net::udp::*;
#[cfg(unix)]
use io::net::unix::*;
use io::process::*;
use str;
use util;
fn f() $b
#[test] fn green() { f() }
#[test] fn native() {
$($a)* #[test] fn green() { f() }
$($a)* #[test] fn native() {
use native;
let (p, c) = Chan::new();
do native::task::spawn { c.send(f()) }
@ -90,9 +92,9 @@ fn base_port() -> u16 {
let bases = [
("32-opt", base + range * 1),
("32-noopt", base + range * 2),
("32-nopt", base + range * 2),
("64-opt", base + range * 3),
("64-noopt", base + range * 4),
("64-nopt", base + range * 4),
("64-opt-vg", base + range * 5),
("all-opt", base + range * 6),
("snap3", base + range * 7),

View file

@ -76,6 +76,7 @@ pub use libc::types::common::posix01::*;
pub use libc::types::common::posix08::*;
pub use libc::types::common::bsd44::*;
pub use libc::types::os::common::posix01::*;
pub use libc::types::os::common::bsd44::*;
pub use libc::types::os::arch::c95::*;
pub use libc::types::os::arch::c99::*;
pub use libc::types::os::arch::posix88::*;
@ -111,6 +112,7 @@ pub use libc::funcs::posix01::glob::*;
pub use libc::funcs::posix01::mman::*;
pub use libc::funcs::posix08::unistd::*;
pub use libc::funcs::bsd43::*;
pub use libc::funcs::bsd44::*;
pub use libc::funcs::extra::*;
@ -240,6 +242,40 @@ pub mod types {
__unused5: *c_void,
}
}
pub mod bsd44 {
pub type socklen_t = u32;
pub type sa_family_t = u16;
pub type in_port_t = u16;
pub type in_addr_t = u32;
pub struct sockaddr {
sa_family: sa_family_t,
sa_data: [u8, ..14],
}
pub struct sockaddr_storage {
ss_family: sa_family_t,
__ss_align: i64,
__ss_pad2: [u8, ..112],
}
pub struct sockaddr_in {
sin_family: sa_family_t,
sin_port: in_port_t,
sin_addr: in_addr,
sin_zero: [u8, ..8],
}
pub struct in_addr {
s_addr: in_addr_t,
}
pub struct sockaddr_in6 {
sin6_family: sa_family_t,
sin6_port: in_port_t,
sin6_flowinfo: u32,
sin6_addr: in6_addr,
sin6_scope_id: u32,
}
pub struct in6_addr {
s6_addr: [u16, ..8]
}
}
}
#[cfg(target_arch = "x86")]
@ -538,6 +574,45 @@ pub mod types {
__unused8: *c_void,
}
}
pub mod bsd44 {
pub type socklen_t = u32;
pub type sa_family_t = u8;
pub type in_port_t = u16;
pub type in_addr_t = u32;
pub struct sockaddr {
sa_len: u8,
sa_family: sa_family_t,
sa_data: [u8, ..14],
}
pub struct sockaddr_storage {
ss_len: u8,
ss_family: sa_family_t,
__ss_pad1: [u8, ..6],
__ss_align: i64,
__ss_pad2: [u8, ..112],
}
pub struct sockaddr_in {
sin_len: u8,
sin_family: sa_family_t,
sin_port: in_port_t,
sin_addr: in_addr,
sin_zero: [u8, ..8],
}
pub struct in_addr {
s_addr: in_addr_t,
}
pub struct sockaddr_in6 {
sin6_len: u8,
sin6_family: sa_family_t,
sin6_port: in_port_t,
sin6_flowinfo: u32,
sin6_addr: in6_addr,
sin6_scope_id: u32,
}
pub struct in6_addr {
s6_addr: [u16, ..8]
}
}
}
#[cfg(target_arch = "x86_64")]
@ -661,6 +736,44 @@ pub mod types {
modtime: time64_t,
}
}
pub mod bsd44 {
use libc::types::os::arch::c95::{c_int, c_uint};
pub type SOCKET = c_uint;
pub type socklen_t = c_int;
pub type sa_family_t = u16;
pub type in_port_t = u16;
pub type in_addr_t = u32;
pub struct sockaddr {
sa_family: sa_family_t,
sa_data: [u8, ..14],
}
pub struct sockaddr_storage {
ss_family: sa_family_t,
__ss_align: i64,
__ss_pad2: [u8, ..112],
}
pub struct sockaddr_in {
sin_family: sa_family_t,
sin_port: in_port_t,
sin_addr: in_addr,
sin_zero: [u8, ..8],
}
pub struct in_addr {
s_addr: in_addr_t,
}
pub struct sockaddr_in6 {
sin6_family: sa_family_t,
sin6_port: in_port_t,
sin6_flowinfo: u32,
sin6_addr: in6_addr,
sin6_scope_id: u32,
}
pub struct in6_addr {
s6_addr: [u16, ..8]
}
}
}
pub mod arch {
@ -900,6 +1013,48 @@ pub mod types {
__unused8: *c_void,
}
}
pub mod bsd44 {
use libc::types::os::arch::c95::c_int;
pub type socklen_t = c_int;
pub type sa_family_t = u8;
pub type in_port_t = u16;
pub type in_addr_t = u32;
pub struct sockaddr {
sa_len: u8,
sa_family: sa_family_t,
sa_data: [u8, ..14],
}
pub struct sockaddr_storage {
ss_len: u8,
ss_family: sa_family_t,
__ss_pad1: [u8, ..6],
__ss_align: i64,
__ss_pad2: [u8, ..112],
}
pub struct sockaddr_in {
sin_len: u8,
sin_family: sa_family_t,
sin_port: in_port_t,
sin_addr: in_addr,
sin_zero: [u8, ..8],
}
pub struct in_addr {
s_addr: in_addr_t,
}
pub struct sockaddr_in6 {
sin6_len: u8,
sin6_family: sa_family_t,
sin6_port: in_port_t,
sin6_flowinfo: u32,
sin6_addr: in6_addr,
sin6_scope_id: u32,
}
pub struct in6_addr {
s6_addr: [u16, ..8]
}
}
}
#[cfg(target_arch = "x86")]
@ -1109,6 +1264,59 @@ pub mod consts {
pub static FILENAME_MAX : c_uint = 260_u32;
pub static L_tmpnam : c_uint = 16_u32;
pub static TMP_MAX : c_uint = 32767_u32;
pub static WSAEINTR: c_int = 10004;
pub static WSAEBADF: c_int = 10009;
pub static WSAEACCES: c_int = 10013;
pub static WSAEFAULT: c_int = 10014;
pub static WSAEINVAL: c_int = 10022;
pub static WSAEMFILE: c_int = 10024;
pub static WSAEWOULDBLOCK: c_int = 10035;
pub static WSAEINPROGRESS: c_int = 10036;
pub static WSAEALREADY: c_int = 10037;
pub static WSAENOTSOCK: c_int = 10038;
pub static WSAEDESTADDRREQ: c_int = 10039;
pub static WSAEMSGSIZE: c_int = 10040;
pub static WSAEPROTOTYPE: c_int = 10041;
pub static WSAENOPROTOOPT: c_int = 10042;
pub static WSAEPROTONOSUPPORT: c_int = 10043;
pub static WSAESOCKTNOSUPPORT: c_int = 10044;
pub static WSAEOPNOTSUPP: c_int = 10045;
pub static WSAEPFNOSUPPORT: c_int = 10046;
pub static WSAEAFNOSUPPORT: c_int = 10047;
pub static WSAEADDRINUSE: c_int = 10048;
pub static WSAEADDRNOTAVAIL: c_int = 10049;
pub static WSAENETDOWN: c_int = 10050;
pub static WSAENETUNREACH: c_int = 10051;
pub static WSAENETRESET: c_int = 10052;
pub static WSAECONNABORTED: c_int = 10053;
pub static WSAECONNRESET: c_int = 10054;
pub static WSAENOBUFS: c_int = 10055;
pub static WSAEISCONN: c_int = 10056;
pub static WSAENOTCONN: c_int = 10057;
pub static WSAESHUTDOWN: c_int = 10058;
pub static WSAETOOMANYREFS: c_int = 10059;
pub static WSAETIMEDOUT: c_int = 10060;
pub static WSAECONNREFUSED: c_int = 10061;
pub static WSAELOOP: c_int = 10062;
pub static WSAENAMETOOLONG: c_int = 10063;
pub static WSAEHOSTDOWN: c_int = 10064;
pub static WSAEHOSTUNREACH: c_int = 10065;
pub static WSAENOTEMPTY: c_int = 10066;
pub static WSAEPROCLIM: c_int = 10067;
pub static WSAEUSERS: c_int = 10068;
pub static WSAEDQUOT: c_int = 10069;
pub static WSAESTALE: c_int = 10070;
pub static WSAEREMOTE: c_int = 10071;
pub static WSASYSNOTREADY: c_int = 10091;
pub static WSAVERNOTSUPPORTED: c_int = 10092;
pub static WSANOTINITIALISED: c_int = 10093;
pub static WSAEDISCON: c_int = 10101;
pub static WSAENOMORE: c_int = 10102;
pub static WSAECANCELLED: c_int = 10103;
pub static WSAEINVALIDPROCTABLE: c_int = 10104;
pub static WSAEINVALIDPROVIDER: c_int = 10105;
pub static WSAEPROVIDERFAILEDINIT: c_int = 10106;
}
pub mod c99 {
}
@ -1149,6 +1357,17 @@ pub mod consts {
pub mod posix08 {
}
pub mod bsd44 {
use libc::types::os::arch::c95::c_int;
pub static AF_INET: c_int = 2;
pub static AF_INET6: c_int = 23;
pub static SOCK_STREAM: c_int = 1;
pub static SOCK_DGRAM: c_int = 2;
pub static IPPROTO_TCP: c_int = 6;
pub static TCP_NODELAY: c_int = 0x0001;
pub static SOL_SOCKET: c_int = 0xffff;
pub static SO_KEEPALIVE: c_int = 8;
}
pub mod extra {
use libc::types::os::arch::c95::c_int;
@ -1845,6 +2064,16 @@ pub mod consts {
pub static MADV_MERGEABLE : c_int = 12;
pub static MADV_UNMERGEABLE : c_int = 13;
pub static MADV_HWPOISON : c_int = 100;
pub static AF_INET: c_int = 2;
pub static AF_INET6: c_int = 10;
pub static SOCK_STREAM: c_int = 1;
pub static SOCK_DGRAM: c_int = 2;
pub static IPPROTO_TCP: c_int = 6;
pub static TCP_NODELAY: c_int = 1;
pub static SOL_SOCKET: c_int = 1;
pub static SO_KEEPALIVE: c_int = 9;
}
#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
@ -2262,6 +2491,17 @@ pub mod consts {
pub static MINCORE_REFERENCED_OTHER : c_int = 0x8;
pub static MINCORE_MODIFIED_OTHER : c_int = 0x10;
pub static MINCORE_SUPER : c_int = 0x20;
pub static AF_INET: c_int = 2;
pub static AF_INET6: c_int = 28;
pub static SOCK_STREAM: c_int = 1;
pub static SOCK_DGRAM: c_int = 2;
pub static IPPROTO_TCP: c_int = 6;
pub static TCP_NODELAY: c_int = 1;
pub static TCP_KEEPIDLE: c_int = 256;
pub static SOL_SOCKET: c_int = 0xffff;
pub static SO_KEEPALIVE: c_int = 0x0008;
}
pub mod extra {
use libc::types::os::arch::c95::c_int;
@ -2616,6 +2856,17 @@ pub mod consts {
pub static MINCORE_MODIFIED : c_int = 0x4;
pub static MINCORE_REFERENCED_OTHER : c_int = 0x8;
pub static MINCORE_MODIFIED_OTHER : c_int = 0x10;
pub static AF_INET: c_int = 2;
pub static AF_INET6: c_int = 30;
pub static SOCK_STREAM: c_int = 1;
pub static SOCK_DGRAM: c_int = 2;
pub static IPPROTO_TCP: c_int = 6;
pub static TCP_NODELAY: c_int = 0x01;
pub static TCP_KEEPALIVE: c_int = 0x10;
pub static SOL_SOCKET: c_int = 0xffff;
pub static SO_KEEPALIVE: c_int = 0x0008;
}
pub mod extra {
use libc::types::os::arch::c95::c_int;
@ -3296,6 +3547,63 @@ pub mod funcs {
}
}
#[cfg(not(windows))]
pub mod bsd43 {
use libc::types::common::c95::{c_void};
use libc::types::os::common::bsd44::{socklen_t, sockaddr};
use libc::types::os::arch::c95::{c_int, size_t};
use libc::types::os::arch::posix88::ssize_t;
extern "system" {
pub fn socket(domain: c_int, ty: c_int, protocol: c_int) -> c_int;
pub fn connect(socket: c_int, address: *sockaddr,
len: socklen_t) -> c_int;
pub fn bind(socket: c_int, address: *sockaddr,
address_len: socklen_t) -> c_int;
pub fn listen(socket: c_int, backlog: c_int) -> c_int;
pub fn accept(socket: c_int, address: *mut sockaddr,
address_len: *mut socklen_t) -> c_int;
pub fn getpeername(socket: c_int, address: *mut sockaddr,
address_len: *mut socklen_t) -> c_int;
pub fn getsockname(socket: c_int, address: *mut sockaddr,
address_len: *mut socklen_t) -> c_int;
pub fn setsockopt(socket: c_int, level: c_int, name: c_int,
value: *c_void, option_len: socklen_t) -> c_int;
pub fn recv(socket: c_int, buf: *mut c_void, len: size_t,
flags: c_int) -> ssize_t;
pub fn send(socket: c_int, buf: *mut c_void, len: size_t,
flags: c_int) -> ssize_t;
}
}
#[cfg(windows)]
pub mod bsd43 {
use libc::types::common::c95::{c_void};
use libc::types::os::common::bsd44::{socklen_t, sockaddr, SOCKET};
use libc::types::os::arch::c95::c_int;
extern "system" {
pub fn socket(domain: c_int, ty: c_int, protocol: c_int) -> SOCKET;
pub fn connect(socket: SOCKET, address: *sockaddr,
len: socklen_t) -> c_int;
pub fn bind(socket: SOCKET, address: *sockaddr,
address_len: socklen_t) -> c_int;
pub fn listen(socket: SOCKET, backlog: c_int) -> c_int;
pub fn accept(socket: SOCKET, address: *mut sockaddr,
address_len: *mut socklen_t) -> SOCKET;
pub fn getpeername(socket: SOCKET, address: *mut sockaddr,
address_len: *mut socklen_t) -> c_int;
pub fn getsockname(socket: SOCKET, address: *mut sockaddr,
address_len: *mut socklen_t) -> c_int;
pub fn setsockopt(socket: SOCKET, level: c_int, name: c_int,
value: *c_void, option_len: socklen_t) -> c_int;
pub fn closesocket(socket: SOCKET) -> c_int;
pub fn recv(socket: SOCKET, buf: *mut c_void, len: c_int,
flags: c_int) -> c_int;
pub fn send(socket: SOCKET, buf: *mut c_void, len: c_int,
flags: c_int) -> c_int;
}
}
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]

View file

@ -339,7 +339,7 @@ mod tests {
use task::spawn;
use unstable::running_on_valgrind;
use io::pipe::PipeStream;
use io::{Writer, Reader, io_error, FileNotFound, OtherIoError};
use io::{Writer, Reader, io_error, FileNotFound};
#[test]
#[cfg(not(target_os="android"))] // FIXME(#10380)

View file

@ -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");
}