1 # Copyright (C) 2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
4 # implements the small subset of Linux::Inotify2 functionality we use
5 # using IO::KQueue on *BSD systems.
6 package PublicInbox::KQNotify;
9 use PublicInbox::DSKQXS; # wraps IO::KQueue for fork-safe DESTROY
10 use PublicInbox::FakeInotify;
11 use Time::HiRes qw(stat);
13 # NOTE_EXTEND detects rename(2), NOTE_WRITE detects link(2)
14 sub MOVED_TO_OR_CREATE () { NOTE_EXTEND|NOTE_WRITE }
18 bless { dskq => PublicInbox::DSKQXS->new, watch => {} }, $class;
22 my ($self, $path, $mask, $cb) = @_;
23 my ($fh, $cls, @extra);
25 opendir($fh, $path) or return;
27 @extra = ($path, $st[10]); # 10: ctime
28 $cls = 'PublicInbox::KQNotify::Watchdir';
30 open($fh, '<', $path) or return;
31 $cls = 'PublicInbox::KQNotify::Watch';
33 my $ident = fileno($fh);
34 $self->{dskq}->{kq}->EV_SET($ident, # ident
35 EVFILT_VNODE, # filter
36 EV_ADD | EV_CLEAR, # flags
39 if ($mask == NOTE_WRITE || $mask == MOVED_TO_OR_CREATE) {
40 $self->{watch}->{$ident} = [ $fh, $cb, @extra ];
42 die "TODO Not implemented: $mask";
47 # emulate Linux::Inotify::fileno
48 sub fileno { ${$_[0]->{dskq}->{kq}} }
50 # noop for Linux::Inotify2 compatibility. Unlike inotify,
51 # kqueue doesn't seem to overflow since it's limited by the number of
52 # open FDs the process has
55 # noop for Linux::Inotify2 compatibility, we use `0' timeout for ->kevent
58 # behave like Linux::Inotify2::poll
61 my @kevents = $self->{dskq}->{kq}->kevent(0);
62 for my $kev (@kevents) {
63 my $ident = $kev->[KQ_IDENT];
64 my $mask = $kev->[KQ_FFLAGS];
65 my ($dh, $cb, $path, $old_ctime) = @{$self->{watch}->{$ident}};
66 if (!defined($path) && ($mask & NOTE_WRITE) == NOTE_WRITE) {
68 } elsif ($mask & MOVED_TO_OR_CREATE) {
69 my @new_st = stat($path) or next;
70 $self->{watch}->{$ident}->[3] = $new_st[10]; # ctime
72 PublicInbox::FakeInotify::on_new_files($dh, $cb,
78 package PublicInbox::KQNotify::Watch;
81 sub cancel { close ${$_[0]} or die "close: $!" }
83 package PublicInbox::KQNotify::Watchdir;
86 sub cancel { closedir ${$_[0]} or die "closedir: $!" }