From 2755c6f839f0a0552cd134160e1691380511a61a Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 13 Dec 2020 22:38:48 +0000 Subject: lei: FD-passing and IPC basics The start of lei, a Local Email Interface. It'll support a daemon via FD passing to avoid startup time penalties if IO::FDPass is installed, but fall back to a slow one-shot mode if not. Compared to traditional socket daemon, FD passing should allow us to eventually do stuff like run "git show" and still have proper terminal support for pager and color. --- script/lei | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100755 script/lei (limited to 'script') diff --git a/script/lei b/script/lei new file mode 100755 index 00000000..1b5af3a1 --- /dev/null +++ b/script/lei @@ -0,0 +1,58 @@ +#!perl -w +# Copyright (C) 2020 all contributors +# License: AGPL-3.0+ +use strict; +use v5.10.1; +use Cwd qw(cwd); +use IO::Socket::UNIX; + +if (eval { require IO::FDPass; 1 }) { # use daemon to reduce load time + my $path = do { + my $runtime_dir = ($ENV{XDG_RUNTIME_DIR} // '') . '/lei'; + if ($runtime_dir eq '/lei') { + require File::Spec; + $runtime_dir = File::Spec->tmpdir."/lei-$<"; + } + unless (-d $runtime_dir && -w _) { + require File::Path; + File::Path::mkpath($runtime_dir, 0, 0700); + } + "$runtime_dir/sock"; + }; + my $sock = IO::Socket::UNIX->new(Peer => $path, Type => SOCK_STREAM); + unless ($sock) { # start the daemon if not started + my $err = $!; + require PublicInbox::LeiDaemon; + $err = PublicInbox::LeiDaemon::lazy_start($path, $err); + # try connecting again anyways, unlink+bind may be racy + $sock = IO::Socket::UNIX->new(Peer => $path, + Type => SOCK_STREAM) // die + "connect($path): $! (bind($path): $err)"; + } + my $pwd = $ENV{PWD}; + my $cwd = cwd(); + if ($pwd) { # prefer ENV{PWD} if it's a symlink to real cwd + my @st_cwd = stat($cwd) or die "stat(cwd=$cwd): $!\n"; + my @st_pwd = stat($pwd); + # make sure st_dev/st_ino match for {PWD} to be valid + $pwd = $cwd if (!@st_pwd || $st_pwd[1] != $st_cwd[1] || + $st_pwd[0] != $st_cwd[0]); + } else { + $pwd = $cwd; + } + local $ENV{PWD} = $pwd; + $sock->autoflush(1); + IO::FDPass::send(fileno($sock), $_) for (0..2); + my $buf = "$$\0\0>" . join("]\0[", @ARGV) . "\0\0>"; + while (my ($k, $v) = each %ENV) { $buf .= "$k=$v\0" } + $buf .= "\0\0"; + print $sock $buf or die "print(sock, buf): $!"; + local $/ = "\n"; + while (my $line = <$sock>) { + $line =~ /\Aexit=([0-9]+)\n\z/ and exit($1 + 0); + die $line; + } +} else { # for systems lacking IO::FDPass + require PublicInbox::LeiDaemon; + PublicInbox::LeiDaemon::oneshot(); +} -- cgit v1.2.3-24-ge0c7