diff --git a/src/libnative/io/file.rs b/src/libnative/io/file.rs index c1a378c7e3cc1..6197bd70c7610 100644 --- a/src/libnative/io/file.rs +++ b/src/libnative/io/file.rs @@ -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 { + // 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 { #[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 { diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index 32056215e7c97..9e76dea5ebf73 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -44,6 +44,7 @@ pub use self::process::Process; // Native I/O implementations pub mod file; pub mod process; +pub mod net; type IoResult = Result; @@ -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 { + 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, diff --git a/src/libnative/io/net.rs b/src/libnative/io/net.rs new file mode 100644 index 0000000000000..aaa95ce0cfbe2 --- /dev/null +++ b/src/libnative/io/net.rs @@ -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 or the MIT license +// , 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::() + } + 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::() + } + }; + return (storage, len); + } +} + +fn socket(addr: ip::SocketAddr) -> IoResult { + 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 +{ + let mut storage: libc::sockaddr_storage = unsafe { intrinsics::init() }; + let mut len = mem::size_of::() 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::()); + 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::()); + 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 { + 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::() + as libc::socklen_t)) + } + } + + fn set_keepalive(&mut self, seconds: Option) -> 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::() + 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::() + 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::() + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + unsafe { + let mut storage: libc::sockaddr_storage = intrinsics::init(); + let storagep = &mut storage as *mut libc::sockaddr_storage; + let size = mem::size_of::(); + 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 { + 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(()) } +} diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs index 64ce9d7e3482d..3fda448692159 100644 --- a/src/libnative/io/process.rs +++ b/src/libnative/io/process.rs @@ -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, + priv exit_code: Option, } 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 { 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 { 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 - } - - #[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 + 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 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 } diff --git a/src/libnative/task.rs b/src/libnative/task.rs index 12e361d8041c8..8f2dff424049b 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -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> { - 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)) } } diff --git a/src/librustuv/lib.rs b/src/librustuv/lib.rs index 62cd0c4836ce6..5d08656b798cb 100644 --- a/src/librustuv/lib.rs +++ b/src/librustuv/lib.rs @@ -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 diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs index ad5fad99f2063..dfe67f050ec9f 100644 --- a/src/librustuv/uvll.rs +++ b/src/librustuv/uvll.rs @@ -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; diff --git a/src/libstd/io/comm_adapters.rs b/src/libstd/io/comm_adapters.rs index 7a78800c33d35..9b8a8b57e678e 100644 --- a/src/libstd/io/comm_adapters.rs +++ b/src/libstd/io/comm_adapters.rs @@ -117,7 +117,6 @@ mod test { use prelude::*; use super::*; use io; - use comm; use task; #[test] diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index e7787692dd2f5..52cca7f622b53 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -134,13 +134,11 @@ impl Acceptor 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()); - } + }) } diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index bbb2a7ef3984d..a8b7e8e00ead0 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -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"); + }) + +} diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index 4be1122796584..92b2cfa8be200 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -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), diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs index 556009c283896..2696e27c37317 100644 --- a/src/libstd/libc.rs +++ b/src/libstd/libc.rs @@ -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")] diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 69704c855ee8f..33be746e604d9 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -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) diff --git a/src/test/run-pass/rtio-processes.rs b/src/test/run-pass/rtio-processes.rs deleted file mode 100644 index 6463a1d532171..0000000000000 --- a/src/test/run-pass/rtio-processes.rs +++ /dev/null @@ -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 or the MIT license -// , 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"); -}