Runtime removal: add private sys, sys_common modules

These modules will house the code that used to be part of the runtime system
in libnative. The `sys_common` module contains a few low-level but
cross-platform details. The `sys` module is set up using `#[cfg()]` to
include either a unix or windows implementation of a common API
surface. This API surface is *not* exported directly in `libstd`, but is
instead used to bulid `std::os` and `std::io`.

Ultimately, the low-level details in `sys` will be exposed in a
controlled way through a separate platform-specific surface, but that
setup is not part of this patch.
This commit is contained in:
Aaron Turon 2014-09-30 17:03:56 -07:00
parent 93c85eb8bd
commit 3a527f2b33
12 changed files with 592 additions and 456 deletions

View File

@ -73,9 +73,6 @@
#[path = "tty_windows.rs"]
mod tty;
#[cfg(unix)] #[path = "c_unix.rs"] mod c;
#[cfg(windows)] #[path = "c_windows.rs"] mod c;
fn unimpl() -> IoError {
#[cfg(unix)] use libc::ENOSYS as ERROR;
#[cfg(windows)] use libc::ERROR_CALL_NOT_IMPLEMENTED as ERROR;

View File

@ -1,209 +0,0 @@
// Copyright 2014 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 libc;
use std::cmp;
use std::mem;
use std::os;
use std::ptr;
use std::rt::rtio::{IoResult, IoError};
use super::c;
use super::net;
use super::{retry, last_error};
#[deriving(Show)]
pub enum SocketStatus {
Readable,
Writable,
}
pub fn timeout(desc: &'static str) -> IoError {
#[cfg(unix)] use libc::ETIMEDOUT as ERROR;
#[cfg(windows)] use libc::ERROR_OPERATION_ABORTED as ERROR;
IoError {
code: ERROR as uint,
extra: 0,
detail: Some(desc.to_string()),
}
}
pub fn short_write(n: uint, desc: &'static str) -> IoError {
#[cfg(unix)] use libc::EAGAIN as ERROR;
#[cfg(windows)] use libc::ERROR_OPERATION_ABORTED as ERROR;
IoError {
code: ERROR as uint,
extra: n,
detail: Some(desc.to_string()),
}
}
pub fn eof() -> IoError {
IoError {
code: libc::EOF as uint,
extra: 0,
detail: None,
}
}
#[cfg(windows)]
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
libc::timeval {
tv_sec: (ms / 1000) as libc::c_long,
tv_usec: ((ms % 1000) * 1000) as libc::c_long,
}
}
#[cfg(not(windows))]
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
libc::timeval {
tv_sec: (ms / 1000) as libc::time_t,
tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
}
}
#[cfg(unix)]
pub fn wouldblock() -> bool {
let err = os::errno();
err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
}
#[cfg(windows)]
pub fn wouldblock() -> bool {
let err = os::errno();
err == libc::WSAEWOULDBLOCK as uint
}
#[cfg(unix)]
pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
let set = nb as libc::c_int;
super::mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
}
#[cfg(windows)]
pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
let mut set = nb as libc::c_ulong;
if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) != 0 } {
Err(last_error())
} else {
Ok(())
}
}
// See http://developerweb.net/viewtopic.php?id=3196 for where this is
// derived from.
pub fn connect_timeout(fd: net::sock_t,
addrp: *const libc::sockaddr,
len: libc::socklen_t,
timeout_ms: u64) -> IoResult<()> {
use std::os;
#[cfg(unix)] use libc::EINPROGRESS as INPROGRESS;
#[cfg(windows)] use libc::WSAEINPROGRESS as INPROGRESS;
#[cfg(unix)] use libc::EWOULDBLOCK as WOULDBLOCK;
#[cfg(windows)] use libc::WSAEWOULDBLOCK as WOULDBLOCK;
// Make sure the call to connect() doesn't block
try!(set_nonblocking(fd, true));
let ret = match unsafe { libc::connect(fd, addrp, len) } {
// If the connection is in progress, then we need to wait for it to
// finish (with a timeout). The current strategy for doing this is
// to use select() with a timeout.
-1 if os::errno() as int == INPROGRESS as int ||
os::errno() as int == WOULDBLOCK as int => {
let mut set: c::fd_set = unsafe { mem::zeroed() };
c::fd_set(&mut set, fd);
match await(fd, &mut set, timeout_ms) {
0 => Err(timeout("connection timed out")),
-1 => Err(last_error()),
_ => {
let err: libc::c_int = try!(
net::getsockopt(fd, libc::SOL_SOCKET, libc::SO_ERROR));
if err == 0 {
Ok(())
} else {
Err(IoError {
code: err as uint,
extra: 0,
detail: Some(os::error_string(err as uint)),
})
}
}
}
}
-1 => Err(last_error()),
_ => Ok(()),
};
// be sure to turn blocking I/O back on
try!(set_nonblocking(fd, false));
return ret;
#[cfg(unix)]
fn await(fd: net::sock_t, set: &mut c::fd_set,
timeout: u64) -> libc::c_int {
let start = ::io::timer::now();
retry(|| unsafe {
// Recalculate the timeout each iteration (it is generally
// undefined what the value of the 'tv' is after select
// returns EINTR).
let mut tv = ms_to_timeval(timeout - (::io::timer::now() - start));
c::select(fd + 1, ptr::null_mut(), set as *mut _,
ptr::null_mut(), &mut tv)
})
}
#[cfg(windows)]
fn await(_fd: net::sock_t, set: &mut c::fd_set,
timeout: u64) -> libc::c_int {
let mut tv = ms_to_timeval(timeout);
unsafe { c::select(1, ptr::null_mut(), set, ptr::null_mut(), &mut tv) }
}
}
pub fn await(fds: &[net::sock_t], deadline: Option<u64>,
status: SocketStatus) -> IoResult<()> {
let mut set: c::fd_set = unsafe { mem::zeroed() };
let mut max = 0;
for &fd in fds.iter() {
c::fd_set(&mut set, fd);
max = cmp::max(max, fd + 1);
}
if cfg!(windows) {
max = fds.len() as net::sock_t;
}
let (read, write) = match status {
Readable => (&mut set as *mut _, ptr::null_mut()),
Writable => (ptr::null_mut(), &mut set as *mut _),
};
let mut tv: libc::timeval = unsafe { mem::zeroed() };
match retry(|| {
let now = ::io::timer::now();
let tvp = match deadline {
None => ptr::null_mut(),
Some(deadline) => {
// If we're past the deadline, then pass a 0 timeout to
// select() so we can poll the status
let ms = if deadline < now {0} else {deadline - now};
tv = ms_to_timeval(ms);
&mut tv as *mut _
}
};
let r = unsafe {
c::select(max as libc::c_int, read, write, ptr::null_mut(), tvp)
};
r
}) {
-1 => Err(last_error()),
0 => Err(timeout("timed out")),
_ => Ok(()),
}
}

View File

@ -236,8 +236,7 @@ fn file_product(p: &Path) -> IoResult<u32> {
use boxed::Box;
use result::{Ok, Err, Result};
use rt::rtio;
use slice::{AsSlice, SlicePrelude};
use str::{Str, StrPrelude};
use sys;
use str;
use string::String;
use uint;
@ -312,91 +311,10 @@ impl IoError {
/// struct is filled with an allocated string describing the error
/// in more detail, retrieved from the operating system.
pub fn from_errno(errno: uint, detail: bool) -> IoError {
#[cfg(windows)]
fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
match errno {
libc::EOF => (EndOfFile, "end of file"),
libc::ERROR_NO_DATA => (BrokenPipe, "the pipe is being closed"),
libc::ERROR_FILE_NOT_FOUND => (FileNotFound, "file not found"),
libc::ERROR_INVALID_NAME => (InvalidInput, "invalid file name"),
libc::WSAECONNREFUSED => (ConnectionRefused, "connection refused"),
libc::WSAECONNRESET => (ConnectionReset, "connection reset"),
libc::ERROR_ACCESS_DENIED | libc::WSAEACCES =>
(PermissionDenied, "permission denied"),
libc::WSAEWOULDBLOCK => {
(ResourceUnavailable, "resource temporarily unavailable")
}
libc::WSAENOTCONN => (NotConnected, "not connected"),
libc::WSAECONNABORTED => (ConnectionAborted, "connection aborted"),
libc::WSAEADDRNOTAVAIL => (ConnectionRefused, "address not available"),
libc::WSAEADDRINUSE => (ConnectionRefused, "address in use"),
libc::ERROR_BROKEN_PIPE => (EndOfFile, "the pipe has ended"),
libc::ERROR_OPERATION_ABORTED =>
(TimedOut, "operation timed out"),
libc::WSAEINVAL => (InvalidInput, "invalid argument"),
libc::ERROR_CALL_NOT_IMPLEMENTED =>
(IoUnavailable, "function not implemented"),
libc::ERROR_INVALID_HANDLE =>
(MismatchedFileTypeForOperation,
"invalid handle provided to function"),
libc::ERROR_NOTHING_TO_TERMINATE =>
(InvalidInput, "no process to kill"),
// libuv maps this error code to EISDIR. we do too. if it is found
// to be incorrect, we can add in some more machinery to only
// return this message when ERROR_INVALID_FUNCTION after certain
// Windows calls.
libc::ERROR_INVALID_FUNCTION => (InvalidInput,
"illegal operation on a directory"),
_ => (OtherIoError, "unknown error")
}
}
#[cfg(not(windows))]
fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
// FIXME: this should probably be a bit more descriptive...
match errno {
libc::EOF => (EndOfFile, "end of file"),
libc::ECONNREFUSED => (ConnectionRefused, "connection refused"),
libc::ECONNRESET => (ConnectionReset, "connection reset"),
libc::EPERM | libc::EACCES =>
(PermissionDenied, "permission denied"),
libc::EPIPE => (BrokenPipe, "broken pipe"),
libc::ENOTCONN => (NotConnected, "not connected"),
libc::ECONNABORTED => (ConnectionAborted, "connection aborted"),
libc::EADDRNOTAVAIL => (ConnectionRefused, "address not available"),
libc::EADDRINUSE => (ConnectionRefused, "address in use"),
libc::ENOENT => (FileNotFound, "no such file or directory"),
libc::EISDIR => (InvalidInput, "illegal operation on a directory"),
libc::ENOSYS => (IoUnavailable, "function not implemented"),
libc::EINVAL => (InvalidInput, "invalid argument"),
libc::ENOTTY =>
(MismatchedFileTypeForOperation,
"file descriptor is not a TTY"),
libc::ETIMEDOUT => (TimedOut, "operation timed out"),
libc::ECANCELED => (TimedOut, "operation aborted"),
// 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 =>
(ResourceUnavailable, "resource temporarily unavailable"),
_ => (OtherIoError, "unknown error")
}
}
let (kind, desc) = get_err(errno as i32);
IoError {
kind: kind,
desc: desc,
detail: if detail && kind == OtherIoError {
Some(os::error_string(errno).as_slice().chars().map(|c| c.to_lowercase()).collect())
} else {
None
},
let mut err = sys::decode_error(errno as i32);
if detail && err.kind == OtherIoError {
err.detail = Some(os::error_string(errno).as_slice().chars()
.map(|c| c.to_lowercase()).collect())
}
}

View File

@ -242,6 +242,13 @@
pub mod path;
pub mod fmt;
#[cfg(unix)]
#[path = "sys/unix/mod.rs"] mod sys;
#[cfg(windows)]
#[path = "sys/windows/mod.rs"] mod sys;
#[path = "sys/common/mod.rs"] mod sys_common;
// FIXME #7809: This shouldn't be pub, and it should be reexported under 'unstable'
// but name resolution doesn't work without it being pub.
pub mod rt;

View File

@ -34,7 +34,7 @@
use clone::Clone;
use error::{FromError, Error};
use fmt;
use io::{IoResult, IoError};
use io::IoResult;
use iter::Iterator;
use libc::{c_void, c_int};
use libc;
@ -43,6 +43,7 @@
use option::{Some, None, Option};
use os;
use path::{Path, GenericPath, BytesContainer};
use sys::os as os_imp;
use ptr::RawPtr;
use ptr;
use result::{Err, Ok, Result};
@ -905,59 +906,9 @@ fn chdir(p: &Path) -> bool {
}
}
#[cfg(unix)]
/// Returns the platform-specific value of errno
pub fn errno() -> int {
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd"))]
fn errno_location() -> *const c_int {
extern {
fn __error() -> *const c_int;
}
unsafe {
__error()
}
}
#[cfg(target_os = "dragonfly")]
fn errno_location() -> *const c_int {
extern {
fn __dfly_error() -> *const c_int;
}
unsafe {
__dfly_error()
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn errno_location() -> *const c_int {
extern {
fn __errno_location() -> *const c_int;
}
unsafe {
__errno_location()
}
}
unsafe {
(*errno_location()) as int
}
}
#[cfg(windows)]
/// Returns the platform-specific value of errno
pub fn errno() -> uint {
use libc::types::os::arch::extra::DWORD;
#[link_name = "kernel32"]
extern "system" {
fn GetLastError() -> DWORD;
}
unsafe {
GetLastError() as uint
}
os_imp::errno() as uint
}
/// Return the string corresponding to an `errno()` value of `errnum`.
@ -969,105 +920,7 @@ pub fn errno() -> uint {
/// println!("{}", os::error_string(os::errno() as uint));
/// ```
pub fn error_string(errnum: uint) -> String {
return strerror(errnum);
#[cfg(unix)]
fn strerror(errnum: uint) -> String {
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly"))]
fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t)
-> c_int {
extern {
fn strerror_r(errnum: c_int, buf: *mut c_char,
buflen: libc::size_t) -> c_int;
}
unsafe {
strerror_r(errnum, buf, buflen)
}
}
// GNU libc provides a non-compliant version of strerror_r by default
// and requires macros to instead use the POSIX compliant variant.
// So we just use __xpg_strerror_r which is always POSIX compliant
#[cfg(target_os = "linux")]
fn strerror_r(errnum: c_int, buf: *mut c_char,
buflen: libc::size_t) -> c_int {
extern {
fn __xpg_strerror_r(errnum: c_int,
buf: *mut c_char,
buflen: libc::size_t)
-> c_int;
}
unsafe {
__xpg_strerror_r(errnum, buf, buflen)
}
}
let mut buf = [0 as c_char, ..TMPBUF_SZ];
let p = buf.as_mut_ptr();
unsafe {
if strerror_r(errnum as c_int, p, buf.len() as libc::size_t) < 0 {
panic!("strerror_r failure");
}
::string::raw::from_buf(p as *const u8)
}
}
#[cfg(windows)]
fn strerror(errnum: uint) -> String {
use libc::types::os::arch::extra::DWORD;
use libc::types::os::arch::extra::LPWSTR;
use libc::types::os::arch::extra::LPVOID;
use libc::types::os::arch::extra::WCHAR;
#[link_name = "kernel32"]
extern "system" {
fn FormatMessageW(flags: DWORD,
lpSrc: LPVOID,
msgId: DWORD,
langId: DWORD,
buf: LPWSTR,
nsize: DWORD,
args: *const c_void)
-> DWORD;
}
static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
// This value is calculated from the macro
// MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
let langId = 0x0800 as DWORD;
let mut buf = [0 as WCHAR, ..TMPBUF_SZ];
unsafe {
let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null_mut(),
errnum as DWORD,
langId,
buf.as_mut_ptr(),
buf.len() as DWORD,
ptr::null());
if res == 0 {
// Sometimes FormatMessageW can fail e.g. system doesn't like langId,
let fm_err = errno();
return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
}
let msg = String::from_utf16(::str::truncate_utf16_at_nul(buf));
match msg {
Some(msg) => format!("OS Error {}: {}", errnum, msg),
None => format!("OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum),
}
}
}
return os_imp::error_string(errnum as i32);
}
/// Get a string representing the platform-dependent last error

View File

@ -0,0 +1,91 @@
// Copyright 2014 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.
#![allow(missing_doc)]
#![allow(dead_code)]
use io::{mod, IoError, IoResult};
use prelude::*;
use num;
use sys::{last_error, retry, fs};
use c_str::CString;
use path::BytesContainer;
use collections;
pub mod net;
// common error constructors
pub fn eof() -> IoError {
IoError {
kind: io::EndOfFile,
desc: "end of file",
detail: None,
}
}
pub fn timeout(desc: &'static str) -> IoError {
IoError {
kind: io::TimedOut,
desc: desc,
detail: None,
}
}
pub fn short_write(n: uint, desc: &'static str) -> IoError {
IoError {
kind: if n == 0 { io::TimedOut } else { io::ShortWrite(n) },
desc: desc,
detail: None,
}
}
// unix has nonzero values as errors
pub fn mkerr_libc<Int: num::Zero>(ret: Int) -> IoResult<()> {
if !ret.is_zero() {
Err(last_error())
} else {
Ok(())
}
}
pub fn keep_going(data: &[u8], f: |*const u8, uint| -> i64) -> i64 {
let origamt = data.len();
let mut data = data.as_ptr();
let mut amt = origamt;
while amt > 0 {
let ret = retry(|| f(data, amt));
if ret == 0 {
break
} else if ret != -1 {
amt -= ret as uint;
data = unsafe { data.offset(ret as int) };
} else {
return ret;
}
}
return (origamt - amt) as i64;
}
// traits for extracting representations from
pub trait AsFileDesc {
fn as_fd(&self) -> &fs::FileDesc;
}
pub trait ProcessConfig<K: BytesContainer, V: BytesContainer> {
fn program(&self) -> &CString;
fn args(&self) -> &[CString];
fn env(&self) -> Option<&collections::HashMap<K, V>>;
fn cwd(&self) -> Option<&CString>;
fn uid(&self) -> Option<uint>;
fn gid(&self) -> Option<uint>;
fn detach(&self) -> bool;
}

View File

@ -11,6 +11,7 @@
//! C definitions used by libnative that don't belong in liblibc
#![allow(dead_code)]
#![allow(non_camel_case_types)]
pub use self::select::fd_set;
pub use self::signal::{sigaction, siginfo, sigset_t};
@ -106,7 +107,7 @@ pub fn fd_set(set: &mut fd_set, fd: i32) {
target_os = "dragonfly",
target_os = "linux"))]
mod select {
use std::uint;
use uint;
use libc;
pub const FD_SETSIZE: uint = 1024;

View File

@ -0,0 +1,92 @@
// Copyright 2014 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.
extern crate libc;
use num;
use prelude::*;
use io::{mod, IoResult, IoError};
pub mod fs;
pub mod os;
pub mod c;
pub type sock_t = io::file::fd_t;
pub type wrlen = libc::size_t;
pub unsafe fn close_sock(sock: sock_t) { let _ = libc::close(sock); }
pub fn last_error() -> IoError {
let errno = os::errno() as i32;
let mut err = decode_error(errno);
err.detail = Some(os::error_string(errno));
err
}
/// Convert an `errno` value into a high-level error variant and description.
pub fn decode_error(errno: i32) -> IoError {
// FIXME: this should probably be a bit more descriptive...
let (kind, desc) = 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"),
libc::ENOENT => (io::FileNotFound, "no such file or directory"),
libc::EISDIR => (io::InvalidInput, "illegal operation on a directory"),
libc::ENOSYS => (io::IoUnavailable, "function not implemented"),
libc::EINVAL => (io::InvalidInput, "invalid argument"),
libc::ENOTTY =>
(io::MismatchedFileTypeForOperation,
"file descriptor is not a TTY"),
libc::ETIMEDOUT => (io::TimedOut, "operation timed out"),
libc::ECANCELED => (io::TimedOut, "operation aborted"),
// 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")
};
IoError { kind: kind, desc: desc, detail: None }
}
#[inline]
pub fn retry<I: PartialEq + num::One + Neg<I>> (f: || -> I) -> I {
let minus_one = -num::one::<I>();
loop {
let n = f();
if n == minus_one && os::errno() == libc::EINTR as int { }
else { return n }
}
}
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
libc::timeval {
tv_sec: (ms / 1000) as libc::time_t,
tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
}
}
pub fn wouldblock() -> bool {
let err = os::errno();
err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
}
pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
let set = nb as libc::c_int;
super::mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
}

101
src/libstd/sys/unix/os.rs Normal file
View File

@ -0,0 +1,101 @@
// Copyright 2014 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 libc;
use libc::{c_int, c_char};
use prelude::*;
use os::TMPBUF_SZ;
/// Returns the platform-specific value of errno
pub fn errno() -> int {
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "freebsd"))]
fn errno_location() -> *const c_int {
extern {
fn __error() -> *const c_int;
}
unsafe {
__error()
}
}
#[cfg(target_os = "dragonfly")]
fn errno_location() -> *const c_int {
extern {
fn __dfly_error() -> *const c_int;
}
unsafe {
__dfly_error()
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn errno_location() -> *const c_int {
extern {
fn __errno_location() -> *const c_int;
}
unsafe {
__errno_location()
}
}
unsafe {
(*errno_location()) as int
}
}
/// Get a detailed string description for the given error number
pub fn error_string(errno: i32) -> String {
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly"))]
fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t)
-> c_int {
extern {
fn strerror_r(errnum: c_int, buf: *mut c_char,
buflen: libc::size_t) -> c_int;
}
unsafe {
strerror_r(errnum, buf, buflen)
}
}
// GNU libc provides a non-compliant version of strerror_r by default
// and requires macros to instead use the POSIX compliant variant.
// So we just use __xpg_strerror_r which is always POSIX compliant
#[cfg(target_os = "linux")]
fn strerror_r(errnum: c_int, buf: *mut c_char,
buflen: libc::size_t) -> c_int {
extern {
fn __xpg_strerror_r(errnum: c_int,
buf: *mut c_char,
buflen: libc::size_t)
-> c_int;
}
unsafe {
__xpg_strerror_r(errnum, buf, buflen)
}
}
let mut buf = [0 as c_char, ..TMPBUF_SZ];
let p = buf.as_mut_ptr();
unsafe {
if strerror_r(errno as c_int, p, buf.len() as libc::size_t) < 0 {
panic!("strerror_r failure");
}
::string::raw::from_buf(p as *const u8)
}
}

View File

@ -11,8 +11,11 @@
//! C definitions used by libnative that don't belong in liblibc
#![allow(overflowing_literals)]
#![allow(dead_code)]
#![allow(non_camel_case_types)]
use libc;
use prelude::*;
pub const WSADESCRIPTION_LEN: uint = 256;
pub const WSASYS_STATUS_LEN: uint = 128;
@ -127,9 +130,10 @@ pub fn CancelIoEx(hFile: libc::HANDLE,
}
pub mod compat {
use std::intrinsics::{atomic_store_relaxed, transmute};
use std::iter::Iterator;
use intrinsics::{atomic_store_relaxed, transmute};
use iter::Iterator;
use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
use prelude::*;
extern "system" {
fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
@ -174,17 +178,17 @@ pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
extern "system" fn thunk($($argname: $argtype),*) -> $rettype {
unsafe {
::io::c::compat::store_func(&mut ptr as *mut _ as *mut uint,
::sys::c::compat::store_func(&mut ptr as *mut _ as *mut uint,
stringify!($module),
stringify!($symbol),
fallback as uint);
::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
}
}
extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback
::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
}
);

View File

@ -0,0 +1,178 @@
// Copyright 2014 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.
#![allow(missing_doc)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused_imports)]
#![allow(dead_code)]
#![allow(unused_unsafe)]
#![allow(unused_mut)]
extern crate libc;
use num;
use mem;
use prelude::*;
use io::{mod, IoResult, IoError};
use sync::{Once, ONCE_INIT};
macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
static $name: Helper<$m> = Helper {
lock: ::rt::mutex::NATIVE_MUTEX_INIT,
chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
signal: ::cell::UnsafeCell { value: 0 },
initialized: ::cell::UnsafeCell { value: false },
};
) )
pub mod fs;
pub mod os;
pub mod c;
pub type sock_t = libc::SOCKET;
pub type wrlen = libc::c_int;
pub unsafe fn close_sock(sock: sock_t) { let _ = libc::closesocket(sock); }
// windows has zero values as errors
fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
if ret == 0 {
Err(last_error())
} else {
Ok(())
}
}
pub fn last_error() -> IoError {
let errno = os::errno() as i32;
let mut err = decode_error(errno);
err.detail = Some(os::error_string(errno));
err
}
pub fn last_net_error() -> IoError {
let errno = unsafe { c::WSAGetLastError() as i32 };
let mut err = decode_error(errno);
err.detail = Some(os::error_string(errno));
err
}
pub fn last_gai_error(_errno: i32) -> IoError {
last_net_error()
}
/// Convert an `errno` value into a high-level error variant and description.
pub fn decode_error(errno: i32) -> IoError {
let (kind, desc) = match errno {
libc::EOF => (io::EndOfFile, "end of file"),
libc::ERROR_NO_DATA => (io::BrokenPipe, "the pipe is being closed"),
libc::ERROR_FILE_NOT_FOUND => (io::FileNotFound, "file not found"),
libc::ERROR_INVALID_NAME => (io::InvalidInput, "invalid file name"),
libc::WSAECONNREFUSED => (io::ConnectionRefused, "connection refused"),
libc::WSAECONNRESET => (io::ConnectionReset, "connection reset"),
libc::ERROR_ACCESS_DENIED | 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"),
libc::ERROR_BROKEN_PIPE => (io::EndOfFile, "the pipe has ended"),
libc::ERROR_OPERATION_ABORTED =>
(io::TimedOut, "operation timed out"),
libc::WSAEINVAL => (io::InvalidInput, "invalid argument"),
libc::ERROR_CALL_NOT_IMPLEMENTED =>
(io::IoUnavailable, "function not implemented"),
libc::ERROR_INVALID_HANDLE =>
(io::MismatchedFileTypeForOperation,
"invalid handle provided to function"),
libc::ERROR_NOTHING_TO_TERMINATE =>
(io::InvalidInput, "no process to kill"),
// libuv maps this error code to EISDIR. we do too. if it is found
// to be incorrect, we can add in some more machinery to only
// return this message when ERROR_INVALID_FUNCTION after certain
// Windows calls.
libc::ERROR_INVALID_FUNCTION => (io::InvalidInput,
"illegal operation on a directory"),
_ => (io::OtherIoError, "unknown error")
};
IoError { kind: kind, desc: desc, detail: None }
}
pub fn decode_error_detailed(errno: i32) -> IoError {
let mut err = decode_error(errno);
err.detail = Some(os::error_string(errno));
err
}
#[inline]
pub fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
libc::timeval {
tv_sec: (ms / 1000) as libc::c_long,
tv_usec: ((ms % 1000) * 1000) as libc::c_long,
}
}
pub fn wouldblock() -> bool {
let err = os::errno();
err == libc::WSAEWOULDBLOCK as uint
}
pub fn set_nonblocking(fd: sock_t, nb: bool) -> IoResult<()> {
let mut set = nb as libc::c_ulong;
if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) != 0 } {
Err(last_error())
} else {
Ok(())
}
}
// FIXME: call this
pub fn init_net() {
unsafe {
static START: Once = ONCE_INIT;
START.doit(|| {
let mut data: c::WSADATA = mem::zeroed();
let ret = c::WSAStartup(0x202, // version 2.2
&mut data);
assert_eq!(ret, 0);
});
}
}
pub fn unimpl() -> IoError {
IoError {
kind: io::IoUnavailable,
desc: "operation is not implemented",
detail: None,
}
}
pub fn to_utf16(s: Option<&str>) -> IoResult<Vec<u16>> {
match s {
Some(s) => Ok({
let mut s = s.utf16_units().collect::<Vec<u16>>();
s.push(0);
s
}),
None => Err(IoError {
kind: io::InvalidInput,
desc: "valid unicode input required",
detail: None
})
}
}

View File

@ -0,0 +1,103 @@
// Copyright 2014 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.
// FIXME: move various extern bindings from here into liblibc or
// something similar
use libc;
use libc::{c_int, c_char, c_void};
use prelude::*;
use io::{IoResult, IoError};
use sys::fs::FileDesc;
use ptr;
use os::TMPBUF_SZ;
pub fn errno() -> uint {
use libc::types::os::arch::extra::DWORD;
#[link_name = "kernel32"]
extern "system" {
fn GetLastError() -> DWORD;
}
unsafe {
GetLastError() as uint
}
}
/// Get a detailed string description for the given error number
pub fn error_string(errnum: i32) -> String {
use libc::types::os::arch::extra::DWORD;
use libc::types::os::arch::extra::LPWSTR;
use libc::types::os::arch::extra::LPVOID;
use libc::types::os::arch::extra::WCHAR;
#[link_name = "kernel32"]
extern "system" {
fn FormatMessageW(flags: DWORD,
lpSrc: LPVOID,
msgId: DWORD,
langId: DWORD,
buf: LPWSTR,
nsize: DWORD,
args: *const c_void)
-> DWORD;
}
static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
// This value is calculated from the macro
// MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
let langId = 0x0800 as DWORD;
let mut buf = [0 as WCHAR, ..TMPBUF_SZ];
unsafe {
let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
ptr::null_mut(),
errnum as DWORD,
langId,
buf.as_mut_ptr(),
buf.len() as DWORD,
ptr::null());
if res == 0 {
// Sometimes FormatMessageW can fail e.g. system doesn't like langId,
let fm_err = errno();
return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
}
let msg = String::from_utf16(::str::truncate_utf16_at_nul(buf));
match msg {
Some(msg) => format!("OS Error {}: {}", errnum, msg),
None => format!("OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum),
}
}
}
pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
// Windows pipes work subtly differently than unix pipes, and their
// inheritance has to be handled in a different way that I do not
// fully understand. Here we explicitly make the pipe non-inheritable,
// which means to pass it to a subprocess they need to be duplicated
// first, as in std::run.
let mut fds = [0, ..2];
match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
(libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
0 => {
assert!(fds[0] != -1 && fds[0] != 0);
assert!(fds[1] != -1 && fds[1] != 0);
Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
}
_ => Err(IoError::last_error()),
}
}