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.
This commit is contained in:
Martin Nordholts 2022-07-05 19:56:22 +02:00
parent ee285eab69
commit ddee45e1d7
46 changed files with 449 additions and 43 deletions

View file

@ -0,0 +1,26 @@
#![feature(rustc_private)]
extern crate libc;
/// So tests don't have to bring libc in scope themselves
pub enum SignalHandler {
Ignore,
Default,
}
/// Helper to assert that [`libc::SIGPIPE`] has the expected signal handler.
pub fn assert_sigpipe_handler(expected_handler: SignalHandler) {
#[cfg(unix)]
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))]
{
let prev = unsafe { libc::signal(libc::SIGPIPE, libc::SIG_IGN) };
let expected = match expected_handler {
SignalHandler::Ignore => libc::SIG_IGN,
SignalHandler::Default => libc::SIG_DFL,
};
assert_eq!(prev, expected);
// Unlikely to matter, but restore the old value anyway
unsafe { libc::signal(libc::SIGPIPE, prev); };
}
}

View file

@ -0,0 +1,4 @@
#![feature(unix_sigpipe)]
#![unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute cannot be used at crate level
fn main() {}

View file

@ -0,0 +1,13 @@
error: `unix_sigpipe` attribute cannot be used at crate level
--> $DIR/unix_sigpipe-crate.rs:2:1
|
LL | #![unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: perhaps you meant to use an outer attribute
|
LL | #[unix_sigpipe = "inherit"]
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error

View file

@ -0,0 +1,5 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_ign"]
#[unix_sigpipe = "inherit"] //~ error: multiple `unix_sigpipe` attributes
fn main() {}

View file

@ -0,0 +1,14 @@
error: multiple `unix_sigpipe` attributes
--> $DIR/unix_sigpipe-duplicates.rs:4:1
|
LL | #[unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
|
note: attribute also specified here
--> $DIR/unix_sigpipe-duplicates.rs:3:1
|
LL | #[unix_sigpipe = "sig_ign"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View file

@ -0,0 +1,13 @@
// run-pass
// aux-build:sigpipe-utils.rs
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_ign"]
fn main() {
extern crate sigpipe_utils;
// #[unix_sigpipe = "sig_ign"] is active, so the legacy behavior of ignoring
// SIGPIPE shall be in effect
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
}

View file

@ -0,0 +1,14 @@
// run-pass
// aux-build:sigpipe-utils.rs
#![feature(unix_sigpipe)]
#[unix_sigpipe = "inherit"]
fn main() {
extern crate sigpipe_utils;
// #[unix_sigpipe = "inherit"] is active, so SIGPIPE shall NOT be ignored,
// instead the default handler shall be installed. (We assume that the
// process that runs these tests have the default handler.)
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
}

View file

@ -0,0 +1,4 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe(inherit)] //~ error: malformed `unix_sigpipe` attribute input
fn main() {}

View file

@ -0,0 +1,15 @@
error: malformed `unix_sigpipe` attribute input
--> $DIR/unix_sigpipe-list.rs:3:1
|
LL | #[unix_sigpipe(inherit)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL | #[unix_sigpipe = "inherit|sig_ign|sig_dfl"]
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL | #[unix_sigpipe]
| ~~~~~~~~~~~~~~~
error: aborting due to previous error

View file

@ -0,0 +1,6 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()`
fn f() {}
fn main() {}

View file

@ -0,0 +1,8 @@
error: `unix_sigpipe` attribute can only be used on `fn main()`
--> $DIR/unix_sigpipe-non-main-fn.rs:3:1
|
LL | #[unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View file

@ -0,0 +1,8 @@
#![feature(unix_sigpipe)]
mod m {
#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on root `fn main()`
fn main() {}
}
fn main() {}

View file

@ -0,0 +1,8 @@
error: `unix_sigpipe` attribute can only be used on root `fn main()`
--> $DIR/unix_sigpipe-non-root-main.rs:4:5
|
LL | #[unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View file

@ -0,0 +1,9 @@
// run-pass
// aux-build:sigpipe-utils.rs
fn main() {
extern crate sigpipe_utils;
// SIGPIPE shall be ignored since #[unix_sigpipe = "..."] is not used
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
}

View file

@ -0,0 +1,13 @@
// run-pass
// aux-build:sigpipe-utils.rs
#![feature(unix_sigpipe)]
fn main() {
extern crate sigpipe_utils;
// Only #![feature(unix_sigpipe)] is enabled, not #[unix_sigpipe = "..."].
// This shall not change any behavior, so we still expect SIGPIPE to be
// ignored
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
}

View file

@ -0,0 +1,15 @@
// run-pass
// aux-build:sigpipe-utils.rs
#![feature(unix_sigpipe)]
#![feature(rustc_attrs)]
#[unix_sigpipe = "sig_dfl"]
#[rustc_main]
fn rustc_main() {
extern crate sigpipe_utils;
// #[unix_sigpipe = "sig_dfl"] is active, so SIGPIPE handler shall be
// SIG_DFL. Note that we have a #[rustc_main], but it should still work.
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
}

View file

@ -0,0 +1,13 @@
// run-pass
// aux-build:sigpipe-utils.rs
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_dfl"]
fn main() {
extern crate sigpipe_utils;
// #[unix_sigpipe = "sig_dfl"] is active, so SIGPIPE shall NOT be ignored, instead
// the default handler shall be installed
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
}

View file

@ -0,0 +1,6 @@
#![feature(start)]
#![feature(unix_sigpipe)]
#[start]
#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()`
fn custom_start(argc: isize, argv: *const *const u8) -> isize { 0 }

View file

@ -0,0 +1,8 @@
error: `unix_sigpipe` attribute can only be used on `fn main()`
--> $DIR/unix_sigpipe-start.rs:5:1
|
LL | #[unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View file

@ -0,0 +1,6 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()`
struct S;
fn main() {}

View file

@ -0,0 +1,8 @@
error: `unix_sigpipe` attribute can only be used on `fn main()`
--> $DIR/unix_sigpipe-struct.rs:3:1
|
LL | #[unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View file

@ -0,0 +1,4 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "wrong"] //~ error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
fn main() {}

View file

@ -0,0 +1,8 @@
error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
--> $DIR/unix_sigpipe-wrong.rs:3:1
|
LL | #[unix_sigpipe = "wrong"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View file

@ -0,0 +1,4 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe] //~ error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
fn main() {}

View file

@ -0,0 +1,8 @@
error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
--> $DIR/unix_sigpipe.rs:3:1
|
LL | #[unix_sigpipe]
| ^^^^^^^^^^^^^^^
error: aborting due to previous error