]> git.lizzy.rs Git - rust.git/blob - src/libnative/io/mod.rs
f1bec440547e1ead57281e19963eab9735490b96
[rust.git] / src / libnative / io / mod.rs
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.
4 //
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.
10
11 //! Native thread-blocking I/O implementation
12 //!
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
16 //! necessary.
17 //!
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.
23
24 use std::c_str::CString;
25 use std::comm::SharedChan;
26 use std::libc::c_int;
27 use std::libc;
28 use std::os;
29 use std::rt::rtio;
30 use std::rt::rtio::{RtioTcpStream, RtioTcpListener, RtioUdpSocket,
31                     RtioUnixListener, RtioPipe, RtioFileStream, RtioProcess,
32                     RtioSignal, RtioTTY, CloseBehavior, RtioTimer};
33 use std::io;
34 use std::io::IoError;
35 use std::io::net::ip::SocketAddr;
36 use std::io::process::ProcessConfig;
37 use std::io::signal::Signum;
38 use ai = std::io::net::addrinfo;
39
40 // Local re-exports
41 pub use self::file::FileDesc;
42 pub use self::process::Process;
43
44 // Native I/O implementations
45 pub mod file;
46 pub mod process;
47 pub mod net;
48
49 type IoResult<T> = Result<T, IoError>;
50
51 fn unimpl() -> IoError {
52     IoError {
53         kind: io::IoUnavailable,
54         desc: "unimplemented I/O interface",
55         detail: None,
56     }
57 }
58
59 fn translate_error(errno: i32, detail: bool) -> IoError {
60     #[cfg(windows)]
61     fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) {
62         match errno {
63             libc::EOF => (io::EndOfFile, "end of file"),
64             libc::WSAECONNREFUSED => (io::ConnectionRefused, "connection refused"),
65             libc::WSAECONNRESET => (io::ConnectionReset, "connection reset"),
66             libc::WSAEACCES => (io::PermissionDenied, "permission denied"),
67             libc::WSAEWOULDBLOCK =>
68                 (io::ResourceUnavailable, "resource temporarily unavailable"),
69             libc::WSAENOTCONN => (io::NotConnected, "not connected"),
70             libc::WSAECONNABORTED => (io::ConnectionAborted, "connection aborted"),
71             libc::WSAEADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
72             libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"),
73
74             x => {
75                 debug!("ignoring {}: {}", x, os::last_os_error());
76                 (io::OtherIoError, "unknown error")
77             }
78         }
79     }
80
81     #[cfg(not(windows))]
82     fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) {
83         // XXX: this should probably be a bit more descriptive...
84         match errno {
85             libc::EOF => (io::EndOfFile, "end of file"),
86             libc::ECONNREFUSED => (io::ConnectionRefused, "connection refused"),
87             libc::ECONNRESET => (io::ConnectionReset, "connection reset"),
88             libc::EPERM | libc::EACCES =>
89                 (io::PermissionDenied, "permission denied"),
90             libc::EPIPE => (io::BrokenPipe, "broken pipe"),
91             libc::ENOTCONN => (io::NotConnected, "not connected"),
92             libc::ECONNABORTED => (io::ConnectionAborted, "connection aborted"),
93             libc::EADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
94             libc::EADDRINUSE => (io::ConnectionRefused, "address in use"),
95
96             // These two constants can have the same value on some systems, but
97             // different values on others, so we can't use a match clause
98             x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
99                 (io::ResourceUnavailable, "resource temporarily unavailable"),
100
101             x => {
102                 debug!("ignoring {}: {}", x, os::last_os_error());
103                 (io::OtherIoError, "unknown error")
104             }
105         }
106     }
107
108     let (kind, desc) = get_err(errno);
109     IoError {
110         kind: kind,
111         desc: desc,
112         detail: if detail {Some(os::last_os_error())} else {None},
113     }
114 }
115
116 fn last_error() -> IoError { translate_error(os::errno() as i32, true) }
117
118 // unix has nonzero values as errors
119 fn mkerr_libc(ret: libc::c_int) -> IoResult<()> {
120     if ret != 0 {
121         Err(last_error())
122     } else {
123         Ok(())
124     }
125 }
126
127 // windows has zero values as errors
128 #[cfg(windows)]
129 fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
130     if ret == 0 {
131         Err(last_error())
132     } else {
133         Ok(())
134     }
135 }
136
137 #[cfg(windows)]
138 #[inline]
139 fn retry(f: || -> libc::c_int) -> libc::c_int {
140     loop {
141         match f() {
142             -1 if os::errno() as int == libc::WSAEINTR as int => {}
143             n => return n,
144         }
145     }
146 }
147
148 #[cfg(unix)]
149 #[inline]
150 fn retry(f: || -> libc::c_int) -> libc::c_int {
151     loop {
152         match f() {
153             -1 if os::errno() as int == libc::EINTR as int => {}
154             n => return n,
155         }
156     }
157 }
158
159 /// Implementation of rt::rtio's IoFactory trait to generate handles to the
160 /// native I/O functionality.
161 pub struct IoFactory {
162     priv cannot_construct_outside_of_this_module: ()
163 }
164
165 impl IoFactory {
166     pub fn new() -> IoFactory {
167         net::init();
168         IoFactory { cannot_construct_outside_of_this_module: () }
169     }
170 }
171
172 impl rtio::IoFactory for IoFactory {
173     // networking
174     fn tcp_connect(&mut self, addr: SocketAddr) -> IoResult<~RtioTcpStream> {
175         net::TcpStream::connect(addr).map(|s| ~s as ~RtioTcpStream)
176     }
177     fn tcp_bind(&mut self, addr: SocketAddr) -> IoResult<~RtioTcpListener> {
178         net::TcpListener::bind(addr).map(|s| ~s as ~RtioTcpListener)
179     }
180     fn udp_bind(&mut self, addr: SocketAddr) -> IoResult<~RtioUdpSocket> {
181         net::UdpSocket::bind(addr).map(|u| ~u as ~RtioUdpSocket)
182     }
183     fn unix_bind(&mut self, _path: &CString) -> IoResult<~RtioUnixListener> {
184         Err(unimpl())
185     }
186     fn unix_connect(&mut self, _path: &CString) -> IoResult<~RtioPipe> {
187         Err(unimpl())
188     }
189     fn get_host_addresses(&mut self, _host: Option<&str>, _servname: Option<&str>,
190                           _hint: Option<ai::Hint>) -> IoResult<~[ai::Info]> {
191         Err(unimpl())
192     }
193
194     // filesystem operations
195     fn fs_from_raw_fd(&mut self, fd: c_int,
196                       close: CloseBehavior) -> ~RtioFileStream {
197         let close = match close {
198             rtio::CloseSynchronously | rtio::CloseAsynchronously => true,
199             rtio::DontClose => false
200         };
201         ~file::FileDesc::new(fd, close) as ~RtioFileStream
202     }
203     fn fs_open(&mut self, path: &CString, fm: io::FileMode, fa: io::FileAccess)
204         -> IoResult<~RtioFileStream> {
205         file::open(path, fm, fa).map(|fd| ~fd as ~RtioFileStream)
206     }
207     fn fs_unlink(&mut self, path: &CString) -> IoResult<()> {
208         file::unlink(path)
209     }
210     fn fs_stat(&mut self, path: &CString) -> IoResult<io::FileStat> {
211         file::stat(path)
212     }
213     fn fs_mkdir(&mut self, path: &CString,
214                 mode: io::FilePermission) -> IoResult<()> {
215         file::mkdir(path, mode)
216     }
217     fn fs_chmod(&mut self, path: &CString,
218                 mode: io::FilePermission) -> IoResult<()> {
219         file::chmod(path, mode)
220     }
221     fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> {
222         file::rmdir(path)
223     }
224     fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> {
225         file::rename(path, to)
226     }
227     fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<~[Path]> {
228         file::readdir(path)
229     }
230     fn fs_lstat(&mut self, path: &CString) -> IoResult<io::FileStat> {
231         file::lstat(path)
232     }
233     fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> {
234         file::chown(path, uid, gid)
235     }
236     fn fs_readlink(&mut self, path: &CString) -> IoResult<Path> {
237         file::readlink(path)
238     }
239     fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
240         file::symlink(src, dst)
241     }
242     fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
243         file::link(src, dst)
244     }
245     fn fs_utime(&mut self, src: &CString, atime: u64,
246                 mtime: u64) -> IoResult<()> {
247         file::utime(src, atime, mtime)
248     }
249
250     // misc
251     fn timer_init(&mut self) -> IoResult<~RtioTimer> {
252         Err(unimpl())
253     }
254     fn spawn(&mut self, config: ProcessConfig)
255             -> IoResult<(~RtioProcess, ~[Option<~RtioPipe>])> {
256         process::Process::spawn(config).map(|(p, io)| {
257             (~p as ~RtioProcess,
258              io.move_iter().map(|p| p.map(|p| ~p as ~RtioPipe)).collect())
259         })
260     }
261     fn pipe_open(&mut self, fd: c_int) -> IoResult<~RtioPipe> {
262         Ok(~file::FileDesc::new(fd, true) as ~RtioPipe)
263     }
264     fn tty_open(&mut self, fd: c_int, _readable: bool) -> IoResult<~RtioTTY> {
265         if unsafe { libc::isatty(fd) } != 0 {
266             Ok(~file::FileDesc::new(fd, true) as ~RtioTTY)
267         } else {
268             Err(IoError {
269                 kind: io::MismatchedFileTypeForOperation,
270                 desc: "file descriptor is not a TTY",
271                 detail: None,
272             })
273         }
274     }
275     fn signal(&mut self, _signal: Signum, _channel: SharedChan<Signum>)
276         -> IoResult<~RtioSignal> {
277         Err(unimpl())
278     }
279 }