| Summary: | /usr/bin/tail -F in 4.1+ doesn't work if file inode changes (works in 4.0) | ||||||
|---|---|---|---|---|---|---|---|
| Product: | Base System | Reporter: | kmarx <kmarx> | ||||
| Component: | bin | Assignee: | freebsd-bugs (Nobody) <bugs> | ||||
| Status: | Closed FIXED | ||||||
| Severity: | Affects Only Me | ||||||
| Priority: | Normal | ||||||
| Version: | 4.2-RELEASE | ||||||
| Hardware: | Any | ||||||
| OS: | Any | ||||||
| Attachments: |
|
||||||
Hello,
Try this patch:
Index: forward.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/tail/forward.c,v
retrieving revision 1.27
diff -u -r1.27 forward.c
--- forward.c 1 Sep 2001 22:22:44 -0000 1.27
+++ forward.c 2 Nov 2001 21:46:27 -0000
@@ -94,10 +94,10 @@
off_t off;
struct stat *sbp;
{
- int ch, kq = -1;
+ int ch, dirfd, kq = -1;
int action = USE_SLEEP;
- struct kevent ev[2];
- struct stat sb2;
+ struct kevent ev[3];
+ struct stat sb2, sbl;
switch(style) {
case FBYTES:
@@ -179,6 +179,15 @@
action = ADD_EVENTS;
}
+ if (lstat(fname, &sbl) != -1) {
+ if ((sbl.st_mode & S_IFLNK) == S_IFLNK) {
+ dirfd = open((const char *)dirname(fname), O_RDONLY);
+ if (dirfd == -1)
+ err(1, "open");
+ }
+ } else
+ err(1, "lstat");
+
for (;;) {
while ((ch = getc(fp)) != EOF)
if (putchar(ch) == EOF)
@@ -202,6 +211,13 @@
EV_ADD | EV_ENABLE | EV_CLEAR,
NOTE_DELETE | NOTE_RENAME, 0, 0);
n++;
+
+ if ((sbl.st_mode & S_IFLNK) == S_IFLNK) {
+ EV_SET(&ev[n], dirfd, EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_CLEAR,
+ NOTE_WRITE, 0, 0);
+ n++;
+ }
}
EV_SET(&ev[n], fileno(fp), EVFILT_READ,
EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0);
--
Maxim Konovalov, MAcomnet, Internet-Intranet Dept., system engineer
phone: +7 (095) 796-9079, mailto: maxim@macomnet.ru
In message <200111030850.fA38o2f55553@freefall.freebsd.org>, Maxim Konovalov wr ites: > Hello, > > Try this patch: Hi, While this patch should address the particular case mentioned in this PR (tail is pointed at a symlink, but the symlink changes), it doesn't solve the general problem of any other component in the specified path changing. Maybe it is best to just resort to a polling approach when the '-F' flag is specified? It could still use kqueue, but just re-stat the path every ~1-10 seconds to check if the inode changed. Ian Hello, On Sun, 18 Nov 2001, Ian Dowse wrote: [...] > Hi, > > While this patch should address the particular case mentioned in this > PR (tail is pointed at a symlink, but the symlink changes), it doesn't > solve the general problem of any other component in the specified > path changing. Maybe it is best to just resort to a polling approach > when the '-F' flag is specified? It could still use kqueue, but just > re-stat the path every ~1-10 seconds to check if the inode changed. What about this one: Index: forward.c =================================================================== RCS file: /home/ncvs/src/usr.bin/tail/forward.c,v retrieving revision 1.27 diff -u -r1.27 forward.c --- forward.c 1 Sep 2001 22:22:44 -0000 1.27 +++ forward.c 19 Nov 2001 18:31:49 -0000 @@ -180,6 +180,9 @@ } for (;;) { + struct timespec ts; + int n; + while ((ch = getc(fp)) != EOF) if (putchar(ch) == EOF) oerr(); @@ -194,8 +197,9 @@ switch (action) { case ADD_EVENTS: { - int n = 0; - struct timespec ts = { 0, 0 }; + n = 0; + ts.tv_sec = 0; + ts.tv_nsec = 0; if (Fflag && fileno(fp) != STDIN_FILENO) { EV_SET(&ev[n], fileno(fp), EVFILT_VNODE, @@ -218,10 +222,16 @@ } case USE_KQUEUE: - if (kevent(kq, NULL, 0, ev, 1, NULL) < 0) + ts.tv_sec = 0; + ts.tv_nsec = 250000; + + if ((n = kevent(kq, NULL, 0, ev, 1, &ts)) < 0) err(1, "kevent"); - if (ev->filter == EVFILT_VNODE) { + if (n == 0) { + /* timeout */ + action = USE_SLEEP; + } else if (ev->filter == EVFILT_VNODE) { /* file was rotated, wait until it reappears */ action = USE_SLEEP; } else if (ev->data < 0) { @@ -234,7 +244,7 @@ break; case USE_SLEEP: - (void) usleep(250000); + (void)usleep(250000); clearerr(fp); if (Fflag && fileno(fp) != STDIN_FILENO && > Ian - -maxim -- Maxim Konovalov, MAcomnet, Internet-Intranet Dept., system engineer phone: +7 (095) 796-9079, mailto: maxim@macomnet.ru In message <20011119214750.L60209-100000@news1.macomnet.ru>, Maxim Konovalov wr ites: > >What about this one: Looks better, but unless I'm mistaken it introduces 250ms polling even when -F is not specified; this would undo the benefit of using kqueue in the first place. How about something like the modified version below? It only polls in the -F case (with a longer 1s interval), and it separates the choice of sleep vs kqueue from the stat calls necessary for -F. We always use kqueue if supported, but in the -F case, the kqueue timeout is set to 1 second. By keeping `kq' open, this patch also allows switching back to kqueue, for example if a symlink is changed so that the path moves from an NFS to a UFS filesystem. Note that the EVFILT_VNODE filter could probably be removed, since it is no longer needed to notice when the file is renamed or deleted. Ian Index: forward.c =================================================================== RCS file: /dump/FreeBSD-CVS/src/usr.bin/tail/forward.c,v retrieving revision 1.27 diff -u -r1.27 forward.c --- forward.c 1 Sep 2001 22:22:44 -0000 1.27 +++ forward.c 19 Nov 2001 22:34:00 -0000 @@ -94,10 +94,11 @@ off_t off; struct stat *sbp; { - int ch, kq = -1; + int ch, n, kq = -1; int action = USE_SLEEP; struct kevent ev[2]; struct stat sb2; + struct timespec ts; switch(style) { case FBYTES: @@ -193,9 +194,10 @@ clearerr(fp); switch (action) { - case ADD_EVENTS: { - int n = 0; - struct timespec ts = { 0, 0 }; + 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, @@ -208,24 +210,27 @@ n++; if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { - close(kq); - kq = -1; action = USE_SLEEP; } else { action = USE_KQUEUE; } break; - } case USE_KQUEUE: - if (kevent(kq, NULL, 0, ev, 1, NULL) < 0) + 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 (ev->filter == EVFILT_VNODE) { - /* file was rotated, wait until it reappears */ - action = USE_SLEEP; - } else if (ev->data < 0) { - /* file shrank, reposition to end */ + 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; @@ -236,9 +241,11 @@ case USE_SLEEP: (void) usleep(250000); clearerr(fp); + break; + } - if (Fflag && fileno(fp) != STDIN_FILENO && - stat(fname, &sb2) != -1) { + if (Fflag && fileno(fp) != STDIN_FILENO) { + if (stat(fname, &sb2) != -1) { if (sb2.st_ino != sbp->st_ino || sb2.st_dev != sbp->st_dev || sb2.st_rdev != sbp->st_rdev || @@ -246,16 +253,15 @@ fp = freopen(fname, "r", fp); if (fp == NULL) { ierr(); - break; - } - *sbp = sb2; - if (kq != -1) + } else { + *sbp = sb2; action = ADD_EVENTS; - } else if (kq != -1) { - action = USE_KQUEUE; + } } + } else { + /* file was rotated, wait until it reappears */ + action = USE_SLEEP; } - break; } } } Hello Ian, On Mon, 19 Nov 2001, Ian Dowse wrote: > In message <20011119214750.L60209-100000@news1.macomnet.ru>, Maxim Konovalov wr > ites: > > > >What about this one: > > Looks better, but unless I'm mistaken it introduces 250ms polling > even when -F is not specified; this would undo the benefit of using > kqueue in the first place. > > How about something like the modified version below? It only polls > in the -F case (with a longer 1s interval), and it separates the > choice of sleep vs kqueue from the stat calls necessary for -F. > We always use kqueue if supported, but in the -F case, the kqueue > timeout is set to 1 second. By keeping `kq' open, this patch also > allows switching back to kqueue, for example if a symlink is changed > so that the path moves from an NFS to a UFS filesystem. > > Note that the EVFILT_VNODE filter could probably be removed, since > it is no longer needed to notice when the file is renamed or deleted. Looks OK for me except one minor problem, see below > Ian > > Index: forward.c > =================================================================== > RCS file: /dump/FreeBSD-CVS/src/usr.bin/tail/forward.c,v > retrieving revision 1.27 > diff -u -r1.27 forward.c > --- forward.c 1 Sep 2001 22:22:44 -0000 1.27 > +++ forward.c 19 Nov 2001 22:34:00 -0000 > @@ -94,10 +94,11 @@ > off_t off; > struct stat *sbp; > { > - int ch, kq = -1; > + int ch, n, kq = -1; > int action = USE_SLEEP; > struct kevent ev[2]; > struct stat sb2; > + struct timespec ts; > > switch(style) { > case FBYTES: > @@ -193,9 +194,10 @@ > clearerr(fp); > > switch (action) { > - case ADD_EVENTS: { > - int n = 0; > - struct timespec ts = { 0, 0 }; > + 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, > @@ -208,24 +210,27 @@ > n++; > > if (kevent(kq, ev, n, NULL, 0, &ts) < 0) { > - close(kq); > - kq = -1; > action = USE_SLEEP; > } else { > action = USE_KQUEUE; > } > break; > - } > > case USE_KQUEUE: > - if (kevent(kq, NULL, 0, ev, 1, NULL) < 0) > + 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 (ev->filter == EVFILT_VNODE) { > - /* file was rotated, wait until it reappears */ > - action = USE_SLEEP; > - } else if (ev->data < 0) { > - /* file shrank, reposition to end */ > + 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; > @@ -236,9 +241,11 @@ > case USE_SLEEP: > (void) usleep(250000); > clearerr(fp); + if (kq != -1) + action = ADD_EVENTS; It will help to break USE_SLEEP cycle when you remove symlink to fname and recreate it again. Of course it will break automatically when new data arrives but it may never happen. > + break; > + } > > - if (Fflag && fileno(fp) != STDIN_FILENO && > - stat(fname, &sb2) != -1) { > + if (Fflag && fileno(fp) != STDIN_FILENO) { > + if (stat(fname, &sb2) != -1) { > if (sb2.st_ino != sbp->st_ino || > sb2.st_dev != sbp->st_dev || > sb2.st_rdev != sbp->st_rdev || > @@ -246,16 +253,15 @@ > fp = freopen(fname, "r", fp); > if (fp == NULL) { > ierr(); > - break; > - } > - *sbp = sb2; > - if (kq != -1) > + } else { > + *sbp = sb2; > action = ADD_EVENTS; > - } else if (kq != -1) { > - action = USE_KQUEUE; > + } > } > + } else { > + /* file was rotated, wait until it reappears */ > + action = USE_SLEEP; > } > - break; > } > } > } > > There is still an issue when fname is renamed. Our tail will continue read data from the renamed file in USE_SLEEP cycle until a new fname appear. The old tail (RELENG_4_0_0_RELEASE f.e.) works in the same way. Imho it is not a correct behaviour but I am not quite sure. -- Maxim Konovalov, MAcomnet, Internet-Intranet Dept., system engineer phone: +7 (095) 796-9079, mailto: maxim@macomnet.ru In message <20011122002632.O38176-100000@news1.macomnet.ru>, Maxim Konovalov wr ites: > >Looks OK for me except one minor problem, see below Yes, I expect there are still a few rough edges :-) >> case USE_SLEEP: >> (void) usleep(250000); >> clearerr(fp); >+ if (kq != -1) >+ action = ADD_EVENTS; > >It will help to break USE_SLEEP cycle when you remove symlink to fname >and recreate it again. Of course it will break automatically when new >data arrives but it may never happen. True, I think I noticed this problem also. I originally had it continuing to use kqueue while it was waiting for the file to reappear (i.e. there was no "else action = USE_SLEEP" for the failed stat case), but I was worried that in some cases the kevent() loop might spin where the file had really gone away (e.g NFS, forcibly unmounted filesystem etc). I guess another option would be to add a while loop around the stat() containing a sleep(), e.g if (Fflag && ...) { while (stat(...) != 0) sleep(1); if (sb2.st_ino != sbp->st_ino || ...) { ... freopen(...); action = ADD_EVENTS; } } That would avoid a potential kevent() spin loop, and revert to the nicer situation where `action' can only change between sleep and kqueue via the ADD_EVENTS code. Any better suggestions welcome! I noticed, BTW, that tail -F doesn't work reliably on NFS (I assume it never did, and it is hard to fix). The problem is that stat() can succeed even after the file has been deleted due to cacheing, but as soon as you try to read, ESTALE is returned, so tail just exits with an error. Ian
Hello Ian,
Here is the final patch. I attached a cumulative patch which includes
the changes below and changes for WARNS?=2.
Index: forward.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/tail/forward.c,v
retrieving revision 1.27
diff -u -r1.27 forward.c
--- forward.c 1 Sep 2001 22:22:44 -0000 1.27
+++ forward.c 24 Nov 2001 19:06:28 -0000
@@ -94,10 +94,11 @@
off_t off;
struct stat *sbp;
{
- int ch, kq = -1;
+ int ch, n, kq = -1;
int action = USE_SLEEP;
struct kevent ev[2];
struct stat sb2;
+ struct timespec ts;
switch(style) {
case FBYTES:
@@ -193,9 +194,10 @@
clearerr(fp);
switch (action) {
- case ADD_EVENTS: {
- int n = 0;
- struct timespec ts = { 0, 0 };
+ 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,
@@ -208,24 +210,27 @@
n++;
if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
- close(kq);
- kq = -1;
action = USE_SLEEP;
} else {
action = USE_KQUEUE;
}
break;
- }
case USE_KQUEUE:
- if (kevent(kq, NULL, 0, ev, 1, NULL) < 0)
+ 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 (ev->filter == EVFILT_VNODE) {
- /* file was rotated, wait until it reappears */
- action = USE_SLEEP;
- } else if (ev->data < 0) {
- /* file shrank, reposition to end */
+ 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;
@@ -236,26 +241,25 @@
case USE_SLEEP:
(void) usleep(250000);
clearerr(fp);
+ break;
+ }
- if (Fflag && fileno(fp) != STDIN_FILENO &&
- stat(fname, &sb2) != -1) {
- if (sb2.st_ino != sbp->st_ino ||
- sb2.st_dev != sbp->st_dev ||
- sb2.st_rdev != sbp->st_rdev ||
- sb2.st_nlink == 0) {
- fp = freopen(fname, "r", fp);
- if (fp == NULL) {
- ierr();
- 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_rdev != sbp->st_rdev ||
+ sb2.st_nlink == 0) {
+ fp = freopen(fname, "r", fp);
+ if (fp == NULL) {
+ ierr();
+ } else {
*sbp = sb2;
- if (kq != -1)
- action = ADD_EVENTS;
- } else if (kq != -1) {
- action = USE_KQUEUE;
+ action = ADD_EVENTS;
}
}
- break;
}
}
}
- -maxim
In message <20011124220954.I87876-200000@news1.macomnet.ru>, Maxim Konovalov wr ites: >Hello Ian, > >Here is the final patch. Committed, thanks! >I attached a cumulative patch which includes >the changes below and changes for WARNS?=2. Thanks, I'll commit this separately. I just had a glance at that patch, and one thing I noticed was that where gcc complains about something like while (c = *p++) { it's better to take the opportunity to make the code more readable rather than just adding brackets. e.g, change it to: while ((c = *p++) != '\0') { Also, with constness warnings, try to avoid adding code to strdup a string unless you actually need to modify it (e.g. make the pointer const if possible). I didn't check the example carefully though, so what you did may be the easiest way. Ian Hello Ian,
WARNS?=2 patch:
Index: Makefile
===================================================================
RCS file: /home/ncvs/src/usr.bin/tail/Makefile,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Makefile
--- Makefile 27 May 1994 12:32:45 -0000 1.1.1.1
+++ Makefile 25 Nov 2001 19:57:39 -0000
@@ -2,5 +2,6 @@
PROG= tail
SRCS= forward.c misc.c read.c reverse.c tail.c
+WARNS?= 2
.include <bsd.prog.mk>
Index: extern.h
===================================================================
RCS file: /home/ncvs/src/usr.bin/tail/extern.h,v
retrieving revision 1.6
diff -u -r1.6 extern.h
--- extern.h 1 Sep 2001 22:22:44 -0000 1.6
+++ extern.h 25 Nov 2001 20:00:04 -0000
@@ -36,7 +36,7 @@
*/
#define WR(p, size) do { \
- if (write(STDOUT_FILENO, p, size) != size) \
+ if (write(STDOUT_FILENO, p, size) != (ssize_t)size) \
oerr(); \
} while(0)
Index: forward.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/tail/forward.c,v
retrieving revision 1.28
diff -u -r1.28 forward.c
--- forward.c 25 Nov 2001 18:03:28 -0000 1.28
+++ forward.c 25 Nov 2001 19:58:20 -0000
@@ -171,6 +171,9 @@
if (lines(fp, off))
return;
break;
+ case REVERSE:
+ case NOTSET:
+ break;
}
if (fflag) {
Index: read.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/tail/read.c,v
retrieving revision 1.8
diff -u -r1.8 read.c
--- read.c 3 Dec 2000 17:05:45 -0000 1.8
+++ read.c 25 Nov 2001 20:00:26 -0000
@@ -115,7 +115,7 @@
} else {
if (wrap && (len = ep - p))
WR(p, len);
- if (len = p - sp)
+ if ((len = p - sp) != 0)
WR(sp, len);
}
return 0;
@@ -140,15 +140,15 @@
u_int blen;
u_int len;
char *l;
- } *lines;
+ } *slines;
int ch;
char *p;
int blen, cnt, recno, wrap;
char *sp;
- if ((lines = malloc(off * sizeof(*lines))) == NULL)
+ if ((slines = malloc(off * sizeof(*slines))) == NULL)
err(1, "malloc");
- bzero(lines, off * sizeof(*lines));
+ bzero(slines, off * sizeof(*slines));
sp = NULL;
blen = cnt = recno = wrap = 0;
@@ -160,13 +160,13 @@
}
*p++ = ch;
if (ch == '\n') {
- if (lines[recno].blen < cnt) {
- lines[recno].blen = cnt + 256;
- if ((lines[recno].l = realloc(lines[recno].l,
- lines[recno].blen)) == NULL)
+ if (slines[recno].blen < (u_int)cnt) {
+ slines[recno].blen = cnt + 256;
+ if ((slines[recno].l = realloc(slines[recno].l,
+ slines[recno].blen)) == NULL)
err(1, "realloc");
}
- bcopy(sp, lines[recno].l, lines[recno].len = cnt);
+ bcopy(sp, slines[recno].l, slines[recno].len = cnt);
cnt = 0;
p = sp;
if (++recno == off) {
@@ -180,8 +180,8 @@
return 1;
}
if (cnt) {
- lines[recno].l = sp;
- lines[recno].len = cnt;
+ slines[recno].l = sp;
+ slines[recno].len = cnt;
if (++recno == off) {
wrap = 1;
recno = 0;
@@ -190,16 +190,16 @@
if (rflag) {
for (cnt = recno - 1; cnt >= 0; --cnt)
- WR(lines[cnt].l, lines[cnt].len);
+ WR(slines[cnt].l, slines[cnt].len);
if (wrap)
for (cnt = off - 1; cnt >= recno; --cnt)
- WR(lines[cnt].l, lines[cnt].len);
+ WR(slines[cnt].l, slines[cnt].len);
} else {
if (wrap)
for (cnt = recno; cnt < off; ++cnt)
- WR(lines[cnt].l, lines[cnt].len);
+ WR(slines[cnt].l, slines[cnt].len);
for (cnt = 0; cnt < recno; ++cnt)
- WR(lines[cnt].l, lines[cnt].len);
+ WR(slines[cnt].l, slines[cnt].len);
}
return 0;
}
Index: reverse.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/tail/reverse.c,v
retrieving revision 1.12
diff -u -r1.12 reverse.c
--- reverse.c 1 Sep 2001 22:22:44 -0000 1.12
+++ reverse.c 25 Nov 2001 20:00:51 -0000
@@ -101,6 +101,8 @@
case REVERSE:
r_buf(fp);
break;
+ case NOTSET:
+ break;
}
}
Index: tail.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/tail/tail.c,v
retrieving revision 1.12
diff -u -r1.12 tail.c
--- tail.c 2 Oct 2001 06:22:01 -0000 1.12
+++ tail.c 25 Nov 2001 20:09:59 -0000
@@ -169,7 +169,7 @@
}
if (*argv)
- for (first = 1; fname = *argv++;) {
+ for (first = 1; (fname = *argv++) != '\0';) {
if ((fp = fopen(fname, "r")) == NULL ||
fstat(fileno(fp), &sb)) {
ierr();
@@ -189,7 +189,7 @@
(void)fclose(fp);
}
else {
- fname = "stdin";
+ (const char *)fname = "stdin";
if (fstat(fileno(stdin), &sb)) {
ierr();
@@ -227,7 +227,7 @@
size_t len;
char *start;
- while (ap = *++argv) {
+ while ((ap = *++argv) != '\0') {
/* Return if "--" or not an option of any form. */
if (ap[0] != '-') {
if (ap[0] != '+')
Please close this PR, a fix was committed to -current and -stable. -- Maxim Konovalov, MAcomnet, Internet-Intranet Dept., system engineer phone: +7 (095) 796-9079, mailto: maxim@macomnet.ru State Changed From-To: open->closed Fixed in both -current and -stable. |
There appears to be a difference in /usr/bin/tail -F between 4.0 and 4.1+. It's not updating the inode it's doing the tail -F on when the inode changes. (E.g., if you tail through a soft link and the link moves.) This doesn't match the man page: -F The -F option implies the -f option, but tail will also check to see if the file being followed has been renamed or rotated. The file is closed and reopened when tail detects that the filename being read from has a new inode number. The -F option is ignored if reading from standard input rather than a file. I just rebuilt a version from a 4.0 soure tree I have, and that works. The difference is in /usr/src/usr.bin/tail/forward.c. The one that doesn't work is: $FreeBSD: src/usr.bin/tail/forward.c,v 1.11.6.1 20 00/07/18 21:49:41 jlemon Exp $. The working 4.0 one has no cvs info. I don't know if this is as it should be. A quick slog throught the freebsd archies shows only a discussion of -f in CURRENT, and a reference to a version of /usr/src/sys/kern/kern_event.c,v 1.14 that doesn't easily compare to our 4.2 kern_event.c,v 1.2.2.4. Doesn't seem relevant... I'll use my 4.0 version of tail, but thought someone might know or care what the real deal is. How-To-Repeat: 1. create file /tmp/poot1 containing: 1 poot1 2 poot1 3 poot1 4 poot1 5 poot1 6 poot1 7 poot1 8 poot1 2. creat file /tmp/poot2 containing: 1 poot2 2 poot2 3 poot2 4 poot2 5 poot2 6 poot2 7 poot2 8 poot2 3. Create a link to /tmp/poot1: ln -s /tmp/poot1 /tmp/pootlnk 4. In a separate shell, tail the link: tail -F /tmp/pootlnk 5. remove the link: rm /tmp/pootlnk 6. recreate the link pointing to the other file ln -s /tmp/poot2 /tmp/pootlnk In 4.0 the tail -F will correctly show: 1 poot1 2 poot1 3 poot1 4 poot1 5 poot1 6 poot1 7 poot1 8 poot1 1 poot2 2 poot2 3 poot2 4 poot2 5 poot2 6 poot2 7 poot2 8 poot2 (And will continue to update correctly for any rm/ln sequence when the new target of the link is != to the previous target) In 4.1+ it will only show: 1 poot1 2 poot1 3 poot1 4 poot1 5 poot1 6 poot1 7 poot1 8 poot1