aboutsummaryrefslogtreecommitdiff
path: root/ctr-std/src/sys/unix
diff options
context:
space:
mode:
authorValentin <[email protected]>2018-06-15 18:57:24 +0200
committerFenrirWolf <[email protected]>2018-06-15 10:57:24 -0600
commitf2a90174bb36b9ad528e863ab34c02ebce002b02 (patch)
tree959e8d67883d3a89e179b3549b1f30d28e51a87c /ctr-std/src/sys/unix
parentMerge pull request #68 from linouxis9/master (diff)
downloadctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.tar.xz
ctru-rs-f2a90174bb36b9ad528e863ab34c02ebce002b02.zip
Update for latest nightly 2018-06-09 (#70)
* Update for latest nightly 2018-06-09 * We now have a proper horizon os and sys modules in libstd
Diffstat (limited to 'ctr-std/src/sys/unix')
-rw-r--r--ctr-std/src/sys/unix/android.rs170
-rw-r--r--ctr-std/src/sys/unix/args.rs202
-rw-r--r--ctr-std/src/sys/unix/backtrace.rs37
-rw-r--r--ctr-std/src/sys/unix/backtrace/mod.rs119
-rw-r--r--ctr-std/src/sys/unix/backtrace/printing/dladdr.rs45
-rw-r--r--ctr-std/src/sys/unix/backtrace/printing/mod.rs43
-rw-r--r--ctr-std/src/sys/unix/backtrace/tracing/backtrace_fn.rs49
-rw-r--r--ctr-std/src/sys/unix/backtrace/tracing/gcc_s.rs107
-rw-r--r--ctr-std/src/sys/unix/backtrace/tracing/mod.rs18
-rw-r--r--ctr-std/src/sys/unix/condvar.rs240
-rw-r--r--ctr-std/src/sys/unix/env.rs171
-rw-r--r--ctr-std/src/sys/unix/ext/ffi.rs8
-rw-r--r--ctr-std/src/sys/unix/ext/fs.rs736
-rw-r--r--ctr-std/src/sys/unix/ext/mod.rs28
-rw-r--r--ctr-std/src/sys/unix/ext/net.rs1818
-rw-r--r--ctr-std/src/sys/unix/ext/process.rs203
-rw-r--r--ctr-std/src/sys/unix/ext/raw.rs17
-rw-r--r--ctr-std/src/sys/unix/ext/thread.rs51
-rw-r--r--ctr-std/src/sys/unix/fast_thread_local.rs130
-rw-r--r--ctr-std/src/sys/unix/fd.rs89
-rw-r--r--ctr-std/src/sys/unix/fs.rs341
-rw-r--r--ctr-std/src/sys/unix/l4re.rs441
-rw-r--r--ctr-std/src/sys/unix/memchr.rs31
-rw-r--r--ctr-std/src/sys/unix/mod.rs61
-rw-r--r--ctr-std/src/sys/unix/mutex.rs104
-rw-r--r--ctr-std/src/sys/unix/net.rs73
-rw-r--r--ctr-std/src/sys/unix/os.rs441
-rw-r--r--ctr-std/src/sys/unix/os_str.rs2
-rw-r--r--ctr-std/src/sys/unix/path.rs14
-rw-r--r--ctr-std/src/sys/unix/pipe.rs124
-rw-r--r--ctr-std/src/sys/unix/process.rs169
-rw-r--r--ctr-std/src/sys/unix/process/mod.rs22
-rw-r--r--ctr-std/src/sys/unix/process/process_common.rs510
-rw-r--r--ctr-std/src/sys/unix/process/process_fuchsia.rs181
-rw-r--r--ctr-std/src/sys/unix/process/process_unix.rs405
-rw-r--r--ctr-std/src/sys/unix/process/zircon.rs279
-rw-r--r--ctr-std/src/sys/unix/rand.rs200
-rw-r--r--ctr-std/src/sys/unix/rwlock.rs173
-rw-r--r--ctr-std/src/sys/unix/stack_overflow.rs207
-rw-r--r--ctr-std/src/sys/unix/thread.rs431
-rw-r--r--ctr-std/src/sys/unix/thread_local.rs56
-rw-r--r--ctr-std/src/sys/unix/time.rs226
-rw-r--r--ctr-std/src/sys/unix/weak.rs79
43 files changed, 7977 insertions, 874 deletions
diff --git a/ctr-std/src/sys/unix/android.rs b/ctr-std/src/sys/unix/android.rs
new file mode 100644
index 0000000..1043672
--- /dev/null
+++ b/ctr-std/src/sys/unix/android.rs
@@ -0,0 +1,170 @@
+// Copyright 2016 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.
+
+//! Android ABI-compatibility module
+//!
+//! The ABI of Android has changed quite a bit over time, and libstd attempts to
+//! be both forwards and backwards compatible as much as possible. We want to
+//! always work with the most recent version of Android, but we also want to
+//! work with older versions of Android for whenever projects need to.
+//!
+//! Our current minimum supported Android version is `android-9`, e.g. Android
+//! with API level 9. We then in theory want to work on that and all future
+//! versions of Android!
+//!
+//! Some of the detection here is done at runtime via `dlopen` and
+//! introspection. Other times no detection is performed at all and we just
+//! provide a fallback implementation as some versions of Android we support
+//! don't have the function.
+//!
+//! You'll find more details below about why each compatibility shim is needed.
+
+#![cfg(target_os = "android")]
+
+use libc::{c_int, c_void, sighandler_t, size_t, ssize_t};
+use libc::{ftruncate, pread, pwrite};
+
+use io;
+use super::{cvt, cvt_r};
+
+// The `log2` and `log2f` functions apparently appeared in android-18, or at
+// least you can see they're not present in the android-17 header [1] and they
+// are present in android-18 [2].
+//
+// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+// /android-17/arch-arm/usr/include/math.h
+// [2]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+// /android-18/arch-arm/usr/include/math.h
+//
+// Note that these shims are likely less precise than directly calling `log2`,
+// but hopefully that should be enough for now...
+//
+// Note that mathematically, for any arbitrary `y`:
+//
+// log_2(x) = log_y(x) / log_y(2)
+// = log_y(x) / (1 / log_2(y))
+// = log_y(x) * log_2(y)
+//
+// Hence because `ln` (log_e) is available on all Android we just choose `y = e`
+// and get:
+//
+// log_2(x) = ln(x) * log_2(e)
+
+#[cfg(not(test))]
+pub fn log2f32(f: f32) -> f32 {
+ f.ln() * ::f32::consts::LOG2_E
+}
+
+#[cfg(not(test))]
+pub fn log2f64(f: f64) -> f64 {
+ f.ln() * ::f64::consts::LOG2_E
+}
+
+// Back in the day [1] the `signal` function was just an inline wrapper
+// around `bsd_signal`, but starting in API level android-20 the `signal`
+// symbols was introduced [2]. Finally, in android-21 the API `bsd_signal` was
+// removed [3].
+//
+// Basically this means that if we want to be binary compatible with multiple
+// Android releases (oldest being 9 and newest being 21) then we need to check
+// for both symbols and not actually link against either.
+//
+// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
+// /android-18/arch-arm/usr/include/signal.h
+// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental
+// /platforms/android-20/arch-arm
+// /usr/include/signal.h
+// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms
+// /android-21/arch-arm/usr/include/signal.h
+pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t {
+ weak!(fn signal(c_int, sighandler_t) -> sighandler_t);
+ weak!(fn bsd_signal(c_int, sighandler_t) -> sighandler_t);
+
+ let f = signal.get().or_else(|| bsd_signal.get());
+ let f = f.expect("neither `signal` nor `bsd_signal` symbols found");
+ f(signum, handler)
+}
+
+// The `ftruncate64` symbol apparently appeared in android-12, so we do some
+// dynamic detection to see if we can figure out whether `ftruncate64` exists.
+//
+// If it doesn't we just fall back to `ftruncate`, generating an error for
+// too-large values.
+#[cfg(target_pointer_width = "32")]
+pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
+ weak!(fn ftruncate64(c_int, i64) -> c_int);
+
+ unsafe {
+ match ftruncate64.get() {
+ Some(f) => cvt_r(|| f(fd, size as i64)).map(|_| ()),
+ None => {
+ if size > i32::max_value() as u64 {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot truncate >2GB"))
+ } else {
+ cvt_r(|| ftruncate(fd, size as i32)).map(|_| ())
+ }
+ }
+ }
+ }
+}
+
+#[cfg(target_pointer_width = "64")]
+pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
+ unsafe {
+ cvt_r(|| ftruncate(fd, size as i64)).map(|_| ())
+ }
+}
+
+#[cfg(target_pointer_width = "32")]
+pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ use convert::TryInto;
+ weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t);
+ pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
+ if let Ok(o) = offset.try_into() {
+ cvt(pread(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pread >2GB"))
+ }
+ })
+}
+
+#[cfg(target_pointer_width = "32")]
+pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ use convert::TryInto;
+ weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t);
+ pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
+ if let Ok(o) = offset.try_into() {
+ cvt(pwrite(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pwrite >2GB"))
+ }
+ })
+}
+
+#[cfg(target_pointer_width = "64")]
+pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ cvt(pread(fd, buf, count, offset))
+}
+
+#[cfg(target_pointer_width = "64")]
+pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64)
+ -> io::Result<ssize_t>
+{
+ cvt(pwrite(fd, buf, count, offset))
+}
diff --git a/ctr-std/src/sys/unix/args.rs b/ctr-std/src/sys/unix/args.rs
index 84d0c85..e1c7ffc 100644
--- a/ctr-std/src/sys/unix/args.rs
+++ b/ctr-std/src/sys/unix/args.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@@ -8,22 +8,26 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+//! Global initialization and retrieval of command line arguments.
+//!
+//! On some platforms these are stored during runtime startup,
+//! and on some they are retrieved from the system on demand.
+
+#![allow(dead_code)] // runtime init functions not used during testing
+
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
-}
+/// One-time global initialization.
+pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) }
-pub unsafe fn cleanup() {
-}
+/// One-time global cleanup.
+pub unsafe fn cleanup() { imp::cleanup() }
+/// Returns the command line arguments
pub fn args() -> Args {
- return Args {
- iter: Vec::new().into_iter(),
- _dont_send_or_sync_me: PhantomData,
- }
+ imp::args()
}
pub struct Args {
@@ -39,22 +43,178 @@ impl Args {
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()
- }
+ 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()
- }
+ fn len(&self) -> usize { self.iter.len() }
}
impl DoubleEndedIterator for Args {
- fn next_back(&mut self) -> Option<OsString> {
- self.iter.next_back()
+ fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() }
+}
+
+#[cfg(any(target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris",
+ target_os = "emscripten",
+ target_os = "haiku",
+ target_os = "l4re",
+ target_os = "fuchsia"))]
+mod imp {
+ use os::unix::prelude::*;
+ use ptr;
+ use ffi::{CStr, OsString};
+ use marker::PhantomData;
+ use libc;
+ use super::Args;
+
+ use sys_common::mutex::Mutex;
+
+ static mut ARGC: isize = 0;
+ static mut ARGV: *const *const u8 = ptr::null();
+ static LOCK: Mutex = Mutex::new();
+
+ pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ LOCK.lock();
+ ARGC = argc;
+ ARGV = argv;
+ LOCK.unlock();
+ }
+
+ pub unsafe fn cleanup() {
+ LOCK.lock();
+ ARGC = 0;
+ ARGV = ptr::null();
+ LOCK.unlock();
+ }
+
+ pub fn args() -> Args {
+ Args {
+ iter: clone().into_iter(),
+ _dont_send_or_sync_me: PhantomData
+ }
+ }
+
+ fn clone() -> Vec<OsString> {
+ unsafe {
+ LOCK.lock();
+ let ret = (0..ARGC).map(|i| {
+ let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char);
+ OsStringExt::from_vec(cstr.to_bytes().to_vec())
+ }).collect();
+ LOCK.unlock();
+ return ret
+ }
+ }
+}
+
+#[cfg(any(target_os = "macos",
+ target_os = "ios"))]
+mod imp {
+ use ffi::CStr;
+ use marker::PhantomData;
+ use libc;
+ use super::Args;
+
+ pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
+ }
+
+ pub fn cleanup() {
+ }
+
+ #[cfg(target_os = "macos")]
+ pub fn args() -> Args {
+ use os::unix::prelude::*;
+ extern {
+ // These functions are in crt_externs.h.
+ fn _NSGetArgc() -> *mut libc::c_int;
+ fn _NSGetArgv() -> *mut *mut *mut libc::c_char;
+ }
+
+ let vec = unsafe {
+ let (argc, argv) = (*_NSGetArgc() as isize,
+ *_NSGetArgv() as *const *const libc::c_char);
+ (0.. argc as isize).map(|i| {
+ let bytes = CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec();
+ OsStringExt::from_vec(bytes)
+ }).collect::<Vec<_>>()
+ };
+ Args {
+ iter: vec.into_iter(),
+ _dont_send_or_sync_me: PhantomData,
+ }
+ }
+
+ // As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
+ // and use underscores in their names - they're most probably
+ // are considered private and therefore should be avoided
+ // Here is another way to get arguments using Objective C
+ // runtime
+ //
+ // In general it looks like:
+ // res = Vec::new()
+ // let args = [[NSProcessInfo processInfo] arguments]
+ // for i in (0..[args count])
+ // res.push([args objectAtIndex:i])
+ // res
+ #[cfg(target_os = "ios")]
+ pub fn args() -> Args {
+ use ffi::OsString;
+ use mem;
+ use str;
+
+ extern {
+ fn sel_registerName(name: *const libc::c_uchar) -> Sel;
+ fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
+ }
+
+ #[cfg(target_arch="aarch64")]
+ extern {
+ fn objc_msgSend(obj: NsId, sel: Sel) -> NsId;
+ #[link_name="objc_msgSend"]
+ fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId;
+ }
+
+ #[cfg(not(target_arch="aarch64"))]
+ extern {
+ fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
+ #[link_name="objc_msgSend"]
+ fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId;
+ }
+
+ type Sel = *const libc::c_void;
+ type NsId = *const libc::c_void;
+
+ let mut res = Vec::new();
+
+ unsafe {
+ let process_info_sel = sel_registerName("processInfo\0".as_ptr());
+ let arguments_sel = sel_registerName("arguments\0".as_ptr());
+ let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
+ let count_sel = sel_registerName("count\0".as_ptr());
+ let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());
+
+ let klass = objc_getClass("NSProcessInfo\0".as_ptr());
+ let info = objc_msgSend(klass, process_info_sel);
+ let args = objc_msgSend(info, arguments_sel);
+
+ let cnt: usize = mem::transmute(objc_msgSend(args, count_sel));
+ for i in 0..cnt {
+ let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong);
+ let utf_c_str: *const libc::c_char =
+ mem::transmute(objc_msgSend(tmp, utf8_sel));
+ let bytes = CStr::from_ptr(utf_c_str).to_bytes();
+ res.push(OsString::from(str::from_utf8(bytes).unwrap()))
+ }
+ }
+
+ Args { iter: res.into_iter(), _dont_send_or_sync_me: PhantomData }
}
}
diff --git a/ctr-std/src/sys/unix/backtrace.rs b/ctr-std/src/sys/unix/backtrace.rs
deleted file mode 100644
index 9a8c48f..0000000
--- a/ctr-std/src/sys/unix/backtrace.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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/backtrace/mod.rs b/ctr-std/src/sys/unix/backtrace/mod.rs
new file mode 100644
index 0000000..b5bf20c
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/mod.rs
@@ -0,0 +1,119 @@
+// 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.
+
+/// Backtrace support built on libgcc with some extra OS-specific support
+///
+/// Some methods of getting a backtrace:
+///
+/// * The backtrace() functions on unix. It turns out this doesn't work very
+/// well for green threads on macOS, and the address to symbol portion of it
+/// suffers problems that are described below.
+///
+/// * Using libunwind. This is more difficult than it sounds because libunwind
+/// isn't installed everywhere by default. It's also a bit of a hefty library,
+/// so possibly not the best option. When testing, libunwind was excellent at
+/// getting both accurate backtraces and accurate symbols across platforms.
+/// This route was not chosen in favor of the next option, however.
+///
+/// * We're already using libgcc_s for exceptions in rust (triggering thread
+/// unwinding and running destructors on the stack), and it turns out that it
+/// conveniently comes with a function that also gives us a backtrace. All of
+/// these functions look like _Unwind_*, but it's not quite the full
+/// repertoire of the libunwind API. Due to it already being in use, this was
+/// the chosen route of getting a backtrace.
+///
+/// After choosing libgcc_s for backtraces, the sad part is that it will only
+/// give us a stack trace of instruction pointers. Thankfully these instruction
+/// pointers are accurate (they work for green and native threads), but it's
+/// then up to us again to figure out how to translate these addresses to
+/// symbols. As with before, we have a few options. Before, that, a little bit
+/// of an interlude about symbols. This is my very limited knowledge about
+/// symbol tables, and this information is likely slightly wrong, but the
+/// general idea should be correct.
+///
+/// When talking about symbols, it's helpful to know a few things about where
+/// symbols are located. Some symbols are located in the dynamic symbol table
+/// of the executable which in theory means that they're available for dynamic
+/// linking and lookup. Other symbols end up only in the local symbol table of
+/// the file. This loosely corresponds to pub and priv functions in Rust.
+///
+/// Armed with this knowledge, we know that our solution for address to symbol
+/// translation will need to consult both the local and dynamic symbol tables.
+/// With that in mind, here's our options of translating an address to
+/// a symbol.
+///
+/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
+/// behind the scenes to translate, and this is why backtrace() was not used.
+/// Conveniently, this method works fantastically on macOS. It appears dladdr()
+/// uses magic to consult the local symbol table, or we're putting everything
+/// in the dynamic symbol table anyway. Regardless, for macOS, this is the
+/// method used for translation. It's provided by the system and easy to do.o
+///
+/// Sadly, all other systems have a dladdr() implementation that does not
+/// consult the local symbol table. This means that most functions are blank
+/// because they don't have symbols. This means that we need another solution.
+///
+/// * Use unw_get_proc_name(). This is part of the libunwind api (not the
+/// libgcc_s version of the libunwind api), but involves taking a dependency
+/// to libunwind. We may pursue this route in the future if we bundle
+/// libunwind, but libunwind was unwieldy enough that it was not chosen at
+/// this time to provide this functionality.
+///
+/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
+/// semi-reasonable solution. The stdlib already knows how to spawn processes,
+/// so in theory it could invoke readelf, parse the output, and consult the
+/// local/dynamic symbol tables from there. This ended up not getting chosen
+/// due to the craziness of the idea plus the advent of the next option.
+///
+/// * Use `libbacktrace`. It turns out that this is a small library bundled in
+/// the gcc repository which provides backtrace and symbol translation
+/// functionality. All we really need from it is the backtrace functionality,
+/// and we only really need this on everything that's not macOS, so this is the
+/// chosen route for now.
+///
+/// In summary, the current situation uses libgcc_s to get a trace of stack
+/// pointers, and we use dladdr() or libbacktrace to translate these addresses
+/// to symbols. This is a bit of a hokey implementation as-is, but it works for
+/// all unix platforms we support right now, so it at least gets the job done.
+
+pub use self::tracing::unwind_backtrace;
+pub use self::printing::{foreach_symbol_fileline, resolve_symname};
+
+// tracing impls:
+mod tracing;
+// symbol resolvers:
+mod printing;
+
+#[cfg(not(target_os = "emscripten"))]
+pub mod gnu {
+ use io;
+ use fs;
+ use libc::c_char;
+
+ #[cfg(not(any(target_os = "macos", target_os = "ios")))]
+ pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
+ Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
+ }
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
+ use env;
+ use os::unix::ffi::OsStrExt;
+
+ let filename = env::current_exe()?;
+ let file = fs::File::open(&filename)?;
+ let mut filename_cstr: Vec<_> = filename.as_os_str().as_bytes().iter()
+ .map(|&x| x as c_char).collect();
+ filename_cstr.push(0); // Null terminate
+ Ok((filename_cstr, file))
+ }
+}
+
+pub struct BacktraceContext;
diff --git a/ctr-std/src/sys/unix/backtrace/printing/dladdr.rs b/ctr-std/src/sys/unix/backtrace/printing/dladdr.rs
new file mode 100644
index 0000000..bc56fd6
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/printing/dladdr.rs
@@ -0,0 +1,45 @@
+// Copyright 2014-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 intrinsics;
+use ffi::CStr;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+pub fn resolve_symname<F>(frame: Frame,
+ callback: F,
+ _: &BacktraceContext) -> io::Result<()>
+ where F: FnOnce(Option<&str>) -> io::Result<()>
+{
+ unsafe {
+ let mut info: Dl_info = intrinsics::init();
+ let symname = if dladdr(frame.exact_position as *mut _, &mut info) == 0 ||
+ info.dli_sname.is_null() {
+ None
+ } else {
+ CStr::from_ptr(info.dli_sname).to_str().ok()
+ };
+ callback(symname)
+ }
+}
+
+#[repr(C)]
+struct Dl_info {
+ dli_fname: *const libc::c_char,
+ dli_fbase: *mut libc::c_void,
+ dli_sname: *const libc::c_char,
+ dli_saddr: *mut libc::c_void,
+}
+
+extern {
+ fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int;
+}
diff --git a/ctr-std/src/sys/unix/backtrace/printing/mod.rs b/ctr-std/src/sys/unix/backtrace/printing/mod.rs
new file mode 100644
index 0000000..caa6071
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/printing/mod.rs
@@ -0,0 +1,43 @@
+// Copyright 2014-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.
+
+mod dladdr;
+
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+use io;
+
+#[cfg(target_os = "emscripten")]
+pub use self::dladdr::resolve_symname;
+
+#[cfg(target_os = "emscripten")]
+pub fn foreach_symbol_fileline<F>(_: Frame, _: F, _: &BacktraceContext) -> io::Result<bool>
+where
+ F: FnMut(&[u8], u32) -> io::Result<()>
+{
+ Ok(false)
+}
+
+#[cfg(not(target_os = "emscripten"))]
+pub use sys_common::gnu::libbacktrace::foreach_symbol_fileline;
+
+#[cfg(not(target_os = "emscripten"))]
+pub fn resolve_symname<F>(frame: Frame, callback: F, bc: &BacktraceContext) -> io::Result<()>
+where
+ F: FnOnce(Option<&str>) -> io::Result<()>
+{
+ ::sys_common::gnu::libbacktrace::resolve_symname(frame, |symname| {
+ if symname.is_some() {
+ callback(symname)
+ } else {
+ dladdr::resolve_symname(frame, callback, bc)
+ }
+ }, bc)
+}
diff --git a/ctr-std/src/sys/unix/backtrace/tracing/backtrace_fn.rs b/ctr-std/src/sys/unix/backtrace/tracing/backtrace_fn.rs
new file mode 100644
index 0000000..6293eeb
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/tracing/backtrace_fn.rs
@@ -0,0 +1,49 @@
+// Copyright 2014-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.
+
+/// As always - iOS on arm uses SjLj exceptions and
+/// _Unwind_Backtrace is even not available there. Still,
+/// backtraces could be extracted using a backtrace function,
+/// which thanks god is public
+///
+/// As mentioned in a huge comment block in `super::super`, backtrace
+/// doesn't play well with green threads, so while it is extremely nice and
+/// simple to use it should be used only on iOS devices as the only viable
+/// option.
+
+use io;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+#[inline(never)] // if we know this is a function call, we can skip it when
+ // tracing
+pub fn unwind_backtrace(frames: &mut [Frame])
+ -> io::Result<(usize, BacktraceContext)>
+{
+ const FRAME_LEN: usize = 100;
+ assert!(FRAME_LEN >= frames.len());
+ let mut raw_frames = [::ptr::null_mut(); FRAME_LEN];
+ let nb_frames = unsafe {
+ backtrace(raw_frames.as_mut_ptr(), raw_frames.len() as libc::c_int)
+ } as usize;
+ for (from, to) in raw_frames.iter().zip(frames.iter_mut()).take(nb_frames) {
+ *to = Frame {
+ exact_position: *from as *mut u8,
+ symbol_addr: *from as *mut u8,
+ inline_context: 0,
+ };
+ }
+ Ok((nb_frames as usize, BacktraceContext))
+}
+
+extern {
+ fn backtrace(buf: *mut *mut libc::c_void, sz: libc::c_int) -> libc::c_int;
+}
diff --git a/ctr-std/src/sys/unix/backtrace/tracing/gcc_s.rs b/ctr-std/src/sys/unix/backtrace/tracing/gcc_s.rs
new file mode 100644
index 0000000..1b92fc0
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/tracing/gcc_s.rs
@@ -0,0 +1,107 @@
+// Copyright 2014-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 error::Error;
+use io;
+use libc;
+use sys::backtrace::BacktraceContext;
+use sys_common::backtrace::Frame;
+
+use unwind as uw;
+
+struct Context<'a> {
+ idx: usize,
+ frames: &'a mut [Frame],
+}
+
+#[derive(Debug)]
+struct UnwindError(uw::_Unwind_Reason_Code);
+
+impl Error for UnwindError {
+ fn description(&self) -> &'static str {
+ "unexpected return value while unwinding"
+ }
+}
+
+impl ::fmt::Display for UnwindError {
+ fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
+ write!(f, "{}: {:?}", self.description(), self.0)
+ }
+}
+
+#[inline(never)] // if we know this is a function call, we can skip it when
+ // tracing
+pub fn unwind_backtrace(frames: &mut [Frame])
+ -> io::Result<(usize, BacktraceContext)>
+{
+ let mut cx = Context {
+ idx: 0,
+ frames,
+ };
+ let result_unwind = unsafe {
+ uw::_Unwind_Backtrace(trace_fn,
+ &mut cx as *mut Context
+ as *mut libc::c_void)
+ };
+ // See libunwind:src/unwind/Backtrace.c for the return values.
+ // No, there is no doc.
+ match result_unwind {
+ // These return codes seem to be benign and need to be ignored for backtraces
+ // to show up properly on all tested platforms.
+ uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
+ Ok((cx.idx, BacktraceContext))
+ }
+ _ => {
+ Err(io::Error::new(io::ErrorKind::Other,
+ UnwindError(result_unwind)))
+ }
+ }
+}
+
+extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
+ arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
+ let cx = unsafe { &mut *(arg as *mut Context) };
+ let mut ip_before_insn = 0;
+ let mut ip = unsafe {
+ uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
+ };
+ if !ip.is_null() && ip_before_insn == 0 {
+ // this is a non-signaling frame, so `ip` refers to the address
+ // after the calling instruction. account for that.
+ ip = (ip as usize - 1) as *mut _;
+ }
+
+ // dladdr() on osx gets whiny when we use FindEnclosingFunction, and
+ // it appears to work fine without it, so we only use
+ // FindEnclosingFunction on non-osx platforms. In doing so, we get a
+ // slightly more accurate stack trace in the process.
+ //
+ // This is often because panic involves the last instruction of a
+ // function being "call std::rt::begin_unwind", with no ret
+ // instructions after it. This means that the return instruction
+ // pointer points *outside* of the calling function, and by
+ // unwinding it we go back to the original function.
+ let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
+ ip
+ } else {
+ unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
+ };
+
+ if cx.idx < cx.frames.len() {
+ cx.frames[cx.idx] = Frame {
+ symbol_addr: symaddr as *mut u8,
+ exact_position: ip as *mut u8,
+ inline_context: 0,
+ };
+ cx.idx += 1;
+ }
+
+ uw::_URC_NO_REASON
+}
diff --git a/ctr-std/src/sys/unix/backtrace/tracing/mod.rs b/ctr-std/src/sys/unix/backtrace/tracing/mod.rs
new file mode 100644
index 0000000..c9c8e26
--- /dev/null
+++ b/ctr-std/src/sys/unix/backtrace/tracing/mod.rs
@@ -0,0 +1,18 @@
+// 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.
+
+pub use self::imp::*;
+
+#[cfg(not(all(target_os = "ios", target_arch = "arm")))]
+#[path = "gcc_s.rs"]
+mod imp;
+#[cfg(all(target_os = "ios", target_arch = "arm"))]
+#[path = "backtrace_fn.rs"]
+mod imp;
diff --git a/ctr-std/src/sys/unix/condvar.rs b/ctr-std/src/sys/unix/condvar.rs
index bfff16b..4f878d8 100644
--- a/ctr-std/src/sys/unix/condvar.rs
+++ b/ctr-std/src/sys/unix/condvar.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,132 +8,180 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-// *Implementation adapted from `/sys/redox/condvar.rs`
-
use cell::UnsafeCell;
-use intrinsics::atomic_cxchg;
-use ptr;
-use time::Duration;
-
+use libc;
use sys::mutex::{self, Mutex};
+use time::Duration;
-pub struct Condvar {
- lock: UnsafeCell<*mut ::libctru::LightLock>,
-}
+pub struct Condvar { inner: UnsafeCell<libc::pthread_cond_t> }
unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}
+const TIMESPEC_MAX: libc::timespec = libc::timespec {
+ tv_sec: <libc::time_t>::max_value(),
+ tv_nsec: 1_000_000_000 - 1,
+};
+
+fn saturating_cast_to_time_t(value: u64) -> libc::time_t {
+ if value > <libc::time_t>::max_value() as u64 {
+ <libc::time_t>::max_value()
+ } else {
+ value as libc::time_t
+ }
+}
+
impl Condvar {
pub const fn new() -> Condvar {
- Condvar {
- lock: UnsafeCell::new(ptr::null_mut()),
- }
+ // Might be moved and address is changing it is better to avoid
+ // initialization of potentially opaque OS data before it landed
+ Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) }
}
- #[inline]
- pub unsafe fn init(&self) {
- *self.lock.get() = ptr::null_mut();
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "l4re",
+ target_os = "android"))]
+ pub unsafe fn init(&mut self) {}
+
+ #[cfg(not(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "l4re",
+ target_os = "android")))]
+ pub unsafe fn init(&mut self) {
+ use mem;
+ let mut attr: libc::pthread_condattr_t = mem::uninitialized();
+ let r = libc::pthread_condattr_init(&mut attr);
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_setclock(&mut attr, libc::CLOCK_MONOTONIC);
+ assert_eq!(r, 0);
+ let r = libc::pthread_cond_init(self.inner.get(), &attr);
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_destroy(&mut attr);
+ assert_eq!(r, 0);
}
#[inline]
- pub fn notify_one(&self) {
- unsafe {
- let arbiter = ::libctru::__sync_get_arbiter();
-
- ::libctru::svcArbitrateAddress(arbiter,
- *self.lock.get() as u32,
- ::libctru::ARBITRATION_SIGNAL,
- 1,
- 0);
- }
+ pub unsafe fn notify_one(&self) {
+ let r = libc::pthread_cond_signal(self.inner.get());
+ debug_assert_eq!(r, 0);
}
#[inline]
- pub fn notify_all(&self) {
- unsafe {
- let lock = self.lock.get();
-
- if *lock == ptr::null_mut() {
- return;
- }
-
- let arbiter = ::libctru::__sync_get_arbiter();
-
- ::libctru::svcArbitrateAddress(arbiter,
- *self.lock.get() as u32,
- ::libctru::ARBITRATION_SIGNAL,
- -1,
- 0);
- }
+ pub unsafe fn notify_all(&self) {
+ let r = libc::pthread_cond_broadcast(self.inner.get());
+ debug_assert_eq!(r, 0);
}
#[inline]
- pub fn wait(&self, mutex: &Mutex) {
- unsafe {
- let lock = self.lock.get();
-
- if *lock != mutex::raw(mutex) {
- if *lock != ptr::null_mut() {
- panic!("Condvar used with more than one Mutex");
- }
-
- atomic_cxchg(lock as *mut usize, 0, mutex::raw(mutex) as usize);
- }
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ let r = libc::pthread_cond_wait(self.inner.get(), mutex::raw(mutex));
+ debug_assert_eq!(r, 0);
+ }
- mutex.unlock();
+ // This implementation is used on systems that support pthread_condattr_setclock
+ // where we configure condition variable to use monotonic clock (instead of
+ // default system clock). This approach avoids all problems that result
+ // from changes made to the system time.
+ #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ use mem;
+
+ let mut now: libc::timespec = mem::zeroed();
+ let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now);
+ assert_eq!(r, 0);
+
+ // Nanosecond calculations can't overflow because both values are below 1e9.
+ let nsec = dur.subsec_nanos() + now.tv_nsec as u32;
+
+ let sec = saturating_cast_to_time_t(dur.as_secs())
+ .checked_add((nsec / 1_000_000_000) as libc::time_t)
+ .and_then(|s| s.checked_add(now.tv_sec));
+ let nsec = nsec % 1_000_000_000;
+
+ let timeout = sec.map(|s| {
+ libc::timespec { tv_sec: s, tv_nsec: nsec as _}
+ }).unwrap_or(TIMESPEC_MAX);
+
+ let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
+ &timeout);
+ assert!(r == libc::ETIMEDOUT || r == 0);
+ r == 0
+ }
- let arbiter = ::libctru::__sync_get_arbiter();
- ::libctru::svcArbitrateAddress(arbiter,
- *self.lock.get() as u32,
- ::libctru::ARBITRATION_WAIT_IF_LESS_THAN,
- 2,
- 0);
+ // This implementation is modeled after libcxx's condition_variable
+ // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
+ // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool {
+ use ptr;
+ use time::Instant;
- mutex.lock();
+ // 1000 years
+ let max_dur = Duration::from_secs(1000 * 365 * 86400);
+
+ if dur > max_dur {
+ // OSX implementation of `pthread_cond_timedwait` is buggy
+ // with super long durations. When duration is greater than
+ // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
+ // in macOS Sierra return error 316.
+ //
+ // This program demonstrates the issue:
+ // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
+ //
+ // To work around this issue, and possible bugs of other OSes, timeout
+ // is clamped to 1000 years, which is allowable per the API of `wait_timeout`
+ // because of spurious wakeups.
+
+ dur = max_dur;
}
+
+ // First, figure out what time it currently is, in both system and
+ // stable time. pthread_cond_timedwait uses system time, but we want to
+ // report timeout based on stable time.
+ let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 };
+ let stable_now = Instant::now();
+ let r = libc::gettimeofday(&mut sys_now, ptr::null_mut());
+ debug_assert_eq!(r, 0);
+
+ let nsec = dur.subsec_nanos() as libc::c_long +
+ (sys_now.tv_usec * 1000) as libc::c_long;
+ let extra = (nsec / 1_000_000_000) as libc::time_t;
+ let nsec = nsec % 1_000_000_000;
+ let seconds = saturating_cast_to_time_t(dur.as_secs());
+
+ let timeout = sys_now.tv_sec.checked_add(extra).and_then(|s| {
+ s.checked_add(seconds)
+ }).map(|s| {
+ libc::timespec { tv_sec: s, tv_nsec: nsec }
+ }).unwrap_or(TIMESPEC_MAX);
+
+ // And wait!
+ let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
+ &timeout);
+ debug_assert!(r == libc::ETIMEDOUT || r == 0);
+
+ // ETIMEDOUT is not a totally reliable method of determining timeout due
+ // to clock shifts, so do the check ourselves
+ stable_now.elapsed() < dur
}
#[inline]
- pub fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
- use time::Instant;
-
- unsafe {
- let lock = self.lock.get();
-
- if *lock != mutex::raw(mutex) {
- if *lock != ptr::null_mut() {
- panic!("Condvar used with more than one Mutex");
- }
-
- atomic_cxchg(lock as *mut usize, 0, mutex::raw(mutex) as usize);
- }
-
- let now = Instant::now();
-
- let nanos = dur.as_secs()
- .saturating_mul(1_000_000_000)
- .saturating_add(dur.subsec_nanos() as u64);
-
- mutex.unlock();
-
- let arbiter = ::libctru::__sync_get_arbiter();
-
- ::libctru::svcArbitrateAddress(arbiter,
- *self.lock.get() as u32,
- ::libctru::ARBITRATION_WAIT_IF_LESS_THAN_TIMEOUT,
- 2,
- nanos as i64);
-
- mutex.lock();
-
- now.elapsed() < dur
- }
+ #[cfg(not(target_os = "dragonfly"))]
+ pub unsafe fn destroy(&self) {
+ let r = libc::pthread_cond_destroy(self.inner.get());
+ debug_assert_eq!(r, 0);
}
#[inline]
+ #[cfg(target_os = "dragonfly")]
pub unsafe fn destroy(&self) {
- *self.lock.get() = ptr::null_mut();
+ let r = libc::pthread_cond_destroy(self.inner.get());
+ // On DragonFly pthread_cond_destroy() returns EINVAL if called on
+ // a condvar that was just initialized with
+ // libc::PTHREAD_COND_INITIALIZER. Once it is used or
+ // pthread_cond_init() is called, this behaviour no longer occurs.
+ debug_assert!(r == 0 || r == libc::EINVAL);
}
}
diff --git a/ctr-std/src/sys/unix/env.rs b/ctr-std/src/sys/unix/env.rs
index 393bdfe..00cf7ec 100644
--- a/ctr-std/src/sys/unix/env.rs
+++ b/ctr-std/src/sys/unix/env.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@@ -8,12 +8,167 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+#[cfg(target_os = "linux")]
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";
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "linux";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "macos")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "macos";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".dylib";
+ pub const DLL_EXTENSION: &'static str = "dylib";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "ios")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "ios";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".dylib";
+ pub const DLL_EXTENSION: &'static str = "dylib";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "freebsd")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "freebsd";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "dragonfly")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "dragonfly";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "bitrig")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "bitrig";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "netbsd")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "netbsd";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "openbsd")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "openbsd";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "android")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "android";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "solaris")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "solaris";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "haiku")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "haiku";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "emscripten";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = ".js";
+ pub const EXE_EXTENSION: &'static str = "js";
+}
+
+#[cfg(all(target_os = "emscripten", target_arch = "wasm32"))]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "emscripten";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = ".js";
+ pub const EXE_EXTENSION: &'static str = "js";
+}
+
+#[cfg(target_os = "fuchsia")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "fuchsia";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
+}
+
+#[cfg(target_os = "l4re")]
+pub mod os {
+ pub const FAMILY: &'static str = "unix";
+ pub const OS: &'static str = "l4re";
+ pub const DLL_PREFIX: &'static str = "lib";
+ pub const DLL_SUFFIX: &'static str = ".so";
+ pub const DLL_EXTENSION: &'static str = "so";
+ pub const EXE_SUFFIX: &'static str = "";
+ pub const EXE_EXTENSION: &'static str = "";
}
diff --git a/ctr-std/src/sys/unix/ext/ffi.rs b/ctr-std/src/sys/unix/ext/ffi.rs
index fb9984c..8347145 100644
--- a/ctr-std/src/sys/unix/ext/ffi.rs
+++ b/ctr-std/src/sys/unix/ext/ffi.rs
@@ -17,7 +17,9 @@ use mem;
use sys::os_str::Buf;
use sys_common::{FromInner, IntoInner, AsInner};
-/// Unix-specific extensions to `OsString`.
+/// Unix-specific extensions to [`OsString`].
+///
+/// [`OsString`]: ../../../../std/ffi/struct.OsString.html
#[stable(feature = "rust1", since = "1.0.0")]
pub trait OsStringExt {
/// Creates an [`OsString`] from a byte vector.
@@ -66,7 +68,9 @@ impl OsStringExt for OsString {
}
}
-/// Unix-specific extensions to `OsStr`.
+/// Unix-specific extensions to [`OsStr`].
+///
+/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html
#[stable(feature = "rust1", since = "1.0.0")]
pub trait OsStrExt {
#[stable(feature = "rust1", since = "1.0.0")]
diff --git a/ctr-std/src/sys/unix/ext/fs.rs b/ctr-std/src/sys/unix/ext/fs.rs
new file mode 100644
index 0000000..4e98101
--- /dev/null
+++ b/ctr-std/src/sys/unix/ext/fs.rs
@@ -0,0 +1,736 @@
+// 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 primitives in the `std::fs` module.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use fs::{self, Permissions, OpenOptions};
+use io;
+use libc;
+use path::Path;
+use sys;
+use sys_common::{FromInner, AsInner, AsInnerMut};
+use sys::platform::fs::MetadataExt as UnixMetadataExt;
+
+/// Unix-specific extensions to [`File`].
+///
+/// [`File`]: ../../../../std/fs/struct.File.html
+#[stable(feature = "file_offset", since = "1.15.0")]
+pub trait FileExt {
+ /// Reads a number of bytes starting from a given offset.
+ ///
+ /// Returns the number of bytes read.
+ ///
+ /// The offset is relative to the start of the file and thus independent
+ /// from the current cursor.
+ ///
+ /// The current file cursor is not affected by this function.
+ ///
+ /// Note that similar to [`File::read`], it is not an error to return with a
+ /// short read.
+ ///
+ /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::fs::File;
+ /// use std::os::unix::prelude::FileExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let mut buf = [0u8; 8];
+ /// let file = File::open("foo.txt")?;
+ ///
+ /// // We now read 8 bytes from the offset 10.
+ /// let num_bytes_read = file.read_at(&mut buf, 10)?;
+ /// println!("read {} bytes: {:?}", num_bytes_read, buf);
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_offset", since = "1.15.0")]
+ fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
+
+ /// Writes a number of bytes starting from a given offset.
+ ///
+ /// Returns the number of bytes written.
+ ///
+ /// The offset is relative to the start of the file and thus independent
+ /// from the current cursor.
+ ///
+ /// The current file cursor is not affected by this function.
+ ///
+ /// When writing beyond the end of the file, the file is appropriately
+ /// extended and the intermediate bytes are initialized with the value 0.
+ ///
+ /// Note that similar to [`File::write`], it is not an error to return a
+ /// short write.
+ ///
+ /// [`File::write`]: ../../../../std/fs/struct.File.html#write.v
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::io;
+ /// use std::os::unix::prelude::FileExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let file = File::open("foo.txt")?;
+ ///
+ /// // We now write at the offset 10.
+ /// file.write_at(b"sushi", 10)?;
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_offset", since = "1.15.0")]
+ fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
+}
+
+#[stable(feature = "file_offset", since = "1.15.0")]
+impl FileExt for fs::File {
+ fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ self.as_inner().read_at(buf, offset)
+ }
+ fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ self.as_inner().write_at(buf, offset)
+ }
+}
+
+/// Unix-specific extensions to [`fs::Permissions`].
+///
+/// [`fs::Permissions`]: ../../../../std/fs/struct.Permissions.html
+#[stable(feature = "fs_ext", since = "1.1.0")]
+pub trait PermissionsExt {
+ /// Returns the underlying raw `st_mode` bits that contain the standard
+ /// Unix permissions for this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::os::unix::fs::PermissionsExt;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let f = File::create("foo.txt")?;
+ /// let metadata = f.metadata()?;
+ /// let permissions = metadata.permissions();
+ ///
+ /// println!("permissions: {}", permissions.mode());
+ /// Ok(()) }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn mode(&self) -> u32;
+
+ /// Sets the underlying raw bits for this set of permissions.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::os::unix::fs::PermissionsExt;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let f = File::create("foo.txt")?;
+ /// let metadata = f.metadata()?;
+ /// let mut permissions = metadata.permissions();
+ ///
+ /// permissions.set_mode(0o644); // Read/write for owner and read for others.
+ /// assert_eq!(permissions.mode(), 0o644);
+ /// Ok(()) }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn set_mode(&mut self, mode: u32);
+
+ /// Creates a new instance of `Permissions` from the given set of Unix
+ /// permission bits.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::fs::Permissions;
+ /// use std::os::unix::fs::PermissionsExt;
+ ///
+ /// // Read/write for owner and read for others.
+ /// let permissions = Permissions::from_mode(0o644);
+ /// assert_eq!(permissions.mode(), 0o644);
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn from_mode(mode: u32) -> Self;
+}
+
+#[stable(feature = "fs_ext", since = "1.1.0")]
+impl PermissionsExt for Permissions {
+ fn mode(&self) -> u32 {
+ self.as_inner().mode()
+ }
+
+ fn set_mode(&mut self, mode: u32) {
+ *self = Permissions::from_inner(FromInner::from_inner(mode));
+ }
+
+ fn from_mode(mode: u32) -> Permissions {
+ Permissions::from_inner(FromInner::from_inner(mode))
+ }
+}
+
+/// Unix-specific extensions to [`fs::OpenOptions`].
+///
+/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html
+#[stable(feature = "fs_ext", since = "1.1.0")]
+pub trait OpenOptionsExt {
+ /// Sets the mode bits that a new file will be created with.
+ ///
+ /// If a new file is created as part of a `File::open_opts` call then this
+ /// specified `mode` will be used as the permission bits for the new file.
+ /// If no `mode` is set, the default of `0o666` will be used.
+ /// The operating system masks out bits with the systems `umask`, to produce
+ /// the final permissions.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::OpenOptions;
+ /// use std::os::unix::fs::OpenOptionsExt;
+ ///
+ /// # fn main() {
+ /// let mut options = OpenOptions::new();
+ /// options.mode(0o644); // Give read/write for owner and read for others.
+ /// let file = options.open("foo.txt");
+ /// # }
+ /// ```
+ #[stable(feature = "fs_ext", since = "1.1.0")]
+ fn mode(&mut self, mode: u32) -> &mut Self;
+
+ /// Pass custom flags to the `flags` argument of `open`.
+ ///
+ /// The bits that define the access mode are masked out with `O_ACCMODE`, to
+ /// ensure they do not interfere with the access mode set by Rusts options.
+ ///
+ /// Custom flags can only set flags, not remove flags set by Rusts options.
+ /// This options overwrites any previously set custom flags.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// # #![feature(libc)]
+ /// extern crate libc;
+ /// use std::fs::OpenOptions;
+ /// use std::os::unix::fs::OpenOptionsExt;
+ ///
+ /// # fn main() {
+ /// let mut options = OpenOptions::new();
+ /// options.write(true);
+ /// if cfg!(unix) {
+ /// options.custom_flags(libc::O_NOFOLLOW);
+ /// }
+ /// let file = options.open("foo.txt");
+ /// # }
+ /// ```
+ #[stable(feature = "open_options_ext", since = "1.10.0")]
+ fn custom_flags(&mut self, flags: i32) -> &mut Self;
+}
+
+#[stable(feature = "fs_ext", since = "1.1.0")]
+impl OpenOptionsExt for OpenOptions {
+ fn mode(&mut self, mode: u32) -> &mut OpenOptions {
+ self.as_inner_mut().mode(mode); self
+ }
+
+ fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
+ self.as_inner_mut().custom_flags(flags); self
+ }
+}
+
+/// Unix-specific extensions to [`fs::Metadata`].
+///
+/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+pub trait MetadataExt {
+ /// Returns the ID of the device containing the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let dev_id = meta.dev();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn dev(&self) -> u64;
+ /// Returns the inode number.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let inode = meta.ino();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ino(&self) -> u64;
+ /// Returns the rights applied to this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let mode = meta.mode();
+ /// let user_has_write_access = mode & 0o200;
+ /// let user_has_read_write_access = mode & 0o600;
+ /// let group_has_read_access = mode & 0o040;
+ /// let others_have_exec_access = mode & 0o001;
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mode(&self) -> u32;
+ /// Returns the number of hard links pointing to this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nb_hard_links = meta.nlink();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn nlink(&self) -> u64;
+ /// Returns the user ID of the owner of this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let user_id = meta.uid();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn uid(&self) -> u32;
+ /// Returns the group ID of the owner of this file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let group_id = meta.gid();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn gid(&self) -> u32;
+ /// Returns the device ID of this file (if it is a special one).
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let device_id = meta.rdev();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn rdev(&self) -> u64;
+ /// Returns the total size of this file in bytes.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let file_size = meta.size();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn size(&self) -> u64;
+ /// Returns the time of the last access to the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let last_access_time = meta.atime();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn atime(&self) -> i64;
+ /// Returns the time of the last access to the file in nanoseconds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nano_last_access_time = meta.atime_nsec();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn atime_nsec(&self) -> i64;
+ /// Returns the time of the last modification of the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let last_modification_time = meta.mtime();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mtime(&self) -> i64;
+ /// Returns the time of the last modification of the file in nanoseconds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nano_last_modification_time = meta.mtime_nsec();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn mtime_nsec(&self) -> i64;
+ /// Returns the time of the last status change of the file.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let last_status_change_time = meta.ctime();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ctime(&self) -> i64;
+ /// Returns the time of the last status change of the file in nanoseconds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let nano_last_status_change_time = meta.ctime_nsec();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn ctime_nsec(&self) -> i64;
+ /// Returns the blocksize for filesystem I/O.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let blocksize = meta.blksize();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn blksize(&self) -> u64;
+ /// Returns the number of blocks allocated to the file, in 512-byte units.
+ ///
+ /// Please note that this may be smaller than `st_size / 512` when the file has holes.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::MetadataExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("some_file")?;
+ /// let blocks = meta.blocks();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "metadata_ext", since = "1.1.0")]
+ fn blocks(&self) -> u64;
+}
+
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+impl MetadataExt for fs::Metadata {
+ fn dev(&self) -> u64 { self.st_dev() }
+ fn ino(&self) -> u64 { self.st_ino() }
+ fn mode(&self) -> u32 { self.st_mode() }
+ fn nlink(&self) -> u64 { self.st_nlink() }
+ fn uid(&self) -> u32 { self.st_uid() }
+ fn gid(&self) -> u32 { self.st_gid() }
+ fn rdev(&self) -> u64 { self.st_rdev() }
+ fn size(&self) -> u64 { self.st_size() }
+ fn atime(&self) -> i64 { self.st_atime() }
+ fn atime_nsec(&self) -> i64 { self.st_atime_nsec() }
+ fn mtime(&self) -> i64 { self.st_mtime() }
+ fn mtime_nsec(&self) -> i64 { self.st_mtime_nsec() }
+ fn ctime(&self) -> i64 { self.st_ctime() }
+ fn ctime_nsec(&self) -> i64 { self.st_ctime_nsec() }
+ fn blksize(&self) -> u64 { self.st_blksize() }
+ fn blocks(&self) -> u64 { self.st_blocks() }
+}
+
+/// Unix-specific extensions for [`FileType`].
+///
+/// Adds support for special Unix file types such as block/character devices,
+/// pipes, and sockets.
+///
+/// [`FileType`]: ../../../../std/fs/struct.FileType.html
+#[stable(feature = "file_type_ext", since = "1.5.0")]
+pub trait FileTypeExt {
+ /// Returns whether this file type is a block device.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("block_device_file")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_block_device());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_block_device(&self) -> bool;
+ /// Returns whether this file type is a char device.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("char_device_file")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_char_device());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_char_device(&self) -> bool;
+ /// Returns whether this file type is a fifo.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("fifo_file")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_fifo());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_fifo(&self) -> bool;
+ /// Returns whether this file type is a socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs;
+ /// use std::os::unix::fs::FileTypeExt;
+ /// use std::io;
+ ///
+ /// fn main() -> io::Result<()> {
+ /// let meta = fs::metadata("unix.socket")?;
+ /// let file_type = meta.file_type();
+ /// assert!(file_type.is_socket());
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "file_type_ext", since = "1.5.0")]
+ fn is_socket(&self) -> bool;
+}
+
+#[stable(feature = "file_type_ext", since = "1.5.0")]
+impl FileTypeExt for fs::FileType {
+ fn is_block_device(&self) -> bool { self.as_inner().is(libc::S_IFBLK) }
+ fn is_char_device(&self) -> bool { self.as_inner().is(libc::S_IFCHR) }
+ fn is_fifo(&self) -> bool { self.as_inner().is(libc::S_IFIFO) }
+ fn is_socket(&self) -> bool { self.as_inner().is(libc::S_IFSOCK) }
+}
+
+/// Unix-specific extension methods for [`fs::DirEntry`].
+///
+/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html
+#[stable(feature = "dir_entry_ext", since = "1.1.0")]
+pub trait DirEntryExt {
+ /// Returns the underlying `d_ino` field in the contained `dirent`
+ /// structure.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::fs;
+ /// use std::os::unix::fs::DirEntryExt;
+ ///
+ /// if let Ok(entries) = fs::read_dir(".") {
+ /// for entry in entries {
+ /// if let Ok(entry) = entry {
+ /// // Here, `entry` is a `DirEntry`.
+ /// println!("{:?}: {}", entry.file_name(), entry.ino());
+ /// }
+ /// }
+ /// }
+ /// ```
+ #[stable(feature = "dir_entry_ext", since = "1.1.0")]
+ fn ino(&self) -> u64;
+}
+
+#[stable(feature = "dir_entry_ext", since = "1.1.0")]
+impl DirEntryExt for fs::DirEntry {
+ fn ino(&self) -> u64 { self.as_inner().ino() }
+}
+
+/// Creates a new symbolic link on the filesystem.
+///
+/// The `dst` path will be a symbolic link pointing to the `src` path.
+///
+/// # Note
+///
+/// On Windows, you must specify whether a symbolic link points to a file
+/// or directory. Use `os::windows::fs::symlink_file` to create a
+/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a
+/// symbolic link to a directory. Additionally, the process must have
+/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a
+/// symbolic link.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::fs;
+///
+/// fn main() -> std::io::Result<()> {
+/// fs::symlink("a.txt", "b.txt")?;
+/// Ok(())
+/// }
+/// ```
+#[stable(feature = "symlink", since = "1.1.0")]
+pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()>
+{
+ sys::fs::symlink(src.as_ref(), dst.as_ref())
+}
+
+/// Unix-specific extensions to [`fs::DirBuilder`].
+///
+/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html
+#[stable(feature = "dir_builder", since = "1.6.0")]
+pub trait DirBuilderExt {
+ /// Sets the mode to create new directories with. This option defaults to
+ /// 0o777.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::DirBuilder;
+ /// use std::os::unix::fs::DirBuilderExt;
+ ///
+ /// let mut builder = DirBuilder::new();
+ /// builder.mode(0o755);
+ /// ```
+ #[stable(feature = "dir_builder", since = "1.6.0")]
+ fn mode(&mut self, mode: u32) -> &mut Self;
+}
+
+#[stable(feature = "dir_builder", since = "1.6.0")]
+impl DirBuilderExt for fs::DirBuilder {
+ fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder {
+ self.as_inner_mut().set_mode(mode);
+ self
+ }
+}
diff --git a/ctr-std/src/sys/unix/ext/mod.rs b/ctr-std/src/sys/unix/ext/mod.rs
index 7b0d175..c221f7c 100644
--- a/ctr-std/src/sys/unix/ext/mod.rs
+++ b/ctr-std/src/sys/unix/ext/mod.rs
@@ -38,11 +38,11 @@
pub mod io;
pub mod ffi;
-//pub mod fs;
-//pub mod process;
+pub mod fs;
+pub mod process;
pub mod raw;
-//pub mod thread;
-//pub mod net;
+pub mod thread;
+pub mod net;
/// A prelude for conveniently writing platform-specific code.
///
@@ -53,14 +53,14 @@ pub mod prelude {
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};
+ #[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/net.rs b/ctr-std/src/sys/unix/ext/net.rs
new file mode 100644
index 0000000..e277b1a
--- /dev/null
+++ b/ctr-std/src/sys/unix/ext/net.rs
@@ -0,0 +1,1818 @@
+// Copyright 2016 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.
+
+#![stable(feature = "unix_socket", since = "1.10.0")]
+
+//! Unix-specific networking functionality
+
+#[cfg(unix)]
+use libc;
+
+// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here?
+#[cfg(not(unix))]
+mod libc {
+ pub use libc::c_int;
+ pub type socklen_t = u32;
+ pub struct sockaddr;
+ #[derive(Clone)]
+ pub struct sockaddr_un;
+}
+
+use ascii;
+use ffi::OsStr;
+use fmt;
+use io::{self, Initializer};
+use mem;
+use net::{self, Shutdown};
+use os::unix::ffi::OsStrExt;
+use os::unix::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd};
+use path::Path;
+use time::Duration;
+use sys::{self, cvt};
+use sys::net::Socket;
+use sys_common::{self, AsInner, FromInner, IntoInner};
+
+#[cfg(any(target_os = "linux", target_os = "android",
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "haiku", target_os = "bitrig"))]
+use libc::MSG_NOSIGNAL;
+#[cfg(not(any(target_os = "linux", target_os = "android",
+ target_os = "dragonfly", target_os = "freebsd",
+ target_os = "openbsd", target_os = "netbsd",
+ target_os = "haiku", target_os = "bitrig")))]
+const MSG_NOSIGNAL: libc::c_int = 0x0;
+
+fn sun_path_offset() -> usize {
+ // Work with an actual instance of the type since using a null pointer is UB
+ let addr: libc::sockaddr_un = unsafe { mem::uninitialized() };
+ let base = &addr as *const _ as usize;
+ let path = &addr.sun_path as *const _ as usize;
+ path - base
+}
+
+unsafe fn sockaddr_un(path: &Path) -> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
+ let mut addr: libc::sockaddr_un = mem::zeroed();
+ addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
+
+ let bytes = path.as_os_str().as_bytes();
+
+ if bytes.contains(&0) {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "paths may not contain interior null bytes"));
+ }
+
+ if bytes.len() >= addr.sun_path.len() {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "path must be shorter than SUN_LEN"));
+ }
+ for (dst, src) in addr.sun_path.iter_mut().zip(bytes.iter()) {
+ *dst = *src as libc::c_char;
+ }
+ // null byte for pathname addresses is already there because we zeroed the
+ // struct
+
+ let mut len = sun_path_offset() + bytes.len();
+ match bytes.get(0) {
+ Some(&0) | None => {}
+ Some(_) => len += 1,
+ }
+ Ok((addr, len as libc::socklen_t))
+}
+
+enum AddressKind<'a> {
+ Unnamed,
+ Pathname(&'a Path),
+ Abstract(&'a [u8]),
+}
+
+/// An address associated with a Unix socket.
+///
+/// # Examples
+///
+/// ```
+/// use std::os::unix::net::UnixListener;
+///
+/// let socket = match UnixListener::bind("/tmp/sock") {
+/// Ok(sock) => sock,
+/// Err(e) => {
+/// println!("Couldn't bind: {:?}", e);
+/// return
+/// }
+/// };
+/// let addr = socket.local_addr().expect("Couldn't get local address");
+/// ```
+#[derive(Clone)]
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct SocketAddr {
+ addr: libc::sockaddr_un,
+ len: libc::socklen_t,
+}
+
+impl SocketAddr {
+ fn new<F>(f: F) -> io::Result<SocketAddr>
+ where F: FnOnce(*mut libc::sockaddr, *mut libc::socklen_t) -> libc::c_int
+ {
+ unsafe {
+ let mut addr: libc::sockaddr_un = mem::zeroed();
+ let mut len = mem::size_of::<libc::sockaddr_un>() as libc::socklen_t;
+ cvt(f(&mut addr as *mut _ as *mut _, &mut len))?;
+ SocketAddr::from_parts(addr, len)
+ }
+ }
+
+ fn from_parts(addr: libc::sockaddr_un, mut len: libc::socklen_t) -> io::Result<SocketAddr> {
+ if len == 0 {
+ // When there is a datagram from unnamed unix socket
+ // linux returns zero bytes of address
+ len = sun_path_offset() as libc::socklen_t; // i.e. zero-length address
+ } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "file descriptor did not correspond to a Unix socket"));
+ }
+
+ Ok(SocketAddr {
+ addr,
+ len,
+ })
+ }
+
+ /// Returns true if and only if the address is unnamed.
+ ///
+ /// # Examples
+ ///
+ /// A named address:
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let socket = UnixListener::bind("/tmp/sock").unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.is_unnamed(), false);
+ /// ```
+ ///
+ /// An unnamed address:
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let socket = UnixDatagram::unbound().unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.is_unnamed(), true);
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn is_unnamed(&self) -> bool {
+ if let AddressKind::Unnamed = self.address() {
+ true
+ } else {
+ false
+ }
+ }
+
+ /// Returns the contents of this address if it is a `pathname` address.
+ ///
+ /// # Examples
+ ///
+ /// With a pathname:
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ /// use std::path::Path;
+ ///
+ /// let socket = UnixListener::bind("/tmp/sock").unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock")));
+ /// ```
+ ///
+ /// Without a pathname:
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let socket = UnixDatagram::unbound().unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// assert_eq!(addr.as_pathname(), None);
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn as_pathname(&self) -> Option<&Path> {
+ if let AddressKind::Pathname(path) = self.address() {
+ Some(path)
+ } else {
+ None
+ }
+ }
+
+ fn address<'a>(&'a self) -> AddressKind<'a> {
+ let len = self.len as usize - sun_path_offset();
+ let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) };
+
+ // macOS seems to return a len of 16 and a zeroed sun_path for unnamed addresses
+ if len == 0
+ || (cfg!(not(any(target_os = "linux", target_os = "android")))
+ && self.addr.sun_path[0] == 0)
+ {
+ AddressKind::Unnamed
+ } else if self.addr.sun_path[0] == 0 {
+ AddressKind::Abstract(&path[1..len])
+ } else {
+ AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref())
+ }
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl fmt::Debug for SocketAddr {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ match self.address() {
+ AddressKind::Unnamed => write!(fmt, "(unnamed)"),
+ AddressKind::Abstract(name) => write!(fmt, "{} (abstract)", AsciiEscaped(name)),
+ AddressKind::Pathname(path) => write!(fmt, "{:?} (pathname)", path),
+ }
+ }
+}
+
+struct AsciiEscaped<'a>(&'a [u8]);
+
+impl<'a> fmt::Display for AsciiEscaped<'a> {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "\"")?;
+ for byte in self.0.iter().cloned().flat_map(ascii::escape_default) {
+ write!(fmt, "{}", byte as char)?;
+ }
+ write!(fmt, "\"")
+ }
+}
+
+/// A Unix stream socket.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::net::UnixStream;
+/// use std::io::prelude::*;
+///
+/// let mut stream = UnixStream::connect("/path/to/my/socket").unwrap();
+/// stream.write_all(b"hello world").unwrap();
+/// let mut response = String::new();
+/// stream.read_to_string(&mut response).unwrap();
+/// println!("{}", response);
+/// ```
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct UnixStream(Socket);
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl fmt::Debug for UnixStream {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let mut builder = fmt.debug_struct("UnixStream");
+ builder.field("fd", self.0.as_inner());
+ if let Ok(addr) = self.local_addr() {
+ builder.field("local", &addr);
+ }
+ if let Ok(addr) = self.peer_addr() {
+ builder.field("peer", &addr);
+ }
+ builder.finish()
+ }
+}
+
+impl UnixStream {
+ /// Connects to the socket named by `path`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = match UnixStream::connect("/tmp/sock") {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't connect: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> {
+ fn inner(path: &Path) -> io::Result<UnixStream> {
+ unsafe {
+ let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
+ let (addr, len) = sockaddr_un(path)?;
+
+ cvt(libc::connect(*inner.as_inner(), &addr as *const _ as *const _, len))?;
+ Ok(UnixStream(inner))
+ }
+ }
+ inner(path.as_ref())
+ }
+
+ /// Creates an unnamed pair of connected sockets.
+ ///
+ /// Returns two `UnixStream`s which are connected to each other.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let (sock1, sock2) = match UnixStream::pair() {
+ /// Ok((sock1, sock2)) => (sock1, sock2),
+ /// Err(e) => {
+ /// println!("Couldn't create a pair of sockets: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn pair() -> io::Result<(UnixStream, UnixStream)> {
+ let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_STREAM)?;
+ Ok((UnixStream(i1), UnixStream(i2)))
+ }
+
+ /// Creates a new independently owned handle to the underlying socket.
+ ///
+ /// The returned `UnixStream` is a reference to the same stream that this
+ /// object references. Both handles will read and write the same stream of
+ /// data, and options set on one stream will be propagated to the other
+ /// stream.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// let sock_copy = socket.try_clone().expect("Couldn't clone socket");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn try_clone(&self) -> io::Result<UnixStream> {
+ self.0.duplicate().map(UnixStream)
+ }
+
+ /// Returns the socket address of the local half of this connection.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// let addr = socket.local_addr().expect("Couldn't get local address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn local_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Returns the socket address of the remote half of this connection.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// let addr = socket.peer_addr().expect("Couldn't get peer address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Sets the read timeout for the socket.
+ ///
+ /// If the provided value is [`None`], then [`read`] calls will block
+ /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err
+ /// [`read`]: ../../../../std/io/trait.Read.html#tymethod.read
+ /// [`Duration`]: ../../../../std/time/struct.Duration.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout");
+ /// ```
+ ///
+ /// An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method:
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.0.set_timeout(timeout, libc::SO_RCVTIMEO)
+ }
+
+ /// Sets the write timeout for the socket.
+ ///
+ /// If the provided value is [`None`], then [`write`] calls will block
+ /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is
+ /// passed to this method.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err
+ /// [`write`]: ../../../../std/io/trait.Write.html#tymethod.write
+ /// [`Duration`]: ../../../../std/time/struct.Duration.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout");
+ /// ```
+ ///
+ /// An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method:
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::net::UdpSocket;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UdpSocket::bind("127.0.0.1:34254").unwrap();
+ /// let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.0.set_timeout(timeout, libc::SO_SNDTIMEO)
+ }
+
+ /// Returns the read timeout of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout");
+ /// assert_eq!(socket.read_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0.timeout(libc::SO_RCVTIMEO)
+ }
+
+ /// Returns the write timeout of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout");
+ /// assert_eq!(socket.write_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0.timeout(libc::SO_SNDTIMEO)
+ }
+
+ /// Moves the socket into or out of nonblocking mode.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.set_nonblocking(true).expect("Couldn't set nonblocking");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ self.0.set_nonblocking(nonblocking)
+ }
+
+ /// Returns the value of the `SO_ERROR` option.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// if let Ok(Some(err)) = socket.take_error() {
+ /// println!("Got error: {:?}", err);
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0.take_error()
+ }
+
+ /// Shuts down the read, write, or both halves of this connection.
+ ///
+ /// This function will cause all pending and future I/O calls on the
+ /// specified portions to immediately return with an appropriate value
+ /// (see the documentation of [`Shutdown`]).
+ ///
+ /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixStream;
+ /// use std::net::Shutdown;
+ ///
+ /// let socket = UnixStream::connect("/tmp/sock").unwrap();
+ /// socket.shutdown(Shutdown::Both).expect("shutdown function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ self.0.shutdown(how)
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl io::Read for UnixStream {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ io::Read::read(&mut &*self, buf)
+ }
+
+ #[inline]
+ unsafe fn initializer(&self) -> Initializer {
+ Initializer::nop()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl<'a> io::Read for &'a UnixStream {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ #[inline]
+ unsafe fn initializer(&self) -> Initializer {
+ Initializer::nop()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl io::Write for UnixStream {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ io::Write::write(&mut &*self, buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ io::Write::flush(&mut &*self)
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl<'a> io::Write for &'a UnixStream {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl AsRawFd for UnixStream {
+ fn as_raw_fd(&self) -> RawFd {
+ *self.0.as_inner()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl FromRawFd for UnixStream {
+ unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
+ UnixStream(Socket::from_inner(fd))
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl IntoRawFd for UnixStream {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_inner()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for net::TcpStream {
+ fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for net::TcpListener {
+ fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl AsRawFd for net::UdpSocket {
+ fn as_raw_fd(&self) -> RawFd { *self.as_inner().socket().as_inner() }
+}
+
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for net::TcpStream {
+ unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream {
+ let socket = sys::net::Socket::from_inner(fd);
+ net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(socket))
+ }
+}
+
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for net::TcpListener {
+ unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener {
+ let socket = sys::net::Socket::from_inner(fd);
+ net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(socket))
+ }
+}
+
+#[stable(feature = "from_raw_os", since = "1.1.0")]
+impl FromRawFd for net::UdpSocket {
+ unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket {
+ let socket = sys::net::Socket::from_inner(fd);
+ net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(socket))
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for net::TcpStream {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_socket().into_inner()
+ }
+}
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for net::TcpListener {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_socket().into_inner()
+ }
+}
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for net::UdpSocket {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_socket().into_inner()
+ }
+}
+
+/// A structure representing a Unix domain socket server.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::thread;
+/// use std::os::unix::net::{UnixStream, UnixListener};
+///
+/// fn handle_client(stream: UnixStream) {
+/// // ...
+/// }
+///
+/// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+///
+/// // accept connections and process them, spawning a new thread for each one
+/// for stream in listener.incoming() {
+/// match stream {
+/// Ok(stream) => {
+/// /* connection succeeded */
+/// thread::spawn(|| handle_client(stream));
+/// }
+/// Err(err) => {
+/// /* connection failed */
+/// break;
+/// }
+/// }
+/// }
+/// ```
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct UnixListener(Socket);
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl fmt::Debug for UnixListener {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let mut builder = fmt.debug_struct("UnixListener");
+ builder.field("fd", self.0.as_inner());
+ if let Ok(addr) = self.local_addr() {
+ builder.field("local", &addr);
+ }
+ builder.finish()
+ }
+}
+
+impl UnixListener {
+ /// Creates a new `UnixListener` bound to the specified socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = match UnixListener::bind("/path/to/the/socket") {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't connect: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
+ fn inner(path: &Path) -> io::Result<UnixListener> {
+ unsafe {
+ let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
+ let (addr, len) = sockaddr_un(path)?;
+
+ cvt(libc::bind(*inner.as_inner(), &addr as *const _ as *const _, len as _))?;
+ cvt(libc::listen(*inner.as_inner(), 128))?;
+
+ Ok(UnixListener(inner))
+ }
+ }
+ inner(path.as_ref())
+ }
+
+ /// Accepts a new incoming connection to this listener.
+ ///
+ /// This function will block the calling thread until a new Unix connection
+ /// is established. When established, the corresponding [`UnixStream`] and
+ /// the remote peer's address will be returned.
+ ///
+ /// [`UnixStream`]: ../../../../std/os/unix/net/struct.UnixStream.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// match listener.accept() {
+ /// Ok((socket, addr)) => println!("Got a client: {:?}", addr),
+ /// Err(e) => println!("accept function failed: {:?}", e),
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> {
+ let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() };
+ let mut len = mem::size_of_val(&storage) as libc::socklen_t;
+ let sock = self.0.accept(&mut storage as *mut _ as *mut _, &mut len)?;
+ let addr = SocketAddr::from_parts(storage, len)?;
+ Ok((UnixStream(sock), addr))
+ }
+
+ /// Creates a new independently owned handle to the underlying socket.
+ ///
+ /// The returned `UnixListener` is a reference to the same socket that this
+ /// object references. Both handles can be used to accept incoming
+ /// connections and options set on one listener will affect the other.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// let listener_copy = listener.try_clone().expect("try_clone failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn try_clone(&self) -> io::Result<UnixListener> {
+ self.0.duplicate().map(UnixListener)
+ }
+
+ /// Returns the local socket address of this listener.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// let addr = listener.local_addr().expect("Couldn't get local address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn local_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Moves the socket into or out of nonblocking mode.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// listener.set_nonblocking(true).expect("Couldn't set non blocking");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ self.0.set_nonblocking(nonblocking)
+ }
+
+ /// Returns the value of the `SO_ERROR` option.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixListener;
+ ///
+ /// let listener = UnixListener::bind("/tmp/sock").unwrap();
+ ///
+ /// if let Ok(Some(err)) = listener.take_error() {
+ /// println!("Got error: {:?}", err);
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0.take_error()
+ }
+
+ /// Returns an iterator over incoming connections.
+ ///
+ /// The iterator will never return [`None`] and will also not yield the
+ /// peer's [`SocketAddr`] structure.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`SocketAddr`]: struct.SocketAddr.html
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::thread;
+ /// use std::os::unix::net::{UnixStream, UnixListener};
+ ///
+ /// fn handle_client(stream: UnixStream) {
+ /// // ...
+ /// }
+ ///
+ /// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+ ///
+ /// for stream in listener.incoming() {
+ /// match stream {
+ /// Ok(stream) => {
+ /// thread::spawn(|| handle_client(stream));
+ /// }
+ /// Err(err) => {
+ /// break;
+ /// }
+ /// }
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn incoming<'a>(&'a self) -> Incoming<'a> {
+ Incoming { listener: self }
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl AsRawFd for UnixListener {
+ fn as_raw_fd(&self) -> RawFd {
+ *self.0.as_inner()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl FromRawFd for UnixListener {
+ unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
+ UnixListener(Socket::from_inner(fd))
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl IntoRawFd for UnixListener {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_inner()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl<'a> IntoIterator for &'a UnixListener {
+ type Item = io::Result<UnixStream>;
+ type IntoIter = Incoming<'a>;
+
+ fn into_iter(self) -> Incoming<'a> {
+ self.incoming()
+ }
+}
+
+/// An iterator over incoming connections to a [`UnixListener`].
+///
+/// It will never return [`None`].
+///
+/// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+/// [`UnixListener`]: struct.UnixListener.html
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::thread;
+/// use std::os::unix::net::{UnixStream, UnixListener};
+///
+/// fn handle_client(stream: UnixStream) {
+/// // ...
+/// }
+///
+/// let listener = UnixListener::bind("/path/to/the/socket").unwrap();
+///
+/// for stream in listener.incoming() {
+/// match stream {
+/// Ok(stream) => {
+/// thread::spawn(|| handle_client(stream));
+/// }
+/// Err(err) => {
+/// break;
+/// }
+/// }
+/// }
+/// ```
+#[derive(Debug)]
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct Incoming<'a> {
+ listener: &'a UnixListener,
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl<'a> Iterator for Incoming<'a> {
+ type Item = io::Result<UnixStream>;
+
+ fn next(&mut self) -> Option<io::Result<UnixStream>> {
+ Some(self.listener.accept().map(|s| s.0))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (usize::max_value(), None)
+ }
+}
+
+/// A Unix datagram socket.
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::net::UnixDatagram;
+///
+/// let socket = UnixDatagram::bind("/path/to/my/socket").unwrap();
+/// socket.send_to(b"hello world", "/path/to/other/socket").unwrap();
+/// let mut buf = [0; 100];
+/// let (count, address) = socket.recv_from(&mut buf).unwrap();
+/// println!("socket {:?} sent {:?}", address, &buf[..count]);
+/// ```
+#[stable(feature = "unix_socket", since = "1.10.0")]
+pub struct UnixDatagram(Socket);
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl fmt::Debug for UnixDatagram {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let mut builder = fmt.debug_struct("UnixDatagram");
+ builder.field("fd", self.0.as_inner());
+ if let Ok(addr) = self.local_addr() {
+ builder.field("local", &addr);
+ }
+ if let Ok(addr) = self.peer_addr() {
+ builder.field("peer", &addr);
+ }
+ builder.finish()
+ }
+}
+
+impl UnixDatagram {
+ /// Creates a Unix datagram socket bound to the given path.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = match UnixDatagram::bind("/path/to/the/socket") {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't bind: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> {
+ fn inner(path: &Path) -> io::Result<UnixDatagram> {
+ unsafe {
+ let socket = UnixDatagram::unbound()?;
+ let (addr, len) = sockaddr_un(path)?;
+
+ cvt(libc::bind(*socket.0.as_inner(), &addr as *const _ as *const _, len as _))?;
+
+ Ok(socket)
+ }
+ }
+ inner(path.as_ref())
+ }
+
+ /// Creates a Unix Datagram socket which is not bound to any address.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = match UnixDatagram::unbound() {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't unbound: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn unbound() -> io::Result<UnixDatagram> {
+ let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_DGRAM)?;
+ Ok(UnixDatagram(inner))
+ }
+
+ /// Create an unnamed pair of connected sockets.
+ ///
+ /// Returns two `UnixDatagrams`s which are connected to each other.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let (sock1, sock2) = match UnixDatagram::pair() {
+ /// Ok((sock1, sock2)) => (sock1, sock2),
+ /// Err(e) => {
+ /// println!("Couldn't unbound: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> {
+ let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_DGRAM)?;
+ Ok((UnixDatagram(i1), UnixDatagram(i2)))
+ }
+
+ /// Connects the socket to the specified address.
+ ///
+ /// The [`send`] method may be used to send data to the specified address.
+ /// [`recv`] and [`recv_from`] will only receive data from that address.
+ ///
+ /// [`send`]: #method.send
+ /// [`recv`]: #method.recv
+ /// [`recv_from`]: #method.recv_from
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// match sock.connect("/path/to/the/socket") {
+ /// Ok(sock) => sock,
+ /// Err(e) => {
+ /// println!("Couldn't connect: {:?}", e);
+ /// return
+ /// }
+ /// };
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
+ fn inner(d: &UnixDatagram, path: &Path) -> io::Result<()> {
+ unsafe {
+ let (addr, len) = sockaddr_un(path)?;
+
+ cvt(libc::connect(*d.0.as_inner(), &addr as *const _ as *const _, len))?;
+
+ Ok(())
+ }
+ }
+ inner(self, path.as_ref())
+ }
+
+ /// Creates a new independently owned handle to the underlying socket.
+ ///
+ /// The returned `UnixDatagram` is a reference to the same socket that this
+ /// object references. Both handles can be used to accept incoming
+ /// connections and options set on one side will affect the other.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap();
+ ///
+ /// let sock_copy = sock.try_clone().expect("try_clone failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn try_clone(&self) -> io::Result<UnixDatagram> {
+ self.0.duplicate().map(UnixDatagram)
+ }
+
+ /// Returns the address of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap();
+ ///
+ /// let addr = sock.local_addr().expect("Couldn't get local address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn local_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Returns the address of this socket's peer.
+ ///
+ /// The [`connect`] method will connect the socket to a peer.
+ ///
+ /// [`connect`]: #method.connect
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.connect("/path/to/the/socket").unwrap();
+ ///
+ /// let addr = sock.peer_addr().expect("Couldn't get peer address");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
+ }
+
+ /// Receives data from the socket.
+ ///
+ /// On success, returns the number of bytes read and the address from
+ /// whence the data came.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// let mut buf = vec![0; 10];
+ /// match sock.recv_from(buf.as_mut_slice()) {
+ /// Ok((size, sender)) => println!("received {} bytes from {:?}", size, sender),
+ /// Err(e) => println!("recv_from function failed: {:?}", e),
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ let mut count = 0;
+ let addr = SocketAddr::new(|addr, len| {
+ unsafe {
+ count = libc::recvfrom(*self.0.as_inner(),
+ buf.as_mut_ptr() as *mut _,
+ buf.len(),
+ 0,
+ addr,
+ len);
+ if count > 0 {
+ 1
+ } else if count == 0 {
+ 0
+ } else {
+ -1
+ }
+ }
+ })?;
+
+ Ok((count as usize, addr))
+ }
+
+ /// Receives data from the socket.
+ ///
+ /// On success, returns the number of bytes read.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap();
+ /// let mut buf = vec![0; 10];
+ /// sock.recv(buf.as_mut_slice()).expect("recv function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ /// Sends data on the socket to the specified address.
+ ///
+ /// On success, returns the number of bytes written.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> {
+ fn inner(d: &UnixDatagram, buf: &[u8], path: &Path) -> io::Result<usize> {
+ unsafe {
+ let (addr, len) = sockaddr_un(path)?;
+
+ let count = cvt(libc::sendto(*d.0.as_inner(),
+ buf.as_ptr() as *const _,
+ buf.len(),
+ MSG_NOSIGNAL,
+ &addr as *const _ as *const _,
+ len))?;
+ Ok(count as usize)
+ }
+ }
+ inner(self, buf, path.as_ref())
+ }
+
+ /// Sends data on the socket to the socket's peer.
+ ///
+ /// The peer address may be set by the `connect` method, and this method
+ /// will return an error if the socket has not already been connected.
+ ///
+ /// On success, returns the number of bytes written.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.connect("/some/sock").expect("Couldn't connect");
+ /// sock.send(b"omelette au fromage").expect("send_to function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ /// Sets the read timeout for the socket.
+ ///
+ /// If the provided value is [`None`], then [`recv`] and [`recv_from`] calls will
+ /// block indefinitely. An [`Err`] is returned if the zero [`Duration`]
+ /// is passed to this method.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`Err`]: ../../../../std/result/enum.Result.html#variant.Err
+ /// [`recv`]: #method.recv
+ /// [`recv_from`]: #method.recv_from
+ /// [`Duration`]: ../../../../std/time/struct.Duration.html
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed");
+ /// ```
+ ///
+ /// An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method:
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixDatagram::unbound().unwrap();
+ /// let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.0.set_timeout(timeout, libc::SO_RCVTIMEO)
+ }
+
+ /// Sets the write timeout for the socket.
+ ///
+ /// If the provided value is [`None`], then [`send`] and [`send_to`] calls will
+ /// block indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method.
+ ///
+ /// [`None`]: ../../../../std/option/enum.Option.html#variant.None
+ /// [`send`]: #method.send
+ /// [`send_to`]: #method.send_to
+ /// [`Duration`]: ../../../../std/time/struct.Duration.html
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_write_timeout(Some(Duration::new(1, 0)))
+ /// .expect("set_write_timeout function failed");
+ /// ```
+ ///
+ /// An [`Err`] is returned if the zero [`Duration`] is passed to this
+ /// method:
+ ///
+ /// ```no_run
+ /// use std::io;
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let socket = UnixDatagram::unbound().unwrap();
+ /// let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
+ /// let err = result.unwrap_err();
+ /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput)
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
+ self.0.set_timeout(timeout, libc::SO_SNDTIMEO)
+ }
+
+ /// Returns the read timeout of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed");
+ /// assert_eq!(sock.read_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0.timeout(libc::SO_RCVTIMEO)
+ }
+
+ /// Returns the write timeout of this socket.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::time::Duration;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_write_timeout(Some(Duration::new(1, 0)))
+ /// .expect("set_write_timeout function failed");
+ /// assert_eq!(sock.write_timeout().unwrap(), Some(Duration::new(1, 0)));
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ self.0.timeout(libc::SO_SNDTIMEO)
+ }
+
+ /// Moves the socket into or out of nonblocking mode.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.set_nonblocking(true).expect("set_nonblocking function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ self.0.set_nonblocking(nonblocking)
+ }
+
+ /// Returns the value of the `SO_ERROR` option.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// if let Ok(Some(err)) = sock.take_error() {
+ /// println!("Got error: {:?}", err);
+ /// }
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ self.0.take_error()
+ }
+
+ /// Shut down the read, write, or both halves of this connection.
+ ///
+ /// This function will cause all pending and future I/O calls on the
+ /// specified portions to immediately return with an appropriate value
+ /// (see the documentation of [`Shutdown`]).
+ ///
+ /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html
+ ///
+ /// ```no_run
+ /// use std::os::unix::net::UnixDatagram;
+ /// use std::net::Shutdown;
+ ///
+ /// let sock = UnixDatagram::unbound().unwrap();
+ /// sock.shutdown(Shutdown::Both).expect("shutdown function failed");
+ /// ```
+ #[stable(feature = "unix_socket", since = "1.10.0")]
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ self.0.shutdown(how)
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl AsRawFd for UnixDatagram {
+ fn as_raw_fd(&self) -> RawFd {
+ *self.0.as_inner()
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl FromRawFd for UnixDatagram {
+ unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
+ UnixDatagram(Socket::from_inner(fd))
+ }
+}
+
+#[stable(feature = "unix_socket", since = "1.10.0")]
+impl IntoRawFd for UnixDatagram {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_inner()
+ }
+}
+
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod test {
+ use thread;
+ use io::{self, ErrorKind};
+ use io::prelude::*;
+ use time::Duration;
+ use sys_common::io::test::tmpdir;
+
+ use super::*;
+
+ macro_rules! or_panic {
+ ($e:expr) => {
+ match $e {
+ Ok(e) => e,
+ Err(e) => panic!("{}", e),
+ }
+ }
+ }
+
+ #[test]
+ fn basic() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+ let msg1 = b"hello";
+ let msg2 = b"world!";
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+ let thread = thread::spawn(move || {
+ let mut stream = or_panic!(listener.accept()).0;
+ let mut buf = [0; 5];
+ or_panic!(stream.read(&mut buf));
+ assert_eq!(&msg1[..], &buf[..]);
+ or_panic!(stream.write_all(msg2));
+ });
+
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ assert_eq!(Some(&*socket_path),
+ stream.peer_addr().unwrap().as_pathname());
+ or_panic!(stream.write_all(msg1));
+ let mut buf = vec![];
+ or_panic!(stream.read_to_end(&mut buf));
+ assert_eq!(&msg2[..], &buf[..]);
+ drop(stream);
+
+ thread.join().unwrap();
+ }
+
+ #[test]
+ fn pair() {
+ let msg1 = b"hello";
+ let msg2 = b"world!";
+
+ let (mut s1, mut s2) = or_panic!(UnixStream::pair());
+ let thread = thread::spawn(move || {
+ // s1 must be moved in or the test will hang!
+ let mut buf = [0; 5];
+ or_panic!(s1.read(&mut buf));
+ assert_eq!(&msg1[..], &buf[..]);
+ or_panic!(s1.write_all(msg2));
+ });
+
+ or_panic!(s2.write_all(msg1));
+ let mut buf = vec![];
+ or_panic!(s2.read_to_end(&mut buf));
+ assert_eq!(&msg2[..], &buf[..]);
+ drop(s2);
+
+ thread.join().unwrap();
+ }
+
+ #[test]
+ fn try_clone() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+ let msg1 = b"hello";
+ let msg2 = b"world";
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+ let thread = thread::spawn(move || {
+ let mut stream = or_panic!(listener.accept()).0;
+ or_panic!(stream.write_all(msg1));
+ or_panic!(stream.write_all(msg2));
+ });
+
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ let mut stream2 = or_panic!(stream.try_clone());
+
+ let mut buf = [0; 5];
+ or_panic!(stream.read(&mut buf));
+ assert_eq!(&msg1[..], &buf[..]);
+ or_panic!(stream2.read(&mut buf));
+ assert_eq!(&msg2[..], &buf[..]);
+
+ thread.join().unwrap();
+ }
+
+ #[test]
+ fn iter() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+ let thread = thread::spawn(move || {
+ for stream in listener.incoming().take(2) {
+ let mut stream = or_panic!(stream);
+ let mut buf = [0];
+ or_panic!(stream.read(&mut buf));
+ }
+ });
+
+ for _ in 0..2 {
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ or_panic!(stream.write_all(&[0]));
+ }
+
+ thread.join().unwrap();
+ }
+
+ #[test]
+ fn long_path() {
+ let dir = tmpdir();
+ let socket_path = dir.path()
+ .join("asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfa\
+ sasdfasdfasdasdfasdfasdfadfasdfasdfasdfasdfasdf");
+ match UnixStream::connect(&socket_path) {
+ Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+ Err(e) => panic!("unexpected error {}", e),
+ Ok(_) => panic!("unexpected success"),
+ }
+
+ match UnixListener::bind(&socket_path) {
+ Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+ Err(e) => panic!("unexpected error {}", e),
+ Ok(_) => panic!("unexpected success"),
+ }
+
+ match UnixDatagram::bind(&socket_path) {
+ Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {}
+ Err(e) => panic!("unexpected error {}", e),
+ Ok(_) => panic!("unexpected success"),
+ }
+ }
+
+ #[test]
+ fn timeouts() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let _listener = or_panic!(UnixListener::bind(&socket_path));
+
+ let stream = or_panic!(UnixStream::connect(&socket_path));
+ let dur = Duration::new(15410, 0);
+
+ assert_eq!(None, or_panic!(stream.read_timeout()));
+
+ or_panic!(stream.set_read_timeout(Some(dur)));
+ assert_eq!(Some(dur), or_panic!(stream.read_timeout()));
+
+ assert_eq!(None, or_panic!(stream.write_timeout()));
+
+ or_panic!(stream.set_write_timeout(Some(dur)));
+ assert_eq!(Some(dur), or_panic!(stream.write_timeout()));
+
+ or_panic!(stream.set_read_timeout(None));
+ assert_eq!(None, or_panic!(stream.read_timeout()));
+
+ or_panic!(stream.set_write_timeout(None));
+ assert_eq!(None, or_panic!(stream.write_timeout()));
+ }
+
+ #[test]
+ fn test_read_timeout() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let _listener = or_panic!(UnixListener::bind(&socket_path));
+
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+ let mut buf = [0; 10];
+ let kind = stream.read(&mut buf).err().expect("expected error").kind();
+ assert!(kind == io::ErrorKind::WouldBlock || kind == io::ErrorKind::TimedOut);
+ }
+
+ #[test]
+ fn test_read_with_timeout() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+
+ let mut stream = or_panic!(UnixStream::connect(&socket_path));
+ or_panic!(stream.set_read_timeout(Some(Duration::from_millis(1000))));
+
+ let mut other_end = or_panic!(listener.accept()).0;
+ or_panic!(other_end.write_all(b"hello world"));
+
+ let mut buf = [0; 11];
+ or_panic!(stream.read(&mut buf));
+ assert_eq!(b"hello world", &buf[..]);
+
+ let kind = stream.read(&mut buf).err().expect("expected error").kind();
+ assert!(kind == io::ErrorKind::WouldBlock || kind == io::ErrorKind::TimedOut);
+ }
+
+ // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+ // when passed zero Durations
+ #[test]
+ fn test_unix_stream_timeout_zero_duration() {
+ let dir = tmpdir();
+ let socket_path = dir.path().join("sock");
+
+ let listener = or_panic!(UnixListener::bind(&socket_path));
+ let stream = or_panic!(UnixStream::connect(&socket_path));
+
+ let result = stream.set_write_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+ let result = stream.set_read_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+ drop(listener);
+ }
+
+ #[test]
+ fn test_unix_datagram() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock1");
+ let path2 = dir.path().join("sock2");
+
+ let sock1 = or_panic!(UnixDatagram::bind(&path1));
+ let sock2 = or_panic!(UnixDatagram::bind(&path2));
+
+ let msg = b"hello world";
+ or_panic!(sock1.send_to(msg, &path2));
+ let mut buf = [0; 11];
+ or_panic!(sock2.recv_from(&mut buf));
+ assert_eq!(msg, &buf[..]);
+ }
+
+ #[test]
+ fn test_unnamed_unix_datagram() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock1");
+
+ let sock1 = or_panic!(UnixDatagram::bind(&path1));
+ let sock2 = or_panic!(UnixDatagram::unbound());
+
+ let msg = b"hello world";
+ or_panic!(sock2.send_to(msg, &path1));
+ let mut buf = [0; 11];
+ let (usize, addr) = or_panic!(sock1.recv_from(&mut buf));
+ assert_eq!(usize, 11);
+ assert!(addr.is_unnamed());
+ assert_eq!(msg, &buf[..]);
+ }
+
+ #[test]
+ fn test_connect_unix_datagram() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock1");
+ let path2 = dir.path().join("sock2");
+
+ let bsock1 = or_panic!(UnixDatagram::bind(&path1));
+ let bsock2 = or_panic!(UnixDatagram::bind(&path2));
+ let sock = or_panic!(UnixDatagram::unbound());
+ or_panic!(sock.connect(&path1));
+
+ // Check send()
+ let msg = b"hello there";
+ or_panic!(sock.send(msg));
+ let mut buf = [0; 11];
+ let (usize, addr) = or_panic!(bsock1.recv_from(&mut buf));
+ assert_eq!(usize, 11);
+ assert!(addr.is_unnamed());
+ assert_eq!(msg, &buf[..]);
+
+ // Changing default socket works too
+ or_panic!(sock.connect(&path2));
+ or_panic!(sock.send(msg));
+ or_panic!(bsock2.recv_from(&mut buf));
+ }
+
+ #[test]
+ fn test_unix_datagram_recv() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock1");
+
+ let sock1 = or_panic!(UnixDatagram::bind(&path1));
+ let sock2 = or_panic!(UnixDatagram::unbound());
+ or_panic!(sock2.connect(&path1));
+
+ let msg = b"hello world";
+ or_panic!(sock2.send(msg));
+ let mut buf = [0; 11];
+ let size = or_panic!(sock1.recv(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+ }
+
+ #[test]
+ fn datagram_pair() {
+ let msg1 = b"hello";
+ let msg2 = b"world!";
+
+ let (s1, s2) = or_panic!(UnixDatagram::pair());
+ let thread = thread::spawn(move || {
+ // s1 must be moved in or the test will hang!
+ let mut buf = [0; 5];
+ or_panic!(s1.recv(&mut buf));
+ assert_eq!(&msg1[..], &buf[..]);
+ or_panic!(s1.send(msg2));
+ });
+
+ or_panic!(s2.send(msg1));
+ let mut buf = [0; 6];
+ or_panic!(s2.recv(&mut buf));
+ assert_eq!(&msg2[..], &buf[..]);
+ drop(s2);
+
+ thread.join().unwrap();
+ }
+
+ // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+ // when passed zero Durations
+ #[test]
+ fn test_unix_datagram_timeout_zero_duration() {
+ let dir = tmpdir();
+ let path = dir.path().join("sock");
+
+ let datagram = or_panic!(UnixDatagram::bind(&path));
+
+ let result = datagram.set_write_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+ let result = datagram.set_read_timeout(Some(Duration::new(0, 0)));
+ let err = result.unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+ }
+
+ #[test]
+ fn abstract_namespace_not_allowed() {
+ assert!(UnixStream::connect("\0asdf").is_err());
+ }
+}
diff --git a/ctr-std/src/sys/unix/ext/process.rs b/ctr-std/src/sys/unix/ext/process.rs
new file mode 100644
index 0000000..21630ae
--- /dev/null
+++ b/ctr-std/src/sys/unix/ext/process.rs
@@ -0,0 +1,203 @@
+// 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 primitives in the `std::process` module.
+
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use io;
+use os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
+use process;
+use sys;
+use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};
+
+/// Unix-specific extensions to the [`process::Command`] builder.
+///
+/// [`process::Command`]: ../../../../std/process/struct.Command.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait CommandExt {
+ /// Sets the child process's user id. This translates to a
+ /// `setuid` call in the child process. Failure in the `setuid`
+ /// call will cause the spawn to fail.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn uid(&mut self, id: u32) -> &mut process::Command;
+
+ /// Similar to `uid`, but sets the group id of the child process. This has
+ /// the same semantics as the `uid` field.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn gid(&mut self, id: u32) -> &mut process::Command;
+
+ /// Schedules a closure to be run just before the `exec` function is
+ /// invoked.
+ ///
+ /// The closure is allowed to return an I/O error whose OS error code will
+ /// be communicated back to the parent and returned as an error from when
+ /// the spawn was requested.
+ ///
+ /// Multiple closures can be registered and they will be called in order of
+ /// their registration. If a closure returns `Err` then no further closures
+ /// will be called and the spawn operation will immediately return with a
+ /// failure.
+ ///
+ /// # Notes
+ ///
+ /// This closure will be run in the context of the child process after a
+ /// `fork`. This primarily means that any modifications made to memory on
+ /// behalf of this closure will **not** be visible to the parent process.
+ /// This is often a very constrained environment where normal operations
+ /// like `malloc` or acquiring a mutex are not guaranteed to work (due to
+ /// other threads perhaps still running when the `fork` was run).
+ ///
+ /// When this closure is run, aspects such as the stdio file descriptors and
+ /// working directory have successfully been changed, so output to these
+ /// locations may not appear where intended.
+ #[stable(feature = "process_exec", since = "1.15.0")]
+ fn before_exec<F>(&mut self, f: F) -> &mut process::Command
+ where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
+
+ /// Performs all the required setup by this `Command`, followed by calling
+ /// the `execvp` syscall.
+ ///
+ /// On success this function will not return, and otherwise it will return
+ /// an error indicating why the exec (or another part of the setup of the
+ /// `Command`) failed.
+ ///
+ /// `exec` not returning has the same implications as calling
+ /// [`process::exit`] – no destructors on the current stack or any other
+ /// thread’s stack will be run. Therefore, it is recommended to only call
+ /// `exec` at a point where it is fine to not run any destructors. Note,
+ /// that the `execvp` syscall independently guarantees that all memory is
+ /// freed and all file descriptors with the `CLOEXEC` option (set by default
+ /// on all file descriptors opened by the standard library) are closed.
+ ///
+ /// This function, unlike `spawn`, will **not** `fork` the process to create
+ /// a new child. Like spawn, however, the default behavior for the stdio
+ /// descriptors will be to inherited from the current process.
+ ///
+ /// [`process::exit`]: ../../../process/fn.exit.html
+ ///
+ /// # Notes
+ ///
+ /// The process may be in a "broken state" if this function returns in
+ /// error. For example the working directory, environment variables, signal
+ /// handling settings, various user/group information, or aspects of stdio
+ /// file descriptors may have changed. If a "transactional spawn" is
+ /// required to gracefully handle errors it is recommended to use the
+ /// cross-platform `spawn` instead.
+ #[stable(feature = "process_exec2", since = "1.9.0")]
+ fn exec(&mut self) -> io::Error;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl CommandExt for process::Command {
+ fn uid(&mut self, id: u32) -> &mut process::Command {
+ self.as_inner_mut().uid(id);
+ self
+ }
+
+ fn gid(&mut self, id: u32) -> &mut process::Command {
+ self.as_inner_mut().gid(id);
+ self
+ }
+
+ fn before_exec<F>(&mut self, f: F) -> &mut process::Command
+ where F: FnMut() -> io::Result<()> + Send + Sync + 'static
+ {
+ self.as_inner_mut().before_exec(Box::new(f));
+ self
+ }
+
+ fn exec(&mut self) -> io::Error {
+ self.as_inner_mut().exec(sys::process::Stdio::Inherit)
+ }
+}
+
+/// Unix-specific extensions to [`process::ExitStatus`].
+///
+/// [`process::ExitStatus`]: ../../../../std/process/struct.ExitStatus.html
+#[stable(feature = "rust1", since = "1.0.0")]
+pub trait ExitStatusExt {
+ /// Creates a new `ExitStatus` from the raw underlying `i32` return value of
+ /// a process.
+ #[stable(feature = "exit_status_from", since = "1.12.0")]
+ fn from_raw(raw: i32) -> Self;
+
+ /// If the process was terminated by a signal, returns that signal.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ fn signal(&self) -> Option<i32>;
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl ExitStatusExt for process::ExitStatus {
+ fn from_raw(raw: i32) -> Self {
+ process::ExitStatus::from_inner(From::from(raw))
+ }
+
+ fn signal(&self) -> Option<i32> {
+ self.as_inner().signal()
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl FromRawFd for process::Stdio {
+ unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
+ let fd = sys::fd::FileDesc::new(fd);
+ let io = sys::process::Stdio::Fd(fd);
+ process::Stdio::from_inner(io)
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl AsRawFd for process::ChildStdin {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl AsRawFd for process::ChildStdout {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "process_extensions", since = "1.2.0")]
+impl AsRawFd for process::ChildStderr {
+ fn as_raw_fd(&self) -> RawFd {
+ self.as_inner().fd().raw()
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for process::ChildStdin {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for process::ChildStdout {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+#[stable(feature = "into_raw_os", since = "1.4.0")]
+impl IntoRawFd for process::ChildStderr {
+ fn into_raw_fd(self) -> RawFd {
+ self.into_inner().into_fd().into_raw()
+ }
+}
+
+/// Returns the OS-assigned process identifier associated with this process's parent.
+#[stable(feature = "unix_ppid", since = "1.27.0")]
+pub fn parent_id() -> u32 {
+ ::sys::os::getppid()
+}
diff --git a/ctr-std/src/sys/unix/ext/raw.rs b/ctr-std/src/sys/unix/ext/raw.rs
index a51f5c1..7972990 100644
--- a/ctr-std/src/sys/unix/ext/raw.rs
+++ b/ctr-std/src/sys/unix/ext/raw.rs
@@ -16,13 +16,18 @@
the standard library, the `libc` crate on \
crates.io should be used instead for the correct \
definitions")]
-#![allow(deprecated, warnings)]
+#![allow(deprecated)]
-#[allow(missing_docs)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type uid_t = u32;
-
-#[allow(missing_docs)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type gid_t = u32;
-
-#[allow(missing_docs)]
#[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/ext/thread.rs b/ctr-std/src/sys/unix/ext/thread.rs
new file mode 100644
index 0000000..8dadf29
--- /dev/null
+++ b/ctr-std/src/sys/unix/ext/thread.rs
@@ -0,0 +1,51 @@
+// 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 primitives in the `std::thread` module.
+
+#![stable(feature = "thread_extensions", since = "1.9.0")]
+
+#[allow(deprecated)]
+use os::unix::raw::pthread_t;
+use sys_common::{AsInner, IntoInner};
+use thread::JoinHandle;
+
+#[stable(feature = "thread_extensions", since = "1.9.0")]
+#[allow(deprecated)]
+pub type RawPthread = pthread_t;
+
+/// Unix-specific extensions to [`thread::JoinHandle`].
+///
+/// [`thread::JoinHandle`]: ../../../../std/thread/struct.JoinHandle.html
+#[stable(feature = "thread_extensions", since = "1.9.0")]
+pub trait JoinHandleExt {
+ /// Extracts the raw pthread_t without taking ownership
+ #[stable(feature = "thread_extensions", since = "1.9.0")]
+ fn as_pthread_t(&self) -> RawPthread;
+
+ /// Consumes the thread, returning the raw pthread_t
+ ///
+ /// This function **transfers ownership** of the underlying pthread_t to
+ /// the caller. Callers are then the unique owners of the pthread_t and
+ /// must either detach or join the pthread_t once it's no longer needed.
+ #[stable(feature = "thread_extensions", since = "1.9.0")]
+ fn into_pthread_t(self) -> RawPthread;
+}
+
+#[stable(feature = "thread_extensions", since = "1.9.0")]
+impl<T> JoinHandleExt for JoinHandle<T> {
+ fn as_pthread_t(&self) -> RawPthread {
+ self.as_inner().id() as RawPthread
+ }
+
+ fn into_pthread_t(self) -> RawPthread {
+ self.into_inner().into_id() as RawPthread
+ }
+}
diff --git a/ctr-std/src/sys/unix/fast_thread_local.rs b/ctr-std/src/sys/unix/fast_thread_local.rs
index 9f0eee0..6cdbe5d 100644
--- a/ctr-std/src/sys/unix/fast_thread_local.rs
+++ b/ctr-std/src/sys/unix/fast_thread_local.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@@ -11,92 +11,56 @@
#![cfg(target_thread_local)]
#![unstable(feature = "thread_local_internals", issue = "0")]
-use cell::{Cell, UnsafeCell};
-use mem;
-use ptr;
-
-
-pub struct Key<T> {
- inner: UnsafeCell<Option<T>>,
-
- // Metadata to keep track of the state of the destructor. Remember that
- // these variables are thread-local, not global.
- dtor_registered: Cell<bool>,
- dtor_running: Cell<bool>,
-}
-
-unsafe impl<T> ::marker::Sync for Key<T> { }
-
-impl<T> Key<T> {
- pub const fn new() -> Key<T> {
- Key {
- inner: UnsafeCell::new(None),
- dtor_registered: Cell::new(false),
- dtor_running: Cell::new(false)
- }
- }
-
- pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> {
- unsafe {
- if mem::needs_drop::<T>() && self.dtor_running.get() {
- return None
- }
- self.register_dtor();
- }
- Some(&self.inner)
+// Since what appears to be glibc 2.18 this symbol has been shipped which
+// GCC and clang both use to invoke destructors in thread_local globals, so
+// let's do the same!
+//
+// Note, however, that we run on lots older linuxes, as well as cross
+// compiling from a newer linux to an older linux, so we also have a
+// fallback implementation to use as well.
+//
+// Due to rust-lang/rust#18804, make sure this is not generic!
+#[cfg(target_os = "linux")]
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
+ use libc;
+ use mem;
+ use sys_common::thread_local::register_dtor_fallback;
+
+ extern {
+ #[linkage = "extern_weak"]
+ static __dso_handle: *mut u8;
+ #[linkage = "extern_weak"]
+ static __cxa_thread_atexit_impl: *const libc::c_void;
}
-
- unsafe fn register_dtor(&self) {
- if !mem::needs_drop::<T>() || self.dtor_registered.get() {
- return
- }
-
- register_dtor(self as *const _ as *mut u8,
- destroy_value::<T>);
- self.dtor_registered.set(true);
+ if !__cxa_thread_atexit_impl.is_null() {
+ type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
+ arg: *mut u8,
+ dso_handle: *mut u8) -> libc::c_int;
+ mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
+ (dtor, t, &__dso_handle as *const _ as *mut _);
+ return
}
+ register_dtor_fallback(t, dtor);
}
+// macOS's analog of the above linux function is this _tlv_atexit function.
+// The disassembly of thread_local globals in C++ (at least produced by
+// clang) will have this show up in the output.
+#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
- // The fallback implementation uses a vanilla OS-based TLS key to track
- // the list of destructors that need to be run for this thread. The key
- // then has its own destructor which runs all the other destructors.
- //
- // The destructor for DTORS is a little special in that it has a `while`
- // loop to continuously drain the list of registered destructors. It
- // *should* be the case that this loop always terminates because we
- // provide the guarantee that a TLS key cannot be set after it is
- // flagged for destruction.
- use sys_common::thread_local as os;
-
- static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors));
- type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>;
- if DTORS.get().is_null() {
- let v: Box<List> = box Vec::new();
- DTORS.set(Box::into_raw(v) as *mut u8);
- }
- let list: &mut List = &mut *(DTORS.get() as *mut List);
- list.push((t, dtor));
-
- unsafe extern fn run_dtors(mut ptr: *mut u8) {
- while !ptr.is_null() {
- let list: Box<List> = Box::from_raw(ptr as *mut List);
- for &(ptr, dtor) in list.iter() {
- dtor(ptr);
- }
- ptr = DTORS.get();
- DTORS.set(ptr::null_mut());
- }
+ extern {
+ fn _tlv_atexit(dtor: unsafe extern fn(*mut u8),
+ arg: *mut u8);
}
+ _tlv_atexit(dtor, t);
}
-pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
- let ptr = ptr as *mut Key<T>;
- // Right before we run the user destructor be sure to flag the
- // destructor as running for this thread so calls to `get` will return
- // `None`.
- (*ptr).dtor_running.set(true);
+// Just use the thread_local fallback implementation, at least until there's
+// a more direct implementation.
+#[cfg(target_os = "fuchsia")]
+pub use sys_common::thread_local::register_dtor_fallback as register_dtor;
+pub fn requires_move_before_drop() -> bool {
// The macOS implementation of TLS apparently had an odd aspect to it
// where the pointer we have may be overwritten while this destructor
// is running. Specifically if a TLS destructor re-accesses TLS it may
@@ -109,13 +73,5 @@ pub unsafe extern fn destroy_value<T>(ptr: *mut u8) {
//
// Hence, we use `ptr::read` on macOS (to move to a "safe" location)
// instead of drop_in_place.
- if cfg!(target_os = "macos") {
- ptr::read((*ptr).inner.get());
- } else {
- ptr::drop_in_place((*ptr).inner.get());
- }
-}
-
-pub fn requires_move_before_drop() -> bool {
- false
+ cfg!(target_os = "macos")
}
diff --git a/ctr-std/src/sys/unix/fd.rs b/ctr-std/src/sys/unix/fd.rs
index d7913e5..4830e38 100644
--- a/ctr-std/src/sys/unix/fd.rs
+++ b/ctr-std/src/sys/unix/fd.rs
@@ -68,9 +68,31 @@ impl FileDesc {
}
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ #[cfg(target_os = "android")]
+ use super::android::cvt_pread64;
+
+ #[cfg(target_os = "emscripten")]
unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
-> io::Result<isize>
{
+ use convert::TryInto;
+ use libc::pread64;
+ // pread64 on emscripten actually takes a 32 bit offset
+ if let Ok(o) = offset.try_into() {
+ cvt(pread64(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pread >2GB"))
+ }
+ }
+
+ #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
+ unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ #[cfg(target_os = "linux")]
+ use libc::pread64;
+ #[cfg(not(target_os = "linux"))]
use libc::pread as pread64;
cvt(pread64(fd, buf, count, offset))
}
@@ -94,9 +116,31 @@ impl FileDesc {
}
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ #[cfg(target_os = "android")]
+ use super::android::cvt_pwrite64;
+
+ #[cfg(target_os = "emscripten")]
unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
-> io::Result<isize>
{
+ use convert::TryInto;
+ use libc::pwrite64;
+ // pwrite64 on emscripten actually takes a 32 bit offset
+ if let Ok(o) = offset.try_into() {
+ cvt(pwrite64(fd, buf, count, o))
+ } else {
+ Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "cannot pwrite >2GB"))
+ }
+ }
+
+ #[cfg(not(any(target_os = "android", target_os = "emscripten")))]
+ unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64)
+ -> io::Result<isize>
+ {
+ #[cfg(target_os = "linux")]
+ use libc::pwrite64;
+ #[cfg(not(target_os = "linux"))]
use libc::pwrite as pwrite64;
cvt(pwrite64(fd, buf, count, offset))
}
@@ -110,11 +154,52 @@ impl FileDesc {
}
}
- // We don't have fork/exec on the 3DS, so this shouldn't need to do anything
+ #[cfg(target_os = "linux")]
+ pub fn get_cloexec(&self) -> io::Result<bool> {
+ unsafe {
+ Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0)
+ }
+ }
+
+ #[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(())
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ unsafe {
+ let v = nonblocking as c_int;
+ cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
+ Ok(())
+ }
}
+ #[cfg(not(target_os = "linux"))]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
unsafe {
let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
diff --git a/ctr-std/src/sys/unix/fs.rs b/ctr-std/src/sys/unix/fs.rs
index 9dba863..7743403 100644
--- a/ctr-std/src/sys/unix/fs.rs
+++ b/ctr-std/src/sys/unix/fs.rs
@@ -8,8 +8,6 @@
// 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};
@@ -25,8 +23,24 @@ use sys::time::SystemTime;
use sys::{cvt, cvt_r};
use sys_common::{AsInner, FromInner};
+#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
+use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64};
+#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
+use libc::{fstatat, dirfd};
+#[cfg(target_os = "android")]
+use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, lseek64,
+ dirent as dirent64, open as open64};
+#[cfg(not(any(target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "android")))]
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};
+#[cfg(not(any(target_os = "linux",
+ target_os = "emscripten",
+ target_os = "solaris",
+ target_os = "l4re",
+ target_os = "fuchsia")))]
use libc::{readdir_r as readdir64_r};
pub struct File(FileDesc);
@@ -36,11 +50,15 @@ pub struct FileAttr {
stat: stat64,
}
-pub struct ReadDir {
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
dirp: Dir,
- root: Arc<PathBuf>,
+ root: PathBuf,
}
+#[derive(Clone)]
+pub struct ReadDir(Arc<InnerReadDir>);
+
struct Dir(*mut libc::DIR);
unsafe impl Send for Dir {}
@@ -48,8 +66,8 @@ unsafe impl Sync for Dir {}
pub struct DirEntry {
entry: dirent64,
- root: Arc<PathBuf>,
- // We need to store an owned copy of the directory name
+ dir: ReadDir,
+ // We need to store an owned copy of the entry 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.
@@ -91,19 +109,63 @@ impl FileAttr {
}
}
+#[cfg(target_os = "netbsd")]
impl FileAttr {
pub fn modified(&self) -> io::Result<SystemTime> {
- Err(io::Error::new(io::ErrorKind::Other,
- "modification time is not available on this platform \
- currently"))
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_mtime as libc::time_t,
+ tv_nsec: self.stat.st_mtimensec as libc::c_long,
+ }))
}
pub fn accessed(&self) -> io::Result<SystemTime> {
- Err(io::Error::new(io::ErrorKind::Other,
- "access time is not available on this platform \
- currently"))
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_atime as libc::time_t,
+ tv_nsec: self.stat.st_atimensec as libc::c_long,
+ }))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_birthtime as libc::time_t,
+ tv_nsec: self.stat.st_birthtimensec as libc::c_long,
+ }))
+ }
+}
+
+#[cfg(not(target_os = "netbsd"))]
+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: self.stat.st_mtime_nsec as _,
+ }))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_atime as libc::time_t,
+ tv_nsec: self.stat.st_atime_nsec as _,
+ }))
}
+ #[cfg(any(target_os = "bitrig",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "ios"))]
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from(libc::timespec {
+ tv_sec: self.stat.st_birthtime as libc::time_t,
+ tv_nsec: self.stat.st_birthtime_nsec as libc::c_long,
+ }))
+ }
+
+ #[cfg(not(any(target_os = "bitrig",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "ios")))]
pub fn created(&self) -> io::Result<SystemTime> {
Err(io::Error::new(io::ErrorKind::Other,
"creation time is not available on this platform \
@@ -151,7 +213,7 @@ impl fmt::Debug for ReadDir {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
// Thus the result will be e g 'ReadDir("/home")'
- fmt::Debug::fmt(&*self.root, f)
+ fmt::Debug::fmt(&*self.0.root, f)
}
}
@@ -167,7 +229,7 @@ impl Iterator for ReadDir {
// 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);
+ let entry_ptr = libc::readdir(self.0.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.
@@ -177,14 +239,14 @@ impl Iterator for ReadDir {
}
}
- let name = (*entry_ptr).d_name.as_ptr() as *const _;
+ 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()
+ dir: self.clone()
};
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
return Some(Ok(ret))
@@ -198,11 +260,11 @@ impl Iterator for ReadDir {
unsafe {
let mut ret = DirEntry {
entry: mem::zeroed(),
- root: self.root.clone()
+ dir: self.clone(),
};
let mut entry_ptr = ptr::null_mut();
loop {
- if readdir64_r(self.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
+ if readdir64_r(self.0.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
return Some(Err(Error::last_os_error()))
}
if entry_ptr.is_null() {
@@ -225,13 +287,27 @@ impl Drop for Dir {
impl DirEntry {
pub fn path(&self) -> PathBuf {
- self.root.join(OsStr::from_bytes(self.name_bytes()))
+ self.dir.0.root.join(OsStr::from_bytes(self.name_bytes()))
}
pub fn file_name(&self) -> OsString {
OsStr::from_bytes(self.name_bytes()).to_os_string()
}
+ #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ let fd = cvt(unsafe {dirfd(self.dir.0.dirp.0)})?;
+ let mut stat: stat64 = unsafe { mem::zeroed() };
+ cvt(unsafe {
+ fstatat(fd,
+ self.entry.d_name.as_ptr(),
+ &mut stat as *mut _ as *mut _,
+ libc::AT_SYMLINK_NOFOLLOW)
+ })?;
+ Ok(FileAttr { stat: stat })
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
pub fn metadata(&self) -> io::Result<FileAttr> {
lstat(&self.path())
}
@@ -255,15 +331,56 @@ impl DirEntry {
}
}
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "android",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "l4re",
+ target_os = "fuchsia"))]
pub fn ino(&self) -> u64 {
self.entry.d_ino as u64
}
+ #[cfg(any(target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "dragonfly"))]
+ pub fn ino(&self) -> u64 {
+ self.entry.d_fileno as u64
+ }
+
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "bitrig"))]
fn name_bytes(&self) -> &[u8] {
unsafe {
- CStr::from_ptr(self.entry.d_name.as_ptr() as *const _).to_bytes()
+ ::slice::from_raw_parts(self.entry.d_name.as_ptr() as *const u8,
+ self.entry.d_namlen as usize)
}
}
+ #[cfg(any(target_os = "android",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "haiku"))]
+ fn name_bytes(&self) -> &[u8] {
+ unsafe {
+ CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes()
+ }
+ }
+ #[cfg(any(target_os = "solaris",
+ target_os = "fuchsia"))]
+ fn name_bytes(&self) -> &[u8] {
+ &*self.name
+ }
}
impl OpenOptions {
@@ -338,21 +455,54 @@ impl File {
opts.get_creation_mode()? |
(opts.custom_flags as c_int & !libc::O_ACCMODE);
let fd = cvt_r(|| unsafe {
- open64(path.as_ptr() as *const _, flags, opts.mode as c_int)
+ open64(path.as_ptr(), flags, opts.mode as c_int)
})?;
let fd = FileDesc::new(fd);
// Currently the standard library supports Linux 2.6.18 which did not
// have the O_CLOEXEC flag (passed above). If we're running on an older
- // Linux kernel then the flag is just ignored by the OS, so we continue
- // to explicitly ask for a CLOEXEC fd here.
+ // Linux kernel then the flag is just ignored by the OS. After we open
+ // the first file, we check whether it has CLOEXEC set. If it doesn't,
+ // we will explicitly ask for a CLOEXEC fd for every further file we
+ // open, if it does, we will skip that step.
//
// 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()?;
+ #[cfg(target_os = "linux")]
+ fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> {
+ use sync::atomic::{AtomicUsize, Ordering};
+
+ const OPEN_CLOEXEC_UNKNOWN: usize = 0;
+ const OPEN_CLOEXEC_SUPPORTED: usize = 1;
+ const OPEN_CLOEXEC_NOTSUPPORTED: usize = 2;
+ static OPEN_CLOEXEC: AtomicUsize = AtomicUsize::new(OPEN_CLOEXEC_UNKNOWN);
+
+ let need_to_set;
+ match OPEN_CLOEXEC.load(Ordering::Relaxed) {
+ OPEN_CLOEXEC_UNKNOWN => {
+ need_to_set = !fd.get_cloexec()?;
+ OPEN_CLOEXEC.store(if need_to_set {
+ OPEN_CLOEXEC_NOTSUPPORTED
+ } else {
+ OPEN_CLOEXEC_SUPPORTED
+ }, Ordering::Relaxed);
+ },
+ OPEN_CLOEXEC_SUPPORTED => need_to_set = false,
+ OPEN_CLOEXEC_NOTSUPPORTED => need_to_set = true,
+ _ => unreachable!(),
+ }
+ if need_to_set {
+ fd.set_cloexec()?;
+ }
+ Ok(())
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ fn ensure_cloexec(_: &FileDesc) -> io::Result<()> {
+ Ok(())
}
+ ensure_cloexec(&fd)?;
Ok(File(fd))
}
@@ -373,6 +523,15 @@ impl File {
cvt_r(|| unsafe { os_datasync(self.0.raw()) })?;
return Ok(());
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ unsafe fn os_datasync(fd: c_int) -> c_int {
+ libc::fcntl(fd, libc::F_FULLFSYNC)
+ }
+ #[cfg(target_os = "linux")]
+ unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) }
+ #[cfg(not(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "linux")))]
unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) }
}
@@ -439,7 +598,7 @@ impl DirBuilder {
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
let p = cstr(p)?;
- cvt(unsafe { libc::mkdir(p.as_ptr() as *const _, self.mode) })?;
+ cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
Ok(())
}
@@ -475,7 +634,7 @@ impl fmt::Debug for File {
// 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() as *const _) };
+ let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
if n == -1 {
return None;
}
@@ -525,40 +684,41 @@ impl fmt::Debug for File {
}
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
- let root = Arc::new(p.to_path_buf());
+ let root = p.to_path_buf();
let p = cstr(p)?;
unsafe {
- let ptr = libc::opendir(p.as_ptr() as *const _);
+ let ptr = libc::opendir(p.as_ptr());
if ptr.is_null() {
Err(Error::last_os_error())
} else {
- Ok(ReadDir { dirp: Dir(ptr), root: root })
+ let inner = InnerReadDir { dirp: Dir(ptr), root };
+ Ok(ReadDir(Arc::new(inner)))
}
}
}
pub fn unlink(p: &Path) -> io::Result<()> {
let p = cstr(p)?;
- cvt(unsafe { libc::unlink(p.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::unlink(p.as_ptr()) })?;
Ok(())
}
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
let old = cstr(old)?;
let new = cstr(new)?;
- cvt(unsafe { libc::rename(old.as_ptr() as *const _, new.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
Ok(())
}
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
let p = cstr(p)?;
- cvt_r(|| unsafe { libc::chmod(p.as_ptr() as *const _, perm.mode) })?;
+ cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
Ok(())
}
pub fn rmdir(p: &Path) -> io::Result<()> {
let p = cstr(p)?;
- cvt(unsafe { libc::rmdir(p.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
Ok(())
}
@@ -585,7 +745,7 @@ fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
let c_path = cstr(p)?;
- let p = c_path.as_ptr() as *const _;
+ let p = c_path.as_ptr();
let mut buf = Vec::with_capacity(256);
@@ -612,14 +772,14 @@ pub fn readlink(p: &Path) -> io::Result<PathBuf> {
pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
let src = cstr(src)?;
let dst = cstr(dst)?;
- cvt(unsafe { libc::symlink(src.as_ptr() as *const _, dst.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?;
Ok(())
}
pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
let src = cstr(src)?;
let dst = cstr(dst)?;
- cvt(unsafe { libc::link(src.as_ptr() as *const _, dst.as_ptr() as *const _) })?;
+ cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
Ok(())
}
@@ -627,7 +787,7 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
let p = cstr(p)?;
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt(unsafe {
- stat64(p.as_ptr() as *const _, &mut stat as *mut _ as *mut _)
+ stat64(p.as_ptr(), &mut stat as *mut _ as *mut _)
})?;
Ok(FileAttr { stat: stat })
}
@@ -636,7 +796,7 @@ pub fn lstat(p: &Path) -> io::Result<FileAttr> {
let p = cstr(p)?;
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt(unsafe {
- lstat64(p.as_ptr() as *const _, &mut stat as *mut _ as *mut _)
+ lstat64(p.as_ptr(), &mut stat as *mut _ as *mut _)
})?;
Ok(FileAttr { stat: stat })
}
@@ -645,18 +805,19 @@ pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
let path = CString::new(p.as_os_str().as_bytes())?;
let buf;
unsafe {
- let r = libc::realpath(path.as_ptr() as *const _, ptr::null_mut());
+ let r = libc::realpath(path.as_ptr(), ptr::null_mut());
if r.is_null() {
return Err(io::Error::last_os_error())
}
- buf = CStr::from_ptr(r as *const _).to_bytes().to_vec();
+ buf = CStr::from_ptr(r).to_bytes().to_vec();
libc::free(r as *mut _);
}
Ok(PathBuf::from(OsString::from_vec(buf)))
}
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
- use fs::{File, set_permissions};
+ use fs::File;
if !from.is_file() {
return Err(Error::new(ErrorKind::InvalidInput,
"the source path is not an existing regular file"))
@@ -667,6 +828,100 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
let perm = reader.metadata()?.permissions();
let ret = io::copy(&mut reader, &mut writer)?;
- set_permissions(to, perm)?;
+ writer.set_permissions(perm)?;
Ok(ret)
}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use cmp;
+ use fs::File;
+ use sync::atomic::{AtomicBool, Ordering};
+
+ // Kernel prior to 4.5 don't have copy_file_range
+ // We store the availability in a global to avoid unneccessary syscalls
+ static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true);
+
+ unsafe fn copy_file_range(
+ fd_in: libc::c_int,
+ off_in: *mut libc::loff_t,
+ fd_out: libc::c_int,
+ off_out: *mut libc::loff_t,
+ len: libc::size_t,
+ flags: libc::c_uint,
+ ) -> libc::c_long {
+ libc::syscall(
+ libc::SYS_copy_file_range,
+ fd_in,
+ off_in,
+ fd_out,
+ off_out,
+ len,
+ flags,
+ )
+ }
+
+ if !from.is_file() {
+ return Err(Error::new(ErrorKind::InvalidInput,
+ "the source path is not an existing regular file"))
+ }
+
+ let mut reader = File::open(from)?;
+ let mut writer = File::create(to)?;
+ let (perm, len) = {
+ let metadata = reader.metadata()?;
+ (metadata.permissions(), metadata.size())
+ };
+
+ let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
+ let mut written = 0u64;
+ while written < len {
+ let copy_result = if has_copy_file_range {
+ let bytes_to_copy = cmp::min(len - written, usize::max_value() as u64) as usize;
+ let copy_result = unsafe {
+ // We actually don't have to adjust the offsets,
+ // because copy_file_range adjusts the file offset automatically
+ cvt(copy_file_range(reader.as_raw_fd(),
+ ptr::null_mut(),
+ writer.as_raw_fd(),
+ ptr::null_mut(),
+ bytes_to_copy,
+ 0)
+ )
+ };
+ if let Err(ref copy_err) = copy_result {
+ match copy_err.raw_os_error() {
+ Some(libc::ENOSYS) | Some(libc::EPERM) => {
+ HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
+ }
+ _ => {}
+ }
+ }
+ copy_result
+ } else {
+ Err(io::Error::from_raw_os_error(libc::ENOSYS))
+ };
+ match copy_result {
+ Ok(ret) => written += ret as u64,
+ Err(err) => {
+ match err.raw_os_error() {
+ Some(os_err) if os_err == libc::ENOSYS
+ || os_err == libc::EXDEV
+ || os_err == libc::EPERM => {
+ // Try fallback io::copy if either:
+ // - Kernel version is < 4.5 (ENOSYS)
+ // - Files are mounted on different fs (EXDEV)
+ // - copy_file_range is disallowed, for example by seccomp (EPERM)
+ assert_eq!(written, 0);
+ let ret = io::copy(&mut reader, &mut writer)?;
+ writer.set_permissions(perm)?;
+ return Ok(ret)
+ },
+ _ => return Err(err),
+ }
+ }
+ }
+ }
+ writer.set_permissions(perm)?;
+ Ok(written)
+}
diff --git a/ctr-std/src/sys/unix/l4re.rs b/ctr-std/src/sys/unix/l4re.rs
new file mode 100644
index 0000000..2121848
--- /dev/null
+++ b/ctr-std/src/sys/unix/l4re.rs
@@ -0,0 +1,441 @@
+// Copyright 2016-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.
+
+macro_rules! unimpl {
+ () => (return Err(io::Error::new(io::ErrorKind::Other, "No networking available on L4Re."));)
+}
+
+pub mod net {
+ #![allow(warnings)]
+ use fmt;
+ use io;
+ use libc;
+ use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
+ use sys_common::{AsInner, FromInner, IntoInner};
+ use sys::fd::FileDesc;
+ use time::Duration;
+
+
+ pub extern crate libc as netc;
+
+ pub struct Socket(FileDesc);
+ impl Socket {
+ pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> {
+ unimpl!();
+ }
+
+ pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn accept(&self, _: *mut libc::sockaddr, _: *mut libc::socklen_t)
+ -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<Socket> {
+ unimpl!();
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn set_timeout(&self, _: Option<Duration>, _: libc::c_int) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn timeout(&self, _: libc::c_int) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+ }
+
+ impl AsInner<libc::c_int> for Socket {
+ fn as_inner(&self) -> &libc::c_int { self.0.as_inner() }
+ }
+
+ impl FromInner<libc::c_int> for Socket {
+ fn from_inner(fd: libc::c_int) -> Socket { Socket(FileDesc::new(fd)) }
+ }
+
+ impl IntoInner<libc::c_int> for Socket {
+ fn into_inner(self) -> libc::c_int { self.0.into_raw() }
+ }
+
+ pub struct TcpStream {
+ inner: Socket,
+ }
+
+ impl TcpStream {
+ pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> {
+ unimpl!();
+ }
+
+ pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket { &self.inner }
+
+ pub fn into_socket(self) -> Socket { self.inner }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ unimpl!();
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+ }
+
+ impl FromInner<Socket> for TcpStream {
+ fn from_inner(socket: Socket) -> TcpStream {
+ TcpStream { inner: socket }
+ }
+ }
+
+ impl fmt::Debug for TcpStream {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "No networking support available on L4Re")
+ }
+ }
+
+ pub struct TcpListener {
+ inner: Socket,
+ }
+
+ impl TcpListener {
+ pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket { &self.inner }
+
+ pub fn into_socket(self) -> Socket { self.inner }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ unimpl!();
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+ }
+
+ impl FromInner<Socket> for TcpListener {
+ fn from_inner(socket: Socket) -> TcpListener {
+ TcpListener { inner: socket }
+ }
+ }
+
+ impl fmt::Debug for TcpListener {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "No networking support available on L4Re.")
+ }
+ }
+
+ pub struct UdpSocket {
+ inner: Socket,
+ }
+
+ impl UdpSocket {
+ pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> {
+ unimpl!();
+ }
+
+ pub fn socket(&self) -> &Socket { &self.inner }
+
+ pub fn into_socket(self) -> Socket { self.inner }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unimpl!();
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unimpl!();
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ unimpl!();
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unimpl!();
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ unimpl!();
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32)
+ -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unimpl!();
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unimpl!();
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unimpl!();
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ unimpl!();
+ }
+
+ pub fn connect(&self, _: &SocketAddr) -> io::Result<()> {
+ unimpl!();
+ }
+ }
+
+ impl FromInner<Socket> for UdpSocket {
+ fn from_inner(socket: Socket) -> UdpSocket {
+ UdpSocket { inner: socket }
+ }
+ }
+
+ impl fmt::Debug for UdpSocket {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "No networking support on L4Re available.")
+ }
+ }
+
+ pub struct LookupHost {
+ original: *mut libc::addrinfo,
+ cur: *mut libc::addrinfo,
+ }
+
+ impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ None
+ }
+ }
+
+ unsafe impl Sync for LookupHost {}
+ unsafe impl Send for LookupHost {}
+
+ pub fn lookup_host(_: &str) -> io::Result<LookupHost> {
+ unimpl!();
+ }
+}
+
diff --git a/ctr-std/src/sys/unix/memchr.rs b/ctr-std/src/sys/unix/memchr.rs
index 71d9111..f49adc2 100644
--- a/ctr-std/src/sys/unix/memchr.rs
+++ b/ctr-std/src/sys/unix/memchr.rs
@@ -1,13 +1,3 @@
-// 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.
@@ -38,6 +28,27 @@ pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
}
pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
+
+ #[cfg(target_os = "linux")]
+ fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
+ use libc;
+
+ // GNU's memrchr() will - unlike memchr() - error if haystack is empty.
+ if haystack.is_empty() {return None}
+ let p = unsafe {
+ libc::memrchr(
+ haystack.as_ptr() as *const libc::c_void,
+ needle as libc::c_int,
+ haystack.len())
+ };
+ if p.is_null() {
+ None
+ } else {
+ Some(p as usize - (haystack.as_ptr() as usize))
+ }
+ }
+
+ #[cfg(not(target_os = "linux"))]
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
::core::slice::memchr::memrchr(needle, haystack)
}
diff --git a/ctr-std/src/sys/unix/mod.rs b/ctr-std/src/sys/unix/mod.rs
index 829f6f0..c1298e5 100644
--- a/ctr-std/src/sys/unix/mod.rs
+++ b/ctr-std/src/sys/unix/mod.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,15 +8,35 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-//! System bindings for the Nintendo 3DS
+#![allow(missing_docs, bad_style)]
use io::{self, ErrorKind};
use libc;
+#[cfg(any(dox, target_os = "linux"))] pub use os::linux as platform;
+
+#[cfg(all(not(dox), target_os = "android"))] pub use os::android as platform;
+#[cfg(all(not(dox), target_os = "bitrig"))] pub use os::bitrig as platform;
+#[cfg(all(not(dox), target_os = "dragonfly"))] pub use os::dragonfly as platform;
+#[cfg(all(not(dox), target_os = "freebsd"))] pub use os::freebsd as platform;
+#[cfg(all(not(dox), target_os = "haiku"))] pub use os::haiku as platform;
+#[cfg(all(not(dox), target_os = "ios"))] pub use os::ios as platform;
+#[cfg(all(not(dox), target_os = "macos"))] pub use os::macos as platform;
+#[cfg(all(not(dox), target_os = "netbsd"))] pub use os::netbsd as platform;
+#[cfg(all(not(dox), target_os = "openbsd"))] pub use os::openbsd as platform;
+#[cfg(all(not(dox), target_os = "solaris"))] pub use os::solaris as platform;
+#[cfg(all(not(dox), target_os = "emscripten"))] pub use os::emscripten as platform;
+#[cfg(all(not(dox), target_os = "fuchsia"))] pub use os::fuchsia as platform;
+#[cfg(all(not(dox), target_os = "l4re"))] pub use os::linux as platform;
+
pub use self::rand::hashmap_random_keys;
pub use libc::strlen;
+#[macro_use]
+pub mod weak;
+
pub mod args;
+pub mod android;
#[cfg(feature = "backtrace")]
pub mod backtrace;
pub mod cmath;
@@ -28,7 +48,12 @@ pub mod fd;
pub mod fs;
pub mod memchr;
pub mod mutex;
+#[cfg(not(target_os = "l4re"))]
pub mod net;
+#[cfg(target_os = "l4re")]
+mod l4re;
+#[cfg(target_os = "l4re")]
+pub use self::l4re::net;
pub mod os;
pub mod os_str;
pub mod path;
@@ -44,16 +69,29 @@ 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();
+ }
-pub fn unsupported<T>() -> io::Result<T> {
- Err(unsupported_err())
+ #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
+ unsafe fn reset_sigpipe() {
+ assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
+ }
+ #[cfg(any(target_os = "emscripten", target_os = "fuchsia"))]
+ unsafe fn reset_sigpipe() {}
}
-pub fn unsupported_err() -> io::Error {
- io::Error::new(io::ErrorKind::Other,
- "operation not supported on 3DS yet")
-}
+#[cfg(target_os = "android")]
+pub use sys::android::signal;
+#[cfg(not(target_os = "android"))]
+pub use libc::signal;
pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as libc::c_int {
@@ -81,11 +119,6 @@ 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;
diff --git a/ctr-std/src/sys/unix/mutex.rs b/ctr-std/src/sys/unix/mutex.rs
index 95c74a4..52cf3f9 100644
--- a/ctr-std/src/sys/unix/mutex.rs
+++ b/ctr-std/src/sys/unix/mutex.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -9,54 +9,93 @@
// except according to those terms.
use cell::UnsafeCell;
+use libc;
use mem;
-pub struct Mutex {
- inner: UnsafeCell<::libctru::LightLock>,
-}
+pub struct Mutex { inner: UnsafeCell<libc::pthread_mutex_t> }
#[inline]
-pub unsafe fn raw(m: &Mutex) -> *mut ::libctru::LightLock {
+pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
m.inner.get()
}
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) }
+ // Might be moved and address is changing it is better to avoid
+ // initialization of potentially opaque OS data before it landed
+ Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
}
-
#[inline]
pub unsafe fn init(&mut self) {
- ::libctru::LightLock_Init(self.inner.get());
+ // Issue #33770
+ //
+ // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
+ // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you
+ // try to re-lock it from the same thread when you already hold a lock.
+ //
+ // In practice, glibc takes advantage of this undefined behavior to
+ // implement hardware lock elision, which uses hardware transactional
+ // memory to avoid acquiring the lock. While a transaction is in
+ // progress, the lock appears to be unlocked. This isn't a problem for
+ // other threads since the transactional memory will abort if a conflict
+ // is detected, however no abort is generated if re-locking from the
+ // same thread.
+ //
+ // Since locking the same mutex twice will result in two aliasing &mut
+ // references, we instead create the mutex with type
+ // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to
+ // re-lock it from the same thread, thus avoiding undefined behavior.
+ //
+ // We can't do anything for StaticMutex, but that type is deprecated
+ // anyways.
+ let mut attr: libc::pthread_mutexattr_t = mem::uninitialized();
+ let r = libc::pthread_mutexattr_init(&mut attr);
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_mutexattr_settype(&mut attr, libc::PTHREAD_MUTEX_NORMAL);
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_mutex_init(self.inner.get(), &attr);
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_mutexattr_destroy(&mut attr);
+ debug_assert_eq!(r, 0);
}
-
#[inline]
pub unsafe fn lock(&self) {
- ::libctru::LightLock_Lock(self.inner.get());
+ let r = libc::pthread_mutex_lock(self.inner.get());
+ debug_assert_eq!(r, 0);
}
-
#[inline]
pub unsafe fn unlock(&self) {
- ::libctru::LightLock_Unlock(self.inner.get());
+ let r = libc::pthread_mutex_unlock(self.inner.get());
+ debug_assert_eq!(r, 0);
}
-
#[inline]
pub unsafe fn try_lock(&self) -> bool {
- match ::libctru::LightLock_TryLock(self.inner.get()) {
- 0 => false,
- _ => true,
- }
+ libc::pthread_mutex_trylock(self.inner.get()) == 0
}
-
#[inline]
+ #[cfg(not(target_os = "dragonfly"))]
pub unsafe fn destroy(&self) {
+ let r = libc::pthread_mutex_destroy(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
+ #[inline]
+ #[cfg(target_os = "dragonfly")]
+ pub unsafe fn destroy(&self) {
+ use libc;
+ let r = libc::pthread_mutex_destroy(self.inner.get());
+ // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
+ // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
+ // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
+ // this behaviour no longer occurs.
+ debug_assert!(r == 0 || r == libc::EINVAL);
}
}
-pub struct ReentrantMutex { inner: UnsafeCell<::libctru::RecursiveLock> }
+pub struct ReentrantMutex { inner: UnsafeCell<libc::pthread_mutex_t> }
unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}
@@ -67,24 +106,35 @@ impl ReentrantMutex {
}
pub unsafe fn init(&mut self) {
- ::libctru::RecursiveLock_Init(self.inner.get());
+ let mut attr: libc::pthread_mutexattr_t = mem::uninitialized();
+ let result = libc::pthread_mutexattr_init(&mut attr as *mut _);
+ debug_assert_eq!(result, 0);
+ let result = libc::pthread_mutexattr_settype(&mut attr as *mut _,
+ libc::PTHREAD_MUTEX_RECURSIVE);
+ debug_assert_eq!(result, 0);
+ let result = libc::pthread_mutex_init(self.inner.get(), &attr as *const _);
+ debug_assert_eq!(result, 0);
+ let result = libc::pthread_mutexattr_destroy(&mut attr as *mut _);
+ debug_assert_eq!(result, 0);
}
pub unsafe fn lock(&self) {
- ::libctru::RecursiveLock_Lock(self.inner.get());
+ let result = libc::pthread_mutex_lock(self.inner.get());
+ debug_assert_eq!(result, 0);
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
- match ::libctru::RecursiveLock_TryLock(self.inner.get()) {
- 0 => false,
- _ => true,
- }
+ libc::pthread_mutex_trylock(self.inner.get()) == 0
}
pub unsafe fn unlock(&self) {
- ::libctru::RecursiveLock_Unlock(self.inner.get());
+ let result = libc::pthread_mutex_unlock(self.inner.get());
+ debug_assert_eq!(result, 0);
}
- pub unsafe fn destroy(&self) {}
+ pub unsafe fn destroy(&self) {
+ let result = libc::pthread_mutex_destroy(self.inner.get());
+ debug_assert_eq!(result, 0);
+ }
}
diff --git a/ctr-std/src/sys/unix/net.rs b/ctr-std/src/sys/unix/net.rs
index 94f757d..04d9f0b 100644
--- a/ctr-std/src/sys/unix/net.rs
+++ b/ctr-std/src/sys/unix/net.rs
@@ -8,11 +8,9 @@
// 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 libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK};
use mem;
use net::{SocketAddr, Shutdown};
use str;
@@ -25,7 +23,6 @@ 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
@@ -54,12 +51,16 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> {
if err == 0 {
return Ok(())
}
+
+ // We may need to trigger a glibc workaround. See on_resolver_failure() for details.
+ on_resolver_failure();
+
if err == EAI_SYSTEM {
return Err(io::Error::last_os_error())
}
let detail = unsafe {
- str::from_utf8(CStr::from_ptr(libc::gai_strerror(err) as *const _).to_bytes()).unwrap()
+ str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap()
.to_owned()
};
Err(io::Error::new(io::ErrorKind::Other,
@@ -143,7 +144,7 @@ impl Socket {
let mut pollfd = libc::pollfd {
fd: self.0.raw(),
- events: 0x10, //libc::POLLOUT; value in the `libc` crate is currently incorrect
+ events: libc::POLLOUT,
revents: 0,
};
@@ -181,9 +182,7 @@ impl Socket {
_ => {
// linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
// for POLLHUP rather than read readiness
- //
- // libc::POLLHUP should be 0x4. the `libc` crate incorrectly lists it as 0x10
- if pollfd.revents & 0x4 != 0 {
+ 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")
@@ -199,6 +198,26 @@ impl Socket {
pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t)
-> io::Result<Socket> {
+ // Unfortunately the only known way right now to accept a socket and
+ // atomically set the CLOEXEC flag is to use the `accept4` syscall on
+ // Linux. This was added in 2.6.28, however, and because we support
+ // 2.6.18 we must detect this support dynamically.
+ if cfg!(target_os = "linux") {
+ weak! {
+ fn accept4(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int
+ }
+ if let Some(accept) = accept4.get() {
+ let res = cvt_r(|| unsafe {
+ accept(self.0.raw(), storage, len, SOCK_CLOEXEC)
+ });
+ match res {
+ Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
+ Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
+ Err(e) => return Err(e),
+ }
+ }
+ }
+
let fd = cvt_r(|| unsafe {
libc::accept(self.0.raw(), storage, len)
})?;
@@ -320,7 +339,8 @@ impl Socket {
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
- self.0.set_nonblocking(nonblocking)
+ let mut nonblocking = nonblocking as libc::c_int;
+ cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(|_| ())
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
@@ -362,36 +382,21 @@ impl IntoInner<c_int> for Socket {
// against glibc version < 2.26. (That is, when we both know its needed and
// believe it's thread-safe).
#[cfg(target_env = "gnu")]
-pub fn res_init_if_glibc_before_2_26() -> io::Result<()> {
+fn on_resolver_failure() {
+ use sys;
+
// 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());
- }
- }
+ if let Some(version) = sys::os::glibc_version() {
+ if version < (2, 26) {
+ unsafe { libc::res_init() };
}
}
- 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(not(target_env = "gnu"))]
+fn on_resolver_failure() {}
-#[cfg(test)]
+#[cfg(all(test, taget_env = "gnu"))]
mod test {
use super::*;
diff --git a/ctr-std/src/sys/unix/os.rs b/ctr-std/src/sys/unix/os.rs
index 3a944dc..4c86fdd 100644
--- a/ctr-std/src/sys/unix/os.rs
+++ b/ctr-std/src/sys/unix/os.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,35 +8,86 @@
// 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};
+use libc::{self, c_int, c_char, c_void};
+use marker::PhantomData;
+use mem;
+use memchr;
use path::{self, PathBuf};
+use ptr;
use slice;
use str;
-use sys::{unsupported, Void};
-use sys::unix::ext::ffi::{OsStrExt, OsStringExt};
+use sys_common::mutex::Mutex;
+use sys::cvt;
+use sys::fd;
+use vec;
const TMPBUF_SZ: usize = 128;
+static ENV_LOCK: Mutex = Mutex::new();
+
-extern "C" {
- fn __errno() -> *mut c_int;
+extern {
+ #[cfg(not(target_os = "dragonfly"))]
+ #[cfg_attr(any(target_os = "linux",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "l4re"),
+ link_name = "__errno_location")]
+ #[cfg_attr(any(target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "android",
+ target_env = "newlib"),
+ link_name = "__errno")]
+ #[cfg_attr(target_os = "solaris", link_name = "___errno")]
+ #[cfg_attr(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd"),
+ link_name = "__error")]
+ #[cfg_attr(target_os = "haiku", link_name = "_errnop")]
+ fn errno_location() -> *mut c_int;
}
/// Returns the platform-specific value of errno
+#[cfg(not(target_os = "dragonfly"))]
pub fn errno() -> i32 {
unsafe {
- (*__errno()) as i32
+ (*errno_location()) as i32
+ }
+}
+
+/// Sets the platform-specific value of errno
+#[cfg(any(target_os = "solaris", target_os = "fuchsia"))] // only needed for readdir so far
+pub fn set_errno(e: i32) {
+ unsafe {
+ *errno_location() = e as c_int
}
}
+#[cfg(target_os = "dragonfly")]
+pub fn errno() -> i32 {
+ extern {
+ #[thread_local]
+ static errno: c_int;
+ }
+
+ unsafe { errno as i32 }
+}
+
/// Gets a detailed string description for the given error number.
pub fn error_string(errno: i32) -> String {
extern {
- #[cfg_attr(any(target_os = "linux", target_env = "newlib"),
+ #[cfg_attr(any(target_os = "linux", target_env = "newlib"),
link_name = "__xpg_strerror_r")]
fn strerror_r(errnum: c_int, buf: *mut c_char,
buflen: libc::size_t) -> c_int;
@@ -61,7 +112,7 @@ pub fn getcwd() -> io::Result<PathBuf> {
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 _).to_bytes().len();
+ 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)));
@@ -78,14 +129,14 @@ pub fn getcwd() -> io::Result<PathBuf> {
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() as *const _) == (0 as c_int) {
+ match libc::chdir(p.as_ptr()) == (0 as c_int) {
true => Ok(()),
false => Err(io::Error::last_os_error()),
}
@@ -145,41 +196,343 @@ impl StdError for JoinPathsError {
fn description(&self) -> &str { "failed to join paths" }
}
+#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub fn current_exe() -> io::Result<PathBuf> {
- unsupported()
+ unsafe {
+ let mut mib = [libc::CTL_KERN as c_int,
+ libc::KERN_PROC as c_int,
+ libc::KERN_PROC_PATHNAME as c_int,
+ -1 as c_int];
+ let mut sz = 0;
+ cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
+ ptr::null_mut(), &mut sz, ptr::null_mut(), 0))?;
+ if sz == 0 {
+ return Err(io::Error::last_os_error())
+ }
+ let mut v: Vec<u8> = Vec::with_capacity(sz);
+ cvt(libc::sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
+ v.as_mut_ptr() as *mut libc::c_void, &mut sz,
+ ptr::null_mut(), 0))?;
+ if sz == 0 {
+ return Err(io::Error::last_os_error());
+ }
+ v.set_len(sz - 1); // chop off trailing NUL
+ Ok(PathBuf::from(OsString::from_vec(v)))
+ }
}
-pub struct Env(Void);
+#[cfg(target_os = "netbsd")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ fn sysctl() -> io::Result<PathBuf> {
+ unsafe {
+ let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME];
+ let mut path_len: usize = 0;
+ cvt(libc::sysctl(mib.as_ptr(), mib.len() as ::libc::c_uint,
+ ptr::null_mut(), &mut path_len,
+ ptr::null(), 0))?;
+ if path_len <= 1 {
+ return Err(io::Error::new(io::ErrorKind::Other,
+ "KERN_PROC_PATHNAME sysctl returned zero-length string"))
+ }
+ let mut path: Vec<u8> = Vec::with_capacity(path_len);
+ cvt(libc::sysctl(mib.as_ptr(), mib.len() as ::libc::c_uint,
+ path.as_ptr() as *mut libc::c_void, &mut path_len,
+ ptr::null(), 0))?;
+ path.set_len(path_len - 1); // chop off NUL
+ Ok(PathBuf::from(OsString::from_vec(path)))
+ }
+ }
+ fn procfs() -> io::Result<PathBuf> {
+ let curproc_exe = path::Path::new("/proc/curproc/exe");
+ if curproc_exe.is_file() {
+ return ::fs::read_link(curproc_exe);
+ }
+ Err(io::Error::new(io::ErrorKind::Other,
+ "/proc/curproc/exe doesn't point to regular file."))
+ }
+ sysctl().or_else(|_| procfs())
+}
+
+#[cfg(any(target_os = "bitrig", target_os = "openbsd"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsafe {
+ let mut mib = [libc::CTL_KERN,
+ libc::KERN_PROC_ARGS,
+ libc::getpid(),
+ libc::KERN_PROC_ARGV];
+ let mib = mib.as_mut_ptr();
+ let mut argv_len = 0;
+ cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len,
+ ptr::null_mut(), 0))?;
+ let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize);
+ cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _,
+ &mut argv_len, ptr::null_mut(), 0))?;
+ argv.set_len(argv_len as usize);
+ if argv[0].is_null() {
+ return Err(io::Error::new(io::ErrorKind::Other,
+ "no current exe available"))
+ }
+ let argv0 = CStr::from_ptr(argv[0]).to_bytes();
+ if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') {
+ ::fs::canonicalize(OsStr::from_bytes(argv0))
+ } else {
+ Ok(PathBuf::from(OsStr::from_bytes(argv0)))
+ }
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ let selfexe = PathBuf::from("/proc/self/exe");
+ if selfexe.exists() {
+ ::fs::read_link(selfexe)
+ } else {
+ Err(io::Error::new(io::ErrorKind::Other, "no /proc/self/exe available. Is /proc mounted?"))
+ }
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ extern {
+ fn _NSGetExecutablePath(buf: *mut libc::c_char,
+ bufsize: *mut u32) -> libc::c_int;
+ }
+ unsafe {
+ let mut sz: u32 = 0;
+ _NSGetExecutablePath(ptr::null_mut(), &mut sz);
+ if sz == 0 { return Err(io::Error::last_os_error()); }
+ let mut v: Vec<u8> = Vec::with_capacity(sz as usize);
+ let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
+ if err != 0 { return Err(io::Error::last_os_error()); }
+ v.set_len(sz as usize - 1); // chop off trailing NUL
+ Ok(PathBuf::from(OsString::from_vec(v)))
+ }
+}
+
+#[cfg(any(target_os = "solaris"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ extern {
+ fn getexecname() -> *const c_char;
+ }
+ unsafe {
+ let path = getexecname();
+ if path.is_null() {
+ Err(io::Error::last_os_error())
+ } else {
+ let filename = CStr::from_ptr(path).to_bytes();
+ let path = PathBuf::from(<OsStr as OsStrExt>::from_bytes(filename));
+
+ // Prepend a current working directory to the path if
+ // it doesn't contain an absolute pathname.
+ if filename[0] == b'/' {
+ Ok(path)
+ } else {
+ getcwd().map(|cwd| cwd.join(path))
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "haiku")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ // Use Haiku's image info functions
+ #[repr(C)]
+ struct image_info {
+ id: i32,
+ type_: i32,
+ sequence: i32,
+ init_order: i32,
+ init_routine: *mut libc::c_void, // function pointer
+ term_routine: *mut libc::c_void, // function pointer
+ device: libc::dev_t,
+ node: libc::ino_t,
+ name: [libc::c_char; 1024], // MAXPATHLEN
+ text: *mut libc::c_void,
+ data: *mut libc::c_void,
+ text_size: i32,
+ data_size: i32,
+ api_version: i32,
+ abi: i32,
+ }
+
+ unsafe {
+ extern {
+ fn _get_next_image_info(team_id: i32, cookie: *mut i32,
+ info: *mut image_info, size: i32) -> i32;
+ }
+
+ let mut info: image_info = mem::zeroed();
+ let mut cookie: i32 = 0;
+ // the executable can be found at team id 0
+ let result = _get_next_image_info(0, &mut cookie, &mut info,
+ mem::size_of::<image_info>() as i32);
+ if result != 0 {
+ use io::ErrorKind;
+ Err(io::Error::new(ErrorKind::Other, "Error getting executable path"))
+ } else {
+ let name = CStr::from_ptr(info.name.as_ptr()).to_bytes();
+ Ok(PathBuf::from(OsStr::from_bytes(name)))
+ }
+ }
+}
+
+#[cfg(any(target_os = "fuchsia", target_os = "l4re"))]
+pub fn current_exe() -> io::Result<PathBuf> {
+ use io::ErrorKind;
+ Err(io::Error::new(ErrorKind::Other, "Not yet implemented!"))
+}
+
+pub struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+ _dont_send_or_sync_me: PhantomData<*mut ()>,
+}
impl Iterator for Env {
type Item = (OsString, OsString);
- fn next(&mut self) -> Option<(OsString, OsString)> {
- match self.0 {}
- }
+ fn next(&mut self) -> Option<(OsString, OsString)> { self.iter.next() }
+ fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+}
+
+#[cfg(target_os = "macos")]
+pub unsafe fn environ() -> *mut *const *const c_char {
+ extern { fn _NSGetEnviron() -> *mut *const *const c_char; }
+ _NSGetEnviron()
}
+#[cfg(not(target_os = "macos"))]
+pub unsafe fn environ() -> *mut *const *const c_char {
+ extern { static mut environ: *const *const c_char; }
+ &mut environ
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
pub fn env() -> Env {
- panic!("not supported on 3DS yet")
+ unsafe {
+ ENV_LOCK.lock();
+ let mut environ = *environ();
+ if environ == ptr::null() {
+ ENV_LOCK.unlock();
+ panic!("os::env() failure getting env string from OS: {}",
+ io::Error::last_os_error());
+ }
+ let mut result = Vec::new();
+ while *environ != ptr::null() {
+ if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ result.push(key_value);
+ }
+ environ = environ.offset(1);
+ }
+ let ret = Env {
+ iter: result.into_iter(),
+ _dont_send_or_sync_me: PhantomData,
+ };
+ ENV_LOCK.unlock();
+ return ret
+ }
+
+ fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+ // Strategy (copied from glibc): Variable name and value are separated
+ // by an ASCII equals sign '='. Since a variable name must not be
+ // empty, allow variable names starting with an equals sign. Skip all
+ // malformed lines.
+ if input.is_empty() {
+ return None;
+ }
+ let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+ pos.map(|p| (
+ OsStringExt::from_vec(input[..p].to_vec()),
+ OsStringExt::from_vec(input[p+1..].to_vec()),
+ ))
+ }
}
-pub fn getenv(_k: &OsStr) -> io::Result<Option<OsString>> {
- return Ok(None)
+pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
+ // environment variables with a nul byte can't be set, so their value is
+ // always None as well
+ let k = CString::new(k.as_bytes())?;
+ unsafe {
+ ENV_LOCK.lock();
+ let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+ let ret = if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+ };
+ ENV_LOCK.unlock();
+ return Ok(ret)
+ }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ let k = CString::new(k.as_bytes())?;
+ let v = CString::new(v.as_bytes())?;
+
+ unsafe {
+ ENV_LOCK.lock();
+ let ret = cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ());
+ ENV_LOCK.unlock();
+ return ret
+ }
}
-pub fn setenv(_k: &OsStr, _v: &OsStr) -> io::Result<()> {
- unsupported()
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+ let nbuf = CString::new(n.as_bytes())?;
+
+ unsafe {
+ ENV_LOCK.lock();
+ let ret = cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ());
+ ENV_LOCK.unlock();
+ return ret
+ }
}
-pub fn unsetenv(_n: &OsStr) -> io::Result<()> {
- unsupported()
+pub fn page_size() -> usize {
+ unsafe {
+ libc::sysconf(libc::_SC_PAGESIZE) as usize
+ }
}
pub fn temp_dir() -> PathBuf {
- PathBuf::from("/tmp")
+ ::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
+ if cfg!(target_os = "android") {
+ PathBuf::from("/data/local/tmp")
+ } else {
+ PathBuf::from("/tmp")
+ }
+ })
}
pub fn home_dir() -> Option<PathBuf> {
- None
+ return ::env::var_os("HOME").or_else(|| unsafe {
+ fallback()
+ }).map(PathBuf::from);
+
+ #[cfg(any(target_os = "android",
+ target_os = "ios",
+ target_os = "emscripten"))]
+ unsafe fn fallback() -> Option<OsString> { None }
+ #[cfg(not(any(target_os = "android",
+ target_os = "ios",
+ target_os = "emscripten")))]
+ unsafe fn fallback() -> Option<OsString> {
+ let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
+ n if n < 0 => 512 as usize,
+ n => n as usize,
+ };
+ let mut buf = Vec::with_capacity(amt);
+ let mut passwd: libc::passwd = mem::zeroed();
+ let mut result = ptr::null_mut();
+ match libc::getpwuid_r(libc::getuid(), &mut passwd, buf.as_mut_ptr(),
+ buf.capacity(), &mut result) {
+ 0 if !result.is_null() => {
+ let ptr = passwd.pw_dir as *const _;
+ let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
+ Some(OsStringExt::from_vec(bytes))
+ },
+ _ => None,
+ }
+ }
}
pub fn exit(code: i32) -> ! {
@@ -187,5 +540,41 @@ pub fn exit(code: i32) -> ! {
}
pub fn getpid() -> u32 {
- panic!("no pids on 3DS")
+ unsafe { libc::getpid() as u32 }
+}
+
+pub fn getppid() -> u32 {
+ unsafe { libc::getppid() as u32 }
+}
+
+#[cfg(target_env = "gnu")]
+pub fn glibc_version() -> Option<(usize, usize)> {
+ if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
+ parse_glibc_version(version_str)
+ } else {
+ None
+ }
+}
+
+#[cfg(target_env = "gnu")]
+fn glibc_version_cstr() -> Option<&'static CStr> {
+ weak! {
+ fn gnu_get_libc_version() -> *const libc::c_char
+ }
+ if let Some(f) = gnu_get_libc_version.get() {
+ unsafe { Some(CStr::from_ptr(f())) }
+ } else {
+ None
+ }
+}
+
+// Returns Some((major, minor)) if the string is a valid "x.y" version,
+// ignoring any extra dot-separated parts. Otherwise return None.
+#[cfg(target_env = "gnu")]
+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
+ }
}
diff --git a/ctr-std/src/sys/unix/os_str.rs b/ctr-std/src/sys/unix/os_str.rs
index e0da5bd..01c0fb8 100644
--- a/ctr-std/src/sys/unix/os_str.rs
+++ b/ctr-std/src/sys/unix/os_str.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
diff --git a/ctr-std/src/sys/unix/path.rs b/ctr-std/src/sys/unix/path.rs
index d96473b..bf9af7a 100644
--- a/ctr-std/src/sys/unix/path.rs
+++ b/ctr-std/src/sys/unix/path.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -21,16 +21,8 @@ pub fn is_verbatim_sep(b: u8) -> bool {
b == b'/'
}
-pub fn parse_prefix(path: &OsStr) -> Option<Prefix> {
- if let Some(path_str) = path.to_str() {
- if let Some(i) = path_str.find(':') {
- Some(Prefix::Verbatim(OsStr::new(&path_str[..i])))
- } else {
- None
- }
- } else {
- None
- }
+pub fn parse_prefix(_: &OsStr) -> Option<Prefix> {
+ None
}
pub const MAIN_SEP_STR: &'static str = "/";
diff --git a/ctr-std/src/sys/unix/pipe.rs b/ctr-std/src/sys/unix/pipe.rs
index 992e1ac..0a5dccd 100644
--- a/ctr-std/src/sys/unix/pipe.rs
+++ b/ctr-std/src/sys/unix/pipe.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -9,27 +9,123 @@
// except according to those terms.
use io;
-use sys::Void;
+use libc::{self, c_int};
+use mem;
+use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
+use sys::fd::FileDesc;
+use sys::{cvt, cvt_r};
-pub struct AnonPipe(Void);
+////////////////////////////////////////////////////////////////////////////////
+// Anonymous pipes
+////////////////////////////////////////////////////////////////////////////////
-impl AnonPipe {
- pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
- match self.0 {}
+pub struct AnonPipe(FileDesc);
+
+pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
+ weak! { fn pipe2(*mut c_int, c_int) -> c_int }
+ static INVALID: AtomicBool = ATOMIC_BOOL_INIT;
+
+ let mut fds = [0; 2];
+
+ // Unfortunately the only known way right now to create atomically set the
+ // CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in
+ // 2.6.27, however, and because we support 2.6.18 we must detect this
+ // support dynamically.
+ if cfg!(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd")) &&
+ !INVALID.load(Ordering::SeqCst)
+ {
+
+ if let Some(pipe) = pipe2.get() {
+ // Note that despite calling a glibc function here we may still
+ // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to
+ // emulate on older kernels, so if you happen to be running on
+ // an older kernel you may see `pipe2` as a symbol but still not
+ // see the syscall.
+ match cvt(unsafe { pipe(fds.as_mut_ptr(), libc::O_CLOEXEC) }) {
+ Ok(_) => {
+ return Ok((AnonPipe(FileDesc::new(fds[0])),
+ AnonPipe(FileDesc::new(fds[1]))));
+ }
+ Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {
+ INVALID.store(true, Ordering::SeqCst);
+ }
+ Err(e) => return Err(e),
+ }
+ }
}
+ cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
+
+ let fd0 = FileDesc::new(fds[0]);
+ let fd1 = FileDesc::new(fds[1]);
+ fd0.set_cloexec()?;
+ fd1.set_cloexec()?;
+ Ok((AnonPipe(fd0), AnonPipe(fd1)))
+}
- pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
- match self.0 {}
+impl AnonPipe {
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
}
- pub fn diverge(&self) -> ! {
- match self.0 {}
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
}
+
+ pub fn fd(&self) -> &FileDesc { &self.0 }
+ pub fn into_fd(self) -> FileDesc { self.0 }
}
pub fn read2(p1: AnonPipe,
- _v1: &mut Vec<u8>,
- _p2: AnonPipe,
- _v2: &mut Vec<u8>) -> io::Result<()> {
- match p1.0 {}
+ v1: &mut Vec<u8>,
+ p2: AnonPipe,
+ v2: &mut Vec<u8>) -> io::Result<()> {
+
+ // Set both pipes into nonblocking mode as we're gonna be reading from both
+ // in the `select` loop below, and we wouldn't want one to block the other!
+ let p1 = p1.into_fd();
+ let p2 = p2.into_fd();
+ p1.set_nonblocking(true)?;
+ p2.set_nonblocking(true)?;
+
+ let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
+ fds[0].fd = p1.raw();
+ fds[0].events = libc::POLLIN;
+ fds[1].fd = p2.raw();
+ fds[1].events = libc::POLLIN;
+ loop {
+ // wait for either pipe to become readable using `poll`
+ cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?;
+
+ if fds[0].revents != 0 && read(&p1, v1)? {
+ p2.set_nonblocking(false)?;
+ return p2.read_to_end(v2).map(|_| ());
+ }
+ if fds[1].revents != 0 && read(&p2, v2)? {
+ p1.set_nonblocking(false)?;
+ return p1.read_to_end(v1).map(|_| ());
+ }
+ }
+
+ // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
+ // EAGAIN. If we hit EOF, then this will happen because the underlying
+ // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
+ // this case we flip the other fd back into blocking mode and read
+ // whatever's leftover on that file descriptor.
+ fn read(fd: &FileDesc, dst: &mut Vec<u8>) -> Result<bool, io::Error> {
+ match fd.read_to_end(dst) {
+ Ok(_) => Ok(true),
+ Err(e) => {
+ if e.raw_os_error() == Some(libc::EWOULDBLOCK) ||
+ e.raw_os_error() == Some(libc::EAGAIN) {
+ Ok(false)
+ } else {
+ Err(e)
+ }
+ }
+ }
+ }
}
diff --git a/ctr-std/src/sys/unix/process.rs b/ctr-std/src/sys/unix/process.rs
deleted file mode 100644
index 06dafc6..0000000
--- a/ctr-std/src/sys/unix/process.rs
+++ /dev/null
@@ -1,169 +0,0 @@
-// 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};
-
-use libc::c_int;
-
-const EXIT_SUCCESS: c_int = 0;
-const EXIT_FAILURE: c_int = 1;
-
-////////////////////////////////////////////////////////////////////////////////
-// 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 {}
- }
-}
-
-#[derive(PartialEq, Eq, Clone, Copy, Debug)]
-pub struct ExitCode(u8);
-
-impl ExitCode {
- pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _);
- pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _);
-
- #[inline]
- pub fn as_i32(&self) -> i32 {
- self.0 as i32
- }
-}
diff --git a/ctr-std/src/sys/unix/process/mod.rs b/ctr-std/src/sys/unix/process/mod.rs
new file mode 100644
index 0000000..d8ac26c
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/mod.rs
@@ -0,0 +1,22 @@
+// Copyright 2016 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 use self::process_common::{Command, ExitStatus, ExitCode, Stdio, StdioPipes};
+pub use self::process_inner::Process;
+
+mod process_common;
+#[cfg(not(target_os = "fuchsia"))]
+#[path = "process_unix.rs"]
+mod process_inner;
+#[cfg(target_os = "fuchsia")]
+#[path = "process_fuchsia.rs"]
+mod process_inner;
+#[cfg(target_os = "fuchsia")]
+mod zircon;
diff --git a/ctr-std/src/sys/unix/process/process_common.rs b/ctr-std/src/sys/unix/process/process_common.rs
new file mode 100644
index 0000000..6396bb3
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/process_common.rs
@@ -0,0 +1,510 @@
+// Copyright 2016 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 os::unix::prelude::*;
+
+use ffi::{OsString, OsStr, CString, CStr};
+use fmt;
+use io;
+use libc::{self, c_int, gid_t, uid_t, c_char, EXIT_SUCCESS, EXIT_FAILURE};
+use ptr;
+use sys::fd::FileDesc;
+use sys::fs::{File, OpenOptions};
+use sys::pipe::{self, AnonPipe};
+use sys_common::process::{CommandEnv, DefaultEnvKey};
+use collections::BTreeMap;
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Command {
+ // Currently we try hard to ensure that the call to `.exec()` doesn't
+ // actually allocate any memory. While many platforms try to ensure that
+ // memory allocation works after a fork in a multithreaded process, it's
+ // been observed to be buggy and somewhat unreliable, so we do our best to
+ // just not do it at all!
+ //
+ // Along those lines, the `argv` and `envp` raw pointers here are exactly
+ // what's gonna get passed to `execvp`. The `argv` array starts with the
+ // `program` and ends with a NULL, and the `envp` pointer, if present, is
+ // also null-terminated.
+ //
+ // Right now we don't support removing arguments, so there's no much fancy
+ // support there, but we support adding and removing environment variables,
+ // so a side table is used to track where in the `envp` array each key is
+ // located. Whenever we add a key we update it in place if it's already
+ // present, and whenever we remove a key we update the locations of all
+ // other keys.
+ program: CString,
+ args: Vec<CString>,
+ argv: Argv,
+ env: CommandEnv<DefaultEnvKey>,
+
+ cwd: Option<CString>,
+ uid: Option<uid_t>,
+ gid: Option<gid_t>,
+ saw_nul: bool,
+ closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
+ stdin: Option<Stdio>,
+ stdout: Option<Stdio>,
+ stderr: Option<Stdio>,
+}
+
+// Create a new type for argv, so that we can make it `Send`
+struct Argv(Vec<*const c_char>);
+
+// It is safe to make Argv Send, because it contains pointers to memory owned by `Command.args`
+unsafe impl Send for Argv {}
+
+// 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>,
+}
+
+// passed to do_exec() with configuration of what the child stdio should look
+// like
+pub struct ChildPipes {
+ pub stdin: ChildStdio,
+ pub stdout: ChildStdio,
+ pub stderr: ChildStdio,
+}
+
+pub enum ChildStdio {
+ Inherit,
+ Explicit(c_int),
+ Owned(FileDesc),
+}
+
+pub enum Stdio {
+ Inherit,
+ Null,
+ MakePipe,
+ Fd(FileDesc),
+}
+
+impl Command {
+ pub fn new(program: &OsStr) -> Command {
+ let mut saw_nul = false;
+ let program = os2c(program, &mut saw_nul);
+ Command {
+ argv: Argv(vec![program.as_ptr(), ptr::null()]),
+ program,
+ args: Vec::new(),
+ env: Default::default(),
+ cwd: None,
+ uid: None,
+ gid: None,
+ saw_nul,
+ closures: Vec::new(),
+ stdin: None,
+ stdout: None,
+ stderr: None,
+ }
+ }
+
+ pub fn arg(&mut self, arg: &OsStr) {
+ // Overwrite the trailing NULL pointer in `argv` and then add a new null
+ // pointer.
+ let arg = os2c(arg, &mut self.saw_nul);
+ self.argv.0[self.args.len() + 1] = arg.as_ptr();
+ self.argv.0.push(ptr::null());
+
+ // Also make sure we keep track of the owned value to schedule a
+ // destructor for this memory.
+ self.args.push(arg);
+ }
+
+ pub fn cwd(&mut self, dir: &OsStr) {
+ self.cwd = Some(os2c(dir, &mut self.saw_nul));
+ }
+ pub fn uid(&mut self, id: uid_t) {
+ self.uid = Some(id);
+ }
+ pub fn gid(&mut self, id: gid_t) {
+ self.gid = Some(id);
+ }
+
+ pub fn saw_nul(&self) -> bool {
+ self.saw_nul
+ }
+ pub fn get_argv(&self) -> &Vec<*const c_char> {
+ &self.argv.0
+ }
+
+ #[allow(dead_code)]
+ pub fn get_cwd(&self) -> &Option<CString> {
+ &self.cwd
+ }
+ #[allow(dead_code)]
+ pub fn get_uid(&self) -> Option<uid_t> {
+ self.uid
+ }
+ #[allow(dead_code)]
+ pub fn get_gid(&self) -> Option<gid_t> {
+ self.gid
+ }
+
+ pub fn get_closures(&mut self) -> &mut Vec<Box<FnMut() -> io::Result<()> + Send + Sync>> {
+ &mut self.closures
+ }
+
+ pub fn before_exec(&mut self,
+ f: Box<FnMut() -> io::Result<()> + Send + Sync>) {
+ self.closures.push(f);
+ }
+
+ pub fn stdin(&mut self, stdin: Stdio) {
+ self.stdin = Some(stdin);
+ }
+
+ pub fn stdout(&mut self, stdout: Stdio) {
+ self.stdout = Some(stdout);
+ }
+
+ pub fn stderr(&mut self, stderr: Stdio) {
+ self.stderr = Some(stderr);
+ }
+
+ pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
+ &mut self.env
+ }
+
+ pub fn capture_env(&mut self) -> Option<CStringArray> {
+ let maybe_env = self.env.capture_if_changed();
+ maybe_env.map(|env| construct_envp(env, &mut self.saw_nul))
+ }
+ #[allow(dead_code)]
+ pub fn env_saw_path(&self) -> bool {
+ self.env.have_changed_path()
+ }
+
+ pub fn setup_io(&self, default: Stdio, needs_stdin: bool)
+ -> io::Result<(StdioPipes, ChildPipes)> {
+ let null = Stdio::Null;
+ let default_stdin = if needs_stdin {&default} else {&null};
+ let stdin = self.stdin.as_ref().unwrap_or(default_stdin);
+ let stdout = self.stdout.as_ref().unwrap_or(&default);
+ let stderr = self.stderr.as_ref().unwrap_or(&default);
+ let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?;
+ let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?;
+ let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?;
+ let ours = StdioPipes {
+ stdin: our_stdin,
+ stdout: our_stdout,
+ stderr: our_stderr,
+ };
+ let theirs = ChildPipes {
+ stdin: their_stdin,
+ stdout: their_stdout,
+ stderr: their_stderr,
+ };
+ Ok((ours, theirs))
+ }
+}
+
+fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
+ CString::new(s.as_bytes()).unwrap_or_else(|_e| {
+ *saw_nul = true;
+ CString::new("<string-with-nul>").unwrap()
+ })
+}
+
+// Helper type to manage ownership of the strings within a C-style array.
+pub struct CStringArray {
+ items: Vec<CString>,
+ ptrs: Vec<*const c_char>
+}
+
+impl CStringArray {
+ pub fn with_capacity(capacity: usize) -> Self {
+ let mut result = CStringArray {
+ items: Vec::with_capacity(capacity),
+ ptrs: Vec::with_capacity(capacity+1)
+ };
+ result.ptrs.push(ptr::null());
+ result
+ }
+ pub fn push(&mut self, item: CString) {
+ let l = self.ptrs.len();
+ self.ptrs[l-1] = item.as_ptr();
+ self.ptrs.push(ptr::null());
+ self.items.push(item);
+ }
+ pub fn as_ptr(&self) -> *const *const c_char {
+ self.ptrs.as_ptr()
+ }
+}
+
+fn construct_envp(env: BTreeMap<DefaultEnvKey, OsString>, saw_nul: &mut bool) -> CStringArray {
+ let mut result = CStringArray::with_capacity(env.len());
+ for (k, v) in env {
+ let mut k: OsString = k.into();
+
+ // Reserve additional space for '=' and null terminator
+ k.reserve_exact(v.len() + 2);
+ k.push("=");
+ k.push(&v);
+
+ // Add the new entry into the array
+ if let Ok(item) = CString::new(k.into_vec()) {
+ result.push(item);
+ } else {
+ *saw_nul = true;
+ }
+ }
+
+ result
+}
+
+impl Stdio {
+ pub fn to_child_stdio(&self, readable: bool)
+ -> io::Result<(ChildStdio, Option<AnonPipe>)> {
+ match *self {
+ Stdio::Inherit => {
+ Ok((ChildStdio::Inherit, None))
+ },
+
+ // Make sure that the source descriptors are not an stdio
+ // descriptor, otherwise the order which we set the child's
+ // descriptors may blow away a descriptor which we are hoping to
+ // save. For example, suppose we want the child's stderr to be the
+ // parent's stdout, and the child's stdout to be the parent's
+ // stderr. No matter which we dup first, the second will get
+ // overwritten prematurely.
+ Stdio::Fd(ref fd) => {
+ if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO {
+ Ok((ChildStdio::Owned(fd.duplicate()?), None))
+ } else {
+ Ok((ChildStdio::Explicit(fd.raw()), None))
+ }
+ }
+
+ Stdio::MakePipe => {
+ let (reader, writer) = pipe::anon_pipe()?;
+ let (ours, theirs) = if readable {
+ (writer, reader)
+ } else {
+ (reader, writer)
+ };
+ Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours)))
+ }
+
+ Stdio::Null => {
+ let mut opts = OpenOptions::new();
+ opts.read(readable);
+ opts.write(!readable);
+ let path = unsafe {
+ CStr::from_ptr("/dev/null\0".as_ptr() as *const _)
+ };
+ let fd = File::open_c(&path, &opts)?;
+ Ok((ChildStdio::Owned(fd.into_fd()), None))
+ }
+ }
+ }
+}
+
+impl From<AnonPipe> for Stdio {
+ fn from(pipe: AnonPipe) -> Stdio {
+ Stdio::Fd(pipe.into_fd())
+ }
+}
+
+impl From<File> for Stdio {
+ fn from(file: File) -> Stdio {
+ Stdio::Fd(file.into_fd())
+ }
+}
+
+impl ChildStdio {
+ pub fn fd(&self) -> Option<c_int> {
+ match *self {
+ ChildStdio::Inherit => None,
+ ChildStdio::Explicit(fd) => Some(fd),
+ ChildStdio::Owned(ref fd) => Some(fd.raw()),
+ }
+ }
+}
+
+impl fmt::Debug for Command {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}", self.program)?;
+ for arg in &self.args {
+ write!(f, " {:?}", arg)?;
+ }
+ Ok(())
+ }
+}
+
+/// Unix exit statuses
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatus(c_int);
+
+impl ExitStatus {
+ pub fn new(status: c_int) -> ExitStatus {
+ ExitStatus(status)
+ }
+
+ fn exited(&self) -> bool {
+ unsafe { libc::WIFEXITED(self.0) }
+ }
+
+ pub fn success(&self) -> bool {
+ self.code() == Some(0)
+ }
+
+ pub fn code(&self) -> Option<i32> {
+ if self.exited() {
+ Some(unsafe { libc::WEXITSTATUS(self.0) })
+ } else {
+ None
+ }
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ if !self.exited() {
+ Some(unsafe { libc::WTERMSIG(self.0) })
+ } else {
+ None
+ }
+ }
+}
+
+impl From<c_int> for ExitStatus {
+ fn from(a: c_int) -> ExitStatus {
+ ExitStatus(a)
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(code) = self.code() {
+ write!(f, "exit code: {}", code)
+ } else {
+ let signal = self.signal().unwrap();
+ write!(f, "signal: {}", signal)
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitCode(u8);
+
+impl ExitCode {
+ pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _);
+ pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _);
+
+ #[inline]
+ pub fn as_i32(&self) -> i32 {
+ self.0 as i32
+ }
+}
+
+#[cfg(all(test, not(target_os = "emscripten")))]
+mod tests {
+ use super::*;
+
+ use ffi::OsStr;
+ use mem;
+ use ptr;
+ use libc;
+ use sys::cvt;
+
+ macro_rules! t {
+ ($e:expr) => {
+ match $e {
+ Ok(t) => t,
+ Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
+ }
+ }
+ }
+
+ // Android with api less than 21 define sig* functions inline, so it is not
+ // available for dynamic link. Implementing sigemptyset and sigaddset allow us
+ // to support older Android version (independent of libc version).
+ // The following implementations are based on https://git.io/vSkNf
+
+ #[cfg(not(target_os = "android"))]
+ extern {
+ #[cfg_attr(target_os = "netbsd", link_name = "__sigemptyset14")]
+ fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int;
+
+ #[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")]
+ fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int;
+ }
+
+ #[cfg(target_os = "android")]
+ unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int {
+ libc::memset(set as *mut _, 0, mem::size_of::<libc::sigset_t>());
+ return 0;
+ }
+
+ #[cfg(target_os = "android")]
+ unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int {
+ use slice;
+
+ let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::<libc::sigset_t>());
+ let bit = (signum - 1) as usize;
+ raw[bit / 8] |= 1 << (bit % 8);
+ return 0;
+ }
+
+ // See #14232 for more information, but it appears that signal delivery to a
+ // newly spawned process may just be raced in the macOS, so to prevent this
+ // test from being flaky we ignore it on macOS.
+ #[test]
+ #[cfg_attr(target_os = "macos", ignore)]
+ // When run under our current QEMU emulation test suite this test fails,
+ // although the reason isn't very clear as to why. For now this test is
+ // ignored there.
+ #[cfg_attr(target_arch = "arm", ignore)]
+ #[cfg_attr(target_arch = "aarch64", ignore)]
+ fn test_process_mask() {
+ unsafe {
+ // Test to make sure that a signal mask does not get inherited.
+ let mut cmd = Command::new(OsStr::new("cat"));
+
+ let mut set: libc::sigset_t = mem::uninitialized();
+ let mut old_set: libc::sigset_t = mem::uninitialized();
+ t!(cvt(sigemptyset(&mut set)));
+ t!(cvt(sigaddset(&mut set, libc::SIGINT)));
+ t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, &mut old_set)));
+
+ cmd.stdin(Stdio::MakePipe);
+ cmd.stdout(Stdio::MakePipe);
+
+ let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
+ let stdin_write = pipes.stdin.take().unwrap();
+ let stdout_read = pipes.stdout.take().unwrap();
+
+ t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &old_set,
+ ptr::null_mut())));
+
+ t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT)));
+ // We need to wait until SIGINT is definitely delivered. The
+ // easiest way is to write something to cat, and try to read it
+ // back: if SIGINT is unmasked, it'll get delivered when cat is
+ // next scheduled.
+ let _ = stdin_write.write(b"Hello");
+ drop(stdin_write);
+
+ // Either EOF or failure (EPIPE) is okay.
+ let mut buf = [0; 5];
+ if let Ok(ret) = stdout_read.read(&mut buf) {
+ assert_eq!(ret, 0);
+ }
+
+ t!(cat.wait());
+ }
+ }
+}
diff --git a/ctr-std/src/sys/unix/process/process_fuchsia.rs b/ctr-std/src/sys/unix/process/process_fuchsia.rs
new file mode 100644
index 0000000..fa48001
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/process_fuchsia.rs
@@ -0,0 +1,181 @@
+// Copyright 2016 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::{self, size_t};
+use mem;
+use ptr;
+
+use sys::process::zircon::{Handle, zx_handle_t};
+use sys::process::process_common::*;
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+ pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
+ -> io::Result<(Process, StdioPipes)> {
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return Err(io::Error::new(io::ErrorKind::InvalidInput,
+ "nul byte found in provided data"));
+ }
+
+ let (ours, theirs) = self.setup_io(default, needs_stdin)?;
+
+ let process_handle = unsafe { self.do_exec(theirs, envp.as_ref())? };
+
+ Ok((Process { handle: Handle::new(process_handle) }, ours))
+ }
+
+ pub fn exec(&mut self, default: Stdio) -> io::Error {
+ if self.saw_nul() {
+ return io::Error::new(io::ErrorKind::InvalidInput,
+ "nul byte found in provided data")
+ }
+
+ match self.setup_io(default, true) {
+ Ok((_, _)) => {
+ // FIXME: This is tough because we don't support the exec syscalls
+ unimplemented!();
+ },
+ Err(e) => e,
+ }
+ }
+
+ unsafe fn do_exec(&mut self, stdio: ChildPipes, maybe_envp: Option<&CStringArray>)
+ -> io::Result<zx_handle_t> {
+ use sys::process::zircon::*;
+
+ let envp = match maybe_envp {
+ Some(envp) => envp.as_ptr(),
+ None => ptr::null(),
+ };
+
+ let transfer_or_clone = |opt_fd, target_fd| if let Some(local_fd) = opt_fd {
+ fdio_spawn_action_t {
+ action: FDIO_SPAWN_ACTION_TRANSFER_FD,
+ local_fd,
+ target_fd,
+ ..Default::default()
+ }
+ } else {
+ fdio_spawn_action_t {
+ action: FDIO_SPAWN_ACTION_CLONE_FD,
+ local_fd: target_fd,
+ target_fd,
+ ..Default::default()
+ }
+ };
+
+ // Clone stdin, stdout, and stderr
+ let action1 = transfer_or_clone(stdio.stdin.fd(), 0);
+ let action2 = transfer_or_clone(stdio.stdout.fd(), 1);
+ let action3 = transfer_or_clone(stdio.stderr.fd(), 2);
+ let actions = [action1, action2, action3];
+
+ // We don't want FileDesc::drop to be called on any stdio. fdio_spawn_etc
+ // always consumes transferred file descriptors.
+ mem::forget(stdio);
+
+ for callback in self.get_closures().iter_mut() {
+ callback()?;
+ }
+
+ let mut process_handle: zx_handle_t = 0;
+ zx_cvt(fdio_spawn_etc(
+ 0,
+ FDIO_SPAWN_CLONE_JOB | FDIO_SPAWN_CLONE_LDSVC | FDIO_SPAWN_CLONE_NAMESPACE,
+ self.get_argv()[0], self.get_argv().as_ptr(), envp, 3, actions.as_ptr(),
+ &mut process_handle,
+ ptr::null_mut(),
+ ))?;
+ // FIXME: See if we want to do something with that err_msg
+
+ Ok(process_handle)
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct Process {
+ handle: Handle,
+}
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ self.handle.raw() as u32
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ use sys::process::zircon::*;
+
+ unsafe { zx_cvt(zx_task_kill(self.handle.raw()))?; }
+
+ Ok(())
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ use default::Default;
+ use sys::process::zircon::*;
+
+ let mut proc_info: zx_info_process_t = Default::default();
+ let mut actual: size_t = 0;
+ let mut avail: size_t = 0;
+
+ unsafe {
+ zx_cvt(zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED,
+ ZX_TIME_INFINITE, ptr::null_mut()))?;
+ zx_cvt(zx_object_get_info(self.handle.raw(), ZX_INFO_PROCESS,
+ &mut proc_info as *mut _ as *mut libc::c_void,
+ mem::size_of::<zx_info_process_t>(), &mut actual,
+ &mut avail))?;
+ }
+ if actual != 1 {
+ return Err(io::Error::new(io::ErrorKind::InvalidData,
+ "Failed to get exit status of process"));
+ }
+ Ok(ExitStatus::new(proc_info.rec.return_code))
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ use default::Default;
+ use sys::process::zircon::*;
+
+ let mut proc_info: zx_info_process_t = Default::default();
+ let mut actual: size_t = 0;
+ let mut avail: size_t = 0;
+
+ unsafe {
+ let status = zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED,
+ 0, ptr::null_mut());
+ match status {
+ 0 => { }, // Success
+ x if x == ERR_TIMED_OUT => {
+ return Ok(None);
+ },
+ _ => { panic!("Failed to wait on process handle: {}", status); },
+ }
+ zx_cvt(zx_object_get_info(self.handle.raw(), ZX_INFO_PROCESS,
+ &mut proc_info as *mut _ as *mut libc::c_void,
+ mem::size_of::<zx_info_process_t>(), &mut actual,
+ &mut avail))?;
+ }
+ if actual != 1 {
+ return Err(io::Error::new(io::ErrorKind::InvalidData,
+ "Failed to get exit status of process"));
+ }
+ Ok(Some(ExitStatus::new(proc_info.rec.return_code)))
+ }
+}
diff --git a/ctr-std/src/sys/unix/process/process_unix.rs b/ctr-std/src/sys/unix/process/process_unix.rs
new file mode 100644
index 0000000..9d6d607
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/process_unix.rs
@@ -0,0 +1,405 @@
+// Copyright 2014-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::{self, Error, ErrorKind};
+use libc::{self, c_int, gid_t, pid_t, uid_t};
+use ptr;
+
+use sys::cvt;
+use sys::process::process_common::*;
+
+////////////////////////////////////////////////////////////////////////////////
+// Command
+////////////////////////////////////////////////////////////////////////////////
+
+impl Command {
+ pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
+ -> io::Result<(Process, StdioPipes)> {
+ use sys;
+
+ const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
+
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return Err(io::Error::new(ErrorKind::InvalidInput,
+ "nul byte found in provided data"));
+ }
+
+ let (ours, theirs) = self.setup_io(default, needs_stdin)?;
+
+ if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
+ return Ok((ret, ours))
+ }
+
+ let (input, output) = sys::pipe::anon_pipe()?;
+
+ let pid = unsafe {
+ match cvt(libc::fork())? {
+ 0 => {
+ drop(input);
+ let err = self.do_exec(theirs, envp.as_ref());
+ let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
+ let bytes = [
+ (errno >> 24) as u8,
+ (errno >> 16) as u8,
+ (errno >> 8) as u8,
+ (errno >> 0) as u8,
+ CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
+ CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
+ ];
+ // pipe I/O up to PIPE_BUF bytes should be atomic, and then
+ // we want to be sure we *don't* run at_exit destructors as
+ // we're being torn down regardless
+ assert!(output.write(&bytes).is_ok());
+ libc::_exit(1)
+ }
+ n => n,
+ }
+ };
+
+ let mut p = Process { pid: pid, status: None };
+ drop(output);
+ let mut bytes = [0; 8];
+
+ // loop to handle EINTR
+ loop {
+ match input.read(&mut bytes) {
+ Ok(0) => return Ok((p, ours)),
+ Ok(8) => {
+ assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
+ "Validation on the CLOEXEC pipe failed: {:?}", bytes);
+ let errno = combine(&bytes[0.. 4]);
+ assert!(p.wait().is_ok(),
+ "wait() should either return Ok or panic");
+ return Err(Error::from_raw_os_error(errno))
+ }
+ Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+ Err(e) => {
+ assert!(p.wait().is_ok(),
+ "wait() should either return Ok or panic");
+ panic!("the CLOEXEC pipe failed: {:?}", e)
+ },
+ Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
+ assert!(p.wait().is_ok(),
+ "wait() should either return Ok or panic");
+ panic!("short read on the CLOEXEC pipe")
+ }
+ }
+ }
+
+ fn combine(arr: &[u8]) -> i32 {
+ let a = arr[0] as u32;
+ let b = arr[1] as u32;
+ let c = arr[2] as u32;
+ let d = arr[3] as u32;
+
+ ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
+ }
+ }
+
+ pub fn exec(&mut self, default: Stdio) -> io::Error {
+ let envp = self.capture_env();
+
+ if self.saw_nul() {
+ return io::Error::new(ErrorKind::InvalidInput,
+ "nul byte found in provided data")
+ }
+
+ match self.setup_io(default, true) {
+ Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) },
+ Err(e) => e,
+ }
+ }
+
+ // And at this point we've reached a special time in the life of the
+ // child. The child must now be considered hamstrung and unable to
+ // do anything other than syscalls really. Consider the following
+ // scenario:
+ //
+ // 1. Thread A of process 1 grabs the malloc() mutex
+ // 2. Thread B of process 1 forks(), creating thread C
+ // 3. Thread C of process 2 then attempts to malloc()
+ // 4. The memory of process 2 is the same as the memory of
+ // process 1, so the mutex is locked.
+ //
+ // This situation looks a lot like deadlock, right? It turns out
+ // that this is what pthread_atfork() takes care of, which is
+ // presumably implemented across platforms. The first thing that
+ // threads to *before* forking is to do things like grab the malloc
+ // mutex, and then after the fork they unlock it.
+ //
+ // Despite this information, libnative's spawn has been witnessed to
+ // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
+ // all collected backtraces point at malloc/free traffic in the
+ // child spawned process.
+ //
+ // For this reason, the block of code below should contain 0
+ // invocations of either malloc of free (or their related friends).
+ //
+ // As an example of not having malloc/free traffic, we don't close
+ // this file descriptor by dropping the FileDesc (which contains an
+ // allocation). Instead we just close it manually. This will never
+ // have the drop glue anyway because this code never returns (the
+ // child will either exec() or invoke libc::exit)
+ unsafe fn do_exec(
+ &mut self,
+ stdio: ChildPipes,
+ maybe_envp: Option<&CStringArray>
+ ) -> io::Error {
+ use sys::{self, cvt_r};
+
+ macro_rules! t {
+ ($e:expr) => (match $e {
+ Ok(e) => e,
+ Err(e) => return e,
+ })
+ }
+
+ if let Some(fd) = stdio.stdin.fd() {
+ t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
+ }
+ if let Some(fd) = stdio.stdout.fd() {
+ t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
+ }
+ if let Some(fd) = stdio.stderr.fd() {
+ t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
+ }
+
+ if cfg!(not(any(target_os = "l4re"))) {
+ if let Some(u) = self.get_gid() {
+ t!(cvt(libc::setgid(u as gid_t)));
+ }
+ if let Some(u) = self.get_uid() {
+ // When dropping privileges from root, the `setgroups` call
+ // will remove any extraneous groups. If we don't call this,
+ // then even though our uid has dropped, we may still have
+ // groups that enable us to do super-user things. This will
+ // fail if we aren't root, so don't bother checking the
+ // return value, this is just done as an optimistic
+ // privilege dropping function.
+ let _ = libc::setgroups(0, ptr::null());
+
+ t!(cvt(libc::setuid(u as uid_t)));
+ }
+ }
+ if let Some(ref cwd) = *self.get_cwd() {
+ t!(cvt(libc::chdir(cwd.as_ptr())));
+ }
+ if let Some(envp) = maybe_envp {
+ *sys::os::environ() = envp.as_ptr();
+ }
+
+ // emscripten has no signal support.
+ #[cfg(not(any(target_os = "emscripten")))]
+ {
+ use mem;
+ // Reset signal handling so the child process starts in a
+ // standardized state. libstd ignores SIGPIPE, and signal-handling
+ // libraries often set a mask. Child processes inherit ignored
+ // signals and the signal mask from their parent, but most
+ // UNIX programs do not reset these things on their own, so we
+ // need to clean things up now to avoid confusing the program
+ // we're about to run.
+ let mut set: libc::sigset_t = mem::uninitialized();
+ if cfg!(target_os = "android") {
+ // Implementing sigemptyset allow us to support older Android
+ // versions. See the comment about Android and sig* functions in
+ // process_common.rs
+ libc::memset(&mut set as *mut _ as *mut _,
+ 0,
+ mem::size_of::<libc::sigset_t>());
+ } else {
+ t!(cvt(libc::sigemptyset(&mut set)));
+ }
+ t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
+ ptr::null_mut())));
+ let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
+ if ret == libc::SIG_ERR {
+ return io::Error::last_os_error()
+ }
+ }
+
+ for callback in self.get_closures().iter_mut() {
+ t!(callback());
+ }
+
+ libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
+ io::Error::last_os_error()
+ }
+
+ #[cfg(not(any(target_os = "macos", target_os = "freebsd",
+ all(target_os = "linux", target_env = "gnu"))))]
+ fn posix_spawn(&mut self, _: &ChildPipes, _: Option<&CStringArray>)
+ -> io::Result<Option<Process>>
+ {
+ Ok(None)
+ }
+
+ // Only support platforms for which posix_spawn() can return ENOENT
+ // directly.
+ #[cfg(any(target_os = "macos", target_os = "freebsd",
+ all(target_os = "linux", target_env = "gnu")))]
+ fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>)
+ -> io::Result<Option<Process>>
+ {
+ use mem;
+ use sys;
+
+ if self.get_cwd().is_some() ||
+ self.get_gid().is_some() ||
+ self.get_uid().is_some() ||
+ self.env_saw_path() ||
+ self.get_closures().len() != 0 {
+ return Ok(None)
+ }
+
+ // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ {
+ if let Some(version) = sys::os::glibc_version() {
+ if version < (2, 24) {
+ return Ok(None)
+ }
+ } else {
+ return Ok(None)
+ }
+ }
+
+ let mut p = Process { pid: 0, status: None };
+
+ struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t);
+
+ impl Drop for PosixSpawnFileActions {
+ fn drop(&mut self) {
+ unsafe {
+ libc::posix_spawn_file_actions_destroy(&mut self.0);
+ }
+ }
+ }
+
+ struct PosixSpawnattr(libc::posix_spawnattr_t);
+
+ impl Drop for PosixSpawnattr {
+ fn drop(&mut self) {
+ unsafe {
+ libc::posix_spawnattr_destroy(&mut self.0);
+ }
+ }
+ }
+
+ unsafe {
+ let mut file_actions = PosixSpawnFileActions(mem::uninitialized());
+ let mut attrs = PosixSpawnattr(mem::uninitialized());
+
+ libc::posix_spawnattr_init(&mut attrs.0);
+ libc::posix_spawn_file_actions_init(&mut file_actions.0);
+
+ if let Some(fd) = stdio.stdin.fd() {
+ cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
+ fd,
+ libc::STDIN_FILENO))?;
+ }
+ if let Some(fd) = stdio.stdout.fd() {
+ cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
+ fd,
+ libc::STDOUT_FILENO))?;
+ }
+ if let Some(fd) = stdio.stderr.fd() {
+ cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
+ fd,
+ libc::STDERR_FILENO))?;
+ }
+
+ let mut set: libc::sigset_t = mem::uninitialized();
+ cvt(libc::sigemptyset(&mut set))?;
+ cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0,
+ &set))?;
+ cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?;
+ cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0,
+ &set))?;
+
+ let flags = libc::POSIX_SPAWN_SETSIGDEF |
+ libc::POSIX_SPAWN_SETSIGMASK;
+ cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?;
+
+ let envp = envp.map(|c| c.as_ptr())
+ .unwrap_or(*sys::os::environ() as *const _);
+ let ret = libc::posix_spawnp(
+ &mut p.pid,
+ self.get_argv()[0],
+ &file_actions.0,
+ &attrs.0,
+ self.get_argv().as_ptr() as *const _,
+ envp as *const _,
+ );
+ if ret == 0 {
+ Ok(Some(p))
+ } else {
+ Err(io::Error::from_raw_os_error(ret))
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Processes
+////////////////////////////////////////////////////////////////////////////////
+
+/// The unique id of the process (this should never be negative).
+pub struct Process {
+ pid: pid_t,
+ status: Option<ExitStatus>,
+}
+
+impl Process {
+ pub fn id(&self) -> u32 {
+ self.pid as u32
+ }
+
+ pub fn kill(&mut self) -> io::Result<()> {
+ // If we've already waited on this process then the pid can be recycled
+ // and used for another process, and we probably shouldn't be killing
+ // random processes, so just return an error.
+ if self.status.is_some() {
+ Err(Error::new(ErrorKind::InvalidInput,
+ "invalid argument: can't kill an exited process"))
+ } else {
+ cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
+ }
+ }
+
+ pub fn wait(&mut self) -> io::Result<ExitStatus> {
+ use sys::cvt_r;
+ if let Some(status) = self.status {
+ return Ok(status)
+ }
+ let mut status = 0 as c_int;
+ cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
+ self.status = Some(ExitStatus::new(status));
+ Ok(ExitStatus::new(status))
+ }
+
+ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
+ if let Some(status) = self.status {
+ return Ok(Some(status))
+ }
+ let mut status = 0 as c_int;
+ let pid = cvt(unsafe {
+ libc::waitpid(self.pid, &mut status, libc::WNOHANG)
+ })?;
+ if pid == 0 {
+ Ok(None)
+ } else {
+ self.status = Some(ExitStatus::new(status));
+ Ok(Some(ExitStatus::new(status)))
+ }
+ }
+}
diff --git a/ctr-std/src/sys/unix/process/zircon.rs b/ctr-std/src/sys/unix/process/zircon.rs
new file mode 100644
index 0000000..a06c73e
--- /dev/null
+++ b/ctr-std/src/sys/unix/process/zircon.rs
@@ -0,0 +1,279 @@
+// Copyright 2016 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(non_camel_case_types, unused)]
+
+use convert::TryInto;
+use io;
+use os::raw::c_char;
+use u64;
+
+use libc::{c_int, c_void, size_t};
+
+pub type zx_handle_t = u32;
+pub type zx_vaddr_t = usize;
+pub type zx_rights_t = u32;
+pub type zx_status_t = i32;
+
+pub const ZX_HANDLE_INVALID: zx_handle_t = 0;
+
+pub type zx_time_t = u64;
+pub const ZX_TIME_INFINITE : zx_time_t = u64::MAX;
+
+pub type zx_signals_t = u32;
+
+pub const ZX_OBJECT_SIGNAL_3 : zx_signals_t = 1 << 3;
+
+pub const ZX_TASK_TERMINATED : zx_signals_t = ZX_OBJECT_SIGNAL_3;
+
+pub const ZX_RIGHT_SAME_RIGHTS : zx_rights_t = 1 << 31;
+
+pub type zx_object_info_topic_t = u32;
+
+pub const ZX_INFO_PROCESS : zx_object_info_topic_t = 3;
+
+pub fn zx_cvt<T>(t: T) -> io::Result<T> where T: TryInto<zx_status_t>+Copy {
+ if let Ok(status) = TryInto::try_into(t) {
+ if status < 0 {
+ Err(io::Error::from_raw_os_error(status))
+ } else {
+ Ok(t)
+ }
+ } else {
+ Err(io::Error::last_os_error())
+ }
+}
+
+// Safe wrapper around zx_handle_t
+pub struct Handle {
+ raw: zx_handle_t,
+}
+
+impl Handle {
+ pub fn new(raw: zx_handle_t) -> Handle {
+ Handle {
+ raw,
+ }
+ }
+
+ pub fn raw(&self) -> zx_handle_t {
+ self.raw
+ }
+}
+
+impl Drop for Handle {
+ fn drop(&mut self) {
+ unsafe { zx_cvt(zx_handle_close(self.raw)).expect("Failed to close zx_handle_t"); }
+ }
+}
+
+// Common ZX_INFO header
+#[derive(Default)]
+#[repr(C)]
+pub struct zx_info_header_t {
+ pub topic: u32, // identifies the info struct
+ pub avail_topic_size: u16, // “native” size of the struct
+ pub topic_size: u16, // size of the returned struct (<=topic_size)
+ pub avail_count: u32, // number of records the kernel has
+ pub count: u32, // number of records returned (limited by buffer size)
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct zx_record_process_t {
+ pub return_code: c_int,
+}
+
+// Returned for topic ZX_INFO_PROCESS
+#[derive(Default)]
+#[repr(C)]
+pub struct zx_info_process_t {
+ pub hdr: zx_info_header_t,
+ pub rec: zx_record_process_t,
+}
+
+extern {
+ pub fn zx_job_default() -> zx_handle_t;
+
+ pub fn zx_task_kill(handle: zx_handle_t) -> zx_status_t;
+
+ pub fn zx_handle_close(handle: zx_handle_t) -> zx_status_t;
+
+ pub fn zx_handle_duplicate(handle: zx_handle_t, rights: zx_rights_t,
+ out: *const zx_handle_t) -> zx_handle_t;
+
+ pub fn zx_object_wait_one(handle: zx_handle_t, signals: zx_signals_t, timeout: zx_time_t,
+ pending: *mut zx_signals_t) -> zx_status_t;
+
+ pub fn zx_object_get_info(handle: zx_handle_t, topic: u32, buffer: *mut c_void,
+ buffer_size: size_t, actual_size: *mut size_t,
+ avail: *mut size_t) -> zx_status_t;
+}
+
+#[derive(Default)]
+#[repr(C)]
+pub struct fdio_spawn_action_t {
+ pub action: u32,
+ pub reserved0: u32,
+ pub local_fd: i32,
+ pub target_fd: i32,
+ pub reserved1: u64,
+}
+
+extern {
+ pub fn fdio_spawn_etc(job: zx_handle_t, flags: u32, path: *const c_char,
+ argv: *const *const c_char, envp: *const *const c_char,
+ action_count: u64, actions: *const fdio_spawn_action_t,
+ process: *mut zx_handle_t, err_msg: *mut c_char) -> zx_status_t;
+}
+
+// fdio_spawn_etc flags
+
+pub const FDIO_SPAWN_CLONE_JOB: u32 = 0x0001;
+pub const FDIO_SPAWN_CLONE_LDSVC: u32 = 0x0002;
+pub const FDIO_SPAWN_CLONE_NAMESPACE: u32 = 0x0004;
+pub const FDIO_SPAWN_CLONE_STDIO: u32 = 0x0008;
+pub const FDIO_SPAWN_CLONE_ENVIRON: u32 = 0x0010;
+pub const FDIO_SPAWN_CLONE_ALL: u32 = 0xFFFF;
+
+// fdio_spawn_etc actions
+
+pub const FDIO_SPAWN_ACTION_CLONE_FD: u32 = 0x0001;
+pub const FDIO_SPAWN_ACTION_TRANSFER_FD: u32 = 0x0002;
+
+// Errors
+
+#[allow(unused)] pub const ERR_INTERNAL: zx_status_t = -1;
+
+// ERR_NOT_SUPPORTED: The operation is not implemented, supported,
+// or enabled.
+#[allow(unused)] pub const ERR_NOT_SUPPORTED: zx_status_t = -2;
+
+// ERR_NO_RESOURCES: The system was not able to allocate some resource
+// needed for the operation.
+#[allow(unused)] pub const ERR_NO_RESOURCES: zx_status_t = -3;
+
+// ERR_NO_MEMORY: The system was not able to allocate memory needed
+// for the operation.
+#[allow(unused)] pub const ERR_NO_MEMORY: zx_status_t = -4;
+
+// ERR_CALL_FAILED: The second phase of zx_channel_call(; did not complete
+// successfully.
+#[allow(unused)] pub const ERR_CALL_FAILED: zx_status_t = -5;
+
+// ERR_INTERRUPTED_RETRY: The system call was interrupted, but should be
+// retried. This should not be seen outside of the VDSO.
+#[allow(unused)] pub const ERR_INTERRUPTED_RETRY: zx_status_t = -6;
+
+// ======= Parameter errors =======
+// ERR_INVALID_ARGS: an argument is invalid, ex. null pointer
+#[allow(unused)] pub const ERR_INVALID_ARGS: zx_status_t = -10;
+
+// ERR_BAD_HANDLE: A specified handle value does not refer to a handle.
+#[allow(unused)] pub const ERR_BAD_HANDLE: zx_status_t = -11;
+
+// ERR_WRONG_TYPE: The subject of the operation is the wrong type to
+// perform the operation.
+// Example: Attempting a message_read on a thread handle.
+#[allow(unused)] pub const ERR_WRONG_TYPE: zx_status_t = -12;
+
+// ERR_BAD_SYSCALL: The specified syscall number is invalid.
+#[allow(unused)] pub const ERR_BAD_SYSCALL: zx_status_t = -13;
+
+// ERR_OUT_OF_RANGE: An argument is outside the valid range for this
+// operation.
+#[allow(unused)] pub const ERR_OUT_OF_RANGE: zx_status_t = -14;
+
+// ERR_BUFFER_TOO_SMALL: A caller provided buffer is too small for
+// this operation.
+#[allow(unused)] pub const ERR_BUFFER_TOO_SMALL: zx_status_t = -15;
+
+// ======= Precondition or state errors =======
+// ERR_BAD_STATE: operation failed because the current state of the
+// object does not allow it, or a precondition of the operation is
+// not satisfied
+#[allow(unused)] pub const ERR_BAD_STATE: zx_status_t = -20;
+
+// ERR_TIMED_OUT: The time limit for the operation elapsed before
+// the operation completed.
+#[allow(unused)] pub const ERR_TIMED_OUT: zx_status_t = -21;
+
+// ERR_SHOULD_WAIT: The operation cannot be performed currently but
+// potentially could succeed if the caller waits for a prerequisite
+// to be satisfied, for example waiting for a handle to be readable
+// or writable.
+// Example: Attempting to read from a message pipe that has no
+// messages waiting but has an open remote will return ERR_SHOULD_WAIT.
+// Attempting to read from a message pipe that has no messages waiting
+// and has a closed remote end will return ERR_REMOTE_CLOSED.
+#[allow(unused)] pub const ERR_SHOULD_WAIT: zx_status_t = -22;
+
+// ERR_CANCELED: The in-progress operation (e.g. a wait) has been
+// // canceled.
+#[allow(unused)] pub const ERR_CANCELED: zx_status_t = -23;
+
+// ERR_PEER_CLOSED: The operation failed because the remote end
+// of the subject of the operation was closed.
+#[allow(unused)] pub const ERR_PEER_CLOSED: zx_status_t = -24;
+
+// ERR_NOT_FOUND: The requested entity is not found.
+#[allow(unused)] pub const ERR_NOT_FOUND: zx_status_t = -25;
+
+// ERR_ALREADY_EXISTS: An object with the specified identifier
+// already exists.
+// Example: Attempting to create a file when a file already exists
+// with that name.
+#[allow(unused)] pub const ERR_ALREADY_EXISTS: zx_status_t = -26;
+
+// ERR_ALREADY_BOUND: The operation failed because the named entity
+// is already owned or controlled by another entity. The operation
+// could succeed later if the current owner releases the entity.
+#[allow(unused)] pub const ERR_ALREADY_BOUND: zx_status_t = -27;
+
+// ERR_UNAVAILABLE: The subject of the operation is currently unable
+// to perform the operation.
+// Note: This is used when there's no direct way for the caller to
+// observe when the subject will be able to perform the operation
+// and should thus retry.
+#[allow(unused)] pub const ERR_UNAVAILABLE: zx_status_t = -28;
+
+// ======= Permission check errors =======
+// ERR_ACCESS_DENIED: The caller did not have permission to perform
+// the specified operation.
+#[allow(unused)] pub const ERR_ACCESS_DENIED: zx_status_t = -30;
+
+// ======= Input-output errors =======
+// ERR_IO: Otherwise unspecified error occurred during I/O.
+#[allow(unused)] pub const ERR_IO: zx_status_t = -40;
+
+// ERR_REFUSED: The entity the I/O operation is being performed on
+// rejected the operation.
+// Example: an I2C device NAK'ing a transaction or a disk controller
+// rejecting an invalid command.
+#[allow(unused)] pub const ERR_IO_REFUSED: zx_status_t = -41;
+
+// ERR_IO_DATA_INTEGRITY: The data in the operation failed an integrity
+// check and is possibly corrupted.
+// Example: CRC or Parity error.
+#[allow(unused)] pub const ERR_IO_DATA_INTEGRITY: zx_status_t = -42;
+
+// ERR_IO_DATA_LOSS: The data in the operation is currently unavailable
+// and may be permanently lost.
+// Example: A disk block is irrecoverably damaged.
+#[allow(unused)] pub const ERR_IO_DATA_LOSS: zx_status_t = -43;
+
+// Filesystem specific errors
+#[allow(unused)] pub const ERR_BAD_PATH: zx_status_t = -50;
+#[allow(unused)] pub const ERR_NOT_DIR: zx_status_t = -51;
+#[allow(unused)] pub const ERR_NOT_FILE: zx_status_t = -52;
+// ERR_FILE_BIG: A file exceeds a filesystem-specific size limit.
+#[allow(unused)] pub const ERR_FILE_BIG: zx_status_t = -53;
+// ERR_NO_SPACE: Filesystem or device space is exhausted.
+#[allow(unused)] pub const ERR_NO_SPACE: zx_status_t = -54;
diff --git a/ctr-std/src/sys/unix/rand.rs b/ctr-std/src/sys/unix/rand.rs
index 4500c9d..caa1894 100644
--- a/ctr-std/src/sys/unix/rand.rs
+++ b/ctr-std/src/sys/unix/rand.rs
@@ -21,23 +21,197 @@ pub fn hashmap_random_keys() -> (u64, u64) {
return v
}
+#[cfg(all(unix,
+ not(target_os = "ios"),
+ not(target_os = "openbsd"),
+ not(target_os = "freebsd"),
+ not(target_os = "fuchsia")))]
mod imp {
- use libctru;
+ use fs::File;
+ use io::Read;
+ use libc;
+ use sys::os::errno;
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ fn getrandom(buf: &mut [u8]) -> libc::c_long {
+ unsafe {
+ libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK)
+ }
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }
+
+ fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
+ let mut read = 0;
+ while read < v.len() {
+ let result = getrandom(&mut v[read..]);
+ if result == -1 {
+ let err = errno() as libc::c_int;
+ if err == libc::EINTR {
+ continue;
+ } else if err == libc::EAGAIN {
+ return false
+ } else {
+ panic!("unexpected getrandom error: {}", err);
+ }
+ } else {
+ read += result as usize;
+ }
+ }
+
+ return true
+ }
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ fn is_getrandom_available() -> bool {
+ use io;
+ use sync::atomic::{AtomicBool, Ordering};
+ use sync::Once;
+
+ static CHECKER: Once = Once::new();
+ static AVAILABLE: AtomicBool = AtomicBool::new(false);
+
+ CHECKER.call_once(|| {
+ let mut buf: [u8; 0] = [];
+ let result = getrandom(&mut buf);
+ let available = if result == -1 {
+ let err = io::Error::last_os_error().raw_os_error();
+ err != Some(libc::ENOSYS)
+ } else {
+ true
+ };
+ AVAILABLE.store(available, Ordering::Relaxed);
+ });
+
+ AVAILABLE.load(Ordering::Relaxed)
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ fn is_getrandom_available() -> bool { false }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
+ // meaning it would have blocked because the non-blocking pool (urandom)
+ // has not initialized in the kernel yet due to a lack of entropy the
+ // fallback we do here is to avoid blocking applications which could
+ // depend on this call without ever knowing they do and don't have a
+ // work around. The PRNG of /dev/urandom will still be used but not
+ // over a completely full entropy pool
+ if is_getrandom_available() && getrandom_fill_bytes(v) {
+ return
+ }
+
+ let mut file = File::open("/dev/urandom")
+ .expect("failed to open /dev/urandom");
+ file.read_exact(v).expect("failed to read /dev/urandom");
+ }
+}
+
+#[cfg(target_os = "openbsd")]
+mod imp {
+ use libc;
+ use sys::os::errno;
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ // getentropy(2) permits a maximum buffer size of 256 bytes
+ for s in v.chunks_mut(256) {
+ let ret = unsafe {
+ libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
+ };
+ if ret == -1 {
+ panic!("unexpected getentropy error: {}", errno());
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "ios")]
+mod imp {
+ use io;
+ use libc::{c_int, size_t};
+ use ptr;
+
+ enum SecRandom {}
+
+ #[allow(non_upper_case_globals)]
+ const kSecRandomDefault: *const SecRandom = ptr::null();
+
+ extern {
+ fn SecRandomCopyBytes(rnd: *const SecRandom,
+ count: size_t,
+ bytes: *mut u8) -> c_int;
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ let ret = unsafe {
+ SecRandomCopyBytes(kSecRandomDefault,
+ v.len(),
+ v.as_mut_ptr())
+ };
+ if ret == -1 {
+ panic!("couldn't generate random bytes: {}",
+ io::Error::last_os_error());
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+mod imp {
+ use libc;
+ use ptr;
pub fn fill_bytes(v: &mut [u8]) {
+ let mib = [libc::CTL_KERN, libc::KERN_ARND];
+ // kern.arandom permits a maximum buffer size of 256 bytes
+ for s in v.chunks_mut(256) {
+ let mut s_len = s.len();
+ let ret = unsafe {
+ libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
+ s.as_mut_ptr() as *mut _, &mut s_len,
+ ptr::null(), 0)
+ };
+ if ret == -1 || s_len != s.len() {
+ panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
+ ret, s.len(), s_len);
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "fuchsia")]
+mod imp {
+ #[link(name = "zircon")]
+ extern {
+ fn zx_cprng_draw(buffer: *mut u8, len: usize, actual: *mut usize) -> i32;
+ }
+
+ fn getrandom(buf: &mut [u8]) -> Result<usize, i32> {
unsafe {
- // Initializing and de-initializing the sslC subsystem every time
- // we initialize a hashmap is pretty dumb, but I can't think of a
- // better method at the moment.
- //
- // lazy_static won't work because
- // destructors (for closing the subsystem on exit) won't run.
- //
- // Perhaps overriding __appInit() and __appExit() will work,
- // but that's an experiment for another time.
- libctru::sslcInit(0);
- libctru::sslcGenerateRandomData(v.as_ptr() as _, v.len() as u32);
- libctru::sslcExit();
+ let mut actual = 0;
+ let status = zx_cprng_draw(buf.as_mut_ptr(), buf.len(), &mut actual);
+ if status == 0 {
+ Ok(actual)
+ } else {
+ Err(status)
+ }
+ }
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ let mut buf = v;
+ while !buf.is_empty() {
+ let ret = getrandom(buf);
+ match ret {
+ Err(err) => {
+ panic!("kernel zx_cprng_draw call failed! (returned {}, buf.len() {})",
+ err, buf.len())
+ }
+ Ok(actual) => {
+ let move_buf = buf;
+ buf = &mut move_buf[(actual as usize)..];
+ }
+ }
}
}
}
diff --git a/ctr-std/src/sys/unix/rwlock.rs b/ctr-std/src/sys/unix/rwlock.rs
index 4a802e9..c754d5b 100644
--- a/ctr-std/src/sys/unix/rwlock.rs
+++ b/ctr-std/src/sys/unix/rwlock.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
@@ -8,16 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+use libc;
use cell::UnsafeCell;
-use super::mutex::Mutex;
-use super::condvar::Condvar;
+use sync::atomic::{AtomicUsize, Ordering};
-// A simple read-preferring RWLock implementation that I found on wikipedia <.<
pub struct RWLock {
- mutex: Mutex,
- cvar: Condvar,
- reader_count: UnsafeCell<u32>,
- writer_active: UnsafeCell<bool>,
+ inner: UnsafeCell<libc::pthread_rwlock_t>,
+ write_locked: UnsafeCell<bool>,
+ num_readers: AtomicUsize,
}
unsafe impl Send for RWLock {}
@@ -26,102 +24,119 @@ unsafe impl Sync for RWLock {}
impl RWLock {
pub const fn new() -> RWLock {
RWLock {
- mutex: Mutex::new(),
- cvar: Condvar::new(),
- reader_count: UnsafeCell::new(0),
- writer_active: UnsafeCell::new(false),
+ inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER),
+ write_locked: UnsafeCell::new(false),
+ num_readers: AtomicUsize::new(0),
}
}
-
#[inline]
pub unsafe fn read(&self) {
- self.mutex.lock();
-
- while *self.writer_active.get() {
- self.cvar.wait(&self.mutex);
+ let r = libc::pthread_rwlock_rdlock(self.inner.get());
+
+ // According to the pthread_rwlock_rdlock spec, this function **may**
+ // fail with EDEADLK if a deadlock is detected. On the other hand
+ // pthread mutexes will *never* return EDEADLK if they are initialized
+ // as the "fast" kind (which ours always are). As a result, a deadlock
+ // situation may actually return from the call to pthread_rwlock_rdlock
+ // instead of blocking forever (as mutexes and Windows rwlocks do). Note
+ // that not all unix implementations, however, will return EDEADLK for
+ // their rwlocks.
+ //
+ // We roughly maintain the deadlocking behavior by panicking to ensure
+ // that this lock acquisition does not succeed.
+ //
+ // We also check whether this lock is already write locked. This
+ // is only possible if it was write locked by the current thread and
+ // the implementation allows recursive locking. The POSIX standard
+ // doesn't require recursively locking a rwlock to deadlock, but we can't
+ // allow that because it could lead to aliasing issues.
+ if r == libc::EAGAIN {
+ panic!("rwlock maximum reader count exceeded");
+ } else if r == libc::EDEADLK || *self.write_locked.get() {
+ if r == 0 {
+ self.raw_unlock();
+ }
+ panic!("rwlock read lock would result in deadlock");
+ } else {
+ debug_assert_eq!(r, 0);
+ self.num_readers.fetch_add(1, Ordering::Relaxed);
}
-
- assert!(*self.reader_count.get() != u32::max_value());
- *self.reader_count.get() += 1;
-
- self.mutex.unlock();
}
-
#[inline]
pub unsafe fn try_read(&self) -> bool {
- if !self.mutex.try_lock() {
- return false
- }
-
- while *self.writer_active.get() {
- self.cvar.wait(&self.mutex);
+ let r = libc::pthread_rwlock_tryrdlock(self.inner.get());
+ if r == 0 {
+ if *self.write_locked.get() {
+ self.raw_unlock();
+ false
+ } else {
+ self.num_readers.fetch_add(1, Ordering::Relaxed);
+ true
+ }
+ } else {
+ false
}
-
- assert!(*self.reader_count.get() != u32::max_value());
- *self.reader_count.get() += 1;
-
- self.mutex.unlock();
- true
}
-
#[inline]
pub unsafe fn write(&self) {
- self.mutex.lock();
-
- while *self.writer_active.get() || *self.reader_count.get() > 0 {
- self.cvar.wait(&self.mutex);
+ let r = libc::pthread_rwlock_wrlock(self.inner.get());
+ // See comments above for why we check for EDEADLK and write_locked. We
+ // also need to check that num_readers is 0.
+ if r == libc::EDEADLK || *self.write_locked.get() ||
+ self.num_readers.load(Ordering::Relaxed) != 0 {
+ if r == 0 {
+ self.raw_unlock();
+ }
+ panic!("rwlock write lock would result in deadlock");
+ } else {
+ debug_assert_eq!(r, 0);
}
-
- *self.writer_active.get() = true;
-
- self.mutex.unlock();
+ *self.write_locked.get() = true;
}
-
#[inline]
pub unsafe fn try_write(&self) -> bool {
- if !self.mutex.try_lock() {
- return false;
- }
-
- while *self.writer_active.get() || *self.reader_count.get() > 0 {
- self.cvar.wait(&self.mutex);
+ let r = libc::pthread_rwlock_trywrlock(self.inner.get());
+ if r == 0 {
+ if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 {
+ self.raw_unlock();
+ false
+ } else {
+ *self.write_locked.get() = true;
+ true
+ }
+ } else {
+ false
}
-
- *self.writer_active.get() = true;
-
- self.mutex.unlock();
- true
}
-
+ #[inline]
+ unsafe fn raw_unlock(&self) {
+ let r = libc::pthread_rwlock_unlock(self.inner.get());
+ debug_assert_eq!(r, 0);
+ }
#[inline]
pub unsafe fn read_unlock(&self) {
- self.mutex.lock();
-
- *self.reader_count.get() -= 1;
-
- if *self.reader_count.get() == 0 {
- self.cvar.notify_one()
- }
-
- self.mutex.unlock();
+ debug_assert!(!*self.write_locked.get());
+ self.num_readers.fetch_sub(1, Ordering::Relaxed);
+ self.raw_unlock();
}
-
#[inline]
pub unsafe fn write_unlock(&self) {
- self.mutex.lock();
-
- *self.writer_active.get() = false;
-
- self.cvar.notify_all();
-
- self.mutex.unlock();
+ debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0);
+ debug_assert!(*self.write_locked.get());
+ *self.write_locked.get() = false;
+ self.raw_unlock();
}
-
#[inline]
pub unsafe fn destroy(&self) {
- self.mutex.destroy();
- self.cvar.destroy();
- *self.reader_count.get() = 0;
- *self.writer_active.get() = false;
+ let r = libc::pthread_rwlock_destroy(self.inner.get());
+ // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a
+ // rwlock that was just initialized with
+ // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked)
+ // or pthread_rwlock_init() is called, this behaviour no longer occurs.
+ if cfg!(target_os = "dragonfly") {
+ debug_assert!(r == 0 || r == libc::EINVAL);
+ } else {
+ debug_assert_eq!(r, 0);
+ }
}
}
diff --git a/ctr-std/src/sys/unix/stack_overflow.rs b/ctr-std/src/sys/unix/stack_overflow.rs
index bed2741..40453f9 100644
--- a/ctr-std/src/sys/unix/stack_overflow.rs
+++ b/ctr-std/src/sys/unix/stack_overflow.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@@ -8,16 +8,213 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-pub struct Handler;
+#![cfg_attr(test, allow(dead_code))]
+
+use libc;
+use self::imp::{make_handler, drop_handler};
+
+pub use self::imp::cleanup;
+pub use self::imp::init;
+
+pub struct Handler {
+ _data: *mut libc::c_void
+}
impl Handler {
pub unsafe fn new() -> Handler {
- Handler
+ make_handler()
}
}
-pub unsafe fn init() {
+impl Drop for Handler {
+ fn drop(&mut self) {
+ unsafe {
+ drop_handler(self);
+ }
+ }
}
-pub unsafe fn cleanup() {
+#[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "bitrig",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "solaris",
+ all(target_os = "netbsd", not(target_vendor = "rumprun")),
+ target_os = "openbsd"))]
+mod imp {
+ use super::Handler;
+ use mem;
+ use ptr;
+ use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
+ use libc::{sigaction, SIGBUS, SIG_DFL,
+ SA_SIGINFO, SA_ONSTACK, sighandler_t};
+ use libc;
+ use libc::{mmap, munmap};
+ use libc::{SIGSEGV, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON};
+ use libc::MAP_FAILED;
+
+ use sys_common::thread_info;
+
+
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
+ #[repr(C)]
+ struct siginfo_t {
+ a: [libc::c_int; 3], // si_signo, si_errno, si_code
+ si_addr: *mut libc::c_void,
+ }
+
+ (*(info as *const siginfo_t)).si_addr as usize
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
+ (*info).si_addr as usize
+ }
+
+ // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
+ // (unmapped pages) at the end of every thread's stack, so if a thread ends
+ // up running into the guard page it'll trigger this handler. We want to
+ // detect these cases and print out a helpful error saying that the stack
+ // has overflowed. All other signals, however, should go back to what they
+ // were originally supposed to do.
+ //
+ // This handler currently exists purely to print an informative message
+ // whenever a thread overflows its stack. We then abort to exit and
+ // indicate a crash, but to avoid a misleading SIGSEGV that might lead
+ // users to believe that unsafe code has accessed an invalid pointer; the
+ // SIGSEGV encountered when overflowing the stack is expected and
+ // well-defined.
+ //
+ // If this is not a stack overflow, the handler un-registers itself and
+ // then returns (to allow the original signal to be delivered again).
+ // Returning from this kind of signal handler is technically not defined
+ // to work when reading the POSIX spec strictly, but in practice it turns
+ // out many large systems and all implementations allow returning from a
+ // signal handler to work. For a more detailed explanation see the
+ // comments on #26458.
+ unsafe extern fn signal_handler(signum: libc::c_int,
+ info: *mut libc::siginfo_t,
+ _data: *mut libc::c_void) {
+ use sys_common::util::report_overflow;
+
+ let guard = thread_info::stack_guard().unwrap_or(0..0);
+ let addr = siginfo_si_addr(info);
+
+ // If the faulting address is within the guard page, then we print a
+ // message saying so and abort.
+ if guard.start <= addr && addr < guard.end {
+ report_overflow();
+ rtabort!("stack overflow");
+ } else {
+ // Unregister ourselves by reverting back to the default behavior.
+ let mut action: sigaction = mem::zeroed();
+ action.sa_sigaction = SIG_DFL;
+ sigaction(signum, &action, ptr::null_mut());
+
+ // See comment above for why this function returns.
+ }
+ }
+
+ static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut();
+
+ pub unsafe fn init() {
+ let mut action: sigaction = mem::zeroed();
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ action.sa_sigaction = signal_handler as sighandler_t;
+ sigaction(SIGSEGV, &action, ptr::null_mut());
+ sigaction(SIGBUS, &action, ptr::null_mut());
+
+ let handler = make_handler();
+ MAIN_ALTSTACK = handler._data;
+ mem::forget(handler);
+ }
+
+ pub unsafe fn cleanup() {
+ Handler { _data: MAIN_ALTSTACK };
+ }
+
+ unsafe fn get_stackp() -> *mut libc::c_void {
+ let stackp = mmap(ptr::null_mut(),
+ SIGSTKSZ,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ -1,
+ 0);
+ if stackp == MAP_FAILED {
+ panic!("failed to allocate an alternative stack");
+ }
+ stackp
+ }
+
+ #[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "bitrig",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ unsafe fn get_stack() -> libc::stack_t {
+ libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
+ }
+
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly"))]
+ unsafe fn get_stack() -> libc::stack_t {
+ libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ }
+ }
+
+ pub unsafe fn make_handler() -> Handler {
+ let mut stack = mem::zeroed();
+ sigaltstack(ptr::null(), &mut stack);
+ // Configure alternate signal stack, if one is not already set.
+ if stack.ss_flags & SS_DISABLE != 0 {
+ stack = get_stack();
+ sigaltstack(&stack, ptr::null_mut());
+ Handler { _data: stack.ss_sp as *mut libc::c_void }
+ } else {
+ Handler { _data: ptr::null_mut() }
+ }
+ }
+
+ pub unsafe fn drop_handler(handler: &mut Handler) {
+ if !handler._data.is_null() {
+ let stack = libc::stack_t {
+ ss_sp: ptr::null_mut(),
+ ss_flags: SS_DISABLE,
+ // Workaround for bug in macOS implementation of sigaltstack
+ // UNIX2003 which returns ENOMEM when disabling a stack while
+ // passing ss_size smaller than MINSIGSTKSZ. According to POSIX
+ // both ss_sp and ss_size should be ignored in this case.
+ ss_size: SIGSTKSZ,
+ };
+ sigaltstack(&stack, ptr::null_mut());
+ munmap(handler._data, SIGSTKSZ);
+ }
+ }
+}
+
+#[cfg(not(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "bitrig",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "solaris",
+ all(target_os = "netbsd", not(target_vendor = "rumprun")),
+ target_os = "openbsd")))]
+mod imp {
+ use ptr;
+
+ pub unsafe fn init() {
+ }
+
+ pub unsafe fn cleanup() {
+ }
+
+ pub unsafe fn make_handler() -> super::Handler {
+ super::Handler { _data: ptr::null_mut() }
+ }
+
+ pub unsafe fn drop_handler(_handler: &mut super::Handler) {
+ }
}
diff --git a/ctr-std/src/sys/unix/thread.rs b/ctr-std/src/sys/unix/thread.rs
index 694f85a..7fdecc9 100644
--- a/ctr-std/src/sys/unix/thread.rs
+++ b/ctr-std/src/sys/unix/thread.rs
@@ -1,4 +1,4 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@@ -8,99 +8,450 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use alloc_crate::boxed::FnBox;
-use libc;
+use boxed::FnBox;
use cmp;
use ffi::CStr;
use io;
+use libc;
use mem;
use ptr;
-use sys_common::thread::start_thread;
+use sys::os;
use time::Duration;
-use libctru::Thread as ThreadHandle;
+use sys_common::thread::*;
+
+#[cfg(not(target_os = "l4re"))]
+pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
+#[cfg(target_os = "l4re")]
+pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
pub struct Thread {
- handle: ThreadHandle,
+ id: libc::pthread_t,
}
+// 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;
+// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc,
+// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS.
+#[cfg(not(target_os = "emscripten"))]
+unsafe fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
+ stack_size: libc::size_t) -> libc::c_int {
+ libc::pthread_attr_setstacksize(attr, stack_size)
+}
+#[cfg(target_os = "emscripten")]
+unsafe fn pthread_attr_setstacksize(_attr: *mut libc::pthread_attr_t,
+ _stack_size: libc::size_t) -> libc::c_int {
+ panic!()
+}
impl Thread {
- pub unsafe fn new<'a>(stack: usize, p: Box<FnBox() + 'a>) -> io::Result<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, DEFAULT_MIN_STACK_SIZE);
+ let mut native: libc::pthread_t = mem::zeroed();
+ let mut attr: libc::pthread_attr_t = mem::zeroed();
+ assert_eq!(libc::pthread_attr_init(&mut attr), 0);
- let mut priority = 0;
- ::libctru::svcGetThreadPriority(&mut priority, 0xFFFF8000);
+ let stack_size = cmp::max(stack, min_stack_size(&attr));
- let handle = ::libctru::threadCreate(Some(thread_func), &*p as *const _ as *mut _,
- stack_size, priority, -2, false);
+ match pthread_attr_setstacksize(&mut attr,
+ stack_size) {
+ 0 => {}
+ n => {
+ assert_eq!(n, libc::EINVAL);
+ // EINVAL means |stack_size| is either too small or not a
+ // multiple of the system page size. Because it's definitely
+ // >= PTHREAD_STACK_MIN, it must be an alignment issue.
+ // Round up to the nearest page and try again.
+ let page_size = os::page_size();
+ let stack_size = (stack_size + page_size - 1) &
+ (-(page_size as isize - 1) as usize - 1);
+ assert_eq!(libc::pthread_attr_setstacksize(&mut attr,
+ stack_size), 0);
+ }
+ };
+
+ let ret = libc::pthread_create(&mut native, &attr, thread_start,
+ &*p as *const _ as *mut _);
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
- return if handle == ptr::null_mut() {
- Err(io::Error::from_raw_os_error(libc::EAGAIN))
+ return if ret != 0 {
+ Err(io::Error::from_raw_os_error(ret))
} else {
- mem::forget(p); // ownership passed to the new thread
- Ok(Thread { handle: handle })
+ mem::forget(p); // ownership passed to pthread_create
+ Ok(Thread { id: native })
};
- extern "C" fn thread_func(start: *mut libc::c_void) {
- unsafe { start_thread(start as *mut u8) }
+ extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
+ unsafe { start_thread(main as *mut u8); }
+ ptr::null_mut()
}
}
-
+
pub fn yield_now() {
+ let ret = unsafe { libc::sched_yield() };
+ debug_assert_eq!(ret, 0);
+ }
+
+ #[cfg(any(target_os = "linux",
+ target_os = "android"))]
+ pub fn set_name(name: &CStr) {
+ const PR_SET_NAME: libc::c_int = 15;
+ // pthread wrapper only appeared in glibc 2.12, so we use syscall
+ // directly.
unsafe {
- ::libctru::svcSleepThread(0)
+ libc::prctl(PR_SET_NAME, name.as_ptr() as libc::c_ulong, 0, 0, 0);
}
}
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "bitrig",
+ target_os = "openbsd"))]
+ pub fn set_name(name: &CStr) {
+ unsafe {
+ libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr());
+ }
+ }
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ pub fn set_name(name: &CStr) {
+ unsafe {
+ libc::pthread_setname_np(name.as_ptr());
+ }
+ }
+
+ #[cfg(target_os = "netbsd")]
+ pub fn set_name(name: &CStr) {
+ use ffi::CString;
+ let cname = CString::new(&b"%s"[..]).unwrap();
+ unsafe {
+ libc::pthread_setname_np(libc::pthread_self(), cname.as_ptr(),
+ name.as_ptr() as *mut libc::c_void);
+ }
+ }
+ #[cfg(any(target_env = "newlib",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "l4re",
+ target_os = "emscripten"))]
pub fn set_name(_name: &CStr) {
- // threads aren't named in libctru
+ // Newlib, Illumos, Haiku, and Emscripten have no way to set a thread name.
+ }
+ #[cfg(target_os = "fuchsia")]
+ pub fn set_name(_name: &CStr) {
+ // FIXME: determine whether Fuchsia has a way to set a thread name.
}
pub fn sleep(dur: Duration) {
+ let mut secs = dur.as_secs();
+ let mut nsecs = dur.subsec_nanos() as _;
+
+ // If we're awoken with a signal then the return value will be -1 and
+ // nanosleep will fill in `ts` with the remaining time.
unsafe {
- let nanos = dur.as_secs()
- .saturating_mul(1_000_000_000)
- .saturating_add(dur.subsec_nanos() as u64);
- ::libctru::svcSleepThread(nanos as i64)
+ while secs > 0 || nsecs > 0 {
+ let mut ts = libc::timespec {
+ tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
+ tv_nsec: nsecs,
+ };
+ secs -= ts.tv_sec as u64;
+ if libc::nanosleep(&ts, &mut ts) == -1 {
+ assert_eq!(os::errno(), libc::EINTR);
+ secs += ts.tv_sec as u64;
+ nsecs = ts.tv_nsec;
+ } else {
+ nsecs = 0;
+ }
+ }
}
}
pub fn join(self) {
unsafe {
- let ret = ::libctru::threadJoin(self.handle, u64::max_value());
- ::libctru::threadFree(self.handle);
+ let ret = libc::pthread_join(self.id, ptr::null_mut());
mem::forget(self);
- debug_assert_eq!(ret, 0);
+ assert!(ret == 0,
+ "failed to join thread: {}", io::Error::from_raw_os_error(ret));
}
}
- #[allow(dead_code)]
- pub fn id(&self) -> ThreadHandle {
- self.handle
- }
+ pub fn id(&self) -> libc::pthread_t { self.id }
- #[allow(dead_code)]
- pub fn into_id(self) -> ThreadHandle {
- let handle = self.handle;
+ pub fn into_id(self) -> libc::pthread_t {
+ let id = self.id;
mem::forget(self);
- handle
+ id
}
}
impl Drop for Thread {
fn drop(&mut self) {
- unsafe { ::libctru::threadDetach(self.handle) }
+ let ret = unsafe { libc::pthread_detach(self.id) };
+ debug_assert_eq!(ret, 0);
}
}
+#[cfg(all(not(all(target_os = "linux", not(target_env = "musl"))),
+ not(target_os = "freebsd"),
+ not(target_os = "macos"),
+ not(target_os = "bitrig"),
+ not(all(target_os = "netbsd", not(target_vendor = "rumprun"))),
+ not(target_os = "openbsd"),
+ not(target_os = "solaris")))]
+#[cfg_attr(test, allow(dead_code))]
+pub mod guard {
+ use ops::Range;
+ pub type Guard = Range<usize>;
+ pub unsafe fn current() -> Option<Guard> { None }
+ pub unsafe fn init() -> Option<Guard> { None }
+ pub unsafe fn deinit() {}
+}
+
+
+#[cfg(any(all(target_os = "linux", not(target_env = "musl")),
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "bitrig",
+ all(target_os = "netbsd", not(target_vendor = "rumprun")),
+ target_os = "openbsd",
+ target_os = "solaris"))]
+#[cfg_attr(test, allow(dead_code))]
pub mod guard {
- pub unsafe fn current() -> Option<usize> { None }
- pub unsafe fn init() -> Option<usize> { None }
+ use libc;
+ use libc::{mmap, mprotect};
+ use libc::{PROT_NONE, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON, MAP_FAILED, MAP_FIXED};
+ use ops::Range;
+ use sys::os;
+
+ // This is initialized in init() and only read from after
+ static mut PAGE_SIZE: usize = 0;
+
+ pub type Guard = Range<usize>;
+
+ #[cfg(target_os = "solaris")]
+ unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+ let mut current_stack: libc::stack_t = ::mem::zeroed();
+ assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
+ Some(current_stack.ss_sp)
+ }
+
+ #[cfg(target_os = "macos")]
+ unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+ let stackaddr = libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize -
+ libc::pthread_get_stacksize_np(libc::pthread_self());
+ Some(stackaddr as *mut libc::c_void)
+ }
+
+ #[cfg(any(target_os = "openbsd", target_os = "bitrig"))]
+ unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+ let mut current_stack: libc::stack_t = ::mem::zeroed();
+ assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(),
+ &mut current_stack), 0);
+
+ let extra = if cfg!(target_os = "bitrig") {3} else {1} * PAGE_SIZE;
+ let stackaddr = if libc::pthread_main_np() == 1 {
+ // main thread
+ current_stack.ss_sp as usize - current_stack.ss_size + extra
+ } else {
+ // new thread
+ current_stack.ss_sp as usize - current_stack.ss_size
+ };
+ Some(stackaddr as *mut libc::c_void)
+ }
+
+ #[cfg(any(target_os = "android", target_os = "freebsd",
+ target_os = "linux", target_os = "netbsd", target_os = "l4re"))]
+ unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
+ let mut ret = None;
+ let mut attr: libc::pthread_attr_t = ::mem::zeroed();
+ assert_eq!(libc::pthread_attr_init(&mut attr), 0);
+ #[cfg(target_os = "freebsd")]
+ let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
+ #[cfg(not(target_os = "freebsd"))]
+ let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
+ if e == 0 {
+ let mut stackaddr = ::ptr::null_mut();
+ let mut stacksize = 0;
+ assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr,
+ &mut stacksize), 0);
+ ret = Some(stackaddr);
+ }
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+ ret
+ }
+
+ // Precondition: PAGE_SIZE is initialized.
+ unsafe fn get_stack_start_aligned() -> Option<*mut libc::c_void> {
+ assert!(PAGE_SIZE != 0);
+ let stackaddr = get_stack_start()?;
+
+ // Ensure stackaddr is page aligned! A parent process might
+ // have reset RLIMIT_STACK to be non-page aligned. The
+ // pthread_attr_getstack() reports the usable stack area
+ // stackaddr < stackaddr + stacksize, so if stackaddr is not
+ // page-aligned, calculate the fix such that stackaddr <
+ // new_page_aligned_stackaddr < stackaddr + stacksize
+ let remainder = (stackaddr as usize) % PAGE_SIZE;
+ Some(if remainder == 0 {
+ stackaddr
+ } else {
+ ((stackaddr as usize) + PAGE_SIZE - remainder) as *mut libc::c_void
+ })
+ }
+
+ pub unsafe fn init() -> Option<Guard> {
+ PAGE_SIZE = os::page_size();
+
+ let stackaddr = get_stack_start_aligned()?;
+
+ if cfg!(target_os = "linux") {
+ // Linux doesn't allocate the whole stack right away, and
+ // the kernel has its own stack-guard mechanism to fault
+ // when growing too close to an existing mapping. If we map
+ // our own guard, then the kernel starts enforcing a rather
+ // large gap above that, rendering much of the possible
+ // stack space useless. See #43052.
+ //
+ // Instead, we'll just note where we expect rlimit to start
+ // faulting, so our handler can report "stack overflow", and
+ // trust that the kernel's own stack guard will work.
+ let stackaddr = stackaddr as usize;
+ Some(stackaddr - PAGE_SIZE..stackaddr)
+ } else {
+ // Reallocate the last page of the stack.
+ // This ensures SIGBUS will be raised on
+ // stack overflow.
+ // Systems which enforce strict PAX MPROTECT do not allow
+ // to mprotect() a mapping with less restrictive permissions
+ // than the initial mmap() used, so we mmap() here with
+ // read/write permissions and only then mprotect() it to
+ // no permissions at all. See issue #50313.
+ let result = mmap(stackaddr, PAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
+ if result != stackaddr || result == MAP_FAILED {
+ panic!("failed to allocate a guard page");
+ }
+
+ let result = mprotect(stackaddr, PAGE_SIZE, PROT_NONE);
+ if result != 0 {
+ panic!("failed to protect the guard page");
+ }
+
+ let guardaddr = stackaddr as usize;
+ let offset = if cfg!(target_os = "freebsd") {
+ 2
+ } else {
+ 1
+ };
+
+ Some(guardaddr..guardaddr + offset * PAGE_SIZE)
+ }
+ }
+
+ pub unsafe fn deinit() {
+ if !cfg!(target_os = "linux") {
+ if let Some(stackaddr) = get_stack_start_aligned() {
+ // Remove the protection on the guard page.
+ // FIXME: we cannot unmap the page, because when we mmap()
+ // above it may be already mapped by the OS, which we can't
+ // detect from mmap()'s return value. If we unmap this page,
+ // it will lead to failure growing stack size on platforms like
+ // macOS. Instead, just restore the page to a writable state.
+ // This ain't Linux, so we probably don't need to care about
+ // execstack.
+ let result = mprotect(stackaddr, PAGE_SIZE, PROT_READ | PROT_WRITE);
+
+ if result != 0 {
+ panic!("unable to reset the guard page");
+ }
+ }
+ }
+ }
+
+ #[cfg(any(target_os = "macos",
+ target_os = "bitrig",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ pub unsafe fn current() -> Option<Guard> {
+ let stackaddr = get_stack_start()? as usize;
+ Some(stackaddr - PAGE_SIZE..stackaddr)
+ }
+
+ #[cfg(any(target_os = "android", target_os = "freebsd",
+ target_os = "linux", target_os = "netbsd", target_os = "l4re"))]
+ pub unsafe fn current() -> Option<Guard> {
+ let mut ret = None;
+ let mut attr: libc::pthread_attr_t = ::mem::zeroed();
+ assert_eq!(libc::pthread_attr_init(&mut attr), 0);
+ #[cfg(target_os = "freebsd")]
+ let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
+ #[cfg(not(target_os = "freebsd"))]
+ let e = libc::pthread_getattr_np(libc::pthread_self(), &mut attr);
+ if e == 0 {
+ let mut guardsize = 0;
+ assert_eq!(libc::pthread_attr_getguardsize(&attr, &mut guardsize), 0);
+ if guardsize == 0 {
+ panic!("there is no guard page");
+ }
+ let mut stackaddr = ::ptr::null_mut();
+ let mut size = 0;
+ assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr,
+ &mut size), 0);
+
+ let stackaddr = stackaddr as usize;
+ ret = if cfg!(target_os = "freebsd") {
+ // FIXME does freebsd really fault *below* the guard addr?
+ let guardaddr = stackaddr - guardsize;
+ Some(guardaddr - PAGE_SIZE..guardaddr)
+ } else if cfg!(target_os = "netbsd") {
+ Some(stackaddr - guardsize..stackaddr)
+ } else if cfg!(all(target_os = "linux", target_env = "gnu")) {
+ // glibc used to include the guard area within the stack, as noted in the BUGS
+ // section of `man pthread_attr_getguardsize`. This has been corrected starting
+ // with glibc 2.27, and in some distro backports, so the guard is now placed at the
+ // end (below) the stack. There's no easy way for us to know which we have at
+ // runtime, so we'll just match any fault in the range right above or below the
+ // stack base to call that fault a stack overflow.
+ Some(stackaddr - guardsize..stackaddr + guardsize)
+ } else {
+ Some(stackaddr..stackaddr + guardsize)
+ };
+ }
+ assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+ ret
+ }
+}
+
+// glibc >= 2.15 has a __pthread_get_minstack() function that returns
+// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
+// storage. We need that information to avoid blowing up when a small stack
+// is created in an application with big thread-local storage requirements.
+// See #6233 for rationale and details.
+#[cfg(target_os = "linux")]
+#[allow(deprecated)]
+fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
+ weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
+
+ match __pthread_get_minstack.get() {
+ None => libc::PTHREAD_STACK_MIN,
+ Some(f) => unsafe { f(attr) },
+ }
+}
+
+// No point in looking up __pthread_get_minstack() on non-glibc
+// platforms.
+#[cfg(all(not(target_os = "linux"),
+ not(target_os = "netbsd")))]
+fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
+ libc::PTHREAD_STACK_MIN
+}
+
+#[cfg(target_os = "netbsd")]
+fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
+ 2048 // just a guess
}
diff --git a/ctr-std/src/sys/unix/thread_local.rs b/ctr-std/src/sys/unix/thread_local.rs
index 3cb3523..2487f6b 100644
--- a/ctr-std/src/sys/unix/thread_local.rs
+++ b/ctr-std/src/sys/unix/thread_local.rs
@@ -1,4 +1,4 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@@ -8,63 +8,35 @@
// 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;
-use ptr;
-use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
-
-pub type Key = usize;
-
-type Dtor = unsafe extern fn(*mut u8);
-
-static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT;
+use mem;
+use libc;
-static mut KEYS: *mut BTreeMap<Key, Option<Dtor>> = ptr::null_mut();
-
-#[thread_local]
-static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut();
-
-unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> {
- if KEYS == ptr::null_mut() {
- KEYS = Box::into_raw(Box::new(BTreeMap::new()));
- }
- &mut *KEYS
-}
-
-unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> {
- if LOCALS == ptr::null_mut() {
- LOCALS = Box::into_raw(Box::new(BTreeMap::new()));
- }
- &mut *LOCALS
-}
+pub type Key = libc::pthread_key_t;
#[inline]
-pub unsafe fn create(dtor: Option<Dtor>) -> Key {
- let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst);
- keys().insert(key, dtor);
+pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
+ let mut key = 0;
+ assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0);
key
}
#[inline]
-pub unsafe fn get(key: Key) -> *mut u8 {
- if let Some(&entry) = locals().get(&key) {
- entry
- } else {
- ptr::null_mut()
- }
+pub unsafe fn set(key: Key, value: *mut u8) {
+ let r = libc::pthread_setspecific(key, value as *mut _);
+ debug_assert_eq!(r, 0);
}
#[inline]
-pub unsafe fn set(key: Key, value: *mut u8) {
- locals().insert(key, value);
+pub unsafe fn get(key: Key) -> *mut u8 {
+ libc::pthread_getspecific(key) as *mut u8
}
#[inline]
pub unsafe fn destroy(key: Key) {
- keys().remove(&key);
+ let r = libc::pthread_key_delete(key);
+ debug_assert_eq!(r, 0);
}
#[inline]
diff --git a/ctr-std/src/sys/unix/time.rs b/ctr-std/src/sys/unix/time.rs
index d5dcbc4..89786eb 100644
--- a/ctr-std/src/sys/unix/time.rs
+++ b/ctr-std/src/sys/unix/time.rs
@@ -14,6 +14,7 @@ use time::Duration;
use core::hash::{Hash, Hasher};
pub use self::inner::{Instant, SystemTime, UNIX_EPOCH};
+use convert::TryInto;
const NSEC_PER_SEC: u64 = 1_000_000_000;
@@ -42,8 +43,12 @@ impl Timespec {
}
fn add_duration(&self, other: &Duration) -> Timespec {
- let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64);
- let mut secs = secs.expect("overflow when adding duration to time");
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `libc::time_t`
+ .ok()
+ .and_then(|secs| self.t.tv_sec.checked_add(secs))
+ .expect("overflow when adding duration to time");
// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
@@ -55,16 +60,19 @@ impl Timespec {
}
Timespec {
t: libc::timespec {
- tv_sec: secs as libc::time_t,
- tv_nsec: nsec as libc::c_long,
+ tv_sec: secs,
+ tv_nsec: nsec as _,
},
}
}
fn sub_duration(&self, other: &Duration) -> Timespec {
- let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64);
- let mut secs = secs.expect("overflow when subtracting duration \
- from time");
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `libc::time_t`
+ .ok()
+ .and_then(|secs| self.t.tv_sec.checked_sub(secs))
+ .expect("overflow when subtracting duration from time");
// Similar to above, nanos can't overflow.
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
@@ -75,8 +83,8 @@ impl Timespec {
}
Timespec {
t: libc::timespec {
- tv_sec: secs as libc::time_t,
- tv_nsec: nsec as libc::c_long,
+ tv_sec: secs,
+ tv_nsec: nsec as _,
},
}
}
@@ -111,6 +119,7 @@ impl Hash for Timespec {
}
}
+#[cfg(any(target_os = "macos", target_os = "ios"))]
mod inner {
use fmt;
use libc;
@@ -122,8 +131,6 @@ mod inner {
use super::NSEC_PER_SEC;
use super::Timespec;
- use libctru;
-
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant {
t: u64
@@ -145,7 +152,7 @@ mod inner {
impl Instant {
pub fn now() -> Instant {
- Instant { t: ctr_absolute_time() }
+ Instant { t: unsafe { libc::mach_absolute_time() } }
}
pub fn sub_instant(&self, other: &Instant) -> Duration {
@@ -166,71 +173,11 @@ mod inner {
pub fn sub_duration(&self, other: &Duration) -> Instant {
Instant {
t: self.t.checked_sub(dur2intervals(other))
- .expect("overflow when adding duration to instant"),
+ .expect("overflow when subtracting duration from instant"),
}
}
}
- // The initial system tick after which all Instants occur
- static mut TICK: u64 = 0;
-
- // A source of monotonic time based on ticks of the 3DS CPU. Returns the
- // number of system ticks elapsed since an arbitrary point in the past
- fn ctr_absolute_time() -> u64 {
- let first_tick = get_first_tick();
- let current_tick = get_system_tick();
- current_tick - first_tick
- }
-
- // The first time this function is called, it generates and returns the
- // initial system tick used to create Instants
- //
- // subsequent calls to this function return the previously generated
- // tick value
- fn get_first_tick() -> u64 {
- static ONCE: Once = Once::new();
- unsafe {
- ONCE.call_once(|| {
- TICK = get_system_tick();
- });
- TICK
- }
- }
-
- // Gets the current system tick
- #[inline]
- fn get_system_tick() -> u64 {
- unsafe { libctru::svcGetSystemTick() }
- }
-
- // A struct representing the clock speed of the 3DS
- struct CtrClockInfo {
- numer: u32,
- denom: u32,
- }
-
- // Initializes the CtrClockInfo struct
- //
- // Note that svcGetSystemTick always runs at 268MHz (268,111,856Hz), even
- // on a New 3DS running in 804MHz mode
- //
- // See https://www.3dbrew.org/wiki/Hardware#Common_hardware
- fn info() -> &'static CtrClockInfo {
- static INFO: CtrClockInfo = CtrClockInfo {
- numer: 1_000_000_000,
- denom: 268_111_856,
- };
- &INFO
- }
-
- fn dur2intervals(dur: &Duration) -> u64 {
- let info = info();
- let nanos = dur.as_secs().checked_mul(NSEC_PER_SEC).and_then(|nanos| {
- nanos.checked_add(dur.subsec_nanos() as u64)
- }).expect("overflow converting duration to nanoseconds");
- mul_div_u64(nanos, info.denom as u64, info.numer as u64)
- }
-
impl SystemTime {
pub fn now() -> SystemTime {
use ptr;
@@ -282,4 +229,137 @@ mod inner {
.finish()
}
}
+
+ fn dur2intervals(dur: &Duration) -> u64 {
+ let info = info();
+ let nanos = dur.as_secs().checked_mul(NSEC_PER_SEC).and_then(|nanos| {
+ nanos.checked_add(dur.subsec_nanos() as u64)
+ }).expect("overflow converting duration to nanoseconds");
+ mul_div_u64(nanos, info.denom as u64, info.numer as u64)
+ }
+
+ fn info() -> &'static libc::mach_timebase_info {
+ static mut INFO: libc::mach_timebase_info = libc::mach_timebase_info {
+ numer: 0,
+ denom: 0,
+ };
+ static ONCE: Once = Once::new();
+
+ unsafe {
+ ONCE.call_once(|| {
+ libc::mach_timebase_info(&mut INFO);
+ });
+ &INFO
+ }
+ }
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+mod inner {
+ use fmt;
+ use libc;
+ use sys::cvt;
+ use time::Duration;
+
+ use super::Timespec;
+
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub struct Instant {
+ t: Timespec,
+ }
+
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub struct SystemTime {
+ t: Timespec,
+ }
+
+ pub const UNIX_EPOCH: SystemTime = SystemTime {
+ t: Timespec {
+ t: libc::timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ },
+ },
+ };
+
+ impl Instant {
+ pub fn now() -> Instant {
+ Instant { t: now(libc::CLOCK_MONOTONIC) }
+ }
+
+ pub fn sub_instant(&self, other: &Instant) -> Duration {
+ self.t.sub_timespec(&other.t).unwrap_or_else(|_| {
+ panic!("specified instant was later than self")
+ })
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> Instant {
+ Instant { t: self.t.add_duration(other) }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> Instant {
+ Instant { t: self.t.sub_duration(other) }
+ }
+ }
+
+ impl fmt::Debug for Instant {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Instant")
+ .field("tv_sec", &self.t.t.tv_sec)
+ .field("tv_nsec", &self.t.t.tv_nsec)
+ .finish()
+ }
+ }
+
+ impl SystemTime {
+ pub fn now() -> SystemTime {
+ SystemTime { t: now(libc::CLOCK_REALTIME) }
+ }
+
+ pub fn sub_time(&self, other: &SystemTime)
+ -> Result<Duration, Duration> {
+ self.t.sub_timespec(&other.t)
+ }
+
+ pub fn add_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime { t: self.t.add_duration(other) }
+ }
+
+ pub fn sub_duration(&self, other: &Duration) -> SystemTime {
+ SystemTime { t: self.t.sub_duration(other) }
+ }
+ }
+
+ impl From<libc::timespec> for SystemTime {
+ fn from(t: libc::timespec) -> SystemTime {
+ SystemTime { t: Timespec { t: t } }
+ }
+ }
+
+ impl fmt::Debug for SystemTime {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("SystemTime")
+ .field("tv_sec", &self.t.t.tv_sec)
+ .field("tv_nsec", &self.t.t.tv_nsec)
+ .finish()
+ }
+ }
+
+ #[cfg(not(target_os = "dragonfly"))]
+ pub type clock_t = libc::c_int;
+ #[cfg(target_os = "dragonfly")]
+ pub type clock_t = libc::c_ulong;
+
+ fn now(clock: clock_t) -> Timespec {
+ let mut t = Timespec {
+ t: libc::timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ }
+ };
+ cvt(unsafe {
+ libc::clock_gettime(clock, &mut t.t)
+ }).unwrap();
+ t
+ }
}
diff --git a/ctr-std/src/sys/unix/weak.rs b/ctr-std/src/sys/unix/weak.rs
new file mode 100644
index 0000000..18944be
--- /dev/null
+++ b/ctr-std/src/sys/unix/weak.rs
@@ -0,0 +1,79 @@
+// Copyright 2016 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.
+
+//! Support for "weak linkage" to symbols on Unix
+//!
+//! Some I/O operations we do in libstd require newer versions of OSes but we
+//! need to maintain binary compatibility with older releases for now. In order
+//! to use the new functionality when available we use this module for
+//! detection.
+//!
+//! One option to use here is weak linkage, but that is unfortunately only
+//! really workable on Linux. Hence, use dlsym to get the symbol value at
+//! runtime. This is also done for compatibility with older versions of glibc,
+//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
+//! we've been dynamically linked to the library the symbol comes from, but that
+//! is currently always the case for things like libpthread/libc.
+//!
+//! A long time ago this used weak linkage for the __pthread_get_minstack
+//! symbol, but that caused Debian to detect an unnecessarily strict versioned
+//! dependency on libc6 (#23628).
+
+use libc;
+
+use ffi::CString;
+use marker;
+use mem;
+use sync::atomic::{AtomicUsize, Ordering};
+
+macro_rules! weak {
+ (fn $name:ident($($t:ty),*) -> $ret:ty) => (
+ static $name: ::sys::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
+ ::sys::weak::Weak::new(stringify!($name));
+ )
+}
+
+pub struct Weak<F> {
+ name: &'static str,
+ addr: AtomicUsize,
+ _marker: marker::PhantomData<F>,
+}
+
+impl<F> Weak<F> {
+ pub const fn new(name: &'static str) -> Weak<F> {
+ Weak {
+ name,
+ addr: AtomicUsize::new(1),
+ _marker: marker::PhantomData,
+ }
+ }
+
+ pub fn get(&self) -> Option<&F> {
+ assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
+ unsafe {
+ if self.addr.load(Ordering::SeqCst) == 1 {
+ self.addr.store(fetch(self.name), Ordering::SeqCst);
+ }
+ if self.addr.load(Ordering::SeqCst) == 0 {
+ None
+ } else {
+ mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
+ }
+ }
+ }
+}
+
+unsafe fn fetch(name: &str) -> usize {
+ let name = match CString::new(name) {
+ Ok(cstr) => cstr,
+ Err(..) => return 0,
+ };
+ libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
+}