diff --git a/Cargo.lock b/Cargo.lock index 41ca974..5d63310 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,6 +57,7 @@ dependencies = [ "aura-registry", "aura-std", "data-server", + "libc", "serde", "serde_json", ] diff --git a/crates/aura-cli/Cargo.toml b/crates/aura-cli/Cargo.toml index 27f8627..72ef1ff 100644 --- a/crates/aura-cli/Cargo.toml +++ b/crates/aura-cli/Cargo.toml @@ -29,3 +29,9 @@ serde = { workspace = true } # and the injected chart traces (window.AURA_TRACES); # admitted under the per-case dependency policy, same as aura-engine. serde_json = { workspace = true } +# libc: restore the default SIGPIPE disposition at startup (see `fn main`) so a +# closed stdout pipe (`aura sweep | head`, a closed UI pane) terminates the +# process cleanly instead of panicking the runtime's SIG_IGN default into an +# EPIPE. The standard vetted crate for signal disposition, already transitive in +# Cargo.lock; admitted under the per-case dependency policy. +libc = "0.2" diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index 8a7a786..5867f6b 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -3082,6 +3082,15 @@ const USAGE: &str = "usage: aura run [--harness ] [--real [--from ] [--to ]] [--trace ] | aura chart [--tap ] [--panels] | aura graph | aura sweep [--strategy ] [--real [--from ] [--to ]] [--name |--trace ] | aura mc [--name |--trace ] | aura mc --strategy stage1-r [--real [--from ] [--to ]] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--block-len ] [--resamples ] [--seed ] | aura walkforward [--strategy ] [--real [--from ] [--to ]] [--name |--trace ] [--fast ] [--slow ] [--stop-length ] [--stop-k ] | aura generalize [--strategy stage1-r] --real --fast --slow --stop-length --stop-k [--from ] [--to ] [--metric ] [--name ] | aura runs families | aura runs family [rank ]"; fn main() { + // Restore the default SIGPIPE disposition. Rust's runtime sets SIGPIPE to SIG_IGN + // at startup, so a write to a closed stdout pipe (`aura sweep | head`, a closed UI + // pane) returns EPIPE and panics in `println!` instead of terminating quietly on + // SIGPIPE — the conventional Unix CLI behaviour. One reset covers every + // family-emitting subcommand at once. + #[cfg(unix)] + unsafe { + libc::signal(libc::SIGPIPE, libc::SIG_DFL); + } // Collect argv and match the whole vector: every accepted form is exhaustive, // so an unexpected trailing token falls through to the usage-error path rather // than masquerading as a successful run (#16 strict reading).