$GLP_PASS->configure(qw(gnu_getopt no_ignore_case auto_abbrev pass_through));
our %PATH2CFG; # persistent for socket daemon
-our $MDIR2CFGPATH; # /path/to/maildir => { /path/to/config => undef }
+our $MDIR2CFGPATH; # /path/to/maildir => { /path/to/config => [ ino watches ] }
# TBD: this is a documentation mechanism to show a subcommand
# (may) pass options through to another command:
sub index_opt {
# TODO: drop underscore variants everywhere, they're undocumented
qw(fsync|sync! jobs|j=i indexlevel|L=s compact
- max_size|max-size=s sequential_shard|sequential-shard
+ max_size|max-size=s sequential-shard
batch_size|batch-size=s skip-docdata)
}
'remove imported messages from IMAP, Maildirs, and MH',
qw(exact! all jobs:i indexed), @c_opt ],
-'add-watch' => [ 'LOCATION', 'watch for new messages and flag changes',
+'add-watch' => [ 'LOCATION...', 'watch for new messages and flag changes',
qw(poll-interval=s state=s recursive|r), @c_opt ],
+'rm-watch' => [ 'LOCATION...', 'remove specified watch(es)',
+ qw(recursive|r), @c_opt ],
'ls-watch' => [ '[FILTER...]', 'list active watches with numbers and status',
qw(l z|0), @c_opt ],
'pause-watch' => [ '[WATCH_NUMBER_OR_FILTER]', qw(all local remote), @c_opt ],
'do not index messages larger than SIZE (default: infinity)' ],
'batch_size|batch-size=s' => [ 'SIZE',
'flush changes to OS after given number of bytes (default: 1m)' ],
-'sequential_shard|sequential-shard' =>
+'sequential-shard' =>
'index Xapian shards sequentially for slow storage',
'skip-docdata' =>
'drop compatibility w/ public-inbox <1.6 to save ~1.5% space',
}
close $listener if $listener;
undef $listener;
- undef $dir_idle;
+ $dir_idle->force_close if $dir_idle;
%PATH2CFG = ();
$MDIR2CFGPATH = {};
eval 'no warnings; undef $PublicInbox::LeiNoteEvent::to_flush';
}
}
$self->{cfg} = $PATH2CFG{$f} = $cfg;
+ refresh_watches($self);
+ $cfg;
}
sub _lei_store ($;$) {
}
umask(077) // die("umask(077): $!");
bind($listener, $addr) or die "bind($path): $!";
- listen($listener, 1024) or die "listen: $!";
$lk->lock_release;
undef $lk;
my @st = stat($path) or die "stat($path): $!";
$state =~ /\Apause|(?:import|index|tag)-(?:ro|rw)\z/;
}
+sub cancel_maildir_watch ($$) {
+ my ($d, $cfg_f) = @_;
+ my $w = delete $MDIR2CFGPATH->{$d}->{$cfg_f};
+ scalar(keys %{$MDIR2CFGPATH->{$d}}) or
+ delete $MDIR2CFGPATH->{$d};
+ for my $x (@{$w // []}) { $x->cancel }
+}
+
sub refresh_watches {
my ($lei) = @_;
my $cfg = _lei_cfg($lei) or return;
- $cfg->{-env} //= { %{$lei->{env}}, PWD => '/' }; # for cfg2lei
+ my $old = $cfg->{-watches};
my $watches = $cfg->{-watches} //= {};
- require PublicInbox::LeiWatch;
+ my %seen;
+ my $cfg_f = $cfg->{'-f'};
for my $w (grep(/\Awatch\..+\.state\z/, keys %$cfg)) {
my $url = substr($w, length('watch.'), -length('.state'));
- my $lw = $watches->{$w} //= PublicInbox::LeiWatch->new($url);
+ require PublicInbox::LeiWatch;
+ my $lw = $watches->{$url} //= PublicInbox::LeiWatch->new($url);
+ $seen{$url} = undef;
my $state = $cfg->get_1("watch.$url", 'state');
if (!watch_state_ok($state)) {
$lei->err("watch.$url.state=$state not supported");
next;
}
- my $f = $cfg->{'-f'};
if ($url =~ /\Amaildir:(.+)/i) {
my $d = File::Spec->canonpath($1);
if ($state eq 'pause') {
- delete $MDIR2CFGPATH->{$d}->{$f};
- scalar(keys %{$MDIR2CFGPATH->{$d}}) or
- delete $MDIR2CFGPATH->{$d};
- } elsif (!exists($MDIR2CFGPATH->{$d}->{$f})) {
- $dir_idle->add_watches(["$d/cur", "$d/new"], 1);
- $MDIR2CFGPATH->{$d}->{$f} = undef;
+ cancel_maildir_watch($d, $cfg_f);
+ } elsif (!exists($MDIR2CFGPATH->{$d}->{$cfg_f})) {
+ my @w = $dir_idle->add_watches(
+ ["$d/cur", "$d/new"], 1);
+ push @{$MDIR2CFGPATH->{$d}->{$cfg_f}}, @w if @w;
}
} else { # TODO: imap/nntp/jmap
$lei->child_error(1,
"E: watch $url not supported, yet");
}
}
+ if ($old) { # cull old non-existent entries
+ for my $url (keys %$old) {
+ next if exists $seen{$url};
+ delete $old->{$url};
+ if ($url =~ /\Amaildir:(.+)/i) {
+ my $d = File::Spec->canonpath($1);
+ cancel_maildir_watch($d, $cfg_f);
+ } else { # TODO: imap/nntp/jmap
+ $lei->child_error(1, "E: watch $url TODO");
+ }
+ }
+ }
+ if (scalar keys %$watches) {
+ $cfg->{-env} //= { %{$lei->{env}}, PWD => '/' }; # for cfg2lei
+ } else {
+ delete $cfg->{-watches};
+ }
}
1;