1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
| | # Copyright (C) all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
# only used for tests at the moment...
package PublicInbox::TailNotify;
use v5.12;
use parent qw(PublicInbox::DirIdle); # not optimal, maybe..
use PublicInbox::DS qw(now);
my ($TAIL_MOD, $ino_cls);
if ($^O eq 'linux' && eval { require PublicInbox::Inotify; 1 }) {
$TAIL_MOD = PublicInbox::Inotify::IN_MOVED_TO() |
PublicInbox::Inotify::IN_CREATE() |
PublicInbox::Inotify::IN_MODIFY();
$ino_cls = 'PublicInbox::Inotify';
} elsif (eval { require PublicInbox::KQNotify }) {
$TAIL_MOD = PublicInbox::KQNotify::MOVED_TO_OR_CREATE() |
IO::KQueue::NOTE_DELETE() | IO::KQueue::NOTE_RENAME();
$ino_cls = 'PublicInbox::KQNotify';
} else {
require PublicInbox::FakeInotify;
$TAIL_MOD = PublicInbox::FakeInotify::MOVED_TO_OR_CREATE() |
PublicInbox::FakeInotify::IN_MODIFY() |
PublicInbox::FakeInotify::IN_DELETE();
}
require IO::Poll if $ino_cls;
sub reopen_file ($) {
my ($self) = @_;
open my $fh, '<', $self->{fn} or return undef;
my @st = stat $fh or die "fstat($self->{fn}): $!";
$self->{ino_dev} = "@st[0, 1]";
$self->{inot}->watch($self->{fn}, $TAIL_MOD);
$self->{watch_fh} = $fh; # return value
}
sub new {
my ($cls, $fn) = @_;
my $self = bless { fn => $fn }, $cls;
if ($ino_cls) {
$self->{inot} = $ino_cls->new or die "E: $ino_cls->new: $!";
$self->{inot}->blocking(0);
my ($dn) = ($fn =~ m!\A(.+)/+[^/]+\z!);
$self->{inot}->watch($dn // '.', $TAIL_MOD);
} else {
$self->{inot} = PublicInbox::FakeInotify->new;
}
reopen_file($self);
$self->{inot}->watch($fn, $TAIL_MOD);
$self;
}
sub delete_self {
for (@_) { return 1 if $_->IN_DELETE_SELF }
undef;
}
sub getlines {
my ($self, $timeo) = @_;
my ($fh, $buf, $rfds, @ret, @events);
my $end = defined($timeo) ? now + $timeo : undef;
again:
while (1) {
@events = $self->{inot}->read; # Linux::Inotify2::read
last if @events;
return () if defined($timeo) && (!$timeo || (now > $end));
my $wait = 0.1;
if ($ino_cls) {
vec($rfds = '', $self->{inot}->fileno, 1) = 1;
if (defined $end) {
$wait = $end - now;
$wait = 0 if $wait < 0;
} else {
undef $wait;
}
}
select($rfds, undef, undef, $wait);
}
if ($fh = $self->{watch_fh}) {
sysread($fh, $buf, -s $fh) and
push @ret, split(/^/sm, $buf);
my @st = stat($self->{fn});
if (!@st || "@st[0, 1]" ne $self->{ino_dev} ||
delete_self(@events)) {
delete @$self{qw(ino_dev watch_fh)};
}
}
if ($fh = $self->{watch_fh} // reopen_file($self)) {
sysread($fh, $buf, -s $fh) and
push @ret, split(/^/sm, $buf);
}
goto again if (!@ret && (!defined($end) || now < $end));
@ret;
}
1;
|