From 5aee95b4e5720934745ecdc3ccdf9b833355f3aa Mon Sep 17 00:00:00 2001
From: Jan Scheer <jhscheer@users.noreply.github.com>
Date: Mon, 16 May 2022 22:10:27 +0200
Subject: [PATCH] tail: add check to detect a closed file descriptor

This is WIP or even WONT-FIX because there's a workaround in Rust's
stdlib which prevents us from detecting a closed FD.

see also the discussion at:
https://github.com/uutils/coreutils/issues/2873
---
 src/uu/tail/src/platform/mod.rs  |  4 +++-
 src/uu/tail/src/platform/unix.rs | 28 +++++++++++++++++++---------
 2 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/src/uu/tail/src/platform/mod.rs b/src/uu/tail/src/platform/mod.rs
index 7b7fd6fa3..f4cd6fb6c 100644
--- a/src/uu/tail/src/platform/mod.rs
+++ b/src/uu/tail/src/platform/mod.rs
@@ -9,7 +9,9 @@
  */
 
 #[cfg(unix)]
-pub use self::unix::{stdin_is_pipe_or_fifo, supports_pid_checks, Pid, ProcessChecker};
+pub use self::unix::{
+    stdin_is_bad_fd, stdin_is_pipe_or_fifo, supports_pid_checks, Pid, ProcessChecker,
+};
 
 #[cfg(windows)]
 pub use self::windows::{supports_pid_checks, Pid, ProcessChecker};
diff --git a/src/uu/tail/src/platform/unix.rs b/src/uu/tail/src/platform/unix.rs
index 7ddf6edd0..00d21a6ae 100644
--- a/src/uu/tail/src/platform/unix.rs
+++ b/src/uu/tail/src/platform/unix.rs
@@ -51,13 +51,23 @@ fn get_errno() -> i32 {
 
 pub fn stdin_is_pipe_or_fifo() -> bool {
     let fd = stdin().lock().as_raw_fd();
-    fd >= 0 // GNU tail checks fd >= 0
-                            && match fstat(fd) {
-                                Ok(stat) => {
-                                    let mode = stat.st_mode as libc::mode_t;
-                                    // NOTE: This is probably not the most correct way to check this
-                                    (mode & S_IFIFO != 0) || (mode & S_IFSOCK != 0)
-                                }
-                                Err(err) => panic!("{}", err),
-                            }
+    // GNU tail checks fd >= 0
+    fd >= 0
+        && match fstat(fd) {
+            Ok(stat) => {
+                let mode = stat.st_mode as libc::mode_t;
+                // NOTE: This is probably not the most correct way to check this
+                (mode & S_IFIFO != 0) || (mode & S_IFSOCK != 0)
+            }
+            Err(err) => panic!("{}", err),
+        }
+}
+
+// FIXME: Detect a closed file descriptor, e.g.: `tail <&-`
+pub fn stdin_is_bad_fd() -> bool {
+    let fd = stdin().as_raw_fd();
+    // this is never `true`, even with `<&-` because stdlib is reopening fds as /dev/null
+    // see also: https://github.com/uutils/coreutils/issues/2873
+    // (gnu/tests/tail-2/follow-stdin.sh fails because of this)
+    unsafe { libc::fcntl(fd, libc::F_GETFD) == -1 && get_errno() == libc::EBADF }
 }