Skip to content

Commit

Permalink
scubainit: Restore the default SIGPIPE action
Browse files Browse the repository at this point in the history
Rust pre-main code may change the SIGPIPE disposition to ignore:
* rust-lang/rust#62569
* rust-lang/rust#97889

We could use the nightly compiler flag -Zon-broken-pipe=inherit to
disable this behavior. Instead, we take the simpler route and restore
the default disposition ourselves.

Fixes #254
  • Loading branch information
JonathonReinhart committed May 29, 2024
1 parent 325fb58 commit 3ddfaf5
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Fixed
- Fixed SIGPIPE disposition being set to ignore (#255)

## [2.13.0] - 2024-03-25
### Added
- Added support for Python 3.12 (#244)
Expand Down
33 changes: 33 additions & 0 deletions scubainit/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ fn run_scubainit() -> Result<()> {
setup_logging()?;
info!("Looking Rusty!");

restore_sigpipe_default()?;

let ctx = process_envvars()?;

if let Some(ref user_info) = ctx.user_info {
Expand Down Expand Up @@ -368,3 +370,34 @@ fn setup_logging() -> Result<()> {
.verbosity(verbosity)
.init()?)
}

// Restore the default SIGPIPE action.
//
// Rust pre-main code may change the SIGPIPE disposition to ignore:
// * https://github.com/rust-lang/rust/issues/62569
// * https://github.com/rust-lang/rust/issues/97889
//
// While scubainit doesn't rely on any specific SIGPIPE behavior, the dispositions of ignored
// signals are inherited through execve and Rust's pre-main code shouldn't influence the process
// executed by scuba.
//
// From man signal(7):
//
// Signal dispositions ... A child created via fork(2) inherits a copy of its parent's signal
// dispositions. During an execve(2), the dispositions of handled signals are reset to the
// default; the dispositions of ignored signals are left unchanged.
//
// This code may be unnecessary in the future if Rust's execve library function is changed to
// restore SIGPIPE.
fn restore_sigpipe_default() -> Result<()> {
// Set the SIGPIPE disposition to default (terminate).
// SAFETY: No signal handler is provided, only the default action,
// so no additional consideration is needed.
unsafe {
let result = libc::signal(libc::SIGPIPE, libc::SIG_DFL);
if result == libc::SIG_ERR {
return Err(std::io::Error::last_os_error().into());
}
}
Ok(())
}
11 changes: 11 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,17 @@ def test_redirect_stdin(self) -> None:
assert_str_equalish(out, test_str)


class TestMainSignals(MainTest):
def test_sigpipe(self) -> None:
"""Verify SIGPIPE is handled correctly"""
# See https://github.com/JonathonReinhart/scuba/issues/254
SCUBA_YML.write_text(f"image: {DOCKER_IMAGE}")

out, err = run_scuba(["sh", "-c", "yes | echo abcd"])
assert_str_equalish(out, "abcd")
assert_str_equalish(err, "")


class TestMainUser(MainTest):
def _test_user(
self,
Expand Down

0 comments on commit 3ddfaf5

Please sign in to comment.