diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2016-12-27 17:02:52 -0800 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2016-12-27 17:02:52 -0800 |
| commit | e766c465d2e4c4e3c106bfa8343cbe6f9192d445 (patch) | |
| tree | 821a7cf1e0b04ac9c0cddede6eb760bbf2d0ce62 /src/libstd/sys | |
| parent | 96c52d4fd86aed6320732a511c04bcbfff7d117f (diff) | |
| parent | 314c28b729ae359b99586cc62c486c28e0d44424 (diff) | |
| download | rust-e766c465d2e4c4e3c106bfa8343cbe6f9192d445.tar.gz rust-e766c465d2e4c4e3c106bfa8343cbe6f9192d445.zip | |
Merge branch 'master' into escape-reason-docs
Diffstat (limited to 'src/libstd/sys')
58 files changed, 6161 insertions, 49 deletions
diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs index 0c45ab8a887..14da376efa9 100644 --- a/src/libstd/sys/mod.rs +++ b/src/libstd/sys/mod.rs @@ -30,8 +30,14 @@ //! inter-dependencies within `std` that will be a challenging goal to //! achieve. +#![allow(missing_debug_implementations)] + pub use self::imp::*; +#[cfg(target_os = "redox")] +#[path = "redox/mod.rs"] +mod imp; + #[cfg(unix)] #[path = "unix/mod.rs"] mod imp; diff --git a/src/libstd/sys/redox/args.rs b/src/libstd/sys/redox/args.rs new file mode 100644 index 00000000000..f6fea2f1076 --- /dev/null +++ b/src/libstd/sys/redox/args.rs @@ -0,0 +1,109 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Global initialization and retreival of command line arguments. +//! +//! On some platforms these are stored during runtime startup, +//! and on some they are retrieved from the system on demand. + +#![allow(dead_code)] // runtime init functions not used during testing + +use ffi::OsString; +use marker::PhantomData; +use vec; + +/// 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 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() } +} + +mod imp { + use os::unix::prelude::*; + use mem; + use ffi::OsString; + use marker::PhantomData; + use slice; + use str; + use super::Args; + + use sys_common::mutex::Mutex; + + static mut GLOBAL_ARGS_PTR: usize = 0; + static LOCK: Mutex = Mutex::new(); + + pub unsafe fn init(argc: isize, argv: *const *const u8) { + let mut args: Vec<Vec<u8>> = Vec::new(); + for i in 0..argc { + let len = *(argv.offset(i * 2)) as usize; + let ptr = *(argv.offset(i * 2 + 1)); + args.push(slice::from_raw_parts(ptr, len).to_vec()); + } + + LOCK.lock(); + let ptr = get_global_ptr(); + assert!((*ptr).is_none()); + (*ptr) = Some(box args); + LOCK.unlock(); + } + + pub unsafe fn cleanup() { + LOCK.lock(); + *get_global_ptr() = None; + LOCK.unlock(); + } + + pub fn args() -> Args { + let bytes = clone().unwrap_or(Vec::new()); + let v: Vec<OsString> = bytes.into_iter().map(|v| { + OsStringExt::from_vec(v) + }).collect(); + Args { iter: v.into_iter(), _dont_send_or_sync_me: PhantomData } + } + + fn clone() -> Option<Vec<Vec<u8>>> { + unsafe { + LOCK.lock(); + let ptr = get_global_ptr(); + let ret = (*ptr).as_ref().map(|s| (**s).clone()); + LOCK.unlock(); + return ret + } + } + + fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> { + unsafe { mem::transmute(&GLOBAL_ARGS_PTR) } + } + +} diff --git a/src/libstd/sys/redox/backtrace.rs b/src/libstd/sys/redox/backtrace.rs new file mode 100644 index 00000000000..6f53841502a --- /dev/null +++ b/src/libstd/sys/redox/backtrace.rs @@ -0,0 +1,18 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use libc; +use io; +use sys_common::backtrace::output; + +#[inline(never)] +pub fn write(w: &mut io::Write) -> io::Result<()> { + output(w, 0, 0 as *mut libc::c_void, None) +} diff --git a/src/libstd/sys/redox/condvar.rs b/src/libstd/sys/redox/condvar.rs new file mode 100644 index 00000000000..0ca0987b245 --- /dev/null +++ b/src/libstd/sys/redox/condvar.rs @@ -0,0 +1,106 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use intrinsics::{atomic_cxchg, atomic_xadd, atomic_xchg}; +use ptr; +use time::Duration; + +use sys::mutex::{mutex_lock, mutex_unlock, Mutex}; +use sys::syscall::{futex, FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE}; + +pub struct Condvar { + lock: UnsafeCell<*mut i32>, + seq: UnsafeCell<i32> +} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { + lock: UnsafeCell::new(ptr::null_mut()), + seq: UnsafeCell::new(0) + } + } + + #[inline] + pub unsafe fn init(&self) { + *self.lock.get() = ptr::null_mut(); + *self.seq.get() = 0; + } + + #[inline] + pub fn notify_one(&self) { + unsafe { + let seq = self.seq.get(); + + atomic_xadd(seq, 1); + + let _ = futex(seq, FUTEX_WAKE, 1, 0, ptr::null_mut()); + } + } + + #[inline] + pub fn notify_all(&self) { + unsafe { + let lock = self.lock.get(); + let seq = self.seq.get(); + + if *lock == ptr::null_mut() { + return; + } + + atomic_xadd(seq, 1); + + let _ = futex(seq, FUTEX_REQUEUE, 1, ::usize::MAX, *lock); + } + } + + #[inline] + pub fn wait(&self, mutex: &Mutex) { + unsafe { + let lock = self.lock.get(); + let seq = self.seq.get(); + + if *lock != mutex.lock.get() { + if *lock != ptr::null_mut() { + panic!("Condvar used with more than one Mutex"); + } + + atomic_cxchg(lock as *mut usize, 0, mutex.lock.get() as usize); + } + + mutex_unlock(*lock); + + let _ = futex(seq, FUTEX_WAIT, *seq, 0, ptr::null_mut()); + + while atomic_xchg(*lock, 2) != 0 { + let _ = futex(*lock, FUTEX_WAIT, 2, 0, ptr::null_mut()); + } + + mutex_lock(*lock); + } + } + + #[inline] + pub fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + ::sys_common::util::dumb_print(format_args!("condvar wait_timeout\n")); + unimplemented!(); + } + + #[inline] + pub unsafe fn destroy(&self) { + *self.lock.get() = ptr::null_mut(); + *self.seq.get() = 0; + } +} + +unsafe impl Send for Condvar {} + +unsafe impl Sync for Condvar {} diff --git a/src/libstd/sys/redox/env.rs b/src/libstd/sys/redox/env.rs new file mode 100644 index 00000000000..669b7520df8 --- /dev/null +++ b/src/libstd/sys/redox/env.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod os { + pub const FAMILY: &'static str = "redox"; + pub const OS: &'static str = "redox"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ""; + pub const EXE_EXTENSION: &'static str = ""; +} diff --git a/src/libstd/sys/redox/ext/ffi.rs b/src/libstd/sys/redox/ext/ffi.rs new file mode 100644 index 00000000000..d59b4fc0b70 --- /dev/null +++ b/src/libstd/sys/redox/ext/ffi.rs @@ -0,0 +1,61 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extension to the primitives in the `std::ffi` module + +#![stable(feature = "rust1", since = "1.0.0")] + +use ffi::{OsStr, OsString}; +use mem; +use sys::os_str::Buf; +use sys_common::{FromInner, IntoInner, AsInner}; + +/// Unix-specific extensions to `OsString`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an `OsString` from a byte vector. + #[stable(feature = "rust1", since = "1.0.0")] + fn from_vec(vec: Vec<u8>) -> Self; + + /// Yields the underlying byte vector of this `OsString`. + #[stable(feature = "rust1", since = "1.0.0")] + fn into_vec(self) -> Vec<u8>; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_vec(vec: Vec<u8>) -> OsString { + FromInner::from_inner(Buf { inner: vec }) + } + fn into_vec(self) -> Vec<u8> { + self.into_inner().inner + } +} + +/// Unix-specific extensions to `OsStr`. +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + #[stable(feature = "rust1", since = "1.0.0")] + fn from_bytes(slice: &[u8]) -> &Self; + + /// Gets the underlying byte view of the `OsStr` slice. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_bytes(&self) -> &[u8]; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + fn from_bytes(slice: &[u8]) -> &OsStr { + unsafe { mem::transmute(slice) } + } + fn as_bytes(&self) -> &[u8] { + &self.as_inner().inner + } +} diff --git a/src/libstd/sys/redox/ext/fs.rs b/src/libstd/sys/redox/ext/fs.rs new file mode 100644 index 00000000000..b4e220971fd --- /dev/null +++ b/src/libstd/sys/redox/ext/fs.rs @@ -0,0 +1,298 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extensions to primitives in the `std::fs` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs::{self, Permissions, OpenOptions}; +use io; +use path::Path; +use sys; +use sys_common::{FromInner, AsInner, AsInnerMut}; + +/// Unix-specific extensions to `Permissions` +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait PermissionsExt { + /// Returns the underlying raw `mode_t` bits that are the standard Unix + /// permissions for this file. + /// + /// # Examples + /// + /// ```rust,ignore + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// let f = try!(File::create("foo.txt")); + /// let metadata = try!(f.metadata()); + /// let permissions = metadata.permissions(); + /// + /// println!("permissions: {}", permissions.mode()); + /// ``` + #[stable(feature = "fs_ext", since = "1.1.0")] + fn mode(&self) -> u32; + + /// Sets the underlying raw bits for this set of permissions. + /// + /// # Examples + /// + /// ```rust,ignore + /// use std::fs::File; + /// use std::os::unix::fs::PermissionsExt; + /// + /// let f = try!(File::create("foo.txt")); + /// let metadata = try!(f.metadata()); + /// let mut permissions = metadata.permissions(); + /// + /// permissions.set_mode(0o644); // Read/write for owner and read for others. + /// assert_eq!(permissions.mode(), 0o644); + /// ``` + #[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 + /// + /// ```rust,ignore + /// 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 `OpenOptions` +#[stable(feature = "fs_ext", since = "1.1.0")] +pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + /// + /// If a new file is created as part of a `File::open_opts` call then this + /// specified `mode` will be used as the permission bits for the new file. + /// If no `mode` is set, the default of `0o666` will be used. + /// The operating system masks out bits with the systems `umask`, to produce + /// the final permissions. + /// + /// # Examples + /// + /// ```rust,ignore + /// extern crate libc; + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// 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` agument 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 + /// + /// ```rust,ignore + /// extern crate libc; + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// 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 + } +} + +// Hm, why are there casts here to the returned type, shouldn't the types always +// be the same? Right you are! Turns out, however, on android at least the types +// in the raw `stat` structure are not the same as the types being returned. Who +// knew! +// +// As a result to make sure this compiles for all platforms we do the manual +// casts and rely on manual lowering to `stat` if the raw type is desired. +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mode(&self) -> u32; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn uid(&self) -> u32; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn gid(&self) -> u32; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn size(&self) -> u64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn atime(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mtime(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ctime(&self) -> i64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn ctime_nsec(&self) -> i64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for fs::Metadata { + fn mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } +} + +/// Add special unix types (block/char device, fifo and socket) +#[stable(feature = "file_type_ext", since = "1.5.0")] +pub trait FileTypeExt { + /// Returns whether this file type is a block device. + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_block_device(&self) -> bool; + /// Returns whether this file type is a char device. + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_char_device(&self) -> bool; + /// Returns whether this file type is a fifo. + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_fifo(&self) -> bool; + /// Returns whether this file type is a socket. + #[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 { false /*FIXME: Implement block device mode*/ } + fn is_char_device(&self) -> bool { false /*FIXME: Implement char device mode*/ } + fn is_fifo(&self) -> bool { false /*FIXME: Implement fifo mode*/ } + fn is_socket(&self) -> bool { false /*FIXME: Implement socket mode*/ } +} + +/// 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 +/// +/// ``` +/// use std::os::unix::fs; +/// +/// # fn foo() -> std::io::Result<()> { +/// try!(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()) +} + +#[stable(feature = "dir_builder", since = "1.6.0")] +/// An extension trait for `fs::DirBuilder` for unix-specific options. +pub trait DirBuilderExt { + /// Sets the mode to create new directories with. This option defaults to + /// 0o777. + /// + /// # Examples + /// + /// ```ignore + /// 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/src/libstd/sys/redox/ext/io.rs b/src/libstd/sys/redox/ext/io.rs new file mode 100644 index 00000000000..8e7cc593dbd --- /dev/null +++ b/src/libstd/sys/redox/ext/io.rs @@ -0,0 +1,151 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extensions to general I/O primitives + +#![stable(feature = "rust1", since = "1.0.0")] + +use fs; +use net; +use sys; +use sys_common::{self, AsInner, FromInner, IntoInner}; + +/// Raw file descriptors. +#[stable(feature = "rust1", since = "1.0.0")] +pub type RawFd = usize; + +/// 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 instances 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 = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpStream { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_inner().fd().raw() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::TcpListener { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_inner().fd().raw() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl AsRawFd for net::UdpSocket { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().as_inner().fd().raw() + } +} + +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::TcpStream { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { + let file = sys::fs::File::from_inner(fd); + net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(file)) + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::TcpListener { + unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { + let file = sys::fs::File::from_inner(fd); + net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(file)) + } +} +#[stable(feature = "from_raw_os", since = "1.1.0")] +impl FromRawFd for net::UdpSocket { + unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { + let file = sys::fs::File::from_inner(fd); + net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(file)) + } +} + +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::TcpStream { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_inner().into_fd().into_raw() + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::TcpListener { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_inner().into_fd().into_raw() + } +} +#[stable(feature = "into_raw_os", since = "1.4.0")] +impl IntoRawFd for net::UdpSocket { + fn into_raw_fd(self) -> RawFd { + self.into_inner().into_inner().into_fd().into_raw() + } +} diff --git a/src/libstd/sys/redox/ext/mod.rs b/src/libstd/sys/redox/ext/mod.rs new file mode 100644 index 00000000000..513ef272e97 --- /dev/null +++ b/src/libstd/sys/redox/ext/mod.rs @@ -0,0 +1,50 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Experimental extensions to `std` for Unix platforms. +//! +//! For now, this module is limited to extracting file descriptors, +//! but its functionality will grow over time. +//! +//! # Example +//! +//! ```no_run +//! use std::fs::File; +//! use std::os::unix::prelude::*; +//! +//! fn main() { +//! let f = File::create("foo.txt").unwrap(); +//! let fd = f.as_raw_fd(); +//! +//! // use fd with native unix bindings +//! } +//! ``` + +#![stable(feature = "rust1", since = "1.0.0")] + +pub mod ffi; +pub mod fs; +pub mod io; +pub mod process; + +/// 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::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::fs::{FileTypeExt, PermissionsExt, OpenOptionsExt, MetadataExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::process::{CommandExt, ExitStatusExt}; +} diff --git a/src/libstd/sys/redox/ext/process.rs b/src/libstd/sys/redox/ext/process.rs new file mode 100644 index 00000000000..c59524974bf --- /dev/null +++ b/src/libstd/sys/redox/ext/process.rs @@ -0,0 +1,183 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Unix-specific extensions to primitives in the `std::process` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use io; +use os::unix::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd}; +use process; +use sys; +use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner}; + +/// Unix-specific extensions to the `std::process::Command` builder +#[stable(feature = "rust1", since = "1.0.0")] +pub trait CommandExt { + /// Sets the child process's user id. This translates to a + /// `setuid` call in the child process. Failure in the `setuid` + /// call will cause the spawn to fail. + #[stable(feature = "rust1", since = "1.0.0")] + fn uid(&mut self, id: u32) -> &mut process::Command; + + /// Similar to `uid`, but sets the group id of the child process. This has + /// the same semantics as the `uid` field. + #[stable(feature = "rust1", since = "1.0.0")] + fn gid(&mut self, id: u32) -> &mut process::Command; + + /// Schedules a closure to be run just before the `exec` function is + /// invoked. + /// + /// The closure is allowed to return an I/O error whose OS error code will + /// be communicated back to the parent and returned as an error from when + /// the spawn was requested. + /// + /// Multiple closures can be registered and they will be called in order of + /// their registration. If a closure returns `Err` then no further closures + /// will be called and the spawn operation will immediately return with a + /// failure. + /// + /// # Notes + /// + /// This closure will be run in the context of the child process after a + /// `fork`. This primarily means that any modificatons made to memory on + /// behalf of this closure will **not** be visible to the parent process. + /// This is often a very constrained environment where normal operations + /// like `malloc` or acquiring a mutex are not guaranteed to work (due to + /// other threads perhaps still running when the `fork` was run). + /// + /// When this closure is run, aspects such as the stdio file descriptors and + /// working directory have successfully been changed, so output to these + /// locations may not appear where intended. + #[stable(feature = "process_exec", since = "1.15.0")] + fn before_exec<F>(&mut self, f: F) -> &mut process::Command + where F: FnMut() -> io::Result<()> + Send + Sync + 'static; + + /// Performs all the required setup by this `Command`, followed by calling + /// the `execvp` syscall. + /// + /// On success this function will not return, and otherwise it will return + /// an error indicating why the exec (or another part of the setup of the + /// `Command`) failed. + /// + /// 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. + /// + /// # Notes + /// + /// The process may be in a "broken state" if this function returns in + /// error. For example the working directory, environment variables, signal + /// handling settings, various user/group information, or aspects of stdio + /// file descriptors may have changed. If a "transactional spawn" is + /// required to gracefully handle errors it is recommended to use the + /// cross-platform `spawn` instead. + #[stable(feature = "process_exec2", since = "1.9.0")] + fn exec(&mut self) -> io::Error; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl CommandExt for process::Command { + fn uid(&mut self, id: u32) -> &mut process::Command { + self.as_inner_mut().uid(id); + self + } + + fn gid(&mut self, id: u32) -> &mut process::Command { + self.as_inner_mut().gid(id); + self + } + + fn before_exec<F>(&mut self, f: F) -> &mut process::Command + where F: FnMut() -> io::Result<()> + Send + Sync + 'static + { + self.as_inner_mut().before_exec(Box::new(f)); + self + } + + fn exec(&mut self) -> io::Error { + self.as_inner_mut().exec(sys::process::Stdio::Inherit) + } +} + +/// Unix-specific extensions to `std::process::ExitStatus` +#[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() + } +} diff --git a/src/libstd/sys/redox/fast_thread_local.rs b/src/libstd/sys/redox/fast_thread_local.rs new file mode 100644 index 00000000000..6eeae2d90ea --- /dev/null +++ b/src/libstd/sys/redox/fast_thread_local.rs @@ -0,0 +1,116 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(target_thread_local)] +#![unstable(feature = "thread_local_internals", issue = "0")] + +use cell::{Cell, UnsafeCell}; +use intrinsics; +use ptr; + +pub struct Key<T> { + inner: UnsafeCell<Option<T>>, + + // Metadata to keep track of the state of the destructor. Remember that + // these variables are thread-local, not global. + dtor_registered: Cell<bool>, + dtor_running: Cell<bool>, +} + +unsafe impl<T> ::marker::Sync for Key<T> { } + +impl<T> Key<T> { + pub const fn new() -> Key<T> { + Key { + inner: UnsafeCell::new(None), + dtor_registered: Cell::new(false), + dtor_running: Cell::new(false) + } + } + + pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { + unsafe { + if intrinsics::needs_drop::<T>() && self.dtor_running.get() { + return None + } + self.register_dtor(); + } + Some(&self.inner) + } + + unsafe fn register_dtor(&self) { + if !intrinsics::needs_drop::<T>() || self.dtor_registered.get() { + return + } + + register_dtor(self as *const _ as *mut u8, + destroy_value::<T>); + self.dtor_registered.set(true); + } +} + +unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { + // The fallback implementation uses a vanilla OS-based TLS key to track + // the list of destructors that need to be run for this thread. The key + // then has its own destructor which runs all the other destructors. + // + // The destructor for DTORS is a little special in that it has a `while` + // loop to continuously drain the list of registered destructors. It + // *should* be the case that this loop always terminates because we + // provide the guarantee that a TLS key cannot be set after it is + // flagged for destruction. + use sys_common::thread_local as os; + + static DTORS: os::StaticKey = os::StaticKey::new(Some(run_dtors)); + type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; + if DTORS.get().is_null() { + let v: Box<List> = box Vec::new(); + DTORS.set(Box::into_raw(v) as *mut u8); + } + let list: &mut List = &mut *(DTORS.get() as *mut List); + list.push((t, dtor)); + + unsafe extern fn run_dtors(mut ptr: *mut u8) { + while !ptr.is_null() { + let list: Box<List> = Box::from_raw(ptr as *mut List); + for &(ptr, dtor) in list.iter() { + dtor(ptr); + } + ptr = DTORS.get(); + DTORS.set(ptr::null_mut()); + } + } +} + +pub unsafe extern fn destroy_value<T>(ptr: *mut u8) { + let ptr = ptr as *mut Key<T>; + // Right before we run the user destructor be sure to flag the + // destructor as running for this thread so calls to `get` will return + // `None`. + (*ptr).dtor_running.set(true); + + // The OSX implementation of TLS apparently had an odd aspect to it + // where the pointer we have may be overwritten while this destructor + // is running. Specifically if a TLS destructor re-accesses TLS it may + // trigger a re-initialization of all TLS variables, paving over at + // least some destroyed ones with initial values. + // + // This means that if we drop a TLS value in place on OSX that we could + // revert the value to its original state halfway through the + // destructor, which would be bad! + // + // Hence, we use `ptr::read` on OSX (to move to a "safe" location) + // instead of drop_in_place. + if cfg!(target_os = "macos") { + ptr::read((*ptr).inner.get()); + } else { + ptr::drop_in_place((*ptr).inner.get()); + } +} diff --git a/src/libstd/sys/redox/fd.rs b/src/libstd/sys/redox/fd.rs new file mode 100644 index 00000000000..b6de68a9dc1 --- /dev/null +++ b/src/libstd/sys/redox/fd.rs @@ -0,0 +1,100 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(reason = "not public", issue = "0", feature = "fd")] + +use io::{self, Read}; +use mem; +use sys::{cvt, syscall}; +use sys_common::AsInner; +use sys_common::io::read_to_end_uninitialized; + +pub struct FileDesc { + fd: usize, +} + +impl FileDesc { + pub fn new(fd: usize) -> FileDesc { + FileDesc { fd: fd } + } + + pub fn raw(&self) -> usize { self.fd } + + /// Extracts the actual filedescriptor without closing it. + pub fn into_raw(self) -> usize { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + cvt(syscall::read(self.fd, buf)) + } + + 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 write(&self, buf: &[u8]) -> io::Result<usize> { + cvt(syscall::write(self.fd, buf)) + } + + pub fn duplicate(&self) -> io::Result<FileDesc> { + let new_fd = cvt(syscall::dup(self.fd, &[]))?; + Ok(FileDesc::new(new_fd)) + } + + pub fn nonblocking(&self) -> io::Result<bool> { + let flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?; + Ok(flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK) + } + + pub fn set_cloexec(&self) -> io::Result<()> { + let mut flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?; + flags |= syscall::O_CLOEXEC; + cvt(syscall::fcntl(self.fd, syscall::F_SETFL, flags)).and(Ok(())) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut flags = cvt(syscall::fcntl(self.fd, syscall::F_GETFL, 0))?; + if nonblocking { + flags |= syscall::O_NONBLOCK; + } else { + flags &= !syscall::O_NONBLOCK; + } + cvt(syscall::fcntl(self.fd, syscall::F_SETFL, flags)).and(Ok(())) + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (**self).read(buf) + } + + fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { + unsafe { read_to_end_uninitialized(self, buf) } + } +} + +impl AsInner<usize> for FileDesc { + fn as_inner(&self) -> &usize { &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 _ = syscall::close(self.fd); + } +} diff --git a/src/libstd/sys/redox/fs.rs b/src/libstd/sys/redox/fs.rs new file mode 100644 index 00000000000..a8391d2b898 --- /dev/null +++ b/src/libstd/sys/redox/fs.rs @@ -0,0 +1,470 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use os::unix::prelude::*; + +use ffi::{OsString, OsStr}; +use fmt; +use io::{self, Error, ErrorKind, SeekFrom}; +use path::{Path, PathBuf}; +use sync::Arc; +use sys::fd::FileDesc; +use sys::time::SystemTime; +use sys::{cvt, syscall}; +use sys_common::{AsInner, FromInner}; + +pub struct File(FileDesc); + +#[derive(Clone)] +pub struct FileAttr { + stat: syscall::Stat, +} + +pub struct ReadDir { + data: Vec<u8>, + i: usize, + root: Arc<PathBuf>, +} + +struct Dir(FileDesc); + +unsafe impl Send for Dir {} +unsafe impl Sync for Dir {} + +pub struct DirEntry { + root: Arc<PathBuf>, + 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: u16, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { mode: u16 } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct FileType { mode: u16 } + +#[derive(Debug)] +pub struct DirBuilder { mode: u16 } + +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 u16) & 0o777 } + } + + pub fn file_type(&self) -> FileType { + FileType { mode: self.stat.st_mode as u16 } + } +} + +impl FileAttr { + pub fn modified(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(syscall::TimeSpec { + tv_sec: self.stat.st_mtime as i64, + tv_nsec: self.stat.st_mtime_nsec as i32, + })) + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(syscall::TimeSpec { + tv_sec: self.stat.st_atime as i64, + tv_nsec: self.stat.st_atime_nsec as i32, + })) + } + + pub fn created(&self) -> io::Result<SystemTime> { + Ok(SystemTime::from(syscall::TimeSpec { + tv_sec: self.stat.st_ctime as i64, + tv_nsec: self.stat.st_ctime_nsec as i32, + })) + } +} + +impl AsInner<syscall::Stat> for FileAttr { + fn as_inner(&self) -> &syscall::Stat { &self.stat } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 } + pub fn set_readonly(&mut self, readonly: bool) { + if readonly { + self.mode &= !0o222; + } else { + self.mode |= 0o222; + } + } + pub fn mode(&self) -> u32 { self.mode as u32 } +} + +impl FileType { + pub fn is_dir(&self) -> bool { self.is(syscall::MODE_DIR) } + pub fn is_file(&self) -> bool { self.is(syscall::MODE_FILE) } + pub fn is_symlink(&self) -> bool { false /*FIXME: Implement symlink mode*/ } + + pub fn is(&self, mode: u16) -> bool { + self.mode & (syscall::MODE_DIR | syscall::MODE_FILE) == mode + } +} + +impl FromInner<u32> for FilePermissions { + fn from_inner(mode: u32) -> FilePermissions { + FilePermissions { mode: mode as u16 } + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("/home")' + fmt::Debug::fmt(&*self.root, f) + } +} + +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; + + fn next(&mut self) -> Option<io::Result<DirEntry>> { + loop { + let start = self.i; + let mut i = self.i; + while i < self.data.len() { + self.i += 1; + if self.data[i] == b'\n' { + break; + } + i += 1; + } + if start < self.i { + let ret = DirEntry { + name: self.data[start .. i].to_owned().into_boxed_slice(), + root: self.root.clone() + }; + if ret.name_bytes() != b"." && ret.name_bytes() != b".." { + return Some(Ok(ret)) + } + } else { + return None; + } + } + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + self.root.join(OsStr::from_bytes(self.name_bytes())) + } + + pub fn file_name(&self) -> OsString { + OsStr::from_bytes(self.name_bytes()).to_os_string() + } + + pub fn metadata(&self) -> io::Result<FileAttr> { + lstat(&self.path()) + } + + pub fn file_type(&self) -> io::Result<FileType> { + lstat(&self.path()).map(|m| m.file_type()) + } + + 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 u16; } + + fn get_access_mode(&self) -> io::Result<usize> { + match (self.read, self.write, self.append) { + (true, false, false) => Ok(syscall::O_RDONLY), + (false, true, false) => Ok(syscall::O_WRONLY), + (true, true, false) => Ok(syscall::O_RDWR), + (false, _, true) => Ok(syscall::O_WRONLY | syscall::O_APPEND), + (true, _, true) => Ok(syscall::O_RDWR | syscall::O_APPEND), + (false, false, false) => Err(Error::from_raw_os_error(syscall::EINVAL)), + } + } + + fn get_creation_mode(&self) -> io::Result<usize> { + match (self.write, self.append) { + (true, false) => {} + (false, false) => + if self.truncate || self.create || self.create_new { + return Err(Error::from_raw_os_error(syscall::EINVAL)); + }, + (_, true) => + if self.truncate && !self.create_new { + return Err(Error::from_raw_os_error(syscall::EINVAL)); + }, + } + + Ok(match (self.create, self.truncate, self.create_new) { + (false, false, false) => 0, + (true, false, false) => syscall::O_CREAT, + (false, true, false) => syscall::O_TRUNC, + (true, true, false) => syscall::O_CREAT | syscall::O_TRUNC, + (_, _, true) => syscall::O_CREAT | syscall::O_EXCL, + }) + } +} + +impl File { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { + let flags = syscall::O_CLOEXEC | + opts.get_access_mode()? as usize | + opts.get_creation_mode()? as usize | + (opts.custom_flags as usize & !syscall::O_ACCMODE); + let fd = cvt(syscall::open(path.to_str().unwrap(), flags | opts.mode as usize))?; + Ok(File(FileDesc::new(fd))) + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + let mut stat = syscall::Stat::default(); + cvt(syscall::fstat(self.0.raw(), &mut stat))?; + Ok(FileAttr { stat: stat }) + } + + pub fn fsync(&self) -> io::Result<()> { + cvt(syscall::fsync(self.0.raw()))?; + Ok(()) + } + + pub fn datasync(&self) -> io::Result<()> { + self.fsync() + } + + pub fn truncate(&self, size: u64) -> io::Result<()> { + cvt(syscall::ftruncate(self.0.raw(), size as usize))?; + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + 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) => (syscall::SEEK_SET, off as i64), + SeekFrom::End(off) => (syscall::SEEK_END, off), + SeekFrom::Current(off) => (syscall::SEEK_CUR, off), + }; + let n = cvt(syscall::lseek(self.0.raw(), pos as isize, whence))?; + Ok(n as u64) + } + + pub fn duplicate(&self) -> io::Result<File> { + self.0.duplicate().map(File) + } + + pub fn dup(&self, buf: &[u8]) -> io::Result<File> { + let fd = cvt(syscall::dup(*self.fd().as_inner() as usize, buf))?; + Ok(File(FileDesc::new(fd))) + } + + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + set_perm(&self.path()?, perm) + } + + pub fn path(&self) -> io::Result<PathBuf> { + let mut buf: [u8; 4096] = [0; 4096]; + let count = cvt(syscall::fpath(*self.fd().as_inner() as usize, &mut buf))?; + Ok(PathBuf::from(unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) })) + } + + pub fn fd(&self) -> &FileDesc { &self.0 } + + pub fn into_fd(self) -> FileDesc { self.0 } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { mode: 0o777 } + } + + pub fn mkdir(&self, p: &Path) -> io::Result<()> { + let flags = syscall::O_CREAT | syscall::O_CLOEXEC | syscall::O_DIRECTORY | syscall::O_EXCL; + let fd = cvt(syscall::open(p.to_str().unwrap(), flags | (self.mode as usize & 0o777)))?; + let _ = syscall::close(fd); + Ok(()) + } + + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as u16; + } +} + +impl FromInner<usize> for File { + fn from_inner(fd: usize) -> File { + File(FileDesc::new(fd)) + } +} + +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut b = f.debug_struct("File"); + b.field("fd", &self.0.raw()); + if let Ok(path) = self.path() { + 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 = Arc::new(p.to_path_buf()); + + let flags = syscall::O_CLOEXEC | syscall::O_RDONLY | syscall::O_DIRECTORY; + let fd = cvt(syscall::open(p.to_str().unwrap(), flags))?; + let file = FileDesc::new(fd); + let mut data = Vec::new(); + file.read_to_end(&mut data)?; + + Ok(ReadDir { data: data, i: 0, root: root }) +} + +pub fn unlink(p: &Path) -> io::Result<()> { + cvt(syscall::unlink(p.to_str().unwrap()))?; + Ok(()) +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + ::sys_common::util::dumb_print(format_args!("Rename\n")); + unimplemented!(); +} + +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + cvt(syscall::chmod(p.to_str().unwrap(), perm.mode as usize))?; + Ok(()) +} + +pub fn rmdir(p: &Path) -> io::Result<()> { + cvt(syscall::rmdir(p.to_str().unwrap()))?; + Ok(()) +} + +pub fn remove_dir_all(path: &Path) -> io::Result<()> { + let filetype = lstat(path)?.file_type(); + if filetype.is_symlink() { + unlink(path) + } else { + remove_dir_all_recursive(path) + } +} + +fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { + for child in readdir(path)? { + let child = child?; + if child.file_type()?.is_dir() { + remove_dir_all_recursive(&child.path())?; + } else { + unlink(&child.path())?; + } + } + rmdir(path) +} + +pub fn readlink(p: &Path) -> io::Result<PathBuf> { + canonicalize(p) +} + +pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> { + ::sys_common::util::dumb_print(format_args!("Symlink\n")); + unimplemented!(); +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + ::sys_common::util::dumb_print(format_args!("Link\n")); + unimplemented!(); +} + +pub fn stat(p: &Path) -> io::Result<FileAttr> { + let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_CLOEXEC | syscall::O_STAT))?; + let file = File(FileDesc::new(fd)); + file.file_attr() +} + +pub fn lstat(p: &Path) -> io::Result<FileAttr> { + stat(p) +} + +pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { + let fd = cvt(syscall::open(p.to_str().unwrap(), syscall::O_CLOEXEC | syscall::O_STAT))?; + let file = File(FileDesc::new(fd)); + file.path() +} + +pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { + use fs::{File, set_permissions}; + if !from.is_file() { + return Err(Error::new(ErrorKind::InvalidInput, + "the source path is not an existing regular file")) + } + + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + let perm = reader.metadata()?.permissions(); + + let ret = io::copy(&mut reader, &mut writer)?; + set_permissions(to, perm)?; + Ok(ret) +} diff --git a/src/libstd/sys/redox/memchr.rs b/src/libstd/sys/redox/memchr.rs new file mode 100644 index 00000000000..4c314b7a472 --- /dev/null +++ b/src/libstd/sys/redox/memchr.rs @@ -0,0 +1,14 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Original implementation taken from rust-memchr +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +pub use sys_common::memchr::fallback::{memchr, memrchr}; diff --git a/src/libstd/sys/redox/mod.rs b/src/libstd/sys/redox/mod.rs new file mode 100644 index 00000000000..5982bdd6549 --- /dev/null +++ b/src/libstd/sys/redox/mod.rs @@ -0,0 +1,95 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code, missing_docs, bad_style)] + +use io::{self, ErrorKind}; + +pub mod args; +#[cfg(any(not(cargobuild), feature = "backtrace"))] +pub mod backtrace; +pub mod condvar; +pub mod env; +pub mod ext; +pub mod fast_thread_local; +pub mod fd; +pub mod fs; +pub mod memchr; +pub mod mutex; +pub mod net; +pub mod os; +pub mod os_str; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rand; +pub mod rwlock; +pub mod stack_overflow; +pub mod stdio; +pub mod syscall; +pub mod thread; +pub mod thread_local; +pub mod time; + +#[cfg(not(test))] +pub fn init() { + use alloc::oom; + + oom::set_oom_handler(oom_handler); + + // A nicer handler for out-of-memory situations than the default one. This + // one prints a message to stderr before aborting. It is critical that this + // code does not allocate any memory since we are in an OOM situation. Any + // errors are ignored while printing since there's nothing we can do about + // them and we are about to exit anyways. + fn oom_handler() -> ! { + use intrinsics; + let msg = "fatal runtime error: out of memory\n"; + unsafe { + let _ = syscall::write(2, msg.as_bytes()); + intrinsics::abort(); + } + } +} + +pub fn decode_error_kind(errno: i32) -> ErrorKind { + match errno { + syscall::ECONNREFUSED => ErrorKind::ConnectionRefused, + syscall::ECONNRESET => ErrorKind::ConnectionReset, + syscall::EPERM | syscall::EACCES => ErrorKind::PermissionDenied, + syscall::EPIPE => ErrorKind::BrokenPipe, + syscall::ENOTCONN => ErrorKind::NotConnected, + syscall::ECONNABORTED => ErrorKind::ConnectionAborted, + syscall::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + syscall::EADDRINUSE => ErrorKind::AddrInUse, + syscall::ENOENT => ErrorKind::NotFound, + syscall::EINTR => ErrorKind::Interrupted, + syscall::EINVAL => ErrorKind::InvalidInput, + syscall::ETIMEDOUT => ErrorKind::TimedOut, + syscall::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 == syscall::EAGAIN || x == syscall::EWOULDBLOCK => + ErrorKind::WouldBlock, + + _ => ErrorKind::Other, + } +} + +pub fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> { + result.map_err(|err| io::Error::from_raw_os_error(err.errno)) +} + +/// On Redox, use an illegal instruction to abort +pub unsafe fn abort_internal() -> ! { + ::core::intrinsics::abort(); +} diff --git a/src/libstd/sys/redox/mutex.rs b/src/libstd/sys/redox/mutex.rs new file mode 100644 index 00000000000..a995f597fc4 --- /dev/null +++ b/src/libstd/sys/redox/mutex.rs @@ -0,0 +1,179 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use intrinsics::{atomic_cxchg, atomic_xchg}; +use ptr; + +use sys::syscall::{futex, getpid, FUTEX_WAIT, FUTEX_WAKE}; + +pub unsafe fn mutex_try_lock(m: *mut i32) -> bool { + atomic_cxchg(m, 0, 1).0 == 0 +} + +pub unsafe fn mutex_lock(m: *mut i32) { + let mut c = 0; + //Set to larger value for longer spin test + for _i in 0..100 { + c = atomic_cxchg(m, 0, 1).0; + if c == 0 { + break; + } + //cpu_relax() + } + if c == 1 { + c = atomic_xchg(m, 2); + } + while c != 0 { + let _ = futex(m, FUTEX_WAIT, 2, 0, ptr::null_mut()); + c = atomic_xchg(m, 2); + } +} + +pub unsafe fn mutex_unlock(m: *mut i32) { + if *m == 2 { + *m = 0; + } else if atomic_xchg(m, 0) == 1 { + return; + } + //Set to larger value for longer spin test + for _i in 0..100 { + if *m != 0 { + if atomic_cxchg(m, 1, 2).0 != 0 { + return; + } + } + //cpu_relax() + } + let _ = futex(m, FUTEX_WAKE, 1, 0, ptr::null_mut()); +} + +pub struct Mutex { + pub lock: UnsafeCell<i32>, +} + +impl Mutex { + /// Create a new mutex. + pub const fn new() -> Self { + Mutex { + lock: UnsafeCell::new(0), + } + } + + #[inline] + pub unsafe fn init(&self) { + *self.lock.get() = 0; + } + + /// Try to lock the mutex + #[inline] + pub unsafe fn try_lock(&self) -> bool { + mutex_try_lock(self.lock.get()) + } + + /// Lock the mutex + #[inline] + pub unsafe fn lock(&self) { + mutex_lock(self.lock.get()); + } + + /// Unlock the mutex + #[inline] + pub unsafe fn unlock(&self) { + mutex_unlock(self.lock.get()); + } + + #[inline] + pub unsafe fn destroy(&self) { + *self.lock.get() = 0; + } +} + +unsafe impl Send for Mutex {} + +unsafe impl Sync for Mutex {} + +pub struct ReentrantMutex { + pub lock: UnsafeCell<i32>, + pub owner: UnsafeCell<usize>, + pub own_count: UnsafeCell<usize>, +} + +impl ReentrantMutex { + pub const fn uninitialized() -> Self { + ReentrantMutex { + lock: UnsafeCell::new(0), + owner: UnsafeCell::new(0), + own_count: UnsafeCell::new(0), + } + } + + #[inline] + pub unsafe fn init(&mut self) { + *self.lock.get() = 0; + *self.owner.get() = 0; + *self.own_count.get() = 0; + } + + /// Try to lock the mutex + #[inline] + pub unsafe fn try_lock(&self) -> bool { + let pid = getpid().unwrap(); + if *self.own_count.get() > 0 && *self.owner.get() == pid { + *self.own_count.get() += 1; + true + } else { + if mutex_try_lock(self.lock.get()) { + *self.owner.get() = pid; + *self.own_count.get() = 1; + true + } else { + false + } + } + } + + /// Lock the mutex + #[inline] + pub unsafe fn lock(&self) { + let pid = getpid().unwrap(); + if *self.own_count.get() > 0 && *self.owner.get() == pid { + *self.own_count.get() += 1; + } else { + mutex_lock(self.lock.get()); + *self.owner.get() = pid; + *self.own_count.get() = 1; + } + } + + /// Unlock the mutex + #[inline] + pub unsafe fn unlock(&self) { + let pid = getpid().unwrap(); + if *self.own_count.get() > 0 && *self.owner.get() == pid { + *self.own_count.get() -= 1; + if *self.own_count.get() == 0 { + *self.owner.get() = 0; + mutex_unlock(self.lock.get()); + } + } + } + + #[inline] + pub unsafe fn destroy(&self) { + *self.lock.get() = 0; + *self.owner.get() = 0; + *self.own_count.get() = 0; + } +} + +unsafe impl Send for ReentrantMutex {} + +unsafe impl Sync for ReentrantMutex {} diff --git a/src/libstd/sys/redox/net/dns/answer.rs b/src/libstd/sys/redox/net/dns/answer.rs new file mode 100644 index 00000000000..8e6aaeb0293 --- /dev/null +++ b/src/libstd/sys/redox/net/dns/answer.rs @@ -0,0 +1,22 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use string::String; +use vec::Vec; + +#[derive(Clone, Debug)] +pub struct DnsAnswer { + pub name: String, + pub a_type: u16, + pub a_class: u16, + pub ttl_a: u16, + pub ttl_b: u16, + pub data: Vec<u8> +} diff --git a/src/libstd/sys/redox/net/dns/mod.rs b/src/libstd/sys/redox/net/dns/mod.rs new file mode 100644 index 00000000000..43c4fe7ac9d --- /dev/null +++ b/src/libstd/sys/redox/net/dns/mod.rs @@ -0,0 +1,217 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::answer::DnsAnswer; +pub use self::query::DnsQuery; + +use slice; +use u16; +use string::String; +use vec::Vec; + +mod answer; +mod query; + +#[unstable(feature = "n16", issue="0")] +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct n16 { + inner: u16 +} + +impl n16 { + #[unstable(feature = "n16", issue="0")] + pub fn as_bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts((&self.inner as *const u16) as *const u8, 2) } + } + + #[unstable(feature = "n16", issue="0")] + pub fn from_bytes(bytes: &[u8]) -> Self { + n16 { + inner: unsafe { slice::from_raw_parts(bytes.as_ptr() as *const u16, bytes.len()/2)[0] } + } + } +} + +#[unstable(feature = "n16", issue="0")] +impl From<u16> for n16 { + fn from(value: u16) -> Self { + n16 { + inner: value.to_be() + } + } +} + +#[unstable(feature = "n16", issue="0")] +impl From<n16> for u16 { + fn from(value: n16) -> Self { + u16::from_be(value.inner) + } +} + +#[derive(Clone, Debug)] +pub struct Dns { + pub transaction_id: u16, + pub flags: u16, + pub queries: Vec<DnsQuery>, + pub answers: Vec<DnsAnswer> +} + +impl Dns { + pub fn compile(&self) -> Vec<u8> { + let mut data = Vec::new(); + + macro_rules! push_u8 { + ($value:expr) => { + data.push($value); + }; + }; + + macro_rules! push_n16 { + ($value:expr) => { + data.extend_from_slice(n16::from($value).as_bytes()); + }; + }; + + push_n16!(self.transaction_id); + push_n16!(self.flags); + push_n16!(self.queries.len() as u16); + push_n16!(self.answers.len() as u16); + push_n16!(0); + push_n16!(0); + + for query in self.queries.iter() { + for part in query.name.split('.') { + push_u8!(part.len() as u8); + data.extend_from_slice(part.as_bytes()); + } + push_u8!(0); + push_n16!(query.q_type); + push_n16!(query.q_class); + } + + data + } + + pub fn parse(data: &[u8]) -> Result<Self, String> { + let mut i = 0; + + macro_rules! pop_u8 { + () => { + { + i += 1; + if i > data.len() { + return Err(format!("{}: {}: pop_u8", file!(), line!())); + } + data[i - 1] + } + }; + }; + + macro_rules! pop_n16 { + () => { + { + i += 2; + if i > data.len() { + return Err(format!("{}: {}: pop_n16", file!(), line!())); + } + u16::from(n16::from_bytes(&data[i - 2 .. i])) + } + }; + }; + + macro_rules! pop_data { + () => { + { + let mut data = Vec::new(); + + let data_len = pop_n16!(); + for _data_i in 0..data_len { + data.push(pop_u8!()); + } + + data + } + }; + }; + + macro_rules! pop_name { + () => { + { + let mut name = String::new(); + + loop { + let name_len = pop_u8!(); + if name_len == 0 { + break; + } + if ! name.is_empty() { + name.push('.'); + } + for _name_i in 0..name_len { + name.push(pop_u8!() as char); + } + } + + name + } + }; + }; + + let transaction_id = pop_n16!(); + let flags = pop_n16!(); + let queries_len = pop_n16!(); + let answers_len = pop_n16!(); + pop_n16!(); + pop_n16!(); + + let mut queries = Vec::new(); + for _query_i in 0..queries_len { + queries.push(DnsQuery { + name: pop_name!(), + q_type: pop_n16!(), + q_class: pop_n16!() + }); + } + + let mut answers = Vec::new(); + for _answer_i in 0..answers_len { + let name_ind = 0b11000000; + let name_test = pop_u8!(); + i -= 1; + + answers.push(DnsAnswer { + name: if name_test & name_ind == name_ind { + let name_off = pop_n16!() - ((name_ind as u16) << 8); + let old_i = i; + i = name_off as usize; + let name = pop_name!(); + i = old_i; + name + } else { + pop_name!() + }, + a_type: pop_n16!(), + a_class: pop_n16!(), + ttl_a: pop_n16!(), + ttl_b: pop_n16!(), + data: pop_data!() + }); + } + + Ok(Dns { + transaction_id: transaction_id, + flags: flags, + queries: queries, + answers: answers, + }) + } +} diff --git a/src/libstd/sys/redox/net/dns/query.rs b/src/libstd/sys/redox/net/dns/query.rs new file mode 100644 index 00000000000..b0dcdcb624a --- /dev/null +++ b/src/libstd/sys/redox/net/dns/query.rs @@ -0,0 +1,18 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use string::String; + +#[derive(Clone, Debug)] +pub struct DnsQuery { + pub name: String, + pub q_type: u16, + pub q_class: u16 +} diff --git a/src/libstd/sys/redox/net/mod.rs b/src/libstd/sys/redox/net/mod.rs new file mode 100644 index 00000000000..3fdf61cfed8 --- /dev/null +++ b/src/libstd/sys/redox/net/mod.rs @@ -0,0 +1,113 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fs::File; +use io::{Error, Result, Read}; +use iter::Iterator; +use net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use str::FromStr; +use string::{String, ToString}; +use sys::syscall::EINVAL; +use time; +use vec::{IntoIter, Vec}; + +use self::dns::{Dns, DnsQuery}; + +pub use self::tcp::{TcpStream, TcpListener}; +pub use self::udp::UdpSocket; + +pub mod netc; + +mod dns; +mod tcp; +mod udp; + +pub struct LookupHost(IntoIter<SocketAddr>); + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } +} + +pub fn lookup_host(host: &str) -> Result<LookupHost> { + let mut ip_string = String::new(); + File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?; + let ip: Vec<u8> = ip_string.trim().split(".").map(|part| part.parse::<u8>() + .unwrap_or(0)).collect(); + + let mut dns_string = String::new(); + File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?; + let dns: Vec<u8> = dns_string.trim().split(".").map(|part| part.parse::<u8>() + .unwrap_or(0)).collect(); + + if ip.len() == 4 && dns.len() == 4 { + let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap(); + let tid = (time.subsec_nanos() >> 16) as u16; + + let packet = Dns { + transaction_id: tid, + flags: 0x0100, + queries: vec![DnsQuery { + name: host.to_string(), + q_type: 0x0001, + q_class: 0x0001, + }], + answers: vec![] + }; + + let packet_data = packet.compile(); + + let my_ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]); + let dns_ip = Ipv4Addr::new(dns[0], dns[1], dns[2], dns[3]); + let socket = UdpSocket::bind(&SocketAddr::V4(SocketAddrV4::new(my_ip, 0)))?; + socket.connect(&SocketAddr::V4(SocketAddrV4::new(dns_ip, 53)))?; + socket.send(&packet_data)?; + + let mut buf = [0; 65536]; + let count = socket.recv(&mut buf)?; + + match Dns::parse(&buf[.. count]) { + Ok(response) => { + let mut addrs = vec![]; + for answer in response.answers.iter() { + if answer.a_type == 0x0001 && answer.a_class == 0x0001 + && answer.data.len() == 4 + { + let answer_ip = Ipv4Addr::new(answer.data[0], + answer.data[1], + answer.data[2], + answer.data[3]); + addrs.push(SocketAddr::V4(SocketAddrV4::new(answer_ip, 0))); + } + } + Ok(LookupHost(addrs.into_iter())) + }, + Err(_err) => Err(Error::from_raw_os_error(EINVAL)) + } + } else { + Err(Error::from_raw_os_error(EINVAL)) + } +} + +fn path_to_peer_addr(path_str: &str) -> SocketAddr { + let mut parts = path_str.split('/').next().unwrap_or("").split(':').skip(1); + let host = Ipv4Addr::from_str(parts.next().unwrap_or("")).unwrap_or(Ipv4Addr::new(0, 0, 0, 0)); + let port = parts.next().unwrap_or("").parse::<u16>().unwrap_or(0); + SocketAddr::V4(SocketAddrV4::new(host, port)) +} + +fn path_to_local_addr(path_str: &str) -> SocketAddr { + let mut parts = path_str.split('/').nth(1).unwrap_or("").split(':'); + let host = Ipv4Addr::from_str(parts.next().unwrap_or("")).unwrap_or(Ipv4Addr::new(0, 0, 0, 0)); + let port = parts.next().unwrap_or("").parse::<u16>().unwrap_or(0); + SocketAddr::V4(SocketAddrV4::new(host, port)) +} diff --git a/src/libstd/sys/redox/net/netc.rs b/src/libstd/sys/redox/net/netc.rs new file mode 100644 index 00000000000..03e1c9fffa4 --- /dev/null +++ b/src/libstd/sys/redox/net/netc.rs @@ -0,0 +1,57 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub type in_addr_t = u32; +pub type in_port_t = u16; + +pub type socklen_t = u32; +pub type sa_family_t = u16; + +pub const AF_INET: sa_family_t = 1; +pub const AF_INET6: sa_family_t = 2; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct in_addr { + pub s_addr: in_addr_t, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct in6_addr { + pub s6_addr: [u8; 16], + __align: [u32; 0], +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct sockaddr { + pub sa_family: sa_family_t, + pub sa_data: [u8; 14], +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: in_port_t, + pub sin_addr: in_addr, + pub sin_zero: [u8; 8], +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: in_port_t, + pub sin6_flowinfo: u32, + pub sin6_addr: in6_addr, + pub sin6_scope_id: u32, +} diff --git a/src/libstd/sys/redox/net/tcp.rs b/src/libstd/sys/redox/net/tcp.rs new file mode 100644 index 00000000000..d5362c9f131 --- /dev/null +++ b/src/libstd/sys/redox/net/tcp.rs @@ -0,0 +1,199 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io::{Error, ErrorKind, Result}; +use net::{SocketAddr, Shutdown}; +use path::Path; +use sys::fs::{File, OpenOptions}; +use sys_common::{AsInner, FromInner, IntoInner}; +use time::Duration; +use vec::Vec; + +use super::{path_to_peer_addr, path_to_local_addr}; + +#[derive(Debug)] +pub struct TcpStream(File); + +impl TcpStream { + pub fn connect(addr: &SocketAddr) -> Result<TcpStream> { + let path = format!("tcp:{}", addr); + let mut options = OpenOptions::new(); + options.read(true); + options.write(true); + Ok(TcpStream(File::open(&Path::new(path.as_str()), &options)?)) + } + + pub fn duplicate(&self) -> Result<TcpStream> { + Ok(TcpStream(self.0.dup(&[])?)) + } + + pub fn read(&self, buf: &mut [u8]) -> Result<usize> { + self.0.read(buf) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> { + self.0.read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> Result<usize> { + self.0.write(buf) + } + + pub fn take_error(&self) -> Result<Option<Error>> { + Ok(None) + } + + pub fn peer_addr(&self) -> Result<SocketAddr> { + let path = self.0.path()?; + Ok(path_to_peer_addr(path.to_str().unwrap_or(""))) + } + + pub fn socket_addr(&self) -> Result<SocketAddr> { + let path = self.0.path()?; + Ok(path_to_local_addr(path.to_str().unwrap_or(""))) + } + + pub fn shutdown(&self, _how: Shutdown) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::shutdown not implemented")) + } + + pub fn nodelay(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "TcpStream::nodelay not implemented")) + } + + pub fn nonblocking(&self) -> Result<bool> { + self.0.fd().nonblocking() + } + + pub fn only_v6(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "TcpStream::only_v6 not implemented")) + } + + pub fn ttl(&self) -> Result<u32> { + Err(Error::new(ErrorKind::Other, "TcpStream::ttl not implemented")) + } + + pub fn read_timeout(&self) -> Result<Option<Duration>> { + Err(Error::new(ErrorKind::Other, "TcpStream::read_timeout not implemented")) + } + + pub fn write_timeout(&self) -> Result<Option<Duration>> { + Err(Error::new(ErrorKind::Other, "TcpStream::write_timeout not implemented")) + } + + pub fn set_nodelay(&self, _nodelay: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_nodelay not implemented")) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { + self.0.fd().set_nonblocking(nonblocking) + } + + pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_only_v6 not implemented")) + } + + pub fn set_ttl(&self, _ttl: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_ttl not implemented")) + } + + pub fn set_read_timeout(&self, _dur: Option<Duration>) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_read_timeout not implemented")) + } + + pub fn set_write_timeout(&self, _dur: Option<Duration>) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpStream::set_write_timeout not implemented")) + } +} + +impl AsInner<File> for TcpStream { + fn as_inner(&self) -> &File { &self.0 } +} + +impl FromInner<File> for TcpStream { + fn from_inner(file: File) -> TcpStream { + TcpStream(file) + } +} + +impl IntoInner<File> for TcpStream { + fn into_inner(self) -> File { self.0 } +} + +#[derive(Debug)] +pub struct TcpListener(File); + +impl TcpListener { + pub fn bind(addr: &SocketAddr) -> Result<TcpListener> { + let path = format!("tcp:/{}", addr); + let mut options = OpenOptions::new(); + options.read(true); + options.write(true); + Ok(TcpListener(File::open(&Path::new(path.as_str()), &options)?)) + } + + pub fn accept(&self) -> Result<(TcpStream, SocketAddr)> { + let file = self.0.dup(b"listen")?; + let path = file.path()?; + let peer_addr = path_to_peer_addr(path.to_str().unwrap_or("")); + Ok((TcpStream(file), peer_addr)) + } + + pub fn duplicate(&self) -> Result<TcpListener> { + Ok(TcpListener(self.0.dup(&[])?)) + } + + pub fn take_error(&self) -> Result<Option<Error>> { + Ok(None) + } + + pub fn socket_addr(&self) -> Result<SocketAddr> { + let path = self.0.path()?; + Ok(path_to_local_addr(path.to_str().unwrap_or(""))) + } + + pub fn nonblocking(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "TcpListener::nonblocking not implemented")) + } + + pub fn only_v6(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "TcpListener::only_v6 not implemented")) + } + + pub fn ttl(&self) -> Result<u32> { + Err(Error::new(ErrorKind::Other, "TcpListener::ttl not implemented")) + } + + pub fn set_nonblocking(&self, _nonblocking: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpListener::set_nonblocking not implemented")) + } + + pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpListener::set_only_v6 not implemented")) + } + + pub fn set_ttl(&self, _ttl: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "TcpListener::set_ttl not implemented")) + } +} + +impl AsInner<File> for TcpListener { + fn as_inner(&self) -> &File { &self.0 } +} + +impl FromInner<File> for TcpListener { + fn from_inner(file: File) -> TcpListener { + TcpListener(file) + } +} + +impl IntoInner<File> for TcpListener { + fn into_inner(self) -> File { self.0 } +} diff --git a/src/libstd/sys/redox/net/udp.rs b/src/libstd/sys/redox/net/udp.rs new file mode 100644 index 00000000000..607c66c2ba7 --- /dev/null +++ b/src/libstd/sys/redox/net/udp.rs @@ -0,0 +1,188 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cell::UnsafeCell; +use io::{Error, ErrorKind, Result}; +use net::{SocketAddr, Ipv4Addr, Ipv6Addr}; +use path::Path; +use sys::fs::{File, OpenOptions}; +use sys_common::{AsInner, FromInner, IntoInner}; +use time::Duration; + +use super::{path_to_peer_addr, path_to_local_addr}; + +#[derive(Debug)] +pub struct UdpSocket(File, UnsafeCell<Option<SocketAddr>>); + +impl UdpSocket { + pub fn bind(addr: &SocketAddr) -> Result<UdpSocket> { + let path = format!("udp:/{}", addr); + let mut options = OpenOptions::new(); + options.read(true); + options.write(true); + Ok(UdpSocket(File::open(&Path::new(path.as_str()), &options)?, UnsafeCell::new(None))) + } + + fn get_conn(&self) -> &mut Option<SocketAddr> { + unsafe { &mut *(self.1.get()) } + } + + pub fn connect(&self, addr: &SocketAddr) -> Result<()> { + unsafe { *self.1.get() = Some(*addr) }; + Ok(()) + } + + pub fn duplicate(&self) -> Result<UdpSocket> { + let new_bind = self.0.dup(&[])?; + let new_conn = *self.get_conn(); + Ok(UdpSocket(new_bind, UnsafeCell::new(new_conn))) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr)> { + let from = self.0.dup(b"listen")?; + let path = from.path()?; + let peer_addr = path_to_peer_addr(path.to_str().unwrap_or("")); + let count = from.read(buf)?; + Ok((count, peer_addr)) + } + + pub fn recv(&self, buf: &mut [u8]) -> Result<usize> { + if let Some(addr) = *self.get_conn() { + let from = self.0.dup(format!("{}", addr).as_bytes())?; + from.read(buf) + } else { + Err(Error::new(ErrorKind::Other, "UdpSocket::recv not connected")) + } + } + + pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> Result<usize> { + let to = self.0.dup(format!("{}", addr).as_bytes())?; + to.write(buf) + } + + pub fn send(&self, buf: &[u8]) -> Result<usize> { + if let Some(addr) = *self.get_conn() { + self.send_to(buf, &addr) + } else { + Err(Error::new(ErrorKind::Other, "UdpSocket::send not connected")) + } + } + + pub fn take_error(&self) -> Result<Option<Error>> { + Ok(None) + } + + pub fn socket_addr(&self) -> Result<SocketAddr> { + let path = self.0.path()?; + Ok(path_to_local_addr(path.to_str().unwrap_or(""))) + } + + pub fn broadcast(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "UdpSocket::broadcast not implemented")) + } + + pub fn multicast_loop_v4(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_loop_v4 not implemented")) + } + + pub fn multicast_loop_v6(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_loop_v6 not implemented")) + } + + pub fn multicast_ttl_v4(&self) -> Result<u32> { + Err(Error::new(ErrorKind::Other, "UdpSocket::multicast_ttl_v4 not implemented")) + } + + pub fn nonblocking(&self) -> Result<bool> { + self.0.fd().nonblocking() + } + + pub fn only_v6(&self) -> Result<bool> { + Err(Error::new(ErrorKind::Other, "UdpSocket::only_v6 not implemented")) + } + + pub fn ttl(&self) -> Result<u32> { + Err(Error::new(ErrorKind::Other, "UdpSocket::ttl not implemented")) + } + + pub fn read_timeout(&self) -> Result<Option<Duration>> { + Err(Error::new(ErrorKind::Other, "UdpSocket::read_timeout not implemented")) + } + + pub fn write_timeout(&self) -> Result<Option<Duration>> { + Err(Error::new(ErrorKind::Other, "UdpSocket::write_timeout not implemented")) + } + + pub fn set_broadcast(&self, _broadcast: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_broadcast not implemented")) + } + + pub fn set_multicast_loop_v4(&self, _multicast_loop_v4: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_loop_v4 not implemented")) + } + + pub fn set_multicast_loop_v6(&self, _multicast_loop_v6: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_loop_v6 not implemented")) + } + + pub fn set_multicast_ttl_v4(&self, _multicast_ttl_v4: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_multicast_ttl_v4 not implemented")) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> { + self.0.fd().set_nonblocking(nonblocking) + } + + pub fn set_only_v6(&self, _only_v6: bool) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_only_v6 not implemented")) + } + + pub fn set_ttl(&self, _ttl: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_ttl not implemented")) + } + + pub fn set_read_timeout(&self, _dur: Option<Duration>) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_read_timeout not implemented")) + } + + pub fn set_write_timeout(&self, _dur: Option<Duration>) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::set_write_timeout not implemented")) + } + + pub fn join_multicast_v4(&self, _multiaddr: &Ipv4Addr, _interface: &Ipv4Addr) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::join_multicast_v4 not implemented")) + } + + pub fn join_multicast_v6(&self, _multiaddr: &Ipv6Addr, _interface: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::join_multicast_v6 not implemented")) + } + + pub fn leave_multicast_v4(&self, _multiaddr: &Ipv4Addr, _interface: &Ipv4Addr) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::leave_multicast_v4 not implemented")) + } + + pub fn leave_multicast_v6(&self, _multiaddr: &Ipv6Addr, _interface: u32) -> Result<()> { + Err(Error::new(ErrorKind::Other, "UdpSocket::leave_multicast_v6 not implemented")) + } +} + +impl AsInner<File> for UdpSocket { + fn as_inner(&self) -> &File { &self.0 } +} + +impl FromInner<File> for UdpSocket { + fn from_inner(file: File) -> UdpSocket { + UdpSocket(file, UnsafeCell::new(None)) + } +} + +impl IntoInner<File> for UdpSocket { + fn into_inner(self) -> File { self.0 } +} diff --git a/src/libstd/sys/redox/os.rs b/src/libstd/sys/redox/os.rs new file mode 100644 index 00000000000..135e972bca4 --- /dev/null +++ b/src/libstd/sys/redox/os.rs @@ -0,0 +1,204 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of `std::os` functionality for unix systems + +#![allow(unused_imports)] // lots of cfg code here + +use os::unix::prelude::*; + +use error::Error as StdError; +use ffi::{OsString, OsStr}; +use fmt; +use io::{self, Read, Write}; +use iter; +use marker::PhantomData; +use mem; +use memchr; +use path::{self, PathBuf}; +use ptr; +use slice; +use str; +use sys_common::mutex::Mutex; +use sys::{cvt, fd, syscall}; +use vec; + +const TMPBUF_SZ: usize = 128; +static ENV_LOCK: Mutex = Mutex::new(); + +/// Returns the platform-specific value of errno +pub fn errno() -> i32 { + 0 +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(errno: i32) -> String { + if let Some(string) = syscall::STR_ERROR.get(errno as usize) { + string.to_string() + } else { + "unknown error".to_string() + } +} + +pub fn getcwd() -> io::Result<PathBuf> { + let mut buf = [0; 4096]; + let count = cvt(syscall::getcwd(&mut buf))?; + Ok(PathBuf::from(OsString::from_vec(buf[.. count].to_vec()))) +} + +pub fn chdir(p: &path::Path) -> io::Result<()> { + cvt(syscall::chdir(p.to_str().unwrap())).and(Ok(())) +} + +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_colon(b: &u8) -> bool { *b == b':' } + let unparsed = unparsed.as_bytes(); + SplitPaths { + iter: unparsed.split(is_colon 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(); + let sep = b':'; + + for (i, path) in paths.enumerate() { + let path = path.as_ref().as_bytes(); + if i > 0 { joined.push(sep) } + if path.contains(&sep) { + 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 { + "path segment contains separator `:`".fmt(f) + } +} + +impl StdError for JoinPathsError { + fn description(&self) -> &str { "failed to join paths" } +} + +pub fn current_exe() -> io::Result<PathBuf> { + use fs::File; + + let mut file = File::open("sys:exe")?; + + let mut path = String::new(); + file.read_to_string(&mut path)?; + + if path.ends_with('\n') { + path.pop(); + } + + Ok(PathBuf::from(path)) +} + +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() } +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + let mut variables: Vec<(OsString, OsString)> = Vec::new(); + if let Ok(mut file) = ::fs::File::open("env:") { + let mut string = String::new(); + if file.read_to_string(&mut string).is_ok() { + for line in string.lines() { + let mut parts = line.splitn(2, '='); + if let Some(name) = parts.next() { + let value = parts.next().unwrap_or(""); + variables.push((OsString::from(name.to_string()), + OsString::from(value.to_string()))); + } + } + } + } + Env { iter: variables.into_iter(), _dont_send_or_sync_me: PhantomData } +} + +pub fn getenv(key: &OsStr) -> io::Result<Option<OsString>> { + if ! key.is_empty() { + if let Ok(mut file) = ::fs::File::open(&("env:".to_owned() + key.to_str().unwrap())) { + let mut string = String::new(); + file.read_to_string(&mut string)?; + Ok(Some(OsString::from(string))) + } else { + Ok(None) + } + } else { + Ok(None) + } +} + +pub fn setenv(key: &OsStr, value: &OsStr) -> io::Result<()> { + if ! key.is_empty() { + let mut file = ::fs::File::open(&("env:".to_owned() + key.to_str().unwrap()))?; + file.write_all(value.as_bytes())?; + file.set_len(value.len() as u64)?; + } + Ok(()) +} + +pub fn unsetenv(key: &OsStr) -> io::Result<()> { + ::fs::remove_file(&("env:".to_owned() + key.to_str().unwrap()))?; + Ok(()) +} + +pub fn page_size() -> usize { + 4096 +} + +pub fn temp_dir() -> PathBuf { + ::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| { + PathBuf::from("/tmp") + }) +} + +pub fn home_dir() -> Option<PathBuf> { + return ::env::var_os("HOME").map(PathBuf::from); +} + +pub fn exit(code: i32) -> ! { + let _ = syscall::exit(code as usize); + unreachable!(); +} diff --git a/src/libstd/sys/redox/os_str.rs b/src/libstd/sys/redox/os_str.rs new file mode 100644 index 00000000000..8922bf04f56 --- /dev/null +++ b/src/libstd/sys/redox/os_str.rs @@ -0,0 +1,119 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Unix systems: just +/// a `Vec<u8>`/`[u8]`. + +use borrow::Cow; +use fmt::{self, Debug}; +use str; +use mem; +use sys_common::{AsInner, IntoInner}; + +#[derive(Clone, Hash)] +pub struct Buf { + pub inner: Vec<u8> +} + +pub struct Slice { + pub inner: [u8] +} + +impl Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.to_string_lossy().fmt(formatter) + } +} + +impl Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.as_slice().fmt(formatter) + } +} + +impl IntoInner<Vec<u8>> for Buf { + fn into_inner(self) -> Vec<u8> { + self.inner + } +} + +impl AsInner<[u8]> for Buf { + fn as_inner(&self) -> &[u8] { + &self.inner + } +} + + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: s.into_bytes() } + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Buf { + Buf { + inner: Vec::with_capacity(capacity) + } + } + + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } + + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(&*self.inner) } + } + + pub fn into_string(self) -> Result<String, Buf> { + String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.extend_from_slice(&s.inner) + } +} + +impl Slice { + fn from_u8_slice(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + pub fn from_str(s: &str) -> &Slice { + Slice::from_u8_slice(s.as_bytes()) + } + + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(&self.inner).ok() + } + + pub fn to_string_lossy(&self) -> Cow<str> { + String::from_utf8_lossy(&self.inner) + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_vec() } + } +} diff --git a/src/libstd/sys/redox/path.rs b/src/libstd/sys/redox/path.rs new file mode 100644 index 00000000000..e6a267dd5d9 --- /dev/null +++ b/src/libstd/sys/redox/path.rs @@ -0,0 +1,39 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsStr; +use 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(path: &OsStr) -> Option<Prefix> { + if let Some(path_str) = path.to_str() { + if let Some(_i) = path_str.find(':') { + // FIXME: Redox specific prefix + // Some(Prefix::Verbatim(OsStr::new(&path_str[..i]))) + None + } else { + None + } + } else { + None + } +} + +pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/redox/pipe.rs b/src/libstd/sys/redox/pipe.rs new file mode 100644 index 00000000000..e7240fbe7bf --- /dev/null +++ b/src/libstd/sys/redox/pipe.rs @@ -0,0 +1,107 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::{cvt, syscall}; +use sys::fd::FileDesc; + +//////////////////////////////////////////////////////////////////////////////// +// Anonymous pipes +//////////////////////////////////////////////////////////////////////////////// + +pub struct AnonPipe(FileDesc); + +pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { + let mut fds = [0; 2]; + cvt(syscall::pipe2(&mut fds, syscall::O_CLOEXEC))?; + Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))) +} + +impl AnonPipe { + pub fn from_fd(fd: FileDesc) -> io::Result<AnonPipe> { + fd.set_cloexec()?; + Ok(AnonPipe(fd)) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + self.0.read_to_end(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + pub fn fd(&self) -> &FileDesc { &self.0 } + pub fn into_fd(self) -> FileDesc { self.0 } +} + +pub fn read2(p1: AnonPipe, + v1: &mut Vec<u8>, + p2: AnonPipe, + v2: &mut Vec<u8>) -> io::Result<()> { + //FIXME: Use event based I/O multiplexing + //unimplemented!() + + p1.read_to_end(v1)?; + p2.read_to_end(v2)?; + + Ok(()) + + /* + // 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)?; + + loop { + // wait for either pipe to become readable using `select` + cvt_r(|| unsafe { + let mut read: libc::fd_set = mem::zeroed(); + libc::FD_SET(p1.raw(), &mut read); + libc::FD_SET(p2.raw(), &mut read); + libc::select(max + 1, &mut read, ptr::null_mut(), ptr::null_mut(), + ptr::null_mut()) + })?; + + // 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. + let read = |fd: &FileDesc, dst: &mut Vec<u8>| { + 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) + } + } + } + }; + if read(&p1, v1)? { + p2.set_nonblocking(false)?; + return p2.read_to_end(v2).map(|_| ()); + } + if read(&p2, v2)? { + p1.set_nonblocking(false)?; + return p1.read_to_end(v1).map(|_| ()); + } + } + */ +} diff --git a/src/libstd/sys/redox/process.rs b/src/libstd/sys/redox/process.rs new file mode 100644 index 00000000000..849f51013e6 --- /dev/null +++ b/src/libstd/sys/redox/process.rs @@ -0,0 +1,504 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use collections::hash_map::HashMap; +use env; +use ffi::OsStr; +use fmt; +use io::{self, Error, ErrorKind}; +use path::Path; +use sys::fd::FileDesc; +use sys::fs::{File, OpenOptions}; +use sys::pipe::{self, AnonPipe}; +use sys::{cvt, syscall}; + +//////////////////////////////////////////////////////////////////////////////// +// 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: String, + args: Vec<String>, + env: HashMap<String, String>, + + cwd: Option<String>, + uid: Option<u32>, + gid: Option<u32>, + saw_nul: bool, + closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>, + stdin: Option<Stdio>, + stdout: Option<Stdio>, + stderr: Option<Stdio>, +} + +// 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 +struct ChildPipes { + stdin: ChildStdio, + stdout: ChildStdio, + stderr: ChildStdio, +} + +enum ChildStdio { + Inherit, + Explicit(usize), + Owned(FileDesc), +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, + Fd(FileDesc), +} + +impl Command { + pub fn new(program: &OsStr) -> Command { + Command { + program: program.to_str().unwrap().to_owned(), + args: Vec::new(), + env: HashMap::new(), + cwd: None, + uid: None, + gid: None, + saw_nul: false, + closures: Vec::new(), + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_str().unwrap().to_owned()); + } + + pub fn env(&mut self, key: &OsStr, val: &OsStr) { + self.env.insert(key.to_str().unwrap().to_owned(), val.to_str().unwrap().to_owned()); + } + + pub fn env_remove(&mut self, key: &OsStr) { + self.env.remove(key.to_str().unwrap()); + } + + pub fn env_clear(&mut self) { + self.env.clear(); + } + + pub fn cwd(&mut self, dir: &OsStr) { + self.cwd = Some(dir.to_str().unwrap().to_owned()); + } + pub fn uid(&mut self, id: u32) { + self.uid = Some(id); + } + pub fn gid(&mut self, id: u32) { + self.gid = Some(id); + } + + pub fn before_exec(&mut self, + f: Box<FnMut() -> io::Result<()> + Send + Sync>) { + self.closures.push(f); + } + + pub fn stdin(&mut self, stdin: Stdio) { + self.stdin = Some(stdin); + } + pub fn stdout(&mut self, stdout: Stdio) { + self.stdout = Some(stdout); + } + pub fn stderr(&mut self, stderr: Stdio) { + self.stderr = Some(stderr); + } + + pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + + 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)?; + let (input, output) = pipe::anon_pipe()?; + + let pid = unsafe { + match cvt(syscall::clone(0))? { + 0 => { + drop(input); + let err = self.do_exec(theirs); + let errno = err.raw_os_error().unwrap_or(syscall::EINVAL) as u32; + let bytes = [ + (errno >> 24) as u8, + (errno >> 16) as u8, + (errno >> 8) as u8, + (errno >> 0) as u8, + CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] + ]; + // pipe I/O up to PIPE_BUF bytes should be atomic, and then + // we want to be sure we *don't* run at_exit destructors as + // we're being torn down regardless + assert!(output.write(&bytes).is_ok()); + let _ = syscall::exit(1); + panic!("failed to exit"); + } + n => n, + } + }; + + let mut p = Process { pid: pid, status: None }; + drop(output); + let mut bytes = [0; 8]; + + // loop to handle EINTR + loop { + match input.read(&mut bytes) { + Ok(0) => return Ok((p, ours)), + Ok(8) => { + assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), + "Validation on the CLOEXEC pipe failed: {:?}", bytes); + let errno = combine(&bytes[0.. 4]); + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + return Err(Error::from_raw_os_error(errno)) + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => { + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("the CLOEXEC pipe failed: {:?}", e) + }, + Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } + } + } + + fn combine(arr: &[u8]) -> i32 { + let a = arr[0] as u32; + let b = arr[1] as u32; + let c = arr[2] as u32; + let d = arr[3] as u32; + + ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 + } + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + if self.saw_nul { + return io::Error::new(ErrorKind::InvalidInput, + "nul byte found in provided data") + } + + match self.setup_io(default, true) { + Ok((_, theirs)) => unsafe { self.do_exec(theirs) }, + 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 OSX 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 syscall::exit) + unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error { + macro_rules! t { + ($e:expr) => (match $e { + Ok(e) => e, + Err(e) => return e, + }) + } + + if let Some(fd) = stdio.stderr.fd() { + let _ = syscall::close(2); + t!(cvt(syscall::dup(fd, &[]))); + let _ = syscall::close(fd); + } + if let Some(fd) = stdio.stdout.fd() { + let _ = syscall::close(1); + t!(cvt(syscall::dup(fd, &[]))); + let _ = syscall::close(fd); + } + if let Some(fd) = stdio.stdin.fd() { + let _ = syscall::close(0); + t!(cvt(syscall::dup(fd, &[]))); + let _ = syscall::close(fd); + } + + if let Some(g) = self.gid { + t!(cvt(syscall::setregid(g as usize, g as usize))); + } + if let Some(u) = self.uid { + t!(cvt(syscall::setreuid(u as usize, u as usize))); + } + if let Some(ref cwd) = self.cwd { + t!(cvt(syscall::chdir(cwd))); + } + + for callback in self.closures.iter_mut() { + t!(callback()); + } + + let mut args: Vec<[usize; 2]> = Vec::new(); + args.push([self.program.as_ptr() as usize, self.program.len()]); + for arg in self.args.iter() { + args.push([arg.as_ptr() as usize, arg.len()]); + } + + for (key, val) in self.env.iter() { + env::set_var(key, val); + } + + let program = if self.program.contains(':') || self.program.contains('/') { + self.program.to_owned() + } else { + let mut path_env = ::env::var("PATH").unwrap_or(".".to_string()); + + if ! path_env.ends_with('/') { + path_env.push('/'); + } + + path_env.push_str(&self.program); + + path_env + }; + + if let Err(err) = syscall::execve(&program, &args) { + io::Error::from_raw_os_error(err.errno as i32) + } else { + panic!("return from exec without err"); + } + } + + + 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)) + } +} + +impl Stdio { + 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() <= 2 { + Ok((ChildStdio::Owned(fd.duplicate()?), None)) + } else { + Ok((ChildStdio::Explicit(fd.raw()), None)) + } + } + + Stdio::MakePipe => { + let (reader, writer) = pipe::anon_pipe()?; + let (ours, theirs) = if readable { + (writer, reader) + } else { + (reader, writer) + }; + Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) + } + + Stdio::Null => { + let mut opts = OpenOptions::new(); + opts.read(readable); + opts.write(!readable); + let fd = File::open(&Path::new("null:"), &opts)?; + Ok((ChildStdio::Owned(fd.into_fd()), None)) + } + } + } +} + +impl ChildStdio { + fn fd(&self) -> Option<usize> { + match *self { + ChildStdio::Inherit => None, + ChildStdio::Explicit(fd) => Some(fd), + ChildStdio::Owned(ref fd) => Some(fd.raw()), + } + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.program)?; + for arg in &self.args { + write!(f, " {:?}", arg)?; + } + Ok(()) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// Unix exit statuses +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(i32); + +impl ExitStatus { + fn exited(&self) -> bool { + self.0 & 0x7F == 0 + } + + pub fn success(&self) -> bool { + self.code() == Some(0) + } + + pub fn code(&self) -> Option<i32> { + if self.exited() { + Some((self.0 >> 8) & 0xFF) + } else { + None + } + } + + pub fn signal(&self) -> Option<i32> { + if !self.exited() { + Some(self.0 & 0x7F) + } else { + None + } + } +} + +impl From<i32> for ExitStatus { + fn from(a: i32) -> 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) + } + } +} + +/// The unique id of the process (this should never be negative). +pub struct Process { + pid: usize, + 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(syscall::kill(self.pid, syscall::SIGKILL))?; + Ok(()) + } + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + if let Some(status) = self.status { + return Ok(status) + } + let mut status = 0; + cvt(syscall::waitpid(self.pid, &mut status, 0))?; + self.status = Some(ExitStatus(status as i32)); + Ok(ExitStatus(status as i32)) + } +} diff --git a/src/libstd/sys/redox/rand.rs b/src/libstd/sys/redox/rand.rs new file mode 100644 index 00000000000..eb28eca38bc --- /dev/null +++ b/src/libstd/sys/redox/rand.rs @@ -0,0 +1,57 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use rand::Rng; + +// FIXME: Use rand: +pub struct OsRng { + state: [u64; 2] +} + +impl OsRng { + /// Create a new `OsRng`. + pub fn new() -> io::Result<OsRng> { + Ok(OsRng { + state: [0xBADF00D1, 0xDEADBEEF] + }) + } +} + +impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + fn next_u64(&mut self) -> u64 { + // Store the first and second part. + let mut x = self.state[0]; + let y = self.state[1]; + + // Put the second part into the first slot. + self.state[0] = y; + // Twist the first slot. + x ^= x << 23; + // Update the second slot. + self.state[1] = x ^ y ^ (x >> 17) ^ (y >> 26); + + // Generate the final integer. + self.state[1].wrapping_add(y) + + } + fn fill_bytes(&mut self, buf: &mut [u8]) { + for chunk in buf.chunks_mut(8) { + let mut rand: u64 = self.next_u64(); + for b in chunk.iter_mut() { + *b = rand as u8; + rand = rand >> 8; + } + } + } +} diff --git a/src/libstd/sys/redox/rwlock.rs b/src/libstd/sys/redox/rwlock.rs new file mode 100644 index 00000000000..d74b614ba47 --- /dev/null +++ b/src/libstd/sys/redox/rwlock.rs @@ -0,0 +1,61 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::mutex::Mutex; + +pub struct RWLock { + mutex: Mutex +} + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { + mutex: Mutex::new() + } + } + + #[inline] + pub unsafe fn read(&self) { + self.mutex.lock(); + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + self.mutex.try_lock() + } + + #[inline] + pub unsafe fn write(&self) { + self.mutex.lock(); + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + self.mutex.try_lock() + } + + #[inline] + pub unsafe fn read_unlock(&self) { + self.mutex.unlock(); + } + + #[inline] + pub unsafe fn write_unlock(&self) { + self.mutex.unlock(); + } + + #[inline] + pub unsafe fn destroy(&self) { + self.mutex.destroy(); + } +} diff --git a/src/libstd/sys/redox/stack_overflow.rs b/src/libstd/sys/redox/stack_overflow.rs new file mode 100644 index 00000000000..760fe06c57f --- /dev/null +++ b/src/libstd/sys/redox/stack_overflow.rs @@ -0,0 +1,27 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg_attr(test, allow(dead_code))] + +pub struct Handler; + +impl Handler { + pub unsafe fn new() -> Handler { + Handler + } +} + +pub unsafe fn init() { + +} + +pub unsafe fn cleanup() { + +} diff --git a/src/libstd/sys/redox/stdio.rs b/src/libstd/sys/redox/stdio.rs new file mode 100644 index 00000000000..607eef051d6 --- /dev/null +++ b/src/libstd/sys/redox/stdio.rs @@ -0,0 +1,81 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::{cvt, syscall}; +use sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +impl Stdin { + pub fn new() -> io::Result<Stdin> { Ok(Stdin(())) } + + pub fn read(&self, data: &mut [u8]) -> io::Result<usize> { + let fd = FileDesc::new(0); + let ret = fd.read(data); + fd.into_raw(); + ret + } + + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { + let fd = FileDesc::new(0); + let ret = fd.read_to_end(buf); + fd.into_raw(); + ret + } +} + +impl Stdout { + pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + let fd = FileDesc::new(1); + let ret = fd.write(data); + fd.into_raw(); + ret + } + + pub fn flush(&self) -> io::Result<()> { + cvt(syscall::fsync(1)).and(Ok(())) + } +} + +impl Stderr { + pub fn new() -> io::Result<Stderr> { Ok(Stderr(())) } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + let fd = FileDesc::new(2); + let ret = fd.write(data); + fd.into_raw(); + ret + } + + pub fn flush(&self) -> io::Result<()> { + cvt(syscall::fsync(2)).and(Ok(())) + } +} + +// FIXME: right now this raw stderr handle is used in a few places because +// std::io::stderr_raw isn't exposed, but once that's exposed this impl +// should go away +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + Stderr::write(self, data) + } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } +} + +pub const EBADF_ERR: i32 = ::sys::syscall::EBADF; +pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; diff --git a/src/libstd/sys/redox/syscall/arch/arm.rs b/src/libstd/sys/redox/syscall/arch/arm.rs new file mode 100644 index 00000000000..9fb3961486d --- /dev/null +++ b/src/libstd/sys/redox/syscall/arch/arm.rs @@ -0,0 +1,83 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::error::{Error, Result}; + +pub unsafe fn syscall0(mut a: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a) + : "memory" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b) + : "memory" + : "volatile"); + + Error::demux(a) +} + +// Clobbers all registers - special for clone +pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b) + : "memory", "r0", "r1", "r2", "r3", "r4" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b), "{r1}"(c) + : "memory" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d) + : "memory" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d), "{r3}"(e) + : "memory" + : "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) + -> Result<usize> { + asm!("swi $$0" + : "={r0}"(a) + : "{r7}"(a), "{r0}"(b), "{r1}"(c), "{r2}"(d), "{r3}"(e), "{r4}"(f) + : "memory" + : "volatile"); + + Error::demux(a) +} diff --git a/src/libstd/sys/redox/syscall/arch/x86.rs b/src/libstd/sys/redox/syscall/arch/x86.rs new file mode 100644 index 00000000000..724a6b927f4 --- /dev/null +++ b/src/libstd/sys/redox/syscall/arch/x86.rs @@ -0,0 +1,83 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::error::{Error, Result}; + +pub unsafe fn syscall0(mut a: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +// Clobbers all registers - special for clone +pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b) + : "memory", "ebx", "ecx", "edx", "esi", "edi" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b), "{ecx}"(c) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d), "{esi}"(e) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) + -> Result<usize> { + asm!("int 0x80" + : "={eax}"(a) + : "{eax}"(a), "{ebx}"(b), "{ecx}"(c), "{edx}"(d), "{esi}"(e), "{edi}"(f) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} diff --git a/src/libstd/sys/redox/syscall/arch/x86_64.rs b/src/libstd/sys/redox/syscall/arch/x86_64.rs new file mode 100644 index 00000000000..a321c31f207 --- /dev/null +++ b/src/libstd/sys/redox/syscall/arch/x86_64.rs @@ -0,0 +1,84 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::error::{Error, Result}; + +pub unsafe fn syscall0(mut a: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall1(mut a: usize, b: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +// Clobbers all registers - special for clone +pub unsafe fn syscall1_clobber(mut a: usize, b: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b) + : "memory", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", + "r9", "r10", "r11", "r12", "r13", "r14", "r15" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b), "{rcx}"(c) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d), "{rsi}"(e) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} + +pub unsafe fn syscall5(mut a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) + -> Result<usize> { + asm!("int 0x80" + : "={rax}"(a) + : "{rax}"(a), "{rbx}"(b), "{rcx}"(c), "{rdx}"(d), "{rsi}"(e), "{rdi}"(f) + : "memory" + : "intel", "volatile"); + + Error::demux(a) +} diff --git a/src/libstd/sys/redox/syscall/call.rs b/src/libstd/sys/redox/syscall/call.rs new file mode 100644 index 00000000000..f58c240f31e --- /dev/null +++ b/src/libstd/sys/redox/syscall/call.rs @@ -0,0 +1,300 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::arch::*; +use super::data::{Stat, StatVfs, TimeSpec}; +use super::error::Result; +use super::number::*; + +use core::mem; + +/// Set the end of the process's heap +/// +/// When `addr` is `0`, this function will return the current break. +/// +/// When `addr` is nonzero, this function will attempt to set the end of the process's +/// heap to `addr` and return the new program break. The new program break should be +/// checked by the allocator, it may not be exactly `addr`, as it may be aligned to a page +/// boundary. +/// +/// On error, `Err(ENOMEM)` will be returned indicating that no memory is available +pub unsafe fn brk(addr: usize) -> Result<usize> { + syscall1(SYS_BRK, addr) +} + +/// Change the process's working directory +/// +/// This function will attempt to set the process's working directory to `path`, which can be +/// either a relative, scheme relative, or absolute path. +/// +/// On success, `Ok(0)` will be returned. On error, one of the following errors will be returned. +/// +/// # Errors +/// +/// * `EACCES` - permission is denied for one of the components of `path`, or `path` +/// * `EFAULT` - `path` does not point to the process's addressible memory +/// * `EIO` - an I/O error occured +/// * `ENOENT` - `path` does not exit +/// * `ENOTDIR` - `path` is not a directory +pub fn chdir(path: &str) -> Result<usize> { + unsafe { syscall2(SYS_CHDIR, path.as_ptr() as usize, path.len()) } +} + +pub fn chmod(path: &str, mode: usize) -> Result<usize> { + unsafe { syscall3(SYS_CHMOD, path.as_ptr() as usize, path.len(), mode) } +} + +/// Produce a fork of the current process, or a new process thread +pub unsafe fn clone(flags: usize) -> Result<usize> { + syscall1_clobber(SYS_CLONE, flags) +} + +/// Close a file +pub fn close(fd: usize) -> Result<usize> { + unsafe { syscall1(SYS_CLOSE, fd) } +} + +/// Get the current system time +pub fn clock_gettime(clock: usize, tp: &mut TimeSpec) -> Result<usize> { + unsafe { syscall2(SYS_CLOCK_GETTIME, clock, tp as *mut TimeSpec as usize) } +} + +/// Copy and transform a file descriptor +pub fn dup(fd: usize, buf: &[u8]) -> Result<usize> { + unsafe { syscall3(SYS_DUP, fd, buf.as_ptr() as usize, buf.len()) } +} + +/// Replace the current process with a new executable +pub fn execve(path: &str, args: &[[usize; 2]]) -> Result<usize> { + unsafe { syscall4(SYS_EXECVE, path.as_ptr() as usize, path.len(), + args.as_ptr() as usize, args.len()) } +} + +/// Exit the current process +pub fn exit(status: usize) -> Result<usize> { + unsafe { syscall1(SYS_EXIT, status) } +} + +/// Register a file for event-based I/O +pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> { + unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) } +} + +/// Register a file for event-based I/O +pub fn fevent(fd: usize, flags: usize) -> Result<usize> { + unsafe { syscall2(SYS_FEVENT, fd, flags) } +} + +/// Map a file into memory +pub unsafe fn fmap(fd: usize, offset: usize, size: usize) -> Result<usize> { + syscall3(SYS_FMAP, fd, offset, size) +} + +/// Unmap a memory-mapped file +pub unsafe fn funmap(addr: usize) -> Result<usize> { + syscall1(SYS_FUNMAP, addr) +} + +/// Retrieve the canonical path of a file +pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> { + unsafe { syscall3(SYS_FPATH, fd, buf.as_mut_ptr() as usize, buf.len()) } +} + +/// Get metadata about a file +pub fn fstat(fd: usize, stat: &mut Stat) -> Result<usize> { + unsafe { syscall3(SYS_FSTAT, fd, stat as *mut Stat as usize, mem::size_of::<Stat>()) } +} + +/// Get metadata about a filesystem +pub fn fstatvfs(fd: usize, stat: &mut StatVfs) -> Result<usize> { + unsafe { syscall3(SYS_FSTATVFS, fd, stat as *mut StatVfs as usize, mem::size_of::<StatVfs>()) } +} + +/// Sync a file descriptor to its underlying medium +pub fn fsync(fd: usize) -> Result<usize> { + unsafe { syscall1(SYS_FSYNC, fd) } +} + +/// Truncate or extend a file to a specified length +pub fn ftruncate(fd: usize, len: usize) -> Result<usize> { + unsafe { syscall2(SYS_FTRUNCATE, fd, len) } +} + +/// Fast userspace mutex +pub unsafe fn futex(addr: *mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32) + -> Result<usize> { + syscall5(SYS_FUTEX, addr as usize, op, (val as isize) as usize, val2, addr2 as usize) +} + +/// Get the current working directory +pub fn getcwd(buf: &mut [u8]) -> Result<usize> { + unsafe { syscall2(SYS_GETCWD, buf.as_mut_ptr() as usize, buf.len()) } +} + +/// Get the effective group ID +pub fn getegid() -> Result<usize> { + unsafe { syscall0(SYS_GETEGID) } +} + +/// Get the effective namespace +pub fn getens() -> Result<usize> { + unsafe { syscall0(SYS_GETENS) } +} + +/// Get the effective user ID +pub fn geteuid() -> Result<usize> { + unsafe { syscall0(SYS_GETEUID) } +} + +/// Get the current group ID +pub fn getgid() -> Result<usize> { + unsafe { syscall0(SYS_GETGID) } +} + +/// Get the current namespace +pub fn getns() -> Result<usize> { + unsafe { syscall0(SYS_GETNS) } +} + +/// Get the current process ID +pub fn getpid() -> Result<usize> { + unsafe { syscall0(SYS_GETPID) } +} + +/// Get the current user ID +pub fn getuid() -> Result<usize> { + unsafe { syscall0(SYS_GETUID) } +} + +/// Set the I/O privilege level +pub unsafe fn iopl(level: usize) -> Result<usize> { + syscall1(SYS_IOPL, level) +} + +/// Send a signal `sig` to the process identified by `pid` +pub fn kill(pid: usize, sig: usize) -> Result<usize> { + unsafe { syscall2(SYS_KILL, pid, sig) } +} + +/// Create a link to a file +pub unsafe fn link(old: *const u8, new: *const u8) -> Result<usize> { + syscall2(SYS_LINK, old as usize, new as usize) +} + +/// Seek to `offset` bytes in a file descriptor +pub fn lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> { + unsafe { syscall3(SYS_LSEEK, fd, offset as usize, whence) } +} + +/// Make a new scheme namespace +pub fn mkns(schemes: &[[usize; 2]]) -> Result<usize> { + unsafe { syscall2(SYS_MKNS, schemes.as_ptr() as usize, schemes.len()) } +} + +/// Sleep for the time specified in `req` +pub fn nanosleep(req: &TimeSpec, rem: &mut TimeSpec) -> Result<usize> { + unsafe { syscall2(SYS_NANOSLEEP, req as *const TimeSpec as usize, + rem as *mut TimeSpec as usize) } +} + +/// Open a file +pub fn open(path: &str, flags: usize) -> Result<usize> { + unsafe { syscall3(SYS_OPEN, path.as_ptr() as usize, path.len(), flags) } +} + +/// Allocate pages, linearly in physical memory +pub unsafe fn physalloc(size: usize) -> Result<usize> { + syscall1(SYS_PHYSALLOC, size) +} + +/// Free physically allocated pages +pub unsafe fn physfree(physical_address: usize, size: usize) -> Result<usize> { + syscall2(SYS_PHYSFREE, physical_address, size) +} + +/// Map physical memory to virtual memory +pub unsafe fn physmap(physical_address: usize, size: usize, flags: usize) -> Result<usize> { + syscall3(SYS_PHYSMAP, physical_address, size, flags) +} + +/// Unmap previously mapped physical memory +pub unsafe fn physunmap(virtual_address: usize) -> Result<usize> { + syscall1(SYS_PHYSUNMAP, virtual_address) +} + +/// Create a pair of file descriptors referencing the read and write ends of a pipe +pub fn pipe2(fds: &mut [usize; 2], flags: usize) -> Result<usize> { + unsafe { syscall2(SYS_PIPE2, fds.as_ptr() as usize, flags) } +} + +/// Read from a file descriptor into a buffer +pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> { + unsafe { syscall3(SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) } +} + +/// Remove a directory +pub fn rmdir(path: &str) -> Result<usize> { + unsafe { syscall2(SYS_RMDIR, path.as_ptr() as usize, path.len()) } +} + +/// Set the current process group IDs +pub fn setregid(rgid: usize, egid: usize) -> Result<usize> { + unsafe { syscall2(SYS_SETREGID, rgid, egid) } +} + +/// Make a new scheme namespace +pub fn setrens(rns: usize, ens: usize) -> Result<usize> { + unsafe { syscall2(SYS_SETRENS, rns, ens) } +} + +/// Set the current process user IDs +pub fn setreuid(ruid: usize, euid: usize) -> Result<usize> { + unsafe { syscall2(SYS_SETREUID, ruid, euid) } +} + +/// Remove a file +pub fn unlink(path: &str) -> Result<usize> { + unsafe { syscall2(SYS_UNLINK, path.as_ptr() as usize, path.len()) } +} + +/// Convert a virtual address to a physical one +pub unsafe fn virttophys(virtual_address: usize) -> Result<usize> { + syscall1(SYS_VIRTTOPHYS, virtual_address) +} + +/// Check if a child process has exited or received a signal +pub fn waitpid(pid: usize, status: &mut usize, options: usize) -> Result<usize> { + unsafe { syscall3(SYS_WAITPID, pid, status as *mut usize as usize, options) } +} + +/// Write a buffer to a file descriptor +/// +/// The kernel will attempt to write the bytes in `buf` to the file descriptor `fd`, returning +/// either an `Err`, explained below, or `Ok(count)` where `count` is the number of bytes which +/// were written. +/// +/// # Errors +/// +/// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block +/// * `EBADF` - the file descriptor is not valid or is not open for writing +/// * `EFAULT` - `buf` does not point to the process's addressible memory +/// * `EIO` - an I/O error occured +/// * `ENOSPC` - the device containing the file descriptor has no room for data +/// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed +pub fn write(fd: usize, buf: &[u8]) -> Result<usize> { + unsafe { syscall3(SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) } +} + +/// Yield the process's time slice to the kernel +/// +/// This function will return Ok(0) on success +pub fn sched_yield() -> Result<usize> { + unsafe { syscall0(SYS_YIELD) } +} diff --git a/src/libstd/sys/redox/syscall/data.rs b/src/libstd/sys/redox/syscall/data.rs new file mode 100644 index 00000000000..ac3946672a3 --- /dev/null +++ b/src/libstd/sys/redox/syscall/data.rs @@ -0,0 +1,86 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::ops::{Deref, DerefMut}; +use core::{mem, slice}; + +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct Stat { + pub st_dev: u64, + pub st_ino: u64, + pub st_mode: u16, + pub st_nlink: u32, + pub st_uid: u32, + pub st_gid: u32, + pub st_size: u64, + pub st_blksize: u32, + pub st_blocks: u64, + pub st_mtime: u64, + pub st_mtime_nsec: u32, + pub st_atime: u64, + pub st_atime_nsec: u32, + pub st_ctime: u64, + pub st_ctime_nsec: u32, +} + +impl Deref for Stat { + type Target = [u8]; + fn deref(&self) -> &[u8] { + unsafe { + slice::from_raw_parts(self as *const Stat as *const u8, + mem::size_of::<Stat>()) as &[u8] + } + } +} + +impl DerefMut for Stat { + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { + slice::from_raw_parts_mut(self as *mut Stat as *mut u8, + mem::size_of::<Stat>()) as &mut [u8] + } + } +} + +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct StatVfs { + pub f_bsize: u32, + pub f_blocks: u64, + pub f_bfree: u64, + pub f_bavail: u64, +} + +impl Deref for StatVfs { + type Target = [u8]; + fn deref(&self) -> &[u8] { + unsafe { + slice::from_raw_parts(self as *const StatVfs as *const u8, + mem::size_of::<StatVfs>()) as &[u8] + } + } +} + +impl DerefMut for StatVfs { + fn deref_mut(&mut self) -> &mut [u8] { + unsafe { + slice::from_raw_parts_mut(self as *mut StatVfs as *mut u8, + mem::size_of::<StatVfs>()) as &mut [u8] + } + } +} + +#[derive(Copy, Clone, Debug, Default)] +#[repr(packed)] +pub struct TimeSpec { + pub tv_sec: i64, + pub tv_nsec: i32, +} diff --git a/src/libstd/sys/redox/syscall/error.rs b/src/libstd/sys/redox/syscall/error.rs new file mode 100644 index 00000000000..d8d78d55016 --- /dev/null +++ b/src/libstd/sys/redox/syscall/error.rs @@ -0,0 +1,325 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::{fmt, result}; + +#[derive(Eq, PartialEq)] +pub struct Error { + pub errno: i32, +} + +pub type Result<T> = result::Result<T, Error>; + +impl Error { + pub fn new(errno: i32) -> Error { + Error { errno: errno } + } + + pub fn mux(result: Result<usize>) -> usize { + match result { + Ok(value) => value, + Err(error) => -error.errno as usize, + } + } + + pub fn demux(value: usize) -> Result<usize> { + let errno = -(value as i32); + if errno >= 1 && errno < STR_ERROR.len() as i32 { + Err(Error::new(errno)) + } else { + Ok(value) + } + } + + pub fn text(&self) -> &str { + if let Some(description) = STR_ERROR.get(self.errno as usize) { + description + } else { + "Unknown Error" + } + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + f.write_str(self.text()) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + f.write_str(self.text()) + } +} + +pub const EPERM: i32 = 1; /* Operation not permitted */ +pub const ENOENT: i32 = 2; /* No such file or directory */ +pub const ESRCH: i32 = 3; /* No such process */ +pub const EINTR: i32 = 4; /* Interrupted system call */ +pub const EIO: i32 = 5; /* I/O error */ +pub const ENXIO: i32 = 6; /* No such device or address */ +pub const E2BIG: i32 = 7; /* Argument list too long */ +pub const ENOEXEC: i32 = 8; /* Exec format error */ +pub const EBADF: i32 = 9; /* Bad file number */ +pub const ECHILD: i32 = 10; /* No child processes */ +pub const EAGAIN: i32 = 11; /* Try again */ +pub const ENOMEM: i32 = 12; /* Out of memory */ +pub const EACCES: i32 = 13; /* Permission denied */ +pub const EFAULT: i32 = 14; /* Bad address */ +pub const ENOTBLK: i32 = 15; /* Block device required */ +pub const EBUSY: i32 = 16; /* Device or resource busy */ +pub const EEXIST: i32 = 17; /* File exists */ +pub const EXDEV: i32 = 18; /* Cross-device link */ +pub const ENODEV: i32 = 19; /* No such device */ +pub const ENOTDIR: i32 = 20; /* Not a directory */ +pub const EISDIR: i32 = 21; /* Is a directory */ +pub const EINVAL: i32 = 22; /* Invalid argument */ +pub const ENFILE: i32 = 23; /* File table overflow */ +pub const EMFILE: i32 = 24; /* Too many open files */ +pub const ENOTTY: i32 = 25; /* Not a typewriter */ +pub const ETXTBSY: i32 = 26; /* Text file busy */ +pub const EFBIG: i32 = 27; /* File too large */ +pub const ENOSPC: i32 = 28; /* No space left on device */ +pub const ESPIPE: i32 = 29; /* Illegal seek */ +pub const EROFS: i32 = 30; /* Read-only file system */ +pub const EMLINK: i32 = 31; /* Too many links */ +pub const EPIPE: i32 = 32; /* Broken pipe */ +pub const EDOM: i32 = 33; /* Math argument out of domain of func */ +pub const ERANGE: i32 = 34; /* Math result not representable */ +pub const EDEADLK: i32 = 35; /* Resource deadlock would occur */ +pub const ENAMETOOLONG: i32 = 36; /* File name too long */ +pub const ENOLCK: i32 = 37; /* No record locks available */ +pub const ENOSYS: i32 = 38; /* Function not implemented */ +pub const ENOTEMPTY: i32 = 39; /* Directory not empty */ +pub const ELOOP: i32 = 40; /* Too many symbolic links encountered */ +pub const EWOULDBLOCK: i32 = 41; /* Operation would block */ +pub const ENOMSG: i32 = 42; /* No message of desired type */ +pub const EIDRM: i32 = 43; /* Identifier removed */ +pub const ECHRNG: i32 = 44; /* Channel number out of range */ +pub const EL2NSYNC: i32 = 45; /* Level 2 not synchronized */ +pub const EL3HLT: i32 = 46; /* Level 3 halted */ +pub const EL3RST: i32 = 47; /* Level 3 reset */ +pub const ELNRNG: i32 = 48; /* Link number out of range */ +pub const EUNATCH: i32 = 49; /* Protocol driver not attached */ +pub const ENOCSI: i32 = 50; /* No CSI structure available */ +pub const EL2HLT: i32 = 51; /* Level 2 halted */ +pub const EBADE: i32 = 52; /* Invalid exchange */ +pub const EBADR: i32 = 53; /* Invalid request descriptor */ +pub const EXFULL: i32 = 54; /* Exchange full */ +pub const ENOANO: i32 = 55; /* No anode */ +pub const EBADRQC: i32 = 56; /* Invalid request code */ +pub const EBADSLT: i32 = 57; /* Invalid slot */ +pub const EDEADLOCK: i32 = 58; /* Resource deadlock would occur */ +pub const EBFONT: i32 = 59; /* Bad font file format */ +pub const ENOSTR: i32 = 60; /* Device not a stream */ +pub const ENODATA: i32 = 61; /* No data available */ +pub const ETIME: i32 = 62; /* Timer expired */ +pub const ENOSR: i32 = 63; /* Out of streams resources */ +pub const ENONET: i32 = 64; /* Machine is not on the network */ +pub const ENOPKG: i32 = 65; /* Package not installed */ +pub const EREMOTE: i32 = 66; /* Object is remote */ +pub const ENOLINK: i32 = 67; /* Link has been severed */ +pub const EADV: i32 = 68; /* Advertise error */ +pub const ESRMNT: i32 = 69; /* Srmount error */ +pub const ECOMM: i32 = 70; /* Communication error on send */ +pub const EPROTO: i32 = 71; /* Protocol error */ +pub const EMULTIHOP: i32 = 72; /* Multihop attempted */ +pub const EDOTDOT: i32 = 73; /* RFS specific error */ +pub const EBADMSG: i32 = 74; /* Not a data message */ +pub const EOVERFLOW: i32 = 75; /* Value too large for defined data type */ +pub const ENOTUNIQ: i32 = 76; /* Name not unique on network */ +pub const EBADFD: i32 = 77; /* File descriptor in bad state */ +pub const EREMCHG: i32 = 78; /* Remote address changed */ +pub const ELIBACC: i32 = 79; /* Can not access a needed shared library */ +pub const ELIBBAD: i32 = 80; /* Accessing a corrupted shared library */ +pub const ELIBSCN: i32 = 81; /* .lib section in a.out corrupted */ +pub const ELIBMAX: i32 = 82; /* Attempting to link in too many shared libraries */ +pub const ELIBEXEC: i32 = 83; /* Cannot exec a shared library directly */ +pub const EILSEQ: i32 = 84; /* Illegal byte sequence */ +pub const ERESTART: i32 = 85; /* Interrupted system call should be restarted */ +pub const ESTRPIPE: i32 = 86; /* Streams pipe error */ +pub const EUSERS: i32 = 87; /* Too many users */ +pub const ENOTSOCK: i32 = 88; /* Socket operation on non-socket */ +pub const EDESTADDRREQ: i32 = 89; /* Destination address required */ +pub const EMSGSIZE: i32 = 90; /* Message too long */ +pub const EPROTOTYPE: i32 = 91; /* Protocol wrong type for socket */ +pub const ENOPROTOOPT: i32 = 92; /* Protocol not available */ +pub const EPROTONOSUPPORT: i32 = 93; /* Protocol not supported */ +pub const ESOCKTNOSUPPORT: i32 = 94; /* Socket type not supported */ +pub const EOPNOTSUPP: i32 = 95; /* Operation not supported on transport endpoint */ +pub const EPFNOSUPPORT: i32 = 96; /* Protocol family not supported */ +pub const EAFNOSUPPORT: i32 = 97; /* Address family not supported by protocol */ +pub const EADDRINUSE: i32 = 98; /* Address already in use */ +pub const EADDRNOTAVAIL: i32 = 99; /* Cannot assign requested address */ +pub const ENETDOWN: i32 = 100; /* Network is down */ +pub const ENETUNREACH: i32 = 101; /* Network is unreachable */ +pub const ENETRESET: i32 = 102; /* Network dropped connection because of reset */ +pub const ECONNABORTED: i32 = 103; /* Software caused connection abort */ +pub const ECONNRESET: i32 = 104; /* Connection reset by peer */ +pub const ENOBUFS: i32 = 105; /* No buffer space available */ +pub const EISCONN: i32 = 106; /* Transport endpoint is already connected */ +pub const ENOTCONN: i32 = 107; /* Transport endpoint is not connected */ +pub const ESHUTDOWN: i32 = 108; /* Cannot send after transport endpoint shutdown */ +pub const ETOOMANYREFS: i32 = 109; /* Too many references: cannot splice */ +pub const ETIMEDOUT: i32 = 110; /* Connection timed out */ +pub const ECONNREFUSED: i32 = 111; /* Connection refused */ +pub const EHOSTDOWN: i32 = 112; /* Host is down */ +pub const EHOSTUNREACH: i32 = 113; /* No route to host */ +pub const EALREADY: i32 = 114; /* Operation already in progress */ +pub const EINPROGRESS: i32 = 115; /* Operation now in progress */ +pub const ESTALE: i32 = 116; /* Stale NFS file handle */ +pub const EUCLEAN: i32 = 117; /* Structure needs cleaning */ +pub const ENOTNAM: i32 = 118; /* Not a XENIX named type file */ +pub const ENAVAIL: i32 = 119; /* No XENIX semaphores available */ +pub const EISNAM: i32 = 120; /* Is a named type file */ +pub const EREMOTEIO: i32 = 121; /* Remote I/O error */ +pub const EDQUOT: i32 = 122; /* Quota exceeded */ +pub const ENOMEDIUM: i32 = 123; /* No medium found */ +pub const EMEDIUMTYPE: i32 = 124; /* Wrong medium type */ +pub const ECANCELED: i32 = 125; /* Operation Canceled */ +pub const ENOKEY: i32 = 126; /* Required key not available */ +pub const EKEYEXPIRED: i32 = 127; /* Key has expired */ +pub const EKEYREVOKED: i32 = 128; /* Key has been revoked */ +pub const EKEYREJECTED: i32 = 129; /* Key was rejected by service */ +pub const EOWNERDEAD: i32 = 130; /* Owner died */ +pub const ENOTRECOVERABLE: i32 = 131; /* State not recoverable */ + +pub static STR_ERROR: [&'static str; 132] = ["Success", + "Operation not permitted", + "No such file or directory", + "No such process", + "Interrupted system call", + "I/O error", + "No such device or address", + "Argument list too long", + "Exec format error", + "Bad file number", + "No child processes", + "Try again", + "Out of memory", + "Permission denied", + "Bad address", + "Block device required", + "Device or resource busy", + "File exists", + "Cross-device link", + "No such device", + "Not a directory", + "Is a directory", + "Invalid argument", + "File table overflow", + "Too many open files", + "Not a typewriter", + "Text file busy", + "File too large", + "No space left on device", + "Illegal seek", + "Read-only file system", + "Too many links", + "Broken pipe", + "Math argument out of domain of func", + "Math result not representable", + "Resource deadlock would occur", + "File name too long", + "No record locks available", + "Function not implemented", + "Directory not empty", + "Too many symbolic links encountered", + "Operation would block", + "No message of desired type", + "Identifier removed", + "Channel number out of range", + "Level 2 not synchronized", + "Level 3 halted", + "Level 3 reset", + "Link number out of range", + "Protocol driver not attached", + "No CSI structure available", + "Level 2 halted", + "Invalid exchange", + "Invalid request descriptor", + "Exchange full", + "No anode", + "Invalid request code", + "Invalid slot", + "Resource deadlock would occur", + "Bad font file format", + "Device not a stream", + "No data available", + "Timer expired", + "Out of streams resources", + "Machine is not on the network", + "Package not installed", + "Object is remote", + "Link has been severed", + "Advertise error", + "Srmount error", + "Communication error on send", + "Protocol error", + "Multihop attempted", + "RFS specific error", + "Not a data message", + "Value too large for defined data type", + "Name not unique on network", + "File descriptor in bad state", + "Remote address changed", + "Can not access a needed shared library", + "Accessing a corrupted shared library", + ".lib section in a.out corrupted", + "Attempting to link in too many shared libraries", + "Cannot exec a shared library directly", + "Illegal byte sequence", + "Interrupted system call should be restarted", + "Streams pipe error", + "Too many users", + "Socket operation on non-socket", + "Destination address required", + "Message too long", + "Protocol wrong type for socket", + "Protocol not available", + "Protocol not supported", + "Socket type not supported", + "Operation not supported on transport endpoint", + "Protocol family not supported", + "Address family not supported by protocol", + "Address already in use", + "Cannot assign requested address", + "Network is down", + "Network is unreachable", + "Network dropped connection because of reset", + "Software caused connection abort", + "Connection reset by peer", + "No buffer space available", + "Transport endpoint is already connected", + "Transport endpoint is not connected", + "Cannot send after transport endpoint shutdown", + "Too many references: cannot splice", + "Connection timed out", + "Connection refused", + "Host is down", + "No route to host", + "Operation already in progress", + "Operation now in progress", + "Stale NFS file handle", + "Structure needs cleaning", + "Not a XENIX named type file", + "No XENIX semaphores available", + "Is a named type file", + "Remote I/O error", + "Quota exceeded", + "No medium found", + "Wrong medium type", + "Operation Canceled", + "Required key not available", + "Key has expired", + "Key has been revoked", + "Key was rejected by service", + "Owner died", + "State not recoverable"]; diff --git a/src/libstd/sys/redox/syscall/flag.rs b/src/libstd/sys/redox/syscall/flag.rs new file mode 100644 index 00000000000..9f0d3e6f779 --- /dev/null +++ b/src/libstd/sys/redox/syscall/flag.rs @@ -0,0 +1,94 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub const CLONE_VM: usize = 0x100; +pub const CLONE_FS: usize = 0x200; +pub const CLONE_FILES: usize = 0x400; +pub const CLONE_VFORK: usize = 0x4000; + +pub const CLOCK_REALTIME: usize = 1; +pub const CLOCK_MONOTONIC: usize = 4; + +pub const EVENT_NONE: usize = 0; +pub const EVENT_READ: usize = 1; +pub const EVENT_WRITE: usize = 2; + +pub const F_GETFL: usize = 1; +pub const F_SETFL: usize = 2; + +pub const FUTEX_WAIT: usize = 0; +pub const FUTEX_WAKE: usize = 1; +pub const FUTEX_REQUEUE: usize = 2; + +pub const MAP_WRITE: usize = 1; +pub const MAP_WRITE_COMBINE: usize = 2; + +pub const MODE_TYPE: u16 = 0xF000; +pub const MODE_DIR: u16 = 0x4000; +pub const MODE_FILE: u16 = 0x8000; + +pub const MODE_PERM: u16 = 0x0FFF; +pub const MODE_SETUID: u16 = 0o4000; +pub const MODE_SETGID: u16 = 0o2000; + +pub const O_RDONLY: usize = 0x0001_0000; +pub const O_WRONLY: usize = 0x0002_0000; +pub const O_RDWR: usize = 0x0003_0000; +pub const O_NONBLOCK: usize = 0x0004_0000; +pub const O_APPEND: usize = 0x0008_0000; +pub const O_SHLOCK: usize = 0x0010_0000; +pub const O_EXLOCK: usize = 0x0020_0000; +pub const O_ASYNC: usize = 0x0040_0000; +pub const O_FSYNC: usize = 0x0080_0000; +pub const O_CLOEXEC: usize = 0x0100_0000; +pub const O_CREAT: usize = 0x0200_0000; +pub const O_TRUNC: usize = 0x0400_0000; +pub const O_EXCL: usize = 0x0800_0000; +pub const O_DIRECTORY: usize = 0x1000_0000; +pub const O_STAT: usize = 0x2000_0000; +pub const O_ACCMODE: usize = O_RDONLY | O_WRONLY | O_RDWR; + +pub const SEEK_SET: usize = 0; +pub const SEEK_CUR: usize = 1; +pub const SEEK_END: usize = 2; + +pub const SIGHUP: usize = 1; +pub const SIGINT: usize = 2; +pub const SIGQUIT: usize = 3; +pub const SIGILL: usize = 4; +pub const SIGTRAP: usize = 5; +pub const SIGABRT: usize = 6; +pub const SIGBUS: usize = 7; +pub const SIGFPE: usize = 8; +pub const SIGKILL: usize = 9; +pub const SIGUSR1: usize = 10; +pub const SIGSEGV: usize = 11; +pub const SIGUSR2: usize = 12; +pub const SIGPIPE: usize = 13; +pub const SIGALRM: usize = 14; +pub const SIGTERM: usize = 15; +pub const SIGSTKFLT: usize= 16; +pub const SIGCHLD: usize = 17; +pub const SIGCONT: usize = 18; +pub const SIGSTOP: usize = 19; +pub const SIGTSTP: usize = 20; +pub const SIGTTIN: usize = 21; +pub const SIGTTOU: usize = 22; +pub const SIGURG: usize = 23; +pub const SIGXCPU: usize = 24; +pub const SIGXFSZ: usize = 25; +pub const SIGVTALRM: usize= 26; +pub const SIGPROF: usize = 27; +pub const SIGWINCH: usize = 28; +pub const SIGIO: usize = 29; +pub const SIGPWR: usize = 30; +pub const SIGSYS: usize = 31; + +pub const WNOHANG: usize = 1; diff --git a/src/libstd/sys/redox/syscall/mod.rs b/src/libstd/sys/redox/syscall/mod.rs new file mode 100644 index 00000000000..ce789c269a7 --- /dev/null +++ b/src/libstd/sys/redox/syscall/mod.rs @@ -0,0 +1,43 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use self::arch::*; +pub use self::call::*; +pub use self::data::*; +pub use self::error::*; +pub use self::flag::*; +pub use self::number::*; + +#[cfg(target_arch = "arm")] +#[path="arch/arm.rs"] +mod arch; + +#[cfg(target_arch = "x86")] +#[path="arch/x86.rs"] +mod arch; + +#[cfg(target_arch = "x86_64")] +#[path="arch/x86_64.rs"] +mod arch; + +/// Function definitions +pub mod call; + +/// Complex structures that are used for some system calls +pub mod data; + +/// All errors that can be generated by a system call +pub mod error; + +/// Flags used as an argument to many system calls +pub mod flag; + +/// Call numbers used by each system call +pub mod number; diff --git a/src/libstd/sys/redox/syscall/number.rs b/src/libstd/sys/redox/syscall/number.rs new file mode 100644 index 00000000000..358746cd20a --- /dev/null +++ b/src/libstd/sys/redox/syscall/number.rs @@ -0,0 +1,73 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub const SYS_CLASS: usize = 0xF000_0000; +pub const SYS_CLASS_PATH: usize=0x1000_0000; +pub const SYS_CLASS_FILE: usize=0x2000_0000; + +pub const SYS_ARG: usize = 0x0F00_0000; +pub const SYS_ARG_SLICE: usize =0x0100_0000; +pub const SYS_ARG_MSLICE: usize=0x0200_0000; +pub const SYS_ARG_PATH: usize = 0x0300_0000; + +pub const SYS_RET: usize = 0x00F0_0000; +pub const SYS_RET_FILE: usize = 0x0010_0000; + +pub const SYS_LINK: usize = SYS_CLASS_PATH | SYS_ARG_PATH | 9; +pub const SYS_OPEN: usize = SYS_CLASS_PATH | SYS_RET_FILE | 5; +pub const SYS_CHMOD: usize = SYS_CLASS_PATH | 15; +pub const SYS_RMDIR: usize = SYS_CLASS_PATH | 84; +pub const SYS_UNLINK: usize = SYS_CLASS_PATH | 10; + +pub const SYS_CLOSE: usize = SYS_CLASS_FILE | 6; +pub const SYS_DUP: usize = SYS_CLASS_FILE | SYS_RET_FILE | 41; +pub const SYS_READ: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 3; +pub const SYS_WRITE: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 4; +pub const SYS_LSEEK: usize = SYS_CLASS_FILE | 19; +pub const SYS_FCNTL: usize = SYS_CLASS_FILE | 55; +pub const SYS_FEVENT: usize = SYS_CLASS_FILE | 927; +pub const SYS_FMAP: usize = SYS_CLASS_FILE | 90; +pub const SYS_FUNMAP: usize = SYS_CLASS_FILE | 91; +pub const SYS_FPATH: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 928; +pub const SYS_FSTAT: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 28; +pub const SYS_FSTATVFS: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 100; +pub const SYS_FSYNC: usize = SYS_CLASS_FILE | 118; +pub const SYS_FTRUNCATE: usize =SYS_CLASS_FILE | 93; + +pub const SYS_BRK: usize = 45; +pub const SYS_CHDIR: usize = 12; +pub const SYS_CLOCK_GETTIME: usize = 265; +pub const SYS_CLONE: usize = 120; +pub const SYS_EXECVE: usize = 11; +pub const SYS_EXIT: usize = 1; +pub const SYS_FUTEX: usize = 240; +pub const SYS_GETCWD: usize = 183; +pub const SYS_GETEGID: usize = 202; +pub const SYS_GETENS: usize = 951; +pub const SYS_GETEUID: usize = 201; +pub const SYS_GETGID: usize = 200; +pub const SYS_GETNS: usize = 950; +pub const SYS_GETPID: usize = 20; +pub const SYS_GETUID: usize = 199; +pub const SYS_IOPL: usize = 110; +pub const SYS_KILL: usize = 37; +pub const SYS_MKNS: usize = 984; +pub const SYS_NANOSLEEP: usize =162; +pub const SYS_PHYSALLOC: usize =945; +pub const SYS_PHYSFREE: usize = 946; +pub const SYS_PHYSMAP: usize = 947; +pub const SYS_PHYSUNMAP: usize =948; +pub const SYS_VIRTTOPHYS: usize=949; +pub const SYS_PIPE2: usize = 331; +pub const SYS_SETREGID: usize = 204; +pub const SYS_SETRENS: usize = 952; +pub const SYS_SETREUID: usize = 203; +pub const SYS_WAITPID: usize = 7; +pub const SYS_YIELD: usize = 158; diff --git a/src/libstd/sys/redox/thread.rs b/src/libstd/sys/redox/thread.rs new file mode 100644 index 00000000000..b2c0e285f06 --- /dev/null +++ b/src/libstd/sys/redox/thread.rs @@ -0,0 +1,91 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use alloc::boxed::FnBox; +use ffi::CStr; +use io; +use mem; +use sys_common::thread::start_thread; +use sys::{cvt, syscall}; +use time::Duration; + +pub struct Thread { + id: usize, +} + +// 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 {} + +impl Thread { + pub unsafe fn new<'a>(_stack: usize, p: Box<FnBox() + 'a>) -> io::Result<Thread> { + let p = box p; + + let id = cvt(syscall::clone(syscall::CLONE_VM | syscall::CLONE_FS | syscall::CLONE_FILES))?; + if id == 0 { + start_thread(&*p as *const _ as *mut _); + let _ = syscall::exit(0); + panic!("thread failed to exit"); + } else { + mem::forget(p); + Ok(Thread { id: id }) + } + } + + pub fn yield_now() { + let ret = syscall::sched_yield().expect("failed to sched_yield"); + debug_assert_eq!(ret, 0); + } + + pub fn set_name(_name: &CStr) { + + } + + pub fn sleep(dur: Duration) { + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as i32; + + // If we're awoken with a signal then the return value will be -1 and + // nanosleep will fill in `ts` with the remaining time. + while secs > 0 || nsecs > 0 { + let req = syscall::TimeSpec { + tv_sec: secs as i64, + tv_nsec: nsecs, + }; + secs -= req.tv_sec as u64; + let mut rem = syscall::TimeSpec::default(); + if syscall::nanosleep(&req, &mut rem).is_err() { + secs += rem.tv_sec as u64; + nsecs = rem.tv_nsec; + } else { + nsecs = 0; + } + } + } + + pub fn join(self) { + let mut status = 0; + syscall::waitpid(self.id, &mut status, 0).unwrap(); + } + + pub fn id(&self) -> usize { self.id } + + pub fn into_id(self) -> usize { + let id = self.id; + mem::forget(self); + id + } +} + +pub mod guard { + pub unsafe fn current() -> Option<usize> { None } + pub unsafe fn init() -> Option<usize> { None } +} diff --git a/src/libstd/sys/redox/thread_local.rs b/src/libstd/sys/redox/thread_local.rs new file mode 100644 index 00000000000..abdd9ace795 --- /dev/null +++ b/src/libstd/sys/redox/thread_local.rs @@ -0,0 +1,66 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] // not used on all platforms + +use collections::BTreeMap; +use ptr; +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +pub type Key = usize; + +type Dtor = unsafe extern fn(*mut u8); + +static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT; + +static mut KEYS: *mut BTreeMap<Key, Option<Dtor>> = ptr::null_mut(); + +#[thread_local] +static mut LOCALS: *mut BTreeMap<Key, *mut u8> = ptr::null_mut(); + +unsafe fn keys() -> &'static mut BTreeMap<Key, Option<Dtor>> { + if KEYS == ptr::null_mut() { + KEYS = Box::into_raw(Box::new(BTreeMap::new())); + } + &mut *KEYS +} + +unsafe fn locals() -> &'static mut BTreeMap<Key, *mut u8> { + if LOCALS == ptr::null_mut() { + LOCALS = Box::into_raw(Box::new(BTreeMap::new())); + } + &mut *LOCALS +} + +#[inline] +pub unsafe fn create(dtor: Option<Dtor>) -> Key { + let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); + keys().insert(key, dtor); + key +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + if let Some(&entry) = locals().get(&key) { + entry + } else { + ptr::null_mut() + } +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + locals().insert(key, value); +} + +#[inline] +pub unsafe fn destroy(key: Key) { + keys().remove(&key); +} diff --git a/src/libstd/sys/redox/time.rs b/src/libstd/sys/redox/time.rs new file mode 100644 index 00000000000..dea406efe6c --- /dev/null +++ b/src/libstd/sys/redox/time.rs @@ -0,0 +1,198 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use cmp::Ordering; +use fmt; +use sys::{cvt, syscall}; +use time::Duration; + +const NSEC_PER_SEC: u64 = 1_000_000_000; + +#[derive(Copy, Clone)] +struct Timespec { + t: syscall::TimeSpec, +} + +impl Timespec { + 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 add_duration(&self, other: &Duration) -> Timespec { + let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64); + let mut secs = secs.expect("overflow when adding duration to time"); + + // 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).expect("overflow when adding \ + duration to time"); + } + Timespec { + t: syscall::TimeSpec { + tv_sec: secs as i64, + tv_nsec: nsec as i32, + }, + } + } + + fn sub_duration(&self, other: &Duration) -> Timespec { + let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64); + let mut secs = secs.expect("overflow when subtracting duration \ + from time"); + + // 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).expect("overflow when subtracting \ + duration from time"); + } + Timespec { + t: syscall::TimeSpec { + tv_sec: secs as i64, + tv_nsec: nsec as i32, + }, + } + } +} + +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) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Instant { + t: Timespec, +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct SystemTime { + t: Timespec, +} + +pub const UNIX_EPOCH: SystemTime = SystemTime { + t: Timespec { + t: syscall::TimeSpec { + tv_sec: 0, + tv_nsec: 0, + }, + }, +}; + +impl Instant { + pub fn now() -> Instant { + Instant { t: now(syscall::CLOCK_MONOTONIC) } + } + + pub fn sub_instant(&self, other: &Instant) -> Duration { + self.t.sub_timespec(&other.t).unwrap_or_else(|_| { + panic!("other was less than the current instant") + }) + } + + pub fn add_duration(&self, other: &Duration) -> Instant { + Instant { t: self.t.add_duration(other) } + } + + pub fn sub_duration(&self, other: &Duration) -> Instant { + Instant { t: self.t.sub_duration(other) } + } +} + +impl fmt::Debug for Instant { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Instant") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + SystemTime { t: now(syscall::CLOCK_REALTIME) } + } + + pub fn sub_time(&self, other: &SystemTime) + -> Result<Duration, Duration> { + self.t.sub_timespec(&other.t) + } + + pub fn add_duration(&self, other: &Duration) -> SystemTime { + SystemTime { t: self.t.add_duration(other) } + } + + pub fn sub_duration(&self, other: &Duration) -> SystemTime { + SystemTime { t: self.t.sub_duration(other) } + } +} + +impl From<syscall::TimeSpec> for SystemTime { + fn from(t: syscall::TimeSpec) -> SystemTime { + SystemTime { t: Timespec { t: t } } + } +} + +impl fmt::Debug for SystemTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SystemTime") + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) + .finish() + } +} + +pub type clock_t = usize; + +fn now(clock: clock_t) -> Timespec { + let mut t = Timespec { + t: syscall::TimeSpec { + tv_sec: 0, + tv_nsec: 0, + } + }; + cvt(syscall::clock_gettime(clock, &mut t.t)).unwrap(); + t +} diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index fcfab051588..900f463fa83 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -21,7 +21,7 @@ use sys_common::{FromInner, AsInner, AsInnerMut}; use sys::platform::fs::MetadataExt as UnixMetadataExt; /// Unix-specific extensions to `File` -#[unstable(feature = "file_offset", issue = "35918")] +#[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { /// Reads a number of bytes starting from a given offset. /// @@ -34,7 +34,7 @@ pub trait FileExt { /// /// Note that similar to `File::read`, it is not an error to return with a /// short read. - #[unstable(feature = "file_offset", issue = "35918")] + #[stable(feature = "file_offset", since = "1.15.0")] fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; /// Writes a number of bytes starting from a given offset. @@ -51,11 +51,11 @@ pub trait FileExt { /// /// Note that similar to `File::write`, it is not an error to return a /// short write. - #[unstable(feature = "file_offset", issue = "35918")] + #[stable(feature = "file_offset", since = "1.15.0")] fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>; } -#[unstable(feature = "file_offset", issue = "35918")] +#[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) diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs index b2483f4e209..1be9f11b92c 100644 --- a/src/libstd/sys/unix/ext/mod.rs +++ b/src/libstd/sys/unix/ext/mod.rs @@ -50,7 +50,7 @@ pub mod prelude { pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::fs::DirEntryExt; - #[doc(no_inline)] #[unstable(feature = "file_offset", issue = "35918")] + #[doc(no_inline)] #[stable(feature = "file_offset", since = "1.15.0")] pub use super::fs::FileExt; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::thread::JoinHandleExt; diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 80f53da1cef..1ba4a104e51 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -85,6 +85,21 @@ enum AddressKind<'a> { } /// 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 { @@ -121,6 +136,28 @@ impl SocketAddr { } /// Returns true if and only if the address is unnamed. + /// + /// # Examples + /// + /// A named address: + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let socket = UnixListener::bind("/tmp/sock").unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), false); + /// ``` + /// + /// An unnamed address: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound().unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), true); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn is_unnamed(&self) -> bool { if let AddressKind::Unnamed = self.address() { @@ -131,6 +168,29 @@ impl SocketAddr { } /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// use std::path::Path; + /// + /// let socket = UnixListener::bind("/tmp/sock").unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); + /// ``` + /// + /// Without a pathname: + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// let socket = UnixDatagram::unbound().unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), None); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn as_pathname(&self) -> Option<&Path> { if let AddressKind::Pathname(path) = self.address() { @@ -182,7 +242,7 @@ impl<'a> fmt::Display for AsciiEscaped<'a> { /// /// # Examples /// -/// ```rust,no_run +/// ```no_run /// use std::os::unix::net::UnixStream; /// use std::io::prelude::*; /// @@ -212,6 +272,20 @@ impl fmt::Debug for UnixStream { 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> { @@ -229,6 +303,20 @@ impl UnixStream { /// 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)?; @@ -241,18 +329,45 @@ impl UnixStream { /// object references. Both handles will read and write the same stream of /// data, and options set on one stream will be propogated to the other /// stream. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn try_clone(&self) -> io::Result<UnixStream> { self.0.duplicate().map(UnixStream) } /// Returns the socket address of the local half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) } /// Returns the socket address of the remote half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) @@ -260,9 +375,23 @@ impl UnixStream { /// Sets the read timeout for the socket. /// - /// If the provided value is `None`, then `read` calls will block - /// indefinitely. It is an error to pass the zero `Duration` to this + /// If the provided value is [`None`], then [`read()`] calls will block + /// indefinitely. It is an error to pass the zero [`Duration`] to this /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`read()`]: ../../../../std/io/trait.Read.html#tymethod.read + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// ``` #[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) @@ -270,33 +399,89 @@ impl UnixStream { /// Sets the write timeout for the socket. /// - /// If the provided value is `None`, then `write` calls will block - /// indefinitely. It is an error to pass the zero `Duration` to this + /// If the provided value is [`None`], then [`write()`] calls will block + /// indefinitely. It is an error to pass the zero [`Duration`] to this /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`read()`]: ../../../../std/io/trait.Write.html#tymethod.write + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_SNDTIMEO) } /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// assert_eq!(socket.read_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn read_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_RCVTIMEO) } /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::time::Duration; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_write_timeout(Some(Duration::new(1, 0))).expect("Couldn't set write timeout"); + /// assert_eq!(socket.write_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn write_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_SNDTIMEO) } /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// if let Ok(Some(err)) = socket.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() @@ -306,7 +491,19 @@ impl UnixStream { /// /// 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`). + /// (see the documentation of [`Shutdown`]). + /// + /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixStream; + /// use std::net::Shutdown; + /// + /// let socket = UnixStream::connect("/tmp/sock").unwrap(); + /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { self.0.shutdown(how) @@ -382,7 +579,7 @@ impl IntoRawFd for UnixStream { /// /// # Examples /// -/// ```rust,no_run +/// ```no_run /// use std::thread; /// use std::os::unix::net::{UnixStream, UnixListener}; /// @@ -405,9 +602,6 @@ impl IntoRawFd for UnixStream { /// } /// } /// } -/// -/// // close the listener socket -/// drop(listener); /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub struct UnixListener(Socket); @@ -426,6 +620,20 @@ impl fmt::Debug for UnixListener { 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> { @@ -445,8 +653,23 @@ impl UnixListener { /// 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 corersponding `UnixStream` and + /// is established. When established, the corersponding [`UnixStream`] and /// the remote peer's address will be returned. + /// + /// [`UnixStream`]: ../../../../std/os/unix/net/struct.UnixStream.html + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// match listener.accept() { + /// Ok((socket, addr)) => println!("Got a client: {:?}", addr), + /// Err(e) => println!("accept function failed: {:?}", e), + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() }; @@ -461,24 +684,66 @@ impl UnixListener { /// The returned `UnixListener` is a reference to the same socket that this /// object references. Both handles can be used to accept incoming /// connections and options set on one listener will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn try_clone(&self) -> io::Result<UnixListener> { self.0.duplicate().map(UnixListener) } /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) } /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixListener; + /// + /// let listener = UnixListener::bind("/tmp/sock").unwrap(); + /// + /// if let Ok(Some(err)) = listener.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() @@ -486,8 +751,35 @@ impl UnixListener { /// Returns an iterator over incoming connections. /// - /// The iterator will never return `None` and will also not yield the - /// peer's `SocketAddr` structure. + /// The iterator will never return [`None`] and will also not yield the + /// peer's [`SocketAddr`] structure. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`SocketAddr`]: struct.SocketAddr.html + /// + /// # Examples + /// + /// ```no_run + /// use std::thread; + /// use std::os::unix::net::{UnixStream, UnixListener}; + /// + /// fn handle_client(stream: UnixStream) { + /// // ... + /// } + /// + /// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// thread::spawn(|| handle_client(stream)); + /// } + /// Err(err) => { + /// break; + /// } + /// } + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn incoming<'a>(&'a self) -> Incoming<'a> { Incoming { listener: self } @@ -525,9 +817,36 @@ impl<'a> IntoIterator for &'a UnixListener { } } -/// An iterator over incoming connections to a `UnixListener`. +/// 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 /// -/// It will never return `None`. +/// ```no_run +/// use std::thread; +/// use std::os::unix::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// let listener = UnixListener::bind("/path/to/the/socket").unwrap(); +/// +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// break; +/// } +/// } +/// } +/// ``` #[derive(Debug)] #[stable(feature = "unix_socket", since = "1.10.0")] pub struct Incoming<'a> { @@ -551,7 +870,7 @@ impl<'a> Iterator for Incoming<'a> { /// /// # Examples /// -/// ```rust,no_run +/// ```no_run /// use std::os::unix::net::UnixDatagram; /// /// let socket = UnixDatagram::bind("/path/to/my/socket").unwrap(); @@ -580,6 +899,20 @@ impl fmt::Debug for UnixDatagram { 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> { @@ -596,6 +929,20 @@ impl UnixDatagram { } /// 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)?; @@ -605,6 +952,20 @@ impl UnixDatagram { /// Create an unnamed pair of connected sockets. /// /// Returns two `UnixDatagrams`s which are connected to each other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let (sock1, sock2) = match UnixDatagram::pair() { + /// Ok((sock1, sock2)) => (sock1, sock2), + /// Err(e) => { + /// println!("Couldn't unbound: {:?}", e); + /// return + /// } + /// }; + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_DGRAM)?; @@ -613,8 +974,27 @@ impl UnixDatagram { /// 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. + /// The [`send()`] method may be used to send data to the specified address. + /// [`recv()`] and [`recv_from()`] will only receive data from that address. + /// + /// [`send()`]: #method.send + /// [`recv()`]: #method.recv + /// [`recv_from()`]: #method.recv_from + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// match sock.connect("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return + /// } + /// }; + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { fn inner(d: &UnixDatagram, path: &Path) -> io::Result<()> { @@ -631,15 +1011,35 @@ impl UnixDatagram { /// Creates a new independently owned handle to the underlying socket. /// - /// The returned `UnixListener` is a reference to the same socket that this + /// 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 listener will affect the other. + /// connections and options set on one side will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap(); + /// + /// let sock_copy = sock.try_clone().expect("try_clone failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn try_clone(&self) -> io::Result<UnixDatagram> { self.0.duplicate().map(UnixDatagram) } /// Returns the address of this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap(); + /// + /// let addr = sock.local_addr().expect("Couldn't get local address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) @@ -647,7 +1047,20 @@ impl UnixDatagram { /// Returns the address of this socket's peer. /// - /// The `connect` method will connect the socket to a peer. + /// The [`connect()`] method will connect the socket to a peer. + /// + /// [`connect()`]: #method.connect + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.connect("/path/to/the/socket").unwrap(); + /// + /// let addr = sock.peer_addr().expect("Couldn't get peer address"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) @@ -657,6 +1070,19 @@ impl UnixDatagram { /// /// On success, returns the number of bytes read and the address from /// whence the data came. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// let mut buf = vec![0; 10]; + /// match sock.recv_from(buf.as_mut_slice()) { + /// Ok((size, sender)) => println!("received {} bytes from {:?}", size, sender), + /// Err(e) => println!("recv_from function failed: {:?}", e), + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { let mut count = 0; @@ -684,6 +1110,16 @@ impl UnixDatagram { /// Receives data from the socket. /// /// On success, returns the number of bytes read. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::bind("/path/to/the/socket").unwrap(); + /// let mut buf = vec![0; 10]; + /// sock.recv(buf.as_mut_slice()).expect("recv function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) @@ -692,6 +1128,15 @@ impl UnixDatagram { /// Sends data on the socket to the specified address. /// /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.send_to(b"omelette au fromage", "/some/sock").expect("send_to function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> { fn inner(d: &UnixDatagram, buf: &[u8], path: &Path) -> io::Result<usize> { @@ -716,6 +1161,16 @@ impl UnixDatagram { /// will return an error if the socket has not already been connected. /// /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.connect("/some/sock").expect("Couldn't connect"); + /// sock.send(b"omelette au fromage").expect("send_to function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn send(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) @@ -723,9 +1178,24 @@ impl UnixDatagram { /// Sets the read timeout for the socket. /// - /// If the provided value is `None`, then `recv` and `recv_from` calls will - /// block indefinitely. It is an error to pass the zero `Duration` to this + /// If the provided value is [`None`], then [`recv()`] and [`recv_from()`] calls will + /// block indefinitely. It is an error to pass the zero [`Duration`] to this /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`recv()`]: #method.recv + /// [`recv_from()`]: #method.recv_from + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed"); + /// ``` #[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) @@ -733,33 +1203,92 @@ impl UnixDatagram { /// Sets the write timeout for the socket. /// - /// If the provided value is `None`, then `send` and `send_to` calls will - /// block indefinitely. It is an error to pass the zero `Duration` to this + /// If the provided value is [`None`], then [`send()`] and [`send_to()`] calls will + /// block indefinitely. It is an error to pass the zero [`Duration`] to this /// method. + /// + /// [`None`]: ../../../../std/option/enum.Option.html#variant.None + /// [`send()`]: #method.send + /// [`send_to()`]: #method.send_to + /// [`Duration`]: ../../../../std/time/struct.Duration.html + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_SNDTIMEO) } /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_read_timeout(Some(Duration::new(1, 0))).expect("set_read_timeout function failed"); + /// assert_eq!(sock.read_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn read_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_RCVTIMEO) } /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// use std::time::Duration; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("set_write_timeout function failed"); + /// assert_eq!(sock.write_timeout().unwrap(), Some(Duration::new(1, 0))); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn write_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_SNDTIMEO) } /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ``` + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.set_nonblocking(true).expect("set_nonblocking function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// if let Ok(Some(err)) = sock.take_error() { + /// println!("Got error: {:?}", err); + /// } + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() @@ -769,7 +1298,17 @@ impl UnixDatagram { /// /// 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`). + /// (see the documentation of [`Shutdown`]). + /// + /// [`Shutdown`]: ../../../../std/net/enum.Shutdown.html + /// + /// ```no_run + /// use std::os::unix::net::UnixDatagram; + /// use std::net::Shutdown; + /// + /// let sock = UnixDatagram::unbound().unwrap(); + /// sock.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { self.0.shutdown(how) diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index 3a7c59d4e6d..585dcbb9a34 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -56,7 +56,7 @@ pub trait CommandExt { /// 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. - #[unstable(feature = "process_exec", issue = "31398")] + #[stable(feature = "process_exec", since = "1.15.0")] fn before_exec<F>(&mut self, f: F) -> &mut process::Command where F: FnMut() -> io::Result<()> + Send + Sync + 'static; diff --git a/src/libstd/sys/unix/fast_thread_local.rs b/src/libstd/sys/unix/fast_thread_local.rs index 0c625e7add9..f4f73646e1b 100644 --- a/src/libstd/sys/unix/fast_thread_local.rs +++ b/src/libstd/sys/unix/fast_thread_local.rs @@ -12,6 +12,7 @@ #![unstable(feature = "thread_local_internals", issue = "0")] use cell::{Cell, UnsafeCell}; +use fmt; use intrinsics; use ptr; @@ -24,6 +25,12 @@ pub struct Key<T> { dtor_running: Cell<bool>, } +impl<T> fmt::Debug for Key<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Key { .. }") + } +} + unsafe impl<T> ::marker::Sync for Key<T> { } impl<T> Key<T> { diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 61eb60da486..2384d959881 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -18,6 +18,7 @@ use sys::cvt; use sys_common::AsInner; use sys_common::io::read_to_end_uninitialized; +#[derive(Debug)] pub struct FileDesc { fd: c_int, } diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 9ee0458b5da..8b5c0c04276 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -66,7 +66,7 @@ pub struct DirEntry { name: Box<[u8]> } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct OpenOptions { // generic read: bool, @@ -86,6 +86,7 @@ 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 } impl FileAttr { diff --git a/src/libstd/sys/unix/process/magenta.rs b/src/libstd/sys/unix/process/magenta.rs index 319fbce35cd..2bb005be4ec 100644 --- a/src/libstd/sys/unix/process/magenta.rs +++ b/src/libstd/sys/unix/process/magenta.rs @@ -23,7 +23,6 @@ pub type mx_rights_t = u32; pub type mx_status_t = i32; pub type mx_size_t = usize; -pub type mx_ssize_t = isize; pub const MX_HANDLE_INVALID: mx_handle_t = 0; diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index 273341b1918..6d38b00b39e 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -43,6 +43,10 @@ impl Stdout { fd.into_raw(); ret } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } } impl Stderr { @@ -54,6 +58,10 @@ impl Stderr { fd.into_raw(); ret } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } } // FIXME: right now this raw stderr handle is used in a few places because @@ -63,7 +71,10 @@ impl io::Write for Stderr { fn write(&mut self, data: &[u8]) -> io::Result<usize> { Stderr::write(self, data) } - fn flush(&mut self) -> io::Result<()> { Ok(()) } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } } pub const EBADF_ERR: i32 = ::libc::EBADF as i32; diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 1a563127f7f..d1c404195bc 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -47,7 +47,9 @@ pub type CHAR = c_char; pub type HCRYPTPROV = LONG_PTR; pub type ULONG_PTR = c_ulonglong; pub type ULONG = c_ulong; +#[cfg(target_arch = "x86_64")] pub type ULONGLONG = u64; +#[cfg(target_arch = "x86_64")] pub type DWORDLONG = ULONGLONG; pub type LPBOOL = *mut BOOL; @@ -66,7 +68,6 @@ pub type LPVOID = *mut c_void; pub type LPWCH = *mut WCHAR; pub type LPWIN32_FIND_DATAW = *mut WIN32_FIND_DATAW; pub type LPWSADATA = *mut WSADATA; -pub type LPWSAPROTOCOLCHAIN = *mut WSAPROTOCOLCHAIN; pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO; pub type LPWSTR = *mut WCHAR; pub type LPFILETIME = *mut FILETIME; @@ -311,8 +312,6 @@ pub struct WSADATA { pub szSystemStatus: [u8; WSASYS_STATUS_LEN + 1], } -pub type WSAEVENT = HANDLE; - #[repr(C)] pub struct WSAPROTOCOL_INFO { pub dwServiceFlags1: DWORD, @@ -819,6 +818,16 @@ pub enum EXCEPTION_DISPOSITION { ExceptionCollidedUnwind } +#[repr(C)] +#[derive(Copy, Clone)] +pub struct CONSOLE_READCONSOLE_CONTROL { + pub nLength: ULONG, + pub nInitialChars: ULONG, + pub dwCtrlWakeupMask: ULONG, + pub dwControlKeyState: ULONG, +} +pub type PCONSOLE_READCONSOLE_CONTROL = *mut CONSOLE_READCONSOLE_CONTROL; + #[link(name = "ws2_32")] #[link(name = "userenv")] #[link(name = "shell32")] @@ -849,12 +858,11 @@ extern "system" { pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION); pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION); - // FIXME - pInputControl should be PCONSOLE_READCONSOLE_CONTROL pub fn ReadConsoleW(hConsoleInput: HANDLE, lpBuffer: LPVOID, nNumberOfCharsToRead: DWORD, lpNumberOfCharsRead: LPDWORD, - pInputControl: LPVOID) -> BOOL; + pInputControl: PCONSOLE_READCONSOLE_CONTROL) -> BOOL; pub fn WriteConsoleW(hConsoleOutput: HANDLE, lpBuffer: LPCVOID, diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index 1e2b8bf38fa..7fc04ad69d6 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -19,7 +19,7 @@ use sys; use sys_common::{AsInnerMut, AsInner}; /// Windows-specific extensions to `File` -#[unstable(feature = "file_offset", issue = "35918")] +#[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { /// Seeks to a given position and reads a number of bytes. /// @@ -35,7 +35,7 @@ pub trait FileExt { /// Note that similar to `File::read`, it is not an error to return with a /// short read. When returning from such a short read, the file pointer is /// still updated. - #[unstable(feature = "file_offset", issue = "35918")] + #[stable(feature = "file_offset", since = "1.15.0")] fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>; /// Seeks to a given position and writes a number of bytes. @@ -52,11 +52,11 @@ pub trait FileExt { /// Note that similar to `File::write`, it is not an error to return a /// short write. When returning from such a short write, the file pointer /// is still updated. - #[unstable(feature = "file_offset", issue = "35918")] + #[stable(feature = "file_offset", since = "1.15.0")] fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>; } -#[unstable(feature = "file_offset", issue = "35918")] +#[stable(feature = "file_offset", since = "1.15.0")] impl FileExt for fs::File { fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { self.as_inner().read_at(buf, offset) diff --git a/src/libstd/sys/windows/ext/mod.rs b/src/libstd/sys/windows/ext/mod.rs index 932bb5e9564..f12e50cc923 100644 --- a/src/libstd/sys/windows/ext/mod.rs +++ b/src/libstd/sys/windows/ext/mod.rs @@ -36,6 +36,6 @@ pub mod prelude { pub use super::ffi::{OsStrExt, OsStringExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::fs::{OpenOptionsExt, MetadataExt}; - #[doc(no_inline)] #[unstable(feature = "file_offset", issue = "35918")] + #[doc(no_inline)] #[stable(feature = "file_offset", since = "1.15.0")] pub use super::fs::FileExt; } diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 7d7d78bbd87..c410fcd1ee0 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -58,7 +58,7 @@ pub struct DirEntry { data: c::WIN32_FIND_DATAW, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct OpenOptions { // generic read: bool, @@ -79,6 +79,7 @@ pub struct OpenOptions { #[derive(Clone, PartialEq, Eq, Debug)] pub struct FilePermissions { attrs: c::DWORD } +#[derive(Debug)] pub struct DirBuilder; impl fmt::Debug for ReadDir { diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index 72788776ded..b1a57c349fb 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -111,19 +111,27 @@ impl Stdin { if utf8.position() as usize == utf8.get_ref().len() { let mut utf16 = vec![0u16; 0x1000]; let mut num = 0; + let mut input_control = readconsole_input_control(CTRL_Z_MASK); cvt(unsafe { c::ReadConsoleW(handle, utf16.as_mut_ptr() as c::LPVOID, utf16.len() as u32, &mut num, - ptr::null_mut()) + &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL) })?; utf16.truncate(num as usize); // FIXME: what to do about this data that has already been read? - let data = match String::from_utf16(&utf16) { + let mut data = match String::from_utf16(&utf16) { Ok(utf8) => utf8.into_bytes(), Err(..) => return Err(invalid_encoding()), }; + if let Output::Console(_) = self.handle { + if let Some(&last_byte) = data.last() { + if last_byte == CTRL_Z { + data.pop(); + } + } + } *utf8 = Cursor::new(data); } @@ -156,6 +164,10 @@ impl Stdout { pub fn write(&self, data: &[u8]) -> io::Result<usize> { write(&self.0, data) } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } } impl Stderr { @@ -166,6 +178,10 @@ impl Stderr { pub fn write(&self, data: &[u8]) -> io::Result<usize> { write(&self.0, data) } + + pub fn flush(&self) -> io::Result<()> { + Ok(()) + } } // FIXME: right now this raw stderr handle is used in a few places because @@ -175,7 +191,10 @@ impl io::Write for Stderr { fn write(&mut self, data: &[u8]) -> io::Result<usize> { Stderr::write(self, data) } - fn flush(&mut self) -> io::Result<()> { Ok(()) } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } } impl NoClose { @@ -206,6 +225,18 @@ fn invalid_encoding() -> io::Error { io::Error::new(io::ErrorKind::InvalidData, "text was not valid unicode") } +fn readconsole_input_control(wakeup_mask: c::ULONG) -> c::CONSOLE_READCONSOLE_CONTROL { + c::CONSOLE_READCONSOLE_CONTROL { + nLength: ::mem::size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as c::ULONG, + nInitialChars: 0, + dwCtrlWakeupMask: wakeup_mask, + dwControlKeyState: 0, + } +} + +const CTRL_Z: u8 = 0x1A; +const CTRL_Z_MASK: c::ULONG = 0x4000000; //1 << 0x1A + pub const EBADF_ERR: i32 = ::sys::c::ERROR_INVALID_HANDLE as i32; // The default buffer capacity is 64k, but apparently windows // doesn't like 64k reads on stdin. See #13304 for details, but the |
