diff options
Diffstat (limited to 'library/std/src/sys/unix')
39 files changed, 11329 insertions, 0 deletions
diff --git a/library/std/src/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs new file mode 100644 index 00000000000..8e193935460 --- /dev/null +++ b/library/std/src/sys/unix/alloc.rs @@ -0,0 +1,97 @@ +use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::ptr; +use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN}; + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // jemalloc provides alignment less than MIN_ALIGN for small allocations. + // So only rely on MIN_ALIGN if size >= align. + // Also see <https://github.com/rust-lang/rust/issues/45955> and + // <https://github.com/rust-lang/rust/issues/62251#issuecomment-507580914>. + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::malloc(layout.size()) as *mut u8 + } else { + #[cfg(target_os = "macos")] + { + if layout.align() > (1 << 31) { + return ptr::null_mut(); + } + } + aligned_malloc(&layout) + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // See the comment above in `alloc` for why this check looks the way it does. + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::calloc(layout.size(), 1) as *mut u8 + } else { + let ptr = self.alloc(layout); + if !ptr.is_null() { + ptr::write_bytes(ptr, 0, layout.size()); + } + ptr + } + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + libc::free(ptr as *mut libc::c_void) + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + } else { + realloc_fallback(self, ptr, layout, new_size) + } + } +} + +#[cfg(any( + target_os = "android", + target_os = "illumos", + target_os = "redox", + target_os = "solaris" +))] +#[inline] +unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { + // On android we currently target API level 9 which unfortunately + // doesn't have the `posix_memalign` API used below. Instead we use + // `memalign`, but this unfortunately has the property on some systems + // where the memory returned cannot be deallocated by `free`! + // + // Upon closer inspection, however, this appears to work just fine with + // Android, so for this platform we should be fine to call `memalign` + // (which is present in API level 9). Some helpful references could + // possibly be chromium using memalign [1], attempts at documenting that + // memalign + free is ok [2] [3], or the current source of chromium + // which still uses memalign on android [4]. + // + // [1]: https://codereview.chromium.org/10796020/ + // [2]: https://code.google.com/p/android/issues/detail?id=35391 + // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 + // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ + // /memory/aligned_memory.cc + libc::memalign(layout.align(), layout.size()) as *mut u8 +} + +#[cfg(not(any( + target_os = "android", + target_os = "illumos", + target_os = "redox", + target_os = "solaris" +)))] +#[inline] +unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { + let mut out = ptr::null_mut(); + // posix_memalign requires that the alignment be a multiple of `sizeof(void*)`. + // Since these are all powers of 2, we can just use max. + let align = layout.align().max(crate::mem::size_of::<usize>()); + let ret = libc::posix_memalign(&mut out, align, layout.size()); + if ret != 0 { ptr::null_mut() } else { out as *mut u8 } +} diff --git a/library/std/src/sys/unix/android.rs b/library/std/src/sys/unix/android.rs new file mode 100644 index 00000000000..ea05ee3d7ce --- /dev/null +++ b/library/std/src/sys/unix/android.rs @@ -0,0 +1,167 @@ +//! 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 super::{cvt, cvt_r}; +use crate::io; + +// 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() * crate::f32::consts::LOG2_E +} + +#[cfg(not(test))] +pub fn log2f64(f: f64) -> f64 { + f.ln() * crate::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(drop), + None => { + if size > i32::MAX as u64 { + Err(io::Error::new(io::ErrorKind::InvalidInput, "cannot truncate >2GB")) + } else { + cvt_r(|| ftruncate(fd, size as i32)).map(drop) + } + } + } + } +} + +#[cfg(target_pointer_width = "64")] +pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { + unsafe { cvt_r(|| ftruncate(fd, size as i64)).map(drop) } +} + +#[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 crate::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 crate::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/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs new file mode 100644 index 00000000000..9bc44a59482 --- /dev/null +++ b/library/std/src/sys/unix/args.rs @@ -0,0 +1,251 @@ +//! 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 crate::ffi::OsString; +use crate::marker::PhantomData; +use crate::vec; + +/// One-time global initialization. +pub unsafe fn init(argc: isize, argv: *const *const u8) { + imp::init(argc, argv) +} + +/// One-time global cleanup. +pub unsafe fn cleanup() { + imp::cleanup() +} + +/// Returns the command line arguments +pub fn args() -> Args { + imp::args() +} + +pub struct Args { + iter: vec::IntoIter<OsString>, + _dont_send_or_sync_me: PhantomData<*mut ()>, +} + +impl Args { + pub fn inner_debug(&self) -> &[OsString] { + self.iter.as_slice() + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option<OsString> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { + self.iter.next_back() + } +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos", + target_os = "emscripten", + target_os = "haiku", + target_os = "l4re", + target_os = "fuchsia", + target_os = "redox" +))] +mod imp { + use super::Args; + use crate::ffi::{CStr, OsString}; + use crate::marker::PhantomData; + use crate::os::unix::prelude::*; + use crate::ptr; + use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering}; + + use crate::sys_common::mutex::Mutex; + + static ARGC: AtomicIsize = AtomicIsize::new(0); + static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); + // We never call `ENV_LOCK.init()`, so it is UB to attempt to + // acquire this mutex reentrantly! + static LOCK: Mutex = Mutex::new(); + + unsafe fn really_init(argc: isize, argv: *const *const u8) { + let _guard = LOCK.lock(); + ARGC.store(argc, Ordering::Relaxed); + ARGV.store(argv as *mut _, Ordering::Relaxed); + } + + #[inline(always)] + pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + // On Linux-GNU, we rely on `ARGV_INIT_ARRAY` below to initialize + // `ARGC` and `ARGV`. But in Miri that does not actually happen so we + // still initialize here. + #[cfg(any(miri, not(all(target_os = "linux", target_env = "gnu"))))] + really_init(_argc, _argv); + } + + /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension. + /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows. + #[cfg(all(target_os = "linux", target_env = "gnu"))] + #[used] + #[link_section = ".init_array.00099"] + static ARGV_INIT_ARRAY: extern "C" fn( + crate::os::raw::c_int, + *const *const u8, + *const *const u8, + ) = { + extern "C" fn init_wrapper( + argc: crate::os::raw::c_int, + argv: *const *const u8, + _envp: *const *const u8, + ) { + unsafe { + really_init(argc as isize, argv); + } + } + init_wrapper + }; + + pub unsafe fn cleanup() { + let _guard = LOCK.lock(); + ARGC.store(0, Ordering::Relaxed); + ARGV.store(ptr::null_mut(), Ordering::Relaxed); + } + + pub fn args() -> Args { + Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData } + } + + fn clone() -> Vec<OsString> { + unsafe { + let _guard = LOCK.lock(); + let argc = ARGC.load(Ordering::Relaxed); + let argv = ARGV.load(Ordering::Relaxed); + (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() + } + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod imp { + use super::Args; + use crate::ffi::CStr; + use crate::marker::PhantomData; + + pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} + + pub fn cleanup() {} + + #[cfg(target_os = "macos")] + pub fn args() -> Args { + use crate::os::unix::prelude::*; + extern "C" { + // 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 crate::ffi::OsString; + use crate::mem; + use crate::str; + + extern "C" { + fn sel_registerName(name: *const libc::c_uchar) -> Sel; + fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; + } + + #[cfg(target_arch = "aarch64")] + extern "C" { + fn objc_msgSend(obj: NsId, sel: Sel) -> NsId; + #[allow(clashing_extern_declarations)] + #[link_name = "objc_msgSend"] + fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId; + } + + #[cfg(not(target_arch = "aarch64"))] + extern "C" { + fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; + #[allow(clashing_extern_declarations)] + #[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/library/std/src/sys/unix/cmath.rs b/library/std/src/sys/unix/cmath.rs new file mode 100644 index 00000000000..f327b69fc75 --- /dev/null +++ b/library/std/src/sys/unix/cmath.rs @@ -0,0 +1,32 @@ +#![cfg(not(test))] + +use libc::{c_double, c_float}; + +extern "C" { + pub fn acos(n: c_double) -> c_double; + pub fn acosf(n: c_float) -> c_float; + pub fn asin(n: c_double) -> c_double; + pub fn asinf(n: c_float) -> c_float; + pub fn atan(n: c_double) -> c_double; + pub fn atan2(a: c_double, b: c_double) -> c_double; + pub fn atan2f(a: c_float, b: c_float) -> c_float; + pub fn atanf(n: c_float) -> c_float; + pub fn cbrt(n: c_double) -> c_double; + pub fn cbrtf(n: c_float) -> c_float; + pub fn cosh(n: c_double) -> c_double; + pub fn coshf(n: c_float) -> c_float; + pub fn expm1(n: c_double) -> c_double; + pub fn expm1f(n: c_float) -> c_float; + pub fn fdim(a: c_double, b: c_double) -> c_double; + pub fn fdimf(a: c_float, b: c_float) -> c_float; + pub fn hypot(x: c_double, y: c_double) -> c_double; + pub fn hypotf(x: c_float, y: c_float) -> c_float; + pub fn log1p(n: c_double) -> c_double; + pub fn log1pf(n: c_float) -> c_float; + pub fn sinh(n: c_double) -> c_double; + pub fn sinhf(n: c_float) -> c_float; + pub fn tan(n: c_double) -> c_double; + pub fn tanf(n: c_float) -> c_float; + pub fn tanh(n: c_double) -> c_double; + pub fn tanhf(n: c_float) -> c_float; +} diff --git a/library/std/src/sys/unix/condvar.rs b/library/std/src/sys/unix/condvar.rs new file mode 100644 index 00000000000..9f1847943f3 --- /dev/null +++ b/library/std/src/sys/unix/condvar.rs @@ -0,0 +1,174 @@ +use crate::cell::UnsafeCell; +use crate::sys::mutex::{self, Mutex}; +use crate::time::Duration; + +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, tv_nsec: 1_000_000_000 - 1 }; + +fn saturating_cast_to_time_t(value: u64) -> libc::time_t { + if value > <libc::time_t>::MAX as u64 { <libc::time_t>::MAX } else { value as libc::time_t } +} + +impl Condvar { + pub const fn new() -> Condvar { + // 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) } + } + + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "l4re", + target_os = "android", + target_os = "redox" + ))] + pub unsafe fn init(&mut self) {} + + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "l4re", + target_os = "android", + target_os = "redox" + )))] + pub unsafe fn init(&mut self) { + use crate::mem::MaybeUninit; + let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit(); + let r = libc::pthread_condattr_init(attr.as_mut_ptr()); + assert_eq!(r, 0); + let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); + assert_eq!(r, 0); + let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr()); + assert_eq!(r, 0); + let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); + assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn notify_one(&self) { + let r = libc::pthread_cond_signal(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn notify_all(&self) { + let r = libc::pthread_cond_broadcast(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = libc::pthread_cond_wait(self.inner.get(), mutex::raw(mutex)); + debug_assert_eq!(r, 0); + } + + // 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 crate::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 + } + + // 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 crate::ptr; + use crate::time::Instant; + + // 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] + #[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) { + 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/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs new file mode 100644 index 00000000000..7f5e9b04dba --- /dev/null +++ b/library/std/src/sys/unix/env.rs @@ -0,0 +1,175 @@ +#[cfg(target_os = "linux")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "linux"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "macos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "macos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "ios")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "ios"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "freebsd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "freebsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "dragonfly")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "dragonfly"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "netbsd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "netbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "openbsd")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "openbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "android")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "android"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "solaris")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "solaris"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "illumos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "illumos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "haiku")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "haiku"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "emscripten"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".js"; + pub const EXE_EXTENSION: &str = "js"; +} + +#[cfg(all(target_os = "emscripten", target_arch = "wasm32"))] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "emscripten"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".js"; + pub const EXE_EXTENSION: &str = "js"; +} + +#[cfg(target_os = "fuchsia")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "fuchsia"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "l4re")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "l4re"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + +#[cfg(target_os = "redox")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "redox"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} diff --git a/library/std/src/sys/unix/ext/ffi.rs b/library/std/src/sys/unix/ext/ffi.rs new file mode 100644 index 00000000000..76b34a6b5d8 --- /dev/null +++ b/library/std/src/sys/unix/ext/ffi.rs @@ -0,0 +1,38 @@ +//! Unix-specific extension to the primitives in the `std::ffi` module +//! +//! # Examples +//! +//! ``` +//! use std::ffi::OsString; +//! use std::os::unix::ffi::OsStringExt; +//! +//! let bytes = b"foo".to_vec(); +//! +//! // OsStringExt::from_vec +//! let os_string = OsString::from_vec(bytes); +//! assert_eq!(os_string.to_str(), Some("foo")); +//! +//! // OsStringExt::into_vec +//! let bytes = os_string.into_vec(); +//! assert_eq!(bytes, b"foo"); +//! ``` +//! +//! ``` +//! use std::ffi::OsStr; +//! use std::os::unix::ffi::OsStrExt; +//! +//! let bytes = b"foo"; +//! +//! // OsStrExt::from_bytes +//! let os_str = OsStr::from_bytes(bytes); +//! assert_eq!(os_str.to_str(), Some("foo")); +//! +//! // OsStrExt::as_bytes +//! let bytes = os_str.as_bytes(); +//! assert_eq!(bytes, b"foo"); +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::sys_common::os_str_bytes::*; diff --git a/library/std/src/sys/unix/ext/fs.rs b/library/std/src/sys/unix/ext/fs.rs new file mode 100644 index 00000000000..f174a59b49a --- /dev/null +++ b/library/std/src/sys/unix/ext/fs.rs @@ -0,0 +1,904 @@ +//! Unix-specific extensions to primitives in the `std::fs` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::fs::{self, OpenOptions, Permissions}; +use crate::io; +use crate::path::Path; +use crate::sys; +use crate::sys::platform::fs::MetadataExt as UnixMetadataExt; +use crate::sys_common::{AsInner, AsInnerMut, FromInner}; + +/// 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>; + + /// Reads the exact number of byte required to fill `buf` from the given offset. + /// + /// 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. + /// + /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`. + /// + /// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact + /// [`read_at`]: #tymethod.read_at + /// + /// # Errors + /// + /// If this function encounters an error of the kind + /// [`ErrorKind::Interrupted`] then the error is ignored and the operation + /// will continue. + /// + /// If this function encounters an "end of file" before completely filling + /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// The contents of `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately + /// returns. The contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it + /// has read, but it will never read more than would be necessary to + /// completely fill the buffer. + /// + /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted + /// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof + /// + /// # 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 exactly 8 bytes from the offset 10. + /// file.read_exact_at(&mut buf, 10)?; + /// println!("read {} bytes: {:?}", buf.len(), buf); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.read_at(buf, offset) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + offset += n as u64; + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + } else { + Ok(()) + } + } + + /// 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#method.write + /// + /// # 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>; + + /// Attempts to write an entire buffer starting from a given offset. + /// + /// 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. + /// + /// This method will continuously call [`write_at`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns. + /// + /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted + /// [`write_at`]: #tymethod.write_at + /// + /// # 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_all_at(b"sushi", 10)?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rw_exact_all_at", since = "1.33.0")] + fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.write_at(buf, offset) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } +} + +#[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: {:o}", 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 an `OpenOptions::open` 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 system's `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(rustc_private)] + /// 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 last access time of the file, in seconds since Unix Epoch. + /// + /// # 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 last access time of the file, in nanoseconds since [`atime`]. + /// + /// [`atime`]: #tymethod.atime + /// + /// # 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 last modification time of the file, in seconds since Unix Epoch. + /// + /// # 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 last modification time of the file, in nanoseconds since [`mtime`]. + /// + /// [`mtime`]: #tymethod.mtime + /// + /// # 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 last status change time of the file, in seconds since Unix Epoch. + /// + /// # 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 last status change time of the file, in nanoseconds since [`ctime`]. + /// + /// [`ctime`]: #tymethod.ctime + /// + /// # 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 block size 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 block_size = 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 `true` if 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 `true` if 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 `true` if 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 `true` if 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/library/std/src/sys/unix/ext/io.rs b/library/std/src/sys/unix/ext/io.rs new file mode 100644 index 00000000000..5077e2e28d1 --- /dev/null +++ b/library/std/src/sys/unix/ext/io.rs @@ -0,0 +1,124 @@ +//! Unix-specific extensions to general I/O primitives + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::fs; +use crate::io; +use crate::os::raw; +use crate::sys; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// Raw file descriptors. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawFd = raw::c_int; + +/// A trait to extract the raw unix file descriptor from an underlying +/// object. +/// +/// This is only available on unix platforms and must be imported in order +/// to call the method. Windows platforms have a corresponding `AsRawHandle` +/// and `AsRawSocket` set of traits. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait AsRawFd { + /// Extracts the raw file descriptor. + /// + /// This method does **not** pass ownership of the raw file descriptor + /// to the caller. The descriptor is only guaranteed to be valid while + /// the original object has not yet been destroyed. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_raw_fd(&self) -> RawFd; +} + +/// A trait to express the ability to construct an object from a raw file +/// descriptor. +#[stable(feature = "from_raw_os", since = "1.1.0")] +pub trait FromRawFd { + /// Constructs a new instance of `Self` from the given raw file + /// descriptor. + /// + /// This function **consumes ownership** of the specified file + /// descriptor. The returned object will take responsibility for closing + /// it when the object goes out of scope. + /// + /// This function is also unsafe as the primitives currently returned + /// have the contract that they are the sole owner of the file + /// descriptor they are wrapping. Usage of this function could + /// accidentally allow violating this contract which can cause memory + /// unsafety in code that relies on it being true. + #[stable(feature = "from_raw_os", since = "1.1.0")] + unsafe fn from_raw_fd(fd: RawFd) -> Self; +} + +/// A trait to express the ability to consume an object and acquire ownership of +/// its raw file descriptor. +#[stable(feature = "into_raw_os", since = "1.4.0")] +pub trait IntoRawFd { + /// Consumes this object, returning the raw underlying file descriptor. + /// + /// This function **transfers ownership** of the underlying file descriptor + /// to the caller. Callers are then the unique owners of the file descriptor + /// and must close the descriptor once it's no longer needed. + #[stable(feature = "into_raw_os", since = "1.4.0")] + fn into_raw_fd(self) -> RawFd; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for fs::File { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().fd().raw() + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for fs::File { + unsafe fn from_raw_fd(fd: RawFd) -> fs::File { + fs::File::from_inner(sys::fs::File::from_inner(fd)) + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for fs::File { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_fd().into_raw() + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawFd for io::Stdin { + fn as_raw_fd(&self) -> RawFd { + libc::STDIN_FILENO + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawFd for io::Stdout { + fn as_raw_fd(&self) -> RawFd { + libc::STDOUT_FILENO + } +} + +#[stable(feature = "asraw_stdio", since = "1.21.0")] +impl AsRawFd for io::Stderr { + fn as_raw_fd(&self) -> RawFd { + libc::STDERR_FILENO + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StdinLock<'a> { + fn as_raw_fd(&self) -> RawFd { + libc::STDIN_FILENO + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StdoutLock<'a> { + fn as_raw_fd(&self) -> RawFd { + libc::STDOUT_FILENO + } +} + +#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] +impl<'a> AsRawFd for io::StderrLock<'a> { + fn as_raw_fd(&self) -> RawFd { + libc::STDERR_FILENO + } +} diff --git a/library/std/src/sys/unix/ext/mod.rs b/library/std/src/sys/unix/ext/mod.rs new file mode 100644 index 00000000000..cbdb1c10049 --- /dev/null +++ b/library/std/src/sys/unix/ext/mod.rs @@ -0,0 +1,66 @@ +//! Platform-specific extensions to `std` for Unix platforms. +//! +//! Provides access to platform-level information on Unix platforms, and +//! exposes Unix-specific functions that would otherwise be inappropriate as +//! part of the core `std` library. +//! +//! It exposes more ways to deal with platform-specific strings (`OsStr`, +//! `OsString`), allows to set permissions more granularly, extract low-level +//! file descriptors from files and sockets, and has platform-specific helpers +//! for spawning processes. +//! +//! # Examples +//! +//! ```no_run +//! use std::fs::File; +//! use std::os::unix::prelude::*; +//! +//! fn main() -> std::io::Result<()> { +//! let f = File::create("foo.txt")?; +//! let fd = f.as_raw_fd(); +//! +//! // use fd with native unix bindings +//! +//! Ok(()) +//! } +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] +#![doc(cfg(unix))] +#![allow(missing_docs)] + +pub mod ffi; +pub mod fs; +pub mod io; +pub mod net; +pub mod process; +pub mod raw; +pub mod thread; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[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::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::fs::{FileTypeExt, MetadataExt, OpenOptionsExt, PermissionsExt}; + #[doc(no_inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + #[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::thread::JoinHandleExt; +} diff --git a/library/std/src/sys/unix/ext/net.rs b/library/std/src/sys/unix/ext/net.rs new file mode 100644 index 00000000000..ada8eaa1c97 --- /dev/null +++ b/library/std/src/sys/unix/ext/net.rs @@ -0,0 +1,2024 @@ +#![stable(feature = "unix_socket", since = "1.10.0")] + +//! Unix-specific networking functionality + +// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? +#[cfg(not(unix))] +#[allow(non_camel_case_types)] +mod libc { + pub use libc::c_int; + pub type socklen_t = u32; + pub struct sockaddr; + #[derive(Clone)] + pub struct sockaddr_un; +} + +use crate::ascii; +use crate::ffi::OsStr; +use crate::fmt; +use crate::io::{self, Initializer, IoSlice, IoSliceMut}; +use crate::mem; +use crate::net::{self, Shutdown}; +use crate::os::unix::ffi::OsStrExt; +use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::path::Path; +use crate::sys::net::Socket; +use crate::sys::{self, cvt}; +use crate::sys_common::{self, AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "haiku" +))] +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" +)))] +const MSG_NOSIGNAL: libc::c_int = 0x0; + +fn sun_path_offset(addr: &libc::sockaddr_un) -> usize { + // Work with an actual instance of the type since using a null pointer is UB + 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(&addr) + 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(&addr) 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 the address is unnamed. + /// + /// # Examples + /// + /// A named address: + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), false); + /// Ok(()) + /// } + /// ``` + /// + /// An unnamed address: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), true); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); + /// Ok(()) + /// } + /// ``` + /// + /// Without a pathname: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), None); + /// Ok(()) + /// } + /// ``` + #[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(&self) -> AddressKind<'_> { + let len = self.len as usize - sun_path_offset(&self.addr); + 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::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut stream = UnixStream::connect("/path/to/my/socket")?; +/// stream.write_all(b"hello world")?; +/// let mut response = String::new(); +/// stream.read_to_string(&mut response)?; +/// println!("{}", response); +/// Ok(()) +/// } +/// ``` +#[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// 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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// 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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("127.0.0.1:34254")?; + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// if let Ok(Some(err)) = socket.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// Ok(()) + /// } + /// ``` + /// + /// # Platform specific + /// On Redox this always returns `None`. + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// Ok(()) + /// } + /// ``` + #[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) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + io::Read::read_vectored(&mut &*self, bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + io::Read::is_read_vectored(&&*self) + } + + #[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) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + #[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 write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + io::Write::write_vectored(&mut &*self, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + io::Write::is_write_vectored(&&*self) + } + + 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 write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + 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) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// // 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; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +#[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; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// match listener.accept() { + /// Ok((socket, addr)) => println!("Got a client: {:?}", addr), + /// Err(e) => println!("accept function failed: {:?}", e), + /// } + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + #[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. + /// + /// This will result in the `accept` operation becoming nonblocking, + /// i.e., immediately returning from their calls. If the IO operation is + /// successful, `Ok` is returned and no further action is required. If the + /// IO operation could not be completed and needs to be retried, an error + /// with kind [`io::ErrorKind::WouldBlock`] is returned. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// Ok(()) + /// } + /// ``` + /// + /// [`io::ErrorKind::WouldBlock`]: ../../../io/enum.ErrorKind.html#variant.WouldBlock + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/tmp/sock")?; + /// + /// if let Ok(Some(err)) = listener.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// Ok(()) + /// } + /// ``` + /// + /// # Platform specific + /// On Redox this always returns `None`. + #[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) { + /// // ... + /// } + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// thread::spawn(|| handle_client(stream)); + /// } + /// Err(err) => { + /// break; + /// } + /// } + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "unix_socket", since = "1.10.0")] + pub fn incoming(&self) -> Incoming<'_> { + 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) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +#[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, None) + } +} + +/// A Unix datagram socket. +/// +/// # Examples +/// +/// ```no_run +/// use std::os::unix::net::UnixDatagram; +/// +/// fn main() -> std::io::Result<()> { +/// let socket = UnixDatagram::bind("/path/to/my/socket")?; +/// socket.send_to(b"hello world", "/path/to/other/socket")?; +/// let mut buf = [0; 100]; +/// let (count, address) = socket.recv_from(&mut buf)?; +/// println!("socket {:?} sent {:?}", address, &buf[..count]); +/// Ok(()) +/// } +/// ``` +#[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)) + } + + /// Creates 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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// match sock.connect("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return Err(e) + /// } + /// }; + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::bind("/path/to/the/socket")?; + /// let sock_copy = sock.try_clone().expect("try_clone failed"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::bind("/path/to/the/socket")?; + /// let addr = sock.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.connect("/path/to/the/socket")?; + /// + /// let addr = sock.peer_addr().expect("Couldn't get peer address"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// let mut buf = vec![0; 10]; + /// let (size, sender) = sock.recv_from(buf.as_mut_slice())?; + /// println!("received {} bytes from {:?}", size, sender); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::bind("/path/to/the/socket")?; + /// let mut buf = vec![0; 10]; + /// sock.recv(buf.as_mut_slice()).expect("recv function failed"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.connect("/some/sock").expect("Couldn't connect"); + /// sock.send(b"omelette au fromage").expect("send_to function failed"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_read_timeout(Some(Duration::new(1, 0))) + /// .expect("set_read_timeout function failed"); + /// Ok(()) + /// } + /// ``` + /// + /// 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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// Ok(()) + /// } + /// ``` + /// + /// 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; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixDatagram::unbound()?; + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_read_timeout(Some(Duration::new(1, 0))) + /// .expect("set_read_timeout function failed"); + /// assert_eq!(sock.read_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// assert_eq!(sock.write_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_nonblocking(true).expect("set_nonblocking function failed"); + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// if let Ok(Some(err)) = sock.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// Ok(()) + /// } + /// ``` + #[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; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// Ok(()) + /// } + /// ``` + #[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 crate::io::prelude::*; + use crate::io::{self, ErrorKind}; + use crate::sys_common::io::test::tmpdir; + use crate::thread; + use crate::time::Duration; + + 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 vectored() { + let (mut s1, mut s2) = or_panic!(UnixStream::pair()); + + let len = or_panic!(s1.write_vectored(&[ + IoSlice::new(b"hello"), + IoSlice::new(b" "), + IoSlice::new(b"world!") + ],)); + assert_eq!(len, 12); + + let mut buf1 = [0; 6]; + let mut buf2 = [0; 7]; + let len = or_panic!( + s2.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],) + ); + assert_eq!(len, 12); + assert_eq!(&buf1, b"hello "); + assert_eq!(&buf2, b"world!\0"); + } + + #[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_exact(&mut buf).err().expect("expected error").kind(); + assert!( + kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", + kind + ); + } + + #[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_exact(&mut buf).err().expect("expected error").kind(); + assert!( + kind == ErrorKind::WouldBlock || kind == ErrorKind::TimedOut, + "unexpected_error: {:?}", + kind + ); + } + + // 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/library/std/src/sys/unix/ext/process.rs b/library/std/src/sys/unix/ext/process.rs new file mode 100644 index 00000000000..048ce24d6ba --- /dev/null +++ b/library/std/src/sys/unix/ext/process.rs @@ -0,0 +1,234 @@ +//! Unix-specific extensions to primitives in the `std::process` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::ffi::OsStr; +use crate::io; +use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::process; +use crate::sys; +use crate::sys_common::{AsInner, AsInnerMut, 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 and Safety + /// + /// 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). + /// + /// This also means that all resources such as file descriptors and + /// memory-mapped regions got duplicated. It is your responsibility to make + /// sure that the closure does not violate library invariants by making + /// invalid use of these duplicates. + /// + /// 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_pre_exec", since = "1.34.0")] + unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command + where + F: FnMut() -> io::Result<()> + Send + Sync + 'static; + + /// Schedules a closure to be run just before the `exec` function is + /// invoked. + /// + /// This method is stable and usable, but it should be unsafe. To fix + /// that, it got deprecated in favor of the unsafe [`pre_exec`]. + /// + /// [`pre_exec`]: #tymethod.pre_exec + #[stable(feature = "process_exec", since = "1.15.0")] + #[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")] + fn before_exec<F>(&mut self, f: F) -> &mut process::Command + where + F: FnMut() -> io::Result<()> + Send + Sync + 'static, + { + unsafe { self.pre_exec(f) } + } + + /// 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; + + /// Set executable argument + /// + /// Set the first process argument, `argv[0]`, to something other than the + /// default executable path. + #[stable(feature = "process_set_argv0", since = "1.45.0")] + fn arg0<S>(&mut self, arg: S) -> &mut process::Command + where + S: AsRef<OsStr>; +} + +#[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 + } + + unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command + where + F: FnMut() -> io::Result<()> + Send + Sync + 'static, + { + self.as_inner_mut().pre_exec(Box::new(f)); + self + } + + fn exec(&mut self) -> io::Error { + self.as_inner_mut().exec(sys::process::Stdio::Inherit) + } + + fn arg0<S>(&mut self, arg: S) -> &mut process::Command + where + S: AsRef<OsStr>, + { + self.as_inner_mut().set_arg_0(arg.as_ref()); + self + } +} + +/// 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 { + crate::sys::os::getppid() +} diff --git a/library/std/src/sys/unix/ext/raw.rs b/library/std/src/sys/unix/ext/raw.rs new file mode 100644 index 00000000000..40fa53d484f --- /dev/null +++ b/library/std/src/sys/unix/ext/raw.rs @@ -0,0 +1,33 @@ +//! Unix-specific primitives available on all unix platforms + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![rustc_deprecated( + since = "1.8.0", + reason = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +#[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(non_camel_case_types)] +pub type uid_t = u32; + +#[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(non_camel_case_types)] +pub type gid_t = u32; + +#[stable(feature = "raw_ext", since = "1.1.0")] +#[allow(non_camel_case_types)] +pub type pid_t = i32; + +#[doc(inline)] +#[stable(feature = "pthread_t", since = "1.8.0")] +pub use crate::sys::platform::raw::pthread_t; +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use crate::sys::platform::raw::{blkcnt_t, time_t}; +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use crate::sys::platform::raw::{blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t}; diff --git a/library/std/src/sys/unix/ext/thread.rs b/library/std/src/sys/unix/ext/thread.rs new file mode 100644 index 00000000000..759ef6236e8 --- /dev/null +++ b/library/std/src/sys/unix/ext/thread.rs @@ -0,0 +1,41 @@ +//! Unix-specific extensions to primitives in the `std::thread` module. + +#![stable(feature = "thread_extensions", since = "1.9.0")] + +#[allow(deprecated)] +use crate::os::unix::raw::pthread_t; +use crate::sys_common::{AsInner, IntoInner}; +use crate::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/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs new file mode 100644 index 00000000000..84c4d662161 --- /dev/null +++ b/library/std/src/sys/unix/fd.rs @@ -0,0 +1,258 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +use crate::cmp; +use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; +use crate::mem; +use crate::sys::cvt; +use crate::sys_common::AsInner; + +use libc::{c_int, c_void}; + +#[derive(Debug)] +pub struct FileDesc { + fd: c_int, +} + +// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, +// with the man page quoting that if the count of bytes to read is +// greater than `SSIZE_MAX` the result is "unspecified". +// +// On macOS, however, apparently the 64-bit libc is either buggy or +// intentionally showing odd behavior by rejecting any read with a size +// larger than or equal to INT_MAX. To handle both of these the read +// size is capped on both platforms. +#[cfg(target_os = "macos")] +const READ_LIMIT: usize = c_int::MAX as usize - 1; +#[cfg(not(target_os = "macos"))] +const READ_LIMIT: usize = libc::ssize_t::MAX as usize; + +impl FileDesc { + pub fn new(fd: c_int) -> FileDesc { + FileDesc { fd } + } + + pub fn raw(&self) -> c_int { + self.fd + } + + /// Extracts the actual file descriptor without closing it. + pub fn into_raw(self) -> c_int { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT)) + })?; + Ok(ret as usize) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::readv( + self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + let mut me = self; + (&mut me).read_to_end(buf) + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + #[cfg(target_os = "android")] + use super::android::cvt_pread64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pread64( + fd: c_int, + buf: *mut c_void, + count: usize, + offset: i64, + ) -> io::Result<isize> { + #[cfg(not(target_os = "linux"))] + use libc::pread as pread64; + #[cfg(target_os = "linux")] + use libc::pread64; + cvt(pread64(fd, buf, count, offset)) + } + + unsafe { + cvt_pread64( + self.fd, + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as i64, + ) + .map(|n| n as usize) + } + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT)) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::writev( + self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + #[cfg(target_os = "android")] + use super::android::cvt_pwrite64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pwrite64( + fd: c_int, + buf: *const c_void, + count: usize, + offset: i64, + ) -> io::Result<isize> { + #[cfg(not(target_os = "linux"))] + use libc::pwrite as pwrite64; + #[cfg(target_os = "linux")] + use libc::pwrite64; + cvt(pwrite64(fd, buf, count, offset)) + } + + unsafe { + cvt_pwrite64( + self.fd, + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as i64, + ) + .map(|n| n as usize) + } + } + + #[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 = "illumos", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "linux", + target_os = "haiku", + target_os = "redox" + )))] + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; + Ok(()) + } + } + #[cfg(any( + target_env = "newlib", + target_os = "solaris", + target_os = "illumos", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "linux", + target_os = "haiku", + target_os = "redox" + ))] + 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))?; + let new = if nonblocking { + previous | libc::O_NONBLOCK + } else { + previous & !libc::O_NONBLOCK + }; + if new != previous { + cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; + } + Ok(()) + } + } + + pub fn duplicate(&self) -> io::Result<FileDesc> { + // We want to atomically duplicate this file descriptor and set the + // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This + // is a POSIX flag that was added to Linux in 2.6.24. + let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?; + Ok(FileDesc::new(fd)) + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (**self).read(buf) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +impl AsInner<c_int> for FileDesc { + fn as_inner(&self) -> &c_int { + &self.fd + } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // opened after we closed ours. + let _ = unsafe { libc::close(self.fd) }; + } +} diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs new file mode 100644 index 00000000000..acb18e6d064 --- /dev/null +++ b/library/std/src/sys/unix/fs.rs @@ -0,0 +1,1310 @@ +use crate::os::unix::prelude::*; + +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::path::{Path, PathBuf}; +use crate::ptr; +use crate::sync::Arc; +use crate::sys::fd::FileDesc; +use crate::sys::time::SystemTime; +use crate::sys::{cvt, cvt_r}; +use crate::sys_common::{AsInner, FromInner}; + +use libc::{c_int, mode_t}; + +#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] +use libc::dirfd; +#[cfg(any(target_os = "linux", target_os = "emscripten"))] +use libc::fstatat64; +#[cfg(not(any( + target_os = "linux", + target_os = "emscripten", + target_os = "solaris", + target_os = "illumos", + target_os = "l4re", + target_os = "fuchsia", + target_os = "redox" +)))] +use libc::readdir_r as readdir64_r; +#[cfg(target_os = "android")] +use libc::{ + dirent as dirent64, fstat as fstat64, fstatat as fstatat64, lseek64, lstat as lstat64, + open as open64, stat as stat64, +}; +#[cfg(not(any( + target_os = "linux", + target_os = "emscripten", + target_os = "l4re", + target_os = "android" +)))] +use libc::{ + dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, + lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, +}; +#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))] +use libc::{ + dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64, +}; + +pub use crate::sys_common::fs::remove_dir_all; + +pub struct File(FileDesc); + +// FIXME: This should be available on Linux with all `target_env`. +// But currently only glibc exposes `statx` fn and structs. +// We don't want to import unverified raw C structs here directly. +// https://github.com/rust-lang/rust/pull/67774 +macro_rules! cfg_has_statx { + ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => { + cfg_if::cfg_if! { + if #[cfg(all(target_os = "linux", target_env = "gnu"))] { + $($then_tt)* + } else { + $($else_tt)* + } + } + }; + ($($block_inner:tt)*) => { + #[cfg(all(target_os = "linux", target_env = "gnu"))] + { + $($block_inner)* + } + }; +} + +cfg_has_statx! {{ + #[derive(Clone)] + pub struct FileAttr { + stat: stat64, + statx_extra_fields: Option<StatxExtraFields>, + } + + #[derive(Clone)] + struct StatxExtraFields { + // This is needed to check if btime is supported by the filesystem. + stx_mask: u32, + stx_btime: libc::statx_timestamp, + } + + // We prefer `statx` on Linux if available, which contains file creation time. + // Default `stat64` contains no creation time. + unsafe fn try_statx( + fd: c_int, + path: *const libc::c_char, + flags: i32, + mask: u32, + ) -> Option<io::Result<FileAttr>> { + use crate::sync::atomic::{AtomicU8, Ordering}; + + // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx` + // We store the availability in global to avoid unnecessary syscalls. + // 0: Unknown + // 1: Not available + // 2: Available + static STATX_STATE: AtomicU8 = AtomicU8::new(0); + syscall! { + fn statx( + fd: c_int, + pathname: *const libc::c_char, + flags: c_int, + mask: libc::c_uint, + statxbuf: *mut libc::statx + ) -> c_int + } + + match STATX_STATE.load(Ordering::Relaxed) { + 0 => { + // It is a trick to call `statx` with NULL pointers to check if the syscall + // is available. According to the manual, it is expected to fail with EFAULT. + // We do this mainly for performance, since it is nearly hundreds times + // faster than a normal successful call. + let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) + .err() + .and_then(|e| e.raw_os_error()); + // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited + // and returns `EPERM`. Listing all possible errors seems not a good idea. + // See: https://github.com/rust-lang/rust/issues/65662 + if err != Some(libc::EFAULT) { + STATX_STATE.store(1, Ordering::Relaxed); + return None; + } + STATX_STATE.store(2, Ordering::Relaxed); + } + 1 => return None, + _ => {} + } + + let mut buf: libc::statx = mem::zeroed(); + if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) { + return Some(Err(err)); + } + + // We cannot fill `stat64` exhaustively because of private padding fields. + let mut stat: stat64 = mem::zeroed(); + // `c_ulong` on gnu-mips, `dev_t` otherwise + stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _; + stat.st_ino = buf.stx_ino as libc::ino64_t; + stat.st_nlink = buf.stx_nlink as libc::nlink_t; + stat.st_mode = buf.stx_mode as libc::mode_t; + stat.st_uid = buf.stx_uid as libc::uid_t; + stat.st_gid = buf.stx_gid as libc::gid_t; + stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _; + stat.st_size = buf.stx_size as off64_t; + stat.st_blksize = buf.stx_blksize as libc::blksize_t; + stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t; + stat.st_atime = buf.stx_atime.tv_sec as libc::time_t; + // `i64` on gnu-x86_64-x32, `c_ulong` otherwise. + stat.st_atime_nsec = buf.stx_atime.tv_nsec as _; + stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t; + stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _; + stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t; + stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _; + + let extra = StatxExtraFields { + stx_mask: buf.stx_mask, + stx_btime: buf.stx_btime, + }; + + Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) })) + } + +} else { + #[derive(Clone)] + pub struct FileAttr { + stat: stat64, + } +}} + +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + dirp: Dir, + root: PathBuf, +} + +#[derive(Clone)] +pub struct ReadDir { + inner: Arc<InnerReadDir>, + end_of_stream: bool, +} + +struct Dir(*mut libc::DIR); + +unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} + +pub struct DirEntry { + entry: dirent64, + 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. + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox" + ))] + name: Box<[u8]>, +} + +#[derive(Clone, Debug)] +pub struct OpenOptions { + // generic + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool, + // system-specific + custom_flags: i32, + mode: mode_t, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + mode: mode_t, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType { + mode: mode_t, +} + +#[derive(Debug)] +pub struct DirBuilder { + mode: mode_t, +} + +cfg_has_statx! {{ + impl FileAttr { + fn from_stat64(stat: stat64) -> Self { + Self { stat, statx_extra_fields: None } + } + } +} else { + impl FileAttr { + fn from_stat64(stat: stat64) -> Self { + Self { stat } + } + } +}} + +impl FileAttr { + pub fn size(&self) -> u64 { + self.stat.st_size as u64 + } + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat.st_mode as mode_t) } + } + + pub fn file_type(&self) -> FileType { + FileType { mode: self.stat.st_mode as mode_t } + } +} + +#[cfg(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_mtimensec as libc::c_long, + })) + } + + 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_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 = "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 = "freebsd", + target_os = "openbsd", + target_os = "macos", + target_os = "ios" + )))] + pub fn created(&self) -> io::Result<SystemTime> { + cfg_has_statx! { + if let Some(ext) = &self.statx_extra_fields { + return if (ext.stx_mask & libc::STATX_BTIME) != 0 { + Ok(SystemTime::from(libc::timespec { + tv_sec: ext.stx_btime.tv_sec as libc::time_t, + tv_nsec: ext.stx_btime.tv_nsec as _, + })) + } else { + Err(io::Error::new( + io::ErrorKind::Other, + "creation time is not available for the filesystem", + )) + }; + } + } + + Err(io::Error::new( + io::ErrorKind::Other, + "creation time is not available on this platform \ + currently", + )) + } +} + +impl AsInner<stat64> for FileAttr { + fn as_inner(&self) -> &stat64 { + &self.stat + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 + } + + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + // remove write permission for all classes; equivalent to `chmod a-w <file>` + self.mode &= !0o222; + } else { + // add write permission for all classes; equivalent to `chmod a+w <file>` + self.mode |= 0o222; + } + } + pub fn mode(&self) -> u32 { + self.mode as u32 + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + self.is(libc::S_IFDIR) + } + pub fn is_file(&self) -> bool { + self.is(libc::S_IFREG) + } + pub fn is_symlink(&self) -> bool { + self.is(libc::S_IFLNK) + } + + pub fn is(&self, mode: mode_t) -> bool { + self.mode & libc::S_IFMT == mode + } +} + +impl FromInner<u32> for FilePermissions { + fn from_inner(mode: u32) -> FilePermissions { + FilePermissions { mode: mode as mode_t } + } +} + +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.inner.root, f) + } +} + +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; + + #[cfg(any( + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos" + ))] + fn next(&mut self) -> Option<io::Result<DirEntry>> { + use crate::slice; + + unsafe { + loop { + // Although readdir_r(3) would be a correct function to use here because + // of the thread safety, on Illumos and Fuchsia the readdir(3C) function + // is safe to use in threaded applications and it is generally preferred + // over the readdir_r(3C) function. + super::os::set_errno(0); + let entry_ptr = libc::readdir(self.inner.dirp.0); + if entry_ptr.is_null() { + // NULL can mean either the end is reached or an error occurred. + // So we had to clear errno beforehand to check for an error now. + return match super::os::errno() { + 0 => None, + e => Some(Err(Error::from_raw_os_error(e))), + }; + } + + let name = (*entry_ptr).d_name.as_ptr(); + let namelen = libc::strlen(name) as usize; + + let ret = DirEntry { + entry: *entry_ptr, + name: slice::from_raw_parts(name as *const u8, namelen as usize) + .to_owned() + .into_boxed_slice(), + dir: self.clone(), + }; + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)); + } + } + } + } + + #[cfg(not(any( + target_os = "solaris", + target_os = "fuchsia", + target_os = "redox", + target_os = "illumos" + )))] + fn next(&mut self) -> Option<io::Result<DirEntry>> { + if self.end_of_stream { + return None; + } + + unsafe { + let mut ret = DirEntry { entry: mem::zeroed(), dir: self.clone() }; + let mut entry_ptr = ptr::null_mut(); + loop { + if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { + if entry_ptr.is_null() { + // We encountered an error (which will be returned in this iteration), but + // we also reached the end of the directory stream. The `end_of_stream` + // flag is enabled to make sure that we return `None` in the next iteration + // (instead of looping forever) + self.end_of_stream = true; + } + return Some(Err(Error::last_os_error())); + } + if entry_ptr.is_null() { + return None; + } + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)); + } + } + } + } +} + +impl Drop for Dir { + fn drop(&mut self) { + let r = unsafe { libc::closedir(self.0) }; + debug_assert_eq!(r, 0); + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.dir.inner.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.inner.dirp.0) })?; + let name = self.entry.d_name.as_ptr(); + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + fd, + name, + libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?; + Ok(FileAttr::from_stat64(stat)) + } + + #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))] + pub fn metadata(&self) -> io::Result<FileAttr> { + lstat(&self.path()) + } + + #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "haiku"))] + pub fn file_type(&self) -> io::Result<FileType> { + lstat(&self.path()).map(|m| m.file_type()) + } + + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "haiku")))] + pub fn file_type(&self) -> io::Result<FileType> { + match self.entry.d_type { + libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), + libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }), + libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }), + libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }), + libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }), + libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), + libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), + _ => lstat(&self.path()).map(|m| m.file_type()), + } + } + + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "linux", + target_os = "emscripten", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "l4re", + target_os = "fuchsia", + target_os = "redox" + ))] + pub fn ino(&self) -> u64 { + self.entry.d_ino as u64 + } + + #[cfg(any( + target_os = "freebsd", + target_os = "openbsd", + 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" + ))] + fn name_bytes(&self) -> &[u8] { + use crate::slice; + unsafe { + 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 = "illumos", + target_os = "fuchsia", + target_os = "redox" + ))] + fn name_bytes(&self) -> &[u8] { + &*self.name + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { + // generic + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false, + // system-specific + custom_flags: 0, + mode: 0o666, + } + } + + pub fn read(&mut self, read: bool) { + self.read = read; + } + pub fn write(&mut self, write: bool) { + self.write = write; + } + pub fn append(&mut self, append: bool) { + self.append = append; + } + pub fn truncate(&mut self, truncate: bool) { + self.truncate = truncate; + } + pub fn create(&mut self, create: bool) { + self.create = create; + } + pub fn create_new(&mut self, create_new: bool) { + self.create_new = create_new; + } + + pub fn custom_flags(&mut self, flags: i32) { + self.custom_flags = flags; + } + pub fn mode(&mut self, mode: u32) { + self.mode = mode as mode_t; + } + + fn get_access_mode(&self) -> io::Result<c_int> { + match (self.read, self.write, self.append) { + (true, false, false) => Ok(libc::O_RDONLY), + (false, true, false) => Ok(libc::O_WRONLY), + (true, true, false) => Ok(libc::O_RDWR), + (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), + (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), + (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), + } + } + + fn get_creation_mode(&self) -> io::Result<c_int> { + match (self.write, self.append) { + (true, false) => {} + (false, false) => { + if self.truncate || self.create || self.create_new { + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + } + (_, true) => { + if self.truncate && !self.create_new { + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + } + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => 0, + (true, false, false) => libc::O_CREAT, + (false, true, false) => libc::O_TRUNC, + (true, true, false) => libc::O_CREAT | libc::O_TRUNC, + (_, _, true) => libc::O_CREAT | libc::O_EXCL, + }) + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { + let path = cstr(path)?; + File::open_c(&path, opts) + } + + pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> { + let flags = libc::O_CLOEXEC + | opts.get_access_mode()? + | opts.get_creation_mode()? + | (opts.custom_flags as c_int & !libc::O_ACCMODE); + // The third argument of `open64` is documented to have type `mode_t`. On + // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`. + // However, since this is a variadic function, C integer promotion rules mean that on + // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms). + let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; + Ok(File(FileDesc::new(fd))) + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + let fd = self.0.raw(); + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + fd, + b"\0" as *const _ as *const libc::c_char, + libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { fstat64(fd, &mut stat) })?; + Ok(FileAttr::from_stat64(stat)) + } + + pub fn fsync(&self) -> io::Result<()> { + cvt_r(|| unsafe { os_fsync(self.0.raw()) })?; + return Ok(()); + + #[cfg(any(target_os = "macos", target_os = "ios"))] + unsafe fn os_fsync(fd: c_int) -> c_int { + libc::fcntl(fd, libc::F_FULLFSYNC) + } + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + unsafe fn os_fsync(fd: c_int) -> c_int { + libc::fsync(fd) + } + } + + pub fn datasync(&self) -> io::Result<()> { + 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) + } + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + #[cfg(target_os = "android")] + return crate::sys::android::ftruncate64(self.0.raw(), size); + + #[cfg(not(target_os = "android"))] + { + use crate::convert::TryInto; + let size: off64_t = + size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; + cvt_r(|| unsafe { ftruncate64(self.0.raw(), size) }).map(drop) + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.0.read_at(buf, offset) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + self.0.write_at(buf, offset) + } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } + + pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { + let (whence, pos) = match pos { + // Casting to `i64` is fine, too large values will end up as + // negative which will cause an error in `lseek64`. + SeekFrom::Start(off) => (libc::SEEK_SET, off as i64), + SeekFrom::End(off) => (libc::SEEK_END, off), + SeekFrom::Current(off) => (libc::SEEK_CUR, off), + }; + let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; + Ok(n as u64) + } + + pub fn duplicate(&self) -> io::Result<File> { + self.0.duplicate().map(File) + } + + pub fn fd(&self) -> &FileDesc { + &self.0 + } + + pub fn into_fd(self) -> FileDesc { + self.0 + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?; + Ok(()) + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; + Ok(()) + } + + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as mode_t; + } +} + +fn cstr(path: &Path) -> io::Result<CString> { + Ok(CString::new(path.as_os_str().as_bytes())?) +} + +impl FromInner<c_int> for File { + fn from_inner(fd: c_int) -> File { + File(FileDesc::new(fd)) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[cfg(target_os = "linux")] + fn get_path(fd: c_int) -> Option<PathBuf> { + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + readlink(&p).ok() + } + + #[cfg(target_os = "macos")] + fn get_path(fd: c_int) -> Option<PathBuf> { + // FIXME: The use of PATH_MAX is generally not encouraged, but it + // is inevitable in this case because macOS defines `fcntl` with + // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no + // alternatives. If a better method is invented, it should be used + // instead. + let mut buf = vec![0; libc::PATH_MAX as usize]; + let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; + if n == -1 { + return None; + } + let l = buf.iter().position(|&c| c == 0).unwrap(); + buf.truncate(l as usize); + buf.shrink_to_fit(); + Some(PathBuf::from(OsString::from_vec(buf))) + } + + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + fn get_path(_fd: c_int) -> Option<PathBuf> { + // FIXME(#24570): implement this for other Unix platforms + None + } + + #[cfg(any(target_os = "linux", target_os = "macos"))] + fn get_mode(fd: c_int) -> Option<(bool, bool)> { + let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; + if mode == -1 { + return None; + } + match mode & libc::O_ACCMODE { + libc::O_RDONLY => Some((true, false)), + libc::O_RDWR => Some((true, true)), + libc::O_WRONLY => Some((false, true)), + _ => None, + } + } + + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + fn get_mode(_fd: c_int) -> Option<(bool, bool)> { + // FIXME(#24570): implement this for other Unix platforms + None + } + + let fd = self.0.raw(); + let mut b = f.debug_struct("File"); + b.field("fd", &fd); + if let Some(path) = get_path(fd) { + b.field("path", &path); + } + if let Some((read, write)) = get_mode(fd) { + b.field("read", &read).field("write", &write); + } + b.finish() + } +} + +pub fn readdir(p: &Path) -> io::Result<ReadDir> { + let root = p.to_path_buf(); + let p = cstr(p)?; + unsafe { + let ptr = libc::opendir(p.as_ptr()); + if ptr.is_null() { + Err(Error::last_os_error()) + } else { + let inner = InnerReadDir { dirp: Dir(ptr), root }; + Ok(ReadDir { inner: Arc::new(inner), end_of_stream: false }) + } + } +} + +pub fn unlink(p: &Path) -> io::Result<()> { + let p = cstr(p)?; + 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(), 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(), perm.mode) })?; + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + let p = cstr(p)?; + cvt(unsafe { libc::rmdir(p.as_ptr()) })?; + Ok(()) +} + +pub fn readlink(p: &Path) -> io::Result<PathBuf> { + let c_path = cstr(p)?; + let p = c_path.as_ptr(); + + let mut buf = Vec::with_capacity(256); + + loop { + let buf_read = + cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize; + + unsafe { + buf.set_len(buf_read); + } + + if buf_read != buf.capacity() { + buf.shrink_to_fit(); + + return Ok(PathBuf::from(OsString::from_vec(buf))); + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. The length is guaranteed to be + // the same as the capacity due to the if statement above. + buf.reserve(1); + } +} + +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { + let src = cstr(src)?; + let dst = cstr(dst)?; + 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(), dst.as_ptr()) })?; + Ok(()) +} + +pub fn stat(p: &Path) -> io::Result<FileAttr> { + let p = cstr(p)?; + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + libc::AT_FDCWD, + p.as_ptr(), + libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?; + Ok(FileAttr::from_stat64(stat)) +} + +pub fn lstat(p: &Path) -> io::Result<FileAttr> { + let p = cstr(p)?; + + cfg_has_statx! { + if let Some(ret) = unsafe { try_statx( + libc::AT_FDCWD, + p.as_ptr(), + libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, + libc::STATX_ALL, + ) } { + return ret; + } + } + + let mut stat: stat64 = unsafe { mem::zeroed() }; + cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?; + Ok(FileAttr::from_stat64(stat)) +} + +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(), ptr::null_mut()); + if r.is_null() { + return Err(io::Error::last_os_error()); + } + buf = CStr::from_ptr(r).to_bytes().to_vec(); + libc::free(r as *mut _); + } + Ok(PathBuf::from(OsString::from_vec(buf))) +} + +fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { + use crate::fs::File; + + let reader = File::open(from)?; + let metadata = reader.metadata()?; + if !metadata.is_file() { + return Err(Error::new( + ErrorKind::InvalidInput, + "the source path is not an existing regular file", + )); + } + Ok((reader, metadata)) +} + +fn open_to_and_set_permissions( + to: &Path, + reader_metadata: crate::fs::Metadata, +) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { + use crate::fs::OpenOptions; + use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt}; + + let perm = reader_metadata.permissions(); + let writer = OpenOptions::new() + // create the file with the correct mode right away + .mode(perm.mode()) + .write(true) + .create(true) + .truncate(true) + .open(to)?; + let writer_metadata = writer.metadata()?; + if writer_metadata.is_file() { + // Set the correct file permissions, in case the file already existed. + // Don't set the permissions on already existing non-files like + // pipes/FIFOs or device nodes. + writer.set_permissions(perm)?; + } + Ok((writer, writer_metadata)) +} + +#[cfg(not(any( + target_os = "linux", + target_os = "android", + target_os = "macos", + target_os = "ios" +)))] +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + let (mut reader, reader_metadata) = open_from(from)?; + let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; + + io::copy(&mut reader, &mut writer) +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + use crate::cmp; + use crate::sync::atomic::{AtomicBool, Ordering}; + + // Kernel prior to 4.5 don't have copy_file_range + // We store the availability in a global to avoid unnecessary 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) + } + + let (mut reader, reader_metadata) = open_from(from)?; + let len = reader_metadata.len(); + let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; + + 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 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::EINVAL + || 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) + // - copy_file_range cannot be used with pipes or device nodes (EINVAL) + assert_eq!(written, 0); + return io::copy(&mut reader, &mut writer); + } + _ => return Err(err), + } + } + } + } + Ok(written) +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + use crate::sync::atomic::{AtomicBool, Ordering}; + + const COPYFILE_ACL: u32 = 1 << 0; + const COPYFILE_STAT: u32 = 1 << 1; + const COPYFILE_XATTR: u32 = 1 << 2; + const COPYFILE_DATA: u32 = 1 << 3; + + const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL; + const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR; + const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA; + + const COPYFILE_STATE_COPIED: u32 = 8; + + #[allow(non_camel_case_types)] + type copyfile_state_t = *mut libc::c_void; + #[allow(non_camel_case_types)] + type copyfile_flags_t = u32; + + extern "C" { + fn fcopyfile( + from: libc::c_int, + to: libc::c_int, + state: copyfile_state_t, + flags: copyfile_flags_t, + ) -> libc::c_int; + fn copyfile_state_alloc() -> copyfile_state_t; + fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int; + fn copyfile_state_get( + state: copyfile_state_t, + flag: u32, + dst: *mut libc::c_void, + ) -> libc::c_int; + } + + struct FreeOnDrop(copyfile_state_t); + impl Drop for FreeOnDrop { + fn drop(&mut self) { + // The code below ensures that `FreeOnDrop` is never a null pointer + unsafe { + // `copyfile_state_free` returns -1 if the `to` or `from` files + // cannot be closed. However, this is not considered this an + // error. + copyfile_state_free(self.0); + } + } + } + + // MacOS prior to 10.12 don't support `fclonefileat` + // We store the availability in a global to avoid unnecessary syscalls + static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true); + syscall! { + fn fclonefileat( + srcfd: libc::c_int, + dst_dirfd: libc::c_int, + dst: *const libc::c_char, + flags: libc::c_int + ) -> libc::c_int + } + + let (reader, reader_metadata) = open_from(from)?; + + // Opportunistically attempt to create a copy-on-write clone of `from` + // using `fclonefileat`. + if HAS_FCLONEFILEAT.load(Ordering::Relaxed) { + let to = cstr(to)?; + let clonefile_result = + cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }); + match clonefile_result { + Ok(_) => return Ok(reader_metadata.len()), + Err(err) => match err.raw_os_error() { + // `fclonefileat` will fail on non-APFS volumes, if the + // destination already exists, or if the source and destination + // are on different devices. In all these cases `fcopyfile` + // should succeed. + Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (), + Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed), + _ => return Err(err), + }, + } + } + + // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. + let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?; + + // We ensure that `FreeOnDrop` never contains a null pointer so it is + // always safe to call `copyfile_state_free` + let state = unsafe { + let state = copyfile_state_alloc(); + if state.is_null() { + return Err(crate::io::Error::last_os_error()); + } + FreeOnDrop(state) + }; + + let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA }; + + cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; + + let mut bytes_copied: libc::off_t = 0; + cvt(unsafe { + copyfile_state_get( + state.0, + COPYFILE_STATE_COPIED, + &mut bytes_copied as *mut libc::off_t as *mut libc::c_void, + ) + })?; + Ok(bytes_copied as u64) +} diff --git a/library/std/src/sys/unix/io.rs b/library/std/src/sys/unix/io.rs new file mode 100644 index 00000000000..deb5ee76bd0 --- /dev/null +++ b/library/std/src/sys/unix/io.rs @@ -0,0 +1,76 @@ +use crate::marker::PhantomData; +use crate::slice; + +use libc::{c_void, iovec}; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: iovec, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { + vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} + +#[repr(transparent)] +pub struct IoSliceMut<'a> { + vec: iovec, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { + vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSliceMut beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs new file mode 100644 index 00000000000..a2912387108 --- /dev/null +++ b/library/std/src/sys/unix/l4re.rs @@ -0,0 +1,503 @@ +macro_rules! unimpl { + () => { + return Err(io::Error::new(io::ErrorKind::Other, "No networking available on L4Re.")); + }; +} + +pub mod net { + #![allow(warnings)] + use crate::convert::TryFrom; + use crate::fmt; + use crate::io::{self, IoSlice, IoSliceMut}; + use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; + use crate::sys::fd::FileDesc; + use crate::sys_common::{AsInner, FromInner, IntoInner}; + use crate::time::Duration; + + #[allow(unused_extern_crates)] + 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 read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + unimpl!(); + } + + pub fn is_read_vectored(&self) -> bool { + 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 write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { + unimpl!(); + } + + pub fn is_write_vectored(&self) -> bool { + 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(_: io::Result<&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 read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + unimpl!(); + } + + pub fn is_read_vectored(&self) -> bool { + unimpl!(); + } + + pub fn write(&self, _: &[u8]) -> io::Result<usize> { + unimpl!(); + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> { + unimpl!(); + } + + pub fn is_write_vectored(&self) -> bool { + 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(_: io::Result<&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(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { + unimpl!(); + } + + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + unimpl!(); + } + + 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, _: io::Result<&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 + } + } + + impl LookupHost { + pub fn port(&self) -> u16 { + unimpl!(); + } + } + + unsafe impl Sync for LookupHost {} + unsafe impl Send for LookupHost {} + + impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &str) -> io::Result<LookupHost> { + unimpl!(); + } + } + + impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { + unimpl!(); + } + } +} diff --git a/library/std/src/sys/unix/memchr.rs b/library/std/src/sys/unix/memchr.rs new file mode 100644 index 00000000000..a9273ea676c --- /dev/null +++ b/library/std/src/sys/unix/memchr.rs @@ -0,0 +1,38 @@ +// Original implementation taken from rust-memchr. +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> { + let p = unsafe { + libc::memchr( + 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)) } +} + +pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> { + #[cfg(target_os = "linux")] + fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> { + // 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) + } + + memrchr_specific(needle, haystack) +} diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs new file mode 100644 index 00000000000..eddf00d3979 --- /dev/null +++ b/library/std/src/sys/unix/mod.rs @@ -0,0 +1,168 @@ +#![allow(missing_docs, nonstandard_style)] + +use crate::io::ErrorKind; + +#[cfg(any(doc, target_os = "linux"))] +pub use crate::os::linux as platform; + +#[cfg(all(not(doc), target_os = "android"))] +pub use crate::os::android as platform; +#[cfg(all(not(doc), target_os = "dragonfly"))] +pub use crate::os::dragonfly as platform; +#[cfg(all(not(doc), target_os = "emscripten"))] +pub use crate::os::emscripten as platform; +#[cfg(all(not(doc), target_os = "freebsd"))] +pub use crate::os::freebsd as platform; +#[cfg(all(not(doc), target_os = "fuchsia"))] +pub use crate::os::fuchsia as platform; +#[cfg(all(not(doc), target_os = "haiku"))] +pub use crate::os::haiku as platform; +#[cfg(all(not(doc), target_os = "illumos"))] +pub use crate::os::illumos as platform; +#[cfg(all(not(doc), target_os = "ios"))] +pub use crate::os::ios as platform; +#[cfg(all(not(doc), target_os = "l4re"))] +pub use crate::os::linux as platform; +#[cfg(all(not(doc), target_os = "macos"))] +pub use crate::os::macos as platform; +#[cfg(all(not(doc), target_os = "netbsd"))] +pub use crate::os::netbsd as platform; +#[cfg(all(not(doc), target_os = "openbsd"))] +pub use crate::os::openbsd as platform; +#[cfg(all(not(doc), target_os = "redox"))] +pub use crate::os::redox as platform; +#[cfg(all(not(doc), target_os = "solaris"))] +pub use crate::os::solaris as platform; + +pub use self::rand::hashmap_random_keys; +pub use libc::strlen; + +#[macro_use] +pub mod weak; + +pub mod alloc; +pub mod android; +pub mod args; +pub mod cmath; +pub mod condvar; +pub mod env; +pub mod ext; +pub mod fd; +pub mod fs; +pub mod io; +#[cfg(target_os = "l4re")] +mod l4re; +pub mod memchr; +pub mod mutex; +#[cfg(not(target_os = "l4re"))] +pub mod net; +#[cfg(target_os = "l4re")] +pub use self::l4re::net; +pub mod os; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rand; +pub mod rwlock; +pub mod stack_overflow; +pub mod stdio; +pub mod thread; +pub mod thread_local_dtor; +pub mod thread_local_key; +pub mod time; + +pub use crate::sys_common::os_str_bytes as os_str; + +#[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(); + } + + #[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() {} +} + +#[cfg(target_os = "android")] +pub use crate::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 { + libc::ECONNREFUSED => ErrorKind::ConnectionRefused, + libc::ECONNRESET => ErrorKind::ConnectionReset, + libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, + libc::EPIPE => ErrorKind::BrokenPipe, + libc::ENOTCONN => ErrorKind::NotConnected, + libc::ECONNABORTED => ErrorKind::ConnectionAborted, + libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + libc::EADDRINUSE => ErrorKind::AddrInUse, + libc::ENOENT => ErrorKind::NotFound, + libc::EINTR => ErrorKind::Interrupted, + libc::EINVAL => ErrorKind::InvalidInput, + libc::ETIMEDOUT => ErrorKind::TimedOut, + libc::EEXIST => ErrorKind::AlreadyExists, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => ErrorKind::WouldBlock, + + _ => ErrorKind::Other, + } +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> { + if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } +} + +pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T> +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + other => return other, + } + } +} + +// On Unix-like platforms, libc::abort will unregister signal handlers +// including the SIGABRT handler, preventing the abort from being blocked, and +// fclose streams, with the side effect of flushing them so libc buffered +// output will be printed. Additionally the shell will generally print a more +// understandable error message like "Abort trap" rather than "Illegal +// instruction" that intrinsics::abort would cause, as intrinsics::abort is +// implemented as an illegal instruction. +pub fn abort_internal() -> ! { + unsafe { libc::abort() } +} diff --git a/library/std/src/sys/unix/mutex.rs b/library/std/src/sys/unix/mutex.rs new file mode 100644 index 00000000000..45c600f75f5 --- /dev/null +++ b/library/std/src/sys/unix/mutex.rs @@ -0,0 +1,137 @@ +use crate::cell::UnsafeCell; +use crate::mem::MaybeUninit; + +pub struct Mutex { + inner: UnsafeCell<libc::pthread_mutex_t>, +} + +#[inline] +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 { + // Might be moved to a different address, so it is better to avoid + // initialization of potentially opaque OS data before it landed. + // Be very careful using this newly constructed `Mutex`, reentrant + // locking is undefined behavior until `init` is called! + Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } + } + #[inline] + pub unsafe fn init(&mut self) { + // 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 + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html). + // This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL + // (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that + // case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same + // as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in + // a Mutex where re-locking is UB. + // + // 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 when 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. + let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); + let r = libc::pthread_mutexattr_init(attr.as_mut_ptr()); + debug_assert_eq!(r, 0); + let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL); + debug_assert_eq!(r, 0); + let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); + debug_assert_eq!(r, 0); + let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn lock(&self) { + let r = libc::pthread_mutex_lock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn unlock(&self) { + let r = libc::pthread_mutex_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_lock(&self) -> bool { + 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) { + 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<libc::pthread_mutex_t>, +} + +unsafe impl Send for ReentrantMutex {} +unsafe impl Sync for ReentrantMutex {} + +impl ReentrantMutex { + pub const unsafe fn uninitialized() -> ReentrantMutex { + ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } + } + + pub unsafe fn init(&self) { + let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit(); + let result = libc::pthread_mutexattr_init(attr.as_mut_ptr()); + debug_assert_eq!(result, 0); + let result = + libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE); + debug_assert_eq!(result, 0); + let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); + debug_assert_eq!(result, 0); + let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); + debug_assert_eq!(result, 0); + } + + pub unsafe fn lock(&self) { + let result = libc::pthread_mutex_lock(self.inner.get()); + debug_assert_eq!(result, 0); + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + libc::pthread_mutex_trylock(self.inner.get()) == 0 + } + + pub unsafe fn unlock(&self) { + let result = libc::pthread_mutex_unlock(self.inner.get()); + debug_assert_eq!(result, 0); + } + + pub unsafe fn destroy(&self) { + let result = libc::pthread_mutex_destroy(self.inner.get()); + debug_assert_eq!(result, 0); + } +} diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs new file mode 100644 index 00000000000..011325fddc5 --- /dev/null +++ b/library/std/src/sys/unix/net.rs @@ -0,0 +1,382 @@ +use crate::cmp; +use crate::ffi::CStr; +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::mem; +use crate::net::{Shutdown, SocketAddr}; +use crate::str; +use crate::sys::fd::FileDesc; +use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::{Duration, Instant}; + +use libc::{c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK}; + +pub use crate::sys::{cvt, cvt_r}; + +#[allow(unused_extern_crates)] +pub extern crate libc as netc; + +pub type wrlen_t = size_t; + +pub struct Socket(FileDesc); + +pub fn init() {} + +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)).to_bytes()).unwrap().to_owned() + }; + Err(io::Error::new( + io::ErrorKind::Other, + &format!("failed to lookup address information: {}", detail)[..], + )) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> { + let fam = match *addr { + SocketAddr::V4(..) => libc::AF_INET, + SocketAddr::V6(..) => libc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { + unsafe { + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + // On Linux we pass the SOCK_CLOEXEC flag to atomically create + // the socket and set it as CLOEXEC, added in 2.6.27. + let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; + Ok(Socket(FileDesc::new(fd))) + } else { + let fd = cvt(libc::socket(fam, ty, 0))?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + let socket = Socket(fd); + + // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(target_vendor = "apple")] + setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; + + Ok(socket) + } + } + } + } + + pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { + unsafe { + let mut fds = [0, 0]; + + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + // Like above, set cloexec atomically + cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; + Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))) + } else { + cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; + let a = FileDesc::new(fds[0]); + let b = FileDesc::new(fds[1]); + a.set_cloexec()?; + b.set_cloexec()?; + Ok((Socket(a), Socket(b))) + } + } + } + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addrp, len) = addr.into_inner(); + cvt(libc::connect(self.0.raw(), addrp, len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout", + )); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { libc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if err.kind() != io::ErrorKind::Interrupted { + return Err(err); + } + } + 0 => {} + _ => { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP rather than read readiness + if pollfd.revents & libc::POLLHUP != 0 { + let e = self.take_error()?.unwrap_or_else(|| { + io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") + }); + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> { + // 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, glibc 2.10 and musl 0.9.5. + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + let fd = cvt_r(|| unsafe { + libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) + })?; + Ok(Socket(FileDesc::new(fd))) + } else { + let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + Ok(Socket(fd)) + } + } + } + + pub fn duplicate(&self) -> io::Result<Socket> { + self.0.duplicate().map(Socket) + } + + fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) + })?; + Ok(ret as usize) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, 0) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { + self.recv_with_flags(buf, MSG_PEEK) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; + + let n = cvt(unsafe { + libc::recvfrom( + self.0.raw(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + &mut storage as *mut _ as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "cannot set a 0 duration timeout", + )); + } + + let secs = if dur.as_secs() > libc::time_t::MAX as u64 { + libc::time_t::MAX + } else { + dur.as_secs() as libc::time_t + }; + let mut timeout = libc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as libc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => libc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, libc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> { + let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => libc::SHUT_RDWR, + }; + cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; + Ok(()) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result<bool> { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; + Ok(raw != 0) + } + + #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as libc::c_int; + cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop) + } + + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + // FIONBIO is inadequate for sockets on illumos/Solaris, so use the + // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. + self.0.set_nonblocking(nonblocking) + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } +} + +impl AsInner<c_int> for Socket { + fn as_inner(&self) -> &c_int { + self.0.as_inner() + } +} + +impl FromInner<c_int> for Socket { + fn from_inner(fd: c_int) -> Socket { + Socket(FileDesc::new(fd)) + } +} + +impl IntoInner<c_int> for Socket { + fn into_inner(self) -> c_int { + self.0.into_raw() + } +} + +// In versions of glibc prior to 2.26, there's a bug where the DNS resolver +// will cache the contents of /etc/resolv.conf, so changes to that file on disk +// can be ignored by a long-running program. That can break DNS lookups on e.g. +// laptops where the network comes and goes. See +// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some +// distros including Debian have patched glibc to fix this for a long time. +// +// A workaround for this bug is to call the res_init libc function, to clear +// the cached configs. Unfortunately, while we believe glibc's implementation +// of res_init is thread-safe, we know that other implementations are not +// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could +// try to synchronize its res_init calls with a Mutex, but that wouldn't +// protect programs that call into libc in other ways. So instead of calling +// res_init unconditionally, we call it only when we detect we're linking +// against glibc version < 2.26. (That is, when we both know its needed and +// believe it's thread-safe). +#[cfg(target_env = "gnu")] +fn on_resolver_failure() { + use crate::sys; + + // If the version fails to parse, we treat it the same as "not glibc". + if let Some(version) = sys::os::glibc_version() { + if version < (2, 26) { + unsafe { libc::res_init() }; + } + } +} + +#[cfg(not(target_env = "gnu"))] +fn on_resolver_failure() {} diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs new file mode 100644 index 00000000000..2fcb5b9c4e6 --- /dev/null +++ b/library/std/src/sys/unix/os.rs @@ -0,0 +1,674 @@ +//! Implementation of `std::os` functionality for unix systems + +#![allow(unused_imports)] // lots of cfg code here + +use crate::os::unix::prelude::*; + +use crate::error::Error as StdError; +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::iter; +use crate::marker::PhantomData; +use crate::mem; +use crate::memchr; +use crate::path::{self, PathBuf}; +use crate::ptr; +use crate::slice; +use crate::str; +use crate::sys::cvt; +use crate::sys::fd; +use crate::sys_common::mutex::{Mutex, MutexGuard}; +use crate::vec; + +use libc::{c_char, c_int, c_void}; + +const TMPBUF_SZ: usize = 128; + +cfg_if::cfg_if! { + if #[cfg(target_os = "redox")] { + const PATH_SEPARATOR: u8 = b';'; + } else { + const PATH_SEPARATOR: u8 = b':'; + } +} + +extern "C" { + #[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 = "netbsd", + target_os = "openbsd", + target_os = "android", + target_os = "redox", + target_env = "newlib" + ), + link_name = "__errno" + )] + #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), 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_location()) as i32 } +} + +/// Sets the platform-specific value of errno +#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly")))] // needed for readdir and syscall! +#[allow(dead_code)] // but not all target cfgs actually end up using it +pub fn set_errno(e: i32) { + unsafe { *errno_location() = e as c_int } +} + +#[cfg(target_os = "dragonfly")] +pub fn errno() -> i32 { + extern "C" { + #[thread_local] + static errno: c_int; + } + + unsafe { errno as i32 } +} + +#[cfg(target_os = "dragonfly")] +pub fn set_errno(e: i32) { + extern "C" { + #[thread_local] + static mut errno: c_int; + } + + unsafe { + errno = e; + } +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(errno: i32) -> String { + extern "C" { + #[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; + } + + let mut buf = [0 as c_char; TMPBUF_SZ]; + + let p = buf.as_mut_ptr(); + unsafe { + if strerror_r(errno as c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + + let p = p as *const _; + str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() + } +} + +pub fn getcwd() -> io::Result<PathBuf> { + let mut buf = Vec::with_capacity(512); + loop { + unsafe { + let ptr = buf.as_mut_ptr() as *mut libc::c_char; + if !libc::getcwd(ptr, buf.capacity()).is_null() { + let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); + buf.set_len(len); + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); + } else { + let error = io::Error::last_os_error(); + if error.raw_os_error() != Some(libc::ERANGE) { + return Err(error); + } + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + } + } +} + +pub fn chdir(p: &path::Path) -> io::Result<()> { + let p: &OsStr = p.as_ref(); + let p = CString::new(p.as_bytes())?; + unsafe { + match libc::chdir(p.as_ptr()) == (0 as c_int) { + true => Ok(()), + false => Err(io::Error::last_os_error()), + } + } +} + +pub struct SplitPaths<'a> { + iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>, fn(&'a [u8]) -> PathBuf>, +} + +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { + fn bytes_to_path(b: &[u8]) -> PathBuf { + PathBuf::from(<OsStr as OsStrExt>::from_bytes(b)) + } + fn is_separator(b: &u8) -> bool { + *b == PATH_SEPARATOR + } + let unparsed = unparsed.as_bytes(); + SplitPaths { + iter: unparsed + .split(is_separator as fn(&u8) -> bool) + .map(bytes_to_path as fn(&[u8]) -> PathBuf), + } +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option<PathBuf> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> +where + I: Iterator<Item = T>, + T: AsRef<OsStr>, +{ + let mut joined = Vec::new(); + + for (i, path) in paths.enumerate() { + let path = path.as_ref().as_bytes(); + if i > 0 { + joined.push(PATH_SEPARATOR) + } + if path.contains(&PATH_SEPARATOR) { + return Err(JoinPathsError); + } + joined.extend_from_slice(path); + } + Ok(OsStringExt::from_vec(joined)) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "path segment contains separator `{}`", PATH_SEPARATOR) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "failed to join paths" + } +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +pub fn current_exe() -> io::Result<PathBuf> { + 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))) + } +} + +#[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 crate::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(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'/') { + crate::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> { + match crate::fs::read_link("/proc/self/exe") { + Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::Error::new( + io::ErrorKind::Other, + "no /proc/self/exe available. Is /proc mounted?", + )), + other => other, + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn current_exe() -> io::Result<PathBuf> { + extern "C" { + 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", target_os = "illumos"))] +pub fn current_exe() -> io::Result<PathBuf> { + extern "C" { + 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 "C" { + 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 crate::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(target_os = "redox")] +pub fn current_exe() -> io::Result<PathBuf> { + crate::fs::read_to_string("sys:exe").map(PathBuf::from) +} + +#[cfg(any(target_os = "fuchsia", target_os = "l4re"))] +pub fn current_exe() -> io::Result<PathBuf> { + use crate::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)> { + 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 "C" { + fn _NSGetEnviron() -> *mut *const *const c_char; + } + _NSGetEnviron() +} + +#[cfg(not(target_os = "macos"))] +pub unsafe fn environ() -> *mut *const *const c_char { + extern "C" { + static mut environ: *const *const c_char; + } + &mut environ +} + +pub unsafe fn env_lock() -> MutexGuard<'static> { + // We never call `ENV_LOCK.init()`, so it is UB to attempt to + // acquire this mutex reentrantly! + static ENV_LOCK: Mutex = Mutex::new(); + ENV_LOCK.lock() +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + unsafe { + let _guard = env_lock(); + let mut environ = *environ(); + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }; + } + + 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>> { + // 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 { + let _guard = env_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())) + }; + 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 { + let _guard = env_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) + } +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + let nbuf = CString::new(n.as_bytes())?; + + unsafe { + let _guard = env_lock(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) + } +} + +pub fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} + +pub fn temp_dir() -> PathBuf { + crate::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> { + return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from); + + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "emscripten", + target_os = "redox" + ))] + unsafe fn fallback() -> Option<OsString> { + None + } + #[cfg(not(any( + target_os = "android", + target_os = "ios", + target_os = "emscripten", + target_os = "redox" + )))] + 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) -> ! { + unsafe { libc::exit(code as c_int) } +} + +pub fn getpid() -> u32 { + 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, + } +} + +#[cfg(all(test, target_env = "gnu"))] +mod test { + use super::*; + + #[test] + fn test_glibc_version() { + // This mostly just tests that the weak linkage doesn't panic wildly... + glibc_version(); + } + + #[test] + fn test_parse_glibc_version() { + let cases = [ + ("0.0", Some((0, 0))), + ("01.+2", Some((1, 2))), + ("3.4.5.six", Some((3, 4))), + ("1", None), + ("1.-2", None), + ("1.foo", None), + ("foo.1", None), + ]; + for &(version_str, parsed) in cases.iter() { + assert_eq!(parsed, parse_glibc_version(version_str)); + } + } +} diff --git a/library/std/src/sys/unix/path.rs b/library/std/src/sys/unix/path.rs new file mode 100644 index 00000000000..840a7ae0426 --- /dev/null +++ b/library/std/src/sys/unix/path.rs @@ -0,0 +1,19 @@ +use crate::ffi::OsStr; +use crate::path::Prefix; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' +} + +pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> { + None +} + +pub const MAIN_SEP_STR: &str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs new file mode 100644 index 00000000000..7ae37bdda70 --- /dev/null +++ b/library/std/src/sys/unix/pipe.rs @@ -0,0 +1,122 @@ +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::mem; +use crate::sys::fd::FileDesc; +use crate::sys::{cvt, cvt_r}; + +//////////////////////////////////////////////////////////////////////////////// +// Anonymous pipes +//////////////////////////////////////////////////////////////////////////////// + +pub struct AnonPipe(FileDesc); + +pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut fds = [0; 2]; + + // The only known way right now to create atomically set the CLOEXEC flag is + // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9 + // and musl 0.9.3, and some other targets also have it. + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox" + ))] { + cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?; + Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))) + } else { + 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))) + } + } +} + +impl AnonPipe { + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + 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<()> { + // 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(drop); + } + if fds[1].revents != 0 && read(&p2, v2)? { + p1.set_nonblocking(false)?; + return p1.read_to_end(v1).map(drop); + } + } + + // 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/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs new file mode 100644 index 00000000000..553e980f08e --- /dev/null +++ b/library/std/src/sys/unix/process/mod.rs @@ -0,0 +1,13 @@ +pub use self::process_common::{Command, ExitCode, Stdio, StdioPipes}; +pub use self::process_inner::{ExitStatus, Process}; +pub use crate::ffi::OsString as EnvKey; + +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/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs new file mode 100644 index 00000000000..6e33cdd3c48 --- /dev/null +++ b/library/std/src/sys/unix/process/process_common.rs @@ -0,0 +1,469 @@ +use crate::os::unix::prelude::*; + +use crate::collections::BTreeMap; +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::ptr; +use crate::sys::fd::FileDesc; +use crate::sys::fs::File; +use crate::sys::pipe::{self, AnonPipe}; +use crate::sys_common::process::CommandEnv; + +#[cfg(not(target_os = "fuchsia"))] +use crate::sys::fs::OpenOptions; + +use libc::{c_char, c_int, gid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; + +cfg_if::cfg_if! { + if #[cfg(target_os = "fuchsia")] { + // fuchsia doesn't have /dev/null + } else if #[cfg(target_os = "redox")] { + const DEV_NULL: &str = "null:\0"; + } else { + const DEV_NULL: &str = "/dev/null\0"; + } +} + +// 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_if::cfg_if! { + if #[cfg(target_os = "android")] { + pub unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int { + set.write_bytes(0u8, 1); + return 0; + } + #[allow(dead_code)] + pub unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int { + use crate::{slice, mem}; + + 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; + } + } else { + pub use libc::{sigemptyset, sigaddset}; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// 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, + + cwd: Option<CString>, + uid: Option<uid_t>, + gid: Option<gid_t>, + saw_nul: bool, + closures: Vec<Box<dyn 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` and `Sync` +struct Argv(Vec<*const c_char>); + +// It is safe to make `Argv` `Send` and `Sync`, because it contains +// pointers to memory owned by `Command.args` +unsafe impl Send for Argv {} +unsafe impl Sync 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), + + // On Fuchsia, null stdio is the default, so we simply don't specify + // any actions at the time of spawning. + #[cfg(target_os = "fuchsia")] + Null, +} + +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()]), + args: vec![program.clone()], + program, + env: Default::default(), + cwd: None, + uid: None, + gid: None, + saw_nul, + closures: Vec::new(), + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn set_arg_0(&mut self, arg: &OsStr) { + // Set a new arg0 + let arg = os2c(arg, &mut self.saw_nul); + debug_assert!(self.argv.0.len() > 1); + self.argv.0[0] = arg.as_ptr(); + self.args[0] = arg; + } + + 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()] = 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 + } + + pub fn get_program(&self) -> &CStr { + &*self.program + } + + #[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<dyn FnMut() -> io::Result<()> + Send + Sync>> { + &mut self.closures + } + + pub unsafe fn pre_exec(&mut self, f: Box<dyn 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 { + &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<OsString, OsString>, saw_nul: &mut bool) -> CStringArray { + let mut result = CStringArray::with_capacity(env.len()); + for (mut k, v) in env { + // 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))) + } + + #[cfg(not(target_os = "fuchsia"))] + Stdio::Null => { + let mut opts = OpenOptions::new(); + opts.read(readable); + opts.write(!readable); + let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) }; + let fd = File::open_c(&path, &opts)?; + Ok((ChildStdio::Owned(fd.into_fd()), None)) + } + + #[cfg(target_os = "fuchsia")] + Stdio::Null => Ok((ChildStdio::Null, 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()), + + #[cfg(target_os = "fuchsia")] + ChildStdio::Null => None, + } + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.program != self.args[0] { + write!(f, "[{:?}] ", self.program)?; + } + write!(f, "{:?}", self.args[0])?; + + for arg in &self.args[1..] { + write!(f, " {:?}", arg)?; + } + Ok(()) + } +} + +#[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 crate::ffi::OsStr; + use crate::mem; + use crate::ptr; + use crate::sys::cvt; + + macro_rules! t { + ($e:expr) => { + match $e { + Ok(t) => t, + Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), + } + }; + } + + // 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)] + #[cfg_attr(target_arch = "riscv64", 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 = mem::MaybeUninit::<libc::sigset_t>::uninit(); + let mut old_set = mem::MaybeUninit::<libc::sigset_t>::uninit(); + t!(cvt(sigemptyset(set.as_mut_ptr()))); + t!(cvt(sigaddset(set.as_mut_ptr(), libc::SIGINT))); + t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), old_set.as_mut_ptr()))); + + 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.as_ptr(), 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/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs new file mode 100644 index 00000000000..6daf2885bae --- /dev/null +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -0,0 +1,260 @@ +use crate::convert::TryInto; +use crate::fmt; +use crate::io; +use crate::mem; +use crate::ptr; + +use crate::sys::process::process_common::*; +use crate::sys::process::zircon::{zx_handle_t, Handle}; + +use libc::{c_int, size_t}; + +//////////////////////////////////////////////////////////////////////////////// +// 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 crate::sys::process::zircon::*; + + let envp = match maybe_envp { + // None means to clone the current environment, which is done in the + // flags below. + None => ptr::null(), + Some(envp) => envp.as_ptr(), + }; + + let make_action = |local_io: &ChildStdio, target_fd| -> io::Result<fdio_spawn_action_t> { + if let Some(local_fd) = local_io.fd() { + Ok(fdio_spawn_action_t { + action: FDIO_SPAWN_ACTION_TRANSFER_FD, + local_fd, + target_fd, + ..Default::default() + }) + } else { + if let ChildStdio::Null = local_io { + // acts as no-op + return Ok(Default::default()); + } + + let mut handle = ZX_HANDLE_INVALID; + let status = fdio_fd_clone(target_fd, &mut handle); + if status == ERR_INVALID_ARGS || status == ERR_NOT_SUPPORTED { + // This descriptor is closed; skip it rather than generating an + // error. + return Ok(Default::default()); + } + zx_cvt(status)?; + + let mut cloned_fd = 0; + zx_cvt(fdio_fd_create(handle, &mut cloned_fd))?; + + Ok(fdio_spawn_action_t { + action: FDIO_SPAWN_ACTION_TRANSFER_FD, + local_fd: cloned_fd as i32, + target_fd, + ..Default::default() + }) + } + }; + + // Clone stdin, stdout, and stderr + let action1 = make_action(&stdio.stdin, 0)?; + let action2 = make_action(&stdio.stdout, 1)?; + let action3 = make_action(&stdio.stderr, 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( + ZX_HANDLE_INVALID, + FDIO_SPAWN_CLONE_JOB + | FDIO_SPAWN_CLONE_LDSVC + | FDIO_SPAWN_CLONE_NAMESPACE + | FDIO_SPAWN_CLONE_ENVIRON, // this is ignored when envp is non-null + self.get_program().as_ptr(), + self.get_argv().as_ptr(), + envp, + actions.len() as size_t, + 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 crate::sys::process::zircon::*; + + unsafe { + zx_cvt(zx_task_kill(self.handle.raw()))?; + } + + Ok(()) + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + use crate::default::Default; + use crate::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(proc_info.return_code)) + } + + pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { + use crate::default::Default; + use crate::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(proc_info.return_code))) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(i64); + +impl ExitStatus { + pub fn success(&self) -> bool { + self.code() == Some(0) + } + + pub fn code(&self) -> Option<i32> { + // FIXME: support extracting return code as an i64 + self.0.try_into().ok() + } + + pub fn signal(&self) -> Option<i32> { + None + } +} + +/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. +impl From<c_int> for ExitStatus { + fn from(a: c_int) -> ExitStatus { + ExitStatus(a as i64) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "exit code: {}", self.0) + } +} diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs new file mode 100644 index 00000000000..0f349dfa302 --- /dev/null +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -0,0 +1,494 @@ +use crate::convert::TryInto; +use crate::fmt; +use crate::io::{self, Error, ErrorKind}; +use crate::ptr; +use crate::sys; +use crate::sys::cvt; +use crate::sys::process::process_common::*; + +use libc::{c_int, gid_t, pid_t, uid_t}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + const CLOEXEC_MSG_FOOTER: [u8; 4] = *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()?; + + // Whatever happens after the fork is almost for sure going to touch or + // look at the environment in one way or another (PATH in `execvp` or + // accessing the `environ` pointer ourselves). Make sure no other thread + // is accessing the environment when we do the fork itself. + // + // Note that as soon as we're done with the fork there's no need to hold + // a lock any more because the parent won't do anything and the child is + // in its own process. + let result = unsafe { + let _env_lock = sys::os::env_lock(); + cvt(libc::fork())? + }; + + let pid = unsafe { + match result { + 0 => { + drop(input); + let Err(err) = self.do_exec(theirs, envp.as_ref()); + let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; + let errno = errno.to_be_bytes(); + let bytes = [ + errno[0], + errno[1], + errno[2], + errno[3], + 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, 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) => { + let (errno, footer) = bytes.split_at(4); + assert_eq!( + CLOEXEC_MSG_FOOTER, footer, + "Validation on the CLOEXEC pipe failed: {:?}", + bytes + ); + let errno = i32::from_be_bytes(errno.try_into().unwrap()); + 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") + } + } + } + } + + 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 { + // Similar to when forking, we want to ensure that access to + // the environment is synchronized, so make sure to grab the + // environment lock before we try to exec. + let _lock = sys::os::env_lock(); + + let Err(e) = self.do_exec(theirs, envp.as_ref()); + e + } + } + 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>, + ) -> Result<!, io::Error> { + use crate::sys::{self, cvt_r}; + + if let Some(fd) = stdio.stdin.fd() { + cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?; + } + if let Some(fd) = stdio.stdout.fd() { + cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?; + } + if let Some(fd) = stdio.stderr.fd() { + cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?; + } + + #[cfg(not(target_os = "l4re"))] + { + if let Some(u) = self.get_gid() { + 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. + //FIXME: Redox kernel does not support setgroups yet + #[cfg(not(target_os = "redox"))] + let _ = libc::setgroups(0, ptr::null()); + cvt(libc::setuid(u as uid_t))?; + } + } + if let Some(ref cwd) = *self.get_cwd() { + cvt(libc::chdir(cwd.as_ptr()))?; + } + + // emscripten has no signal support. + #[cfg(not(target_os = "emscripten"))] + { + use crate::mem::MaybeUninit; + // 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 = MaybeUninit::<libc::sigset_t>::uninit(); + cvt(sigemptyset(set.as_mut_ptr()))?; + cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), ptr::null_mut()))?; + let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL); + if ret == libc::SIG_ERR { + return Err(io::Error::last_os_error()); + } + } + + for callback in self.get_closures().iter_mut() { + callback()?; + } + + // Although we're performing an exec here we may also return with an + // error from this function (without actually exec'ing) in which case we + // want to be sure to restore the global environment back to what it + // once was, ensuring that our temporary override, when free'd, doesn't + // corrupt our process's environment. + let mut _reset = None; + if let Some(envp) = maybe_envp { + struct Reset(*const *const libc::c_char); + + impl Drop for Reset { + fn drop(&mut self) { + unsafe { + *sys::os::environ() = self.0; + } + } + } + + _reset = Some(Reset(*sys::os::environ())); + *sys::os::environ() = envp.as_ptr(); + } + + libc::execvp(self.get_program().as_ptr(), self.get_argv().as_ptr()); + Err(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 crate::mem::MaybeUninit; + use crate::sys; + + if self.get_gid().is_some() + || self.get_uid().is_some() + || self.env_saw_path() + || !self.get_closures().is_empty() + { + 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); + } + } + + // Solaris and glibc 2.29+ can set a new working directory, and maybe + // others will gain this non-POSIX function too. We'll check for this + // weak symbol as soon as it's needed, so we can return early otherwise + // to do a manual chdir before exec. + weak! { + fn posix_spawn_file_actions_addchdir_np( + *mut libc::posix_spawn_file_actions_t, + *const libc::c_char + ) -> libc::c_int + } + let addchdir = match self.get_cwd() { + Some(cwd) => match posix_spawn_file_actions_addchdir_np.get() { + Some(f) => Some((f, cwd)), + None => return Ok(None), + }, + None => None, + }; + + let mut p = Process { pid: 0, status: None }; + + struct PosixSpawnFileActions(MaybeUninit<libc::posix_spawn_file_actions_t>); + + impl Drop for PosixSpawnFileActions { + fn drop(&mut self) { + unsafe { + libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr()); + } + } + } + + struct PosixSpawnattr(MaybeUninit<libc::posix_spawnattr_t>); + + impl Drop for PosixSpawnattr { + fn drop(&mut self) { + unsafe { + libc::posix_spawnattr_destroy(self.0.as_mut_ptr()); + } + } + } + + unsafe { + let mut file_actions = PosixSpawnFileActions(MaybeUninit::uninit()); + let mut attrs = PosixSpawnattr(MaybeUninit::uninit()); + + libc::posix_spawnattr_init(attrs.0.as_mut_ptr()); + libc::posix_spawn_file_actions_init(file_actions.0.as_mut_ptr()); + + if let Some(fd) = stdio.stdin.fd() { + cvt(libc::posix_spawn_file_actions_adddup2( + file_actions.0.as_mut_ptr(), + fd, + libc::STDIN_FILENO, + ))?; + } + if let Some(fd) = stdio.stdout.fd() { + cvt(libc::posix_spawn_file_actions_adddup2( + file_actions.0.as_mut_ptr(), + fd, + libc::STDOUT_FILENO, + ))?; + } + if let Some(fd) = stdio.stderr.fd() { + cvt(libc::posix_spawn_file_actions_adddup2( + file_actions.0.as_mut_ptr(), + fd, + libc::STDERR_FILENO, + ))?; + } + if let Some((f, cwd)) = addchdir { + cvt(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?; + } + + let mut set = MaybeUninit::<libc::sigset_t>::uninit(); + cvt(sigemptyset(set.as_mut_ptr()))?; + cvt(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?; + cvt(sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?; + cvt(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?; + + let flags = libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK; + cvt(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; + + // Make sure we synchronize access to the global `environ` resource + let _env_lock = sys::os::env_lock(); + let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); + let ret = libc::posix_spawnp( + &mut p.pid, + self.get_program().as_ptr(), + file_actions.0.as_ptr(), + attrs.0.as_ptr(), + 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(drop) + } + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + use crate::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))) + } + } +} + +/// 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 } + } +} + +/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. +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) + } + } +} diff --git a/library/std/src/sys/unix/process/zircon.rs b/library/std/src/sys/unix/process/zircon.rs new file mode 100644 index 00000000000..750b8f0762a --- /dev/null +++ b/library/std/src/sys/unix/process/zircon.rs @@ -0,0 +1,307 @@ +#![allow(non_camel_case_types, unused)] + +use crate::convert::TryInto; +use crate::i64; +use crate::io; +use crate::mem::MaybeUninit; +use crate::os::raw::c_char; + +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 = i64; +pub const ZX_TIME_INFINITE: zx_time_t = i64::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"); + } + } +} + +// Returned for topic ZX_INFO_PROCESS +#[derive(Default)] +#[repr(C)] +pub struct zx_info_process_t { + pub return_code: i64, + pub started: bool, + pub exited: bool, + pub debugger_attached: bool, +} + +extern "C" { + 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 "C" { + 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: size_t, + actions: *const fdio_spawn_action_t, + process: *mut zx_handle_t, + err_msg: *mut c_char, + ) -> zx_status_t; + + pub fn fdio_fd_clone(fd: c_int, out_handle: *mut zx_handle_t) -> zx_status_t; + pub fn fdio_fd_create(handle: zx_handle_t, fd: *mut c_int) -> 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/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs new file mode 100644 index 00000000000..eed6fbf13b7 --- /dev/null +++ b/library/std/src/sys/unix/rand.rs @@ -0,0 +1,230 @@ +use crate::mem; +use crate::slice; + +pub fn hashmap_random_keys() -> (u64, u64) { + let mut v = (0, 0); + unsafe { + let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8, mem::size_of_val(&v)); + imp::fill_bytes(view); + } + v +} + +#[cfg(all( + unix, + not(target_os = "macos"), + not(target_os = "ios"), + not(target_os = "openbsd"), + not(target_os = "freebsd"), + not(target_os = "netbsd"), + not(target_os = "fuchsia"), + not(target_os = "redox") +))] +mod imp { + use crate::fs::File; + use crate::io::Read; + + #[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_fill_bytes(_buf: &mut [u8]) -> bool { + false + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + fn getrandom_fill_bytes(v: &mut [u8]) -> bool { + use crate::sync::atomic::{AtomicBool, Ordering}; + use crate::sys::os::errno; + + static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false); + if GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed) { + return false; + } + + 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::ENOSYS || err == libc::EPERM { + // Fall back to reading /dev/urandom if `getrandom` is not + // supported on the current kernel. + // + // Also fall back in case it is disabled by something like + // seccomp or inside of virtual machines. + GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed); + return false; + } else if err == libc::EAGAIN { + return false; + } else { + panic!("unexpected getrandom error: {}", err); + } + } else { + read += result as usize; + } + } + true + } + + 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 over a + // possibly predictable entropy pool. + if getrandom_fill_bytes(v) { + return; + } + + // getrandom failed because it is permanently or temporarily (because + // of missing entropy) unavailable. Open /dev/urandom, read from it, + // and close it again. + 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 = "macos")] +mod imp { + use crate::fs::File; + use crate::io::Read; + use crate::sys::os::errno; + use libc::{c_int, c_void, size_t}; + + fn getentropy_fill_bytes(v: &mut [u8]) -> bool { + weak!(fn getentropy(*mut c_void, size_t) -> c_int); + + getentropy + .get() + .map(|f| { + // getentropy(2) permits a maximum buffer size of 256 bytes + for s in v.chunks_mut(256) { + let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) }; + if ret == -1 { + panic!("unexpected getentropy error: {}", errno()); + } + } + true + }) + .unwrap_or(false) + } + + pub fn fill_bytes(v: &mut [u8]) { + if getentropy_fill_bytes(v) { + return; + } + + // for older macos which doesn't support getentropy + 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 crate::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()); + } + } + } +} + +// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with +// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded +// from `/dev/random` and which runs on its own thread accessed via GCD. +// This seems needlessly heavyweight for the purposes of generating two u64s +// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is +// only used on iOS where direct access to `/dev/urandom` is blocked by the +// sandbox. +#[cfg(target_os = "ios")] +mod imp { + use crate::io; + use crate::ptr; + use libc::{c_int, size_t}; + + enum SecRandom {} + + #[allow(non_upper_case_globals)] + const kSecRandomDefault: *const SecRandom = ptr::null(); + + extern "C" { + 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(any(target_os = "freebsd", target_os = "netbsd"))] +mod imp { + use crate::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 "C" { + fn zx_cprng_draw(buffer: *mut u8, len: usize); + } + + pub fn fill_bytes(v: &mut [u8]) { + unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) } + } +} + +#[cfg(target_os = "redox")] +mod imp { + use crate::fs::File; + use crate::io::Read; + + pub fn fill_bytes(v: &mut [u8]) { + // Open rand:, read from it, and close it again. + let mut file = File::open("rand:").expect("failed to open rand:"); + file.read_exact(v).expect("failed to read rand:") + } +} diff --git a/library/std/src/sys/unix/rwlock.rs b/library/std/src/sys/unix/rwlock.rs new file mode 100644 index 00000000000..2b5067a34f6 --- /dev/null +++ b/library/std/src/sys/unix/rwlock.rs @@ -0,0 +1,141 @@ +use crate::cell::UnsafeCell; +use crate::sync::atomic::{AtomicUsize, Ordering}; + +pub struct RWLock { + inner: UnsafeCell<libc::pthread_rwlock_t>, + write_locked: UnsafeCell<bool>, // guarded by the `inner` RwLock + num_readers: AtomicUsize, +} + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { + inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), + write_locked: UnsafeCell::new(false), + num_readers: AtomicUsize::new(0), + } + } + #[inline] + pub unsafe fn read(&self) { + let r = libc::pthread_rwlock_rdlock(self.inner.get()); + + // According to POSIX, when a thread tries to acquire this read lock + // while it already holds the write lock + // (or vice versa, or tries to acquire the write lock twice), + // "the call shall either deadlock or return [EDEADLK]" + // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html, + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html). + // So, in principle, all we have to do here is check `r == 0` to be sure we properly + // got the lock. + // + // However, (at least) glibc before version 2.25 does not conform to this spec, + // and can return `r == 0` even when this thread already holds the write lock. + // We thus check for this situation ourselves and panic when detecting that a thread + // got the write lock more than once, or got a read and a write lock. + if r == libc::EAGAIN { + panic!("rwlock maximum reader count exceeded"); + } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) { + // Above, we make sure to only access `write_locked` when `r == 0` to avoid + // data races. + if r == 0 { + // `pthread_rwlock_rdlock` succeeded when it should not have. + self.raw_unlock(); + } + panic!("rwlock read lock would result in deadlock"); + } else { + // According to POSIX, for a properly initialized rwlock this can only + // return EAGAIN or EDEADLK or 0. We rely on that. + debug_assert_eq!(r, 0); + self.num_readers.fetch_add(1, Ordering::Relaxed); + } + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); + if r == 0 { + if *self.write_locked.get() { + // `pthread_rwlock_tryrdlock` succeeded when it should not have. + self.raw_unlock(); + false + } else { + self.num_readers.fetch_add(1, Ordering::Relaxed); + true + } + } else { + false + } + } + #[inline] + pub unsafe fn write(&self) { + let r = libc::pthread_rwlock_wrlock(self.inner.get()); + // See comments above for why we check for EDEADLK and write_locked. For the same reason, + // we also need to check that there are no readers (tracked in `num_readers`). + if r == libc::EDEADLK + || (r == 0 && *self.write_locked.get()) + || self.num_readers.load(Ordering::Relaxed) != 0 + { + // Above, we make sure to only access `write_locked` when `r == 0` to avoid + // data races. + if r == 0 { + // `pthread_rwlock_wrlock` succeeded when it should not have. + self.raw_unlock(); + } + panic!("rwlock write lock would result in deadlock"); + } else { + // According to POSIX, for a properly initialized rwlock this can only + // return EDEADLK or 0. We rely on that. + debug_assert_eq!(r, 0); + } + *self.write_locked.get() = true; + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + let r = libc::pthread_rwlock_trywrlock(self.inner.get()); + if r == 0 { + if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { + // `pthread_rwlock_trywrlock` succeeded when it should not have. + self.raw_unlock(); + false + } else { + *self.write_locked.get() = true; + true + } + } else { + false + } + } + #[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) { + debug_assert!(!*self.write_locked.get()); + self.num_readers.fetch_sub(1, Ordering::Relaxed); + self.raw_unlock(); + } + #[inline] + pub unsafe fn write_unlock(&self) { + 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) { + 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/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs new file mode 100644 index 00000000000..c74fc2b5903 --- /dev/null +++ b/library/std/src/sys/unix/stack_overflow.rs @@ -0,0 +1,234 @@ +#![cfg_attr(test, allow(dead_code))] + +use self::imp::{drop_handler, make_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 { + make_handler() + } + + fn null() -> Handler { + Handler { _data: crate::ptr::null_mut() } + } +} + +impl Drop for Handler { + fn drop(&mut self) { + unsafe { + drop_handler(self); + } + } +} + +#[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "solaris", + target_os = "illumos", + all(target_os = "netbsd", not(target_vendor = "rumprun")), + target_os = "openbsd" +))] +mod imp { + use super::Handler; + use crate::mem; + use crate::ptr; + + use libc::MAP_FAILED; + use libc::{mmap, munmap}; + use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; + use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE}; + use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV}; + + use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; + use crate::sys::unix::os::page_size; + use crate::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 "C" fn signal_handler( + signum: libc::c_int, + info: *mut libc::siginfo_t, + _data: *mut libc::c_void, + ) { + use crate::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 MAIN_ALTSTACK: AtomicPtr<libc::c_void> = AtomicPtr::new(ptr::null_mut()); + static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); + + pub unsafe fn init() { + let mut action: sigaction = mem::zeroed(); + for &signal in &[SIGSEGV, SIGBUS] { + sigaction(signal, ptr::null_mut(), &mut action); + // Configure our signal handler if one is not already set. + if action.sa_sigaction == SIG_DFL { + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + action.sa_sigaction = signal_handler as sighandler_t; + sigaction(signal, &action, ptr::null_mut()); + NEED_ALTSTACK.store(true, Ordering::Relaxed); + } + } + + let handler = make_handler(); + MAIN_ALTSTACK.store(handler._data, Ordering::Relaxed); + mem::forget(handler); + } + + pub unsafe fn cleanup() { + Handler { _data: MAIN_ALTSTACK.load(Ordering::Relaxed) }; + } + + unsafe fn get_stackp() -> *mut libc::c_void { + let stackp = mmap( + ptr::null_mut(), + SIGSTKSZ + page_size(), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0, + ); + if stackp == MAP_FAILED { + panic!("failed to allocate an alternative stack"); + } + let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE); + if guard_result != 0 { + panic!("failed to set up alternative stack guard page"); + } + stackp.add(page_size()) + } + + #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "illumos" + ))] + unsafe fn get_stack() -> libc::stack_t { + libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ } + } + + #[cfg(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 { + if !NEED_ALTSTACK.load(Ordering::Relaxed) { + return Handler::null(); + } + 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::null() + } + } + + 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()); + // We know from `get_stackp` that the alternate stack we installed is part of a mapping + // that started one page earlier, so walk back a page and unmap from there. + munmap(handler._data.sub(page_size()), SIGSTKSZ + page_size()); + } + } +} + +#[cfg(not(any( + target_os = "linux", + target_os = "macos", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "solaris", + target_os = "illumos", + all(target_os = "netbsd", not(target_vendor = "rumprun")), + target_os = "openbsd" +)))] +mod imp { + pub unsafe fn init() {} + + pub unsafe fn cleanup() {} + + pub unsafe fn make_handler() -> super::Handler { + super::Handler::null() + } + + pub unsafe fn drop_handler(_handler: &mut super::Handler) {} +} diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs new file mode 100644 index 00000000000..f8353214cbc --- /dev/null +++ b/library/std/src/sys/unix/stdio.rs @@ -0,0 +1,88 @@ +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +use crate::sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +impl Stdin { + pub fn new() -> io::Result<Stdin> { + Ok(Stdin(())) + } +} + +impl io::Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read(buf) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs) + } + + #[inline] + fn is_read_vectored(&self) -> bool { + true + } +} + +impl Stdout { + pub fn new() -> io::Result<Stdout> { + Ok(Stdout(())) + } +} + +impl io::Write for Stdout { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Stderr { + pub fn new() -> io::Result<Stderr> { + Ok(Stderr(())) + } +} + +impl io::Write for Stderr { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write(buf) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(libc::EBADF as i32) +} + +pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn panic_output() -> Option<impl io::Write> { + Stderr::new().ok() +} diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs new file mode 100644 index 00000000000..c1bda6b430e --- /dev/null +++ b/library/std/src/sys/unix/thread.rs @@ -0,0 +1,465 @@ +use crate::cmp; +use crate::ffi::CStr; +use crate::io; +use crate::mem; +use crate::ptr; +use crate::sys::{os, stack_overflow}; +use crate::time::Duration; + +#[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 { + 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 {} + +// 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 { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> { + let p = Box::into_raw(box p); + 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 stack_size = cmp::max(stack, min_stack_size(&attr)); + + 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 *mut _); + // Note: if the thread creation fails and this assert fails, then p will + // be leaked. However, an alternative design could cause double-free + // which is clearly worse. + assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); + + return if ret != 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + drop(Box::from_raw(p)); + Err(io::Error::from_raw_os_error(ret)) + } else { + Ok(Thread { id: native }) + }; + + extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + unsafe { + // Next, set up our stack overflow handler which may get triggered if we run + // out of stack. + let _handler = stack_overflow::Handler::new(); + // Finally, let's run some code. + Box::from_raw(main as *mut Box<dyn FnOnce()>)(); + } + 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 { + 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 = "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 crate::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_os = "solaris", target_os = "illumos"))] + pub fn set_name(name: &CStr) { + weak! { + fn pthread_setname_np( + libc::pthread_t, *const libc::c_char + ) -> libc::c_int + } + + if let Some(f) = pthread_setname_np.get() { + unsafe { + f(libc::pthread_self(), name.as_ptr()); + } + } + } + + #[cfg(any( + target_env = "newlib", + target_os = "haiku", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox" + ))] + pub fn set_name(_name: &CStr) { + // Newlib, 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 { + while secs > 0 || nsecs > 0 { + let mut ts = libc::timespec { + tv_sec: cmp::min(libc::time_t::MAX 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 = libc::pthread_join(self.id, ptr::null_mut()); + mem::forget(self); + assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); + } + } + + pub fn id(&self) -> libc::pthread_t { + self.id + } + + pub fn into_id(self) -> libc::pthread_t { + let id = self.id; + mem::forget(self); + id + } +} + +impl Drop for Thread { + fn drop(&mut self) { + 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(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 crate::ops::Range; + pub type Guard = Range<usize>; + pub unsafe fn current() -> Option<Guard> { + None + } + pub unsafe fn init() -> Option<Guard> { + None + } +} + +#[cfg(any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "freebsd", + target_os = "macos", + all(target_os = "netbsd", not(target_vendor = "rumprun")), + target_os = "openbsd", + target_os = "solaris" +))] +#[cfg_attr(test, allow(dead_code))] +pub mod guard { + use libc::{mmap, mprotect}; + use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; + + use crate::ops::Range; + use crate::sync::atomic::{AtomicUsize, Ordering}; + use crate::sys::os; + + // This is initialized in init() and only read from after + static PAGE_SIZE: AtomicUsize = AtomicUsize::new(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 = crate::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 th = libc::pthread_self(); + let stackaddr = + libc::pthread_get_stackaddr_np(th) as usize - libc::pthread_get_stacksize_np(th); + Some(stackaddr as *mut libc::c_void) + } + + #[cfg(target_os = "openbsd")] + unsafe fn get_stack_start() -> Option<*mut libc::c_void> { + let mut current_stack: libc::stack_t = crate::mem::zeroed(); + assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(), &mut current_stack), 0); + + let stackaddr = if libc::pthread_main_np() == 1 { + // main thread + current_stack.ss_sp as usize - current_stack.ss_size + PAGE_SIZE.load(Ordering::Relaxed) + } 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 = crate::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 = crate::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> { + let page_size = PAGE_SIZE.load(Ordering::Relaxed); + 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> { + let page_size = os::page_size(); + PAGE_SIZE.store(page_size, Ordering::Relaxed); + + 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) + } + } + + #[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "solaris"))] + pub unsafe fn current() -> Option<Guard> { + let stackaddr = get_stack_start()? as usize; + Some(stackaddr - PAGE_SIZE.load(Ordering::Relaxed)..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 = crate::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 = crate::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.load(Ordering::Relaxed)..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 bytes 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/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs new file mode 100644 index 00000000000..c3275eb6f0e --- /dev/null +++ b/library/std/src/sys/unix/thread_local_dtor.rs @@ -0,0 +1,94 @@ +#![cfg(target_thread_local)] +#![unstable(feature = "thread_local_internals", issue = "none")] + +//! Provides thread-local destructors without an associated "key", which +//! can be more efficient. + +// 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. +#[cfg(any( + target_os = "linux", + target_os = "fuchsia", + target_os = "redox", + target_os = "emscripten" +))] +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + use crate::mem; + use crate::sys_common::thread_local_dtor::register_dtor_fallback; + + extern "C" { + #[linkage = "extern_weak"] + static __dso_handle: *mut u8; + #[linkage = "extern_weak"] + static __cxa_thread_atexit_impl: *const libc::c_void; + } + if !__cxa_thread_atexit_impl.is_null() { + type F = unsafe extern "C" fn( + dtor: unsafe extern "C" 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); +} + +// This implementation is very similar to register_dtor_fallback in +// sys_common/thread_local.rs. The main difference is that we want to hook into +// macOS's analog of the above linux function, _tlv_atexit. OSX will run the +// registered dtors before any TLS slots get freed, and when the main thread +// exits. +// +// Unfortunately, calling _tlv_atexit while tls dtors are running is UB. The +// workaround below is to register, via _tlv_atexit, a custom DTOR list once per +// thread. thread_local dtors are pushed to the DTOR list without calling +// _tlv_atexit. +#[cfg(target_os = "macos")] +pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { + use crate::cell::Cell; + use crate::ptr; + + #[thread_local] + static REGISTERED: Cell<bool> = Cell::new(false); + if !REGISTERED.get() { + _tlv_atexit(run_dtors, ptr::null_mut()); + REGISTERED.set(true); + } + + type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>; + + #[thread_local] + static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut()); + if DTORS.get().is_null() { + let v: Box<List> = box Vec::new(); + DTORS.set(Box::into_raw(v)); + } + + extern "C" { + fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8); + } + + let list: &mut List = &mut *DTORS.get(); + list.push((t, dtor)); + + unsafe extern "C" fn run_dtors(_: *mut u8) { + let mut ptr = DTORS.replace(ptr::null_mut()); + while !ptr.is_null() { + let list = Box::from_raw(ptr); + for (ptr, dtor) in list.into_iter() { + dtor(ptr); + } + ptr = DTORS.replace(ptr::null_mut()); + } + } +} diff --git a/library/std/src/sys/unix/thread_local_key.rs b/library/std/src/sys/unix/thread_local_key.rs new file mode 100644 index 00000000000..2c5b94b1e61 --- /dev/null +++ b/library/std/src/sys/unix/thread_local_key.rs @@ -0,0 +1,34 @@ +#![allow(dead_code)] // not used on all platforms + +use crate::mem; + +pub type Key = libc::pthread_key_t; + +#[inline] +pub unsafe fn create(dtor: Option<unsafe extern "C" 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 set(key: Key, value: *mut u8) { + let r = libc::pthread_setspecific(key, value as *mut _); + debug_assert_eq!(r, 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + libc::pthread_getspecific(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(key: Key) { + let r = libc::pthread_key_delete(key); + debug_assert_eq!(r, 0); +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs new file mode 100644 index 00000000000..6707f790cab --- /dev/null +++ b/library/std/src/sys/unix/time.rs @@ -0,0 +1,352 @@ +use crate::cmp::Ordering; +use crate::time::Duration; + +use core::hash::{Hash, Hasher}; + +pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; +use crate::convert::TryInto; + +const NSEC_PER_SEC: u64 = 1_000_000_000; + +#[derive(Copy, Clone)] +struct Timespec { + t: libc::timespec, +} + +impl Timespec { + const fn zero() -> Timespec { + Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } + } + + fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { + if self >= other { + Ok(if self.t.tv_nsec >= other.t.tv_nsec { + Duration::new( + (self.t.tv_sec - other.t.tv_sec) as u64, + (self.t.tv_nsec - other.t.tv_nsec) as u32, + ) + } else { + Duration::new( + (self.t.tv_sec - 1 - other.t.tv_sec) as u64, + self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, + ) + }) + } else { + match other.sub_timespec(self) { + Ok(d) => Err(d), + Err(d) => Ok(d), + } + } + } + + fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { + 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))?; + + // Nano calculations can't overflow because nanos are <1B which fit + // in a u32. + let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; + if nsec >= NSEC_PER_SEC as u32 { + nsec -= NSEC_PER_SEC as u32; + secs = secs.checked_add(1)?; + } + Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) + } + + fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { + 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))?; + + // Similar to above, nanos can't overflow. + let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; + if nsec < 0 { + nsec += NSEC_PER_SEC as i32; + secs = secs.checked_sub(1)?; + } + Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) + } +} + +impl PartialEq for Timespec { + fn eq(&self, other: &Timespec) -> bool { + self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec + } +} + +impl Eq for Timespec {} + +impl PartialOrd for Timespec { + fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for Timespec { + fn cmp(&self, other: &Timespec) -> Ordering { + let me = (self.t.tv_sec, self.t.tv_nsec); + let other = (other.t.tv_sec, other.t.tv_nsec); + me.cmp(&other) + } +} + +impl Hash for Timespec { + fn hash<H: Hasher>(&self, state: &mut H) { + self.t.tv_sec.hash(state); + self.t.tv_nsec.hash(state); + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod inner { + use crate::fmt; + use crate::mem; + use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + use crate::sys::cvt; + use crate::sys_common::mul_div_u64; + use crate::time::Duration; + + use super::Timespec; + use super::NSEC_PER_SEC; + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] + pub struct Instant { + t: u64, + } + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct SystemTime { + t: Timespec, + } + + pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; + + #[repr(C)] + #[derive(Copy, Clone)] + struct mach_timebase_info { + numer: u32, + denom: u32, + } + type mach_timebase_info_t = *mut mach_timebase_info; + type kern_return_t = libc::c_int; + + impl Instant { + pub fn now() -> Instant { + extern "C" { + fn mach_absolute_time() -> u64; + } + Instant { t: unsafe { mach_absolute_time() } } + } + + pub const fn zero() -> Instant { + Instant { t: 0 } + } + + pub fn actually_monotonic() -> bool { + true + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + let diff = self.t.checked_sub(other.t)?; + let info = info(); + let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64); + Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32)) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_add(checked_dur2intervals(other)?)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_sub(checked_dur2intervals(other)?)? }) + } + } + + impl SystemTime { + pub fn now() -> SystemTime { + use crate::ptr; + + let mut s = libc::timeval { tv_sec: 0, tv_usec: 0 }; + cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap(); + return SystemTime::from(s); + } + + pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { + self.t.sub_timespec(&other.t) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + } + } + + impl From<libc::timeval> for SystemTime { + fn from(t: libc::timeval) -> SystemTime { + SystemTime::from(libc::timespec { + tv_sec: t.tv_sec, + tv_nsec: (t.tv_usec * 1000) as libc::c_long, + }) + } + } + + impl From<libc::timespec> for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { 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() + } + } + + fn checked_dur2intervals(dur: &Duration) -> Option<u64> { + let nanos = + dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?; + let info = info(); + Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) + } + + fn info() -> mach_timebase_info { + static mut INFO: mach_timebase_info = mach_timebase_info { numer: 0, denom: 0 }; + static STATE: AtomicUsize = AtomicUsize::new(0); + + unsafe { + // If a previous thread has filled in this global state, use that. + if STATE.load(SeqCst) == 2 { + return INFO; + } + + // ... otherwise learn for ourselves ... + let mut info = mem::zeroed(); + extern "C" { + fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t; + } + + mach_timebase_info(&mut info); + + // ... and attempt to be the one thread that stores it globally for + // all other threads + if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { + INFO = info; + STATE.store(2, SeqCst); + } + return info; + } + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +mod inner { + use crate::fmt; + use crate::sys::cvt; + use crate::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::zero() }; + + impl Instant { + pub fn now() -> Instant { + Instant { t: now(libc::CLOCK_MONOTONIC) } + } + + pub const fn zero() -> Instant { + Instant { t: Timespec::zero() } + } + + pub fn actually_monotonic() -> bool { + (cfg!(target_os = "linux") && cfg!(target_arch = "x86_64")) + || (cfg!(target_os = "linux") && cfg!(target_arch = "x86")) + || cfg!(target_os = "fuchsia") + } + + pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> { + self.t.sub_timespec(&other.t).ok() + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_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 checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_add_duration(other)? }) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) + } + } + + impl From<libc::timespec> for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { 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/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs new file mode 100644 index 00000000000..f4b33a00f7c --- /dev/null +++ b/library/std/src/sys/unix/weak.rs @@ -0,0 +1,101 @@ +//! 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). + +// There are a variety of `#[cfg]`s controlling which targets are involved in +// each instance of `weak!` and `syscall!`. Rather than trying to unify all of +// that, we'll just allow that some unix targets don't use this module at all. +#![allow(dead_code, unused_macros)] + +use crate::ffi::CStr; +use crate::marker; +use crate::mem; +use crate::sync::atomic::{AtomicUsize, Ordering}; + +macro_rules! weak { + (fn $name:ident($($t:ty),*) -> $ret:ty) => ( + static $name: crate::sys::weak::Weak<unsafe extern fn($($t),*) -> $ret> = + crate::sys::weak::Weak::new(concat!(stringify!($name), '\0')); + ) +} + +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); + } + match self.addr.load(Ordering::SeqCst) { + 0 => None, + addr => Some(mem::transmute_copy::<usize, F>(&addr)), + } + } + } +} + +unsafe fn fetch(name: &str) -> usize { + let name = match CStr::from_bytes_with_nul(name.as_bytes()) { + Ok(cstr) => cstr, + Err(..) => return 0, + }; + libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize +} + +#[cfg(not(target_os = "linux"))] +macro_rules! syscall { + (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( + unsafe fn $name($($arg_name: $t),*) -> $ret { + use super::os; + + weak! { fn $name($($t),*) -> $ret } + + if let Some(fun) = $name.get() { + fun($($arg_name),*) + } else { + os::set_errno(libc::ENOSYS); + -1 + } + } + ) +} + +#[cfg(target_os = "linux")] +macro_rules! syscall { + (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( + unsafe fn $name($($arg_name:$t),*) -> $ret { + // This looks like a hack, but concat_idents only accepts idents + // (not paths). + use libc::*; + + syscall( + concat_idents!(SYS_, $name), + $($arg_name as c_long),* + ) as $ret + } + ) +} |
