Skip to content

Commit d264f0c

Browse files
committed
fix: ignore stdout flush errors when stdout was already closed
- Added check to capture if stdout was closed before utility execution using fcntl on Unix. - Modified flush error handling to ignore EBADF errors when stdout was pre-closed, avoiding false failures for silent commands.
1 parent 42e8a1e commit d264f0c

File tree

1 file changed

+30
-1
lines changed

1 file changed

+30
-1
lines changed

src/uucore/src/lib/lib.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,42 @@ macro_rules! bin {
212212
std::process::exit(99)
213213
});
214214

215+
// Capture whether stdout was already closed before we run the utility.
216+
// This avoids treating a closed stdout as a flush failure for "silent" commands.
217+
let stdout_was_closed = {
218+
#[cfg(unix)]
219+
{
220+
use nix::fcntl::{fcntl, FcntlArg};
221+
match fcntl(std::io::stdout(), FcntlArg::F_GETFL) {
222+
Ok(_) => false,
223+
Err(nix::errno::Errno::EBADF) => true,
224+
Err(_) => false,
225+
}
226+
}
227+
#[cfg(not(unix))]
228+
{
229+
false
230+
}
231+
};
232+
215233
// execute utility code
216234
let mut code = $util::uumain(uucore::args_os());
217235
// (defensively) flush stdout for utility prior to exit; see <https://github.com/rust-lang/rust/issues/23818>
218236
if let Err(e) = std::io::stdout().flush() {
219237
// Treat write errors as a failure, but ignore BrokenPipe to avoid
220238
// breaking utilities that intentionally silence it (e.g., seq).
221-
if e.kind() != std::io::ErrorKind::BrokenPipe {
239+
let ignore_closed_stdout = stdout_was_closed
240+
&& {
241+
#[cfg(unix)]
242+
{
243+
e.raw_os_error() == Some(nix::errno::Errno::EBADF as i32)
244+
}
245+
#[cfg(not(unix))]
246+
{
247+
false
248+
}
249+
};
250+
if e.kind() != std::io::ErrorKind::BrokenPipe && !ignore_closed_stdout {
222251
eprintln!("Error flushing stdout: {e}");
223252
if code == 0 {
224253
code = 1;

0 commit comments

Comments
 (0)