From d4a949ab31178ec260994c396c31bf3242aeb627 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Fri, 22 May 2015 16:39:21 -0400 Subject: [PATCH 1/6] 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 ae9316ddd622b..e20d35c094999 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -737,24 +737,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 9d22df54330192f6afb0d5764c83701290d2811d Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Fri, 22 May 2015 17:30:06 -0400 Subject: [PATCH 2/6] 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/sys/unix/c.rs | 80 +++------------------------------------- 1 file changed, 5 insertions(+), 75 deletions(-) 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 11604f7375b4f1e866de2b23bdc5a10faa3704c2 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Sat, 23 May 2015 18:07:52 -0400 Subject: [PATCH 3/6] 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). Between the last commit and this one, we can now drop `#![allow(dead_code)]` from c.rs, which should help avoid binding rot in the future. --- src/libstd/sys/unix/c.rs | 328 +++++++++++++++----------- src/libstd/sys/unix/stack_overflow.rs | 140 +---------- 2 files changed, 199 insertions(+), 269 deletions(-) diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index de19206d93dd7..80788ed5e4fb8 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -8,14 +8,23 @@ // 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 +106,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 +124,16 @@ extern { pub fn waitpid(pid: libc::pid_t, status: *mut libc::c_int, options: libc::c_int) -> libc::pid_t; + 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 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::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..f05d88d35a553 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -44,9 +44,9 @@ 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, signal, raise}; use libc; use libc::funcs::posix88::mman::{mmap, munmap}; use libc::consts::os::posix88::{SIGSEGV, @@ -120,7 +120,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 +143,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 15d62f6646c1e777468c925d1b27068eecedd5fe Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Sat, 23 May 2015 22:25:49 -0400 Subject: [PATCH 4/6] 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 | 13 ++++++++++ src/test/run-pass/process-sigpipe.rs | 36 ++++++++++++++++++++++++++++ 3 files changed, 60 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 80788ed5e4fb8..a7fc4e875b56a 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -24,7 +24,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; @@ -111,6 +111,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 f4bc597304097..aa5f6699144a4 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::pipe::AnonPipe; use sys::{self, c, cvt, cvt_r}; @@ -311,6 +312,18 @@ impl Process { if !envp.is_null() { *sys::os::environ() = envp as *const _; } + + // Reset signal handling so the child process starts in a + // standardized state. We ignore SIGPIPE, and signal-handling + // libraries often set a mask; both of these get inherited, + // which most UNIX programs don't expect. + 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 || + c::signal(libc::SIGPIPE, c::SIG_DFL) == c::SIG_ERR { + fail(&mut output); + } + let _ = libc::execvp(*argv, argv as *mut _); fail(&mut output) } diff --git a/src/test/run-pass/process-sigpipe.rs b/src/test/run-pass/process-sigpipe.rs new file mode 100644 index 0000000000000..447c0c4794ac9 --- /dev/null +++ b/src/test/run-pass/process-sigpipe.rs @@ -0,0 +1,36 @@ +// 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. + +// 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 6a1ecbb4128013a1ff1bcfbd29b208abb5c41eff Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Tue, 26 May 2015 16:17:29 -0400 Subject: [PATCH 5/6] sys/unix/process.rs: Clarify comment, per code review feedback --- src/libstd/sys/unix/process.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index aa5f6699144a4..b154805b675d6 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -314,9 +314,12 @@ impl Process { } // Reset signal handling so the child process starts in a - // standardized state. We ignore SIGPIPE, and signal-handling - // libraries often set a mask; both of these get inherited, - // which most UNIX programs don't expect. + // 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 || From 8e2e0eed6dbac279d5f355861807d9983fbe4204 Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Wed, 27 May 2015 22:36:01 -0400 Subject: [PATCH 6/6] sys/unix/process: Add test for non-inherited signal masks --- src/libstd/sys/unix/process.rs | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index b154805b675d6..71b798945b98a 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -432,3 +432,57 @@ 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::Piped(stdin_read), + Stdio::Piped(stdout_write), + Stdio::None).unwrap(); + + 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(); + } + } +}