-# Copyright (C) 2015-2020 all contributors <meta@public-inbox.org>
+# Copyright (C) 2015-2021 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
use strict;
use warnings;
use Test::More;
use PublicInbox::Spawn qw(which spawn popen_rd);
use PublicInbox::Sigfd;
+use Socket qw(AF_UNIX SOCK_STREAM);
+
+SKIP: {
+ my $recv_3fds = PublicInbox::Spawn->can('recv_3fds');
+ my $send_3fds = PublicInbox::Spawn->can('send_3fds');
+ skip 'Inline::C not enabled', 3 unless $send_3fds && $recv_3fds;
+ my ($s1, $s2);
+ socketpair($s1, $s2, AF_UNIX, SOCK_STREAM, 0) or BAIL_OUT $!;
+ pipe(my ($r, $w)) or BAIL_OUT $!;
+ my @orig = ($r, $w, $s2);
+ my @fd = map { fileno($_) } @orig;
+ ok($send_3fds->(fileno($s1), $fd[0], $fd[1], $fd[2]),
+ 'FDs sent');
+ my (@fds) = $recv_3fds->(fileno($s2));
+ is(scalar(@fds), 3, 'got 3 fds');
+ use Data::Dumper; diag Dumper(\@fds);
+ is(scalar(grep(/\A\d+\z/, @fds)), 3, 'all valid FDs');
+ my $i = 0;
+ my @cmp = map {
+ open my $new, $_, shift(@fds) or BAIL_OUT "open $! $i => $_";
+ ($new, shift(@orig), $i++);
+ } (qw(<&= >&= +<&=));
+ while (my ($new, $old, $fd) = splice(@cmp, 0, 3)) {
+ my @new = stat($new);
+ my @old = stat($old);
+ is("$old[0]\0$old[1]", "$new[0]\0$new[1]",
+ "device/inode matches on received FD:$fd");
+ }
+}
{
my $true = which('true');
isnt($?, 0, '$? set properly: '.$?);
}
+{ # ->CLOSE vs ->DESTROY waitpid caller distinction
+ my @c;
+ my $fh = popen_rd(['true'], undef, { cb => sub { @c = caller } });
+ ok(close($fh), '->CLOSE fired and successful');
+ ok(scalar(@c), 'callback fired by ->CLOSE');
+ ok(grep(!m[/PublicInbox/DS\.pm\z], @c), 'callback not invoked by DS');
+
+ @c = ();
+ $fh = popen_rd(['true'], undef, { cb => sub { @c = caller } });
+ undef $fh; # ->DESTROY
+ ok(scalar(@c), 'callback fired by ->DESTROY');
+ ok(grep(!m[/PublicInbox/ProcessPipe\.pm\z], @c),
+ 'callback not invoked by ProcessPipe');
+}
+
+{ # children don't wait on siblings
+ use POSIX qw(_exit);
+ pipe(my ($r, $w)) or BAIL_OUT $!;
+ my $cb = sub { warn "x=$$\n" };
+ my $fh = popen_rd(['cat'], undef, { 0 => $r, cb => $cb });
+ my $pp = tied *$fh;
+ my $pid = fork // BAIL_OUT $!;
+ local $SIG{__WARN__} = sub { _exit(1) };
+ if ($pid == 0) {
+ local $SIG{__DIE__} = sub { _exit(2) };
+ undef $fh;
+ _exit(0);
+ }
+ waitpid($pid, 0);
+ is($?, 0, 'forked process exited');
+ my @w;
+ local $SIG{__WARN__} = sub { push @w, @_ };
+ close $w;
+ close $fh;
+ is($?, 0, 'cat exited');
+ is_deeply(\@w, [ "x=$$\n" ], 'callback fired from owner');
+}
+
SKIP: {
eval {
require BSD::Resource;