our @EXPORT_OK = qw(which spawn popen_rd run_die nodatacow_dir);
our @RLIMITS = qw(RLIMIT_CPU RLIMIT_CORE RLIMIT_DATA);
-my $vfork_spawn = <<'VFORK_SPAWN';
+my $all_libc = <<'ALL_LIBC'; # all *nix systems we support
+#include <sys/resource.h>
+#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
-#include <sys/resource.h>
+#include <sys/uio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
}
return (int)pid;
}
-VFORK_SPAWN
+
+#if defined(CMSG_SPACE) && defined(CMSG_LEN)
+#define SEND_FD_CAPA 10
+#define SEND_FD_SPACE (SEND_FD_CAPA * sizeof(int))
+union my_cmsg {
+ struct cmsghdr hdr;
+ char pad[sizeof(struct cmsghdr) + 16 + SEND_FD_SPACE];
+};
+
+SV *send_cmd4(PerlIO *s, SV *svfds, SV *data, int flags)
+{
+ struct msghdr msg = { 0 };
+ union my_cmsg cmsg = { 0 };
+ STRLEN dlen = 0;
+ struct iovec iov;
+ ssize_t sent;
+ AV *fds = (AV *)SvRV(svfds);
+ I32 i, nfds = av_len(fds) + 1;
+ int *fdp;
+
+ if (SvOK(data)) {
+ iov.iov_base = SvPV(data, dlen);
+ iov.iov_len = dlen;
+ }
+ if (!dlen) { /* must be non-zero */
+ iov.iov_base = &msg.msg_namelen; /* whatever */
+ iov.iov_len = 1;
+ }
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ if (nfds) {
+ if (nfds > SEND_FD_CAPA) {
+ fprintf(stderr, "FIXME: bump SEND_FD_CAPA=%d\n", nfds);
+ nfds = SEND_FD_CAPA;
+ }
+ msg.msg_control = &cmsg.hdr;
+ msg.msg_controllen = CMSG_SPACE(nfds * sizeof(int));
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_RIGHTS;
+ cmsg.hdr.cmsg_len = CMSG_LEN(nfds * sizeof(int));
+ fdp = (int *)CMSG_DATA(&cmsg.hdr);
+ for (i = 0; i < nfds; i++) {
+ SV **fd = av_fetch(fds, i, 0);
+ *fdp++ = SvIV(*fd);
+ }
+ }
+ sent = sendmsg(PerlIO_fileno(s), &msg, flags);
+ return sent >= 0 ? newSViv(sent) : &PL_sv_undef;
+}
+
+void recv_cmd4(PerlIO *s, SV *buf, STRLEN n)
+{
+ union my_cmsg cmsg = { 0 };
+ struct msghdr msg = { 0 };
+ struct iovec iov;
+ ssize_t i;
+ Inline_Stack_Vars;
+ Inline_Stack_Reset;
+
+ if (!SvOK(buf))
+ sv_setpvn(buf, "", 0);
+ iov.iov_base = SvGROW(buf, n + 1);
+ iov.iov_len = n;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsg.hdr;
+ msg.msg_controllen = CMSG_SPACE(SEND_FD_SPACE);
+
+ i = recvmsg(PerlIO_fileno(s), &msg, 0);
+ if (i < 0)
+ Inline_Stack_Push(&PL_sv_undef);
+ else
+ SvCUR_set(buf, i);
+ if (i > 0 && cmsg.hdr.cmsg_level == SOL_SOCKET &&
+ cmsg.hdr.cmsg_type == SCM_RIGHTS) {
+ size_t len = cmsg.hdr.cmsg_len;
+ int *fdp = (int *)CMSG_DATA(&cmsg.hdr);
+ for (i = 0; CMSG_LEN((i + 1) * sizeof(int)) <= len; i++)
+ Inline_Stack_Push(sv_2mortal(newSViv(*fdp++)));
+ }
+ Inline_Stack_Done;
+}
+#endif /* defined(CMSG_SPACE) && defined(CMSG_LEN) */
+ALL_LIBC
# btrfs on Linux is copy-on-write (COW) by default. As of Linux 5.7,
# this still leads to fragmentation for SQLite and Xapian files where
( ($ENV{HOME} // '/nonexistent').'/.cache' )
).'/public-inbox/inline-c';
-$set_nodatacow = $vfork_spawn = undef unless -d $inline_dir && -w _;
-if (defined $vfork_spawn) {
+$set_nodatacow = $all_libc = undef unless -d $inline_dir && -w _;
+if (defined $all_libc) {
# Inline 0.64 or later has locking in multi-process env,
# but we support 0.5 on Debian wheezy
use Fcntl qw(:flock);
my $f = "$inline_dir/.public-inbox.lock";
open my $fh, '>', $f or die "failed to open $f: $!\n";
flock($fh, LOCK_EX) or die "LOCK_EX failed on $f: $!\n";
- eval 'use Inline C => $vfork_spawn . $set_nodatacow';
+ eval 'use Inline C => $all_libc.$set_nodatacow';
+ # . ', BUILD_NOISY => 1';
my $err = $@;
my $ndc_err;
if ($err && $set_nodatacow) { # missing Linux kernel headers
$ndc_err = $err;
undef $set_nodatacow;
- eval 'use Inline C => $vfork_spawn';
+ eval 'use Inline C => $all_libc';
}
flock($fh, LOCK_UN) or die "LOCK_UN failed on $f: $!\n";
die $err if $err;
};
if ($@) {
warn "Inline::C failed for vfork: $@\n";
- $set_nodatacow = $vfork_spawn = undef;
+ $set_nodatacow = $all_libc = undef;
}
}
-unless (defined $vfork_spawn) {
+unless ($all_libc) {
require PublicInbox::SpawnPP;
*pi_fork_exec = \&PublicInbox::SpawnPP::pi_fork_exec
}
*nodatacow_fd = \&PublicInbox::NDC_PP::nodatacow_fd;
*nodatacow_dir = \&PublicInbox::NDC_PP::nodatacow_dir;
}
+
undef $set_nodatacow;
-undef $vfork_spawn;
+undef $all_libc;
sub which ($) {
my ($file) = @_;
return $file if index($file, '/') >= 0;
- foreach my $p (split(':', $ENV{PATH})) {
+ for my $p (split(/:/, $ENV{PATH})) {
$p .= "/$file";
return $p if -x $p;
}
sub spawn ($;$$) {
my ($cmd, $env, $opts) = @_;
- my $f = which($cmd->[0]);
- defined $f or die "$cmd->[0]: command not found\n";
+ my $f = which($cmd->[0]) // die "$cmd->[0]: command not found\n";
my @env;
$opts ||= {};
for my $child_fd (0..2) {
my $parent_fd = $opts->{$child_fd};
if (defined($parent_fd) && $parent_fd !~ /\A[0-9]+\z/) {
- defined(my $fd = fileno($parent_fd)) or
+ my $fd = fileno($parent_fd) //
die "$parent_fd not an IO GLOB? $!";
$parent_fd = $fd;
}
my $rlim = [];
foreach my $l (@RLIMITS) {
- defined(my $v = $opts->{$l}) or next;
+ my $v = $opts->{$l} // next;
my $r = eval "require BSD::Resource; BSD::Resource::$l();";
unless (defined $r) {
warn "$l undefined by BSD::Resource: $@\n";
}
sub popen_rd {
- my ($cmd, $env, $opts) = @_;
+ my ($cmd, $env, $opt) = @_;
pipe(my ($r, $w)) or die "pipe: $!\n";
- $opts ||= {};
- $opts->{1} = fileno($w);
- my $pid = spawn($cmd, $env, $opts);
+ $opt ||= {};
+ $opt->{1} = fileno($w);
+ my $pid = spawn($cmd, $env, $opt);
return ($r, $pid) if wantarray;
my $ret = gensym;
- tie *$ret, 'PublicInbox::ProcessPipe', $pid, $r;
+ tie *$ret, 'PublicInbox::ProcessPipe', $pid, $r, @$opt{qw(cb arg)};
$ret;
}