5 rrr-server - watch a tree and continuously update indexfiles
9 rrr-server [options] principalfile
17 my @opt = <<'=back' =~ /B<--(\S+)>/g;
21 Prints a brief message and exists.
31 After you have setup a tree watch it with inotify and keep it
32 uptodate. Depends on inotify which probably only exists on linux.
38 It is not declared as prerequisites of the F:R:M:Recent package
39 because server side handling is optional. XXX Todo: make server side
40 handling a separate package so we can declare Inotify2 as prereq.
47 use File
::Find
qw(find);
48 use lib
"/home/k/sources/rersyncrecent/lib";
49 use File
::Rsync
::Mirror
::Recent
;
52 use Pod
::Usage
qw(pod2usage);
53 use Time
::HiRes
qw(time);
68 my($principal) = @ARGV;
70 $principal = File
::Spec
->rel2abs($principal);
71 my $recc = File
::Rsync
::Mirror
::Recent
->new
72 (local => $principal);
73 my($rf) = $recc->principal_recentfile;
74 my $rootdir = $rf->localroot;
76 for my $req (qw(Linux::Inotify2 File::Find::Rule)) {
77 eval qq{ require $req; 1 };
79 die "Failing on 'require $req': $@"
85 my $inotify = new Linux
::Inotify2
86 or die "Unable to create new inotify object: $!";
90 unless ($inotify->watch
101 # or die "watch creation failed: $!";
103 die "Alert: ENOSPC reached, probably your system needs to increase the amount of inotify watches allowed per user via '/proc/sys/fs/inotify/max_user_watches'\n";
105 for my $err (qw(ENOENT EBADF EEXIST)) {
107 warn "$err encountered on '$directory'. Giving up on this watch, trying to continue.\n" if $Opt{verbose
};
115 foreach my $directory ( File
::Find
::Rule
->new->directory->in($rootdir) ) {
116 my_inotify
($directory);
120 my($rf,$fullname,$type,$batch) = @_;
121 push @
$batch, {path
=> $fullname, type
=> $type};
125 my($inotify,$rf,$fullname,$batch) = @_;
126 my_inotify
($fullname);
127 # immediately inspect it, we certainly have missed the first
128 # events in this directory
129 opendir my $dh, $fullname or return;
130 for my $dirent (readdir $dh) {
131 next if $dirent eq "." || $dirent eq "..";
132 my $abs = File
::Spec
->catfile($fullname,$dirent);
133 if (-l
$abs || -f _
) {
134 warn "[..:..:..] Readdir_F $abs\n" if $Opt{verbose
};
135 handle_file
($rf,$abs,"new",$batch);
137 warn "[..:..:..] Readdir_D $abs\n" if $Opt{verbose
};
138 newdir
($inotify,$rf,$abs,$batch);
143 my $requires_fsck = 1;
148 "IN_CREATE", "IN_CLOSE_WRITE", "IN_MOVED_TO", # new
149 "IN_DELETE", "IN_MOVED_FROM", # delete
150 "IN_DELETE_SELF", "IN_MOVE_SELF", # self
153 push @stringifiedmask, $watch;
154 # new directories must be added to the watches, deleted
155 # directories deleted; moved directories both
158 # warn sprintf "rootdir[$rootdir]time[%s]ev.w.name[%s]ev.name[%s]ev.fullname[%s]mask[%s]\n", time, $ev->w->name, $ev->name, $ev->fullname, join("|",@stringifiedmask);
160 if ($ev->w->name eq $rootdir) {
161 my $meta = $rf->meta_data;
162 my $ignore_rx = qr
((?x
: ^ \Q
$meta->{filenameroot
}\E
- [0-9]*[smhdWMQYZ
] \Q
$meta->{serializer_suffix
}\E
));
163 if ($ev->name =~ $ignore_rx) {
164 # warn sprintf "==> Ignoring object in rootdir looking like internal file: %s", $ev->name;
169 my $fullname = $ev->fullname;
170 my($reportname) = $fullname =~ m{^\Q$rootdir\E/(.*)};
171 my $time = sprintf "%02d:%02d:%02d", (localtime)[2,1,0];
173 } elsif ($ev->IN_Q_OVERFLOW) {
175 } elsif ($ev->IN_DELETE || $ev->IN_MOVED_FROM) {
176 # we don't know whether it was a directory, we simply pass
177 # it to $rf. $rf must be robust enough to swallow bogus
178 # deletes. Alternatively we could look into $rf whether
179 # this object is known, couldn't we?
180 handle_file
($rf,$fullname,"delete",$batch);
181 warn "[$time] Deleteobj $reportname (@stringifiedmask)\n" if $Opt{verbose
};
182 } elsif ($ev->IN_DELETE_SELF || $ev->IN_MOVE_SELF) {
184 warn "[$time] Delwatcher $reportname (@stringifiedmask)\n" if $Opt{verbose
};
185 } elsif ($ev->IN_ISDIR) {
186 newdir
($inotify,$rf,$fullname,$batch);
187 warn "[$time] Newwatcher $reportname (@stringifiedmask)\n" if $Opt{verbose
};
188 } elsif (-l
$fullname) {
189 handle_file
($rf,$fullname,"new",$batch);
190 warn "[$time] Updatelink $reportname (@stringifiedmask)\n" if $Opt{verbose
};
192 if ($ev->IN_CLOSE_WRITE || $ev->IN_MOVED_TO) {
193 handle_file
($rf,$fullname,"new",$batch);
194 warn "[$time] Updatefile $reportname (@stringifiedmask)\n" if $Opt{verbose
};
197 warn "[$time] Ignore $reportname (@stringifiedmask)\n" if $Opt{verbose
};
202 my $last_aggregate_call = 0;
203 my $have_warned_fsck = 0;
206 my @events = $inotify->read;
207 unless ( @events > 0 ) {
208 print "Alert: inotify read error: $!";
212 foreach my $event (@events) {
213 handle_event
($event,\
@batch);
215 $rf->batch_update(\
@batch);
216 if (time > $last_aggregate_call + 30) { # arbitrary
219 if (! defined $pid) {
230 $last_aggregate_call = time;
232 if ($requires_fsck) {
233 if (time > $have_warned_fsck + 3600) {
234 warn "REMINDER: TODO: HANDLE FSCK" if $Opt{verbose
};
235 $have_warned_fsck = time;
246 # cperl-indent-level: 4