1#!/usr/bin/perl -w
2
3use IO::KQueue;
4use Getopt::Std;
5
6my $START = 10;
7
8my %opts;
9
10getopts('h', \%opts);
11help() if $opts{h};
12
13my $file = shift(@ARGV) || "-";
14open(my $fh, $file) || die "$0: open($file): $!";
15
16my @buf;
17while (<$fh>) {
18    $buf[$. % $START] = $_;
19}
20my @tail = (@buf[ ($. % $START + 1) .. $#buf ],
21			@buf[  0 .. $. % $START ]);
22for (@tail) {
23    print if $_; # @tail may begin or end with undef
24}
25
26my $kq = IO::KQueue->new();
27
28$kq->EV_SET(fileno($fh), EVFILT_READ, EV_ADD, 0, 0, \&read_file);
29$kq->EV_SET(fileno($fh), EVFILT_VNODE, EV_ADD, NOTE_DELETE | NOTE_RENAME, 0, \&reopen_file);
30
31while (1) {
32    my @events = $kq->kevent();
33
34    foreach my $kevent (@events) {
35        my $sub = $kevent->[KQ_UDATA];
36        $sub->($kevent) if ref($sub) eq 'CODE';
37    }
38}
39
40sub read_file {
41    my $kev = shift;
42
43    if ($kev->[KQ_DATA] < 0) {
44        print "$file has shrunk\n";
45        seek($fh, 0, 2);
46        return;
47    }
48
49    while (<$fh>) {
50        print;
51    }
52}
53
54sub reopen_file {
55    # renamed or deleted...
56    close($fh);
57    open($fh, $file) || die "$0: $file went away\n";
58    $kq->EV_SET(fileno($fh), EVFILT_READ, EV_ADD, 0, 0, \&read_file);
59    $kq->EV_SET(fileno($fh), EVFILT_VNODE, EV_ADD, NOTE_DELETE | NOTE_RENAME, 0, \&reopen_file);
60}
61
62sub help {
63    print <<EOT;
64$0 [-h] [file]
65
66Tail a file forever, like tail -F.
67EOT
68    exit(0);
69}
70