X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FLEI.pm;h=fd59235846ae889b5ffab63bb5e0da7a8816b23a;hb=1c52f49354aa83e71fcceccae888da0c77f2391d;hp=8b0614f27980f8e4e53f31681562ae25cc83b119;hpb=f3d0e746c6a35c8600b91af99958a52cbc114a4b;p=public-inbox.git diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm index 8b0614f2..fd592358 100644 --- a/lib/PublicInbox/LEI.pm +++ b/lib/PublicInbox/LEI.pm @@ -18,8 +18,7 @@ use POSIX qw(strftime); use IO::Handle (); use Fcntl qw(SEEK_SET); use PublicInbox::Config; -use PublicInbox::Syscall qw(SFD_NONBLOCK EPOLLIN EPOLLET); -use PublicInbox::Sigfd; +use PublicInbox::Syscall qw(EPOLLIN EPOLLET); use PublicInbox::DS qw(now dwaitpid); use PublicInbox::Spawn qw(spawn popen_rd); use PublicInbox::Lock; @@ -40,7 +39,6 @@ $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 => [ ino watches ] } -our %LIVE_SOCK; # "GLOB(0x....)" => $lei->{sock} # TBD: this is a documentation mechanism to show a subcommand # (may) pass options through to another command: @@ -202,12 +200,12 @@ our %CMD = ( # sorted in order of importance/use: 'rediff' => [ '--stdin|LOCATION...', 'regenerate a diff with different options', 'stdin|', # /|\z/ must be first for lone dash - qw(git-dir=s@ cwd! verbose|v+ color:s no-color), + qw(git-dir=s@ cwd! verbose|v+ color:s no-color drq:1 dequote-only:1), @diff_opt, @lxs_opt, @net_opt, @c_opt ], 'add-external' => [ 'LOCATION', 'add/set priority of a publicinbox|extindex for extra matches', - qw(boost=i mirror=s inbox-version=i verbose|v+), + qw(boost=i mirror=s inbox-version=i epoch=s verbose|v+), @c_opt, index_opt(), @net_opt ], 'ls-external' => [ '[FILTER]', 'list publicinbox|extindex locations', qw(format|f=s z|0 globoff|g invert-match|v local remote), @c_opt ], @@ -215,7 +213,7 @@ our %CMD = ( # sorted in order of importance/use: 'ls-mail-sync' => [ '[FILTER]', 'list mail sync folders', qw(z|0 globoff|g invert-match|v local remote), @c_opt ], 'ls-mail-source' => [ 'URL', 'list IMAP or NNTP mail source folders', - qw(z|0 ascii l url), @c_opt ], + qw(z|0 ascii l pretty url), @c_opt ], 'forget-external' => [ 'LOCATION...|--prune', 'exclude further results from a publicinbox|extindex', qw(prune), @c_opt ], @@ -279,7 +277,7 @@ our %CMD = ( # sorted in order of importance/use: 'config' => [ '[...]', sub { 'git-config(1) wrapper for '._config_path($_[0]); }, qw(config-file|system|global|file|f=s), # for conflict detection - qw(c=s@ C=s@), pass_through('git config') ], + qw(edit|e c=s@ C=s@), pass_through('git config') ], 'inspect' => [ 'ITEMS...|--stdin', 'inspect lei/store and/or local external', qw(stdin| pretty ascii dir=s), @c_opt ], @@ -421,7 +419,9 @@ my %OPTDESC = ( 'remote' => 'limit operations to those requiring network access', 'remote!' => 'prevent operations requiring network access', -'all:s up' => ['local|remote', 'update all remote or local saved searches' ], +# up, refresh-mail-sync, export-kw +'all:s' => ['TYPE|local|remote', 'all remote or local folders' ], + 'remote-fudge-time=s' => [ 'INTERVAL', 'look for mail INTERVAL older than the last successful query' ], @@ -518,8 +518,7 @@ sub fail ($$;$) { my ($self, $buf, $exit_code) = @_; $self->{failed}++; err($self, $buf) if defined $buf; - # calls fail_handler - $self->{pkt_op_p}->pkt_do('!') if $self->{pkt_op_p}; + $self->{pkt_op_p}->pkt_do('fail_handler') if $self->{pkt_op_p}; x_it($self, ($exit_code // 1) << 8); undef; } @@ -551,7 +550,7 @@ sub child_error { # passes non-fatal curl exit codes to user sub note_sigpipe { # triggers sigpipe_handler my ($self, $fd) = @_; close(delete($self->{$fd})); # explicit close silences Perl warning - $self->{pkt_op_p}->pkt_do('|') if $self->{pkt_op_p}; + $self->{pkt_op_p}->pkt_do('sigpipe_handler') if $self->{pkt_op_p}; x_it($self, 13); } @@ -578,9 +577,9 @@ sub _lei_atfork_child { close $listener if $listener; undef $listener; $dir_idle->force_close if $dir_idle; + undef $dir_idle; %PATH2CFG = (); $MDIR2CFGPATH = {}; - %LIVE_SOCK = (); eval 'no warnings; undef $PublicInbox::LeiNoteEvent::to_flush'; undef $errors_log; $quit = \&CORE::exit; @@ -614,12 +613,11 @@ sub incr { sub pkt_ops { my ($lei, $ops) = @_; - $ops->{'!'} = [ \&fail_handler, $lei ]; - $ops->{'|'} = [ \&sigpipe_handler, $lei ]; - $ops->{x_it} = [ \&x_it, $lei ]; - $ops->{child_error} = [ \&child_error, $lei ]; - $ops->{incr} = [ \&incr, $lei ]; - $ops->{sto_done_request} = [ \&sto_done_request, $lei, $lei->{sock} ]; + $ops->{fail_handler} = [ $lei ]; + $ops->{sigpipe_handler} = [ $lei ]; + $ops->{x_it} = [ $lei ]; + $ops->{child_error} = [ $lei ]; + $ops->{incr} = [ $lei ]; $ops; } @@ -630,7 +628,6 @@ sub workers_start { my $end = $lei->pkt_op_pair; my $ident = $wq->{-wq_ident} // "lei-$lei->{cmd} worker"; $flds->{lei} = $lei; - $wq->{-wq_nr_workers} //= $jobs; # lock, no incrementing $wq->wq_workers_start($ident, $jobs, $lei->oldset, $flds); delete $lei->{pkt_op_p}; my $op_c = delete $lei->{pkt_op_c}; @@ -874,14 +871,6 @@ sub _config { waitpid(spawn($cmd, \%env, \%rdr), 0); } -sub lei_config { - my ($self, @argv) = @_; - $self->{opt}->{'config-file'} and return fail $self, - "config file switches not supported by `lei config'"; - _config(@_); - x_it($self, $?) if $?; -} - sub lei_daemon_pid { puts shift, $$ } sub lei_daemon_kill { @@ -1122,7 +1111,6 @@ sub dclose { my ($self) = @_; delete $self->{-progress}; _drop_wq($self) if $self->{failed}; - close(delete $self->{1}) if $self->{1}; # may reap_compress $self->close if $self->{-event_init_done}; # PublicInbox::DS::close } @@ -1130,23 +1118,28 @@ sub dclose { sub event_step { my ($self) = @_; local %ENV = %{$self->{env}}; - my $sock = $self->{sock}; local $current_lei = $self; eval { - while (my @fds = $recv_cmd->($sock, my $buf, 4096)) { + my $buf; + while (my @fds = $recv_cmd->($self->{sock}, $buf, 4096)) { if (scalar(@fds) == 1 && !defined($fds[0])) { return if $! == EAGAIN; next if $! == EINTR; last if $! == ECONNRESET; die "recvmsg: $!"; } - for my $fd (@fds) { - open my $rfh, '+<&=', $fd; + for (@fds) { open my $rfh, '+<&=', $_ } + } + if ($buf eq '') { + _drop_wq($self); # EOF, client disconnected + dclose($self); + } elsif ($buf =~ /\A(STOP|CONT)\z/) { + for my $wq (grep(defined, @$self{@WQ_KEYS})) { + $wq->wq_kill($buf) or $wq->wq_kill_old($buf); } + } else { die "unrecognized client signal: $buf"; } - _drop_wq($self); # EOF, client disconnected - dclose($self); }; if (my $err = $@) { eval { $self->fail($err) }; @@ -1158,6 +1151,7 @@ sub event_step_init { my ($self) = @_; my $sock = $self->{sock} or return; $self->{-event_init_done} //= do { # persist til $ops done + $sock->blocking(0); $self->SUPER::new($sock, EPOLLIN|EPOLLET); $sock; }; @@ -1206,7 +1200,7 @@ sub dir_idle_handler ($) { # PublicInbox::DirIdle callback $lei->dispatch('note-event', "maildir:$mdir", $nc, $bn, $fn); }; - warn "E note-event $f: $@\n" if $@; + warn "E: note-event $f: $@\n" if $@; } } if ($ev->can('cancel') && ($ev->IN_IGNORE || $ev->IN_UNMOUNT)) { @@ -1231,6 +1225,7 @@ sub lazy_start { $errors_log = "$sock_dir/errors.log"; my $addr = pack_sockaddr_un($path); my $lk = bless { lock_path => $errors_log }, 'PublicInbox::Lock'; + umask(077) // die("umask(077): $!"); $lk->lock_acquire; socket($listener, AF_UNIX, SOCK_SEQPACKET, 0) or die "socket: $!"; if ($errno == ECONNREFUSED || $errno == ENOENT) { @@ -1242,7 +1237,6 @@ sub lazy_start { $! = $errno; # allow interpolation to stringify in die die "connect($path): $!"; } - umask(077) // die("umask(077): $!"); bind($listener, $addr) or die "bind($path): $!"; $lk->lock_release; undef $lk; @@ -1296,23 +1290,11 @@ sub lazy_start { USR1 => \&noop, USR2 => \&noop, }; - my $sigfd = PublicInbox::Sigfd->new($sig, SFD_NONBLOCK); - local @SIG{keys %$sig} = values(%$sig) unless $sigfd; - undef $sig; - local $SIG{PIPE} = 'IGNORE'; require PublicInbox::DirIdle; local $dir_idle = PublicInbox::DirIdle->new([$sock_dir], sub { # just rely on wakeup to hit PostLoopCallback set below dir_idle_handler($_[0]) if $_[0]->fullname ne $path; }, 1); - if ($sigfd) { - undef $sigfd; # unref, already in DS::DescriptorMap - } else { - # wake up every second to accept signals if we don't - # have signalfd or IO::KQueue: - PublicInbox::DS::sig_setmask($oldset); - PublicInbox::DS->SetLoopTimeout(1000); - } PublicInbox::DS->SetPostLoopCallback(sub { my ($dmap, undef) = @_; if (@st = defined($path) ? stat($path) : ()) { @@ -1349,8 +1331,10 @@ sub lazy_start { open STDERR, '>&STDIN' or die "redirect stderr failed: $!"; open STDOUT, '>&STDIN' or die "redirect stdout failed: $!"; # $daemon pipe to `lei' closed, main loop begins: - eval { PublicInbox::DS->EventLoop }; + eval { PublicInbox::DS::event_loop($sig, $oldset) }; warn "event loop error: $@\n" if $@; + # exit() may trigger waitpid via various DESTROY, ensure interruptible + PublicInbox::DS::sig_setmask($oldset); dump_and_clear_log(); exit($exit_code // 0); } @@ -1391,7 +1375,7 @@ sub fchdir { sub wq_eof { # EOF callback for main daemon my ($lei) = @_; my $wq1 = delete $lei->{wq1} // return $lei->fail; # already failed - $wq1->wq_wait_old(\&wq_done_wait, $lei); + $wq1->wq_wait_old($wq1->can('_wq_done_wait') // \&wq_done_wait, $lei); } sub watch_state_ok ($) { @@ -1417,6 +1401,7 @@ sub add_maildir_watch ($$) { sub refresh_watches { my ($lei) = @_; + $dir_idle or return; my $cfg = _lei_cfg($lei) or return; my $old = $cfg->{-watches}; my $watches = $cfg->{-watches} //= {}; @@ -1427,7 +1412,7 @@ sub refresh_watches { require PublicInbox::LeiWatch; $watches->{$url} //= PublicInbox::LeiWatch->new($url); $seen{$url} = undef; - my $state = $cfg->get_1("watch.$url", 'state'); + my $state = $cfg->get_1("watch.$url.state"); if (!watch_state_ok($state)) { $lei->err("watch.$url.state=$state not supported"); next; @@ -1496,22 +1481,24 @@ sub lms { (-f $f || $rw) ? PublicInbox::LeiMailSync->new($f) : undef; } -sub sto_done_request { # only call this from lei-daemon process (not workers) +sub sto_done_request { my ($lei, $sock) = @_; eval { if ($sock //= $lei->{sock}) { # issue, async wait - $LIVE_SOCK{"$sock"} = $sock; - $lei->{sto}->ipc_do('done', "$sock"); + $lei->{sto}->wq_io_do('done', [ $sock ]); } else { # forcibly wait - my $wait = $lei->{sto}->ipc_do('done'); + my $wait = $lei->{sto}->wq_do('done'); } }; $lei->err($@) if $@; } -sub sto_done_complete { # called in lei-daemon when LeiStore->done is complete - my ($sock_str) = @_; - delete $LIVE_SOCK{$sock_str}; # frees {sock} for waiting lei clients +sub cfg_dump ($$) { + my ($lei, $f) = @_; + my $ret = eval { PublicInbox::Config->git_config_dump($f, $lei->{2}) }; + return $ret if !$@; + $lei->err($@); + undef; } 1;