]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/LEI.pm
lei: prefer IO::FDPass over our Inline::C recv_3fds
[public-inbox.git] / lib / PublicInbox / LEI.pm
index 2bc4a9169b2ac1311126b4a47195bf5db1e35afc..f41f63ed126870683e4f93fd499e50c345605053 100644 (file)
@@ -25,6 +25,7 @@ use Text::Wrap qw(wrap);
 use File::Path qw(mkpath);
 use File::Spec;
 our $quit = \&CORE::exit;
+my $recv_3fds;
 my $GLP = Getopt::Long::Parser->new;
 $GLP->configure(qw(gnu_getopt no_ignore_case auto_abbrev));
 my $GLP_PASS = Getopt::Long::Parser->new;
@@ -235,6 +236,7 @@ my %CONFIG_KEYS = (
 
 sub x_it ($$) { # pronounced "exit"
        my ($self, $code) = @_;
+       $self->{1}->autoflush(1); # make sure client sees stdout before exit
        if (my $sig = ($code & 127)) {
                kill($sig, $self->{pid} // $$);
        } else {
@@ -613,26 +615,28 @@ sub accept_dispatch { # Listener {post_accept} callback
        my $self = bless { sock => $sock }, __PACKAGE__;
        vec(my $rin = '', fileno($sock), 1) = 1;
        # `say $sock' triggers "die" in lei(1)
-       for my $i (0..2) {
-               if (select(my $rout = $rin, undef, undef, 1)) {
-                       my $fd = IO::FDPass::recv(fileno($sock));
-                       if ($fd >= 0) {
-                               my $rdr = ($fd == 0 ? '<&=' : '>&=');
+       if (select(my $rout = $rin, undef, undef, 1)) {
+               my @fds = $recv_3fds->(fileno($sock));
+               if (scalar(@fds) == 3) {
+                       my $i = 0;
+                       for my $rdr (qw(<&= >&= >&=)) {
+                               my $fd = shift(@fds);
                                if (open(my $fh, $rdr, $fd)) {
-                                       $self->{$i} = $fh;
-                               } else {
+                                       $self->{$i++} = $fh;
+                               }  else {
                                        say $sock "open($rdr$fd) (FD=$i): $!";
                                        return;
                                }
-                       } else {
-                               say $sock "recv FD=$i: $!";
-                               return;
                        }
                } else {
-                       say $sock "timed out waiting to recv FD=$i";
+                       say $sock "recv_3fds failed: $!";
                        return;
                }
+       } else {
+               say $sock "timed out waiting to recv FDs";
+               return;
        }
+       $self->{2}->autoflush(1); # keep stdout buffered until x_it|DESTROY
        # $ARGV_STR = join("]\0[", @ARGV);
        # $ENV_STR = join('', map { "$_=$ENV{$_}\0" } keys %ENV);
        # $line = "$$\0\0>$ARGV_STR\0\0>$ENV_STR\0\0";
@@ -656,7 +660,7 @@ sub noop {}
 
 # lei(1) calls this when it can't connect
 sub lazy_start {
-       my ($path, $errno) = @_;
+       my ($path, $errno, $nfd) = @_;
        if ($errno == ECONNREFUSED) {
                unlink($path) or die "unlink($path): $!";
        } elsif ($errno != ENOENT) {
@@ -671,7 +675,14 @@ sub lazy_start {
        my $dev_ino_expect = pack('dd', $st[0], $st[1]); # dev+ino
        pipe(my ($eof_r, $eof_w)) or die "pipe: $!";
        my $oldset = PublicInbox::Sigfd::block_signals();
-       require IO::FDPass;
+       if ($nfd == 1) {
+               require IO::FDPass;
+               $recv_3fds = sub { map { IO::FDPass::recv($_[0]) } (0..2) };
+       } elsif ($nfd == 3) {
+               $recv_3fds = PublicInbox::Spawn->can('recv_3fds');
+       }
+       $recv_3fds or die
+               "IO::FDPass missing or Inline::C not installed/configured\n";
        require PublicInbox::Listener;
        require PublicInbox::EOFpipe;
        (-p STDOUT) or die "E: stdout must be a pipe\n";
@@ -770,4 +781,8 @@ sub oneshot {
        }, __PACKAGE__), @ARGV);
 }
 
+# ensures stdout hits the FS before sock disconnects so a client
+# can immediately reread it
+sub DESTROY { $_[0]->{1}->autoflush(1) }
+
 1;