1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Native thread-blocking I/O implementation
13 //! This module contains the implementation of native thread-blocking
14 //! implementations of I/O on all platforms. This module is not intended to be
15 //! used directly, but rather the rust runtime will fall back to using it if
18 //! Rust code normally runs inside of green tasks with a local scheduler using
19 //! asynchronous I/O to cooperate among tasks. This model is not always
20 //! available, however, and that's where these native implementations come into
21 //! play. The only dependencies of these modules are the normal system libraries
22 //! that you would find on the respective platform.
24 use std::c_str::CString;
25 use std::comm::SharedChan;
28 use std::io::net::ip::SocketAddr;
29 use std::io::process::ProcessConfig;
30 use std::io::signal::Signum;
35 use std::rt::rtio::{RtioTcpStream, RtioTcpListener, RtioUdpSocket,
36 RtioUnixListener, RtioPipe, RtioFileStream, RtioProcess,
37 RtioSignal, RtioTTY, CloseBehavior, RtioTimer};
38 use ai = std::io::net::addrinfo;
41 pub use self::file::FileDesc;
42 pub use self::process::Process;
44 // Native I/O implementations
50 type IoResult<T> = Result<T, IoError>;
52 fn unimpl() -> IoError {
54 kind: io::IoUnavailable,
55 desc: "unimplemented I/O interface",
60 fn translate_error(errno: i32, detail: bool) -> IoError {
62 fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) {
64 libc::EOF => (io::EndOfFile, "end of file"),
65 libc::WSAECONNREFUSED => (io::ConnectionRefused, "connection refused"),
66 libc::WSAECONNRESET => (io::ConnectionReset, "connection reset"),
67 libc::WSAEACCES => (io::PermissionDenied, "permission denied"),
68 libc::WSAEWOULDBLOCK =>
69 (io::ResourceUnavailable, "resource temporarily unavailable"),
70 libc::WSAENOTCONN => (io::NotConnected, "not connected"),
71 libc::WSAECONNABORTED => (io::ConnectionAborted, "connection aborted"),
72 libc::WSAEADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
73 libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"),
76 debug!("ignoring {}: {}", x, os::last_os_error());
77 (io::OtherIoError, "unknown error")
83 fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) {
84 // XXX: this should probably be a bit more descriptive...
86 libc::EOF => (io::EndOfFile, "end of file"),
87 libc::ECONNREFUSED => (io::ConnectionRefused, "connection refused"),
88 libc::ECONNRESET => (io::ConnectionReset, "connection reset"),
89 libc::EPERM | libc::EACCES =>
90 (io::PermissionDenied, "permission denied"),
91 libc::EPIPE => (io::BrokenPipe, "broken pipe"),
92 libc::ENOTCONN => (io::NotConnected, "not connected"),
93 libc::ECONNABORTED => (io::ConnectionAborted, "connection aborted"),
94 libc::EADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
95 libc::EADDRINUSE => (io::ConnectionRefused, "address in use"),
97 // These two constants can have the same value on some systems, but
98 // different values on others, so we can't use a match clause
99 x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
100 (io::ResourceUnavailable, "resource temporarily unavailable"),
103 debug!("ignoring {}: {}", x, os::last_os_error());
104 (io::OtherIoError, "unknown error")
109 let (kind, desc) = get_err(errno);
113 detail: if detail {Some(os::last_os_error())} else {None},
117 fn last_error() -> IoError { translate_error(os::errno() as i32, true) }
119 // unix has nonzero values as errors
120 fn mkerr_libc(ret: libc::c_int) -> IoResult<()> {
128 // windows has zero values as errors
130 fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
140 fn retry(f: || -> libc::c_int) -> libc::c_int {
143 -1 if os::errno() as int == libc::WSAEINTR as int => {}
151 fn retry(f: || -> libc::c_int) -> libc::c_int {
154 -1 if os::errno() as int == libc::EINTR as int => {}
160 /// Implementation of rt::rtio's IoFactory trait to generate handles to the
161 /// native I/O functionality.
162 pub struct IoFactory {
163 priv cannot_construct_outside_of_this_module: ()
167 pub fn new() -> IoFactory {
169 IoFactory { cannot_construct_outside_of_this_module: () }
173 impl rtio::IoFactory for IoFactory {
175 fn tcp_connect(&mut self, addr: SocketAddr) -> IoResult<~RtioTcpStream> {
176 net::TcpStream::connect(addr).map(|s| ~s as ~RtioTcpStream)
178 fn tcp_bind(&mut self, addr: SocketAddr) -> IoResult<~RtioTcpListener> {
179 net::TcpListener::bind(addr).map(|s| ~s as ~RtioTcpListener)
181 fn udp_bind(&mut self, addr: SocketAddr) -> IoResult<~RtioUdpSocket> {
182 net::UdpSocket::bind(addr).map(|u| ~u as ~RtioUdpSocket)
184 fn unix_bind(&mut self, _path: &CString) -> IoResult<~RtioUnixListener> {
187 fn unix_connect(&mut self, _path: &CString) -> IoResult<~RtioPipe> {
190 fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>,
191 hint: Option<ai::Hint>) -> IoResult<~[ai::Info]> {
192 addrinfo::GetAddrInfoRequest::run(host, servname, hint)
195 // filesystem operations
196 fn fs_from_raw_fd(&mut self, fd: c_int,
197 close: CloseBehavior) -> ~RtioFileStream {
198 let close = match close {
199 rtio::CloseSynchronously | rtio::CloseAsynchronously => true,
200 rtio::DontClose => false
202 ~file::FileDesc::new(fd, close) as ~RtioFileStream
204 fn fs_open(&mut self, path: &CString, fm: io::FileMode, fa: io::FileAccess)
205 -> IoResult<~RtioFileStream> {
206 file::open(path, fm, fa).map(|fd| ~fd as ~RtioFileStream)
208 fn fs_unlink(&mut self, path: &CString) -> IoResult<()> {
211 fn fs_stat(&mut self, path: &CString) -> IoResult<io::FileStat> {
214 fn fs_mkdir(&mut self, path: &CString,
215 mode: io::FilePermission) -> IoResult<()> {
216 file::mkdir(path, mode)
218 fn fs_chmod(&mut self, path: &CString,
219 mode: io::FilePermission) -> IoResult<()> {
220 file::chmod(path, mode)
222 fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> {
225 fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> {
226 file::rename(path, to)
228 fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<~[Path]> {
231 fn fs_lstat(&mut self, path: &CString) -> IoResult<io::FileStat> {
234 fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> {
235 file::chown(path, uid, gid)
237 fn fs_readlink(&mut self, path: &CString) -> IoResult<Path> {
240 fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
241 file::symlink(src, dst)
243 fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
246 fn fs_utime(&mut self, src: &CString, atime: u64,
247 mtime: u64) -> IoResult<()> {
248 file::utime(src, atime, mtime)
252 fn timer_init(&mut self) -> IoResult<~RtioTimer> {
255 fn spawn(&mut self, config: ProcessConfig)
256 -> IoResult<(~RtioProcess, ~[Option<~RtioPipe>])> {
257 process::Process::spawn(config).map(|(p, io)| {
259 io.move_iter().map(|p| p.map(|p| ~p as ~RtioPipe)).collect())
262 fn pipe_open(&mut self, fd: c_int) -> IoResult<~RtioPipe> {
263 Ok(~file::FileDesc::new(fd, true) as ~RtioPipe)
265 fn tty_open(&mut self, fd: c_int, _readable: bool) -> IoResult<~RtioTTY> {
266 if unsafe { libc::isatty(fd) } != 0 {
267 Ok(~file::FileDesc::new(fd, true) as ~RtioTTY)
270 kind: io::MismatchedFileTypeForOperation,
271 desc: "file descriptor is not a TTY",
276 fn signal(&mut self, _signal: Signum, _channel: SharedChan<Signum>)
277 -> IoResult<~RtioSignal> {