bin/101979: tail -F fails to print remainder of old file

Jos Backus jos at catnook.com
Sun Aug 13 23:30:23 UTC 2006


>Number:         101979
>Category:       bin
>Synopsis:       tail -F fails to print remainder of old file
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Aug 13 23:30:16 GMT 2006
>Closed-Date:
>Last-Modified:
>Originator:     Jos Backus
>Release:        FreeBSD 7.0-CURRENT i386
>Organization:
CatNook
>Environment:
System: FreeBSD lizzy.catnook.local 7.0-CURRENT FreeBSD 7.0-CURRENT #27: Fri Aug 11 20:47:50 PDT 2006 root at lizzy.catnook.local:/usr/obj/usr/src/sys/LIZZY i386


>Description:
	
	I am trying to use tail -F to follow a file that is rotated
	occasionally. It appears that when tail -F notices that the dev/ino
	combo associated with the filename has changed, it does not attempt to
	print the remainder of the file before opening the new one. The patch
	below attempts to address this by adding a -d (drain) option to tail
	that works in conjunction with -F. When specified, tail makes one last
	attempt to show the remainder of the file. This drastically reduces
	(but does not eliminate completely) the number of lines skipped.
	
	I have verified this using a testbed that employs djb's multilog,
	tail'ing the `current' file it employs.  When the `current' is deemed
	full, multilog fsync's and close's the file, then renames it to
	`previous' before reopening the file named 'current' that will then
	receive new input. The fsync/close should guarantee that all lines hit
	the fisk and thus are visible to the tail -F. Yet while the patch
	below drastically reduces the skippage, it does not eleminate it
	completely meaning there is still some race condition present
	somewhere.

	Details of the test setup (using sysutils/daemontools):

	lizzy:/var/service# cat blabber/run
	#!/usr/bin/perl -w

	use strict;

	my $counter  = 0;
	my $lastTime = 0;

	while (1) {
	  print $counter++, ' - ', time(), ' - ',
            substr(rand(1000) + 1000, 0, 4), $/;
	}

	lizzy:/var/service# cat blabber/log/run    
	#!/bin/sh

	exec multilog t n100 s65536 ./main 

	lizzy:/var/service# cat tail-blabber/run   
	#!/bin/sh

	/usr/bin/tail -F /service/blabber/log/main/current

	lizzy:/var/service# cat tail-blabber/log/run 
	#!/bin/sh

	exec ./bin/checktail > blabber.log

	lizzy:/var/service# cat tail-blabber/log/bin/checktail 
	#!/usr/bin/perl -w

	use strict;

	my $counter     = undef;
	my $lastCounter = undef;

	$| = 1;
	while (<>) {
	  print, next if /^XXX/;

	  #@4000000044dd1b7007f88124 5814025 - 1155341158 - 1529
	  if (/^@/) {
	    my ($counter) = (split / /)[1];

	    if (defined($lastCounter)) {
	      my $diff = $counter - 1 - $lastCounter;
	      if ($diff != 0) {
		  print "Found $lastCounter != ($counter - 1) (diff = $diff)\n";
	      }
	    }
	    $lastCounter = $counter;
	  }
	}
	lizzy:/var/service#

	The blabber script generates output; the checktail scripts verifies
	that there are no gaps in the output by making sure the 2nd column
	increases monotonically, and logging info when a gap is seen.

>How-To-Repeat:
	Set up the above two services (blabber and tail-blabber) and watch
	tail-blabber/log/blabber.log grow.

>Fix:

	Partial fix:

--- extern.h.orig	Sat Aug 12 08:29:54 2006
+++ extern.h	Fri Aug 11 23:43:16 2006
@@ -72,5 +72,5 @@
 int mapprint(struct mapinfo *, off_t, off_t);
 int maparound(struct mapinfo *, off_t);
 
-extern int Fflag, fflag, qflag, rflag, rval, no_files;
+extern int dflag, Fflag, fflag, qflag, rflag, rval, no_files;
 extern const char *fname;
--- forward.c.orig	Fri Aug 11 21:12:37 2006
+++ forward.c	Fri Aug 11 23:43:35 2006
@@ -355,6 +355,8 @@
 				if (sb2.st_ino != file->st.st_ino ||
 				    sb2.st_dev != file->st.st_dev ||
 				    sb2.st_nlink == 0) {
+					if (dflag)
+						show(file);
 					file->fp = freopen(file->file_name, "r", file->fp);
 					if (file->fp == NULL) {
 						ierr();
--- tail.c.orig	Fri Aug 11 23:44:49 2006
+++ tail.c	Fri Aug 11 23:44:07 2006
@@ -60,7 +60,7 @@
 
 #include "extern.h"
 
-int Fflag, fflag, qflag, rflag, rval, no_files;
+int dflag, Fflag, fflag, qflag, rflag, rval, no_files;
 const char *fname;
 
 file_info_t *files;
@@ -125,6 +125,9 @@
 		case 'c':
 			ARG(1, FBYTES, RBYTES);
 			break;
+		case 'd':
+			dflag = 1;
+			break;
 		case 'f':
 			fflag = 1;
 			break;
@@ -332,7 +335,7 @@
 usage(void)
 {
 	(void)fprintf(stderr,
-	    "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]"
+	    "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #] [-d]"
 	    " [file ...]\n");
 	exit(1);
 }
--- tail.1.orig	Sat Aug 12 08:31:49 2006
+++ tail.1	Sat Aug 12 08:34:54 2006
@@ -43,6 +43,7 @@
 .Nd display the last part of a file
 .Sh SYNOPSIS
 .Nm
+.Op Fl d
 .Oo
 .Fl F |
 .Fl f |
@@ -87,6 +88,15 @@
 The location is
 .Ar number
 bytes.
+.It Fl d
+The
+.Fl d
+option causes
+.Nm
+to drain and show the old file before starting to follow the new file when the
+old file has been renamed or rotated. This can reduce the amount of lines that
+.Nm
+misses reading from the old file before switching to the new file.
 .It Fl f
 The
 .Fl f


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list