From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id E90EC1F934 for ; Thu, 14 Jan 2021 07:06:27 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 01/14] cmd_ipc: support + test EINTR + EAGAIN, no FDs Date: Wed, 13 Jan 2021 19:06:14 -1200 Message-Id: <20210114070627.18195-2-e@80x24.org> In-Reply-To: <20210114070627.18195-1-e@80x24.org> References: <20210114070627.18195-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: We'll ensure our {send,recv}_cmd4 implementations are consistent w.r.t. non-blocking and interrupted sockets. We'll also support receiving messages without FDs associated so we don't have to send dummy FDs to keep receivers from reporting EOF. --- lib/PublicInbox/CmdIPC4.pm | 6 +++--- lib/PublicInbox/Spawn.pm | 13 ++++++++----- t/cmd_ipc.t | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/lib/PublicInbox/CmdIPC4.pm b/lib/PublicInbox/CmdIPC4.pm index c4fcb0d6..c244f6a1 100644 --- a/lib/PublicInbox/CmdIPC4.pm +++ b/lib/PublicInbox/CmdIPC4.pm @@ -18,17 +18,17 @@ no warnings 'once'; my $mh = Socket::MsgHdr->new(buf => $_[2]); $mh->cmsghdr(SOL_SOCKET, SCM_RIGHTS, pack('i' x scalar(@$fds), @$fds)); - Socket::MsgHdr::sendmsg($sock, $mh, $flags) or die "sendmsg: $!"; + Socket::MsgHdr::sendmsg($sock, $mh, $flags); }; *recv_cmd4 = sub ($$$) { my ($s, undef, $len) = @_; # $_[1] = destination buffer my $mh = Socket::MsgHdr->new(buflen => $len, controllen => 256); - my $r = Socket::MsgHdr::recvmsg($s, $mh, 0) // die "recvmsg: $!"; + my $r = Socket::MsgHdr::recvmsg($s, $mh, 0) // return ($_[1] = undef); $_[1] = $mh->buf; return () if $r == 0; my (undef, undef, $data) = $mh->cmsghdr; - unpack('i' x (length($data) / 4), $data); + defined($data) ? unpack('i' x (length($data) / 4), $data) : (); }; } } # /eval /BEGIN diff --git a/lib/PublicInbox/Spawn.pm b/lib/PublicInbox/Spawn.pm index ef822e1b..e5c0b1e9 100644 --- a/lib/PublicInbox/Spawn.pm +++ b/lib/PublicInbox/Spawn.pm @@ -216,12 +216,13 @@ union my_cmsg { char pad[sizeof(struct cmsghdr) + 16 + SEND_FD_SPACE]; }; -int send_cmd4(PerlIO *s, SV *svfds, SV *data, int flags) +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; @@ -252,7 +253,8 @@ int send_cmd4(PerlIO *s, SV *svfds, SV *data, int flags) *fdp++ = SvIV(*fd); } } - return sendmsg(PerlIO_fileno(s), &msg, flags) >= 0; + sent = sendmsg(PerlIO_fileno(s), &msg, flags); + return sent >= 0 ? newSViv(sent) : &PL_sv_undef; } void recv_cmd4(PerlIO *s, SV *buf, STRLEN n) @@ -260,7 +262,7 @@ void recv_cmd4(PerlIO *s, SV *buf, STRLEN n) union my_cmsg cmsg = { 0 }; struct msghdr msg = { 0 }; struct iovec iov; - size_t i; + ssize_t i; Inline_Stack_Vars; Inline_Stack_Reset; @@ -275,8 +277,9 @@ void recv_cmd4(PerlIO *s, SV *buf, STRLEN n) i = recvmsg(PerlIO_fileno(s), &msg, 0); if (i < 0) - croak("recvmsg: %s", strerror(errno)); - SvCUR_set(buf, i); + 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; diff --git a/t/cmd_ipc.t b/t/cmd_ipc.t index 0a0a4e00..96510175 100644 --- a/t/cmd_ipc.t +++ b/t/cmd_ipc.t @@ -10,6 +10,7 @@ pipe(my ($r, $w)) or BAIL_OUT; my ($send, $recv); require_ok 'PublicInbox::Spawn'; my $SOCK_SEQPACKET = eval { Socket::SOCK_SEQPACKET() } // undef; +use Time::HiRes qw(alarm); my $do_test = sub { SKIP: { my ($type, $flag, $desc) = @_; @@ -45,11 +46,42 @@ my $do_test = sub { SKIP: { is($buf, (',' x 1023) . '-', 'silently truncated buf'); $opens->(); $r1 = $w1 = $s1a = undef; + + $s2->blocking(0); + @fds = $recv->($s2, $buf, length($src) + 1); + ok($!{EAGAIN}, "EAGAIN set by ($desc)"); + is_deeply(\@fds, [ undef ], "EAGAIN $desc"); + $s2->blocking(1); + + my $alrm = 0; + local $SIG{ALRM} = sub { $alrm++ }; + alarm(0.001); + @fds = $recv->($s2, $buf, length($src) + 1); + ok($!{EINTR}, "EINTR set by ($desc)"); + is_deeply(\@fds, [ undef ], "EINTR $desc"); + is($alrm, 1, 'SIGALRM hit'); + close $s1; @fds = $recv->($s2, $buf, length($src) + 1); is_deeply(\@fds, [], "no FDs on EOF $desc"); is($buf, '', "buffer cleared on EOF ($desc)"); + socketpair($s1, $s2, AF_UNIX, $type, 0) or BAIL_OUT $!; + $s1->blocking(0); + my $nsent = 0; + while (defined(my $n = $send->($s1, $sfds, $src, $flag))) { + $nsent += $n; + fail "sent 0 bytes" if $n == 0; + } + ok($!{EAGAIN}, "hit EAGAIN on send $desc"); + ok($nsent > 0, 'sent some bytes'); + + socketpair($s1, $s2, AF_UNIX, $type, 0) or BAIL_OUT $!; + is($send->($s1, [], $src, $flag), length($src), 'sent w/o FDs'); + $buf = 'nope'; + @fds = $recv->($s2, $buf, length($src)); + is(scalar(@fds), 0, 'no FDs received'); + is($buf, $src, 'recv w/o FDs'); } } };