-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Spurious "broken pipe" error messages, when used in typical UNIX shell pipelines #46016
Comments
We could provide a function in std::io to unignore SIGPIPE so applications could more easily opt-in to acting like a "standard" command line program. |
Perhaps only the error message should be suppressed, it looks like the "traditional" programs do fail as a result of a broken pipe:
141 seems to be the traditional exit code for a broken pipe. |
141 is the exit code set by the kernel after it has terminated a process due to a SIGPIPE. |
I'm not sure what that API would look like: call a magic The former feels like it's just setting a global variable, which has a pretty bad smell. The latter means that unless you switch to using the new SIGPIPE-respecting macros throughout, your code might still generate the error. What's not obvious to me is why Rust ignores that signal in the first place. I see that there's a test in place designed to ensure that the process shouldn't just crash, but at the same time the whole point of SIGPIPE is to terminate the receiving process silently. My intuition of correct behavior from Rust would be for it to do the same thing it does on SIGTERM: immediately, cleanly, and quietly shut itself down. |
That's what it would be presumably.
Signal disposition is a process-global setting. Feel free to complain to the POSIX standards commitee about the smell of their global variables.
SIGPIPE is a kind of hacky thing that only really makes sense when writing command line applications designed to be used in pipelines that only poke at their standard inputs and outputs. If you are writing anything more complex then it's something you need to turn off. Imagine a web server that crashed any time a client hung up, or a command line application that talks to the internet and crashed every time the server hung up. |
Haha, fair enough. I also do appreciate the explanation of the reasoning of turning it off by default. My own Rust applications tend to be unixy command-line applications which only ever really poke at their standard inputs and outputs, so that's the lens through which I view this issue, but I couldn't argue against the assertion that ignoring SIGPIPE is a more useful default. In that case, I'd say that having an |
It'd just run this code: rust/src/libstd/sys/unix/process/process_unix.rs Lines 199 to 222 in c284f88
|
1) If output is piped into `less`, and `less` aborts, then the ensuing EPIPE failures should not cause a panic. See Rust issue: rust-lang/rust#46016 The workaround is to not use `print!()` or `println!()`, unfortunately. 2) Before this change, if any of the child processes has terminated pty-for-each has not really waited for the pid, but only for the closing the terminal pipe. This change makes pty-for-each wait for both. 3) Any non-zero exit status for one or more of the child processes should cause a non-zero exit status from pty-for-each by default. This change implements an exit status of 0xfe if all processes exited with a signal 0xff if all processes exited normally with a non-zero exit status, and 0xfd on mixed situations of the former two.
Found this out today when piping stdout to head. Any ideas how to fix it nicely? Maybe we can take inspiration from other languages implementations. |
Until someone implements and merges |
In the short term you can do:
|
I seem to have run into this as well (see above issue), and I am finding the various linked and recommended solutions a bit vague. It's not clear to me how to assemble the bits and pieces to go about using it with I was able to get a simple solution working with the I must say it does seem a bit strange for my program to crash because of what another program downstream does or doesn't do with its inputs, but I would welcome any input on what a clear drop-in solution is that doesn't introduce any performance cost. |
|
I was having trouble sorting out how to use |
This affects the compiler itself:
|
Allows better handling of SIGPIPE failures. Ref rust-lang/rust#46016
The way I've worked around this is by replacing the // These macros are needed because the normal ones panic when there's a broken pipe.
// This is especially problematic for CLI tools that are frequently piped into `head` or `grep -q`
macro_rules! println {
() => (print!("\n"));
($fmt:expr) => ({
writeln!(std::io::stdout(), $fmt)
});
($fmt:expr, $($arg:tt)*) => ({
writeln!(std::io::stdout(), $fmt, $($arg)*)
})
}
macro_rules! print {
() => (print!("\n"));
($fmt:expr) => ({
write!(std::io::stdout(), $fmt)
});
($fmt:expr, $($arg:tt)*) => ({
write!(std::io::stdout(), $fmt, $($arg)*)
})
} And then handle the std::io::Error appropriately: fn main() -> ExitCode {
match run() {
Err(Error::IOError(err)) if err.kind() == io::ErrorKind::BrokenPipe => {
// Okay, this happens when the output is piped to a program like `head`
ExitCode::SUCCESS
}
Err(err) => {
eprintln!("{}", err).ok();
ExitCode::FAILURE
}
Ok(_) => ExitCode::SUCCESS,
}
} |
Fixes #687 See Misterio77/flavours#16 and rust-lang/rust#46016 for more context.
This was originally filed here but @sfackler determined the cause:
Note that to see the backtrace, the data being piped has to be large enough to overflow the kernel pipe buffer.
The text was updated successfully, but these errors were encountered: