2 # Copyright (C) 2021 all contributors <meta@public-inbox.org>
3 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
7 use PublicInbox::TestCommon;
8 use Socket qw(AF_UNIX SOCK_STREAM MSG_EOR);
9 pipe(my ($r, $w)) or BAIL_OUT;
11 require_ok 'PublicInbox::Spawn';
12 my $SOCK_SEQPACKET = eval { Socket::SOCK_SEQPACKET() } // undef;
13 use Time::HiRes qw(usleep);
15 my $do_test = sub { SKIP: {
16 my ($type, $flag, $desc) = @_;
17 defined $type or skip 'SOCK_SEQPACKET missing', 7;
19 my $src = 'some payload' x 40;
20 socketpair($s1, $s2, AF_UNIX, $type, 0) or BAIL_OUT $!;
21 my $sfds = [ fileno($r), fileno($w), fileno($s1) ];
22 $send->($s1, $sfds, $src, $flag);
23 my (@fds) = $recv->($s2, my $buf, length($src) + 1);
24 is($buf, $src, 'got buffer payload '.$desc);
27 ok(open($r1, '<&=', $fds[0]), 'opened received $r');
28 ok(open($w1, '>&=', $fds[1]), 'opened received $w');
29 ok(open($s1a, '+>&=', $fds[2]), 'opened received $s1');
34 is("$exp[0]\0$exp[1]", "$cur[0]\0$cur[1]", '$r dev/ino matches');
37 is("$exp[0]\0$exp[1]", "$cur[0]\0$cur[1]", '$w dev/ino matches');
40 is("$exp[0]\0$exp[1]", "$cur[0]\0$cur[1]", '$s1 dev/ino matches');
41 if (defined($SOCK_SEQPACKET) && $type == $SOCK_SEQPACKET) {
42 $r1 = $w1 = $s1a = undef;
43 $src = (',' x 1023) . '-' .('.' x 1024);
44 $send->($s1, $sfds, $src, $flag);
45 (@fds) = $recv->($s2, $buf, 1024);
46 is($buf, (',' x 1023) . '-', 'silently truncated buf');
48 $r1 = $w1 = $s1a = undef;
51 @fds = $recv->($s2, $buf, length($src) + 1);
52 ok($!{EAGAIN}, "EAGAIN set by ($desc)");
53 is_deeply(\@fds, [ undef ], "EAGAIN $desc");
56 if ($ENV{TEST_ALRM}) {
58 local $SIG{ALRM} = sub { $alrm++ };
60 my $pid = fork // xbail "fork: $!";
62 # need to loop since Perl signals are racy
63 # (the interpreter doesn't self-pipe)
64 while (usleep(1000)) {
68 @fds = $recv->($s2, $buf, length($src) + 1);
69 ok($!{EINTR}, "EINTR set by ($desc)");
72 is_deeply(\@fds, [ undef ], "EINTR $desc");
73 ok($alrm, 'SIGALRM hit');
77 @fds = $recv->($s2, $buf, length($src) + 1);
78 is_deeply(\@fds, [], "no FDs on EOF $desc");
79 is($buf, '', "buffer cleared on EOF ($desc)");
81 socketpair($s1, $s2, AF_UNIX, $type, 0) or BAIL_OUT $!;
84 while (defined(my $n = $send->($s1, $sfds, $src, $flag))) {
86 fail "sent 0 bytes" if $n == 0;
88 ok($!{EAGAIN} || $!{ETOOMANYREFS},
89 "hit EAGAIN || ETOOMANYREFS on send $desc") or
90 diag "send failed with: $!";
91 ok($nsent > 0, 'sent some bytes');
93 socketpair($s1, $s2, AF_UNIX, $type, 0) or BAIL_OUT $!;
94 is($send->($s1, [], $src, $flag), length($src), 'sent w/o FDs');
96 @fds = $recv->($s2, $buf, length($src));
97 is(scalar(@fds), 0, 'no FDs received');
98 is($buf, $src, 'recv w/o FDs');
100 my $nr = 2 * 1024 * 1024;
102 vec(my $vec = '', $nr * 8 - 1, 1) = 1;
103 my $n = $send->($s1, [], $vec, $flag);
105 $n == length($vec) or
106 fail "short send: $n != ".length($vec);
107 diag "sent $nr, retrying with more";
108 $nr += 2 * 1024 * 1024;
110 ok($!{EMSGSIZE} || $!{ENOBUFS},
111 'got EMSGSIZE or ENOBUFS') or
112 diag "$nr bytes fails with: $!";
119 my $send_ic = PublicInbox::Spawn->can('send_cmd4');
120 my $recv_ic = PublicInbox::Spawn->can('recv_cmd4');
122 ($send_ic && $recv_ic) or skip 'Inline::C not installed/enabled', 12;
125 $do_test->(SOCK_STREAM, 0, 'Inline::C stream');
126 $do_test->($SOCK_SEQPACKET, MSG_EOR, 'Inline::C seqpacket');
130 require_mods('Socket::MsgHdr', 13);
131 require_ok 'PublicInbox::CmdIPC4';
132 $send = PublicInbox::CmdIPC4->can('send_cmd4');
133 $recv = PublicInbox::CmdIPC4->can('recv_cmd4');
134 $do_test->(SOCK_STREAM, 0, 'MsgHdr stream');
135 $do_test->($SOCK_SEQPACKET, MSG_EOR, 'MsgHdr seqpacket');
137 ($send_ic && $recv_ic) or
138 skip 'Inline::C not installed/enabled', 12;
140 $do_test->(SOCK_STREAM, 0, 'Inline::C -> MsgHdr stream');
141 $do_test->($SOCK_SEQPACKET, 0, 'Inline::C -> MsgHdr seqpacket');