Bug 203662

Summary: tail -F <file> misbehaves with stdin closed
Product: Base System Reporter: Timo Buhrmester <fstd.lkml>
Component: binAssignee: Fernando ApesteguĂ­a <fernape>
Status: Closed FIXED    
Severity: Affects Some People CC: fernape
Priority: --- Keywords: patch
Version: 10.1-RELEASE   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
tail: Don't assume fd 0 is standard input, because it might not be. Use the `stdin`FILE pointer instead. none

Description Timo Buhrmester 2015-10-09 18:35:33 UTC
Created attachment 161859 [details]
tail: Don't assume fd 0 is standard input, because it might not be. Use the `stdin`FILE pointer instead.

I have fixed a bug in NetBSD's tail(1), it turns out FreeBSD also has it.
Below is the original Problem Report incl. steps to reproduce; attached is a patch adapted to FreeBSD's tail.


[Quoting from http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=50322]

>Description:
The point of tail -F /some/file is to follow the contents in /some/file, even if /some/file is occasionally replaced with a different file, e.g. due to newsyslog(8) running.  In this mode of operation, standard input is ignored, as it should be.

Now, if standard input happens to be closed, for example by running
tail -F /some/file <&-
or by it being used inside a script that has its standard input closed (possibly as a result of daemonizing the script), tail's attempt to fopen(3) /some/file will open the file at fd 0 - the typical fd for stdin.

However, later on, tail effectively uses fd == 0 to determine whether it is reading from stdin or not.  If /some/file was opened at fd 0, tail will hence consider itself to be reading from stdin and omit adding the vnode filters to kqueue/kevent.

The result is that it will not follow the file after newsyslog(8) rotated the file away; it will instead forever be stuck in kevent(2).

I've been hit by this for a long time, only yesterday managed to finally find this (Heisen)bug.

The root cause of the problem is that tail assumes fd 0 is standard input.  It does this in two places by comparing `fileno(fp)` to `STDIN_FILENO`.  (`fp` being a FILE * as returned by fopen(3) or freopen(3)).
It should instead compare `fp` to `stdin`.

>How-To-Repeat:
Terminal 1:
$ touch /tmp/foo
$ tail -F /tmp/foo <&-

Terminal 2:
$ echo foo >>/tmp/foo  # Outputs 'foo' on Terminal 1
$ rm /tmp/foo
$ echo bar >>/tmp/foo  # SHOULD output 'bar' on Terminal 1, but does not.


Cheers,
Timo Buhrmester
Comment 1 Jilles Tjoelker freebsd_committer freebsd_triage 2015-10-11 13:13:02 UTC
The patch does not look wrong, but I doubt we want to support running standard utilities with fd 0, 1 or 2 closed. For daemonizing, the file descriptors should be reopened from/to /dev/null, not closed.
Comment 2 Fernando ApesteguĂ­a freebsd_committer freebsd_triage 2023-02-19 18:40:48 UTC
Seems to work fine now:

Terminal 1:
$ sudo tail -F /tmp/foo <&-
Password:
foo
bar

Terminal 2
$ sudo echo foo >>/tmp/foo
Password:
$ sudo rm /tmp/foo
$ sudo echo bar >>/tmp/foo

Please reopen if I misunderstood the test case.