diff options
Diffstat (limited to 'lib/PublicInbox')
-rw-r--r-- | lib/PublicInbox/IPC.pm | 35 | ||||
-rw-r--r-- | lib/PublicInbox/LEI.pm | 33 | ||||
-rw-r--r-- | lib/PublicInbox/LeiExternal.pm | 31 | ||||
-rw-r--r-- | lib/PublicInbox/LeiForgetExternal.pm | 8 | ||||
-rw-r--r-- | lib/PublicInbox/LeiImport.pm | 22 | ||||
-rw-r--r-- | lib/PublicInbox/LeiMirror.pm | 32 | ||||
-rw-r--r-- | lib/PublicInbox/LeiQuery.pm | 2 | ||||
-rw-r--r-- | lib/PublicInbox/LeiSelfSocket.pm | 5 | ||||
-rw-r--r-- | lib/PublicInbox/LeiStore.pm | 2 |
9 files changed, 97 insertions, 73 deletions
diff --git a/lib/PublicInbox/IPC.pm b/lib/PublicInbox/IPC.pm index da534aa7..1f0e87ee 100644 --- a/lib/PublicInbox/IPC.pm +++ b/lib/PublicInbox/IPC.pm @@ -216,9 +216,27 @@ sub ipc_sibling_atfork_child { $pid == $$ and die "BUG: $$ ipc_atfork_child called on itself"; } +sub send_cmd ($$$$) { + my ($s, $fds, $buf, $fl) = @_; + while (1) { + my $n = $send_cmd->($s, $fds, $buf, $fl); + next if !defined($n) && $!{EINTR}; + return $n; + } +} + +sub recv_cmd ($$$) { + my ($s, undef, $len) = @_; # $_[1] is $buf + while (1) { + my @fds = $recv_cmd->($s, $_[1], $len); + next if scalar(@fds) == 1 && !defined($fds[0]) && $!{EINTR}; + return @fds; + } +} + sub recv_and_run { my ($self, $s2, $len, $full_stream) = @_; - my @fds = $recv_cmd->($s2, my $buf, $len // $MY_MAX_ARG_STRLEN); + my @fds = recv_cmd($s2, my $buf, $len // $MY_MAX_ARG_STRLEN); return if scalar(@fds) && !defined($fds[0]); my $n = length($buf) or return 0; my $nfd = 0; @@ -283,15 +301,18 @@ sub stream_in_full ($$$) { my ($s1, $fds, $buf) = @_; socketpair(my $r, my $w, AF_UNIX, SOCK_STREAM, 0) or croak "socketpair: $!"; - my $n = $send_cmd->($s1, [ fileno($r) ], + my $n = send_cmd($s1, [ fileno($r) ], ipc_freeze(['do_sock_stream', length($buf)]), MSG_EOR) // croak "sendmsg: $!"; undef $r; - $n = $send_cmd->($w, $fds, $buf, 0) // croak "sendmsg: $!"; + $n = send_cmd($w, $fds, $buf, 0) // croak "sendmsg: $!"; while ($n < length($buf)) { - my $x = syswrite($w, $buf, length($buf) - $n, $n) // - croak "syswrite: $!"; - croak "syswrite wrote 0 bytes" if $x == 0; + my $x = syswrite($w, $buf, length($buf) - $n, $n); + if (!defined($n)) { + next if $!{EINTR}; + croak "syswrite: $!"; + } + $x or croak "syswrite wrote 0 bytes"; $n += $x; } } @@ -304,7 +325,7 @@ sub wq_io_do { # always async if (length($buf) > $MY_MAX_ARG_STRLEN) { stream_in_full($s1, $fds, $buf); } else { - my $n = $send_cmd->($s1, $fds, $buf, MSG_EOR); + my $n = send_cmd $s1, $fds, $buf, MSG_EOR; return if defined($n); # likely $!{ETOOMANYREFS} and croak "sendmsg: $! (check RLIMIT_NOFILE)"; diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm index eb9799f6..578686e2 100644 --- a/lib/PublicInbox/LEI.pm +++ b/lib/PublicInbox/LEI.pm @@ -23,13 +23,14 @@ use PublicInbox::Lock; use PublicInbox::Eml; use PublicInbox::Import; use PublicInbox::ContentHash qw(git_sha); +use PublicInbox::IPC; use Time::HiRes qw(stat); # ctime comparisons for config cache use File::Path (); use File::Spec; +use Carp (); use Sys::Syslog qw(openlog syslog closelog); our $quit = \&CORE::exit; -our ($current_lei, $errors_log, $listener, $oldset, $dir_idle, - $recv_cmd, $send_cmd); +our ($current_lei, $errors_log, $listener, $oldset, $dir_idle); my $GLP = Getopt::Long::Parser->new; $GLP->configure(qw(gnu_getopt no_ignore_case auto_abbrev)); my $GLP_PASS = Getopt::Long::Parser->new; @@ -1013,9 +1014,11 @@ sub start_mua { sub send_exec_cmd { # tell script/lei to execute a command my ($self, $io, $cmd, $env) = @_; - my $sock = $self->{sock} // die 'lei client gone'; - my $fds = [ map { fileno($_) } @$io ]; - $send_cmd->($sock, $fds, exec_buf($cmd, $env), MSG_EOR); + PublicInbox::IPC::send_cmd( + $self->{sock} // die('lei client gone'), + [ map { fileno($_) } @$io ], + exec_buf($cmd, $env), MSG_EOR) // + Carp::croak("sendmsg: $!"); } sub poke_mua { # forces terminal MUAs to wake up and hopefully notice new mail @@ -1109,7 +1112,8 @@ sub accept_dispatch { # Listener {post_accept} callback select($rvec, undef, undef, 60) or return send($sock, 'timed out waiting to recv FDs', MSG_EOR); # (4096 * 33) >MAX_ARG_STRLEN - my @fds = $recv_cmd->($sock, my $buf, 4096 * 33) or return; # EOF + my @fds = PublicInbox::IPC::recv_cmd($sock, my $buf, 4096 * 33) or + return; # EOF if (!defined($fds[0])) { warn(my $msg = "recv_cmd failed: $!"); return send($sock, $msg, MSG_EOR); @@ -1147,7 +1151,8 @@ sub event_step { local %ENV = %{$self->{env}}; local $current_lei = $self; eval { - my @fds = $recv_cmd->($self->{sock} // return, my $buf, 4096); + my @fds = PublicInbox::IPC::recv_cmd( + $self->{sock} // return, my $buf, 4096); if (scalar(@fds) == 1 && !defined($fds[0])) { return if $! == EAGAIN; die "recvmsg: $!" if $! != ECONNRESET; @@ -1273,18 +1278,8 @@ sub lazy_start { my @st = stat($path) or die "stat($path): $!"; my $dev_ino_expect = pack('dd', $st[0], $st[1]); # dev+ino local $oldset = PublicInbox::DS::block_signals(); - if ($narg == 5) { - $send_cmd = PublicInbox::Spawn->can('send_cmd4'); - $recv_cmd = PublicInbox::Spawn->can('recv_cmd4') // do { - require PublicInbox::CmdIPC4; - $send_cmd = PublicInbox::CmdIPC4->can('send_cmd4'); - PublicInbox::CmdIPC4->can('recv_cmd4'); - } // do { - $send_cmd = PublicInbox::Syscall->can('send_cmd4'); - PublicInbox::Syscall->can('recv_cmd4'); - }; - } - $recv_cmd or die <<""; + die "incompatible narg=$narg" if $narg != 5; + $PublicInbox::IPC::send_cmd or die <<""; (Socket::MsgHdr || Inline::C) missing/unconfigured (narg=$narg); require PublicInbox::Listener; diff --git a/lib/PublicInbox/LeiExternal.pm b/lib/PublicInbox/LeiExternal.pm index 3e2a2288..31b9bd1e 100644 --- a/lib/PublicInbox/LeiExternal.pm +++ b/lib/PublicInbox/LeiExternal.pm @@ -86,39 +86,34 @@ sub canonicalize_excludes { # returns an anonymous sub which returns an array of potential results sub complete_url_prepare { my $argv = $_[-1]; # $_[0] may be $lei - # Workaround bash word-splitting URLs to ['https', ':', '//' ...] - # Maybe there's a better way to go about this in - # contrib/completion/lei-completion.bash - my $re = ''; - my $cur = pop(@$argv) // ''; + # Workaround bash default COMP_WORDBREAKS splitting URLs to + # ['https', ':', '//', ...]. COMP_WORDBREAKS is global for all + # completions loaded, not just ours, so we can't change it. + # cf. contrib/completion/lei-completion.bash + my ($pfx, $cur) = ('', pop(@$argv) // ''); if (@$argv) { my @x = @$argv; - if ($cur eq ':' && @x) { + if ($cur =~ /\A[:;=]\z/) { # COMP_WORDBREAKS + URL union push @x, $cur; $cur = ''; } - while (@x > 2 && $x[0] !~ /\A(?:http|nntp|imap)s?\z/i && - $x[1] ne ':') { - shift @x; + while (@x && $pfx !~ m!\A(?: (?:[\+\-]?(?:L|kw):) | + (?:(?:imap|nntp|http)s?:) | + (?:--\w?\z)|(?:-\w?\z) )!x) { + $pfx = pop(@x).$pfx; } - if (@x >= 2) { # qw(https : hostname : 443) or qw(http :) - $re = join('', @x); - } else { # just filter out the flags and hope for the best - $re = join('', grep(!/^-/, @$argv)); - } - $re = quotemeta($re); } + my $re = qr!\A\Q$pfx\E(\Q$cur\E.*)!; my $match_cb = sub { # the "//;" here (for AUTH=ANONYMOUS) interacts badly with # bash tab completion, strip it out for now since our commands # work w/o it. Not sure if there's a better solution... $_[0] =~ s!//;AUTH=ANONYMOUS\@!//!i; - $_[0] =~ s!;!\\;!g; # only return the part specified on the CLI # don't duplicate if already 100% completed - $_[0] =~ /\A$re(\Q$cur\E.*)/ ? ($cur eq $1 ? () : $1) : () + $_[0] =~ $re ? ($cur eq $1 ? () : $1) : () }; - wantarray ? ($re, $cur, $match_cb) : $match_cb; + wantarray ? ($pfx, $cur, $match_cb) : $match_cb; } 1; diff --git a/lib/PublicInbox/LeiForgetExternal.pm b/lib/PublicInbox/LeiForgetExternal.pm index 07f0ac80..39bfc60b 100644 --- a/lib/PublicInbox/LeiForgetExternal.pm +++ b/lib/PublicInbox/LeiForgetExternal.pm @@ -32,14 +32,10 @@ sub lei_forget_external { sub _complete_forget_external { my ($lei, @argv) = @_; my $cfg = $lei->_lei_cfg or return (); - my ($cur, $re, $match_cb) = $lei->complete_url_prepare(\@argv); - # FIXME: bash completion off "http:" or "https:" when the last - # character is a colon doesn't work properly even if we're - # returning "//$HTTP_HOST/$PATH_INFO/", not sure why, could - # be a bash issue. + my ($pfx, $cur, $match_cb) = $lei->complete_url_prepare(\@argv); map { $match_cb->(substr($_, length('external.'))); - } grep(/\Aexternal\.$re\Q$cur/, @{$cfg->{-section_order}}); + } grep(/\Aexternal\.\Q$pfx$cur/, @{$cfg->{-section_order}}); } 1; diff --git a/lib/PublicInbox/LeiImport.pm b/lib/PublicInbox/LeiImport.pm index 2d91e4c4..9053048a 100644 --- a/lib/PublicInbox/LeiImport.pm +++ b/lib/PublicInbox/LeiImport.pm @@ -115,18 +115,24 @@ sub lei_import { # the main "lei import" method sub _complete_import { my ($lei, @argv) = @_; - my ($re, $cur, $match_cb) = $lei->complete_url_prepare(\@argv); - my @k = $lei->url_folder_cache->keys($argv[-1] // undef, 1); + my $has_arg = @argv; + my ($pfx, $cur, $match_cb) = $lei->complete_url_prepare(\@argv); + my @try = $has_arg ? ($pfx.$cur, $argv[-1]) : ($argv[-1]); + push(@try, undef) if defined $try[-1]; + my (@f, @k); + for (@try) { + @k = $lei->url_folder_cache->keys($_, 1) and last; + } my @L = eval { $lei->_lei_store->search->all_terms('L') }; push(@k, map { "+L:$_" } @L); - my @m = map { $match_cb->($_) } @k; - my %f = map { $_ => 1 } (@m ? @m : @k); if (my $lms = $lei->lms) { - @k = $lms->folders($argv[-1] // undef, 1); - @m = map { $match_cb->($_) } @k; - if (@m) { @f{@m} = @m } else { @f{@k} = @k } + for (@try) { + @f = $lms->folders($_, 1) and last; + } + push @k, @f; } - keys %f; + my @m = map { $match_cb->($_) } @k; + @m ? @m : @k; } no warnings 'once'; diff --git a/lib/PublicInbox/LeiMirror.pm b/lib/PublicInbox/LeiMirror.pm index abb68f70..c437b789 100644 --- a/lib/PublicInbox/LeiMirror.pm +++ b/lib/PublicInbox/LeiMirror.pm @@ -413,13 +413,13 @@ sub fgrp_fetch_all { while (my ($osdir, $fgrp_old_new) = each %$todo) { my $f = "$osdir/config"; return if !keep_going($self); - my ($fgrpv, $new) = @$fgrp_old_new; - @$fgrpv = sort { $b->{-sort} <=> $a->{-sort} } @$fgrpv; - push @$fgrpv, @$new; # $new is ordered by references - + my ($old, $new) = @$fgrp_old_new; + @$old = sort { $b->{-sort} <=> $a->{-sort} } @$old; + # $new is ordered by {references} my $cmd = ['git', "--git-dir=$osdir", qw(config -f), $f ]; - # clobber group from previous run atomically - for ("remotes.$grp") { # TODO: hideRefs + + # clobber settings from previous run atomically + for ("remotes.$grp", 'fetch.hideRefs') { my $c = [ @$cmd, '--unset-all', $_ ]; $self->{lei}->qerr("# @$c"); next if $self->{dry_run}; @@ -430,7 +430,7 @@ sub fgrp_fetch_all { # permanent configs: my $cfg = PublicInbox::Config->git_config_dump($f); - for my $fgrp (@$fgrpv) { + for my $fgrp (@$old, @$new) { my $u = $fgrp->{-uri} // die 'BUG: no {-uri}'; my $rn = $fgrp->{-remote} // die 'BUG: no {-remote}'; for ("url=$u", "fetch=+refs/*:refs/remotes/$rn/*", @@ -452,14 +452,24 @@ sub fgrp_fetch_all { or die "open($f.lock): $!"; open my $fh, '>>', $f or die "open(>>$f): $!"; $fh->autoflush(1); - my $buf = join('', "[remotes]\n", - map { "\t$grp = $_->{-remote}\n" } @$fgrpv); + my $buf = ''; + if (@$old) { + $buf = "[fetch]\n\thideRefs = refs\n"; + $buf .= join('', map { + "\thideRefs = !refs/remotes/" . + "$_->{-remote}/\n"; + } @$old); + } + $buf .= join('', "[remotes]\n", + (map { "\t$grp = $_->{-remote}\n" } @$old), + (map { "\t$grp = $_->{-remote}\n" } @$new)); print $fh $buf or die "print($f): $!"; close $fh or die "close($f): $!"; unlink("$f.lock") or die "unlink($f.lock): $!"; } - $cmd = [ @git, "--git-dir=$osdir", @fetch, $grp ]; - my $end = PublicInbox::OnDestroy->new($$, \&fgrpv_done, $fgrpv); + $cmd = [ @git, "--git-dir=$osdir", @fetch, $grp ]; + push @$old, @$new; + my $end = PublicInbox::OnDestroy->new($$, \&fgrpv_done, $old); start_cmd($self, $cmd, $opt, $end); } } diff --git a/lib/PublicInbox/LeiQuery.pm b/lib/PublicInbox/LeiQuery.pm index 358574ea..3337e5d4 100644 --- a/lib/PublicInbox/LeiQuery.pm +++ b/lib/PublicInbox/LeiQuery.pm @@ -173,6 +173,8 @@ no query allowed on command-line with --stdin # shell completion helper called by lei__complete sub _complete_q { my ($self, @argv) = @_; + join('', @argv) =~ /\bL:\S*\z/ and + return eval { $self->_lei_store->search->all_terms('L') }; my @cur; my $cb = $self->lazy_cb(qw(forget-external _complete_)); while (@argv) { diff --git a/lib/PublicInbox/LeiSelfSocket.pm b/lib/PublicInbox/LeiSelfSocket.pm index 860020cb..690cda3f 100644 --- a/lib/PublicInbox/LeiSelfSocket.pm +++ b/lib/PublicInbox/LeiSelfSocket.pm @@ -12,20 +12,19 @@ use Data::Dumper; $Data::Dumper::Useqq = 1; # should've been the Perl default :P use PublicInbox::Syscall qw(EPOLLIN); use PublicInbox::Spawn; -my $recv_cmd; +use PublicInbox::IPC; sub new { my ($cls, $r) = @_; my $self = bless { sock => $r }, $cls; $r->blocking(0); no warnings 'once'; - $recv_cmd = $PublicInbox::LEI::recv_cmd; $self->SUPER::new($r, EPOLLIN); } sub event_step { my ($self) = @_; - my (@fds) = $recv_cmd->($self->{sock}, my $buf, 4096 * 33); + my @fds = PublicInbox::IPC::recv_cmd($self->{sock}, my $buf, 4096 * 33); if (scalar(@fds) == 1 && !defined($fds[0])) { return if $!{EAGAIN}; die "recvmsg: $!" unless $!{ECONNRESET}; diff --git a/lib/PublicInbox/LeiStore.pm b/lib/PublicInbox/LeiStore.pm index fce15a72..cf5a03a0 100644 --- a/lib/PublicInbox/LeiStore.pm +++ b/lib/PublicInbox/LeiStore.pm @@ -612,7 +612,7 @@ sub _sto_atexit { # awaitpid cb sub write_prepare { my ($self, $lei) = @_; $lei // die 'BUG: $lei not passed'; - unless ($self->{-ipc_req}) { + unless ($self->{-wq_s1}) { my $dir = $lei->store_path; substr($dir, -length('/lei/store'), 10, ''); pipe(my ($r, $w)) or die "pipe: $!"; |