Add support for following more than one file i.e.

tail -f file1 file2
This commit is contained in:
Paul Richards 2004-11-04 19:18:19 +00:00
parent dba13dd1d3
commit 15a55f7926
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=137225
3 changed files with 200 additions and 96 deletions

View File

@ -50,8 +50,17 @@ struct mapinfo {
int fd;
};
struct file_info {
FILE *fp;
char *file_name;
struct stat st;
};
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 *);
@ -63,5 +72,5 @@ void oerr(void);
int mapprint(struct mapinfo *, off_t, off_t);
int maparound(struct mapinfo *, off_t);
extern int Fflag, fflag, rflag, rval;
extern int Fflag, fflag, rflag, rval, no_files;
extern const char *fname;

View File

@ -66,6 +66,10 @@ static void rlines(FILE *, off_t, struct stat *);
#define USE_KQUEUE 1
#define ADD_EVENTS 2
struct kevent *ev;
int action = USE_SLEEP;
int kq;
/*
* forward -- display the file, from an offset, forward.
*
@ -172,95 +176,14 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
break;
}
if (fflag) {
kq = kqueue();
if (kq < 0)
err(1, "kqueue");
action = ADD_EVENTS;
}
for (;;) {
while ((ch = getc(fp)) != EOF)
if (putchar(ch) == EOF)
oerr();
if (ferror(fp)) {
ierr();
return;
}
(void)fflush(stdout);
if (! fflag)
break;
clearerr(fp);
switch (action) {
case ADD_EVENTS:
n = 0;
ts.tv_sec = 0;
ts.tv_nsec = 0;
if (Fflag && fileno(fp) != STDIN_FILENO) {
EV_SET(&ev[n], fileno(fp), EVFILT_VNODE,
EV_ADD | EV_ENABLE | EV_CLEAR,
NOTE_DELETE | NOTE_RENAME, 0, 0);
n++;
}
EV_SET(&ev[n], fileno(fp), EVFILT_READ,
EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0);
n++;
if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
action = USE_SLEEP;
} else {
action = USE_KQUEUE;
}
break;
case USE_KQUEUE:
ts.tv_sec = 1;
ts.tv_nsec = 0;
/*
* In the -F case we set a timeout to ensure that
* we re-stat the file at least once every second.
*/
n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL);
if (n < 0)
err(1, "kevent");
if (n == 0) {
/* timeout */
break;
} else if (ev->filter == EVFILT_READ && ev->data < 0) {
/* file shrank, reposition to end */
if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
ierr();
return;
}
}
break;
case USE_SLEEP:
(void) usleep(250000);
clearerr(fp);
break;
}
if (Fflag && fileno(fp) != STDIN_FILENO) {
while (stat(fname, &sb2) != 0)
/* file was rotated, wait until it reappears */
(void)sleep(1);
if (sb2.st_ino != sbp->st_ino ||
sb2.st_dev != sbp->st_dev ||
sb2.st_nlink == 0) {
fp = freopen(fname, "r", fp);
if (fp == NULL) {
ierr();
return;
} else {
*sbp = sb2;
action = ADD_EVENTS;
}
}
}
while ((ch = getc(fp)) != EOF)
if (putchar(ch) == EOF)
oerr();
if (ferror(fp)) {
ierr();
return;
}
(void)fflush(stdout);
}
/*
@ -316,3 +239,152 @@ rlines(fp, off, sbp)
return;
}
}
/*
* follow -- display the file, from an offset, forward.
*
*/
void
show(file_info_t *file)
{
int ch, first;
first = 1;
while ((ch = getc(file->fp)) != EOF) {
if (first && no_files > 1) {
(void)printf("\n==> %s <==\n", file->file_name);
first = 0;
}
if (putchar(ch) == EOF)
oerr();
}
(void)fflush(stdout);
if (ferror(file->fp)) {
file->fp = NULL;
ierr();
} else
clearerr(file->fp);
}
void
set_events(file_info_t *files)
{
int i, n = 0;
file_info_t *file;
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 0;
action = USE_KQUEUE;
for (i = 0, file = files; i < no_files; i++, file++) {
if (! file->fp)
continue;
if (Fflag && fileno(file->fp) != STDIN_FILENO) {
EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE,
EV_ADD | EV_ENABLE | EV_CLEAR,
NOTE_DELETE | NOTE_RENAME, 0, 0);
n++;
}
EV_SET(&ev[n], fileno(file->fp), EVFILT_READ,
EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0);
n++;
}
if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
action = USE_SLEEP;
}
}
void
follow(file_info_t *files, enum STYLE style, off_t off)
{
int active, i, n = -1;
struct stat sb2;
struct stat *sbp;
file_info_t *file;
long spin=1;
struct timespec ts;
/* Position each of the files */
file = files;
active = 0;
n = 0;
for (i = 0; i < no_files; i++, file++) {
if (file->fp) {
active = 1;
n++;
if (no_files > 1)
(void)printf("\n==> %s <==\n", file->file_name);
forward(file->fp, style, off, &file->st);
if (Fflag && fileno(file->fp) != STDIN_FILENO)
n++;
}
}
if (! active)
return;
kq = kqueue();
if (kq < 0)
err(1, "kqueue");
ev = malloc(n * sizeof(struct kevent));
if (! ev)
err(1, "Couldn't allocate memory for kevents.");
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) {
/* file was rotated, skip it until it reappears */
continue;
}
if (sb2.st_ino != file->st.st_ino ||
sb2.st_dev != file->st.st_dev ||
sb2.st_nlink == 0) {
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);
}
}
}
show(file);
}
switch (action) {
case USE_KQUEUE:
ts.tv_sec = 1;
ts.tv_nsec = 0;
/*
* In the -F case we set a timeout to ensure that
* we re-stat the file at least once every second.
*/
n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL);
if (n < 0)
err(1, "kevent");
if (n == 0) {
/* timeout */
break;
} else if (ev->filter == EVFILT_READ && ev->data < 0) {
/* file shrank, reposition to end */
if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) {
ierr();
continue;
}
}
break;
case USE_SLEEP:
(void) usleep(250000);
break;
}
}
}

View File

@ -60,9 +60,11 @@ static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
#include "extern.h"
int Fflag, fflag, rflag, rval;
int Fflag, fflag, rflag, rval, no_files;
const char *fname;
file_info_t *files;
static void obsolete(char **);
static void usage(void);
@ -73,7 +75,8 @@ main(int argc, char *argv[])
FILE *fp;
off_t off;
enum STYLE style;
int ch, first;
int i, ch, first;
file_info_t *file;
char *p;
/*
@ -138,8 +141,7 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
if (fflag && argc > 1)
errx(1, "-f option only appropriate for a single file");
no_files = argc ? argc : 1;
/*
* If displaying in reverse, don't permit follow option, and convert
@ -168,7 +170,29 @@ main(int argc, char *argv[])
}
}
if (*argv)
if (*argv && fflag) {
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);
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;
}
}
follow(files, style, off);
for (i = 0, file = files; i < no_files; i++, file++) {
free(file->file_name);
}
free(files);
} else if (*argv) {
for (first = 1; (fname = *argv++);) {
if ((fp = fopen(fname, "r")) == NULL ||
fstat(fileno(fp), &sb)) {
@ -186,9 +210,8 @@ main(int argc, char *argv[])
reverse(fp, style, off, &sb);
else
forward(fp, style, off, &sb);
(void)fclose(fp);
}
else {
} else {
fname = "stdin";
if (fstat(fileno(stdin), &sb)) {