Change the behaviour of -F slightly; it now persists (forever) in

trying to open files rather than giving up when it encounters an
error.  ENOENT errors are not reported.

As a result, files that are moved away then recreated are not at
risk of being 'lost' to tail.  Files that are recreated and
temporarily have unreadable permissions will be shown when they
are fixed.

This behaviour is consistent with the GNU version of tail but
without the verbiage that goes with the GNU version.

This change also fixes error messages accompanying -f and -F.
They no longer report problems with (null)!

MFC after:	3 weeks
This commit is contained in:
Brian Somers 2009-06-05 09:08:53 +00:00
parent ca67260eba
commit 22da50cfc8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=193488
7 changed files with 144 additions and 107 deletions

View File

@ -61,16 +61,15 @@ typedef struct file_info file_info_t;
enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE };
void follow(file_info_t *, enum STYLE, off_t);
void forward(FILE *, enum STYLE, off_t, struct stat *);
void reverse(FILE *, enum STYLE, off_t, struct stat *);
void forward(FILE *, const char *, enum STYLE, off_t, struct stat *);
void reverse(FILE *, const char *, enum STYLE, off_t, struct stat *);
int bytes(FILE *, off_t);
int lines(FILE *, off_t);
int bytes(FILE *, const char *, off_t);
int lines(FILE *, const char *, off_t);
void ierr(void);
void ierr(const char *);
void oerr(void);
int mapprint(struct mapinfo *, off_t, off_t);
int maparound(struct mapinfo *, off_t);
extern int Fflag, fflag, qflag, rflag, rval, no_files;
extern const char *fname;

View File

@ -61,8 +61,8 @@ static const char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93";
#include "extern.h"
static void rlines(FILE *, off_t, struct stat *);
static void show(file_info_t *);
static void rlines(FILE *, const char *fn, off_t, struct stat *);
static int show(file_info_t *);
static void set_events(file_info_t *files);
/* defines for inner loop actions */
@ -99,7 +99,7 @@ static const file_info_t *last;
* NOREG cyclically read lines into a wrap-around array of buffers
*/
void
forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
{
int ch;
@ -111,13 +111,13 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
if (sbp->st_size < off)
off = sbp->st_size;
if (fseeko(fp, off, SEEK_SET) == -1) {
ierr();
ierr(fn);
return;
}
} else while (off--)
if ((ch = getc(fp)) == EOF) {
if (ferror(fp)) {
ierr();
ierr(fn);
return;
}
break;
@ -129,7 +129,7 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
for (;;) {
if ((ch = getc(fp)) == EOF) {
if (ferror(fp)) {
ierr();
ierr(fn);
return;
}
break;
@ -142,36 +142,36 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
if (S_ISREG(sbp->st_mode)) {
if (sbp->st_size >= off &&
fseeko(fp, -off, SEEK_END) == -1) {
ierr();
ierr(fn);
return;
}
} else if (off == 0) {
while (getc(fp) != EOF);
if (ferror(fp)) {
ierr();
ierr(fn);
return;
}
} else
if (bytes(fp, off))
if (bytes(fp, fn, off))
return;
break;
case RLINES:
if (S_ISREG(sbp->st_mode))
if (!off) {
if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
ierr();
ierr(fn);
return;
}
} else
rlines(fp, off, sbp);
rlines(fp, fn, off, sbp);
else if (off == 0) {
while (getc(fp) != EOF);
if (ferror(fp)) {
ierr();
ierr(fn);
return;
}
} else
if (lines(fp, off))
if (lines(fp, fn, off))
return;
break;
default:
@ -182,7 +182,7 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
if (putchar(ch) == EOF)
oerr();
if (ferror(fp)) {
ierr();
ierr(fn);
return;
}
(void)fflush(stdout);
@ -192,10 +192,7 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
* rlines -- display the last offset lines of the file.
*/
static void
rlines(fp, off, sbp)
FILE *fp;
off_t off;
struct stat *sbp;
rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
{
struct mapinfo map;
off_t curoff, size;
@ -214,7 +211,7 @@ rlines(fp, off, sbp)
curoff = size - 2;
while (curoff >= 0) {
if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
ierr();
ierr(fn);
return;
}
for (i = curoff - map.mapoff; i >= 0; i--)
@ -227,41 +224,44 @@ rlines(fp, off, sbp)
}
curoff++;
if (mapprint(&map, curoff, size - curoff) != 0) {
ierr();
ierr(fn);
exit(1);
}
/* Set the file pointer to reflect the length displayed. */
if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
ierr();
ierr(fn);
return;
}
if (map.start != NULL && munmap(map.start, map.maplen)) {
ierr();
ierr(fn);
return;
}
}
static void
static int
show(file_info_t *file)
{
int ch;
int ch;
while ((ch = getc(file->fp)) != EOF) {
if (last != file && no_files > 1) {
if (!qflag)
(void)printf("\n==> %s <==\n", file->file_name);
last = file;
while ((ch = getc(file->fp)) != EOF) {
if (last != file && no_files > 1) {
if (!qflag)
(void)printf("\n==> %s <==\n", file->file_name);
last = file;
}
if (putchar(ch) == EOF)
oerr();
}
if (putchar(ch) == EOF)
oerr();
}
(void)fflush(stdout);
if (ferror(file->fp)) {
file->fp = NULL;
ierr();
} else
clearerr(file->fp);
(void)fflush(stdout);
if (ferror(file->fp)) {
fclose(file->fp);
file->fp = NULL;
ierr(file->file_name);
return 0;
}
clearerr(file->fp);
return 1;
}
static void
@ -309,7 +309,7 @@ set_events(file_info_t *files)
void
follow(file_info_t *files, enum STYLE style, off_t off)
{
int active, i, n = -1;
int active, ev_change, i, n = -1;
struct stat sb2;
file_info_t *file;
struct timespec ts;
@ -325,12 +325,12 @@ follow(file_info_t *files, enum STYLE style, off_t off)
n++;
if (no_files > 1 && !qflag)
(void)printf("\n==> %s <==\n", file->file_name);
forward(file->fp, style, off, &file->st);
forward(file->fp, file->file_name, style, off, &file->st);
if (Fflag && fileno(file->fp) != STDIN_FILENO)
n++;
n++;
}
}
if (! active)
if (!Fflag && !active)
return;
last = --file;
@ -344,28 +344,56 @@ follow(file_info_t *files, enum STYLE style, off_t off)
set_events(files);
for (;;) {
for (i = 0, file = files; i < no_files; i++, file++) {
if (! file->fp)
continue;
if (Fflag && file->fp && fileno(file->fp) != STDIN_FILENO) {
if (stat(file->file_name, &sb2) == 0 &&
(sb2.st_ino != file->st.st_ino ||
sb2.st_dev != file->st.st_dev ||
sb2.st_nlink == 0)) {
show(file);
file->fp = freopen(file->file_name, "r", file->fp);
if (file->fp == NULL) {
ierr();
continue;
} else {
memcpy(&file->st, &sb2, sizeof(struct stat));
set_events(files);
ev_change = 0;
if (Fflag) {
for (i = 0, file = files; i < no_files; i++, file++) {
if (!file->fp) {
file->fp = fopen(file->file_name, "r");
if (file->fp != NULL &&
fstat(fileno(file->fp), &file->st)
== -1) {
fclose(file->fp);
file->fp = NULL;
}
if (file->fp != NULL)
ev_change++;
continue;
}
if (fileno(file->fp) == STDIN_FILENO)
continue;
if (stat(file->file_name, &sb2) == -1) {
if (errno != ENOENT)
ierr(file->file_name);
show(file);
fclose(file->fp);
file->fp = NULL;
ev_change++;
continue;
}
if (sb2.st_ino != file->st.st_ino ||
sb2.st_dev != file->st.st_dev ||
sb2.st_nlink == 0) {
show(file);
file->fp = freopen(file->file_name, "r",
file->fp);
if (file->fp != NULL)
memcpy(&file->st, &sb2,
sizeof(struct stat));
else if (errno != ENOENT)
ierr(file->file_name);
ev_change++;
}
}
show(file);
}
for (i = 0, file = files; i < no_files; i++, file++)
if (file->fp && !show(file))
ev_change++;
if (ev_change)
set_events(files);
switch (action) {
case USE_KQUEUE:
ts.tv_sec = 1;
@ -381,9 +409,9 @@ follow(file_info_t *files, enum STYLE style, off_t off)
/* timeout */
break;
} else if (ev->filter == EVFILT_READ && ev->data < 0) {
/* file shrank, reposition to end */
/* file shrank, reposition to end */
if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) {
ierr();
ierr(file->file_name);
continue;
}
}

View File

@ -56,7 +56,7 @@ static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
#include "extern.h"
void
ierr()
ierr(const char *fname)
{
warn("%s", fname);
rval = 1;

View File

@ -66,7 +66,7 @@ static const char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/6/93";
* the end.
*/
int
bytes(FILE *fp, off_t off)
bytes(FILE *fp, const char *fn, off_t off)
{
int ch, len, tlen;
char *ep, *p, *t;
@ -84,7 +84,7 @@ bytes(FILE *fp, off_t off)
}
}
if (ferror(fp)) {
ierr();
ierr(fn);
free(sp);
return 1;
}
@ -136,7 +136,7 @@ bytes(FILE *fp, off_t off)
* the end.
*/
int
lines(FILE *fp, off_t off)
lines(FILE *fp, const char *fn, off_t off)
{
struct {
int blen;
@ -178,7 +178,7 @@ lines(FILE *fp, off_t off)
}
}
if (ferror(fp)) {
ierr();
ierr(fn);
rc = 1;
goto done;
}

View File

@ -58,8 +58,8 @@ __FBSDID("$FreeBSD$");
#include "extern.h"
static void r_buf(FILE *);
static void r_reg(FILE *, enum STYLE, off_t, struct stat *);
static void r_buf(FILE *, const char *);
static void r_reg(FILE *, const char *, enum STYLE, off_t, struct stat *);
/*
* reverse -- display input in reverse order by line.
@ -80,25 +80,25 @@ static void r_reg(FILE *, enum STYLE, off_t, struct stat *);
* NOREG cyclically read input into a linked list of buffers
*/
void
reverse(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
reverse(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
{
if (style != REVERSE && off == 0)
return;
if (S_ISREG(sbp->st_mode))
r_reg(fp, style, off, sbp);
r_reg(fp, fn, style, off, sbp);
else
switch(style) {
case FBYTES:
case RBYTES:
bytes(fp, off);
bytes(fp, fn, off);
break;
case FLINES:
case RLINES:
lines(fp, off);
lines(fp, fn, off);
break;
case REVERSE:
r_buf(fp);
r_buf(fp, fn);
break;
default:
break;
@ -109,7 +109,7 @@ reverse(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
* r_reg -- display a regular file in reverse order by line.
*/
static void
r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
r_reg(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
{
struct mapinfo map;
off_t curoff, size, lineend;
@ -132,7 +132,7 @@ r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
if (curoff < map.mapoff ||
curoff >= map.mapoff + (off_t)map.maplen) {
if (maparound(&map, curoff) != 0) {
ierr();
ierr(fn);
return;
}
}
@ -149,7 +149,7 @@ r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
/* Print the line and update offsets. */
if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) {
ierr();
ierr(fn);
return;
}
lineend = curoff + 1;
@ -165,11 +165,11 @@ r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
}
}
if (curoff < 0 && mapprint(&map, 0, lineend) != 0) {
ierr();
ierr(fn);
return;
}
if (map.start != NULL && munmap(map.start, map.maplen))
ierr();
ierr(fn);
}
typedef struct bf {
@ -190,7 +190,7 @@ typedef struct bf {
* user warned).
*/
static void
r_buf(FILE *fp)
r_buf(FILE *fp, const char *fn)
{
BF *mark, *tl, *tr;
int ch, len, llen;
@ -227,7 +227,7 @@ r_buf(FILE *fp)
*p++ = ch;
if (ferror(fp)) {
ierr();
ierr(fn);
return;
}

View File

@ -35,7 +35,7 @@
.\" @(#)tail.1 8.1 (Berkeley) 6/6/93
.\" $FreeBSD$
.\"
.Dd June 29, 2006
.Dd June 05, 2009
.Dt TAIL 1
.Os
.Sh NAME
@ -106,9 +106,16 @@ will also check to see if the file being followed has been renamed or rotated.
The file is closed and reopened when
.Nm
detects that the filename being read from has a new inode number.
.Pp
If the file being followed does not (yet) exist or if it is removed, tail
will keep looking and will display the file from the beginning if and when
it is created.
.Pp
The
.Fl F
option is ignored if reading from standard input rather than a file.
option is the same as the
.Fl f
option if reading from standard input rather than a file.
.It Fl n Ar number
The location is
.Ar number

View File

@ -61,7 +61,6 @@ static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
#include "extern.h"
int Fflag, fflag, qflag, rflag, rval, no_files;
const char *fname;
file_info_t *files;
@ -72,6 +71,7 @@ int
main(int argc, char *argv[])
{
struct stat sb;
const char *fn;
FILE *fp;
off_t off;
enum STYLE style;
@ -175,20 +175,23 @@ main(int argc, char *argv[])
}
if (*argv && fflag) {
files = (struct file_info *) malloc(no_files * sizeof(struct file_info));
if (! files)
files = (struct file_info *) malloc(no_files *
sizeof(struct file_info));
if (!files)
err(1, "Couldn't malloc space for file descriptors.");
for (file = files; (fname = *argv++); file++) {
file->file_name = malloc(strlen(fname)+1);
for (file = files; (fn = *argv++); file++) {
file->file_name = strdup(fn);
if (! file->file_name)
errx(1, "Couldn't malloc space for file name.");
strncpy(file->file_name, fname, strlen(fname)+1);
if ((file->fp = fopen(file->file_name, "r")) == NULL ||
fstat(fileno(file->fp), &file->st)) {
file->fp = NULL;
ierr();
continue;
if (file->fp != NULL) {
fclose(file->fp);
file->fp = NULL;
}
if (!Fflag || errno != ENOENT)
ierr(file->file_name);
}
}
follow(files, style, off);
@ -197,29 +200,29 @@ main(int argc, char *argv[])
}
free(files);
} else if (*argv) {
for (first = 1; (fname = *argv++);) {
if ((fp = fopen(fname, "r")) == NULL ||
for (first = 1; (fn = *argv++);) {
if ((fp = fopen(fn, "r")) == NULL ||
fstat(fileno(fp), &sb)) {
ierr();
ierr(fn);
continue;
}
if (argc > 1 && !qflag) {
(void)printf("%s==> %s <==\n",
first ? "" : "\n", fname);
first ? "" : "\n", fn);
first = 0;
(void)fflush(stdout);
}
if (rflag)
reverse(fp, style, off, &sb);
reverse(fp, fn, style, off, &sb);
else
forward(fp, style, off, &sb);
forward(fp, fn, style, off, &sb);
}
} else {
fname = "stdin";
fn = "stdin";
if (fstat(fileno(stdin), &sb)) {
ierr();
ierr(fn);
exit(1);
}
@ -234,9 +237,9 @@ main(int argc, char *argv[])
}
if (rflag)
reverse(stdin, style, off, &sb);
reverse(stdin, fn, style, off, &sb);
else
forward(stdin, style, off, &sb);
forward(stdin, fn, style, off, &sb);
}
exit(rval);
}