diff options
| author | Fenrir <[email protected]> | 2018-01-21 14:06:28 -0700 |
|---|---|---|
| committer | FenrirWolf <[email protected]> | 2018-01-21 19:16:33 -0700 |
| commit | 23be3f4885688e5e0011005e2295c75168854c0a (patch) | |
| tree | dd0850f9c73c489e114a761d5c0757f3dbec3a65 /ctr-std/src/sys | |
| parent | Update CI for Rust nightly-2017-12-01 + other fixes (diff) | |
| download | ctru-rs-23be3f4885688e5e0011005e2295c75168854c0a.tar.xz ctru-rs-23be3f4885688e5e0011005e2295c75168854c0a.zip | |
Recreate ctr-std from latest nightly
Diffstat (limited to 'ctr-std/src/sys')
27 files changed, 1539 insertions, 224 deletions
diff --git a/ctr-std/src/sys/mod.rs b/ctr-std/src/sys/mod.rs index 175e227..569336d 100644 --- a/ctr-std/src/sys/mod.rs +++ b/ctr-std/src/sys/mod.rs @@ -13,25 +13,78 @@ //! The `std::sys` module is the abstracted interface through which //! `std` talks to the underlying operating system. It has different //! implementations for different operating system families, today -//! just Unix and Windows. +//! just Unix and Windows, and initial support for Redox. //! //! The centralization of platform-specific code in this module is //! enforced by the "platform abstraction layer" tidy script in -//! `tools/tidy/pal.rs`. +//! `tools/tidy/src/pal.rs`. //! //! This module is closely related to the platform-independent system //! integration code in `std::sys_common`. See that module's //! documentation for details. //! -//! In the future it would be desirable for the indepedent +//! In the future it would be desirable for the independent //! implementations of this module to be extracted to their own crates //! that `std` can link to, thus enabling their implementation //! out-of-tree via crate replacement. Though due to the complex //! inter-dependencies within `std` that will be a challenging goal to //! achieve. -pub use self::imp::*; +#![allow(missing_debug_implementations)] -#[cfg(unix)] -#[path = "unix/mod.rs"] -mod imp; +cfg_if! { + if #[cfg(unix)] { + mod unix; + pub use self::unix::*; + } else { + compile_error!("libstd doesn't compile for this platform yet"); + } +} + +// Import essential modules from both platforms when documenting. These are +// then later used in the `std::os` module when documenting, for example, +// Windows when we're compiling for Linux. + +#[cfg(dox)] +cfg_if! { + if #[cfg(any(unix, target_os = "redox"))] { + // On unix we'll document what's already available + pub use self::ext as unix_ext; + } else if #[cfg(any(target_os = "cloudabi", target_arch = "wasm32"))] { + // On CloudABI and wasm right now the module below doesn't compile + // (missing things in `libc` which is empty) so just omit everything + // with an empty module + #[unstable(issue = "0", feature = "std_internals")] + pub mod unix_ext {} + } else { + // On other platforms like Windows document the bare bones of unix + use os::linux as platform; + #[path = "unix/ext/mod.rs"] + pub mod unix_ext; + } +} + +#[cfg(dox)] +cfg_if! { + if #[cfg(windows)] { + // On windows we'll just be documenting what's already available + pub use self::ext as windows_ext; + } else if #[cfg(any(target_os = "cloudabi", target_arch = "wasm32"))] { + // On CloudABI and wasm right now the shim below doesn't compile, so + // just omit it + #[unstable(issue = "0", feature = "std_internals")] + pub mod windows_ext {} + } else { + // On all other platforms (aka linux/osx/etc) then pull in a "minimal" + // amount of windows goop which ends up compiling + #[macro_use] + #[path = "windows/compat.rs"] + mod compat; + + #[path = "windows/c.rs"] + mod c; + + #[path = "windows/ext/mod.rs"] + pub mod windows_ext; + } +} diff --git a/ctr-std/src/sys/unix/args.rs b/ctr-std/src/sys/unix/args.rs new file mode 100644 index 0000000..84d0c85 --- /dev/null +++ b/ctr-std/src/sys/unix/args.rs @@ -0,0 +1,60 @@ +// Copyright 2017 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 ffi::OsString; +use marker::PhantomData; +use vec; + +pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + // Currently null because we haven't implemented args yet +} + +pub unsafe fn cleanup() { +} + +pub fn args() -> Args { + return Args { + iter: Vec::new().into_iter(), + _dont_send_or_sync_me: PhantomData, + } +} + +pub struct Args { + iter: vec::IntoIter<OsString>, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +impl Args { + pub fn inner_debug(&self) -> &[OsString] { + self.iter.as_slice() + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option<OsString> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { + self.iter.next_back() + } +} diff --git a/ctr-std/src/sys/unix/backtrace.rs b/ctr-std/src/sys/unix/backtrace.rs new file mode 100644 index 0000000..9a8c48f --- /dev/null +++ b/ctr-std/src/sys/unix/backtrace.rs @@ -0,0 +1,37 @@ +// Copyright 2017 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 io; +use sys::unsupported; +use sys_common::backtrace::Frame; + +pub struct BacktraceContext; + +pub fn unwind_backtrace(_frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + unsupported() +} + +pub fn resolve_symname<F>(_frame: Frame, + _callback: F, + _: &BacktraceContext) -> io::Result<()> + where F: FnOnce(Option<&str>) -> io::Result<()> +{ + unsupported() +} + +pub fn foreach_symbol_fileline<F>(_: Frame, + _: F, + _: &BacktraceContext) -> io::Result<bool> + where F: FnMut(&[u8], u32) -> io::Result<()> +{ + unsupported() +} diff --git a/ctr-std/src/sys/unix/cmath.rs b/ctr-std/src/sys/unix/cmath.rs new file mode 100644 index 0000000..2bc9665 --- /dev/null +++ b/ctr-std/src/sys/unix/cmath.rs @@ -0,0 +1,43 @@ +// Copyright 2017 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. + +#![cfg(not(test))] + +use libc::{c_float, c_double}; + +#[link_name = "m"] +extern { + pub fn acos(n: c_double) -> c_double; + pub fn acosf(n: c_float) -> c_float; + pub fn asin(n: c_double) -> c_double; + pub fn asinf(n: c_float) -> c_float; + pub fn atan(n: c_double) -> c_double; + pub fn atan2(a: c_double, b: c_double) -> c_double; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn cbrt(n: c_double) -> c_double; + pub fn cbrtf(n: c_float) -> c_float; + pub fn cosh(n: c_double) -> c_double; + pub fn coshf(n: c_float) -> c_float; + pub fn expm1(n: c_double) -> c_double; + pub fn expm1f(n: c_float) -> c_float; + pub fn fdim(a: c_double, b: c_double) -> c_double; + pub fn fdimf(a: c_float, b: c_float) -> c_float; + pub fn hypot(x: c_double, y: c_double) -> c_double; + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn log1p(n: c_double) -> c_double; + pub fn log1pf(n: c_float) -> c_float; + pub fn sinh(n: c_double) -> c_double; + pub fn sinhf(n: c_float) -> c_float; + pub fn tan(n: c_double) -> c_double; + pub fn tanf(n: c_float) -> c_float; + pub fn tanh(n: c_double) -> c_double; + pub fn tanhf(n: c_float) -> c_float; +} diff --git a/ctr-std/src/sys/unix/condvar.rs b/ctr-std/src/sys/unix/condvar.rs index 938df8f..bfff16b 100644 --- a/ctr-std/src/sys/unix/condvar.rs +++ b/ctr-std/src/sys/unix/condvar.rs @@ -112,7 +112,9 @@ impl Condvar { let now = Instant::now(); - let nanos = dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64; + let nanos = dur.as_secs() + .saturating_mul(1_000_000_000) + .saturating_add(dur.subsec_nanos() as u64); mutex.unlock(); diff --git a/ctr-std/src/sys/unix/env.rs b/ctr-std/src/sys/unix/env.rs new file mode 100644 index 0000000..393bdfe --- /dev/null +++ b/ctr-std/src/sys/unix/env.rs @@ -0,0 +1,19 @@ +// Copyright 2017 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. + +pub mod os { + pub const FAMILY: &'static str = "nintendo"; + pub const OS: &'static str = "horizon"; + pub const DLL_PREFIX: &'static str = ""; + pub const DLL_SUFFIX: &'static str = ".cro"; + pub const DLL_EXTENSION: &'static str = ".cro"; + pub const EXE_SUFFIX: &'static str = ".3dsx"; + pub const EXE_EXTENSION: &'static str = "3dsx"; +} diff --git a/ctr-std/src/sys/unix/ext/ffi.rs b/ctr-std/src/sys/unix/ext/ffi.rs index d59b4fc..fb9984c 100644 --- a/ctr-std/src/sys/unix/ext/ffi.rs +++ b/ctr-std/src/sys/unix/ext/ffi.rs @@ -20,11 +20,38 @@ use sys_common::{FromInner, IntoInner, AsInner}; /// Unix-specific extensions to `OsString`. #[stable(feature = "rust1", since = "1.0.0")] pub trait OsStringExt { - /// Creates an `OsString` from a byte vector. + /// Creates an [`OsString`] from a byte vector. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// use std::os::unix::ffi::OsStringExt; + /// + /// let bytes = b"foo".to_vec(); + /// let os_string = OsString::from_vec(bytes); + /// assert_eq!(os_string.to_str(), Some("foo")); + /// ``` + /// + /// [`OsString`]: ../../../ffi/struct.OsString.html #[stable(feature = "rust1", since = "1.0.0")] fn from_vec(vec: Vec<u8>) -> Self; - /// Yields the underlying byte vector of this `OsString`. + /// Yields the underlying byte vector of this [`OsString`]. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsString; + /// use std::os::unix::ffi::OsStringExt; + /// + /// let mut os_string = OsString::new(); + /// os_string.push("foo"); + /// let bytes = os_string.into_vec(); + /// assert_eq!(bytes, b"foo"); + /// ``` + /// + /// [`OsString`]: ../../../ffi/struct.OsString.html #[stable(feature = "rust1", since = "1.0.0")] fn into_vec(self) -> Vec<u8>; } @@ -43,9 +70,36 @@ impl OsStringExt for OsString { #[stable(feature = "rust1", since = "1.0.0")] pub trait OsStrExt { #[stable(feature = "rust1", since = "1.0.0")] + /// Creates an [`OsStr`] from a byte slice. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// use std::os::unix::ffi::OsStrExt; + /// + /// let bytes = b"foo"; + /// let os_str = OsStr::from_bytes(bytes); + /// assert_eq!(os_str.to_str(), Some("foo")); + /// ``` + /// + /// [`OsStr`]: ../../../ffi/struct.OsStr.html fn from_bytes(slice: &[u8]) -> &Self; - /// Gets the underlying byte view of the `OsStr` slice. + /// Gets the underlying byte view of the [`OsStr`] slice. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::OsStr; + /// use std::os::unix::ffi::OsStrExt; + /// + /// let mut os_str = OsStr::new("foo"); + /// let bytes = os_str.as_bytes(); + /// assert_eq!(bytes, b"foo"); + /// ``` + /// + /// [`OsStr`]: ../../../ffi/struct.OsStr.html #[stable(feature = "rust1", since = "1.0.0")] fn as_bytes(&self) -> &[u8]; } diff --git a/ctr-std/src/sys/unix/ext/io.rs b/ctr-std/src/sys/unix/ext/io.rs new file mode 100644 index 0000000..c9fe359 --- /dev/null +++ b/ctr-std/src/sys/unix/ext/io.rs @@ -0,0 +1,108 @@ +// Copyright 2015 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. + +//! Unix-specific extensions to general I/O primitives + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs; +use os::raw; +use sys; +use io; +use sys_common::{AsInner, FromInner, IntoInner}; +use libc; + +/// Raw file descriptors. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawFd = raw::c_int; + +/// A trait to extract the raw unix file descriptor from an underlying +/// object. +/// +/// This is only available on unix platforms and must be imported in order +/// to call the method. Windows platforms have a corresponding `AsRawHandle` +/// and `AsRawSocket` set of traits. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawFd { + /// Extracts the raw file descriptor. + /// + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guaranteed to be valid while + /// the original object has not yet been destroyed. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_fd(&self) -> RawFd; +} + +/// A trait to express the ability to construct an object from a raw file +/// descriptor. +#[stable(feature = "from_raw_os", since = "1.1.0")] +pub trait FromRawFd { + /// Constructs a new instance of `Self` from the given raw file + /// descriptor. + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + #[stable(feature = "from_raw_os", since = "1.1.0")] + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw file descriptor. +#[stable(feature = "into_raw_os", since = "1.4.0")] +pub trait IntoRawFd { + /// Consumes this object, returning the raw underlying file descriptor. + /// + /// This function **transfers ownership** of the underlying file descriptor + /// to the caller. Callers are then the unique owners of the file descriptor + /// and must close the descriptor once it's no longer needed. + #[stable(feature = "into_raw_os", since = "1.4.0")] + fn into_raw_fd(self) -> RawFd; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> fs::File { + fs::File::from_inner(sys::fs::File::from_inner(fd)) + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for fs::File { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawFd for io::Stdin { + fn as_raw_fd(&self) -> RawFd { libc::STDIN_FILENO } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawFd for io::Stdout { + fn as_raw_fd(&self) -> RawFd { libc::STDOUT_FILENO } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawFd for io::Stderr { + fn as_raw_fd(&self) -> RawFd { libc::STDERR_FILENO } +} diff --git a/ctr-std/src/sys/unix/ext/mod.rs b/ctr-std/src/sys/unix/ext/mod.rs index 04ea563..7b0d175 100644 --- a/ctr-std/src/sys/unix/ext/mod.rs +++ b/ctr-std/src/sys/unix/ext/mod.rs @@ -10,10 +10,16 @@ //! Experimental extensions to `std` for Unix platforms. //! -//! For now, this module is limited to extracting file descriptors, -//! but its functionality will grow over time. +//! Provides access to platform-level information on Unix platforms, and +//! exposes Unix-specific functions that would otherwise be inappropriate as +//! part of the core `std` library. //! -//! # Example +//! It exposes more ways to deal with platform-specific strings (`OsStr`, +//! `OsString`), allows to set permissions more granularly, extract low-level +//! file descriptors from files and sockets, and has platform-specific helpers +//! for spawning processes. +//! +//! # Examples //! //! ```no_run //! use std::fs::File; @@ -28,8 +34,15 @@ //! ``` #![stable(feature = "rust1", since = "1.0.0")] +#![doc(cfg(unix))] +pub mod io; pub mod ffi; +//pub mod fs; +//pub mod process; +pub mod raw; +//pub mod thread; +//pub mod net; /// A prelude for conveniently writing platform-specific code. /// @@ -37,5 +50,17 @@ pub mod ffi; #[stable(feature = "rust1", since = "1.0.0")] pub mod prelude { #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::ffi::{OsStrExt, OsStringExt}; + //#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + //pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt}; + //#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + //pub use super::fs::DirEntryExt; + //#[doc(no_inline)] #[stable(feature = "file_offset", since = "1.15.0")] + //pub use super::fs::FileExt; + //#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + //pub use super::thread::JoinHandleExt; + //#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + //pub use super::process::{CommandExt, ExitStatusExt}; } diff --git a/ctr-std/src/sys/unix/ext/raw.rs b/ctr-std/src/sys/unix/ext/raw.rs new file mode 100644 index 0000000..7e4a439 --- /dev/null +++ b/ctr-std/src/sys/unix/ext/raw.rs @@ -0,0 +1,33 @@ +// Copyright 2015 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. + +//! Unix-specific primitives available on all unix platforms + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![rustc_deprecated(since = "1.8.0", + reason = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions")] +#![allow(deprecated, warnings)] + +#[stable(feature = "raw_ext", since = "1.1.0")] pub type uid_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type gid_t = u32; +#[stable(feature = "raw_ext", since = "1.1.0")] pub type pid_t = i32; + +#[doc(inline)] +#[stable(feature = "pthread_t", since = "1.8.0")] +pub use sys::platform::raw::pthread_t; +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use sys::platform::raw::{dev_t, ino_t, mode_t, nlink_t, off_t, blksize_t}; +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use sys::platform::raw::{blkcnt_t, time_t}; diff --git a/ctr-std/src/sys/unix/fd.rs b/ctr-std/src/sys/unix/fd.rs index 91adac1..e19cbb0 100644 --- a/ctr-std/src/sys/unix/fd.rs +++ b/ctr-std/src/sys/unix/fd.rs @@ -14,9 +14,9 @@ use cmp; use io::{self, Read}; use libc::{self, c_int, c_void, ssize_t}; use mem; +use sync::atomic::{AtomicBool, Ordering}; use sys::cvt; use sys_common::AsInner; -use sys_common::io::read_to_end_uninitialized; #[derive(Debug)] pub struct FileDesc { @@ -28,7 +28,7 @@ fn max_len() -> usize { // with the man page quoting that if the count of bytes to read is // greater than `SSIZE_MAX` the result is "unspecified". // - // On OSX, however, apparently the 64-bit libc is either buggy or + // On macOS, however, apparently the 64-bit libc is either buggy or // intentionally showing odd behavior by rejecting any read with a size // larger than or equal to INT_MAX. To handle both of these the read // size is capped on both platforms. @@ -110,22 +110,98 @@ impl FileDesc { } } - // This is a unix-specific operation that the 3DS likely doesn't support. - // However, there's no reason to make calling this function an error either. + #[cfg(not(any(target_env = "newlib", + target_os = "solaris", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "haiku")))] pub fn set_cloexec(&self) -> io::Result<()> { - Ok(()) + unsafe { + cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; + Ok(()) + } + } + #[cfg(any(target_env = "newlib", + target_os = "solaris", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "haiku"))] + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; + let new = previous | libc::FD_CLOEXEC; + if new != previous { + cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; + } + Ok(()) + } } - // This is a unix-specific operation that the 3DS likely doesn't support. - // However, there's no reason to make calling this function an error either. - pub fn set_nonblocking(&self) -> io::Result<()> { - Ok(()) + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + unsafe { + let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?; + let new = if nonblocking { + previous | libc::O_NONBLOCK + } else { + previous & !libc::O_NONBLOCK + }; + if new != previous { + cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; + } + Ok(()) + } } - // The sdmc and romfs devoptabs definitely don't support this operation. - // Not sure if it will be needed for network support or not. pub fn duplicate(&self) -> io::Result<FileDesc> { - unimplemented!() + // We want to atomically duplicate this file descriptor and set the + // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This + // flag, however, isn't supported on older Linux kernels (earlier than + // 2.6.24). + // + // To detect this and ensure that CLOEXEC is still set, we + // follow a strategy similar to musl [1] where if passing + // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not + // supported (the third parameter, 0, is always valid), so we stop + // trying that. + // + // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to + // resolve so we at least compile this. + // + // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963 + #[cfg(any(target_os = "android", target_os = "haiku"))] + use libc::F_DUPFD as F_DUPFD_CLOEXEC; + #[cfg(not(any(target_os = "android", target_os="haiku")))] + use libc::F_DUPFD_CLOEXEC; + + let make_filedesc = |fd| { + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + Ok(fd) + }; + static TRY_CLOEXEC: AtomicBool = + AtomicBool::new(!cfg!(target_os = "android")); + let fd = self.raw(); + if TRY_CLOEXEC.load(Ordering::Relaxed) { + match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) { + // We *still* call the `set_cloexec` method as apparently some + // linux kernel at some point stopped setting CLOEXEC even + // though it reported doing so on F_DUPFD_CLOEXEC. + Ok(fd) => { + return Ok(if cfg!(target_os = "linux") { + make_filedesc(fd)? + } else { + FileDesc::new(fd) + }) + } + Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => { + TRY_CLOEXEC.store(false, Ordering::Relaxed); + } + Err(e) => return Err(e), + } + } + cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc) } } @@ -133,10 +209,6 @@ impl<'a> Read for &'a FileDesc { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) } - - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { - unsafe { read_to_end_uninitialized(self, buf) } - } } impl AsInner<c_int> for FileDesc { diff --git a/ctr-std/src/sys/unix/fs.rs b/ctr-std/src/sys/unix/fs.rs index 06413c3..f30b270 100644 --- a/ctr-std/src/sys/unix/fs.rs +++ b/ctr-std/src/sys/unix/fs.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![allow(dead_code)] + use os::unix::prelude::*; use ffi::{CString, CStr, OsString, OsStr}; @@ -23,8 +25,8 @@ use sys::time::SystemTime; use sys::{cvt, cvt_r}; use sys_common::{AsInner, FromInner}; -use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, lseek as lseek64, - dirent as dirent64, open as open64, ftruncate as ftruncate64, off_t as off64_t}; +use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off_t as off64_t, + ftruncate as ftruncate64, lseek as lseek64, dirent as dirent64, open as open64}; use libc::{readdir_r as readdir64_r}; pub struct File(FileDesc); @@ -47,6 +49,12 @@ unsafe impl Sync for Dir {} pub struct DirEntry { entry: dirent64, root: Arc<PathBuf>, + // We need to store an owned copy of the directory name + // on Solaris and Fuchsia because a) it uses a zero-length + // array to store the name, b) its lifetime between readdir + // calls is not guaranteed. + #[cfg(any(target_os = "solaris", target_os = "fuchsia"))] + name: Box<[u8]> } #[derive(Clone, Debug)] @@ -75,7 +83,7 @@ pub struct DirBuilder { mode: mode_t } impl FileAttr { pub fn size(&self) -> u64 { self.stat.st_size as u64 } pub fn perm(&self) -> FilePermissions { - FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 } + FilePermissions { mode: (self.stat.st_mode as mode_t) } } pub fn file_type(&self) -> FileType { @@ -85,17 +93,15 @@ impl FileAttr { impl FileAttr { pub fn modified(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: 0, - })) + Err(io::Error::new(io::ErrorKind::Other, + "modification time is not available on this platform \ + currently")) } pub fn accessed(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: 0, - })) + Err(io::Error::new(io::ErrorKind::Other, + "access time is not available on this platform \ + currently")) } pub fn created(&self) -> io::Result<SystemTime> { @@ -110,11 +116,17 @@ impl AsInner<stat64> for FileAttr { } impl FilePermissions { - pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 + } + pub fn set_readonly(&mut self, readonly: bool) { if readonly { + // remove write permission for all classes; equivalent to `chmod a-w <file>` self.mode &= !0o222; } else { + // add write permission for all classes; equivalent to `chmod a+w <file>` self.mode |= 0o222; } } @@ -146,6 +158,42 @@ impl fmt::Debug for ReadDir { impl Iterator for ReadDir { type Item = io::Result<DirEntry>; + #[cfg(any(target_os = "solaris", target_os = "fuchsia"))] + fn next(&mut self) -> Option<io::Result<DirEntry>> { + unsafe { + loop { + // Although readdir_r(3) would be a correct function to use here because + // of the thread safety, on Illumos and Fuchsia the readdir(3C) function + // is safe to use in threaded applications and it is generally preferred + // over the readdir_r(3C) function. + super::os::set_errno(0); + let entry_ptr = libc::readdir(self.dirp.0); + if entry_ptr.is_null() { + // NULL can mean either the end is reached or an error occurred. + // So we had to clear errno beforehand to check for an error now. + return match super::os::errno() { + 0 => None, + e => Some(Err(Error::from_raw_os_error(e))), + } + } + + let name = (*entry_ptr).d_name.as_ptr(); + let namelen = libc::strlen(name) as usize; + + let ret = DirEntry { + entry: *entry_ptr, + name: ::slice::from_raw_parts(name as *const u8, + namelen as usize).to_owned().into_boxed_slice(), + root: self.root.clone() + }; + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)) + } + } + } + } + + #[cfg(not(any(target_os = "solaris", target_os = "fuchsia")))] fn next(&mut self) -> Option<io::Result<DirEntry>> { unsafe { let mut ret = DirEntry { @@ -188,6 +236,12 @@ impl DirEntry { lstat(&self.path()) } + #[cfg(any(target_os = "solaris", target_os = "haiku"))] + pub fn file_type(&self) -> io::Result<FileType> { + lstat(&self.path()).map(|m| m.file_type()) + } + + #[cfg(not(any(target_os = "solaris", target_os = "haiku")))] pub fn file_type(&self) -> io::Result<FileType> { match self.entry.d_type { libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), @@ -293,7 +347,7 @@ impl File { // Linux kernel then the flag is just ignored by the OS, so we continue // to explicitly ask for a CLOEXEC fd here. // - // The CLOEXEC flag, however, is supported on versions of OSX/BSD/etc + // The CLOEXEC flag, however, is supported on versions of macOS/BSD/etc // that we support, so we only do this on Linux currently. if cfg!(target_os = "linux") { fd.set_cloexec()?; @@ -323,6 +377,10 @@ impl File { } pub fn truncate(&self, size: u64) -> io::Result<()> { + #[cfg(target_os = "android")] + return ::sys::android::ftruncate64(self.0.raw(), size); + + #[cfg(not(target_os = "android"))] return cvt_r(|| unsafe { ftruncate64(self.0.raw(), size as off64_t) }).map(|_| ()); @@ -332,10 +390,6 @@ impl File { self.0.read(buf) } - pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { - self.0.read_to_end(buf) - } - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { self.0.read_at(buf, offset) } @@ -358,6 +412,8 @@ impl File { SeekFrom::End(off) => (libc::SEEK_END, off), SeekFrom::Current(off) => (libc::SEEK_CUR, off), }; + #[cfg(target_os = "emscripten")] + let pos = pos as i32; let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; Ok(n as u64) } @@ -404,11 +460,52 @@ impl FromInner<c_int> for File { impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + #[cfg(target_os = "linux")] + fn get_path(fd: c_int) -> Option<PathBuf> { + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + readlink(&p).ok() + } + + #[cfg(target_os = "macos")] + fn get_path(fd: c_int) -> Option<PathBuf> { + // FIXME: The use of PATH_MAX is generally not encouraged, but it + // is inevitable in this case because macOS defines `fcntl` with + // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no + // alternatives. If a better method is invented, it should be used + // instead. + let mut buf = vec![0;libc::PATH_MAX as usize]; + let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; + if n == -1 { + return None; + } + let l = buf.iter().position(|&c| c == 0).unwrap(); + buf.truncate(l as usize); + buf.shrink_to_fit(); + Some(PathBuf::from(OsString::from_vec(buf))) + } + + #[cfg(not(any(target_os = "linux", target_os = "macos")))] fn get_path(_fd: c_int) -> Option<PathBuf> { // FIXME(#24570): implement this for other Unix platforms None } + #[cfg(any(target_os = "linux", target_os = "macos"))] + fn get_mode(fd: c_int) -> Option<(bool, bool)> { + let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; + if mode == -1 { + return None; + } + match mode & libc::O_ACCMODE { + libc::O_RDONLY => Some((true, false)), + libc::O_RDWR => Some((true, true)), + libc::O_WRONLY => Some((false, true)), + _ => None + } + } + + #[cfg(not(any(target_os = "linux", target_os = "macos")))] fn get_mode(_fd: c_int) -> Option<(bool, bool)> { // FIXME(#24570): implement this for other Unix platforms None diff --git a/ctr-std/src/sys/unix/io.rs b/ctr-std/src/sys/unix/io.rs deleted file mode 100644 index 6d38b00..0000000 --- a/ctr-std/src/sys/unix/io.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015 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 io; -use libc; -use sys::fd::FileDesc; - -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); - -impl Stdin { - pub fn new() -> io::Result<Stdin> { Ok(Stdin(())) } - - pub fn read(&self, data: &mut [u8]) -> io::Result<usize> { - let fd = FileDesc::new(libc::STDIN_FILENO); - let ret = fd.read(data); - fd.into_raw(); - ret - } - - pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { - let fd = FileDesc::new(libc::STDIN_FILENO); - let ret = fd.read_to_end(buf); - fd.into_raw(); - ret - } -} - -impl Stdout { - pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) } - - pub fn write(&self, data: &[u8]) -> io::Result<usize> { - let fd = FileDesc::new(libc::STDOUT_FILENO); - let ret = fd.write(data); - fd.into_raw(); - ret - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub fn new() -> io::Result<Stderr> { Ok(Stderr(())) } - - pub fn write(&self, data: &[u8]) -> io::Result<usize> { - let fd = FileDesc::new(libc::STDERR_FILENO); - let ret = fd.write(data); - fd.into_raw(); - ret - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } -} - -// FIXME: right now this raw stderr handle is used in a few places because -// std::io::stderr_raw isn't exposed, but once that's exposed this impl -// should go away -impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result<usize> { - Stderr::write(self, data) - } - - fn flush(&mut self) -> io::Result<()> { - Stderr::flush(self) - } -} - -pub const EBADF_ERR: i32 = ::libc::EBADF as i32; -pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; diff --git a/ctr-std/src/sys/unix/memchr.rs b/ctr-std/src/sys/unix/memchr.rs index ae8e3d0..71d9111 100644 --- a/ctr-std/src/sys/unix/memchr.rs +++ b/ctr-std/src/sys/unix/memchr.rs @@ -1,3 +1,13 @@ +// Copyright 2017 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. + // Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. @@ -28,10 +38,8 @@ pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> { } pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> { - // turns out that newlib doesn't have memrchr(), so we - // use the fallback version instead fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> { - ::sys_common::memchr::fallback::memrchr(needle, haystack) + ::core::slice::memchr::memrchr(needle, haystack) } memrchr_specific(needle, haystack) diff --git a/ctr-std/src/sys/unix/mod.rs b/ctr-std/src/sys/unix/mod.rs index c0e4eb4..11be366 100644 --- a/ctr-std/src/sys/unix/mod.rs +++ b/ctr-std/src/sys/unix/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,46 +8,53 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(missing_docs, bad_style)] +//! System bindings for the Nintendo 3DS use io::{self, ErrorKind}; use libc; +#[cfg(any(dox, target_os = "linux", target_os = "horizon"))] pub use os::linux as platform; + pub use self::rand::hashmap_random_keys; +pub use libc::strlen; +pub mod args; +#[cfg(feature = "backtrace")] +pub mod backtrace; +pub mod cmath; pub mod condvar; +pub mod env; pub mod ext; pub mod fast_thread_local; pub mod fd; pub mod fs; -pub mod stdio; pub mod memchr; pub mod mutex; +pub mod net; pub mod os; pub mod os_str; pub mod path; +pub mod pipe; +pub mod process; +pub mod rand; pub mod rwlock; +pub mod stack_overflow; pub mod thread; -pub mod rand; pub mod thread_local; pub mod time; +pub mod stdio; #[cfg(not(test))] pub fn init() { - // By default, some platforms will send a *signal* when an EPIPE error - // would otherwise be delivered. This runtime doesn't install a SIGPIPE - // handler, causing it to kill the program, which isn't exactly what we - // want! - // - // Hence, we set SIGPIPE to ignore when the program starts up in order - // to prevent this problem. - unsafe { - reset_sigpipe(); - } +} - // I don't think we have signal handling on the 3DS, so let's leave this - // blank for now - unsafe fn reset_sigpipe() {} +pub fn unsupported<T>() -> io::Result<T> { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> io::Error { + io::Error::new(io::ErrorKind::Other, + "operation not supported on 3DS yet") } pub fn decode_error_kind(errno: i32) -> ErrorKind { @@ -76,6 +83,11 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { } } +// This enum is used as the storage for a bunch of types which can't actually +// exist. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Void {} + #[doc(hidden)] pub trait IsMinusOne { fn is_minus_one(&self) -> bool; @@ -111,6 +123,13 @@ pub fn cvt_r<T, F>(mut f: F) -> io::Result<T> } } +// On Unix-like platforms, libc::abort will unregister signal handlers +// including the SIGABRT handler, preventing the abort from being blocked, and +// fclose streams, with the side effect of flushing them so libc bufferred +// output will be printed. Additionally the shell will generally print a more +// understandable error message like "Abort trap" rather than "Illegal +// instruction" that intrinsics::abort would cause, as intrinsics::abort is +// implemented as an illegal instruction. pub unsafe fn abort_internal() -> ! { ::libc::abort() } diff --git a/ctr-std/src/sys/unix/mutex.rs b/ctr-std/src/sys/unix/mutex.rs index 0cfd392..51e6b47 100644 --- a/ctr-std/src/sys/unix/mutex.rs +++ b/ctr-std/src/sys/unix/mutex.rs @@ -1,4 +1,4 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -11,7 +11,9 @@ use cell::UnsafeCell; use mem; -pub struct Mutex { inner: UnsafeCell<::libctru::LightLock> } +pub struct Mutex { + inner: UnsafeCell<::libctru::LightLock>, +} #[inline] pub unsafe fn raw(m: &Mutex) -> *mut ::libctru::LightLock { @@ -21,23 +23,26 @@ pub unsafe fn raw(m: &Mutex) -> *mut ::libctru::LightLock { unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} -#[allow(dead_code)] // sys isn't exported yet impl Mutex { pub const fn new() -> Mutex { Mutex { inner: UnsafeCell::new(0) } } + #[inline] pub unsafe fn init(&mut self) { ::libctru::LightLock_Init(self.inner.get()); } + #[inline] pub unsafe fn lock(&self) { ::libctru::LightLock_Lock(self.inner.get()); } + #[inline] pub unsafe fn unlock(&self) { ::libctru::LightLock_Unlock(self.inner.get()); } + #[inline] pub unsafe fn try_lock(&self) -> bool { match ::libctru::LightLock_TryLock(self.inner.get()) { @@ -45,8 +50,10 @@ impl Mutex { _ => false, } } + #[inline] - pub unsafe fn destroy(&self) {} + pub unsafe fn destroy(&self) { + } } pub struct ReentrantMutex { inner: UnsafeCell<::libctru::RecursiveLock> } @@ -58,18 +65,15 @@ impl ReentrantMutex { pub unsafe fn uninitialized() -> ReentrantMutex { ReentrantMutex { inner: mem::uninitialized() } } - #[inline] + pub unsafe fn init(&mut self) { ::libctru::RecursiveLock_Init(self.inner.get()); } - #[inline] + pub unsafe fn lock(&self) { ::libctru::RecursiveLock_Lock(self.inner.get()); } - #[inline] - pub unsafe fn unlock(&self) { - ::libctru::RecursiveLock_Unlock(self.inner.get()); - } + #[inline] pub unsafe fn try_lock(&self) -> bool { match ::libctru::RecursiveLock_TryLock(self.inner.get()) { @@ -77,6 +81,10 @@ impl ReentrantMutex { _ => false, } } - #[inline] + + pub unsafe fn unlock(&self) { + ::libctru::RecursiveLock_Unlock(self.inner.get()); + } + pub unsafe fn destroy(&self) {} } diff --git a/ctr-std/src/sys/unix/net.rs b/ctr-std/src/sys/unix/net.rs new file mode 100644 index 0000000..b9b1a39 --- /dev/null +++ b/ctr-std/src/sys/unix/net.rs @@ -0,0 +1,418 @@ +// Copyright 2015 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(dead_code)] + +use ffi::CStr; +use io; +use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_NONAME as EAI_SYSTEM, MSG_PEEK}; +use mem; +use net::{SocketAddr, Shutdown}; +use str; +use sys::fd::FileDesc; +use sys_common::{AsInner, FromInner, IntoInner}; +use sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use time::{Duration, Instant}; +use cmp; + +pub use sys::{cvt, cvt_r}; +pub extern crate libc as netc; + +#[allow(non_camel_case_types)] +pub type wrlen_t = size_t; + +// See below for the usage of SOCK_CLOEXEC, but this constant is only defined on +// Linux currently (e.g. support doesn't exist on other platforms). In order to +// get name resolution to work and things to compile we just define a dummy +// SOCK_CLOEXEC here for other platforms. Note that the dummy constant isn't +// actually ever used (the blocks below are wrapped in `if cfg!` as well. +#[cfg(target_os = "linux")] +use libc::SOCK_CLOEXEC; +#[cfg(not(target_os = "linux"))] +const SOCK_CLOEXEC: c_int = 0; + +// Another conditional contant for name resolution: Macos et iOS use +// SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. +// Other platforms do otherwise. +#[cfg(target_vendor = "apple")] +use libc::SO_NOSIGPIPE; +#[cfg(not(target_vendor = "apple"))] +const SO_NOSIGPIPE: c_int = 0; + +pub struct Socket(FileDesc); + +pub fn init() {} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + return Ok(()) + } + if err == EAI_SYSTEM { + return Err(io::Error::last_os_error()) + } + + let detail = unsafe { + str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap() + .to_owned() + }; + Err(io::Error::new(io::ErrorKind::Other, + &format!("failed to lookup address information: {}", + detail)[..])) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> { + let fam = match *addr { + SocketAddr::V4(..) => libc::AF_INET, + SocketAddr::V6(..) => libc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { + unsafe { + // On linux we first attempt to pass the SOCK_CLOEXEC flag to + // atomically create the socket and set it as CLOEXEC. Support for + // this option, however, was added in 2.6.27, and we still support + // 2.6.18 as a kernel, so if the returned error is EINVAL we + // fallthrough to the fallback. + if cfg!(target_os = "linux") { + match cvt(libc::socket(fam, ty | SOCK_CLOEXEC, 0)) { + Ok(fd) => return Ok(Socket(FileDesc::new(fd))), + Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {} + Err(e) => return Err(e), + } + } + + let fd = cvt(libc::socket(fam, ty, 0))?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + let socket = Socket(fd); + if cfg!(target_vendor = "apple") { + setsockopt(&socket, libc::SOL_SOCKET, SO_NOSIGPIPE, 1)?; + } + Ok(socket) + } + } + + pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { + unsafe { + let mut fds = [0, 0]; + + // Like above, see if we can set cloexec atomically + if cfg!(target_os = "linux") { + match cvt(libc::socketpair(fam, ty | SOCK_CLOEXEC, 0, fds.as_mut_ptr())) { + Ok(_) => { + return Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))); + } + Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}, + Err(e) => return Err(e), + } + } + + cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; + let a = FileDesc::new(fds[0]); + let b = FileDesc::new(fds[1]); + a.set_cloexec()?; + b.set_cloexec()?; + Ok((Socket(a), Socket(b))) + } + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addrp, len) = addr.into_inner(); + cvt(libc::connect(self.0.raw(), addrp, len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = libc::pollfd { + fd: self.0.raw(), + events: libc::POLLOUT, + revents: 0, + }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout.as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::max_value() as u64) as c_int; + + match unsafe { libc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if err.kind() != io::ErrorKind::Interrupted { + return Err(err); + } + } + 0 => {} + _ => { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP rather than read readiness + if pollfd.revents & libc::POLLHUP != 0 { + let e = self.take_error()? + .unwrap_or_else(|| { + io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") + }); + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) + -> io::Result<Socket> { + let fd = cvt_r(|| unsafe { + libc::accept(self.0.raw(), storage, len) + })?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + Ok(Socket(fd)) + } + + pub fn duplicate(&self) -> io::Result<Socket> { + self.0.duplicate().map(Socket) + } + + fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::recv(self.0.raw(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags) + })?; + Ok(ret as usize) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, 0) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, MSG_PEEK) + } + + fn recv_from_with_flags(&self, buf: &mut [u8], flags: c_int) + -> io::Result<(usize, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; + + let n = cvt(unsafe { + libc::recvfrom(self.0.raw(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + &mut storage as *mut _ as *mut _, + &mut addrlen) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout")); + } + + let secs = if dur.as_secs() > libc::time_t::max_value() as u64 { + libc::time_t::max_value() + } else { + dur.as_secs() as libc::time_t + }; + let mut timeout = libc::timeval { + tv_sec: secs, + tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => { + libc::timeval { + tv_sec: 0, + tv_usec: 0, + } + } + }; + setsockopt(self, libc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> { + let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => libc::SHUT_RDWR, + }; + cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; + Ok(()) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result<bool> { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; + Ok(raw != 0) + } + + // TODO: Fix libc::FIONBIO and remove explicit cast + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as libc::c_int; + cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO as u32, &mut nonblocking) }).map(|_| ()) + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + if raw == 0 { + Ok(None) + } else { + Ok(Some(io::Error::from_raw_os_error(raw as i32))) + } + } +} + +impl AsInner<c_int> for Socket { + fn as_inner(&self) -> &c_int { self.0.as_inner() } +} + +impl FromInner<c_int> for Socket { + fn from_inner(fd: c_int) -> Socket { Socket(FileDesc::new(fd)) } +} + +impl IntoInner<c_int> for Socket { + fn into_inner(self) -> c_int { self.0.into_raw() } +} + +// In versions of glibc prior to 2.26, there's a bug where the DNS resolver +// will cache the contents of /etc/resolv.conf, so changes to that file on disk +// can be ignored by a long-running program. That can break DNS lookups on e.g. +// laptops where the network comes and goes. See +// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some +// distros including Debian have patched glibc to fix this for a long time. +// +// A workaround for this bug is to call the res_init libc function, to clear +// the cached configs. Unfortunately, while we believe glibc's implementation +// of res_init is thread-safe, we know that other implementations are not +// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could +// try to synchronize its res_init calls with a Mutex, but that wouldn't +// protect programs that call into libc in other ways. So instead of calling +// res_init unconditionally, we call it only when we detect we're linking +// against glibc version < 2.26. (That is, when we both know its needed and +// believe it's thread-safe). +pub fn res_init_if_glibc_before_2_26() -> io::Result<()> { + // If the version fails to parse, we treat it the same as "not glibc". + if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) { + if let Some(version) = parse_glibc_version(version_str) { + if version < (2, 26) { + let ret = unsafe { libc::res_init() }; + if ret != 0 { + return Err(io::Error::last_os_error()); + } + } + } + } + Ok(()) +} + +fn glibc_version_cstr() -> Option<&'static CStr> { + None +} + +// Returns Some((major, minor)) if the string is a valid "x.y" version, +// ignoring any extra dot-separated parts. Otherwise return None. +fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { + let mut parsed_ints = version.split(".").map(str::parse::<usize>).fuse(); + match (parsed_ints.next(), parsed_ints.next()) { + (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)), + _ => None + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_res_init() { + // This mostly just tests that the weak linkage doesn't panic wildly... + res_init_if_glibc_before_2_26().unwrap(); + } + + #[test] + fn test_parse_glibc_version() { + let cases = [ + ("0.0", Some((0, 0))), + ("01.+2", Some((1, 2))), + ("3.4.5.six", Some((3, 4))), + ("1", None), + ("1.-2", None), + ("1.foo", None), + ("foo.1", None), + ]; + for &(version_str, parsed) in cases.iter() { + assert_eq!(parsed, parse_glibc_version(version_str)); + } + } +} diff --git a/ctr-std/src/sys/unix/os.rs b/ctr-std/src/sys/unix/os.rs index de087d9..5de756d 100644 --- a/ctr-std/src/sys/unix/os.rs +++ b/ctr-std/src/sys/unix/os.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,26 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Implementation of `std::os` functionality for unix systems - -#![allow(unused_imports)] // lots of cfg code here - -use os::unix::prelude::*; - use error::Error as StdError; use ffi::{CString, CStr, OsString, OsStr}; use fmt; use io; use iter; -use libc::{self, c_int, c_char, c_void}; -use marker::PhantomData; -use mem; -use memchr; +use libc::{self, c_int, c_char}; use path::{self, PathBuf}; -use ptr; use slice; use str; -use vec; +use sys::{unsupported, Void}; +use sys::unix::ext::ffi::{OsStrExt, OsStringExt}; const TMPBUF_SZ: usize = 128; @@ -64,6 +55,42 @@ pub fn error_string(errno: i32) -> String { } } +pub fn getcwd() -> io::Result<PathBuf> { + let mut buf = Vec::with_capacity(512); + loop { + unsafe { + let ptr = buf.as_mut_ptr() as *mut libc::c_char; + if !libc::getcwd(ptr, buf.capacity()).is_null() { + let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); + buf.set_len(len); + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); + } else { + let error = io::Error::last_os_error(); + if error.raw_os_error() != Some(libc::ERANGE) { + return Err(error); + } + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + } + } +} + +pub fn chdir(p: &path::Path) -> io::Result<()> { + let p: &OsStr = p.as_ref(); + let p = CString::new(p.as_bytes())?; + unsafe { + match libc::chdir(p.as_ptr()) == (0 as c_int) { + true => Ok(()), + false => Err(io::Error::last_os_error()), + } + } +} pub struct SplitPaths<'a> { iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>, @@ -118,6 +145,47 @@ impl StdError for JoinPathsError { fn description(&self) -> &str { "failed to join paths" } } +pub fn current_exe() -> io::Result<PathBuf> { + unsupported() +} + +pub struct Env(Void); + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + match self.0 {} + } +} + +pub fn env() -> Env { + panic!("not supported on 3DS yet") +} + +pub fn getenv(_k: &OsStr) -> io::Result<Option<OsString>> { + return Ok(None) +} + +pub fn setenv(_k: &OsStr, _v: &OsStr) -> io::Result<()> { + unsupported() +} + +pub fn unsetenv(_n: &OsStr) -> io::Result<()> { + unsupported() +} + +pub fn temp_dir() -> PathBuf { + PathBuf::from("/tmp") +} + +pub fn home_dir() -> Option<PathBuf> { + None +} + pub fn exit(code: i32) -> ! { unsafe { libc::exit(code as c_int) } } + +pub fn getpid() -> u32 { + panic!("no pids on 3DS") +} diff --git a/ctr-std/src/sys/unix/os_str.rs b/ctr-std/src/sys/unix/os_str.rs index 5a733c0..543c22e 100644 --- a/ctr-std/src/sys/unix/os_str.rs +++ b/ctr-std/src/sys/unix/os_str.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -12,10 +12,14 @@ /// a `Vec<u8>`/`[u8]`. use borrow::Cow; -use fmt::{self, Debug}; +use fmt; use str; use mem; +use rc::Rc; +use sync::Arc; use sys_common::{AsInner, IntoInner}; +use sys_common::bytestring::debug_fmt_bytestring; +use std_unicode::lossy::Utf8Lossy; #[derive(Clone, Hash)] pub struct Buf { @@ -26,15 +30,27 @@ pub struct Slice { pub inner: [u8] } -impl Debug for Slice { - fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.to_string_lossy().fmt(formatter) +impl fmt::Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + debug_fmt_bytestring(&self.inner, formatter) } } -impl Debug for Buf { - fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { - self.as_slice().fmt(formatter) +impl fmt::Display for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) + } +} + +impl fmt::Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self.as_slice(), formatter) + } +} + +impl fmt::Display for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self.as_slice(), formatter) } } @@ -83,6 +99,11 @@ impl Buf { self.inner.reserve_exact(additional) } + #[inline] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + pub fn as_slice(&self) -> &Slice { unsafe { mem::transmute(&*self.inner) } } @@ -94,6 +115,27 @@ impl Buf { pub fn push_slice(&mut self, s: &Slice) { self.inner.extend_from_slice(&s.inner) } + + #[inline] + pub fn into_box(self) -> Box<Slice> { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } + + #[inline] + pub fn from_box(boxed: Box<Slice>) -> Buf { + let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; + Buf { inner: inner.into_vec() } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc<Slice> { + self.as_slice().into_rc() + } } impl Slice { @@ -116,4 +158,27 @@ impl Slice { pub fn to_owned(&self) -> Buf { Buf { inner: self.inner.to_vec() } } + + #[inline] + pub fn into_box(&self) -> Box<Slice> { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box<Slice> { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + let arc: Arc<[u8]> = Arc::from(&self.inner); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc<Slice> { + let rc: Rc<[u8]> = Rc::from(&self.inner); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } } diff --git a/ctr-std/src/sys/unix/path.rs b/ctr-std/src/sys/unix/path.rs index bf9af7a..395b8c1 100644 --- a/ctr-std/src/sys/unix/path.rs +++ b/ctr-std/src/sys/unix/path.rs @@ -1,4 +1,4 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // diff --git a/ctr-std/src/sys/unix/pipe.rs b/ctr-std/src/sys/unix/pipe.rs new file mode 100644 index 0000000..992e1ac --- /dev/null +++ b/ctr-std/src/sys/unix/pipe.rs @@ -0,0 +1,35 @@ +// Copyright 2017 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 io; +use sys::Void; + +pub struct AnonPipe(Void); + +impl AnonPipe { + pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +pub fn read2(p1: AnonPipe, + _v1: &mut Vec<u8>, + _p2: AnonPipe, + _v2: &mut Vec<u8>) -> io::Result<()> { + match p1.0 {} +} diff --git a/ctr-std/src/sys/unix/process.rs b/ctr-std/src/sys/unix/process.rs new file mode 100644 index 0000000..f058aa9 --- /dev/null +++ b/ctr-std/src/sys/unix/process.rs @@ -0,0 +1,151 @@ +// Copyright 2017 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 ffi::OsStr; +use fmt; +use io; +use sys::fs::File; +use sys::pipe::AnonPipe; +use sys::{unsupported, Void}; +use sys_common::process::{CommandEnv, DefaultEnvKey}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + env: CommandEnv<DefaultEnvKey> +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option<AnonPipe>, + pub stdout: Option<AnonPipe>, + pub stderr: Option<AnonPipe>, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(_program: &OsStr) -> Command { + Command { + env: Default::default() + } + } + + pub fn arg(&mut self, _arg: &OsStr) { + } + + pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> { + &mut self.env + } + + pub fn cwd(&mut self, _dir: &OsStr) { + } + + pub fn stdin(&mut self, _stdin: Stdio) { + } + + pub fn stdout(&mut self, _stdout: Stdio) { + } + + pub fn stderr(&mut self, _stderr: Stdio) { + } + + pub fn spawn(&mut self, _default: Stdio, _needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + unsupported() + } +} + +impl From<AnonPipe> for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From<File> for Stdio { + fn from(_file: File) -> Stdio { + //file.diverge() + unimplemented!() + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + Ok(()) + } +} + +pub struct ExitStatus(Void); + +impl ExitStatus { + pub fn success(&self) -> bool { + match self.0 {} + } + + pub fn code(&self) -> Option<i32> { + match self.0 {} + } +} + +impl Clone for ExitStatus { + fn clone(&self) -> ExitStatus { + match self.0 {} + } +} + +impl Copy for ExitStatus {} + +impl PartialEq for ExitStatus { + fn eq(&self, _other: &ExitStatus) -> bool { + match self.0 {} + } +} + +impl Eq for ExitStatus { +} + +impl fmt::Debug for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub struct Process(Void); + +impl Process { + pub fn id(&self) -> u32 { + match self.0 {} + } + + pub fn kill(&mut self) -> io::Result<()> { + match self.0 {} + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + match self.0 {} + } + + pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { + match self.0 {} + } +} diff --git a/ctr-std/src/sys/unix/stack_overflow.rs b/ctr-std/src/sys/unix/stack_overflow.rs new file mode 100644 index 0000000..bed2741 --- /dev/null +++ b/ctr-std/src/sys/unix/stack_overflow.rs @@ -0,0 +1,23 @@ +// Copyright 2017 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. + +pub struct Handler; + +impl Handler { + pub unsafe fn new() -> Handler { + Handler + } +} + +pub unsafe fn init() { +} + +pub unsafe fn cleanup() { +} diff --git a/ctr-std/src/sys/unix/stdio.rs b/ctr-std/src/sys/unix/stdio.rs index 6d38b00..e9b3d4a 100644 --- a/ctr-std/src/sys/unix/stdio.rs +++ b/ctr-std/src/sys/unix/stdio.rs @@ -25,13 +25,6 @@ impl Stdin { fd.into_raw(); ret } - - pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { - let fd = FileDesc::new(libc::STDIN_FILENO); - let ret = fd.read_to_end(buf); - fd.into_raw(); - ret - } } impl Stdout { @@ -77,5 +70,8 @@ impl io::Write for Stderr { } } -pub const EBADF_ERR: i32 = ::libc::EBADF as i32; +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(libc::EBADF as i32) +} + pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; diff --git a/ctr-std/src/sys/unix/thread.rs b/ctr-std/src/sys/unix/thread.rs index 0b21bff..21224ad 100644 --- a/ctr-std/src/sys/unix/thread.rs +++ b/ctr-std/src/sys/unix/thread.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -18,29 +18,28 @@ use ptr; use sys_common::thread::start_thread; use time::Duration; -use libctru::{svcSleepThread, svcGetThreadPriority, - threadCreate, threadJoin, threadFree, threadDetach, - Thread as ThreadHandle}; +use libctru::Thread as ThreadHandle; pub struct Thread { handle: ThreadHandle, } -// Some platforms may have pthread_t as a pointer in which case we still want -// a thread to be Send/Sync unsafe impl Send for Thread {} unsafe impl Sync for Thread {} +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + + impl Thread { pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) -> io::Result<Thread> { let p = box p; - let stack_size = cmp::max(stack, 0x10000); + let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); let mut priority = 0; - svcGetThreadPriority(&mut priority, 0xFFFF8000); + ::libctru::svcGetThreadPriority(&mut priority, 0xFFFF8000); - let handle = threadCreate(Some(thread_func), &*p as *const _ as *mut _, - stack_size, priority, -2, false); + let handle = ::libctru::threadCreate(Some(thread_func), &*p as *const _ as *mut _, + stack_size, priority, -2, false); return if handle == ptr::null_mut() { Err(io::Error::from_raw_os_error(libc::EAGAIN)) @@ -50,12 +49,14 @@ impl Thread { }; extern "C" fn thread_func(start: *mut libc::c_void) { - unsafe { start_thread(start) } + unsafe { start_thread(start as *mut u8) } } } - + pub fn yield_now() { - unsafe { svcSleepThread(0) } + unsafe { + ::libctru::svcSleepThread(0) + } } pub fn set_name(_name: &CStr) { @@ -64,35 +65,21 @@ impl Thread { pub fn sleep(dur: Duration) { unsafe { - let nanos = dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64; - svcSleepThread(nanos as i64) + let nanos = dur.as_secs() + .saturating_mul(1_000_000_000) + .saturating_add(dur.subsec_nanos() as u64); + ::libctru::svcSleepThread(nanos as i64) } } pub fn join(self) { unsafe { - let ret = threadJoin(self.handle, u64::max_value()); - threadFree(self.handle); + let ret = ::libctru::threadJoin(self.handle, u64::max_value()); + ::libctru::threadFree(self.handle); mem::forget(self); debug_assert_eq!(ret, 0); } } - - pub fn id(&self) -> ThreadHandle { - self.handle - } - - pub fn into_id(self) -> ThreadHandle { - let handle = self.handle; - mem::forget(self); - handle - } -} - -impl Drop for Thread { - fn drop(&mut self) { - unsafe { threadDetach(self.handle) } - } } pub mod guard { diff --git a/ctr-std/src/sys/unix/thread_local.rs b/ctr-std/src/sys/unix/thread_local.rs index abdd9ac..3cb3523 100644 --- a/ctr-std/src/sys/unix/thread_local.rs +++ b/ctr-std/src/sys/unix/thread_local.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Borrowed from /sys/redox/thread_local.rs + #![allow(dead_code)] // not used on all platforms use collections::BTreeMap; @@ -64,3 +66,8 @@ pub unsafe fn set(key: Key, value: *mut u8) { pub unsafe fn destroy(key: Key) { keys().remove(&key); } + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/ctr-std/src/sys/unix/time.rs b/ctr-std/src/sys/unix/time.rs index 3bc1dca..d5dcbc4 100644 --- a/ctr-std/src/sys/unix/time.rs +++ b/ctr-std/src/sys/unix/time.rs @@ -11,6 +11,7 @@ use cmp::Ordering; use libc; use time::Duration; +use core::hash::{Hash, Hasher}; pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; @@ -103,6 +104,13 @@ impl Ord for Timespec { } } +impl Hash for Timespec { + fn hash<H : Hasher>(&self, state: &mut H) { + self.t.tv_sec.hash(state); + self.t.tv_nsec.hash(state); + } +} + mod inner { use fmt; use libc; @@ -116,12 +124,12 @@ mod inner { use libctru; - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct Instant { t: u64 } - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SystemTime { t: Timespec, } |