fix(audit): exit silently on broken pipe#337
Conversation
intent(audit): make `runok audit --json | head -1` and similar pipelines match Unix conventions (e.g. `yes | head`) instead of surfacing a Rust panic with `failed printing to stdout: Broken pipe (os error 32)`. decision(main): restore SIG_DFL for SIGPIPE at process startup so EPIPE terminates the process quietly. Covers every stdout/stderr writer at once (audit --json, config-schema, future commands) with one syscall. rejected(audit): catching `ErrorKind::BrokenPipe` per call site — would need to be repeated for every `println!` in the binary and would not protect future output paths added later. constraint(main): only applied on `cfg(unix)`; Windows has no SIGPIPE. learned(rust): the standard library installs SIG_IGN for SIGPIPE at startup, which converts pipe writes into EPIPE — the `println!` macro then panics on the resulting `io::Error`. The upstream Rust issue tracking this is still open, so the fix has to live in the application.
…mber intent(docs): satisfy the project rule that next.md entries must carry a resolvable PR link by the time the entry merges.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #337 +/- ##
==========================================
- Coverage 88.74% 88.73% -0.02%
==========================================
Files 53 53
Lines 12163 12168 +5
==========================================
+ Hits 10794 10797 +3
- Misses 1369 1371 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review
This pull request fixes a panic that occurs when runok audit --json is piped into a consumer that closes stdout early, such as head. The fix involves restoring the default SIGPIPE handler on Unix systems to ensure the process terminates silently on EPIPE. The changes include a new regression test and an update to the release notes. Feedback includes replacing a placeholder link in the documentation and optimizing string construction in the test suite by using String::with_capacity and writeln! to avoid inefficient repeated allocations.
…ed buffer intent(audit): keep test setup cheap by avoiding 5000 short-lived `format!` allocations when assembling the ~5 MiB JSONL payload used to overflow the pipe buffer.
Purpose
runok audit --json | head -1(or similar consumers that close stdout early) makes runok panic withfailed printing to stdout: Broken pipe (os error 32), so a Rust backtrace is printed to stderr next to the JSON lineReproduction steps
runok audit --json | head -1Approach
SIG_DFL) at the start ofmainso the process exits silently on EPIPESIG_IGNfor SIGPIPE at startup, which turns pipe writes intoio::Errorand makesprintln!panic on the failureDesign decisions
How to handle EPIPE
SIG_DFLat the start ofmainunsafelibc callwriteln!per call site and swallowBrokenPipeunsafe