-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
yes, timeout: adding pipeline signal handlers #10166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -488,6 +488,25 @@ pub const fn sigpipe_was_ignored() -> bool { | |
| false | ||
| } | ||
|
|
||
| /// Configures a Command to preserve SIGPIPE disposition for child processes. | ||
| /// When the parent had SIGPIPE ignored, this ensures the child also has it ignored. | ||
| #[cfg(unix)] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a comment explaining that Rust's stdlib resets SIGPIPE to SIG_DFL when spawning child processes (Rust source: https://github.com/rust-lang/rust/blob/fe98ddcfcfb6f185dbf4adeaf439d8a756da0273/library/std/src/sys/process/unix/unix.rs#L742 ), unless the -Zon-broken-pipe flag is used. This breaks Unix signal inheritance when the parent has SIGPIPE ignored, which is why preserve_sigpipe_for_child() uses pre_exec() to restore the ignored state before exec. It is critical context that the rust stdlib restores SIGPIPE to SIG_DFL when calling Command functions. Without this context, I'm concerned that future readers would not understand the role of preserve_sigpipe_for_child(). |
||
| pub fn preserve_sigpipe_for_child(cmd: &mut std::process::Command) { | ||
| use std::os::unix::process::CommandExt; | ||
| if sigpipe_was_ignored() { | ||
| unsafe { | ||
| cmd.pre_exec(|| { | ||
| nix::sys::signal::signal( | ||
| nix::sys::signal::Signal::SIGPIPE, | ||
| nix::sys::signal::SigHandler::SigIgn, | ||
| ) | ||
| .map_err(std::io::Error::other)?; | ||
| Ok(()) | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[cfg(target_os = "linux")] | ||
| pub fn ensure_stdout_not_broken() -> std::io::Result<bool> { | ||
| use nix::{ | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,10 @@ use rstest::rstest; | |||||
|
|
||||||
| use uucore::display::Quotable; | ||||||
| use uutests::new_ucmd; | ||||||
| #[cfg(all(unix, feature = "yes"))] | ||||||
| use uutests::util::TestScenario; | ||||||
| #[cfg(all(unix, feature = "yes"))] | ||||||
| use uutests::util_name; | ||||||
|
|
||||||
| #[test] | ||||||
| fn test_invalid_arg() { | ||||||
|
|
@@ -235,3 +239,17 @@ fn test_command_cannot_invoke() { | |||||
| // Try to execute a directory (should give permission denied or similar) | ||||||
| new_ucmd!().args(&["1", "/"]).fails_with_code(126); | ||||||
| } | ||||||
|
|
||||||
| /// Test for https://github.com/uutils/coreutils/issues/7252 | ||||||
| #[test] | ||||||
| #[cfg(unix)] | ||||||
| #[cfg(feature = "yes")] | ||||||
| fn test_sigpipe_ignored_preserves_for_child() { | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| let ts = TestScenario::new(util_name!()); | ||||||
| let bin = ts.bin_path.clone().into_os_string(); | ||||||
| let result = ts | ||||||
| .cmd_shell("trap '' PIPE; \"$BIN\" timeout 10 \"$BIN\" yes 2>err |:; cat err") | ||||||
| .env("BIN", &bin) | ||||||
| .succeeds(); | ||||||
| assert!(result.stdout_str().contains("Broken pipe")); | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, we should use "err.kind()" instead of "err" in order to match exactly GNU yes error string and remove the "os error 32":