-use Socket qw(AF_UNIX SOCK_STREAM pack_sockaddr_un);
-if (my ($sock, $pwd) = eval {
- require IO::FDPass; # will try to use a 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-$<";
+use Socket qw(AF_UNIX SOCK_SEQPACKET MSG_EOR pack_sockaddr_un);
+use PublicInbox::CmdIPC4;
+my $narg = 5;
+my $sock;
+my $recv_cmd = PublicInbox::CmdIPC4->can('recv_cmd4');
+my $send_cmd = PublicInbox::CmdIPC4->can('send_cmd4') // do {
+ my $inline_dir = $ENV{PERL_INLINE_DIRECTORY} //= (
+ $ENV{XDG_CACHE_HOME} //
+ ( ($ENV{HOME} // '/nonexistent').'/.cache' )
+ ).'/public-inbox/inline-c';
+ if (!-d $inline_dir) {
+ require File::Path;
+ File::Path::make_path($inline_dir);
+ }
+ require PublicInbox::Spawn; # takes ~50ms even if built *sigh*
+ $recv_cmd = PublicInbox::Spawn->can('recv_cmd4');
+ PublicInbox::Spawn->can('send_cmd4');
+} // die 'please install Inline::C or Socket::MsgHdr';
+
+my %pids;
+my $sigchld = sub {
+ my $flags = scalar(@_) ? POSIX::WNOHANG() : 0;
+ for my $pid (keys %pids) {
+ delete($pids{$pid}) if waitpid($pid, $flags) == $pid;
+ }
+};
+my @parent;
+my $exec_cmd = sub {
+ my ($fds, $argc, @argv) = @_;
+ my $parent = $$;
+ require POSIX;
+ my @old = (*STDIN{IO}, *STDOUT{IO}, *STDERR{IO});
+ my @rdr;
+ for my $fd (@$fds) {
+ open(my $newfh, '+<&=', $fd) or die "open +<&=$fd: $!";
+ push @rdr, shift(@old), $newfh;
+ }
+ my $do_exec = sub {
+ my @non_std; # ex. $op_p from lei_edit_search
+ while (my ($io, $newfh) = splice(@rdr, 0, 2)) {
+ my $old_io = !!$io;
+ open $io, '+<&', $newfh or die "open +<&=: $!";
+ push @non_std, $io unless $old_io;