Skip to content

Commit d6e72ee

Browse files
committed
Support #[unix_sigpipe = "inherit|sig_dfl|sig_ign"] on fn main()
This makes it possible to instruct libstd to never touch the signal handler for `SIGPIPE`, which makes programs pipeable by default (e.g. with `./your-program | head -n 1`) without `ErrorKind::BrokenPipe` errors.
1 parent a5ef2a5 commit d6e72ee

File tree

7 files changed

+37
-14
lines changed

7 files changed

+37
-14
lines changed

std/src/rt.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ macro_rules! rtunwrap {
7373
// SAFETY: must be called only once during runtime initialization.
7474
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
7575
#[cfg_attr(test, allow(dead_code))]
76-
unsafe fn init(argc: isize, argv: *const *const u8) {
76+
unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
7777
unsafe {
78-
sys::init(argc, argv);
78+
sys::init(argc, argv, sigpipe);
7979

8080
let main_guard = sys::thread::guard::init();
8181
// Next, set up the current Thread with the guard information we just
@@ -107,6 +107,7 @@ fn lang_start_internal(
107107
main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
108108
argc: isize,
109109
argv: *const *const u8,
110+
sigpipe: u8,
110111
) -> Result<isize, !> {
111112
use crate::{mem, panic};
112113
let rt_abort = move |e| {
@@ -124,7 +125,7 @@ fn lang_start_internal(
124125
// prevent libstd from accidentally introducing a panic to these functions. Another is from
125126
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
126127
// SAFETY: Only called once during runtime initialization.
127-
panic::catch_unwind(move || unsafe { init(argc, argv) }).map_err(rt_abort)?;
128+
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
128129
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
129130
.map_err(move |e| {
130131
mem::forget(e);
@@ -140,11 +141,16 @@ fn lang_start<T: crate::process::Termination + 'static>(
140141
main: fn() -> T,
141142
argc: isize,
142143
argv: *const *const u8,
144+
#[cfg(not(bootstrap))] sigpipe: u8,
143145
) -> isize {
144146
let Ok(v) = lang_start_internal(
145147
&move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(),
146148
argc,
147149
argv,
150+
#[cfg(bootstrap)]
151+
2, // Temporary inlining of sigpipe::DEFAULT until bootstrap stops being special
152+
#[cfg(not(bootstrap))]
153+
sigpipe,
148154
);
149155
v
150156
}

std/src/sys/hermit/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub extern "C" fn __rust_abort() {
9898

9999
// SAFETY: must be called only once during runtime initialization.
100100
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
101-
pub unsafe fn init(argc: isize, argv: *const *const u8) {
101+
pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
102102
let _ = net::init();
103103
args::init(argc, argv);
104104
}

std/src/sys/sgx/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub mod locks {
4747

4848
// SAFETY: must be called only once during runtime initialization.
4949
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
50-
pub unsafe fn init(argc: isize, argv: *const *const u8) {
50+
pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
5151
unsafe {
5252
args::init(argc, argv);
5353
}

std/src/sys/solid/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub mod locks {
5656

5757
// SAFETY: must be called only once during runtime initialization.
5858
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
59-
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
59+
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
6060

6161
// SAFETY: must be called only once during runtime cleanup.
6262
pub unsafe fn cleanup() {}

std/src/sys/unix/mod.rs

+23-6
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ pub mod thread_parker;
4444
pub mod time;
4545

4646
#[cfg(target_os = "espidf")]
47-
pub fn init(argc: isize, argv: *const *const u8) {}
47+
pub fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {}
4848

4949
#[cfg(not(target_os = "espidf"))]
5050
// SAFETY: must be called only once during runtime initialization.
5151
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
52-
pub unsafe fn init(argc: isize, argv: *const *const u8) {
52+
pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
5353
// The standard streams might be closed on application startup. To prevent
5454
// std::io::{stdin, stdout,stderr} objects from using other unrelated file
5555
// resources opened later, we reopen standards streams when they are closed.
@@ -61,8 +61,9 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
6161
// want!
6262
//
6363
// Hence, we set SIGPIPE to ignore when the program starts up in order
64-
// to prevent this problem.
65-
reset_sigpipe();
64+
// to prevent this problem. Add `#[unix_sigpipe = "..."]` above `fn main()` to
65+
// alter this behavior.
66+
reset_sigpipe(sigpipe);
6667

6768
stack_overflow::init();
6869
args::init(argc, argv);
@@ -151,9 +152,25 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
151152
}
152153
}
153154

154-
unsafe fn reset_sigpipe() {
155+
unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) {
155156
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))]
156-
rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
157+
{
158+
// We don't want to add this as a public type to libstd, nor do we want to
159+
// duplicate the code, so we choose to include this compiler file like this.
160+
mod sigpipe {
161+
include!("../../../../../compiler/rustc_session/src/config/sigpipe.rs");
162+
}
163+
164+
let handler = match sigpipe {
165+
sigpipe::INHERIT => None,
166+
sigpipe::SIG_IGN => Some(libc::SIG_IGN),
167+
sigpipe::SIG_DFL => Some(libc::SIG_DFL),
168+
_ => unreachable!(),
169+
};
170+
if let Some(handler) = handler {
171+
rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR);
172+
}
173+
}
157174
}
158175
}
159176

std/src/sys/unsupported/common.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub mod memchr {
66

77
// SAFETY: must be called only once during runtime initialization.
88
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
9-
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
9+
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
1010

1111
// SAFETY: must be called only once during runtime cleanup.
1212
// NOTE: this is not guaranteed to run, for example when the program aborts.

std/src/sys/windows/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ cfg_if::cfg_if! {
4848

4949
// SAFETY: must be called only once during runtime initialization.
5050
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
51-
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
51+
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {
5252
stack_overflow::init();
5353

5454
// Normally, `thread::spawn` will call `Thread::set_name` but since this thread already

0 commit comments

Comments
 (0)