Add supporting for vxWorks

r? @alexcrichton
This commit is contained in:
Baoshan Pang 2019-07-15 23:57:53 -07:00
parent 4a95e9704d
commit 4c0c0f6158
69 changed files with 8791 additions and 3 deletions

View file

@ -45,6 +45,8 @@ fn cc2ar(cc: &Path, target: &str) -> Option<PathBuf> {
Some(PathBuf::from("ar"))
} else if target.contains("openbsd") {
Some(PathBuf::from("ar"))
} else if target.contains("vxworks") {
Some(PathBuf::from("vx-ar"))
} else {
let parent = cc.parent().unwrap();
let file = cc.file_name().unwrap().to_str().unwrap();

View file

@ -0,0 +1,24 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.max_atomic_width = Some(128);
Ok(Target {
llvm_target: "aarch64-unknown-linux-gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
target_env: "gnu".to_string(),
data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(),
arch: "aarch64".to_string(),
target_os: "vxworks".to_string(),
target_vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
abi_blacklist: super::arm_base::abi_blacklist(),
target_mcount: "\u{1}_mcount".to_string(),
.. base
},
})
}

View file

@ -0,0 +1,32 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
// This target is for glibc Linux on ARMv7 without NEON or
// thumb-mode. See the thumbv7neon variant for enabling both.
pub fn target() -> TargetResult {
let base = super::vxworks_base::opts();
Ok(Target {
llvm_target: "armv7-unknown-linux-gnueabihf".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
arch: "arm".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
// Info about features at https://wiki.debian.org/ArmHardFloatPort
features: "+v7,+vfp3,+d16,+thumb2,-neon".to_string(),
cpu: "generic".to_string(),
max_atomic_width: Some(64),
abi_blacklist: super::arm_base::abi_blacklist(),
target_mcount: "\u{1}__gnu_mcount_nc".to_string(),
// tls_model: "local-exec".to_string(),
position_independent_executables: false,
.. base
}
})
}

View file

@ -0,0 +1,25 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.max_atomic_width = Some(64);
Ok(Target {
llvm_target: "arm-unknown-linux-gnueabi".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
arch: "arm".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
features: "+strict-align,+v6".to_string(),
abi_blacklist: super::arm_base::abi_blacklist(),
target_mcount: "\u{1}__gnu_mcount_nc".to_string(),
.. base
},
})
}

View file

@ -0,0 +1,32 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
// This target is for glibc Linux on ARMv7 without NEON or
// thumb-mode. See the thumbv7neon variant for enabling both.
pub fn target() -> TargetResult {
let base = super::vxworks_base::opts();
Ok(Target {
llvm_target: "armv7-unknown-linux-gnueabihf".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
arch: "arm".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
// Info about features at https://wiki.debian.org/ArmHardFloatPort
features: "+v7,+vfp3,+d16,+thumb2,-neon".to_string(),
cpu: "generic".to_string(),
max_atomic_width: Some(64),
abi_blacklist: super::arm_base::abi_blacklist(),
target_mcount: "\u{1}__gnu_mcount_nc".to_string(),
// tls_model: "local-exec".to_string(),
position_independent_executables: false,
.. base
}
})
}

View file

@ -0,0 +1,8 @@
use crate::spec::TargetResult;
pub fn target() -> TargetResult {
let mut base = super::i686_wrs_vxworks::target()?;
base.options.cpu = "pentium".to_string();
base.llvm_target = "i586-unknown-linux-gnu".to_string();
Ok(base)
}

View file

@ -0,0 +1,23 @@
use crate::spec::{LinkerFlavor, Target, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.cpu = "pentium4".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.stack_probes = true;
Ok(Target {
llvm_target: "i686-unknown-linux-gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
arch: "x86".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: base,
})
}

View file

@ -0,0 +1,23 @@
use crate::spec::{LinkerFlavor, Target, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.cpu = "pentium4".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.stack_probes = true;
Ok(Target {
llvm_target: "i686-unknown-linux-gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128".to_string(),
arch: "x86".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: base,
})
}

View file

@ -66,6 +66,7 @@ mod fuchsia_base;
mod redox_base;
mod riscv_base;
mod wasm32_base;
mod vxworks_base;
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash,
RustcEncodable, RustcDecodable)]
@ -361,7 +362,6 @@ supported_targets! {
("thumbv7neon-unknown-linux-gnueabihf", thumbv7neon_unknown_linux_gnueabihf),
("armv7-unknown-linux-musleabihf", armv7_unknown_linux_musleabihf),
("aarch64-unknown-linux-gnu", aarch64_unknown_linux_gnu),
("aarch64-unknown-linux-musl", aarch64_unknown_linux_musl),
("x86_64-unknown-linux-musl", x86_64_unknown_linux_musl),
("i686-unknown-linux-musl", i686_unknown_linux_musl),
@ -476,6 +476,15 @@ supported_targets! {
("x86_64-unknown-uefi", x86_64_unknown_uefi),
("nvptx64-nvidia-cuda", nvptx64_nvidia_cuda),
("x86_64-wrs-vxworks", x86_64_wrs_vxworks),
("i686-wrs-vxworks", i686_wrs_vxworks),
("i586-wrs-vxworks", i586_wrs_vxworks),
("armv7-wrs-vxworks", armv7_wrs_vxworks),
("aarch64-wrs-vxworks", aarch64_wrs_vxworks),
("powerpc-wrs-vxworks", powerpc_wrs_vxworks),
("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe),
("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks),
}
/// Everything `rustc` knows about how to compile for a specific target.

View file

@ -0,0 +1,25 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.cpu = "ppc64".to_string();
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string());
base.max_atomic_width = Some(64);
Ok(Target {
llvm_target: "powerpc64-unknown-linux-gnu".to_string(),
target_endian: "big".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "E-m:e-i64:64-n32:64".to_string(),
arch: "powerpc64".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
target_mcount: "_mcount".to_string(),
.. base
},
})
}

View file

@ -0,0 +1,26 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.cpu = "ppc64".to_string();
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string());
base.max_atomic_width = Some(64);
Ok(Target {
llvm_target: "powerpc64-unknown-linux-gnu".to_string(),
target_endian: "big".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "E-m:e-i64:64-n32:64".to_string(),
arch: "powerpc64".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "unknown".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
features: "-hard-float".to_string(),
target_mcount: "_mcount".to_string(),
.. base
},
})
}

View file

@ -0,0 +1,26 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string());
base.max_atomic_width = Some(32);
Ok(Target {
llvm_target: "powerpc-unknown-linux-gnu".to_string(),
target_endian: "big".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(),
arch: "powerpc".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "wrs".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
features: "+secure-plt".to_string(),
target_mcount: "_mcount".to_string(),
.. base
},
})
}

View file

@ -0,0 +1,26 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string());
base.max_atomic_width = Some(32);
Ok(Target {
llvm_target: "powerpc-unknown-linux-gnu".to_string(),
target_endian: "big".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(),
arch: "powerpc".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "wrs".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
features: "+secure-plt,-hard-float".to_string(),
target_mcount: "_mcount".to_string(),
.. base
},
})
}

View file

@ -0,0 +1,27 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mspe".to_string());
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string());
base.max_atomic_width = Some(32);
Ok(Target {
llvm_target: "powerpc-unknown-linux-gnuspe".to_string(),
target_endian: "big".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(),
arch: "powerpc".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "wrs".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
// feature msync would disable instruction 'fsync' which is not supported by fsl_p1p2
features: "+secure-plt,+msync,-hard-float".to_string(),
target_mcount: "_mcount".to_string(),
.. base
},
})
}

View file

@ -0,0 +1,27 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mspe".to_string());
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string());
base.max_atomic_width = Some(32);
Ok(Target {
llvm_target: "powerpc-unknown-linux-gnuspe".to_string(),
target_endian: "big".to_string(),
target_pointer_width: "32".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(),
arch: "powerpc".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "wrs".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: TargetOptions {
// feature msync would disable instruction 'fsync' which is not supported by fsl_p1p2
features: "+secure-plt,+msync".to_string(),
target_mcount: "_mcount".to_string(),
.. base
},
})
}

View file

@ -0,0 +1,41 @@
use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions, RelroLevel};
use std::default::Default;
pub fn opts() -> TargetOptions {
let mut args = LinkArgs::new();
args.insert(LinkerFlavor::Gcc, vec![
// We want to be able to strip as much executable code as possible
// from the linker command line, and this flag indicates to the
// linker that it can avoid linking in dynamic libraries that don't
// actually satisfy any symbols up to that point (as with many other
// resolutions the linker does). This option only applies to all
// following libraries so we're sure to pass it as one of the first
// arguments.
"-Wl,--as-needed".to_string(),
// Always enable NX protection when it is available
"-Wl,-z,noexecstack".to_string(),
]);
let mut late_lk_args = LinkArgs::new();
late_lk_args.insert(LinkerFlavor::Gcc, vec![
"-lnet".to_string(),
"-lunix".to_string(),
]);
TargetOptions {
linker: Some("vx-cxx".to_string()),
exe_suffix: ".vxe".to_string(),
late_link_args: late_lk_args,
dynamic_linking: true,
executables: true,
target_family: Some("unix".to_string()),
linker_is_gnu: true,
has_rpath: true,
pre_link_args: args,
position_independent_executables: true,
relro_level: RelroLevel::Full,
has_elf_tls: true,
.. Default::default()
}
}

View file

@ -0,0 +1,23 @@
use crate::spec::{LinkerFlavor, Target, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::vxworks_base::opts();
base.cpu = "x86-64".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string());
base.stack_probes = true;
Ok(Target {
llvm_target: "x86_64-unknown-linux-gnu".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
target_c_int_width: "32".to_string(),
data_layout: "e-m:e-i64:64-f80:128-n8:16:32:64-S128".to_string(),
arch: "x86_64".to_string(),
target_os: "vxworks".to_string(),
target_env: "gnu".to_string(),
target_vendor: "wrs".to_string(),
linker_flavor: LinkerFlavor::Gcc,
options: base,
})
}

View file

@ -24,7 +24,7 @@ cfg_if::cfg_if! {
// If we're not documenting libstd then we just expose the main modules
// as we otherwise would.
#[cfg(any(target_os = "redox", unix))]
#[cfg(any(target_os = "redox", unix, target_os = "vxworks"))]
#[stable(feature = "rust1", since = "1.0.0")]
pub use crate::sys::ext as unix;
@ -51,6 +51,7 @@ cfg_if::cfg_if! {
#[cfg(target_os = "fuchsia")] pub mod fuchsia;
#[cfg(target_os = "hermit")] pub mod hermit;
#[cfg(target_os = "wasi")] pub mod wasi;
#[cfg(target_os = "vxworks")] pub mod vxworks;
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] pub mod fortanix_sgx;
pub mod raw;

View file

@ -25,6 +25,10 @@
target_arch = "arm",
target_arch = "powerpc")),
all(target_os = "openbsd", target_arch = "aarch64"),
all(target_os = "vxworks", any(target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "powerpc")),
all(target_os = "fuchsia", target_arch = "aarch64")))]
#[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = u8;
#[doc(include = "os/raw/char.md")]
@ -44,6 +48,10 @@
target_arch = "arm",
target_arch = "powerpc")),
all(target_os = "openbsd", target_arch = "aarch64"),
all(target_os = "vxworks", any(target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
target_arch = "powerpc")),
all(target_os = "fuchsia", target_arch = "aarch64"))))]
#[stable(feature = "raw_os", since = "1.1.0")] pub type c_char = i8;
#[doc(include = "os/raw/schar.md")]

View file

@ -0,0 +1,84 @@
#![stable(feature = "metadata_ext", since = "1.1.0")]
use crate::fs::Metadata;
use crate::sys_common::AsInner;
///
/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
#[stable(feature = "metadata_ext", since = "1.1.0")]
pub trait MetadataExt {
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_dev(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_ino(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_mode(&self) -> u32;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_nlink(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_uid(&self) -> u32;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_gid(&self) -> u32;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_rdev(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_size(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_atime(&self) -> i64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_mtime(&self) -> i64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_ctime(&self) -> i64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_blksize(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_blocks(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_attrib(&self) -> u8;
}
#[stable(feature = "metadata_ext", since = "1.1.0")]
impl MetadataExt for Metadata {
fn st_dev(&self) -> u64 {
self.as_inner().as_inner().st_dev as u64
}
fn st_ino(&self) -> u64 {
self.as_inner().as_inner().st_ino as u64
}
fn st_mode(&self) -> u32 {
self.as_inner().as_inner().st_mode as u32
}
fn st_nlink(&self) -> u64 {
self.as_inner().as_inner().st_nlink as u64
}
fn st_uid(&self) -> u32 {
self.as_inner().as_inner().st_uid as u32
}
fn st_gid(&self) -> u32 {
self.as_inner().as_inner().st_gid as u32
}
fn st_rdev(&self) -> u64 {
self.as_inner().as_inner().st_rdev as u64
}
fn st_size(&self) -> u64 {
self.as_inner().as_inner().st_size as u64
}
fn st_atime(&self) -> i64 {
self.as_inner().as_inner().st_atime as i64
}
fn st_mtime(&self) -> i64 {
self.as_inner().as_inner().st_mtime as i64
}
fn st_ctime(&self) -> i64 {
self.as_inner().as_inner().st_ctime as i64
}
fn st_blksize(&self) -> u64 {
self.as_inner().as_inner().st_blksize as u64
}
fn st_blocks(&self) -> u64 {
self.as_inner().as_inner().st_blocks as u64
}
fn st_attrib(&self) -> u8 {
self.as_inner().as_inner().st_attrib as u8
}
}

View file

@ -0,0 +1,6 @@
//! VxWorks-specific definitions
#![stable(feature = "raw_ext", since = "1.1.0")]
pub mod raw;
pub mod fs;

View file

@ -0,0 +1,7 @@
//! VxWorks-specific raw type definitions
#![stable(feature = "metadata_ext", since = "1.1.0")]
use crate::os::raw::{c_ulong};
#[stable(feature = "pthread_t", since = "1.8.0")]
pub type pthread_t = c_ulong;

View file

@ -23,7 +23,10 @@
#![allow(missing_debug_implementations)]
cfg_if::cfg_if! {
if #[cfg(unix)] {
if #[cfg(target_os = "vxworks")] {
mod vxworks;
pub use self::vxworks::*;
} else if #[cfg(unix)] {
mod unix;
pub use self::unix::*;
} else if #[cfg(windows)] {

View file

@ -0,0 +1,83 @@
use crate::ptr;
use crate::sys_common::alloc::{MIN_ALIGN, realloc_fallback};
use crate::alloc::{GlobalAlloc, Layout, System};
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
libc::malloc(layout.size()) as *mut u8
} else {
aligned_malloc(&layout)
}
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
libc::calloc(layout.size(), 1) as *mut u8
} else {
let ptr = self.alloc(layout.clone());
if !ptr.is_null() {
ptr::write_bytes(ptr, 0, layout.size());
}
ptr
}
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
libc::free(ptr as *mut libc::c_void)
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
} else {
realloc_fallback(self, ptr, layout, new_size)
}
}
}
#[cfg(any(target_os = "android",
target_os = "hermit",
target_os = "redox",
target_os = "solaris"))]
#[inline]
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
// On android we currently target API level 9 which unfortunately
// doesn't have the `posix_memalign` API used below. Instead we use
// `memalign`, but this unfortunately has the property on some systems
// where the memory returned cannot be deallocated by `free`!
//
// Upon closer inspection, however, this appears to work just fine with
// Android, so for this platform we should be fine to call `memalign`
// (which is present in API level 9). Some helpful references could
// possibly be chromium using memalign [1], attempts at documenting that
// memalign + free is ok [2] [3], or the current source of chromium
// which still uses memalign on android [4].
//
// [1]: https://codereview.chromium.org/10796020/
// [2]: https://code.google.com/p/android/issues/detail?id=35391
// [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
// [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
// /memory/aligned_memory.cc
libc::memalign(layout.align(), layout.size()) as *mut u8
}
#[cfg(not(any(target_os = "android",
target_os = "hermit",
target_os = "redox",
target_os = "solaris")))]
#[inline]
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
let mut out = ptr::null_mut();
let ret = libc::posix_memalign(&mut out, layout.align(), layout.size());
if ret != 0 {
ptr::null_mut()
} else {
out as *mut u8
}
}

View file

@ -0,0 +1,160 @@
//! Android ABI-compatibility module
//!
//! The ABI of Android has changed quite a bit over time, and libstd attempts to
//! be both forwards and backwards compatible as much as possible. We want to
//! always work with the most recent version of Android, but we also want to
//! work with older versions of Android for whenever projects need to.
//!
//! Our current minimum supported Android version is `android-9`, e.g., Android
//! with API level 9. We then in theory want to work on that and all future
//! versions of Android!
//!
//! Some of the detection here is done at runtime via `dlopen` and
//! introspection. Other times no detection is performed at all and we just
//! provide a fallback implementation as some versions of Android we support
//! don't have the function.
//!
//! You'll find more details below about why each compatibility shim is needed.
#![cfg(target_os = "android")]
use libc::{c_int, c_void, sighandler_t, size_t, ssize_t};
use libc::{ftruncate, pread, pwrite};
use crate::io;
use super::{cvt, cvt_r};
// The `log2` and `log2f` functions apparently appeared in android-18, or at
// least you can see they're not present in the android-17 header [1] and they
// are present in android-18 [2].
//
// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
// /android-17/arch-arm/usr/include/math.h
// [2]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
// /android-18/arch-arm/usr/include/math.h
//
// Note that these shims are likely less precise than directly calling `log2`,
// but hopefully that should be enough for now...
//
// Note that mathematically, for any arbitrary `y`:
//
// log_2(x) = log_y(x) / log_y(2)
// = log_y(x) / (1 / log_2(y))
// = log_y(x) * log_2(y)
//
// Hence because `ln` (log_e) is available on all Android we just choose `y = e`
// and get:
//
// log_2(x) = ln(x) * log_2(e)
#[cfg(not(test))]
pub fn log2f32(f: f32) -> f32 {
f.ln() * crate::f32::consts::LOG2_E
}
#[cfg(not(test))]
pub fn log2f64(f: f64) -> f64 {
f.ln() * crate::f64::consts::LOG2_E
}
// Back in the day [1] the `signal` function was just an inline wrapper
// around `bsd_signal`, but starting in API level android-20 the `signal`
// symbols was introduced [2]. Finally, in android-21 the API `bsd_signal` was
// removed [3].
//
// Basically this means that if we want to be binary compatible with multiple
// Android releases (oldest being 9 and newest being 21) then we need to check
// for both symbols and not actually link against either.
//
// [1]: https://chromium.googlesource.com/android_tools/+/20ee6d20/ndk/platforms
// /android-18/arch-arm/usr/include/signal.h
// [2]: https://chromium.googlesource.com/android_tools/+/fbd420/ndk_experimental
// /platforms/android-20/arch-arm
// /usr/include/signal.h
// [3]: https://chromium.googlesource.com/android_tools/+/20ee6d/ndk/platforms
// /android-21/arch-arm/usr/include/signal.h
pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t {
weak!(fn signal(c_int, sighandler_t) -> sighandler_t);
weak!(fn bsd_signal(c_int, sighandler_t) -> sighandler_t);
let f = signal.get().or_else(|| bsd_signal.get());
let f = f.expect("neither `signal` nor `bsd_signal` symbols found");
f(signum, handler)
}
// The `ftruncate64` symbol apparently appeared in android-12, so we do some
// dynamic detection to see if we can figure out whether `ftruncate64` exists.
//
// If it doesn't we just fall back to `ftruncate`, generating an error for
// too-large values.
#[cfg(target_pointer_width = "32")]
pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
weak!(fn ftruncate64(c_int, i64) -> c_int);
unsafe {
match ftruncate64.get() {
Some(f) => cvt_r(|| f(fd, size as i64)).map(|_| ()),
None => {
if size > i32::max_value() as u64 {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"cannot truncate >2GB"))
} else {
cvt_r(|| ftruncate(fd, size as i32)).map(|_| ())
}
}
}
}
}
#[cfg(target_pointer_width = "64")]
pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
unsafe {
cvt_r(|| ftruncate(fd, size as i64)).map(|_| ())
}
}
#[cfg(target_pointer_width = "32")]
pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64)
-> io::Result<ssize_t>
{
use crate::convert::TryInto;
weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t);
pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
if let Ok(o) = offset.try_into() {
cvt(pread(fd, buf, count, o))
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"cannot pread >2GB"))
}
})
}
#[cfg(target_pointer_width = "32")]
pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64)
-> io::Result<ssize_t>
{
use crate::convert::TryInto;
weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t);
pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
if let Ok(o) = offset.try_into() {
cvt(pwrite(fd, buf, count, o))
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"cannot pwrite >2GB"))
}
})
}
#[cfg(target_pointer_width = "64")]
pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64)
-> io::Result<ssize_t>
{
cvt(pread(fd, buf, count, offset))
}
#[cfg(target_pointer_width = "64")]
pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64)
-> io::Result<ssize_t>
{
cvt(pwrite(fd, buf, count, offset))
}

View file

@ -0,0 +1,85 @@
#![allow(dead_code)] // runtime init functions not used during testing
use crate::ffi::OsString;
use crate::marker::PhantomData;
use crate::vec;
/// One-time global initialization.
pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv) }
/// One-time global cleanup.
pub unsafe fn cleanup() { imp::cleanup() }
/// Returns the command line arguments
pub fn args() -> Args {
imp::args()
}
pub struct Args {
iter: vec::IntoIter<OsString>,
_dont_send_or_sync_me: PhantomData<*mut ()>,
}
impl Args {
pub fn inner_debug(&self) -> &[OsString] {
self.iter.as_slice()
}
}
impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> { self.iter.next() }
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}
impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.iter.len() }
}
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() }
}
mod imp {
use crate::ptr;
use crate::ffi::{CStr, OsString};
use crate::marker::PhantomData;
use libc;
use super::Args;
use crate::sys_common::mutex::Mutex;
static mut ARGC: isize = 0;
static mut ARGV: *const *const u8 = ptr::null();
static LOCK: Mutex = Mutex::new();
pub unsafe fn init(argc: isize, argv: *const *const u8) {
let _guard = LOCK.lock();
ARGC = argc;
ARGV = argv;
}
pub unsafe fn cleanup() {
let _guard = LOCK.lock();
ARGC = 0;
ARGV = ptr::null();
}
pub fn args() -> Args {
Args {
iter: clone().into_iter(),
_dont_send_or_sync_me: PhantomData
}
}
fn clone() -> Vec<OsString> {
unsafe {
let _guard = LOCK.lock();
let ret = (0..ARGC).map(|i| {
let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char);
use crate::sys::vxworks::ext::ffi::OsStringExt;
OsStringExt::from_vec(cstr.to_bytes().to_vec())
}).collect();
return ret
}
}
}

View file

@ -0,0 +1,110 @@
/// Backtrace support built on libgcc with some extra OS-specific support
///
/// Some methods of getting a backtrace:
///
/// * The backtrace() functions on unix. It turns out this doesn't work very
/// well for green threads on macOS, and the address to symbol portion of it
/// suffers problems that are described below.
///
/// * Using libunwind. This is more difficult than it sounds because libunwind
/// isn't installed everywhere by default. It's also a bit of a hefty library,
/// so possibly not the best option. When testing, libunwind was excellent at
/// getting both accurate backtraces and accurate symbols across platforms.
/// This route was not chosen in favor of the next option, however.
///
/// * We're already using libgcc_s for exceptions in rust (triggering thread
/// unwinding and running destructors on the stack), and it turns out that it
/// conveniently comes with a function that also gives us a backtrace. All of
/// these functions look like _Unwind_*, but it's not quite the full
/// repertoire of the libunwind API. Due to it already being in use, this was
/// the chosen route of getting a backtrace.
///
/// After choosing libgcc_s for backtraces, the sad part is that it will only
/// give us a stack trace of instruction pointers. Thankfully these instruction
/// pointers are accurate (they work for green and native threads), but it's
/// then up to us again to figure out how to translate these addresses to
/// symbols. As with before, we have a few options. Before, that, a little bit
/// of an interlude about symbols. This is my very limited knowledge about
/// symbol tables, and this information is likely slightly wrong, but the
/// general idea should be correct.
///
/// When talking about symbols, it's helpful to know a few things about where
/// symbols are located. Some symbols are located in the dynamic symbol table
/// of the executable which in theory means that they're available for dynamic
/// linking and lookup. Other symbols end up only in the local symbol table of
/// the file. This loosely corresponds to pub and priv functions in Rust.
///
/// Armed with this knowledge, we know that our solution for address to symbol
/// translation will need to consult both the local and dynamic symbol tables.
/// With that in mind, here's our options of translating an address to
/// a symbol.
///
/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
/// behind the scenes to translate, and this is why backtrace() was not used.
/// Conveniently, this method works fantastically on macOS. It appears dladdr()
/// uses magic to consult the local symbol table, or we're putting everything
/// in the dynamic symbol table anyway. Regardless, for macOS, this is the
/// method used for translation. It's provided by the system and easy to do.o
///
/// Sadly, all other systems have a dladdr() implementation that does not
/// consult the local symbol table. This means that most functions are blank
/// because they don't have symbols. This means that we need another solution.
///
/// * Use unw_get_proc_name(). This is part of the libunwind api (not the
/// libgcc_s version of the libunwind api), but involves taking a dependency
/// to libunwind. We may pursue this route in the future if we bundle
/// libunwind, but libunwind was unwieldy enough that it was not chosen at
/// this time to provide this functionality.
///
/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
/// semi-reasonable solution. The stdlib already knows how to spawn processes,
/// so in theory it could invoke readelf, parse the output, and consult the
/// local/dynamic symbol tables from there. This ended up not getting chosen
/// due to the craziness of the idea plus the advent of the next option.
///
/// * Use `libbacktrace`. It turns out that this is a small library bundled in
/// the gcc repository which provides backtrace and symbol translation
/// functionality. All we really need from it is the backtrace functionality,
/// and we only really need this on everything that's not macOS, so this is the
/// chosen route for now.
///
/// In summary, the current situation uses libgcc_s to get a trace of stack
/// pointers, and we use dladdr() or libbacktrace to translate these addresses
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
/// all unix platforms we support right now, so it at least gets the job done.
pub use self::tracing::unwind_backtrace;
pub use self::printing::{foreach_symbol_fileline, resolve_symname};
// tracing impls:
mod tracing;
// symbol resolvers:
mod printing;
#[cfg(not(target_os = "emscripten"))]
pub mod gnu {
use crate::io;
use crate::fs;
use libc::c_char;
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn get_executable_filename() -> io::Result<(Vec<c_char>, fs::File)> {
use crate::env;
use crate::os::unix::ffi::OsStrExt;
let filename = env::current_exe()?;
let file = fs::File::open(&filename)?;
let mut filename_cstr: Vec<_> = filename.as_os_str().as_bytes().iter()
.map(|&x| x as c_char).collect();
filename_cstr.push(0); // Null terminate
Ok((filename_cstr, file))
}
}
pub struct BacktraceContext;

View file

@ -0,0 +1,35 @@
use crate::io;
use crate::intrinsics;
use crate::ffi::CStr;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
pub fn resolve_symname<F>(frame: Frame,
callback: F,
_: &BacktraceContext) -> io::Result<()>
where F: FnOnce(Option<&str>) -> io::Result<()>
{
unsafe {
let mut info: Dl_info = intrinsics::init();
let symname = if dladdr(frame.exact_position as *mut _, &mut info) == 0 ||
info.dli_sname.is_null() {
None
} else {
CStr::from_ptr(info.dli_sname).to_str().ok()
};
callback(symname)
}
}
#[repr(C)]
struct Dl_info {
dli_fname: *const libc::c_char,
dli_fbase: *mut libc::c_void,
dli_sname: *const libc::c_char,
dli_saddr: *mut libc::c_void,
}
extern {
#[ link_name = "_rtld_dladdr" ]
fn dladdr(addr: *const libc::c_void, info: *mut Dl_info) -> libc::c_int;
}

View file

@ -0,0 +1,33 @@
mod dladdr;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
use crate::io;
#[cfg(target_os = "emscripten")]
pub use self::dladdr::resolve_symname;
#[cfg(target_os = "emscripten")]
pub fn foreach_symbol_fileline<F>(_: Frame, _: F, _: &BacktraceContext) -> io::Result<bool>
where
F: FnMut(&[u8], u32) -> io::Result<()>
{
Ok(false)
}
#[cfg(not(target_os = "emscripten"))]
pub use crate::sys_common::gnu::libbacktrace::foreach_symbol_fileline;
#[cfg(not(target_os = "emscripten"))]
pub fn resolve_symname<F>(frame: Frame, callback: F, bc: &BacktraceContext) -> io::Result<()>
where
F: FnOnce(Option<&str>) -> io::Result<()>
{
crate::sys_common::gnu::libbacktrace::resolve_symname(frame, |symname| {
if symname.is_some() {
callback(symname)
} else {
dladdr::resolve_symname(frame, callback, bc)
}
}, bc)
}

View file

@ -0,0 +1,39 @@
/// As always - iOS on arm uses SjLj exceptions and
/// _Unwind_Backtrace is even not available there. Still,
/// backtraces could be extracted using a backtrace function,
/// which thanks god is public
///
/// As mentioned in a huge comment block in `super::super`, backtrace
/// doesn't play well with green threads, so while it is extremely nice and
/// simple to use it should be used only on iOS devices as the only viable
/// option.
use crate::io;
use crate::ptr;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
#[inline(never)] // if we know this is a function call, we can skip it when
// tracing
pub fn unwind_backtrace(frames: &mut [Frame])
-> io::Result<(usize, BacktraceContext)>
{
const FRAME_LEN: usize = 100;
assert!(FRAME_LEN >= frames.len());
let mut raw_frames = [ptr::null_mut(); FRAME_LEN];
let nb_frames = unsafe {
backtrace(raw_frames.as_mut_ptr(), raw_frames.len() as libc::c_int)
} as usize;
for (from, to) in raw_frames.iter().zip(frames.iter_mut()).take(nb_frames) {
*to = Frame {
exact_position: *from as *mut u8,
symbol_addr: *from as *mut u8,
inline_context: 0,
};
}
Ok((nb_frames as usize, BacktraceContext))
}
extern {
fn backtrace(buf: *mut *mut libc::c_void, sz: libc::c_int) -> libc::c_int;
}

View file

@ -0,0 +1,99 @@
use crate::error::Error;
use crate::fmt;
use crate::io;
use crate::sys::backtrace::BacktraceContext;
use crate::sys_common::backtrace::Frame;
use unwind as uw;
struct Context<'a> {
idx: usize,
frames: &'a mut [Frame],
}
#[derive(Debug)]
struct UnwindError(uw::_Unwind_Reason_Code);
impl Error for UnwindError {
fn description(&self) -> &'static str {
"unexpected return value while unwinding"
}
}
impl fmt::Display for UnwindError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {:?}", self.description(), self.0)
}
}
#[inline(never)] // if we know this is a function call, we can skip it when
// tracing
pub fn unwind_backtrace(frames: &mut [Frame])
-> io::Result<(usize, BacktraceContext)>
{
let mut cx = Context {
idx: 0,
frames,
};
let result_unwind = unsafe {
uw::_Unwind_Backtrace(trace_fn,
&mut cx as *mut Context<'_>
as *mut libc::c_void)
};
// See libunwind:src/unwind/Backtrace.c for the return values.
// No, there is no doc.
match result_unwind {
// These return codes seem to be benign and need to be ignored for backtraces
// to show up properly on all tested platforms.
uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
Ok((cx.idx, BacktraceContext))
}
_ => {
Err(io::Error::new(io::ErrorKind::Other,
UnwindError(result_unwind)))
}
}
}
extern fn trace_fn(ctx: *mut uw::_Unwind_Context,
arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code {
let cx = unsafe { &mut *(arg as *mut Context<'_>) };
if cx.idx >= cx.frames.len() {
return uw::_URC_NORMAL_STOP;
}
let mut ip_before_insn = 0;
let mut ip = unsafe {
uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void
};
if !ip.is_null() && ip_before_insn == 0 {
// this is a non-signaling frame, so `ip` refers to the address
// after the calling instruction. account for that.
ip = (ip as usize - 1) as *mut _;
}
// dladdr() on osx gets whiny when we use FindEnclosingFunction, and
// it appears to work fine without it, so we only use
// FindEnclosingFunction on non-osx platforms. In doing so, we get a
// slightly more accurate stack trace in the process.
//
// This is often because panic involves the last instruction of a
// function being "call std::rt::begin_unwind", with no ret
// instructions after it. This means that the return instruction
// pointer points *outside* of the calling function, and by
// unwinding it we go back to the original function.
let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
ip
} else {
unsafe { uw::_Unwind_FindEnclosingFunction(ip) }
};
cx.frames[cx.idx] = Frame {
symbol_addr: symaddr as *mut u8,
exact_position: ip as *mut u8,
inline_context: 0,
};
cx.idx += 1;
uw::_URC_NO_REASON
}

View file

@ -0,0 +1,8 @@
pub use self::imp::*;
#[cfg(not(all(target_os = "ios", target_arch = "arm")))]
#[path = "gcc_s.rs"]
mod imp;
#[cfg(all(target_os = "ios", target_arch = "arm"))]
#[path = "backtrace_fn.rs"]
mod imp;

View file

@ -0,0 +1,33 @@
#![cfg(not(test))]
use libc::{c_float, c_double};
#[link_name = "m"]
extern {
pub fn acos(n: c_double) -> c_double;
pub fn acosf(n: c_float) -> c_float;
pub fn asin(n: c_double) -> c_double;
pub fn asinf(n: c_float) -> c_float;
pub fn atan(n: c_double) -> c_double;
pub fn atan2(a: c_double, b: c_double) -> c_double;
pub fn atan2f(a: c_float, b: c_float) -> c_float;
pub fn atanf(n: c_float) -> c_float;
pub fn cbrt(n: c_double) -> c_double;
pub fn cbrtf(n: c_float) -> c_float;
pub fn cosh(n: c_double) -> c_double;
pub fn coshf(n: c_float) -> c_float;
pub fn expm1(n: c_double) -> c_double;
pub fn expm1f(n: c_float) -> c_float;
pub fn fdim(a: c_double, b: c_double) -> c_double;
pub fn fdimf(a: c_float, b: c_float) -> c_float;
pub fn hypot(x: c_double, y: c_double) -> c_double;
pub fn hypotf(x: c_float, y: c_float) -> c_float;
pub fn log1p(n: c_double) -> c_double;
pub fn log1pf(n: c_float) -> c_float;
pub fn sinh(n: c_double) -> c_double;
pub fn sinhf(n: c_float) -> c_float;
pub fn tan(n: c_double) -> c_double;
pub fn tanf(n: c_float) -> c_float;
pub fn tanh(n: c_double) -> c_double;
pub fn tanhf(n: c_float) -> c_float;
}

View file

@ -0,0 +1,169 @@
use crate::cell::UnsafeCell;
use crate::sys::mutex::{self, Mutex};
use crate::time::Duration;
pub struct Condvar { inner: UnsafeCell<libc::pthread_cond_t> }
unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}
const TIMESPEC_MAX: libc::timespec = libc::timespec {
tv_sec: <libc::time_t>::max_value(),
tv_nsec: 1_000_000_000 - 1,
};
fn saturating_cast_to_time_t(value: u64) -> libc::time_t {
if value > <libc::time_t>::max_value() as u64 {
<libc::time_t>::max_value()
} else {
value as libc::time_t
}
}
impl Condvar {
pub const fn new() -> Condvar {
// Might be moved and address is changing it is better to avoid
// initialization of potentially opaque OS data before it landed
Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) }
}
pub unsafe fn init(&mut self) {
use crate::mem::MaybeUninit;
let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
let r = libc::pthread_condattr_init(attr.as_mut_ptr());
assert_eq!(r, 0);
let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
assert_eq!(r, 0);
let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr());
assert_eq!(r, 0);
let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
assert_eq!(r, 0);
}
#[inline]
pub unsafe fn notify_one(&self) {
let r = libc::pthread_cond_signal(self.inner.get());
debug_assert_eq!(r, 0);
}
#[inline]
pub unsafe fn notify_all(&self) {
let r = libc::pthread_cond_broadcast(self.inner.get());
debug_assert_eq!(r, 0);
}
#[inline]
pub unsafe fn wait(&self, mutex: &Mutex) {
let r = libc::pthread_cond_wait(self.inner.get(), mutex::raw(mutex));
debug_assert_eq!(r, 0);
}
// This implementation is used on systems that support pthread_condattr_setclock
// where we configure condition variable to use monotonic clock (instead of
// default system clock). This approach avoids all problems that result
// from changes made to the system time.
#[cfg(not(any(target_os = "macos",
target_os = "ios",
target_os = "android",
target_os = "hermit")))]
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
use crate::mem;
let mut now: libc::timespec = mem::zeroed();
let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now);
assert_eq!(r, 0);
// Nanosecond calculations can't overflow because both values are below 1e9.
let nsec = dur.subsec_nanos() + now.tv_nsec as u32;
let sec = saturating_cast_to_time_t(dur.as_secs())
.checked_add((nsec / 1_000_000_000) as libc::time_t)
.and_then(|s| s.checked_add(now.tv_sec));
let nsec = nsec % 1_000_000_000;
let timeout = sec.map(|s| {
libc::timespec { tv_sec: s, tv_nsec: nsec as _}
}).unwrap_or(TIMESPEC_MAX);
let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
&timeout);
assert!(r == libc::ETIMEDOUT || r == 0);
r == 0
}
// This implementation is modeled after libcxx's condition_variable
// https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
// https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "android", target_os = "hermit"))]
pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool {
use crate::ptr;
use crate::time::Instant;
// 1000 years
let max_dur = Duration::from_secs(1000 * 365 * 86400);
if dur > max_dur {
// OSX implementation of `pthread_cond_timedwait` is buggy
// with super long durations. When duration is greater than
// 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
// in macOS Sierra return error 316.
//
// This program demonstrates the issue:
// https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
//
// To work around this issue, and possible bugs of other OSes, timeout
// is clamped to 1000 years, which is allowable per the API of `wait_timeout`
// because of spurious wakeups.
dur = max_dur;
}
// First, figure out what time it currently is, in both system and
// stable time. pthread_cond_timedwait uses system time, but we want to
// report timeout based on stable time.
let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 };
let stable_now = Instant::now();
let r = libc::gettimeofday(&mut sys_now, ptr::null_mut());
debug_assert_eq!(r, 0);
let nsec = dur.subsec_nanos() as libc::c_long +
(sys_now.tv_usec * 1000) as libc::c_long;
let extra = (nsec / 1_000_000_000) as libc::time_t;
let nsec = nsec % 1_000_000_000;
let seconds = saturating_cast_to_time_t(dur.as_secs());
let timeout = sys_now.tv_sec.checked_add(extra).and_then(|s| {
s.checked_add(seconds)
}).map(|s| {
libc::timespec { tv_sec: s, tv_nsec: nsec }
}).unwrap_or(TIMESPEC_MAX);
// And wait!
let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex),
&timeout);
debug_assert!(r == libc::ETIMEDOUT || r == 0);
// ETIMEDOUT is not a totally reliable method of determining timeout due
// to clock shifts, so do the check ourselves
stable_now.elapsed() < dur
}
#[inline]
#[cfg(not(target_os = "dragonfly"))]
pub unsafe fn destroy(&self) {
let r = libc::pthread_cond_destroy(self.inner.get());
debug_assert_eq!(r, 0);
}
#[inline]
#[cfg(target_os = "dragonfly")]
pub unsafe fn destroy(&self) {
let r = libc::pthread_cond_destroy(self.inner.get());
// On DragonFly pthread_cond_destroy() returns EINVAL if called on
// a condvar that was just initialized with
// libc::PTHREAD_COND_INITIALIZER. Once it is used or
// pthread_cond_init() is called, this behaviour no longer occurs.
debug_assert!(r == 0 || r == libc::EINVAL);
}
}

View file

@ -0,0 +1,9 @@
pub mod os {
pub const FAMILY: &str = "vxworks";
pub const OS: &str = "vxworks";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}

View file

@ -0,0 +1,38 @@
//! Unix-specific extension to the primitives in the `std::ffi` module
//!
//! # Examples
//!
//! ```
//! use std::ffi::OsString;
//! use std::os::unix::ffi::OsStringExt;
//!
//! let bytes = b"foo".to_vec();
//!
//! // OsStringExt::from_vec
//! let os_string = OsString::from_vec(bytes);
//! assert_eq!(os_string.to_str(), Some("foo"));
//!
//! // OsStringExt::into_vec
//! let bytes = os_string.into_vec();
//! assert_eq!(bytes, b"foo");
//! ```
//!
//! ```
//! use std::ffi::OsStr;
//! use std::os::unix::ffi::OsStrExt;
//!
//! let bytes = b"foo";
//!
//! // OsStrExt::from_bytes
//! let os_str = OsStr::from_bytes(bytes);
//! assert_eq!(os_str.to_str(), Some("foo"));
//!
//! // OsStrExt::as_bytes
//! let bytes = os_str.as_bytes();
//! assert_eq!(bytes, b"foo");
//! ```
#![stable(feature = "rust1", since = "1.0.0")]
#[stable(feature = "rust1", since = "1.0.0")]
pub use crate::sys_common::os_str_bytes::*;

View file

@ -0,0 +1,801 @@
#![stable(feature = "rust1", since = "1.0.0")]
use crate::fs::{self, Permissions};
use crate::io;
use libc;
use crate::path::Path;
use crate::sys;
use crate::sys_common::{FromInner, AsInner, AsInnerMut};
use crate::sys::platform::fs::MetadataExt as UnixMetadataExt;
/// Unix-specific extensions to [`File`].
///
/// [`File`]: ../../../../std/fs/struct.File.html
#[stable(feature = "file_offset", since = "1.15.0")]
pub trait FileExt {
/// Reads a number of bytes starting from a given offset.
///
/// Returns the number of bytes read.
///
/// The offset is relative to the start of the file and thus independent
/// from the current cursor.
///
/// The current file cursor is not affected by this function.
///
/// Note that similar to [`File::read`], it is not an error to return with a
/// short read.
///
/// [`File::read`]: ../../../../std/fs/struct.File.html#method.read
///
/// # Examples
///
/// ```no_run
/// use std::io;
/// use std::fs::File;
/// use std::os::unix::prelude::FileExt;
///
/// fn main() -> io::Result<()> {
/// let mut buf = [0u8; 8];
/// let file = File::open("foo.txt")?;
///
/// // We now read 8 bytes from the offset 10.
/// let num_bytes_read = file.read_at(&mut buf, 10)?;
/// println!("read {} bytes: {:?}", num_bytes_read, buf);
/// Ok(())
/// }
/// ```
#[stable(feature = "file_offset", since = "1.15.0")]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
/// Reads the exact number of byte required to fill `buf` from the given offset.
///
/// The offset is relative to the start of the file and thus independent
/// from the current cursor.
///
/// The current file cursor is not affected by this function.
///
/// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`.
///
/// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact
/// [`read_at`]: #tymethod.read_at
///
/// # Errors
///
/// If this function encounters an error of the kind
/// [`ErrorKind::Interrupted`] then the error is ignored and the operation
/// will continue.
///
/// If this function encounters an "end of file" before completely filling
/// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
/// The contents of `buf` are unspecified in this case.
///
/// If any other read error is encountered then this function immediately
/// returns. The contents of `buf` are unspecified in this case.
///
/// If this function returns an error, it is unspecified how many bytes it
/// has read, but it will never read more than would be necessary to
/// completely fill the buffer.
///
/// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
/// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof
///
/// # Examples
///
/// ```no_run
/// #![feature(rw_exact_all_at)]
/// use std::io;
/// use std::fs::File;
/// use std::os::unix::prelude::FileExt;
///
/// fn main() -> io::Result<()> {
/// let mut buf = [0u8; 8];
/// let file = File::open("foo.txt")?;
///
/// // We now read exactly 8 bytes from the offset 10.
/// file.read_exact_at(&mut buf, 10)?;
/// println!("read {} bytes: {:?}", buf.len(), buf);
/// Ok(())
/// }
/// ```
#[stable(feature = "rw_exact_all_at", since = "1.33.0")]
fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.read_at(buf, offset) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
offset += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
if !buf.is_empty() {
Err(io::Error::new(io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer"))
} else {
Ok(())
}
}
/// Writes a number of bytes starting from a given offset.
///
/// Returns the number of bytes written.
///
/// The offset is relative to the start of the file and thus independent
/// from the current cursor.
///
/// The current file cursor is not affected by this function.
///
/// When writing beyond the end of the file, the file is appropriately
/// extended and the intermediate bytes are initialized with the value 0.
///
/// Note that similar to [`File::write`], it is not an error to return a
/// short write.
///
/// [`File::write`]: ../../../../std/fs/struct.File.html#write.v
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
/// use std::io;
/// use std::os::unix::prelude::FileExt;
///
/// fn main() -> io::Result<()> {
/// let file = File::open("foo.txt")?;
///
/// // We now write at the offset 10.
/// file.write_at(b"sushi", 10)?;
/// Ok(())
/// }
/// ```
#[stable(feature = "file_offset", since = "1.15.0")]
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
/// Attempts to write an entire buffer starting from a given offset.
///
/// The offset is relative to the start of the file and thus independent
/// from the current cursor.
///
/// The current file cursor is not affected by this function.
///
/// This method will continuously call [`write_at`] until there is no more data
/// to be written or an error of non-[`ErrorKind::Interrupted`] kind is
/// returned. This method will not return until the entire buffer has been
/// successfully written or such an error occurs. The first error that is
/// not of [`ErrorKind::Interrupted`] kind generated from this method will be
/// returned.
///
/// # Errors
///
/// This function will return the first error of
/// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns.
///
/// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
/// [`write_at`]: #tymethod.write_at
///
/// # Examples
///
/// ```no_run
/// #![feature(rw_exact_all_at)]
/// use std::fs::File;
/// use std::io;
/// use std::os::unix::prelude::FileExt;
///
/// fn main() -> io::Result<()> {
/// let file = File::open("foo.txt")?;
///
/// // We now write at the offset 10.
/// file.write_all_at(b"sushi", 10)?;
/// Ok(())
/// }
/// ```
#[stable(feature = "rw_exact_all_at", since = "1.33.0")]
fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.write_at(buf, offset) {
Ok(0) => return Err(io::Error::new(io::ErrorKind::WriteZero,
"failed to write whole buffer")),
Ok(n) => {
buf = &buf[n..];
offset += n as u64
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
}
#[stable(feature = "file_offset", since = "1.15.0")]
impl FileExt for fs::File {
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.as_inner().read_at(buf, offset)
}
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.as_inner().write_at(buf, offset)
}
}
/// Unix-specific extensions to [`fs::Permissions`].
///
/// [`fs::Permissions`]: ../../../../std/fs/struct.Permissions.html
#[stable(feature = "fs_ext", since = "1.1.0")]
pub trait PermissionsExt {
/// Returns the underlying raw `st_mode` bits that contain the standard
/// Unix permissions for this file.
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
/// use std::os::unix::fs::PermissionsExt;
///
/// fn main() -> std::io::Result<()> {
/// let f = File::create("foo.txt")?;
/// let metadata = f.metadata()?;
/// let permissions = metadata.permissions();
///
/// println!("permissions: {}", permissions.mode());
/// Ok(()) }
/// ```
#[stable(feature = "fs_ext", since = "1.1.0")]
fn mode(&self) -> u32;
/// Sets the underlying raw bits for this set of permissions.
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
/// use std::os::unix::fs::PermissionsExt;
///
/// fn main() -> std::io::Result<()> {
/// let f = File::create("foo.txt")?;
/// let metadata = f.metadata()?;
/// let mut permissions = metadata.permissions();
///
/// permissions.set_mode(0o644); // Read/write for owner and read for others.
/// assert_eq!(permissions.mode(), 0o644);
/// Ok(()) }
/// ```
#[stable(feature = "fs_ext", since = "1.1.0")]
fn set_mode(&mut self, mode: u32);
/// Creates a new instance of `Permissions` from the given set of Unix
/// permission bits.
///
/// # Examples
///
/// ```
/// use std::fs::Permissions;
/// use std::os::unix::fs::PermissionsExt;
///
/// // Read/write for owner and read for others.
/// let permissions = Permissions::from_mode(0o644);
/// assert_eq!(permissions.mode(), 0o644);
/// ```
#[stable(feature = "fs_ext", since = "1.1.0")]
fn from_mode(mode: u32) -> Self;
}
#[stable(feature = "fs_ext", since = "1.1.0")]
impl PermissionsExt for Permissions {
fn mode(&self) -> u32 {
self.as_inner().mode()
}
fn set_mode(&mut self, mode: u32) {
*self = Permissions::from_inner(FromInner::from_inner(mode));
}
fn from_mode(mode: u32) -> Permissions {
Permissions::from_inner(FromInner::from_inner(mode))
}
}
/// Unix-specific extensions to [`fs::OpenOptions`].
///
/// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html
#[stable(feature = "fs_ext", since = "1.1.0")]
pub trait OpenOptionsExt {
/// Sets the mode bits that a new file will be created with.
///
/// If a new file is created as part of a `File::open_opts` call then this
/// specified `mode` will be used as the permission bits for the new file.
/// If no `mode` is set, the default of `0o666` will be used.
/// The operating system masks out bits with the systems `umask`, to produce
/// the final permissions.
///
/// # Examples
///
/// ```no_run
/// use std::fs::OpenOptions;
/// use std::os::unix::fs::OpenOptionsExt;
///
/// # fn main() {
/// let mut options = OpenOptions::new();
/// options.mode(0o644); // Give read/write for owner and read for others.
/// let file = options.open("foo.txt");
/// # }
/// ```
#[stable(feature = "fs_ext", since = "1.1.0")]
fn mode(&mut self, mode: u32) -> &mut Self;
/// Pass custom flags to the `flags` argument of `open`.
///
/// The bits that define the access mode are masked out with `O_ACCMODE`, to
/// ensure they do not interfere with the access mode set by Rusts options.
///
/// Custom flags can only set flags, not remove flags set by Rusts options.
/// This options overwrites any previously set custom flags.
///
/// # Examples
///
/// ```no_run
/// # #![feature(libc)]
/// extern crate libc;
/// use std::fs::OpenOptions;
/// use std::os::unix::fs::OpenOptionsExt;
///
/// # fn main() {
/// let mut options = OpenOptions::new();
/// options.write(true);
/// if cfg!(unix) {
/// options.custom_flags(libc::O_NOFOLLOW);
/// }
/// let file = options.open("foo.txt");
/// # }
/// ```
#[stable(feature = "open_options_ext", since = "1.10.0")]
fn custom_flags(&mut self, flags: i32) -> &mut Self;
}
/*#[stable(feature = "fs_ext", since = "1.1.0")]
impl OpenOptionsExt for OpenOptions {
fn mode(&mut self, mode: u32) -> &mut OpenOptions {
self.as_inner_mut().mode(mode); self
}
fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions {
self.as_inner_mut().custom_flags(flags); self
}
}
*/
/// Unix-specific extensions to [`fs::Metadata`].
///
/// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
#[stable(feature = "metadata_ext", since = "1.1.0")]
pub trait MetadataExt {
/// Returns the ID of the device containing the file.
///
/// # Examples
///
/// ```no_run
/// use std::io;
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let dev_id = meta.dev();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn dev(&self) -> u64;
/// Returns the inode number.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let inode = meta.ino();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn ino(&self) -> u64;
/// Returns the rights applied to this file.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let mode = meta.mode();
/// let user_has_write_access = mode & 0o200;
/// let user_has_read_write_access = mode & 0o600;
/// let group_has_read_access = mode & 0o040;
/// let others_have_exec_access = mode & 0o001;
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn mode(&self) -> u32;
/// Returns the number of hard links pointing to this file.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let nb_hard_links = meta.nlink();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn nlink(&self) -> u64;
/// Returns the user ID of the owner of this file.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let user_id = meta.uid();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn uid(&self) -> u32;
/// Returns the group ID of the owner of this file.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let group_id = meta.gid();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn gid(&self) -> u32;
/// Returns the device ID of this file (if it is a special one).
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let device_id = meta.rdev();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn rdev(&self) -> u64;
/// Returns the total size of this file in bytes.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let file_size = meta.size();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn size(&self) -> u64;
/// Returns the time of the last access to the file.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let last_access_time = meta.atime();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn atime(&self) -> i64;
/// Returns the time of the last access to the file in nanoseconds.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let nano_last_access_time = meta.atime_nsec();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn mtime(&self) -> i64;
/// Returns the time of the last modification of the file in nanoseconds.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let nano_last_modification_time = meta.mtime_nsec();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn ctime(&self) -> i64;
/// Returns the time of the last status change of the file in nanoseconds.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let nano_last_status_change_time = meta.ctime_nsec();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn blksize(&self) -> u64;
/// Returns the number of blocks allocated to the file, in 512-byte units.
///
/// Please note that this may be smaller than `st_size / 512` when the file has holes.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::MetadataExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("some_file")?;
/// let blocks = meta.blocks();
/// Ok(())
/// }
/// ```
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn blocks(&self) -> u64;
#[stable(feature = "metadata_ext", since = "1.1.0")]
fn attrib(&self) -> u8;
}
#[stable(feature = "metadata_ext", since = "1.1.0")]
impl MetadataExt for fs::Metadata {
fn dev(&self) -> u64 { self.st_dev() }
fn ino(&self) -> u64 { self.st_ino() }
fn mode(&self) -> u32 { self.st_mode() }
fn nlink(&self) -> u64 { self.st_nlink() }
fn uid(&self) -> u32 { self.st_uid() }
fn gid(&self) -> u32 { self.st_gid() }
fn rdev(&self) -> u64 { self.st_rdev() }
fn size(&self) -> u64 { self.st_size() }
fn atime(&self) -> i64 { self.st_atime() }
fn mtime(&self) -> i64 { self.st_mtime() }
fn ctime(&self) -> i64 { self.st_ctime() }
fn blksize(&self) -> u64 { self.st_blksize() }
fn blocks(&self) -> u64 { self.st_blocks() }
fn attrib(&self) -> u8 {self.st_attrib() }
}
/// Unix-specific extensions for [`FileType`].
///
/// Adds support for special Unix file types such as block/character devices,
/// pipes, and sockets.
///
/// [`FileType`]: ../../../../std/fs/struct.FileType.html
#[stable(feature = "file_type_ext", since = "1.5.0")]
pub trait FileTypeExt {
/// Returns whether this file type is a block device.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::FileTypeExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("block_device_file")?;
/// let file_type = meta.file_type();
/// assert!(file_type.is_block_device());
/// Ok(())
/// }
/// ```
#[stable(feature = "file_type_ext", since = "1.5.0")]
fn is_block_device(&self) -> bool;
/// Returns whether this file type is a char device.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::FileTypeExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("char_device_file")?;
/// let file_type = meta.file_type();
/// assert!(file_type.is_char_device());
/// Ok(())
/// }
/// ```
#[stable(feature = "file_type_ext", since = "1.5.0")]
fn is_char_device(&self) -> bool;
/// Returns whether this file type is a fifo.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::FileTypeExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("fifo_file")?;
/// let file_type = meta.file_type();
/// assert!(file_type.is_fifo());
/// Ok(())
/// }
/// ```
#[stable(feature = "file_type_ext", since = "1.5.0")]
fn is_fifo(&self) -> bool;
/// Returns whether this file type is a socket.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::unix::fs::FileTypeExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata("unix.socket")?;
/// let file_type = meta.file_type();
/// assert!(file_type.is_socket());
/// Ok(())
/// }
/// ```
#[stable(feature = "file_type_ext", since = "1.5.0")]
fn is_socket(&self) -> bool;
}
#[stable(feature = "file_type_ext", since = "1.5.0")]
impl FileTypeExt for fs::FileType {
fn is_block_device(&self) -> bool { self.as_inner().is(libc::S_IFBLK) }
fn is_char_device(&self) -> bool { self.as_inner().is(libc::S_IFCHR) }
fn is_fifo(&self) -> bool { self.as_inner().is(libc::S_IFIFO) }
fn is_socket(&self) -> bool { self.as_inner().is(libc::S_IFSOCK) }
}
/// Unix-specific extension methods for [`fs::DirEntry`].
///
/// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html
#[stable(feature = "dir_entry_ext", since = "1.1.0")]
pub trait DirEntryExt {
/// Returns the underlying `d_ino` field in the contained `dirent`
/// structure.
///
/// # Examples
///
/// ```
/// use std::fs;
/// use std::os::unix::fs::DirEntryExt;
///
/// if let Ok(entries) = fs::read_dir(".") {
/// for entry in entries {
/// if let Ok(entry) = entry {
/// // Here, `entry` is a `DirEntry`.
/// println!("{:?}: {}", entry.file_name(), entry.ino());
/// }
/// }
/// }
/// ```
#[stable(feature = "dir_entry_ext", since = "1.1.0")]
fn ino(&self) -> u64;
}
#[stable(feature = "dir_entry_ext", since = "1.1.0")]
impl DirEntryExt for fs::DirEntry {
fn ino(&self) -> u64 { self.as_inner().ino() }
}
/// Creates a new symbolic link on the filesystem.
///
/// The `dst` path will be a symbolic link pointing to the `src` path.
///
/// # Note
///
/// On Windows, you must specify whether a symbolic link points to a file
/// or directory. Use `os::windows::fs::symlink_file` to create a
/// symbolic link to a file, or `os::windows::fs::symlink_dir` to create a
/// symbolic link to a directory. Additionally, the process must have
/// `SeCreateSymbolicLinkPrivilege` in order to be able to create a
/// symbolic link.
///
/// # Examples
///
/// ```no_run
/// use std::os::unix::fs;
///
/// fn main() -> std::io::Result<()> {
/// fs::symlink("a.txt", "b.txt")?;
/// Ok(())
/// }
/// ```
#[stable(feature = "symlink", since = "1.1.0")]
pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()>
{
sys::fs::symlink(src.as_ref(), dst.as_ref())
}
/// Unix-specific extensions to [`fs::DirBuilder`].
///
/// [`fs::DirBuilder`]: ../../../../std/fs/struct.DirBuilder.html
#[stable(feature = "dir_builder", since = "1.6.0")]
pub trait DirBuilderExt {
/// Sets the mode to create new directories with. This option defaults to
/// 0o777.
///
/// # Examples
///
/// ```no_run
/// use std::fs::DirBuilder;
/// use std::os::unix::fs::DirBuilderExt;
///
/// let mut builder = DirBuilder::new();
/// builder.mode(0o755);
/// ```
#[stable(feature = "dir_builder", since = "1.6.0")]
fn mode(&mut self, mode: u32) -> &mut Self;
}
#[stable(feature = "dir_builder", since = "1.6.0")]
impl DirBuilderExt for fs::DirBuilder {
fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder {
self.as_inner_mut().set_mode(mode);
self
}
}

View file

@ -0,0 +1,112 @@
//! Unix-specific extensions to general I/O primitives
#![stable(feature = "rust1", since = "1.0.0")]
use crate::fs;
use crate::os::raw;
use crate::sys;
use crate::io;
use crate::sys_common::{AsInner, FromInner, IntoInner};
/// Raw file descriptors.
#[stable(feature = "rust1", since = "1.0.0")]
pub type RawFd = raw::c_int;
/// A trait to extract the raw unix file descriptor from an underlying
/// object.
///
/// This is only available on unix platforms and must be imported in order
/// to call the method. Windows platforms have a corresponding `AsRawHandle`
/// and `AsRawSocket` set of traits.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsRawFd {
/// Extracts the raw file descriptor.
///
/// This method does **not** pass ownership of the raw file descriptor
/// to the caller. The descriptor is only guaranteed to be valid while
/// the original object has not yet been destroyed.
#[stable(feature = "rust1", since = "1.0.0")]
fn as_raw_fd(&self) -> RawFd;
}
/// A trait to express the ability to construct an object from a raw file
/// descriptor.
#[stable(feature = "from_raw_os", since = "1.1.0")]
pub trait FromRawFd {
/// Constructs a new instance of `Self` from the given raw file
/// descriptor.
///
/// This function **consumes ownership** of the specified file
/// descriptor. The returned object will take responsibility for closing
/// it when the object goes out of scope.
///
/// This function is also unsafe as the primitives currently returned
/// have the contract that they are the sole owner of the file
/// descriptor they are wrapping. Usage of this function could
/// accidentally allow violating this contract which can cause memory
/// unsafety in code that relies on it being true.
#[stable(feature = "from_raw_os", since = "1.1.0")]
unsafe fn from_raw_fd(fd: RawFd) -> Self;
}
/// A trait to express the ability to consume an object and acquire ownership of
/// its raw file descriptor.
#[stable(feature = "into_raw_os", since = "1.4.0")]
pub trait IntoRawFd {
/// Consumes this object, returning the raw underlying file descriptor.
///
/// This function **transfers ownership** of the underlying file descriptor
/// to the caller. Callers are then the unique owners of the file descriptor
/// and must close the descriptor once it's no longer needed.
#[stable(feature = "into_raw_os", since = "1.4.0")]
fn into_raw_fd(self) -> RawFd;
}
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRawFd for fs::File {
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
}
#[stable(feature = "from_raw_os", since = "1.1.0")]
impl FromRawFd for fs::File {
unsafe fn from_raw_fd(fd: RawFd) -> fs::File {
fs::File::from_inner(sys::fs::File::from_inner(fd))
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for fs::File {
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
}
#[stable(feature = "asraw_stdio", since = "1.21.0")]
impl AsRawFd for io::Stdin {
fn as_raw_fd(&self) -> RawFd { libc::STDIN_FILENO }
}
#[stable(feature = "asraw_stdio", since = "1.21.0")]
impl AsRawFd for io::Stdout {
fn as_raw_fd(&self) -> RawFd { libc::STDOUT_FILENO }
}
#[stable(feature = "asraw_stdio", since = "1.21.0")]
impl AsRawFd for io::Stderr {
fn as_raw_fd(&self) -> RawFd { libc::STDERR_FILENO }
}
#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
impl<'a> AsRawFd for io::StdinLock<'a> {
fn as_raw_fd(&self) -> RawFd { libc::STDIN_FILENO }
}
#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
impl<'a> AsRawFd for io::StdoutLock<'a> {
fn as_raw_fd(&self) -> RawFd { libc::STDOUT_FILENO }
}
#[stable(feature = "asraw_stdio_locks", since = "1.35.0")]
impl<'a> AsRawFd for io::StderrLock<'a> {
fn as_raw_fd(&self) -> RawFd { libc::STDERR_FILENO }
}

View file

@ -0,0 +1,20 @@
// Uhhh
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
pub mod io;
pub mod ffi;
pub mod fs;
pub mod raw;
pub mod process;
pub mod net;
#[stable(feature = "rust1", since = "1.0.0")]
pub mod prelude {
#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
pub use super::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd};
#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
pub use super::ffi::{OsStrExt, OsStringExt};
#[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")]
pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,213 @@
//! Unix-specific extensions to primitives in the `std::process` module.
#![stable(feature = "rust1", since = "1.0.0")]
use crate::io;
use crate::sys::vxworks::ext::io::{FromRawFd, RawFd, AsRawFd, IntoRawFd};
use crate::process;
use crate::sys;
use crate::sys_common::{AsInnerMut, AsInner, FromInner, IntoInner};
/// Unix-specific extensions to the [`process::Command`] builder.
///
/// [`process::Command`]: ../../../../std/process/struct.Command.html
#[stable(feature = "rust1", since = "1.0.0")]
pub trait CommandExt {
/// Sets the child process's user ID. This translates to a
/// `setuid` call in the child process. Failure in the `setuid`
/// call will cause the spawn to fail.
#[stable(feature = "rust1", since = "1.0.0")]
fn uid(&mut self, id: u16) -> &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: u16) -> &mut process::Command;
/// Schedules a closure to be run just before the `exec` function is
/// invoked.
///
/// The closure is allowed to return an I/O error whose OS error code will
/// be communicated back to the parent and returned as an error from when
/// the spawn was requested.
///
/// Multiple closures can be registered and they will be called in order of
/// their registration. If a closure returns `Err` then no further closures
/// will be called and the spawn operation will immediately return with a
/// failure.
///
/// # Notes and Safety
///
/// This closure will be run in the context of the child process after a
/// `fork`. This primarily means that any modifications made to memory on
/// behalf of this closure will **not** be visible to the parent process.
/// This is often a very constrained environment where normal operations
/// like `malloc` or acquiring a mutex are not guaranteed to work (due to
/// other threads perhaps still running when the `fork` was run).
///
/// This also means that all resources such as file descriptors and
/// memory-mapped regions got duplicated. It is your responsibility to make
/// sure that the closure does not violate library invariants by making
/// invalid use of these duplicates.
///
/// When this closure is run, aspects such as the stdio file descriptors and
/// working directory have successfully been changed, so output to these
/// locations may not appear where intended.
#[stable(feature = "process_pre_exec", since = "1.34.0")]
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
/// Schedules a closure to be run just before the `exec` function is
/// invoked.
///
/// This method is stable and usable, but it should be unsafe. To fix
/// that, it got deprecated in favor of the unsafe [`pre_exec`].
///
/// [`pre_exec`]: #tymethod.pre_exec
#[stable(feature = "process_exec", since = "1.15.0")]
#[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")]
fn before_exec<F>(&mut self, f: F) -> &mut process::Command
where F: FnMut() -> io::Result<()> + Send + Sync + 'static
{
unsafe { self.pre_exec(f) }
}
/// Performs all the required setup by this `Command`, followed by calling
/// the `execvp` syscall.
///
/// On success this function will not return, and otherwise it will return
/// an error indicating why the exec (or another part of the setup of the
/// `Command`) failed.
///
/// `exec` not returning has the same implications as calling
/// [`process::exit`] no destructors on the current stack or any other
/// threads stack will be run. Therefore, it is recommended to only call
/// `exec` at a point where it is fine to not run any destructors. Note,
/// that the `execvp` syscall independently guarantees that all memory is
/// freed and all file descriptors with the `CLOEXEC` option (set by default
/// on all file descriptors opened by the standard library) are closed.
///
/// This function, unlike `spawn`, will **not** `fork` the process to create
/// a new child. Like spawn, however, the default behavior for the stdio
/// descriptors will be to inherited from the current process.
///
/// [`process::exit`]: ../../../process/fn.exit.html
///
/// # Notes
///
/// The process may be in a "broken state" if this function returns in
/// error. For example the working directory, environment variables, signal
/// handling settings, various user/group information, or aspects of stdio
/// file descriptors may have changed. If a "transactional spawn" is
/// required to gracefully handle errors it is recommended to use the
/// cross-platform `spawn` instead.
#[stable(feature = "process_exec2", since = "1.9.0")]
fn exec(&mut self) -> io::Error;
}
#[stable(feature = "rust1", since = "1.0.0")]
impl CommandExt for process::Command {
fn uid(&mut self, id: u16) -> &mut process::Command {
self.as_inner_mut().uid(id);
self
}
fn gid(&mut self, id: u16) -> &mut process::Command {
self.as_inner_mut().gid(id);
self
}
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
where F: FnMut() -> io::Result<()> + Send + Sync + 'static
{
self.as_inner_mut().pre_exec(Box::new(f));
self
}
fn exec(&mut self) -> io::Error {
self.as_inner_mut().exec(sys::process::Stdio::Inherit)
}
}
/// Unix-specific extensions to [`process::ExitStatus`].
///
/// [`process::ExitStatus`]: ../../../../std/process/struct.ExitStatus.html
#[stable(feature = "rust1", since = "1.0.0")]
pub trait ExitStatusExt {
/// Creates a new `ExitStatus` from the raw underlying `i32` return value of
/// a process.
#[stable(feature = "exit_status_from", since = "1.12.0")]
fn from_raw(raw: i32) -> Self;
/// If the process was terminated by a signal, returns that signal.
#[stable(feature = "rust1", since = "1.0.0")]
fn signal(&self) -> Option<i32>;
}
#[stable(feature = "rust1", since = "1.0.0")]
impl ExitStatusExt for process::ExitStatus {
fn from_raw(raw: i32) -> Self {
process::ExitStatus::from_inner(From::from(raw))
}
fn signal(&self) -> Option<i32> {
self.as_inner().signal()
}
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawFd for process::Stdio {
unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
let fd = sys::fd::FileDesc::new(fd);
let io = sys::process::Stdio::Fd(fd);
process::Stdio::from_inner(io)
}
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdin {
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdout {
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
}
#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStderr {
fn as_raw_fd(&self) -> RawFd {
self.as_inner().fd().raw()
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdin {
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdout {
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
}
#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStderr {
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_fd().into_raw()
}
}
/// Returns the OS-assigned process identifier associated with this process's parent.
#[stable(feature = "unix_ppid", since = "1.27.0")]
pub fn parent_id() -> u32 {
crate::sys::os::getppid()
}

View file

@ -0,0 +1,5 @@
#![stable(feature = "raw_ext", since = "1.1.0")]
#[doc(inline)]
#[stable(feature = "pthread_t", since = "1.8.0")]
pub use crate::sys::platform::raw::pthread_t;

View file

@ -0,0 +1,36 @@
#![cfg(target_thread_local)]
#![unstable(feature = "thread_local_internals", issue = "0")]
// Since what appears to be glibc 2.18 this symbol has been shipped which
// GCC and clang both use to invoke destructors in thread_local globals, so
// let's do the same!
//
// Note, however, that we run on lots older linuxes, as well as cross
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
//
// Due to rust-lang/rust#18804, make sure this is not generic!
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) {
use crate::mem;
use crate::sys_common::thread_local::register_dtor_fallback;
extern {
#[linkage = "extern_weak"]
static __dso_handle: *mut u8;
#[linkage = "extern_weak"]
static __cxa_thread_atexit_impl: *const libc::c_void;
}
if !__cxa_thread_atexit_impl.is_null() {
type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
arg: *mut u8,
dso_handle: *mut u8) -> libc::c_int;
mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
(dtor, t, &__dso_handle as *const _ as *mut _);
return
}
register_dtor_fallback(t, dtor);
}
pub fn requires_move_before_drop() -> bool {
false
}

View file

@ -0,0 +1,188 @@
#![unstable(reason = "not public", issue = "0", feature = "fd")]
use crate::cmp;
use crate::io::{self, Read, Initializer, IoSlice, IoSliceMut};
use crate::mem;
use crate::sys::cvt;
use crate::sys_common::AsInner;
use libc::{self, c_int, c_void, ssize_t};
#[derive(Debug)]
pub struct FileDesc {
fd: c_int,
}
fn max_len() -> usize {
// The maximum read limit on most posix-like systems is `SSIZE_MAX`,
// with the man page quoting that if the count of bytes to read is
// greater than `SSIZE_MAX` the result is "unspecified".
<ssize_t>::max_value() as usize
}
impl FileDesc {
pub fn new(fd: c_int) -> FileDesc {
FileDesc { fd: fd }
}
pub fn raw(&self) -> c_int { self.fd }
/// Extracts the actual filedescriptor without closing it.
pub fn into_raw(self) -> c_int {
let fd = self.fd;
mem::forget(self);
fd
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::read(self.fd,
buf.as_mut_ptr() as *mut c_void,
cmp::min(buf.len(), max_len()))
})?;
Ok(ret as usize)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::readv(self.fd,
bufs.as_ptr() as *const libc::iovec,
cmp::min(bufs.len(), c_int::max_value() as usize) as c_int)
})?;
Ok(ret as usize)
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
let mut me = self;
(&mut me).read_to_end(buf)
}
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
unsafe fn cvt_pread(fd: c_int, buf: *mut c_void, count: usize, offset: i64)
-> io::Result<isize>
{
use libc::pread;
cvt(pread(fd, buf, count, offset))
}
unsafe {
cvt_pread(self.fd,
buf.as_mut_ptr() as *mut c_void,
cmp::min(buf.len(), max_len()),
offset as i64)
.map(|n| n as usize)
}
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::write(self.fd,
buf.as_ptr() as *const c_void,
cmp::min(buf.len(), max_len()))
})?;
Ok(ret as usize)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::writev(self.fd,
bufs.as_ptr() as *const libc::iovec,
cmp::min(bufs.len(), c_int::max_value() as usize) as c_int)
})?;
Ok(ret as usize)
}
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
unsafe fn cvt_pwrite(fd: c_int, buf: *const c_void, count: usize, offset: i64)
-> io::Result<isize>
{
use libc::pwrite;
cvt(pwrite(fd, buf, count, offset))
}
unsafe {
cvt_pwrite(self.fd,
buf.as_ptr() as *const c_void,
cmp::min(buf.len(), max_len()),
offset as i64)
.map(|n| n as usize)
}
}
pub fn get_cloexec(&self) -> io::Result<bool> {
unsafe {
Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0)
}
}
pub fn set_cloexec(&self) -> io::Result<()> {
unsafe {
let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
let new = previous | libc::FD_CLOEXEC;
if new != previous {
cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
}
Ok(())
}
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
unsafe {
let v = nonblocking as c_int;
cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
Ok(())
}
}
// refer to pxPipeDrv library documentation.
// VxWorks uses fcntl to set O_NONBLOCK to the pipes
pub fn set_nonblocking_pipe(&self, nonblocking: bool) -> io::Result<()> {
unsafe {
let mut flags = cvt(libc::fcntl(self.fd, libc::F_GETFL, 0))?;
flags = if nonblocking {
flags | libc::O_NONBLOCK
} else {
flags & !libc::O_NONBLOCK
};
cvt(libc::fcntl(self.fd, libc::F_SETFL, flags))?;
Ok(())
}
}
pub fn duplicate(&self) -> io::Result<FileDesc> {
let fd = self.raw();
match cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) }) {
Ok(newfd) => {
Ok(FileDesc::new(newfd))
}
Err(e) => return Err(e),
}
}
}
impl<'a> Read for &'a FileDesc {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(**self).read(buf)
}
#[inline]
unsafe fn initializer(&self) -> Initializer {
Initializer::nop()
}
}
impl AsInner<c_int> for FileDesc {
fn as_inner(&self) -> &c_int { &self.fd }
}
impl Drop for FileDesc {
fn drop(&mut self) {
// Note that errors are ignored when closing a file descriptor. The
// reason for this is that if an error occurs we don't actually know if
// the file descriptor was closed or not, and if we retried (for
// something like EINTR), we might close another valid file descriptor
// (opened after we closed ours.
let _ = unsafe { libc::close(self.fd) };
}
}

View file

@ -0,0 +1,590 @@
// copies from linuxx
use crate::ffi::{CString, CStr, OsString, OsStr};
use crate::sys::vxworks::ext::ffi::OsStrExt;
use crate::fmt;
use crate::io::{self, Error, ErrorKind, SeekFrom, IoSlice, IoSliceMut};
use crate::mem;
use crate::path::{Path, PathBuf};
use crate::ptr;
use crate::sync::Arc;
use crate::sys::fd::FileDesc;
use crate::sys::time::SystemTime;
use crate::sys::{cvt, cvt_r};
use crate::sys_common::{AsInner, FromInner};
use libc::{self, c_int, mode_t, stat64, off_t};
use libc::{ftruncate, lseek, dirent, readdir_r as readdir64_r, open};
use crate::sys::vxworks::ext::ffi::OsStringExt;
pub struct File(FileDesc);
#[derive(Clone)]
pub struct FileAttr {
stat: stat64,
}
// all DirEntry's will have a reference to this struct
struct InnerReadDir {
dirp: Dir,
root: PathBuf,
}
#[derive(Clone)]
pub struct ReadDir {
inner: Arc<InnerReadDir>,
end_of_stream: bool,
}
struct Dir(*mut libc::DIR);
unsafe impl Send for Dir {}
unsafe impl Sync for Dir {}
pub struct DirEntry {
entry: dirent,
dir: ReadDir,
}
#[derive(Clone, Debug)]
pub struct OpenOptions {
// generic
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
// system-specific
custom_flags: i32,
mode: mode_t,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FilePermissions { mode: mode_t }
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct FileType { mode: mode_t }
#[derive(Debug)]
pub struct DirBuilder { mode: mode_t }
impl FileAttr {
pub fn size(&self) -> u64 { self.stat.st_size as u64 }
pub fn perm(&self) -> FilePermissions {
FilePermissions { mode: (self.stat.st_mode as mode_t) }
}
pub fn file_type(&self) -> FileType {
FileType { mode: self.stat.st_mode as mode_t }
}
pub fn modified(&self) -> io::Result<SystemTime> {
Ok(SystemTime::from(libc::timespec {
tv_sec: self.stat.st_mtime as libc::time_t,
tv_nsec: 0, // hack 2.0;
}))
}
pub fn accessed(&self) -> io::Result<SystemTime> {
Ok(SystemTime::from(libc::timespec {
tv_sec: self.stat.st_atime as libc::time_t,
tv_nsec: 0, // hack - a proper fix would be better
}))
}
pub fn created(&self) -> io::Result<SystemTime> {
Err(io::Error::new(io::ErrorKind::Other,
"creation time is not available on this platform currently"))
}
}
impl AsInner<stat64> for FileAttr {
fn as_inner(&self) -> &stat64 { &self.stat }
}
impl FilePermissions {
pub fn readonly(&self) -> bool {
// check if any class (owner, group, others) has write permission
self.mode & 0o222 == 0
}
pub fn set_readonly(&mut self, readonly: bool) {
if readonly {
// remove write permission for all classes; equivalent to `chmod a-w <file>`
self.mode &= !0o222;
} else {
// add write permission for all classes; equivalent to `chmod a+w <file>`
self.mode |= 0o222;
}
}
pub fn mode(&self) -> u32 { self.mode as u32 }
}
impl FileType {
pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) }
pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) }
pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) }
pub fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode }
}
impl FromInner<u32> for FilePermissions {
fn from_inner(mode: u32) -> FilePermissions {
FilePermissions { mode: mode as mode_t }
}
}
impl fmt::Debug for ReadDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
// Thus the result will be e g 'ReadDir("/home")'
fmt::Debug::fmt(&*self.inner.root, f)
}
}
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
if self.end_of_stream {
return None;
}
unsafe {
let mut ret = DirEntry {
entry: mem::zeroed(),
dir: self.clone(),
};
let mut entry_ptr = ptr::null_mut();
loop {
if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
if entry_ptr.is_null() {
// We encountered an error (which will be returned in this iteration), but
// we also reached the end of the directory stream. The `end_of_stream`
// flag is enabled to make sure that we return `None` in the next iteration
// (instead of looping forever)
self.end_of_stream = true;
}
return Some(Err(Error::last_os_error()))
}
if entry_ptr.is_null() {
return None
}
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
return Some(Ok(ret))
}
}
}
}
}
impl Drop for Dir {
fn drop(&mut self) {
let r = unsafe { libc::closedir(self.0) };
debug_assert_eq!(r, 0);
}
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
use crate::sys::vxworks::ext::ffi::OsStrExt;
self.dir.inner.root.join(OsStr::from_bytes(self.name_bytes()))
}
pub fn file_name(&self) -> OsString {
OsStr::from_bytes(self.name_bytes()).to_os_string()
}
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())
}
pub fn ino(&self) -> u64 {
self.entry.d_ino as u64
}
fn name_bytes(&self) -> &[u8] {
unsafe {
//&*self.name
CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes()
}
}
}
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 mode(&mut self, mode: u32) { self.mode = mode as mode_t; }
fn get_access_mode(&self) -> io::Result<c_int> {
match (self.read, self.write, self.append) {
(true, false, false) => Ok(libc::O_RDONLY),
(false, true, false) => Ok(libc::O_WRONLY),
(true, true, false) => Ok(libc::O_RDWR),
(false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
(true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
(false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
}
}
fn get_creation_mode(&self) -> io::Result<c_int> {
match (self.write, self.append) {
(true, false) => {}
(false, false) =>
if self.truncate || self.create || self.create_new {
return Err(Error::from_raw_os_error(libc::EINVAL));
},
(_, true) =>
if self.truncate && !self.create_new {
return Err(Error::from_raw_os_error(libc::EINVAL));
},
}
Ok(match (self.create, self.truncate, self.create_new) {
(false, false, false) => 0,
(true, false, false) => libc::O_CREAT,
(false, true, false) => libc::O_TRUNC,
(true, true, false) => libc::O_CREAT | libc::O_TRUNC,
(_, _, true) => libc::O_CREAT | libc::O_EXCL,
})
}
}
impl File {
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
let path = cstr(path)?;
File::open_c(&path, opts)
}
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
let flags = libc::O_CLOEXEC |
opts.get_access_mode()? |
opts.get_creation_mode()? |
(opts.custom_flags as c_int & !libc::O_ACCMODE);
let fd = cvt_r(|| unsafe {
open(path.as_ptr(), flags, opts.mode as c_int)
})?;
let fd = FileDesc::new(fd);
// Currently the standard library supports Linux 2.6.18 which did not
// have the O_CLOEXEC flag (passed above). If we're running on an older
// Linux kernel then the flag is just ignored by the OS. After we open
// the first file, we check whether it has CLOEXEC set. If it doesn't,
// we will explicitly ask for a CLOEXEC fd for every further file we
// open, if it does, we will skip that step.
//
// The CLOEXEC flag, however, is supported on versions of macOS/BSD/etc
// that we support, so we only do this on Linux currently.
fn ensure_cloexec(_: &FileDesc) -> io::Result<()> {
Ok(())
}
ensure_cloexec(&fd)?;
Ok(File(fd))
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt(unsafe {
::libc::fstat(self.0.raw(), &mut stat)
})?;
Ok(FileAttr { stat: stat })
}
pub fn fsync(&self) -> io::Result<()> {
cvt_r(|| unsafe { libc::fsync(self.0.raw()) })?;
Ok(())
}
pub fn datasync(&self) -> io::Result<()> {
cvt_r(|| unsafe { os_datasync(self.0.raw()) })?;
return Ok(());
unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) } //not supported
}
pub fn truncate(&self, size: u64) -> io::Result<()> {
return cvt_r(|| unsafe {
ftruncate(self.0.raw(), size as off_t)
}).map(|_| ());
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.0.read_at(buf, offset)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.0.write_vectored(bufs)
}
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.0.write_at(buf, offset)
}
pub fn flush(&self) -> io::Result<()> { Ok(()) }
pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
let (whence, pos) = match pos {
// Casting to `i64` is fine, too large values will end up as
// negative which will cause an error in `"lseek64"`.
SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
SeekFrom::End(off) => (libc::SEEK_END, off),
SeekFrom::Current(off) => (libc::SEEK_CUR, off),
};
let n = cvt(unsafe { lseek(self.0.raw(), pos, whence) })?;
Ok(n as u64)
}
pub fn duplicate(&self) -> io::Result<File> {
self.0.duplicate().map(File)
}
pub fn fd(&self) -> &FileDesc { &self.0 }
pub fn into_fd(self) -> FileDesc { self.0 }
pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?;
Ok(())
}
pub fn diverge(&self) -> ! {
panic!()
}
}
impl DirBuilder {
pub fn new() -> DirBuilder {
DirBuilder { mode: 0o777 }
}
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
let p = cstr(p)?;
cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
Ok(())
}
pub fn set_mode(&mut self, mode: u32) {
self.mode = mode as mode_t;
}
}
fn cstr(path: &Path) -> io::Result<CString> {
use crate::sys::vxworks::ext::ffi::OsStrExt;
Ok(CString::new(path.as_os_str().as_bytes())?)
}
impl FromInner<c_int> for File {
fn from_inner(fd: c_int) -> File {
File(FileDesc::new(fd))
}
}
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn get_path(_fd: c_int) -> Option<PathBuf> {
// FIXME(#:(): implement this for VxWorks
None
}
fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
// FIXME(#:(): implement this for VxWorks
None
}
let fd = self.0.raw();
let mut b = f.debug_struct("File");
b.field("fd", &fd);
if let Some(path) = get_path(fd) {
b.field("path", &path);
}
if let Some((read, write)) = get_mode(fd) {
b.field("read", &read).field("write", &write);
}
b.finish()
}
}
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
let root = p.to_path_buf();
let p = cstr(p)?;
unsafe {
let ptr = libc::opendir(p.as_ptr());
if ptr.is_null() {
Err(Error::last_os_error())
} else {
let inner = InnerReadDir { dirp: Dir(ptr), root };
Ok(ReadDir{
inner: Arc::new(inner),
end_of_stream: false,
})
}
}
}
pub fn unlink(p: &Path) -> io::Result<()> {
let p = cstr(p)?;
cvt(unsafe { libc::unlink(p.as_ptr()) })?;
Ok(())
}
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
let old = cstr(old)?;
let new = cstr(new)?;
cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
Ok(())
}
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
let p = cstr(p)?;
cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
Ok(())
}
pub fn rmdir(p: &Path) -> io::Result<()> {
let p = cstr(p)?;
cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
Ok(())
}
pub fn 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> {
let c_path = cstr(p)?;
let p = c_path.as_ptr();
let mut buf = Vec::with_capacity(256);
loop {
let buf_read = cvt(unsafe {
libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity())
})? as usize;
unsafe { buf.set_len(buf_read); }
if buf_read != buf.capacity() {
buf.shrink_to_fit();
return Ok(PathBuf::from(OsString::from_vec(buf)));
}
// Trigger the internal buffer resizing logic of `Vec` by requiring
// more space than the current capacity. The length is guaranteed to be
// the same as the capacity due to the if statement above.
buf.reserve(1);
}
}
pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
let src = cstr(src)?;
let dst = cstr(dst)?;
cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?;
Ok(())
}
pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
let src = cstr(src)?;
let dst = cstr(dst)?;
cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
Ok(())
}
pub fn stat(p: &Path) -> io::Result<FileAttr> {
let p = cstr(p)?;
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt(unsafe {
libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _)
})?;
Ok(FileAttr { stat })
}
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
let p = cstr(p)?;
let mut stat: stat64 = unsafe { mem::zeroed() };
cvt(unsafe {
::libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _)
})?;
Ok(FileAttr { stat })
}
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
use crate::sys::vxworks::ext::ffi::OsStrExt;
let path = CString::new(p.as_os_str().as_bytes())?;
let buf;
unsafe {
let r = libc::realpath(path.as_ptr(), ptr::null_mut());
if r.is_null() {
return Err(io::Error::last_os_error())
}
buf = CStr::from_ptr(r).to_bytes().to_vec();
libc::free(r as *mut _);
}
Ok(PathBuf::from(OsString::from_vec(buf)))
}
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
use crate::fs::File;
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)?;
writer.set_permissions(perm)?;
Ok(ret)
}

View file

@ -0,0 +1,62 @@
use crate::marker::PhantomData;
use crate::slice;
use libc::{iovec, c_void};
#[repr(transparent)]
pub struct IoSlice<'a> {
vec: iovec,
_p: PhantomData<&'a [u8]>,
}
impl<'a> IoSlice<'a> {
#[inline]
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
IoSlice {
vec: iovec {
iov_base: buf.as_ptr() as *mut u8 as *mut c_void,
iov_len: buf.len()
},
_p: PhantomData,
}
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len)
}
}
}
pub struct IoSliceMut<'a> {
vec: iovec,
_p: PhantomData<&'a mut [u8]>,
}
impl<'a> IoSliceMut<'a> {
#[inline]
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
IoSliceMut {
vec: iovec {
iov_base: buf.as_mut_ptr() as *mut c_void,
iov_len: buf.len()
},
_p: PhantomData,
}
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len)
}
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len)
}
}
}

View file

@ -0,0 +1,469 @@
macro_rules! unimpl {
() => (return Err(io::Error::new(io::ErrorKind::Other, "No networking available on L4Re."));)
}
pub mod net {
#![allow(warnings)]
use crate::fmt;
use crate::io::{self, IoVec, IoVecMut};
use crate::net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::sys::fd::FileDesc;
use crate::time::Duration;
use crate::convert::TryFrom;
#[allow(unused_extern_crates)]
pub extern crate libc as netc;
pub struct Socket(FileDesc);
impl Socket {
pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result<Socket> {
unimpl!();
}
pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result<Socket> {
unimpl!();
}
pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> {
unimpl!();
}
pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> {
unimpl!();
}
pub fn accept(&self, _: *mut libc::sockaddr, _: *mut libc::socklen_t)
-> io::Result<Socket> {
unimpl!();
}
pub fn duplicate(&self) -> io::Result<Socket> {
unimpl!();
}
pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
unimpl!();
}
pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> {
unimpl!();
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
unimpl!();
}
pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
unimpl!();
}
pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
unimpl!();
}
pub fn write(&self, _: &[u8]) -> io::Result<usize> {
unimpl!();
}
pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> {
unimpl!();
}
pub fn set_timeout(&self, _: Option<Duration>, _: libc::c_int) -> io::Result<()> {
unimpl!();
}
pub fn timeout(&self, _: libc::c_int) -> io::Result<Option<Duration>> {
unimpl!();
}
pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
unimpl!();
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unimpl!();
}
pub fn nodelay(&self) -> io::Result<bool> {
unimpl!();
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
unimpl!();
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
unimpl!();
}
}
impl AsInner<libc::c_int> for Socket {
fn as_inner(&self) -> &libc::c_int { self.0.as_inner() }
}
impl FromInner<libc::c_int> for Socket {
fn from_inner(fd: libc::c_int) -> Socket { Socket(FileDesc::new(fd)) }
}
impl IntoInner<libc::c_int> for Socket {
fn into_inner(self) -> libc::c_int { self.0.into_raw() }
}
pub struct TcpStream {
inner: Socket,
}
impl TcpStream {
pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
unimpl!();
}
pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
unimpl!();
}
pub fn socket(&self) -> &Socket { &self.inner }
pub fn into_socket(self) -> Socket { self.inner }
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
unimpl!();
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
unimpl!();
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
unimpl!();
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
unimpl!();
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
unimpl!();
}
pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
unimpl!();
}
pub fn read_vectored(&self, _: &mut [IoVecMut<'_>]) -> io::Result<usize> {
unimpl!();
}
pub fn write(&self, _: &[u8]) -> io::Result<usize> {
unimpl!();
}
pub fn write_vectored(&self, _: &[IoVec<'_>]) -> io::Result<usize> {
unimpl!();
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
unimpl!();
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
unimpl!();
}
pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
unimpl!();
}
pub fn duplicate(&self) -> io::Result<TcpStream> {
unimpl!();
}
pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
unimpl!();
}
pub fn nodelay(&self) -> io::Result<bool> {
unimpl!();
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
unimpl!();
}
pub fn ttl(&self) -> io::Result<u32> {
unimpl!();
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
unimpl!();
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
unimpl!();
}
}
impl FromInner<Socket> for TcpStream {
fn from_inner(socket: Socket) -> TcpStream {
TcpStream { inner: socket }
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "No networking support available on L4Re")
}
}
pub struct TcpListener {
inner: Socket,
}
impl TcpListener {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
unimpl!();
}
pub fn socket(&self) -> &Socket { &self.inner }
pub fn into_socket(self) -> Socket { self.inner }
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
unimpl!();
}
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
unimpl!();
}
pub fn duplicate(&self) -> io::Result<TcpListener> {
unimpl!();
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
unimpl!();
}
pub fn ttl(&self) -> io::Result<u32> {
unimpl!();
}
pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
unimpl!();
}
pub fn only_v6(&self) -> io::Result<bool> {
unimpl!();
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
unimpl!();
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
unimpl!();
}
}
impl FromInner<Socket> for TcpListener {
fn from_inner(socket: Socket) -> TcpListener {
TcpListener { inner: socket }
}
}
impl fmt::Debug for TcpListener {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "No networking support available on L4Re.")
}
}
pub struct UdpSocket {
inner: Socket,
}
impl UdpSocket {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
unimpl!();
}
pub fn socket(&self) -> &Socket { &self.inner }
pub fn into_socket(self) -> Socket { self.inner }
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
unimpl!();
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
unimpl!();
}
pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
unimpl!();
}
pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
unimpl!();
}
pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
unimpl!();
}
pub fn duplicate(&self) -> io::Result<UdpSocket> {
unimpl!();
}
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
unimpl!();
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
unimpl!();
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
unimpl!();
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
unimpl!();
}
pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
unimpl!();
}
pub fn broadcast(&self) -> io::Result<bool> {
unimpl!();
}
pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
unimpl!();
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
unimpl!();
}
pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
unimpl!();
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
unimpl!();
}
pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
unimpl!();
}
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
unimpl!();
}
pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
-> io::Result<()> {
unimpl!();
}
pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32)
-> io::Result<()> {
unimpl!();
}
pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr)
-> io::Result<()> {
unimpl!();
}
pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32)
-> io::Result<()> {
unimpl!();
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
unimpl!();
}
pub fn ttl(&self) -> io::Result<u32> {
unimpl!();
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
unimpl!();
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
unimpl!();
}
pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
unimpl!();
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
unimpl!();
}
pub fn send(&self, _: &[u8]) -> io::Result<usize> {
unimpl!();
}
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
unimpl!();
}
}
impl FromInner<Socket> for UdpSocket {
fn from_inner(socket: Socket) -> UdpSocket {
UdpSocket { inner: socket }
}
}
impl fmt::Debug for UdpSocket {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "No networking support on L4Re available.")
}
}
pub struct LookupHost {
original: *mut libc::addrinfo,
cur: *mut libc::addrinfo,
}
impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
None
}
}
impl LookupHost {
pub fn port(&self) -> u16 {
unimpl!();
}
}
unsafe impl Sync for LookupHost {}
unsafe impl Send for LookupHost {}
impl TryFrom<&str> for LookupHost {
type Error = io::Error;
fn try_from(_v: &str) -> io::Result<LookupHost> {
unimpl!();
}
}
impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
type Error = io::Error;
fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
unimpl!();
}
}
}

View file

@ -0,0 +1,43 @@
// Original implementation taken from rust-memchr.
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
let p = unsafe {
libc::memchr(
haystack.as_ptr() as *const libc::c_void,
needle as libc::c_int,
haystack.len())
};
if p.is_null() {
None
} else {
Some(p as usize - (haystack.as_ptr() as usize))
}
}
pub fn memrchr(needle: u8, haystack: &[u8]) -> Option<usize> {
#[cfg(target_os = "linux")]
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
// GNU's memrchr() will - unlike memchr() - error if haystack is empty.
if haystack.is_empty() {return None}
let p = unsafe {
libc::memrchr(
haystack.as_ptr() as *const libc::c_void,
needle as libc::c_int,
haystack.len())
};
if p.is_null() {
None
} else {
Some(p as usize - (haystack.as_ptr() as usize))
}
}
#[cfg(not(target_os = "linux"))]
fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option<usize> {
core::slice::memchr::memrchr(needle, haystack)
}
memrchr_specific(needle, haystack)
}

View file

@ -0,0 +1,139 @@
#![allow(dead_code)]
#![allow(missing_docs, nonstandard_style)]
use crate::io::ErrorKind;
pub use crate::os::vxworks as platform;
pub use self::rand::hashmap_random_keys;
pub use libc::strlen;
#[macro_use]
pub mod weak;
pub mod alloc;
pub mod args;
pub mod android;
//#[cfg(feature = "backtrace")]
//pub mod backtrace;
pub mod cmath;
pub mod condvar;
pub mod env;
pub mod ext;
pub mod fast_thread_local;
pub mod fd;
pub mod fs;
pub mod memchr;
pub mod io;
pub mod mutex;
#[cfg(not(target_os = "l4re"))]
pub mod net;
#[cfg(target_os = "l4re")]
mod l4re;
#[cfg(target_os = "l4re")]
pub use self::l4re::net;
pub mod os;
pub mod path;
pub mod pipe;
pub mod process;
pub mod rand;
pub mod rwlock;
pub mod stack_overflow;
pub mod thread;
pub mod thread_local;
pub mod time;
pub mod stdio;
pub use crate::sys_common::os_str_bytes as os_str;
#[cfg(not(test))]
pub fn init() {
// By default, some platforms will send a *signal* when an EPIPE error
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
// handler, causing it to kill the program, which isn't exactly what we
// want!
//
// Hence, we set SIGPIPE to ignore when the program starts up in order
// to prevent this problem.
unsafe {
reset_sigpipe();
}
unsafe fn reset_sigpipe() { }
}
#[cfg(target_os = "android")]
pub use crate::sys::android::signal;
#[cfg(not(target_os = "android"))]
pub use libc::signal;
pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as libc::c_int {
libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
libc::ECONNRESET => ErrorKind::ConnectionReset,
libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
libc::EPIPE => ErrorKind::BrokenPipe,
libc::ENOTCONN => ErrorKind::NotConnected,
libc::ECONNABORTED => ErrorKind::ConnectionAborted,
libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
libc::EADDRINUSE => ErrorKind::AddrInUse,
libc::ENOENT => ErrorKind::NotFound,
libc::EINTR => ErrorKind::Interrupted,
libc::EINVAL => ErrorKind::InvalidInput,
libc::ETIMEDOUT => ErrorKind::TimedOut,
libc::EEXIST => ErrorKind::AlreadyExists,
// These two constants can have the same value on some systems,
// but different values on others, so we can't use a match
// clause
x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
ErrorKind::WouldBlock,
_ => ErrorKind::Other,
}
}
#[doc(hidden)]
pub trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}
macro_rules! impl_is_minus_one {
($($t:ident)*) => ($(impl IsMinusOne for $t {
fn is_minus_one(&self) -> bool {
*self == -1
}
})*)
}
impl_is_minus_one! { i8 i16 i32 i64 isize }
pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
if t.is_minus_one() {
Err(crate::io::Error::last_os_error())
} else {
Ok(t)
}
}
pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
where T: IsMinusOne,
F: FnMut() -> T
{
loop {
match cvt(f()) {
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
other => return other,
}
}
}
// On Unix-like platforms, libc::abort will unregister signal handlers
// including the SIGABRT handler, preventing the abort from being blocked, and
// fclose streams, with the side effect of flushing them so libc bufferred
// output will be printed. Additionally the shell will generally print a more
// understandable error message like "Abort trap" rather than "Illegal
// instruction" that intrinsics::abort would cause, as intrinsics::abort is
// implemented as an illegal instruction.
pub unsafe fn abort_internal() -> ! {
libc::abort()
}

View file

@ -0,0 +1,127 @@
use crate::cell::UnsafeCell;
use crate::mem::MaybeUninit;
pub struct Mutex { inner: UnsafeCell<libc::pthread_mutex_t> }
#[inline]
pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t {
m.inner.get()
}
unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}
#[allow(dead_code)] // sys isn't exported yet
impl Mutex {
pub const fn new() -> Mutex {
// Might be moved to a different address, so it is better to avoid
// initialization of potentially opaque OS data before it landed.
// Be very careful using this newly constructed `Mutex`, reentrant
// locking is undefined behavior until `init` is called!
Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
}
#[inline]
pub unsafe fn init(&mut self) {
// Issue #33770
//
// A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
// a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you
// try to re-lock it from the same thread when you already hold a lock.
//
// In practice, glibc takes advantage of this undefined behavior to
// implement hardware lock elision, which uses hardware transactional
// memory to avoid acquiring the lock. While a transaction is in
// progress, the lock appears to be unlocked. This isn't a problem for
// other threads since the transactional memory will abort if a conflict
// is detected, however no abort is generated if re-locking from the
// same thread.
//
// Since locking the same mutex twice will result in two aliasing &mut
// references, we instead create the mutex with type
// PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to
// re-lock it from the same thread, thus avoiding undefined behavior.
let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
let r = libc::pthread_mutexattr_init(attr.as_mut_ptr());
debug_assert_eq!(r, 0);
let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL);
debug_assert_eq!(r, 0);
let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr());
debug_assert_eq!(r, 0);
let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
debug_assert_eq!(r, 0);
}
#[inline]
pub unsafe fn lock(&self) {
let r = libc::pthread_mutex_lock(self.inner.get());
debug_assert_eq!(r, 0);
}
#[inline]
pub unsafe fn unlock(&self) {
let r = libc::pthread_mutex_unlock(self.inner.get());
debug_assert_eq!(r, 0);
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
libc::pthread_mutex_trylock(self.inner.get()) == 0
}
#[inline]
#[cfg(not(target_os = "dragonfly"))]
pub unsafe fn destroy(&self) {
let r = libc::pthread_mutex_destroy(self.inner.get());
debug_assert_eq!(r, 0);
}
#[inline]
#[cfg(target_os = "dragonfly")]
pub unsafe fn destroy(&self) {
let r = libc::pthread_mutex_destroy(self.inner.get());
// On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
// mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
// Once it is used (locked/unlocked) or pthread_mutex_init() is called,
// this behaviour no longer occurs.
debug_assert!(r == 0 || r == libc::EINVAL);
}
}
pub struct ReentrantMutex { inner: UnsafeCell<libc::pthread_mutex_t> }
unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}
impl ReentrantMutex {
pub unsafe fn uninitialized() -> ReentrantMutex {
ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
}
pub unsafe fn init(&mut self) {
let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
let result = libc::pthread_mutexattr_init(attr.as_mut_ptr());
debug_assert_eq!(result, 0);
let result = libc::pthread_mutexattr_settype(attr.as_mut_ptr(),
libc::PTHREAD_MUTEX_RECURSIVE);
debug_assert_eq!(result, 0);
let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr());
debug_assert_eq!(result, 0);
let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
debug_assert_eq!(result, 0);
}
pub unsafe fn lock(&self) {
let result = libc::pthread_mutex_lock(self.inner.get());
debug_assert_eq!(result, 0);
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
libc::pthread_mutex_trylock(self.inner.get()) == 0
}
pub unsafe fn unlock(&self) {
let result = libc::pthread_mutex_unlock(self.inner.get());
debug_assert_eq!(result, 0);
}
pub unsafe fn destroy(&self) {
let result = libc::pthread_mutex_destroy(self.inner.get());
debug_assert_eq!(result, 0);
}
}

View file

@ -0,0 +1,374 @@
use crate::ffi::CStr;
use crate::io;
use crate::io::{IoSlice, IoSliceMut};
use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK};
use crate::mem;
use crate::net::{SocketAddr, Shutdown};
use crate::str;
use crate::sys::fd::FileDesc;
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
use crate::time::{Duration, Instant};
use crate::cmp;
pub use crate::sys::{cvt, cvt_r};
#[allow(unused_extern_crates)]
pub extern crate libc as netc;
pub type wrlen_t = size_t;
const SOCK_CLOEXEC: c_int = 0;
const SO_NOSIGPIPE: c_int = 0;
pub struct Socket(FileDesc);
pub fn init() {}
pub fn cvt_gai(err: c_int) -> io::Result<()> {
if err == 0 {
return Ok(())
}
// We may need to trigger a glibc workaround. See on_resolver_failure() for details.
on_resolver_failure();
if err == EAI_SYSTEM {
return Err(io::Error::last_os_error())
}
let detail = unsafe {
str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap()
.to_owned()
};
Err(io::Error::new(io::ErrorKind::Other,
&format!("failed to lookup address information: {}",
detail)[..]))
}
impl Socket {
pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> {
let fam = match *addr {
SocketAddr::V4(..) => libc::AF_INET,
SocketAddr::V6(..) => libc::AF_INET6,
};
Socket::new_raw(fam, ty)
}
pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
unsafe {
// On linux we first attempt to pass the SOCK_CLOEXEC flag to
// atomically create the socket and set it as CLOEXEC. Support for
// this option, however, was added in 2.6.27, and we still support
// 2.6.18 as a kernel, so if the returned error is EINVAL we
// fallthrough to the fallback.
if cfg!(target_os = "linux") {
match cvt(libc::socket(fam, ty | SOCK_CLOEXEC, 0)) {
Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
Err(e) => return Err(e),
}
}
let fd = cvt(libc::socket(fam, ty, 0))?;
let fd = FileDesc::new(fd);
fd.set_cloexec()?;
let socket = Socket(fd);
Ok(socket)
}
}
pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> {
unimplemented!();
}
pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
let r = unsafe {
let (addrp, len) = addr.into_inner();
cvt(libc::connect(self.0.raw(), addrp, len))
};
self.set_nonblocking(false)?;
match r {
Ok(_) => return Ok(()),
// there's no ErrorKind for EINPROGRESS :(
Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
Err(e) => return Err(e),
}
let mut pollfd = libc::pollfd {
fd: self.0.raw(),
events: libc::POLLOUT,
revents: 0,
};
if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
"cannot set a 0 duration timeout"));
}
let start = Instant::now();
loop {
let elapsed = start.elapsed();
if elapsed >= timeout {
return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out"));
}
let timeout = timeout - elapsed;
let mut timeout = timeout.as_secs()
.saturating_mul(1_000)
.saturating_add(timeout.subsec_nanos() as u64 / 1_000_000);
if timeout == 0 {
timeout = 1;
}
let timeout = cmp::min(timeout, c_int::max_value() as u64) as c_int;
match unsafe { libc::poll(&mut pollfd, 1, timeout) } {
-1 => {
let err = io::Error::last_os_error();
if err.kind() != io::ErrorKind::Interrupted {
return Err(err);
}
}
0 => {}
_ => {
// linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
// for POLLHUP rather than read readiness
if pollfd.revents & libc::POLLHUP != 0 {
let e = self.take_error()?
.unwrap_or_else(|| {
io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP")
});
return Err(e);
}
return Ok(());
}
}
}
}
pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t)
-> io::Result<Socket> {
// Unfortunately the only known way right now to accept a socket and
// atomically set the CLOEXEC flag is to use the `accept4` syscall on
// Linux. This was added in 2.6.28, however, and because we support
// 2.6.18 we must detect this support dynamically.
let fd = cvt_r(|| unsafe {
libc::accept(self.0.raw(), storage, len)
})?;
let fd = FileDesc::new(fd);
fd.set_cloexec()?;
Ok(Socket(fd))
}
pub fn duplicate(&self) -> io::Result<Socket> {
self.0.duplicate().map(Socket)
}
fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::recv(self.0.raw(),
buf.as_mut_ptr() as *mut c_void,
buf.len(),
flags)
})?;
Ok(ret as usize)
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.recv_with_flags(buf, 0)
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.recv_with_flags(buf, MSG_PEEK)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
fn recv_from_with_flags(&self, buf: &mut [u8], flags: c_int)
-> io::Result<(usize, SocketAddr)> {
let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t;
let n = cvt(unsafe {
libc::recvfrom(self.0.raw(),
buf.as_mut_ptr() as *mut c_void,
buf.len(),
flags,
&mut storage as *mut _ as *mut _,
&mut addrlen)
})?;
Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?))
}
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
self.recv_from_with_flags(buf, 0)
}
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
self.recv_from_with_flags(buf, MSG_PEEK)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.0.write_vectored(bufs)
}
pub fn set_timeout(&self, dur: Option<Duration>, kind: libc::c_int) -> io::Result<()> {
let timeout = match dur {
Some(dur) => {
if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
"cannot set a 0 duration timeout"));
}
let secs = if dur.as_secs() > libc::time_t::max_value() as u64 {
libc::time_t::max_value()
} else {
dur.as_secs() as libc::time_t
};
let mut timeout = libc::timeval {
tv_sec: secs,
tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t,
};
if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
timeout.tv_usec = 1;
}
timeout
}
None => {
libc::timeval {
tv_sec: 0,
tv_usec: 0,
}
}
};
setsockopt(self, libc::SOL_SOCKET, kind, timeout)
}
pub fn timeout(&self, kind: libc::c_int) -> io::Result<Option<Duration>> {
let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?;
if raw.tv_sec == 0 && raw.tv_usec == 0 {
Ok(None)
} else {
let sec = raw.tv_sec as u64;
let nsec = (raw.tv_usec as u32) * 1000;
Ok(Some(Duration::new(sec, nsec)))
}
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
let how = match how {
Shutdown::Write => libc::SHUT_WR,
Shutdown::Read => libc::SHUT_RD,
Shutdown::Both => libc::SHUT_RDWR,
};
cvt(unsafe { libc::shutdown(self.0.raw(), how) })?;
Ok(())
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int)
}
pub fn nodelay(&self) -> io::Result<bool> {
let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?;
Ok(raw != 0)
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
let mut nonblocking = nonblocking as libc::c_int;
cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(|_| ())
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
if raw == 0 {
Ok(None)
} else {
Ok(Some(io::Error::from_raw_os_error(raw as i32)))
}
}
}
impl AsInner<c_int> for Socket {
fn as_inner(&self) -> &c_int { self.0.as_inner() }
}
impl FromInner<c_int> for Socket {
fn from_inner(fd: c_int) -> Socket { Socket(FileDesc::new(fd)) }
}
impl IntoInner<c_int> for Socket {
fn into_inner(self) -> c_int { self.0.into_raw() }
}
// In versions of glibc prior to 2.26, there's a bug where the DNS resolver
// will cache the contents of /etc/resolv.conf, so changes to that file on disk
// can be ignored by a long-running program. That can break DNS lookups on e.g.
// laptops where the network comes and goes. See
// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some
// distros including Debian have patched glibc to fix this for a long time.
//
// A workaround for this bug is to call the res_init libc function, to clear
// the cached configs. Unfortunately, while we believe glibc's implementation
// of res_init is thread-safe, we know that other implementations are not
// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could
// try to synchronize its res_init calls with a Mutex, but that wouldn't
// protect programs that call into libc in other ways. So instead of calling
// res_init unconditionally, we call it only when we detect we're linking
// against glibc version < 2.26. (That is, when we both know its needed and
// believe it's thread-safe).
#[cfg(target_env = "gnu")]
fn on_resolver_failure() {
/*
use crate::sys;
// If the version fails to parse, we treat it the same as "not glibc".
if let Some(version) = sys::os::glibc_version() {
if version < (2, 26) {
unsafe { libc::res_init() };
}
}
*/
}
#[cfg(not(target_env = "gnu"))]
fn on_resolver_failure() {}
#[cfg(all(test, taget_env = "gnu"))]
mod test {
use super::*;
#[test]
fn test_res_init() {
// This mostly just tests that the weak linkage doesn't panic wildly...
res_init_if_glibc_before_2_26().unwrap();
}
#[test]
fn test_parse_glibc_version() {
let cases = [
("0.0", Some((0, 0))),
("01.+2", Some((1, 2))),
("3.4.5.six", Some((3, 4))),
("1", None),
("1.-2", None),
("1.foo", None),
("foo.1", None),
];
for &(version_str, parsed) in cases.iter() {
assert_eq!(parsed, parse_glibc_version(version_str));
}
}
}

View file

@ -0,0 +1,324 @@
use crate::error::Error as StdError;
use crate::ffi::{CString, CStr, OsString, OsStr};
use crate::fmt;
use crate::io;
use crate::iter;
use libc::{self, c_int, c_char /*,c_void */};
use crate::marker::PhantomData;
use crate::mem;
use crate::memchr;
use crate::path::{self, PathBuf, Path};
use crate::ptr;
use crate::slice;
use crate::str;
use crate::sys_common::mutex::Mutex;
use crate::sys::cvt;
/*use sys::fd; this one is probably important */
use crate::vec;
const TMPBUF_SZ: usize = 128;
static ENV_LOCK: Mutex = Mutex::new();
// This is a terrible fix
use crate::sys::os_str::Buf;
use crate::sys_common::{FromInner, IntoInner, AsInner};
pub trait OsStringExt {
fn from_vec(vec: Vec<u8>) -> Self;
fn into_vec(self) -> Vec<u8>;
}
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
}
}
pub trait OsStrExt {
fn from_bytes(slice: &[u8]) -> &Self;
fn as_bytes(&self) -> &[u8];
}
impl OsStrExt for OsStr {
fn from_bytes(slice: &[u8]) -> &OsStr {
unsafe { mem::transmute(slice) }
}
fn as_bytes(&self) -> &[u8] {
&self.as_inner().inner
}
}
pub fn errno() -> i32 {
unsafe { libc::errnoGet() }
}
pub fn set_errno(e: i32) {
unsafe { libc::errnoSet(e as c_int); }
}
/// Gets a detailed string description for the given error number.
pub fn error_string(errno: i32) -> String {
let mut buf = [0 as c_char; TMPBUF_SZ];
extern {
fn strerror_r(
errnum: c_int,
buf: *mut c_char,
buflen: libc::size_t
) -> c_int;
}
let p = buf.as_mut_ptr();
unsafe {
if strerror_r(errno as c_int, p, buf.len()) < 0 {
panic!("strerror_r failure");
}
let p = p as *const _;
str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned()
}
}
pub fn getcwd() -> io::Result<PathBuf> {
let mut buf = Vec::with_capacity(512);
loop {
unsafe {
let ptr = buf.as_mut_ptr() as *mut libc::c_char;
if !libc::getcwd(ptr, buf.capacity() as libc::size_t).is_null() {
let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
buf.set_len(len);
buf.shrink_to_fit();
return Ok(PathBuf::from(OsString::from_vec(buf)));
} else {
let error = io::Error::last_os_error();
if error.raw_os_error() != Some(libc::ERANGE) {
return Err(error);
}
}
// Trigger the internal buffer resizing logic of `Vec` by requiring
// more space than the current capacity.
let cap = buf.capacity();
buf.set_len(cap);
buf.reserve(1);
}
}
}
pub fn chdir(p: &path::Path) -> io::Result<()> {
let p: &OsStr = p.as_ref();
let p = CString::new(p.as_bytes())?;
unsafe {
match libc::chdir(p.as_ptr()) == (0 as c_int) {
true => Ok(()),
false => Err(io::Error::last_os_error()),
}
}
}
pub struct SplitPaths<'a> {
iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>,
fn(&'a [u8]) -> PathBuf>,
}
pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
fn bytes_to_path(b: &[u8]) -> PathBuf {
PathBuf::from(<OsStr as OsStrExt>::from_bytes(b))
}
fn is_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> {
#[cfg(test)]
use realstd::env;
#[cfg(not(test))]
use crate::env;
let exe_path = env::args().next().unwrap();
let path = Path::new(&exe_path);
path.canonicalize()
}
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() }
}
pub unsafe fn environ() -> *mut *const *const c_char {
extern { static mut environ: *const *const c_char; }
&mut environ
}
/// Returns a vector of (variable, value) byte-vector pairs for all the
/// environment variables of the current process.
pub fn env() -> Env {
unsafe {
let _guard = ENV_LOCK.lock();
let mut environ = *environ();
if environ == ptr::null() {
panic!("os::env() failure getting env string from OS: {}",
io::Error::last_os_error());
}
let mut result = Vec::new();
while *environ != ptr::null() {
if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
result.push(key_value);
}
environ = environ.offset(1);
}
return Env {
iter: result.into_iter(),
_dont_send_or_sync_me: PhantomData,
}
}
fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
// Strategy (copied from glibc): Variable name and value are separated
// by an ASCII equals sign '='. Since a variable name must not be
// empty, allow variable names starting with an equals sign. Skip all
// malformed lines.
if input.is_empty() {
return None;
}
let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
pos.map(|p| (
OsStringExt::from_vec(input[..p].to_vec()),
OsStringExt::from_vec(input[p+1..].to_vec()),
))
}
}
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
// environment variables with a nul byte can't be set, so their value is
// always None as well
let k = CString::new(k.as_bytes())?;
unsafe {
let _guard = ENV_LOCK.lock();
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
let ret = if s.is_null() {
None
} else {
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
};
Ok(ret)
}
}
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let k = CString::new(k.as_bytes())?;
let v = CString::new(v.as_bytes())?;
unsafe {
let _guard = ENV_LOCK.lock();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ())
}
}
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
let nbuf = CString::new(n.as_bytes())?;
unsafe {
let _guard = ENV_LOCK.lock();
cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ())
}
}
pub fn page_size() -> usize {
unsafe {
libc::sysconf(libc::_SC_PAGESIZE) as usize
}
}
pub fn temp_dir() -> PathBuf {
crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
PathBuf::from("/tmp")
})
}
pub fn home_dir() -> Option<PathBuf> {
return crate::env::var_os("HOME").or_else(|| unsafe {
fallback()
}).map(PathBuf::from);
unsafe fn fallback() -> Option<OsString> {
let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
n if n < 0 => 512 as usize,
n => n as usize,
};
let mut buf = Vec::with_capacity(amt);
let mut passwd: libc::passwd = mem::zeroed();
let mut result = ptr::null_mut();
match libc::getpwuid_r(libc::getuid(), &mut passwd, buf.as_mut_ptr(),
buf.capacity(), &mut result) {
0 if !result.is_null() => {
let ptr = passwd.pw_dir as *const _;
let bytes = CStr::from_ptr(ptr).to_bytes().to_vec();
Some(OsStringExt::from_vec(bytes))
},
_ => None,
}
}
}
pub fn exit(code: i32) -> ! {
unsafe { libc::exit(code as c_int) }
}
pub fn getpid() -> u32 {
unsafe { libc::getpid() as u32 }
}
pub fn getppid() -> u32 {
unsafe { libc::getppid() as u32 }
}

View file

@ -0,0 +1,19 @@
use crate::path::Prefix;
use crate::ffi::OsStr;
#[inline]
pub fn is_sep_byte(b: u8) -> bool {
b == b'/'
}
#[inline]
pub fn is_verbatim_sep(b: u8) -> bool {
b == b'/'
}
pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
None
}
pub const MAIN_SEP_STR: &str = "/";
pub const MAIN_SEP: char = '/';

View file

@ -0,0 +1,100 @@
use crate::io::{self, IoSlice, IoSliceMut};
use libc::{self /*, c_int apparently not used? */};
use crate::mem;
use crate::sync::atomic::{AtomicBool};
use crate::sys::fd::FileDesc;
use crate::sys::{cvt, cvt_r};
pub struct AnonPipe(FileDesc);
pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
static INVALID: AtomicBool = AtomicBool::new(false);
let mut fds = [0; 2];
// Unfortunately the only known way right now to create atomically set the
// CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in
// 2.6.27, however, and because we support 2.6.18 we must detect this
// support dynamically.
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
let fd0 = FileDesc::new(fds[0]);
let fd1 = FileDesc::new(fds[1]);
fd0.set_cloexec()?;
fd1.set_cloexec()?;
Ok((AnonPipe(fd0), AnonPipe(fd1)))
}
impl AnonPipe {
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.0.write_vectored(bufs)
}
pub fn fd(&self) -> &FileDesc { &self.0 }
pub fn into_fd(self) -> FileDesc { self.0 }
pub fn diverge(&self) -> ! {
panic!()
}
}
pub fn read2(p1: AnonPipe,
v1: &mut Vec<u8>,
p2: AnonPipe,
v2: &mut Vec<u8>) -> io::Result<()> {
// Set both pipes into nonblocking mode as we're gonna be reading from both
// in the `select` loop below, and we wouldn't want one to block the other!
let p1 = p1.into_fd();
let p2 = p2.into_fd();
p1.set_nonblocking_pipe(true)?;
p2.set_nonblocking_pipe(true)?;
let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
fds[0].fd = p1.raw();
fds[0].events = libc::POLLIN;
fds[1].fd = p2.raw();
fds[1].events = libc::POLLIN;
loop {
// wait for either pipe to become readable using `poll`
cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?;
if fds[0].revents != 0 && read(&p1, v1)? {
p2.set_nonblocking_pipe(false)?;
return p2.read_to_end(v2).map(|_| ());
}
if fds[1].revents != 0 && read(&p2, v2)? {
p1.set_nonblocking_pipe(false)?;
return p1.read_to_end(v1).map(|_| ());
}
}
// Read as much as we can from each pipe, ignoring EWOULDBLOCK or
// EAGAIN. If we hit EOF, then this will happen because the underlying
// reader will return Ok(0), in which case we'll see `Ok` ourselves. In
// this case we flip the other fd back into blocking mode and read
// whatever's leftover on that file descriptor.
fn read(fd: &FileDesc, dst: &mut Vec<u8>) -> Result<bool, io::Error> {
match fd.read_to_end(dst) {
Ok(_) => Ok(true),
Err(e) => {
if e.raw_os_error() == Some(libc::EWOULDBLOCK) ||
e.raw_os_error() == Some(libc::EAGAIN) {
Ok(false)
} else {
Err(e)
}
}
}
}
}

View file

@ -0,0 +1,7 @@
pub use self::process_common::{Command, ExitStatus, ExitCode, Stdio, StdioPipes};
pub use self::process_inner::Process;
mod process_common;
#[path = "process_vxworks.rs"]
mod process_inner;
mod rtp;

View file

@ -0,0 +1,503 @@
use crate::os::unix::prelude::*;
use crate::ffi::{OsString, OsStr, CString, CStr};
use crate::fmt;
use crate::io;
use crate::ptr;
use crate::sys::fd::FileDesc;
use crate::sys::fs::{File, OpenOptions};
use crate::sys::pipe::{self, AnonPipe};
use crate::sys_common::process::{CommandEnv, DefaultEnvKey};
use crate::collections::BTreeMap;
use libc::{c_int, gid_t, uid_t, c_char, EXIT_SUCCESS, EXIT_FAILURE};
////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////
pub struct Command {
// Currently we try hard to ensure that the call to `.exec()` doesn't
// actually allocate any memory. While many platforms try to ensure that
// memory allocation works after a fork in a multithreaded process, it's
// been observed to be buggy and somewhat unreliable, so we do our best to
// just not do it at all!
//
// Along those lines, the `argv` and `envp` raw pointers here are exactly
// what's gonna get passed to `execvp`. The `argv` array starts with the
// `program` and ends with a NULL, and the `envp` pointer, if present, is
// also null-terminated.
//
// Right now we don't support removing arguments, so there's no much fancy
// support there, but we support adding and removing environment variables,
// so a side table is used to track where in the `envp` array each key is
// located. Whenever we add a key we update it in place if it's already
// present, and whenever we remove a key we update the locations of all
// other keys.
program: CString,
args: Vec<CString>,
argv: Argv,
env: CommandEnv<DefaultEnvKey>,
cwd: Option<CString>,
uid: Option<uid_t>,
gid: Option<gid_t>,
saw_nul: bool,
closures: Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>>,
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
}
// Create a new type for argv, so that we can make it `Send`
struct Argv(Vec<*const c_char>);
// It is safe to make Argv Send, because it contains pointers to memory owned by `Command.args`
unsafe impl Send for Argv {}
// passed back to std::process with the pipes connected to the child, if any
// were requested
pub struct StdioPipes {
pub stdin: Option<AnonPipe>,
pub stdout: Option<AnonPipe>,
pub stderr: Option<AnonPipe>,
}
// passed to do_exec() with configuration of what the child stdio should look
// like
pub struct ChildPipes {
pub stdin: ChildStdio,
pub stdout: ChildStdio,
pub stderr: ChildStdio,
}
pub enum ChildStdio {
Inherit,
Explicit(c_int),
Owned(FileDesc),
}
pub enum Stdio {
Inherit,
Null,
MakePipe,
Fd(FileDesc),
}
impl Command {
pub fn new(program: &OsStr) -> Command {
let mut saw_nul = false;
let program = os2c(program, &mut saw_nul);
Command {
argv: Argv(vec![program.as_ptr(), ptr::null()]),
program,
args: Vec::new(),
env: Default::default(),
cwd: None,
uid: None,
gid: None,
saw_nul,
closures: Vec::new(),
stdin: None,
stdout: None,
stderr: None,
}
}
pub fn arg(&mut self, arg: &OsStr) {
// Overwrite the trailing NULL pointer in `argv` and then add a new null
// pointer.
let arg = os2c(arg, &mut self.saw_nul);
self.argv.0[self.args.len() + 1] = arg.as_ptr();
self.argv.0.push(ptr::null());
// Also make sure we keep track of the owned value to schedule a
// destructor for this memory.
self.args.push(arg);
}
pub fn cwd(&mut self, dir: &OsStr) {
self.cwd = Some(os2c(dir, &mut self.saw_nul));
}
pub fn uid(&mut self, id: uid_t) {
self.uid = Some(id);
}
pub fn gid(&mut self, id: gid_t) {
self.gid = Some(id);
}
pub fn saw_nul(&self) -> bool {
self.saw_nul
}
pub fn get_argv(&self) -> &Vec<*const c_char> {
&self.argv.0
}
#[allow(dead_code)]
pub fn get_cwd(&self) -> &Option<CString> {
&self.cwd
}
#[allow(dead_code)]
pub fn get_uid(&self) -> Option<uid_t> {
self.uid
}
#[allow(dead_code)]
pub fn get_gid(&self) -> Option<gid_t> {
self.gid
}
pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> {
&mut self.closures
}
pub unsafe fn pre_exec(
&mut self,
_f: Box<dyn FnMut() -> io::Result<()> + Send + Sync>,
) {
// Fork() is not supported in vxWorks so no way to run the closure in the new procecss.
unimplemented!();;
}
pub fn stdin(&mut self, stdin: Stdio) {
self.stdin = Some(stdin);
}
pub fn stdout(&mut self, stdout: Stdio) {
self.stdout = Some(stdout);
}
pub fn stderr(&mut self, stderr: Stdio) {
self.stderr = Some(stderr);
}
pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> {
&mut self.env
}
pub fn capture_env(&mut self) -> Option<CStringArray> {
let maybe_env = self.env.capture_if_changed();
maybe_env.map(|env| construct_envp(env, &mut self.saw_nul))
}
#[allow(dead_code)]
pub fn env_saw_path(&self) -> bool {
self.env.have_changed_path()
}
pub fn setup_io(&self, default: Stdio, needs_stdin: bool)
-> io::Result<(StdioPipes, ChildPipes)> {
let null = Stdio::Null;
let default_stdin = if needs_stdin {&default} else {&null};
let stdin = self.stdin.as_ref().unwrap_or(default_stdin);
let stdout = self.stdout.as_ref().unwrap_or(&default);
let stderr = self.stderr.as_ref().unwrap_or(&default);
let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?;
let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?;
let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?;
let ours = StdioPipes {
stdin: our_stdin,
stdout: our_stdout,
stderr: our_stderr,
};
let theirs = ChildPipes {
stdin: their_stdin,
stdout: their_stdout,
stderr: their_stderr,
};
Ok((ours, theirs))
}
}
fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
CString::new(s.as_bytes()).unwrap_or_else(|_e| {
*saw_nul = true;
CString::new("<string-with-nul>").unwrap()
})
}
// Helper type to manage ownership of the strings within a C-style array.
pub struct CStringArray {
items: Vec<CString>,
ptrs: Vec<*const c_char>
}
impl CStringArray {
pub fn with_capacity(capacity: usize) -> Self {
let mut result = CStringArray {
items: Vec::with_capacity(capacity),
ptrs: Vec::with_capacity(capacity+1)
};
result.ptrs.push(ptr::null());
result
}
pub fn push(&mut self, item: CString) {
let l = self.ptrs.len();
self.ptrs[l-1] = item.as_ptr();
self.ptrs.push(ptr::null());
self.items.push(item);
}
pub fn as_ptr(&self) -> *const *const c_char {
self.ptrs.as_ptr()
}
}
fn construct_envp(env: BTreeMap<DefaultEnvKey, OsString>, saw_nul: &mut bool) -> CStringArray {
let mut result = CStringArray::with_capacity(env.len());
for (k, v) in env {
let mut k: OsString = k.into();
// Reserve additional space for '=' and null terminator
k.reserve_exact(v.len() + 2);
k.push("=");
k.push(&v);
// Add the new entry into the array
if let Ok(item) = CString::new(k.into_vec()) {
result.push(item);
} else {
*saw_nul = true;
}
}
result
}
impl Stdio {
pub fn to_child_stdio(&self, readable: bool)
-> io::Result<(ChildStdio, Option<AnonPipe>)> {
match *self {
Stdio::Inherit => {
Ok((ChildStdio::Inherit, None))
},
// Make sure that the source descriptors are not an stdio
// descriptor, otherwise the order which we set the child's
// descriptors may blow away a descriptor which we are hoping to
// save. For example, suppose we want the child's stderr to be the
// parent's stdout, and the child's stdout to be the parent's
// stderr. No matter which we dup first, the second will get
// overwritten prematurely.
Stdio::Fd(ref fd) => {
if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO {
Ok((ChildStdio::Owned(fd.duplicate()?), None))
} else {
Ok((ChildStdio::Explicit(fd.raw()), None))
}
}
Stdio::MakePipe => {
let (reader, writer) = pipe::anon_pipe()?;
let (ours, theirs) = if readable {
(writer, reader)
} else {
(reader, writer)
};
Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours)))
}
Stdio::Null => {
let mut opts = OpenOptions::new();
opts.read(readable);
opts.write(!readable);
let path = unsafe {
CStr::from_ptr("/null\0".as_ptr() as *const _)
};
let fd = File::open_c(&path, &opts)?;
Ok((ChildStdio::Owned(fd.into_fd()), None))
}
}
}
}
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
Stdio::Fd(pipe.into_fd())
}
}
impl From<File> for Stdio {
fn from(file: File) -> Stdio {
Stdio::Fd(file.into_fd())
}
}
impl ChildStdio {
pub fn fd(&self) -> Option<c_int> {
match *self {
ChildStdio::Inherit => None,
ChildStdio::Explicit(fd) => Some(fd),
ChildStdio::Owned(ref fd) => Some(fd.raw()),
}
}
}
impl fmt::Debug for Command {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.program)?;
for arg in &self.args {
write!(f, " {:?}", arg)?;
}
Ok(())
}
}
/// Unix exit statuses
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitStatus(c_int);
impl ExitStatus {
pub fn new(status: c_int) -> ExitStatus {
ExitStatus(status)
}
fn exited(&self) -> bool {
/*unsafe*/ { libc::WIFEXITED(self.0) }
}
pub fn success(&self) -> bool {
self.code() == Some(0)
}
pub fn code(&self) -> Option<i32> {
if self.exited() {
Some(/*unsafe*/ { libc::WEXITSTATUS(self.0) })
} else {
None
}
}
pub fn signal(&self) -> Option<i32> {
if !self.exited() {
Some(/*unsafe*/ { libc::WTERMSIG(self.0) })
} else {
None
}
}
}
impl From<c_int> for ExitStatus {
fn from(a: c_int) -> ExitStatus {
ExitStatus(a)
}
}
impl fmt::Display for ExitStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.code() {
write!(f, "exit code: {}", code)
} else {
let signal = self.signal().unwrap();
write!(f, "signal: {}", signal)
}
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitCode(u8);
impl ExitCode {
pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _);
pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _);
#[inline]
pub fn as_i32(&self) -> i32 {
self.0 as i32
}
}
#[cfg(all(test, not(target_os = "emscripten")))]
mod tests {
use super::*;
use crate::ffi::OsStr;
use crate::mem;
use crate::ptr;
use crate::sys::cvt;
macro_rules! t {
($e:expr) => {
match $e {
Ok(t) => t,
Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
}
}
}
// Android with api less than 21 define sig* functions inline, so it is not
// available for dynamic link. Implementing sigemptyset and sigaddset allow us
// to support older Android version (independent of libc version).
// The following implementations are based on https://git.io/vSkNf
#[cfg(not(target_os = "android"))]
extern {
#[cfg_attr(target_os = "netbsd", link_name = "__sigemptyset14")]
fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int;
#[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")]
fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int;
}
#[cfg(target_os = "android")]
unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int {
libc::memset(set as *mut _, 0, mem::size_of::<libc::sigset_t>());
return 0;
}
#[cfg(target_os = "android")]
unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int {
use crate::slice;
let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::<libc::sigset_t>());
let bit = (signum - 1) as usize;
raw[bit / 8] |= 1 << (bit % 8);
return 0;
}
// See #14232 for more information, but it appears that signal delivery to a
// newly spawned process may just be raced in the macOS, so to prevent this
// test from being flaky we ignore it on macOS.
#[test]
#[cfg_attr(target_os = "macos", ignore)]
// When run under our current QEMU emulation test suite this test fails,
// although the reason isn't very clear as to why. For now this test is
// ignored there.
#[cfg_attr(target_arch = "arm", ignore)]
#[cfg_attr(target_arch = "aarch64", ignore)]
fn test_process_mask() {
unsafe {
// Test to make sure that a signal mask does not get inherited.
let mut cmd = Command::new(OsStr::new("cat"));
let mut set: libc::sigset_t = mem::uninitialized();
let mut old_set: libc::sigset_t = mem::uninitialized();
t!(cvt(sigemptyset(&mut set)));
t!(cvt(sigaddset(&mut set, libc::SIGINT)));
t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, &mut old_set)));
cmd.stdin(Stdio::MakePipe);
cmd.stdout(Stdio::MakePipe);
let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
let stdin_write = pipes.stdin.take().unwrap();
let stdout_read = pipes.stdout.take().unwrap();
t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &old_set,
ptr::null_mut())));
t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT)));
// We need to wait until SIGINT is definitely delivered. The
// easiest way is to write something to cat, and try to read it
// back: if SIGINT is unmasked, it'll get delivered when cat is
// next scheduled.
let _ = stdin_write.write(b"Hello");
drop(stdin_write);
// Either EOF or failure (EPIPE) is okay.
let mut buf = [0; 5];
if let Ok(ret) = stdout_read.read(&mut buf) {
assert_eq!(ret, 0);
}
t!(cat.wait());
}
}
}

View file

@ -0,0 +1,167 @@
use crate::io::{self, Error, ErrorKind};
use libc::{self, c_int};
use libc::{RTP_ID};
use crate::sys::cvt;
use crate::sys::process::rtp;
use crate::sys::process::process_common::*;
////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////
impl Command {
pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
-> io::Result<(Process, StdioPipes)> {
use crate::sys::{cvt_r};
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
let envp = self.capture_env();
if self.saw_nul() {
return Err(io::Error::new(ErrorKind::InvalidInput,
"nul byte found in provided data"));
}
let (ours, theirs) = self.setup_io(default, needs_stdin)?;
let mut p = Process { pid: 0, status: None };
unsafe {
macro_rules! t {
($e:expr) => (match $e {
Ok(e) => e,
Err(e) => return Err(e.into()),
})
}
let mut orig_stdin = libc::STDIN_FILENO;
let mut orig_stdout = libc::STDOUT_FILENO;
let mut orig_stderr = libc::STDERR_FILENO;
if let Some(fd) = theirs.stdin.fd() {
orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO)));
t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
}
if let Some(fd) = theirs.stdout.fd() {
orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO)));
t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
}
if let Some(fd) = theirs.stderr.fd() {
orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO)));
t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
}
if let Some(ref cwd) = *self.get_cwd() {
t!(cvt(libc::chdir(cwd.as_ptr())));
}
// let envp = envp.map(|c| c.as_ptr())
// .unwrap_or(*sys::os::environ() as *const _);
// FIXME: https://github.com/rust-lang/rust/issues/61993
let envp_empty = CStringArray::with_capacity(0);
let envp = match envp {
Some(x) => x,
None => envp_empty,
};
let envp = envp.as_ptr();
let ret = rtp::rtpSpawn(
self.get_argv()[0], // executing program
self.get_argv().as_ptr() as *const _, // argv
envp as *const _, // environment variable pointers
100 as c_int, // initial priority
0x16000, // initial stack size. 0 defaults
// to 0x4000 in 32 bit and 0x8000 in 64 bit
0, // options
0 // task options
);
// Because FileDesc was not used, each duplicated file descriptor
// needs to be closed manually
if orig_stdin != libc::STDIN_FILENO {
t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO)));
libc::close(orig_stdin);
}
if orig_stdout != libc::STDOUT_FILENO {
t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO)));
libc::close(orig_stdout);
}
if orig_stderr != libc::STDERR_FILENO {
t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO)));
libc::close(orig_stderr);
}
if ret != rtp::RTP_ID_ERROR {
p.pid = ret;
Ok((p, ours))
} else {
Err(io::Error::last_os_error())
}
}
}
pub fn exec(&mut self, default: Stdio) -> io::Error {
let ret = Command::spawn(self, default, false);
match ret {
Ok(t) => unsafe {
let mut status = 0 as c_int;
libc::waitpid(t.0.pid, &mut status, 0);
libc::exit(0);
},
Err(e) => e,
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////
/// The unique id of the process (this should never be negative).
pub struct Process {
pid: RTP_ID,
status: Option<ExitStatus>,
}
impl Process {
pub fn id(&self) -> u32 {
self.pid as u32
}
pub fn kill(&mut self) -> io::Result<()> {
// If we've already waited on this process then the pid can be recycled
// and used for another process, and we probably shouldn't be killing
// random processes, so just return an error.
if self.status.is_some() {
Err(Error::new(ErrorKind::InvalidInput,
"invalid argument: can't kill an exited process"))
} else {
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
}
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
use crate::sys::cvt_r;
if let Some(status) = self.status {
return Ok(status)
}
let mut status = 0 as c_int;
cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
self.status = Some(ExitStatus::new(status));
Ok(ExitStatus::new(status))
}
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
if let Some(status) = self.status {
return Ok(Some(status))
}
let mut status = 0 as c_int;
let pid = cvt(unsafe {
libc::waitpid(self.pid, &mut status, libc::WNOHANG)
})?;
if pid == 0 {
Ok(None)
} else {
self.status = Some(ExitStatus::new(status));
Ok(Some(ExitStatus::new(status)))
}
}
}

View file

@ -0,0 +1,298 @@
#![allow(non_camel_case_types, unused)]
use libc::{self, c_int, size_t, c_char, BOOL, RTP_DESC, RTP_ID, TASK_ID};
// Copied directly from rtpLibCommon.h, rtpLib.h, signal.h and taskLibCommon.h (for task options)
// **** definitions for rtpLibCommon.h ****
pub const RTP_GLOBAL_SYMBOLS : c_int = 0x01; // register global symbols for RTP
pub const RTP_LOCAL_SYMBOLS : c_int = 0x02; // idem for local symbols
pub const RTP_ALL_SYMBOLS : c_int = (RTP_GLOBAL_SYMBOLS | RTP_LOCAL_SYMBOLS);
pub const RTP_DEBUG : c_int = 0x10; // set RTP in debug mode when created
pub const RTP_BUFFER_VAL_OFF : c_int = 0x20; // disable buffer validation for all
// system calls issued from the RTP
pub const RTP_LOADED_WAIT : c_int = 0x40; // Wait until the RTP is loaded
pub const RTP_CPU_AFFINITY_NONE : c_int = 0x80; // Remove any CPU affinity (SMP)
// Error Status codes
pub const M_rtpLib : c_int = 178 << 16;
pub const S_rtpLib_INVALID_FILE : c_int = (M_rtpLib | 1);
pub const S_rtpLib_INVALID_OPTION : c_int = (M_rtpLib | 2);
pub const S_rtpLib_ACCESS_DENIED : c_int = (M_rtpLib | 3);
pub const S_rtpLib_INVALID_RTP_ID : c_int = (M_rtpLib | 4);
pub const S_rtpLib_NO_SYMBOL_TABLE : c_int = (M_rtpLib | 5);
pub const S_rtpLib_INVALID_SEGMENT_START_ADDRESS : c_int = (M_rtpLib | 6);
pub const S_rtpLib_INVALID_SYMBOL_REGISTR_POLICY : c_int = (M_rtpLib | 7);
pub const S_rtpLib_INSTANTIATE_FAILED : c_int = (M_rtpLib | 8);
pub const S_rtpLib_INVALID_TASK_OPTION : c_int = (M_rtpLib | 9);
pub const S_rtpLib_RTP_NAME_LENGTH_EXCEEDED : c_int = (M_rtpLib | 10); // rtpInfoGet
pub const VX_RTP_NAME_LENGTH : c_int = 255; // max name length for diplay
// The 'status' field (32 bit integer) of a RTP holds the RTP state and status.
//
// NOTE: RTP_STATE_GET() : read the RTP state(s)
// RTP_STATE_PUT() : write the RTP state(s)
// RTP_STATE_SET() : set a RTP state
// RTP_STATE_UNSET() : unset a RTP state
//
// RTP_STATUS_GET() : read the RTP status
// RTP_STATUS_PUT() : write the RTP status
// RTP_STATUS_SET() : set a RTP status
// RTP_STATUS_UNSET() : unset a RTP status
//
// The PUT/SET/UNSET macros are available only in the kernel headers.
// RTP states
pub const RTP_STATE_CREATE : c_int = 0x0001; // RrtpStructTP is under construction
pub const RTP_STATE_NORMAL : c_int = 0x0002; // RrtpStructTP is ready
pub const RTP_STATE_DELETE : c_int = 0x0004; // RrtpStructTP is being deleted
pub const RTP_STATUS_STOP : c_int = 0x0100; // RTP hrtpStructas recieved stopped signal
pub const RTP_STATUS_ELECTED_DELETER : c_int = 0x0200; // RTP drtpStructelete has started
pub const RTP_STATE_MASK : c_int = (RTP_STATE_CREATE | RTP_STATE_NORMAL |
RTP_STATE_DELETE);
pub const RTP_STATUS_MASK : c_int = (RTP_STATUS_STOP | RTP_STATUS_ELECTED_DELETER);
pub fn RTP_STATE_GET (value : c_int) -> c_int {
value & RTP_STATE_MASK
}
pub fn RTP_STATUS_GET (value : c_int) -> c_int {
value & RTP_STATUS_MASK
}
// Indicates that the RTP_ID returned is not valid.
// RTP_ID_ERROR is supposed to be set to -1, but you can't set
// an unsigned value to a negative without casting, and you
// can't cast unless the size of the integer types are the same,
// but the size of RTP_ID may differ between kernel and user space.
// Bitwise or-ing min and max should get the same result.
pub const RTP_ID_ERROR : RTP_ID = RTP_ID::min_value() | RTP_ID::max_value();
// IS_RTP_ C macros
pub fn IS_RTP_STATE_NORMAL (value : c_int) -> bool {
(RTP_STATE_GET(value) & RTP_STATE_NORMAL) == RTP_STATE_NORMAL
}
pub fn IS_RTP_STATE_CREATE (value : c_int) -> bool {
(RTP_STATE_GET(value) & RTP_STATE_CREATE) == RTP_STATE_CREATE
}
pub fn IS_RTP_STATE_DELETE (value : c_int) -> bool {
(RTP_STATE_GET(value) & RTP_STATE_DELETE) == RTP_STATE_DELETE
}
pub fn IS_RTP_STATUS_STOP (value : c_int) -> bool {
(RTP_STATUS_GET(value) & RTP_STATUS_STOP ) == RTP_STATUS_STOP
}
pub fn IS_RTP_STATUS_ELECTED_DELETER (value : c_int) -> bool {
(RTP_STATUS_GET(value) & RTP_STATUS_ELECTED_DELETER) == RTP_STATUS_ELECTED_DELETER
}
// **** end of definitions for rtpLibCommon.h ****
// **** definitions for rtpLib.h ****
pub fn rtpExit(exitCode : c_int) -> ! {
unsafe{ libc::exit (exitCode) }
}
/* rtpLib.h in the kernel
pub const RTP_DEL_VIA_TASK_DELETE : c_int = 0x1; // rtpDelete() via taskDestroy()
pub const RTP_DEL_FORCE : c_int = 0x2; // Forceful rtpDelete()
pub const RTP_ID_ANY : RTP_ID = 0; // used for when a kernel task
// wants to wait for the next
// RTP to finish
// Function pointers
pub type RTP_PRE_CREATE_HOOK = size_t;
pub type RTP_POST_CREATE_HOOK = size_t;
pub type RTP_INIT_COMPLETE_HOOK = size_t;
pub type RTP_DELETE_HOOK = size_t;
*/
// **** end of definitions for rtpLib.h ****
// **** definitions for signal.h ****
pub fn rtpKill(rtpId : RTP_ID, signo : c_int) -> c_int {
unsafe{ libc::kill(rtpId as c_int, signo) }
}
pub fn rtpSigqueue(rtpId : RTP_ID, signo : c_int, value : size_t) -> c_int {
unsafe{ libc::sigqueue(rtpId as c_int, signo, value) }
}
pub fn _rtpSigqueue(rtpId : RTP_ID, signo : c_int, value : *mut size_t, code : c_int) -> c_int {
unsafe{ libc::_sigqueue(rtpId, signo, value, code) }
}
pub fn taskRaise(signo : c_int) -> c_int {
unsafe{ libc::taskKill(libc::taskIdSelf(), signo) }
}
pub fn rtpRaise(signo : c_int) -> c_int {
unsafe{ libc::raise(signo) }
}
// **** end of definitions for signal.h ****
// **** definitions for taskLibCommon.h ****
pub const VX_PRIVATE_ENV : c_int = 0x0080; // 1 = private environment variables
pub const VX_NO_STACK_FILL : c_int = 0x0100; // 1 = avoid stack fill of 0xee
pub const VX_PRIVATE_UMASK : c_int = 0x0400; // 1 = private file creation mode mask
pub const VX_TASK_NOACTIVATE : c_int = 0x2000; // taskOpen() does not taskActivate()
pub const VX_NO_STACK_PROTECT : c_int = 0x4000; // no over/underflow stack protection,
// stack space remains executable
// define for all valid user task options
pub const VX_USR_TASK_OPTIONS_BASE: c_int = (VX_PRIVATE_ENV |
VX_NO_STACK_FILL |
VX_TASK_NOACTIVATE |
VX_NO_STACK_PROTECT |
VX_PRIVATE_UMASK);
// **** end of definitions for taskLibCommon.h ****
extern "C" {
// functions in rtpLibCommon.h
// forward declarations
pub fn rtpSpawn (
pubrtpFileName : *const c_char,
argv : *const *const c_char,
envp : *const *const c_char,
priority : c_int,
uStackSize : size_t,
options : c_int,
taskOptions : c_int,
) -> RTP_ID;
pub fn rtpInfoGet (
rtpId : RTP_ID,
rtpStruct : *mut RTP_DESC,
) -> c_int;
/* functions in rtpLib.h for kernel
// function declarations
pub fn rtpDelete (
id : RTP_ID,
options : c_int,
status : c_int,
) -> c_int;
pub fn rtpDeleteForce (
rtpId : RTP_ID
) -> c_int;
pub fn rtpShow (
rtpNameOrId : *mut c_char,
level : c_int,
) -> BOOL;
// RTP signals are always present when RTPs are included. The public RTP
// signal APIs are declared here.
pub fn rtpKill (
rtpId : RTP_ID,
signo : c_int,
) -> c_int;
pub fn rtpSigqueue (
rtpId : RTP_ID,
signo : c_int,
value : size_t, // Actual type is const union sigval value,
// which is a union of int and void *
) -> c_int;
pub fn rtpTaskKill (
tid : TASK_ID,
signo : c_int,
) -> c_int;
pub fn rtpTaskSigqueue (
tid : TASK_ID,
signo : c_int,
value : const size_t, // Actual type is const union sigval,
// which is a union of int and void *
) -> c_int;
pub fn rtpWait (
rtpWaitId : RTP_ID,
timeout : libc::alloc_jemalloc_Vx_ticks_t,
pRtpId : *mut RTP_ID,
pStatus : *mut c_int,
) -> c_int;
// Other public functions
pub fn rtpPreCreateHookAdd (
hook : RTP_PRE_CREATE_HOOK,
addToHead : BOOL,
) -> c_int;
pub fn rtpPreCreateHookDelete (
hook : RTP_POST_CREATE_HOOK,
) -> c_int;
pub fn rtpPostCreateHookAdd (
hook : RTP_POST_CREATE_HOOK,
addToHead : BOOL,
) -> c_int;
pub fn rtpPostCreateHookDelete (
hook : RTP_POST_CREATE_HOOK,
) -> c_int;
pub fn rtpInitCompleteHookAdd (
hook : RTP_INIT_COMPLETE_HOOK,
addToHead : BOOL,
) -> c_int;
pub fn rtpInitCompleteHookDelete (
hook : RTP_INIT_COMPLETE_HOOK,
) -> c_int;
pub fn rtpDeleteHookAdd (
hook : RTP_DELETE_HOOK,
addToHead : BOOL,
) -> c_int;
pub fn rtpDeleteHookDelete (
hook : RTP_DELETE_HOOK,
) -> c_int;
pub fn rtpMemShow (
rtpNameOrId : *mut c_char,
level : c_int,
) -> c_int;
pub fn rtpHookShow (
);
*/
}

View file

@ -0,0 +1,31 @@
use crate::mem;
use crate::slice;
pub fn hashmap_random_keys() -> (u64, u64) {
let mut v = (0, 0);
unsafe {
let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8,
mem::size_of_val(&v));
imp::fill_bytes(view);
}
return v
}
mod imp {
use libc;
use crate::io;
extern "C" {
fn randBytes (randBuf: *mut libc::c_uchar,
numOfBytes: libc::c_int) -> libc::c_int;
}
pub fn fill_bytes(v: &mut [u8]) {
let ret = unsafe {
randBytes(v.as_mut_ptr() as *mut libc::c_uchar, v.len() as libc::c_int)
};
if ret == -1 {
panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
}
}
}

View file

@ -0,0 +1,113 @@
use libc;
use crate::cell::UnsafeCell;
use crate::sync::atomic::{AtomicUsize, Ordering};
pub struct RWLock {
inner: UnsafeCell<libc::pthread_rwlock_t>,
write_locked: UnsafeCell<bool>,
num_readers: AtomicUsize,
}
unsafe impl Send for RWLock {}
unsafe impl Sync for RWLock {}
impl RWLock {
pub const fn new() -> RWLock {
RWLock {
inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER),
write_locked: UnsafeCell::new(false),
num_readers: AtomicUsize::new(0),
}
}
#[inline]
pub unsafe fn read(&self) {
let r = libc::pthread_rwlock_rdlock(self.inner.get());
if r == libc::EAGAIN {
panic!("rwlock maximum reader count exceeded");
} else if r == libc::EDEADLK || *self.write_locked.get() {
if r == 0 {
self.raw_unlock();
}
panic!("rwlock read lock would result in deadlock");
} else {
debug_assert_eq!(r, 0);
self.num_readers.fetch_add(1, Ordering::Relaxed);
}
}
#[inline]
pub unsafe fn try_read(&self) -> bool {
let r = libc::pthread_rwlock_tryrdlock(self.inner.get());
if r == 0 {
if *self.write_locked.get() {
self.raw_unlock();
false
} else {
self.num_readers.fetch_add(1, Ordering::Relaxed);
true
}
} else {
false
}
}
#[inline]
pub unsafe fn write(&self) {
let r = libc::pthread_rwlock_wrlock(self.inner.get());
// See comments above for why we check for EDEADLK and write_locked. We
// also need to check that num_readers is 0.
if r == libc::EDEADLK || *self.write_locked.get() ||
self.num_readers.load(Ordering::Relaxed) != 0 {
if r == 0 {
self.raw_unlock();
}
panic!("rwlock write lock would result in deadlock");
} else {
debug_assert_eq!(r, 0);
}
*self.write_locked.get() = true;
}
#[inline]
pub unsafe fn try_write(&self) -> bool {
let r = libc::pthread_rwlock_trywrlock(self.inner.get());
if r == 0 {
if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 {
self.raw_unlock();
false
} else {
*self.write_locked.get() = true;
true
}
} else {
false
}
}
#[inline]
unsafe fn raw_unlock(&self) {
let r = libc::pthread_rwlock_unlock(self.inner.get());
debug_assert_eq!(r, 0);
}
#[inline]
pub unsafe fn read_unlock(&self) {
debug_assert!(!*self.write_locked.get());
self.num_readers.fetch_sub(1, Ordering::Relaxed);
self.raw_unlock();
}
#[inline]
pub unsafe fn write_unlock(&self) {
debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0);
debug_assert!(*self.write_locked.get());
*self.write_locked.get() = false;
self.raw_unlock();
}
#[inline]
pub unsafe fn destroy(&self) {
let r = libc::pthread_rwlock_destroy(self.inner.get());
debug_assert_eq!(r, 0);
}
}

View file

@ -0,0 +1,209 @@
#![cfg_attr(test, allow(dead_code))]
use self::imp::{make_handler, drop_handler};
pub use self::imp::cleanup;
pub use self::imp::init;
pub struct Handler {
_data: *mut libc::c_void
}
impl Handler {
pub unsafe fn new() -> Handler {
make_handler()
}
}
impl Drop for Handler {
fn drop(&mut self) {
unsafe {
drop_handler(self);
}
}
}
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "bitrig",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "solaris",
all(target_os = "netbsd", not(target_vendor = "rumprun")),
target_os = "openbsd"))]
mod imp {
use super::Handler;
use crate::mem;
use crate::ptr;
use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
use libc::{sigaction, SIGBUS, SIG_DFL,
SA_SIGINFO, SA_ONSTACK, sighandler_t};
use libc::{mmap, munmap};
use libc::{SIGSEGV, PROT_READ, PROT_WRITE, MAP_PRIVATE, MAP_ANON};
use libc::MAP_FAILED;
use crate::sys_common::thread_info;
#[cfg(any(target_os = "linux", target_os = "android"))]
unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
#[repr(C)]
struct siginfo_t {
a: [libc::c_int; 3], // si_signo, si_errno, si_code
si_addr: *mut libc::c_void,
}
(*(info as *const siginfo_t)).si_addr as usize
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
(*info).si_addr as usize
}
// Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
// (unmapped pages) at the end of every thread's stack, so if a thread ends
// up running into the guard page it'll trigger this handler. We want to
// detect these cases and print out a helpful error saying that the stack
// has overflowed. All other signals, however, should go back to what they
// were originally supposed to do.
//
// This handler currently exists purely to print an informative message
// whenever a thread overflows its stack. We then abort to exit and
// indicate a crash, but to avoid a misleading SIGSEGV that might lead
// users to believe that unsafe code has accessed an invalid pointer; the
// SIGSEGV encountered when overflowing the stack is expected and
// well-defined.
//
// If this is not a stack overflow, the handler un-registers itself and
// then returns (to allow the original signal to be delivered again).
// Returning from this kind of signal handler is technically not defined
// to work when reading the POSIX spec strictly, but in practice it turns
// out many large systems and all implementations allow returning from a
// signal handler to work. For a more detailed explanation see the
// comments on #26458.
unsafe extern fn signal_handler(signum: libc::c_int,
info: *mut libc::siginfo_t,
_data: *mut libc::c_void) {
use crate::sys_common::util::report_overflow;
let guard = thread_info::stack_guard().unwrap_or(0..0);
let addr = siginfo_si_addr(info);
// If the faulting address is within the guard page, then we print a
// message saying so and abort.
if guard.start <= addr && addr < guard.end {
report_overflow();
rtabort!("stack overflow");
} else {
// Unregister ourselves by reverting back to the default behavior.
let mut action: sigaction = mem::zeroed();
action.sa_sigaction = SIG_DFL;
sigaction(signum, &action, ptr::null_mut());
// See comment above for why this function returns.
}
}
static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut();
pub unsafe fn init() {
let mut action: sigaction = mem::zeroed();
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
action.sa_sigaction = signal_handler as sighandler_t;
sigaction(SIGSEGV, &action, ptr::null_mut());
sigaction(SIGBUS, &action, ptr::null_mut());
let handler = make_handler();
MAIN_ALTSTACK = handler._data;
mem::forget(handler);
}
pub unsafe fn cleanup() {
Handler { _data: MAIN_ALTSTACK };
}
unsafe fn get_stackp() -> *mut libc::c_void {
let stackp = mmap(ptr::null_mut(),
SIGSTKSZ,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0);
if stackp == MAP_FAILED {
panic!("failed to allocate an alternative stack");
}
stackp
}
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "bitrig",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"))]
unsafe fn get_stack() -> libc::stack_t {
libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ }
}
#[cfg(target_os = "dragonfly")]
unsafe fn get_stack() -> libc::stack_t {
libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ }
}
pub unsafe fn make_handler() -> Handler {
let mut stack = mem::zeroed();
sigaltstack(ptr::null(), &mut stack);
// Configure alternate signal stack, if one is not already set.
if stack.ss_flags & SS_DISABLE != 0 {
stack = get_stack();
sigaltstack(&stack, ptr::null_mut());
Handler { _data: stack.ss_sp as *mut libc::c_void }
} else {
Handler { _data: ptr::null_mut() }
}
}
pub unsafe fn drop_handler(handler: &mut Handler) {
if !handler._data.is_null() {
let stack = libc::stack_t {
ss_sp: ptr::null_mut(),
ss_flags: SS_DISABLE,
// Workaround for bug in macOS implementation of sigaltstack
// UNIX2003 which returns ENOMEM when disabling a stack while
// passing ss_size smaller than MINSIGSTKSZ. According to POSIX
// both ss_sp and ss_size should be ignored in this case.
ss_size: SIGSTKSZ,
};
sigaltstack(&stack, ptr::null_mut());
munmap(handler._data, SIGSTKSZ);
}
}
}
#[cfg(not(any(target_os = "linux",
target_os = "macos",
target_os = "bitrig",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "solaris",
all(target_os = "netbsd", not(target_vendor = "rumprun")),
target_os = "openbsd")))]
mod imp {
use crate::ptr;
pub unsafe fn init() {
}
pub unsafe fn cleanup() {
}
pub unsafe fn make_handler() -> super::Handler {
super::Handler { _data: ptr::null_mut() }
}
pub unsafe fn drop_handler(_handler: &mut super::Handler) {
}
}

View file

@ -0,0 +1,63 @@
use crate::io;
use crate::sys::fd::FileDesc;
pub struct Stdin(());
pub struct Stdout(());
pub struct Stderr(());
impl Stdin {
pub fn new() -> io::Result<Stdin> { Ok(Stdin(())) }
}
impl io::Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let fd = FileDesc::new(libc::STDIN_FILENO);
let ret = fd.read(buf);
fd.into_raw(); // do not close this FD
ret
}
}
impl Stdout {
pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) }
}
impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let fd = FileDesc::new(libc::STDOUT_FILENO);
let ret = fd.write(buf);
fd.into_raw(); // do not close this FD
ret
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Stderr {
pub fn new() -> io::Result<Stderr> { Ok(Stderr(())) }
}
impl io::Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let fd = FileDesc::new(libc::STDERR_FILENO);
let ret = fd.write(buf);
fd.into_raw(); // do not close this FD
ret
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub fn is_ebadf(err: &io::Error) -> bool {
err.raw_os_error() == Some(libc::EBADF as i32)
}
pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
pub fn panic_output() -> Option<impl io::Write> {
Stderr::new().ok()
}

View file

@ -0,0 +1,179 @@
//use crate::boxed::FnBox;
use crate::cmp;
use crate::ffi::CStr;
use crate::io;
use crate::mem;
use crate::ptr;
use crate::sys::os;
use crate::time::Duration;
use crate::sys_common::thread::*;
#[cfg(not(target_os = "l4re"))]
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
#[cfg(target_os = "l4re")]
pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024;
pub struct Thread {
id: libc::pthread_t,
}
// Some platforms may have pthread_t as a pointer in which case we still want
// a thread to be Send/Sync
unsafe impl Send for Thread {}
unsafe impl Sync for Thread {}
// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc,
// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS.
#[cfg(not(target_os = "emscripten"))]
unsafe fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
stack_size: libc::size_t) -> libc::c_int {
libc::pthread_attr_setstacksize(attr, stack_size)
}
#[cfg(target_os = "emscripten")]
unsafe fn pthread_attr_setstacksize(_attr: *mut libc::pthread_attr_t,
_stack_size: libc::size_t) -> libc::c_int {
panic!()
}
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>)
-> io::Result<Thread> {
let p = box p;
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed();
assert_eq!(libc::pthread_attr_init(&mut attr), 0);
let stack_size = cmp::max(stack, min_stack_size(&attr));
match pthread_attr_setstacksize(&mut attr,
stack_size) {
0 => {}
n => {
assert_eq!(n, libc::EINVAL);
// EINVAL means |stack_size| is either too small or not a
// multiple of the system page size. Because it's definitely
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
// Round up to the nearest page and try again.
let page_size = os::page_size();
let stack_size = (stack_size + page_size - 1) &
(-(page_size as isize - 1) as usize - 1);
assert_eq!(libc::pthread_attr_setstacksize(&mut attr,
stack_size), 0);
}
};
let ret = libc::pthread_create(&mut native, &attr, thread_start,
&*p as *const _ as *mut _);
assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
return if ret != 0 {
Err(io::Error::from_raw_os_error(ret))
} else {
mem::forget(p); // ownership passed to pthread_create
Ok(Thread { id: native })
};
extern fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
unsafe { start_thread(main as *mut u8); }
ptr::null_mut()
}
}
pub fn yield_now() {
let ret = unsafe { libc::sched_yield() };
debug_assert_eq!(ret, 0);
}
pub fn set_name(_name: &CStr) {
assert!(false, "FIXME: set_name");
}
pub fn sleep(dur: Duration) {
let mut secs = dur.as_secs();
let mut nsecs = dur.subsec_nanos() as _;
// If we're awoken with a signal then the return value will be -1 and
// nanosleep will fill in `ts` with the remaining time.
unsafe {
while secs > 0 || nsecs > 0 {
let mut ts = libc::timespec {
tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t,
tv_nsec: nsecs,
};
secs -= ts.tv_sec as u64;
if libc::nanosleep(&ts, &mut ts) == -1 {
assert_eq!(os::errno(), libc::EINTR);
secs += ts.tv_sec as u64;
nsecs = ts.tv_nsec;
} else {
nsecs = 0;
}
}
}
}
pub fn join(self) {
unsafe {
let ret = libc::pthread_join(self.id, ptr::null_mut());
mem::forget(self);
assert!(ret == 0,
"failed to join thread: {}", io::Error::from_raw_os_error(ret));
}
}
pub fn id(&self) -> libc::pthread_t { self.id }
pub fn into_id(self) -> libc::pthread_t {
let id = self.id;
mem::forget(self);
id
}
}
impl Drop for Thread {
fn drop(&mut self) {
let ret = unsafe { libc::pthread_detach(self.id) };
debug_assert_eq!(ret, 0);
}
}
#[cfg_attr(test, allow(dead_code))]
pub mod guard {
use crate::ops::Range;
pub type Guard = Range<usize>;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
pub unsafe fn deinit() {}
}
// glibc >= 2.15 has a __pthread_get_minstack() function that returns
// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
// storage. We need that information to avoid blowing up when a small stack
// is created in an application with big thread-local storage requirements.
// See #6233 for rationale and details.
#[cfg(target_os = "linux")]
#[allow(deprecated)]
fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
match __pthread_get_minstack.get() {
None => libc::PTHREAD_STACK_MIN,
Some(f) => unsafe { f(attr) },
}
}
// No point in looking up __pthread_get_minstack() on non-glibc
// platforms.
#[cfg(all(not(target_os = "linux"),
not(target_os = "netbsd")))]
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
libc::PTHREAD_STACK_MIN
}
#[cfg(target_os = "netbsd")]
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
2048 // just a guess
}

View file

@ -0,0 +1,34 @@
#![allow(dead_code)] // not used on all platforms
use crate::mem;
pub type Key = libc::pthread_key_t;
#[inline]
pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
let mut key = 0;
assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0);
key
}
#[inline]
pub unsafe fn set(key: Key, value: *mut u8) {
let r = libc::pthread_setspecific(key, value as *mut _);
debug_assert_eq!(r, 0);
}
#[inline]
pub unsafe fn get(key: Key) -> *mut u8 {
libc::pthread_getspecific(key) as *mut u8
}
#[inline]
pub unsafe fn destroy(key: Key) {
let r = libc::pthread_key_delete(key);
debug_assert_eq!(r, 0);
}
#[inline]
pub fn requires_synchronized_create() -> bool {
false
}

View file

@ -0,0 +1,224 @@
use crate::cmp::Ordering;
use libc;
use crate::time::Duration;
use ::core::hash::{Hash, Hasher};
pub use self::inner::{Instant, SystemTime, UNIX_EPOCH};
use crate::convert::TryInto;
const NSEC_PER_SEC: u64 = 1_000_000_000;
#[derive(Copy, Clone)]
struct Timespec {
t: libc::timespec,
}
impl Timespec {
const fn zero() -> Timespec {
Timespec {
t: libc::timespec { tv_sec: 0, tv_nsec: 0 },
}
}
fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
if self >= other {
Ok(if self.t.tv_nsec >= other.t.tv_nsec {
Duration::new((self.t.tv_sec - other.t.tv_sec) as u64,
(self.t.tv_nsec - other.t.tv_nsec) as u32)
} else {
Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64,
self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) -
other.t.tv_nsec as u32)
})
} else {
match other.sub_timespec(self) {
Ok(d) => Err(d),
Err(d) => Ok(d),
}
}
}
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = other
.as_secs()
.try_into() // <- target type would be `libc::time_t`
.ok()
.and_then(|secs| self.t.tv_sec.checked_add(secs))?;
// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
if nsec >= NSEC_PER_SEC as u32 {
nsec -= NSEC_PER_SEC as u32;
secs = secs.checked_add(1)?;
}
Some(Timespec {
t: libc::timespec {
tv_sec: secs,
tv_nsec: nsec as _,
},
})
}
fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = other
.as_secs()
.try_into() // <- target type would be `libc::time_t`
.ok()
.and_then(|secs| self.t.tv_sec.checked_sub(secs))?;
// Similar to above, nanos can't overflow.
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
if nsec < 0 {
nsec += NSEC_PER_SEC as i32;
secs = secs.checked_sub(1)?;
}
Some(Timespec {
t: libc::timespec {
tv_sec: secs,
tv_nsec: nsec as _,
},
})
}
}
impl PartialEq for Timespec {
fn eq(&self, other: &Timespec) -> bool {
self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec
}
}
impl Eq for Timespec {}
impl PartialOrd for Timespec {
fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Timespec {
fn cmp(&self, other: &Timespec) -> Ordering {
let me = (self.t.tv_sec, self.t.tv_nsec);
let other = (other.t.tv_sec, other.t.tv_nsec);
me.cmp(&other)
}
}
impl Hash for Timespec {
fn hash<H : Hasher>(&self, state: &mut H) {
self.t.tv_sec.hash(state);
self.t.tv_nsec.hash(state);
}
}
mod inner {
use crate::fmt;
use libc;
use crate::sys::cvt;
use crate::time::Duration;
use super::Timespec;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant {
t: Timespec,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SystemTime {
t: Timespec,
}
pub const UNIX_EPOCH: SystemTime = SystemTime {
t: Timespec {
t: libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
},
};
impl Instant {
pub fn now() -> Instant {
Instant { t: now(libc::CLOCK_MONOTONIC) }
}
pub const fn zero() -> Instant {
Instant {
t: Timespec::zero(),
}
}
pub fn actually_monotonic() -> bool {
true
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.t.sub_timespec(&other.t).ok()
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_add_duration(other)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub_duration(other)? })
}
}
impl fmt::Debug for Instant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Instant")
.field("tv_sec", &self.t.t.tv_sec)
.field("tv_nsec", &self.t.t.tv_nsec)
.finish()
}
}
impl SystemTime {
pub fn now() -> SystemTime {
SystemTime { t: now(libc::CLOCK_REALTIME) }
}
pub fn sub_time(&self, other: &SystemTime)
-> Result<Duration, Duration> {
self.t.sub_timespec(&other.t)
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime { t: self.t.checked_add_duration(other)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime { t: self.t.checked_sub_duration(other)? })
}
}
impl From<libc::timespec> for SystemTime {
fn from(t: libc::timespec) -> SystemTime {
SystemTime { t: Timespec { t: 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 = libc::c_int;
fn now(clock: clock_t) -> Timespec {
let mut t = Timespec {
t: libc::timespec {
tv_sec: 0,
tv_nsec: 0,
}
};
cvt(unsafe {
libc::clock_gettime(clock, &mut t.t)
}).unwrap();
t
}
}

View file

@ -0,0 +1,60 @@
//! Support for "weak linkage" to symbols on Unix
//!
//! Some I/O operations we do in libstd require newer versions of OSes but we
//! need to maintain binary compatibility with older releases for now. In order
//! to use the new functionality when available we use this module for
//! detection.
//!
//! One option to use here is weak linkage, but that is unfortunately only
//! really workable on Linux. Hence, use dlsym to get the symbol value at
//! runtime. This is also done for compatibility with older versions of glibc,
//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
//! we've been dynamically linked to the library the symbol comes from, but that
//! is currently always the case for things like libpthread/libc.
//!
//! A long time ago this used weak linkage for the __pthread_get_minstack
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
//! dependency on libc6 (#23628).
use crate::ffi::CStr;
use crate::marker;
use crate::mem;
use crate::sync::atomic::{AtomicUsize, Ordering};
pub struct Weak<F> {
name: &'static str,
addr: AtomicUsize,
_marker: marker::PhantomData<F>,
}
impl<F> Weak<F> {
pub const fn new(name: &'static str) -> Weak<F> {
Weak {
name,
addr: AtomicUsize::new(1),
_marker: marker::PhantomData,
}
}
pub fn get(&self) -> Option<F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
unsafe {
if self.addr.load(Ordering::SeqCst) == 1 {
self.addr.store(fetch(self.name), Ordering::SeqCst);
}
match self.addr.load(Ordering::SeqCst) {
0 => None,
addr => Some(mem::transmute_copy::<usize, F>(&addr)),
}
}
}
}
unsafe fn fetch(name: &str) -> usize {
let name = match CStr::from_bytes_with_nul(name.as_bytes()) {
Ok(cstr) => cstr,
Err(..) => return 0,
};
assert!(false, "FIXME: fetch");
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
}

View file

@ -1263,6 +1263,12 @@ fn get_concurrency() -> usize {
1
}
#[cfg(target_os = "vxworks")]
fn num_cpus() -> usize {
// FIXME: Implement num_cpus on vxWorks
1
}
#[cfg(any(
all(target_arch = "wasm32", not(target_os = "emscripten")),
all(target_vendor = "fortanix", target_env = "sgx")

View file

@ -1653,6 +1653,18 @@ impl<'test> TestCx<'test> {
None,
)
}
_ if self.config.target.contains("vxworks") => {
let aux_dir = self.aux_output_dir_name();
let ProcArgs { prog, args } = self.make_run_args();
let mut vx_run = Command::new("vx-run");
vx_run.args(&[&prog]).args(args).envs(env.clone());
self.compose_and_run(
vx_run,
self.config.run_lib_path.to_str().unwrap(),
Some(aux_dir.to_str().unwrap()),
None,
)
}
_ => {
let aux_dir = self.aux_output_dir_name();
let ProcArgs { prog, args } = self.make_run_args();

View file

@ -30,6 +30,7 @@ const OS_TABLE: &'static [(&'static str, &'static str)] = &[
("solaris", "solaris"),
("win32", "windows"),
("windows", "windows"),
("vxworks", "vxworks"),
];
const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[