From feba393b8ed60efe79f9f5207fde904d085949f5 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Fri, 22 May 2015 16:39:21 -0400 Subject: [PATCH 1/5] std::process: Remove helper function pwd_cmd from test module The test that used it was removed in 700e627cf727873a472b1876238aac10b932258b. --- src/libstd/process.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/libstd/process.rs b/src/libstd/process.rs index d70b9f099037a..a8127b3200f36 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -769,24 +769,6 @@ mod tests { } } - #[cfg(all(unix, not(target_os="android")))] - pub fn pwd_cmd() -> Command { - Command::new("pwd") - } - #[cfg(target_os="android")] - pub fn pwd_cmd() -> Command { - let mut cmd = Command::new("/system/bin/sh"); - cmd.arg("-c").arg("pwd"); - cmd - } - - #[cfg(windows)] - pub fn pwd_cmd() -> Command { - let mut cmd = Command::new("cmd"); - cmd.arg("/c").arg("cd"); - cmd - } - #[cfg(all(unix, not(target_os="android")))] pub fn env_cmd() -> Command { Command::new("env") From e13642163ab258c58295bcb1f2eaa27266724f73 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Fri, 22 May 2015 17:30:06 -0400 Subject: [PATCH 2/5] sys/unix/c.rs: Remove unused code It looks like a lot of this dated to previous incarnations of the io module, etc., and went unused in the reworking leading up to 1.0. Remove everything we're not actively using (except for signal handling, which will be reworked in the next commit). --- src/libstd/lib.rs | 2 - src/libstd/sys/unix/c.rs | 80 +++------------------------------------- 2 files changed, 5 insertions(+), 77 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e5242c5bf861a..e27e4ba5af2cf 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -148,8 +148,6 @@ #![feature(vec_push_all)] #![feature(wrapping)] #![feature(zero_one)] -#![cfg_attr(all(unix, not(target_os = "macos"), not(target_os = "ios")), - feature(num_bits_bytes))] #![cfg_attr(windows, feature(str_utf16))] #![cfg_attr(test, feature(float_from_str_radix, range_inclusive, float_extras))] #![cfg_attr(test, feature(test, rustc_private, float_consts))] diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index 1e68eac5a6735..de19206d93dd7 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -13,7 +13,6 @@ #![allow(dead_code)] #![allow(non_camel_case_types)] -pub use self::select::fd_set; pub use self::signal::{sigaction, siginfo, sigset_t}; pub use self::signal::{SA_ONSTACK, SA_RESTART, SA_RESETHAND, SA_NOCLDSTOP}; pub use self::signal::{SA_NODEFER, SA_NOCLDWAIT, SA_SIGINFO, SIGCHLD}; @@ -26,45 +25,21 @@ use libc; target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd"))] -mod consts { - use libc; - pub const FIONBIO: libc::c_ulong = 0x8004667e; - pub const FIOCLEX: libc::c_ulong = 0x20006601; - pub const FIONCLEX: libc::c_ulong = 0x20006602; -} +pub const FIOCLEX: libc::c_ulong = 0x20006601; + #[cfg(any(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64", target_arch = "arm", target_arch = "aarch64")), target_os = "android"))] -mod consts { - use libc; - pub const FIONBIO: libc::c_ulong = 0x5421; - pub const FIOCLEX: libc::c_ulong = 0x5451; - pub const FIONCLEX: libc::c_ulong = 0x5450; -} +pub const FIOCLEX: libc::c_ulong = 0x5451; + #[cfg(all(target_os = "linux", any(target_arch = "mips", target_arch = "mipsel", target_arch = "powerpc")))] -mod consts { - use libc; - pub const FIONBIO: libc::c_ulong = 0x667e; - pub const FIOCLEX: libc::c_ulong = 0x6601; - pub const FIONCLEX: libc::c_ulong = 0x6600; -} -pub use self::consts::*; - -#[cfg(any(target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig", - target_os = "openbsd"))] -pub const MSG_DONTWAIT: libc::c_int = 0x80; -#[cfg(any(target_os = "linux", target_os = "android"))] -pub const MSG_DONTWAIT: libc::c_int = 0x40; +pub const FIOCLEX: libc::c_ulong = 0x6601; pub const WNOHANG: libc::c_int = 1; @@ -123,13 +98,6 @@ pub struct passwd { } extern { - pub fn gettimeofday(timeval: *mut libc::timeval, - tzp: *mut libc::c_void) -> libc::c_int; - pub fn select(nfds: libc::c_int, - readfds: *mut fd_set, - writefds: *mut fd_set, - errorfds: *mut fd_set, - timeout: *mut libc::timeval) -> libc::c_int; pub fn getsockopt(sockfd: libc::c_int, level: libc::c_int, optname: libc::c_int, @@ -165,44 +133,6 @@ extern { -> *mut libc::c_char; } -#[cfg(any(target_os = "macos", target_os = "ios"))] -mod select { - pub const FD_SETSIZE: usize = 1024; - - #[repr(C)] - pub struct fd_set { - fds_bits: [i32; (FD_SETSIZE / 32)] - } - - pub fn fd_set(set: &mut fd_set, fd: i32) { - set.fds_bits[(fd / 32) as usize] |= 1 << ((fd % 32) as usize); - } -} - -#[cfg(any(target_os = "android", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "bitrig", - target_os = "openbsd", - target_os = "linux"))] -mod select { - use usize; - use libc; - - pub const FD_SETSIZE: usize = 1024; - - #[repr(C)] - pub struct fd_set { - // FIXME: shouldn't this be a c_ulong? - fds_bits: [libc::uintptr_t; (FD_SETSIZE / usize::BITS)] - } - - pub fn fd_set(set: &mut fd_set, fd: i32) { - let fd = fd as usize; - set.fds_bits[fd / usize::BITS] |= 1 << (fd % usize::BITS); - } -} - #[cfg(any(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64", From 56d904c4bb4a10e6da3f03d11279e9a3f3d20d8b Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Sat, 23 May 2015 18:07:52 -0400 Subject: [PATCH 3/5] sys/unix: Consolidate signal-handling FFI bindings Both c.rs and stack_overflow.rs had bindings of libc's signal-handling routines. It looks like the split dated from #16388, when (what is now) c.rs was in libnative but not libgreen. Nobody is currently using the c.rs bindings, but they're a bit more accurate in some places. Move everything to c.rs (since I'll need signal handling in process.rs, and we should avoid duplication), clean up the bindings, and manually double-check everything against the relevant system headers (fixing a few things in the process). --- src/libstd/sys/unix/c.rs | 326 +++++++++++++++----------- src/libstd/sys/unix/stack_overflow.rs | 141 +---------- 2 files changed, 199 insertions(+), 268 deletions(-) diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index de19206d93dd7..17e40bca37527 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -8,14 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! C definitions used by libnative that don't belong in liblibc +//! C definitions used by std::sys that don't belong in liblibc + +// These are definitions sufficient for the users in this directory. +// This is not a general-purpose binding to this functionality, and in +// some cases (notably the definition of siginfo_t), we intentionally +// have incomplete bindings so that we don't need to fight with unions. +// +// Note that these types need to match the definitions from the platform +// libc (currently glibc on Linux), not the kernel definitions / the +// syscall interface. This has a few weirdnesses, like glibc's sigset_t +// being 1024 bits on all platforms. If you're adding a new GNU/Linux +// port, check glibc's sysdeps/unix/sysv/linux, not the kernel headers. #![allow(dead_code)] #![allow(non_camel_case_types)] -pub use self::signal::{sigaction, siginfo, sigset_t}; -pub use self::signal::{SA_ONSTACK, SA_RESTART, SA_RESETHAND, SA_NOCLDSTOP}; -pub use self::signal::{SA_NODEFER, SA_NOCLDWAIT, SA_SIGINFO, SIGCHLD}; +pub use self::signal_os::{sigaction, siginfo, sigset_t, sigaltstack}; +pub use self::signal_os::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSTKSZ}; use libc; @@ -97,6 +107,12 @@ pub struct passwd { pub pw_shell: *mut libc::c_char, } +// This is really a function pointer (or a union of multiple function +// pointers), except for constants like SIG_DFL. +pub type sighandler_t = *mut libc::c_void; + +pub const SIG_DFL: sighandler_t = 0 as sighandler_t; + extern { pub fn getsockopt(sockfd: libc::c_int, level: libc::c_int, @@ -109,12 +125,15 @@ extern { pub fn waitpid(pid: libc::pid_t, status: *mut libc::c_int, options: libc::c_int) -> libc::pid_t; + pub fn raise(signum: libc::c_int) -> libc::c_int; + pub fn sigaction(signum: libc::c_int, act: *const sigaction, oldact: *mut sigaction) -> libc::c_int; - pub fn sigaddset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int; - pub fn sigdelset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int; + pub fn sigaltstack(ss: *const sigaltstack, + oss: *mut sigaltstack) -> libc::c_int; + pub fn sigemptyset(set: *mut sigset_t) -> libc::c_int; #[cfg(not(target_os = "ios"))] @@ -133,123 +152,174 @@ extern { -> *mut libc::c_char; } -#[cfg(any(all(target_os = "linux", - any(target_arch = "x86", - target_arch = "x86_64", - target_arch = "arm", - target_arch = "aarch64")), +#[cfg(any(target_os = "linux", target_os = "android"))] -mod signal { +mod signal_os { + pub use self::arch::{SA_ONSTACK, SA_SIGINFO, SIGBUS, + sigaction, sigaltstack}; use libc; - pub const SA_NOCLDSTOP: libc::c_ulong = 0x00000001; - pub const SA_NOCLDWAIT: libc::c_ulong = 0x00000002; - pub const SA_NODEFER: libc::c_ulong = 0x40000000; - pub const SA_ONSTACK: libc::c_ulong = 0x08000000; - pub const SA_RESETHAND: libc::c_ulong = 0x80000000; - pub const SA_RESTART: libc::c_ulong = 0x10000000; - pub const SA_SIGINFO: libc::c_ulong = 0x00000004; - pub const SIGCHLD: libc::c_int = 17; - - // This definition is not as accurate as it could be, {pid, uid, status} is - // actually a giant union. Currently we're only interested in these fields, - // however. + #[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + pub const SIGSTKSZ: libc::size_t = 8192; + + // This is smaller on musl and Android, but no harm in being generous. + #[cfg(any(target_arch = "aarch64", + target_arch = "powerpc"))] + pub const SIGSTKSZ: libc::size_t = 16384; + + // This definition is intentionally a subset of the C structure: the + // fields after si_code are actually a giant union. We're only + // interested in si_addr for this module, though. #[repr(C)] pub struct siginfo { - si_signo: libc::c_int, - si_errno: libc::c_int, - si_code: libc::c_int, - pub pid: libc::pid_t, - pub uid: libc::uid_t, - pub status: libc::c_int, + _signo: libc::c_int, + _errno: libc::c_int, + _code: libc::c_int, + // This structure will need extra padding here for MIPS64. + pub si_addr: *mut libc::c_void } + #[cfg(all(target_os = "linux", target_pointer_width = "32"))] #[repr(C)] - pub struct sigaction { - pub sa_handler: extern fn(libc::c_int), - pub sa_mask: sigset_t, - pub sa_flags: libc::c_ulong, - sa_restorer: *mut libc::c_void, - } - - unsafe impl ::marker::Send for sigaction { } - unsafe impl ::marker::Sync for sigaction { } - - #[repr(C)] - #[cfg(target_pointer_width = "32")] pub struct sigset_t { __val: [libc::c_ulong; 32], } + #[cfg(all(target_os = "linux", target_pointer_width = "64"))] #[repr(C)] - #[cfg(target_pointer_width = "64")] pub struct sigset_t { __val: [libc::c_ulong; 16], } -} -#[cfg(all(target_os = "linux", - any(target_arch = "mips", - target_arch = "mipsel", - target_arch = "powerpc")))] -mod signal { - use libc; - - pub const SA_NOCLDSTOP: libc::c_ulong = 0x00000001; - pub const SA_NOCLDWAIT: libc::c_ulong = 0x00010000; - pub const SA_NODEFER: libc::c_ulong = 0x40000000; - pub const SA_ONSTACK: libc::c_ulong = 0x08000000; - pub const SA_RESETHAND: libc::c_ulong = 0x80000000; - pub const SA_RESTART: libc::c_ulong = 0x10000000; - pub const SA_SIGINFO: libc::c_ulong = 0x00000008; - pub const SIGCHLD: libc::c_int = 18; - - // This definition is not as accurate as it could be, {pid, uid, status} is - // actually a giant union. Currently we're only interested in these fields, - // however. - #[repr(C)] - pub struct siginfo { - si_signo: libc::c_int, - si_code: libc::c_int, - si_errno: libc::c_int, - pub pid: libc::pid_t, - pub uid: libc::uid_t, - pub status: libc::c_int, + // Android for MIPS has a 128-bit sigset_t, but we don't currently + // support it. Android for AArch64 technically has a structure of a + // single ulong. + #[cfg(target_os = "android")] + pub type sigset_t = libc::c_ulong; + + #[cfg(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "powerpc", + target_arch = "arm", + target_arch = "aarch64"))] + mod arch { + use libc; + use super::super::sighandler_t; + use super::sigset_t; + + pub const SA_ONSTACK: libc::c_ulong = 0x08000000; + pub const SA_SIGINFO: libc::c_ulong = 0x00000004; + + pub const SIGBUS: libc::c_int = 7; + + #[cfg(target_os = "linux")] + #[repr(C)] + pub struct sigaction { + pub sa_sigaction: sighandler_t, + pub sa_mask: sigset_t, + pub sa_flags: libc::c_ulong, + _restorer: *mut libc::c_void, + } + + #[cfg(all(target_os = "android", target_pointer_width = "32"))] + #[repr(C)] + pub struct sigaction { + pub sa_sigaction: sighandler_t, + pub sa_flags: libc::c_ulong, + _restorer: *mut libc::c_void, + pub sa_mask: sigset_t, + } + + #[cfg(all(target_os = "android", target_pointer_width = "64"))] + #[repr(C)] + pub struct sigaction { + pub sa_flags: libc::c_uint, + pub sa_sigaction: sighandler_t, + pub sa_mask: sigset_t, + _restorer: *mut libc::c_void, + } + + #[repr(C)] + pub struct sigaltstack { + pub ss_sp: *mut libc::c_void, + pub ss_flags: libc::c_int, + pub ss_size: libc::size_t + } } - #[repr(C)] - pub struct sigaction { - pub sa_flags: libc::c_uint, - pub sa_handler: extern fn(libc::c_int), - pub sa_mask: sigset_t, - sa_restorer: *mut libc::c_void, - sa_resv: [libc::c_int; 1], - } - - unsafe impl ::marker::Send for sigaction { } - unsafe impl ::marker::Sync for sigaction { } - - #[repr(C)] - pub struct sigset_t { - __val: [libc::c_ulong; 32], + #[cfg(any(target_arch = "mips", + target_arch = "mipsel"))] + mod arch { + use libc; + use super::super::sighandler_t; + use super::sigset_t; + + pub const SA_ONSTACK: libc::c_ulong = 0x08000000; + pub const SA_SIGINFO: libc::c_ulong = 0x00000008; + + pub const SIGBUS: libc::c_int = 10; + + #[cfg(all(target_os = "linux", not(target_env = "musl")))] + #[repr(C)] + pub struct sigaction { + pub sa_flags: libc::c_uint, + pub sa_sigaction: sighandler_t, + pub sa_mask: sigset_t, + _restorer: *mut libc::c_void, + _resv: [libc::c_int; 1], + } + + #[cfg(target_env = "musl")] + #[repr(C)] + pub struct sigaction { + pub sa_sigaction: sighandler_t, + pub sa_mask: sigset_t, + pub sa_flags: libc::c_ulong, + _restorer: *mut libc::c_void, + } + + #[cfg(target_os = "android")] + #[repr(C)] + pub struct sigaction { + pub sa_flags: libc::c_uint, + pub sa_sigaction: sighandler_t, + pub sa_mask: sigset_t, + } + + #[repr(C)] + pub struct sigaltstack { + pub ss_sp: *mut libc::c_void, + pub ss_size: libc::size_t, + pub ss_flags: libc::c_int, + } } } #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", - target_os = "dragonfly"))] -mod signal { + target_os = "dragonfly", + target_os = "bitrig", + target_os = "openbsd"))] +mod signal_os { use libc; + use super::sighandler_t; pub const SA_ONSTACK: libc::c_int = 0x0001; - pub const SA_RESTART: libc::c_int = 0x0002; - pub const SA_RESETHAND: libc::c_int = 0x0004; - pub const SA_NOCLDSTOP: libc::c_int = 0x0008; - pub const SA_NODEFER: libc::c_int = 0x0010; - pub const SA_NOCLDWAIT: libc::c_int = 0x0020; pub const SA_SIGINFO: libc::c_int = 0x0040; - pub const SIGCHLD: libc::c_int = 20; + + pub const SIGBUS: libc::c_int = 10; + + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub const SIGSTKSZ: libc::size_t = 131072; + // FreeBSD's is actually arch-dependent, but never more than 40960. + // No harm in being generous. + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + pub const SIGSTKSZ: libc::size_t = 40960; #[cfg(any(target_os = "macos", target_os = "ios"))] @@ -259,61 +329,53 @@ mod signal { pub struct sigset_t { bits: [u32; 4], } + #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] + pub type sigset_t = libc::c_uint; // This structure has more fields, but we're not all that interested in // them. + #[cfg(any(target_os = "macos", target_os = "ios", + target_os = "freebsd", target_os = "dragonfly"))] + #[repr(C)] + pub struct siginfo { + pub _signo: libc::c_int, + pub _errno: libc::c_int, + pub _code: libc::c_int, + pub _pid: libc::pid_t, + pub _uid: libc::uid_t, + pub _status: libc::c_int, + pub si_addr: *mut libc::c_void + } + #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] #[repr(C)] pub struct siginfo { pub si_signo: libc::c_int, - pub si_errno: libc::c_int, pub si_code: libc::c_int, - pub pid: libc::pid_t, - pub uid: libc::uid_t, - pub status: libc::c_int, + pub si_errno: libc::c_int, + pub si_addr: *mut libc::c_void } + #[cfg(any(target_os = "macos", target_os = "ios", + target_os = "bitrig", target_os = "openbsd"))] #[repr(C)] pub struct sigaction { - pub sa_handler: extern fn(libc::c_int), - pub sa_flags: libc::c_int, + pub sa_sigaction: sighandler_t, pub sa_mask: sigset_t, + pub sa_flags: libc::c_int, } -} - -#[cfg(any(target_os = "bitrig", target_os = "openbsd"))] -mod signal { - use libc; - pub const SA_ONSTACK: libc::c_int = 0x0001; - pub const SA_RESTART: libc::c_int = 0x0002; - pub const SA_RESETHAND: libc::c_int = 0x0004; - pub const SA_NOCLDSTOP: libc::c_int = 0x0008; - pub const SA_NODEFER: libc::c_int = 0x0010; - pub const SA_NOCLDWAIT: libc::c_int = 0x0020; - pub const SA_SIGINFO: libc::c_int = 0x0040; - pub const SIGCHLD: libc::c_int = 20; - - pub type sigset_t = libc::c_uint; - - // This structure has more fields, but we're not all that interested in - // them. + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] #[repr(C)] - pub struct siginfo { - pub si_signo: libc::c_int, - pub si_code: libc::c_int, - pub si_errno: libc::c_int, - // FIXME: Bitrig has a crazy union here in the siginfo, I think this - // layout will still work tho. The status might be off by the size of - // a clock_t by my reading, but we can fix this later. - pub pid: libc::pid_t, - pub uid: libc::uid_t, - pub status: libc::c_int, + pub struct sigaction { + pub sa_sigaction: sighandler_t, + pub sa_flags: libc::c_int, + pub sa_mask: sigset_t, } #[repr(C)] - pub struct sigaction { - pub sa_handler: extern fn(libc::c_int), - pub sa_mask: sigset_t, - pub sa_flags: libc::c_int, + pub struct sigaltstack { + pub ss_sp: *mut libc::c_void, + pub ss_size: libc::size_t, + pub ss_flags: libc::c_int, } } diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs index 2bc280d127439..52494a17b9d24 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -44,11 +44,12 @@ mod imp { use mem; use ptr; use intrinsics; - use self::signal::{siginfo, sigaction, SIGBUS, SIG_DFL, - SA_SIGINFO, SA_ONSTACK, sigaltstack, - SIGSTKSZ}; + use sys::c::{siginfo, sigaction, SIGBUS, SIG_DFL, + SA_SIGINFO, SA_ONSTACK, sigaltstack, + SIGSTKSZ, sighandler_t, raise}; use libc; use libc::funcs::posix88::mman::{mmap, munmap}; + use libc::funcs::posix01::signal::signal; use libc::consts::os::posix88::{SIGSEGV, PROT_READ, PROT_WRITE, @@ -120,7 +121,7 @@ mod imp { pub unsafe fn make_handler() -> Handler { let alt_stack = mmap(ptr::null_mut(), - signal::SIGSTKSZ, + SIGSTKSZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, @@ -143,138 +144,6 @@ mod imp { pub unsafe fn drop_handler(handler: &mut Handler) { munmap(handler._data, SIGSTKSZ); } - - pub type sighandler_t = *mut libc::c_void; - - #[cfg(any(all(target_os = "linux", target_arch = "x86"), // may not match - all(target_os = "linux", target_arch = "x86_64"), - all(target_os = "linux", target_arch = "arm"), // may not match - all(target_os = "linux", target_arch = "aarch64"), - all(target_os = "linux", target_arch = "mips"), // may not match - all(target_os = "linux", target_arch = "mipsel"), // may not match - all(target_os = "linux", target_arch = "powerpc"), // may not match - target_os = "android"))] // may not match - mod signal { - use libc; - pub use super::sighandler_t; - - pub static SA_ONSTACK: libc::c_int = 0x08000000; - pub static SA_SIGINFO: libc::c_int = 0x00000004; - pub static SIGBUS: libc::c_int = 7; - - pub static SIGSTKSZ: libc::size_t = 8192; - - pub const SIG_DFL: sighandler_t = 0 as sighandler_t; - - // This definition is not as accurate as it could be, {si_addr} is - // actually a giant union. Currently we're only interested in that field, - // however. - #[repr(C)] - pub struct siginfo { - si_signo: libc::c_int, - si_errno: libc::c_int, - si_code: libc::c_int, - pub si_addr: *mut libc::c_void - } - - #[repr(C)] - pub struct sigaction { - pub sa_sigaction: sighandler_t, - pub sa_mask: sigset_t, - pub sa_flags: libc::c_int, - sa_restorer: *mut libc::c_void, - } - - #[cfg(target_pointer_width = "32")] - #[repr(C)] - pub struct sigset_t { - __val: [libc::c_ulong; 32], - } - #[cfg(target_pointer_width = "64")] - #[repr(C)] - pub struct sigset_t { - __val: [libc::c_ulong; 16], - } - - #[repr(C)] - pub struct sigaltstack { - pub ss_sp: *mut libc::c_void, - pub ss_flags: libc::c_int, - pub ss_size: libc::size_t - } - - } - - #[cfg(any(target_os = "macos", - target_os = "bitrig", - target_os = "openbsd"))] - mod signal { - use libc; - pub use super::sighandler_t; - - pub const SA_ONSTACK: libc::c_int = 0x0001; - pub const SA_SIGINFO: libc::c_int = 0x0040; - pub const SIGBUS: libc::c_int = 10; - - #[cfg(target_os = "macos")] - pub const SIGSTKSZ: libc::size_t = 131072; - #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] - pub const SIGSTKSZ: libc::size_t = 40960; - - pub const SIG_DFL: sighandler_t = 0 as sighandler_t; - - pub type sigset_t = u32; - - // This structure has more fields, but we're not all that interested in - // them. - #[cfg(target_os = "macos")] - #[repr(C)] - pub struct siginfo { - pub si_signo: libc::c_int, - pub si_errno: libc::c_int, - pub si_code: libc::c_int, - pub pid: libc::pid_t, - pub uid: libc::uid_t, - pub status: libc::c_int, - pub si_addr: *mut libc::c_void - } - - #[cfg(any(target_os = "bitrig", target_os = "openbsd"))] - #[repr(C)] - pub struct siginfo { - pub si_signo: libc::c_int, - pub si_code: libc::c_int, - pub si_errno: libc::c_int, - //union - pub si_addr: *mut libc::c_void - } - - #[repr(C)] - pub struct sigaltstack { - pub ss_sp: *mut libc::c_void, - pub ss_size: libc::size_t, - pub ss_flags: libc::c_int - } - - #[repr(C)] - pub struct sigaction { - pub sa_sigaction: sighandler_t, - pub sa_mask: sigset_t, - pub sa_flags: libc::c_int, - } - } - - extern { - pub fn signal(signum: libc::c_int, handler: sighandler_t) -> sighandler_t; - pub fn raise(signum: libc::c_int) -> libc::c_int; - - pub fn sigaction(signum: libc::c_int, - act: *const sigaction, - oldact: *mut sigaction) -> libc::c_int; - - pub fn sigaltstack(ss: *const sigaltstack, - oss: *mut sigaltstack) -> libc::c_int; - } } #[cfg(not(any(target_os = "linux", From cae005162d1d7aea6cffdc299fedf0d2bb2a4b28 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Sat, 23 May 2015 22:25:49 -0400 Subject: [PATCH 4/5] sys/unix/process: Reset signal behavior before exec Make sure that child processes don't get affected by libstd's desire to ignore SIGPIPE, nor a third-party library's signal mask (which is needed to use either a signal-handling thread correctly or to use signalfd / kqueue correctly). --- src/libstd/sys/unix/c.rs | 13 ++++- src/libstd/sys/unix/process.rs | 74 ++++++++++++++++++++++++++++ src/test/run-pass/process-sigpipe.rs | 39 +++++++++++++++ 3 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/process-sigpipe.rs diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index 17e40bca37527..431312b3d8fec 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -25,7 +25,7 @@ #![allow(non_camel_case_types)] pub use self::signal_os::{sigaction, siginfo, sigset_t, sigaltstack}; -pub use self::signal_os::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSTKSZ}; +pub use self::signal_os::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIGSTKSZ, SIG_SETMASK}; use libc; @@ -112,6 +112,7 @@ pub struct passwd { pub type sighandler_t = *mut libc::c_void; pub const SIG_DFL: sighandler_t = 0 as sighandler_t; +pub const SIG_ERR: sighandler_t = !0 as sighandler_t; extern { pub fn getsockopt(sockfd: libc::c_int, @@ -135,6 +136,8 @@ extern { oss: *mut sigaltstack) -> libc::c_int; pub fn sigemptyset(set: *mut sigset_t) -> libc::c_int; + pub fn pthread_sigmask(how: libc::c_int, set: *const sigset_t, + oldset: *mut sigset_t) -> libc::c_int; #[cfg(not(target_os = "ios"))] pub fn getpwuid_r(uid: libc::uid_t, @@ -155,7 +158,7 @@ extern { #[cfg(any(target_os = "linux", target_os = "android"))] mod signal_os { - pub use self::arch::{SA_ONSTACK, SA_SIGINFO, SIGBUS, + pub use self::arch::{SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_SETMASK, sigaction, sigaltstack}; use libc; @@ -216,6 +219,8 @@ mod signal_os { pub const SIGBUS: libc::c_int = 7; + pub const SIG_SETMASK: libc::c_int = 2; + #[cfg(target_os = "linux")] #[repr(C)] pub struct sigaction { @@ -263,6 +268,8 @@ mod signal_os { pub const SIGBUS: libc::c_int = 10; + pub const SIG_SETMASK: libc::c_int = 3; + #[cfg(all(target_os = "linux", not(target_env = "musl")))] #[repr(C)] pub struct sigaction { @@ -321,6 +328,8 @@ mod signal_os { #[cfg(not(any(target_os = "macos", target_os = "ios")))] pub const SIGSTKSZ: libc::size_t = 40960; + pub const SIG_SETMASK: libc::c_int = 3; + #[cfg(any(target_os = "macos", target_os = "ios"))] pub type sigset_t = u32; diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index acf6fbc24e461..85ce8d798801f 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -17,6 +17,7 @@ use ffi::{OsString, OsStr, CString, CStr}; use fmt; use io::{self, Error, ErrorKind}; use libc::{self, pid_t, c_void, c_int, gid_t, uid_t}; +use mem; use ptr; use sys::fd::FileDesc; use sys::fs::{File, OpenOptions}; @@ -313,6 +314,23 @@ impl Process { if !envp.is_null() { *sys::os::environ() = envp as *const _; } + + // Reset signal handling so the child process starts in a + // standardized state. libstd ignores SIGPIPE, and signal-handling + // libraries often set a mask. Child processes inherit ignored + // signals and the signal mask from their parent, but most + // UNIX programs do not reset these things on their own, so we + // need to clean things up now to avoid confusing the program + // we're about to run. + let mut set: c::sigset_t = mem::uninitialized(); + if c::sigemptyset(&mut set) != 0 || + c::pthread_sigmask(c::SIG_SETMASK, &set, ptr::null_mut()) != 0 || + libc::funcs::posix01::signal::signal( + libc::SIGPIPE, mem::transmute(c::SIG_DFL) + ) == mem::transmute(c::SIG_ERR) { + fail(&mut output); + } + let _ = libc::execvp(*argv, argv); fail(&mut output) } @@ -418,3 +436,59 @@ fn translate_status(status: c_int) -> ExitStatus { ExitStatus::Signal(imp::WTERMSIG(status)) } } + +#[cfg(test)] +mod tests { + use super::*; + use prelude::v1::*; + + use ffi::OsStr; + use mem; + use ptr; + use libc; + use sys::{self, c, cvt, pipe}; + + extern { + fn sigaddset(set: *mut c::sigset_t, signum: libc::c_int) -> libc::c_int; + } + + #[test] + fn test_process_mask() { + unsafe { + // Test to make sure that a signal mask does not get inherited. + let cmd = Command::new(OsStr::new("cat")); + let (stdin_read, stdin_write) = sys::pipe::anon_pipe().unwrap(); + let (stdout_read, stdout_write) = sys::pipe::anon_pipe().unwrap(); + + let mut set: c::sigset_t = mem::uninitialized(); + let mut old_set: c::sigset_t = mem::uninitialized(); + cvt(c::sigemptyset(&mut set)).unwrap(); + cvt(sigaddset(&mut set, libc::SIGINT)).unwrap(); + cvt(c::pthread_sigmask(c::SIG_SETMASK, &set, &mut old_set)).unwrap(); + + let cat = Process::spawn(&cmd, Stdio::Raw(stdin_read.raw()), + Stdio::Raw(stdout_write.raw()), + Stdio::None).unwrap(); + drop(stdin_read); + drop(stdout_write); + + cvt(c::pthread_sigmask(c::SIG_SETMASK, &old_set, ptr::null_mut())).unwrap(); + + cvt(libc::funcs::posix88::signal::kill(cat.id() as libc::pid_t, libc::SIGINT)).unwrap(); + // 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!(ret == 0); + } + + cat.wait().unwrap(); + } + } +} diff --git a/src/test/run-pass/process-sigpipe.rs b/src/test/run-pass/process-sigpipe.rs new file mode 100644 index 0000000000000..5bff4fa080a34 --- /dev/null +++ b/src/test/run-pass/process-sigpipe.rs @@ -0,0 +1,39 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-android since the dynamic linker sets a SIGPIPE handler (to do +// a crash report) so inheritance is moot on the entire platform + +// libstd ignores SIGPIPE, and other libraries may set signal masks. +// Make sure that these behaviors don't get inherited to children +// spawned via std::process, since they're needed for traditional UNIX +// filter behavior. This test checks that `yes | head` terminates +// (instead of running forever), and that it does not print an error +// message about a broken pipe. + +use std::process; +use std::thread; + +#[cfg(unix)] +fn main() { + // Just in case `yes` doesn't check for EPIPE... + thread::spawn(|| { + thread::sleep_ms(5000); + process::exit(1); + }); + let output = process::Command::new("sh").arg("-c").arg("yes | head").output().unwrap(); + assert!(output.status.success()); + assert!(output.stderr.len() == 0); +} + +#[cfg(not(unix))] +fn main() { + // Not worried about signal masks on other platforms +} From a8dbb92b471cae1d3f8225857f5553311dd8aeb3 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Thu, 11 Jun 2015 16:23:57 -0400 Subject: [PATCH 5/5] Fix build on Android API levels below 21 signal(), sigemptyset(), and sigaddset() are only available as inline functions until Android API 21. liblibc already handles signal() appropriately, so drop it from c.rs; translate sigemptyset() and sigaddset() (which is only used in a test) by hand from the C inlines. We probably want to revert this commit when we bump Android API level. --- src/libstd/sys/unix/c.rs | 10 ++++++++++ src/libstd/sys/unix/process.rs | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index 431312b3d8fec..99a6731c57d95 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -135,7 +135,9 @@ extern { pub fn sigaltstack(ss: *const sigaltstack, oss: *mut sigaltstack) -> libc::c_int; + #[cfg(not(target_os = "android"))] pub fn sigemptyset(set: *mut sigset_t) -> libc::c_int; + pub fn pthread_sigmask(how: libc::c_int, set: *const sigset_t, oldset: *mut sigset_t) -> libc::c_int; @@ -155,6 +157,14 @@ extern { -> *mut libc::c_char; } +// Ugh. This is only available as an inline until Android API 21. +#[cfg(target_os = "android")] +pub unsafe fn sigemptyset(set: *mut sigset_t) -> libc::c_int { + use intrinsics; + intrinsics::write_bytes(set, 0, 1); + return 0; +} + #[cfg(any(target_os = "linux", target_os = "android"))] mod signal_os { diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 85ce8d798801f..695d0ddfaaf61 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -446,12 +446,22 @@ mod tests { use mem; use ptr; use libc; + use slice; use sys::{self, c, cvt, pipe}; + #[cfg(not(target_os = "android"))] extern { fn sigaddset(set: *mut c::sigset_t, signum: libc::c_int) -> libc::c_int; } + #[cfg(target_os = "android")] + unsafe fn sigaddset(set: *mut c::sigset_t, signum: libc::c_int) -> libc::c_int { + let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::()); + let bit = (signum - 1) as usize; + raw[bit / 8] |= 1 << (bit % 8); + return 0; + } + #[test] fn test_process_mask() { unsafe {