about summary refs log tree commit homepage
path: root/script
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2021-01-10 12:15:02 +0000
committerEric Wong <e@80x24.org>2021-01-12 03:51:42 +0000
commitc17c44d9e0ef28f0f0521656f335f836ad8b7754 (patch)
tree32ee54ba703a76adbdb91beeba761f18a3d0884b /script
parenta7e6a8cd68fb6d700337d8dbc7ee2c65ff3d2fc1 (diff)
downloadpublic-inbox-c17c44d9e0ef28f0f0521656f335f836ad8b7754.tar.gz
For another step in in syscall reduction, we'll support
transferring 3 FDs and a buffer with a single sendmsg/recvmsg
syscall using Socket::MsgHdr if available.

Beyond script/lei itself, this will be used for internal IPC
between search backends (perhaps with SOCK_SEQPACKET).  There's
a chance this could make it to the public-facing daemons, too.

This adds an optional dependency on the Socket::MsgHdr package,
available as libsocket-msghdr-perl on Debian-based distros
(but not CentOS 7.x and FreeBSD 11.x, at least).

Our Inline::C version in PublicInbox::Spawn remains the last
choice for script/lei due to the high startup time, and
IO::FDPass remains supported for non-Debian distros.

Since the socket name prefix changes from 3 to 4, we'll also
take this opportunity to make the argv+env buffer transfer less
error-prone by relying on argc instead of designated delimiters.
Diffstat (limited to 'script')
-rwxr-xr-xscript/lei53
1 files changed, 26 insertions, 27 deletions
diff --git a/script/lei b/script/lei
index 2ea98da4..d954b9eb 100755
--- a/script/lei
+++ b/script/lei
@@ -4,17 +4,20 @@
 use strict;
 use v5.10.1;
 use Socket qw(AF_UNIX SOCK_STREAM pack_sockaddr_un);
-my ($send_3fds, $nfd);
-if (my ($sock, $pwd) = eval {
-        $send_3fds = eval {
-                require IO::FDPass;
-                $nfd = 1; # 1 FD per-sendmsg
-                sub { IO::FDPass::send($_[0], $_[$_]) for (1..3) }
-        } // do {
-                require PublicInbox::Spawn; # takes ~50ms even if built *sigh*
-                $nfd = 3; # 3 FDs per-sendmsg(2)
-                PublicInbox::Spawn->can('send_3fds');
-        } // die "IO::FDPass missing or Inline::C not installed/configured\n";
+use PublicInbox::CmdIPC4;
+my $narg = 4;
+my $send_cmd = PublicInbox::CmdIPC4->can('send_cmd4') // do {
+        require PublicInbox::CmdIPC1; # 2nd choice
+        $narg = 1;
+        PublicInbox::CmdIPC1->can('send_cmd1');
+} // do {
+        require PublicInbox::Spawn; # takes ~50ms even if built *sigh*
+        $narg = 4;
+        PublicInbox::Spawn->can('send_cmd4');
+};
+
+my ($sock, $pwd);
+if ($send_cmd && eval {
         my $path = do {
                 my $runtime_dir = ($ENV{XDG_RUNTIME_DIR} // '') . '/lei';
                 if ($runtime_dir eq '/lei') {
@@ -25,29 +28,27 @@ if (my ($sock, $pwd) = eval {
                         require File::Path;
                         File::Path::mkpath($runtime_dir, 0, 0700);
                 }
-                "$runtime_dir/$nfd.sock";
+                "$runtime_dir/$narg.sock";
         };
         my $addr = pack_sockaddr_un($path);
-        socket(my $sock, AF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
+        socket($sock, AF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
         unless (connect($sock, $addr)) { # start the daemon if not started
                 local $ENV{PERL5LIB} = join(':', @INC);
                 open(my $daemon, '-|', $^X, qw[-MPublicInbox::LEI
                         -E PublicInbox::LEI::lazy_start(@ARGV)],
-                        $path, $! + 0, $nfd) or die "popen: $!";
+                        $path, $! + 0, $narg) or die "popen: $!";
                 while (<$daemon>) { warn $_ } # EOF when STDERR is redirected
                 close($daemon) or warn <<"";
 lei-daemon could not start, exited with \$?=$?
 
                 # try connecting again anyways, unlink+bind may be racy
-                unless (connect($sock, $addr)) {
-                        die <<"";
+                connect($sock, $addr) or die <<"";
 connect($path): $! (after attempted daemon start)
 Falling back to (slow) one-shot mode
 
-                }
         }
         require Cwd;
-        my $pwd = $ENV{PWD} // '';
+        $pwd = $ENV{PWD} // '';
         my $cwd = Cwd::fastcwd() // die "fastcwd(PWD=$pwd): $!";
         if ($pwd ne $cwd) { # prefer ENV{PWD} if it's a symlink to real cwd
                 my @st_cwd = stat($cwd) or die "stat(cwd=$cwd): $!";
@@ -58,23 +59,21 @@ Falling back to (slow) one-shot mode
         } else {
                 $pwd = $cwd;
         }
-        ($sock, $pwd);
-}) { # IO::FDPass, $sock, $pwd are all available:
+        1;
+}) { # (Socket::MsgHdr|IO::FDPass|Inline::C), $sock, $pwd are all available:
         local $ENV{PWD} = $pwd;
-        my $buf = "$$\0\0>" . join("]\0[", @ARGV) . "\0\0>";
-        while (my ($k, $v) = each %ENV) { $buf .= "$k=$v\0" }
+        my $buf = join("\0", $$, scalar(@ARGV), @ARGV);
+        while (my ($k, $v) = each %ENV) { $buf .= "\0$k=$v" }
         $buf .= "\0\0";
         select $sock;
         $| = 1; # unbuffer selected $sock
-        $send_3fds->(fileno($sock), 0, 1, 2);
-        print $sock $buf or die "print(sock, buf): $!";
+        $send_cmd->($sock, 0, 1, 2, $buf, 0);
         while ($buf = <$sock>) {
                 $buf =~ /\Aexit=([0-9]+)\n\z/ and exit($1 + 0);
                 die $buf;
         }
-} else { # for systems lacking IO::FDPass
-        # don't warn about IO::FDPass since it's not commonly installed
-        warn $@ if $@ && index($@, 'IO::FDPass') < 0;
+} else { # for systems lacking Socket::MsgHdr, IO::FDPass or Inline::C
+        warn $@ if $@;
         require PublicInbox::LEI;
         PublicInbox::LEI::oneshot(__PACKAGE__);
 }