]> git.ipfire.org Git - thirdparty/public-inbox.git/commitdiff
drop Socket::MsgHdr support
authorEric Wong <e@80x24.org>
Mon, 27 Jul 2026 12:30:05 +0000 (12:30 +0000)
committerEric Wong <e@80x24.org>
Fri, 31 Jul 2026 22:33:03 +0000 (22:33 +0000)
Unfortunately, Socket::MsgHdr::recvmsg fails to set the `flags'
field in the Socket::MsgHdr object according to
`struct msghdr.msg_flags', meaning soft error conditions such
as MSG_TRUNC and MSG_CTRUNC are not detectable when they're
set in the .msg_flags field upon recvmsg(2).

While a patch exists[1] for Socket::MsgHdr, it's uncertain if
upstream will act on it and old versions will exist in distros
for a long time.

Instead, we'll fix a formerly innocuous pack template bug for
64-bit Linux and to allow syscall(SYS_recvmsg, ....) to detect
such a problem.  Inline::C use is probably common for our users,
anyways, and remains an option for now.

[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1142663

Documentation/lei-overview.pod
MANIFEST
lib/PublicInbox/CmdIPC4.pm [deleted file]
lib/PublicInbox/IPC.pm
lib/PublicInbox/LEI.pm
lib/PublicInbox/Syscall.pm
lib/PublicInbox/TestCommon.pm
script/lei
t/cmd_ipc.t
t/lei-daemon.t
xt/check-run.t

index e9a97d64fb56d56be69c0265d0d8195151b3d17b..386c180b97e454a4e45e862e6a2b6128fe375b18 100644 (file)
@@ -127,10 +127,6 @@ L<IO::KQueue> (p5-IO-KQueue on FreeBSD) and L<Linux::Inotify2>
 (liblinux-inotify2-perl and perl-Linux-Inotify2 in .deb and .rpm-based
 distros, respectively) are recommended.
 
-L<Socket::MsgHdr> is optional (libsocket-msghdr-perl in Debian),
-and further improves startup performance.  Its effect is most felt
-when using shell completion.
-
 =head1 BASH COMPLETION
 
 Preliminary Bash completion for lei is provided in
index e041e3fded4596b7f4e9c6d9acc33ea0d64bebe5..7bbe4c4dd970144f18f1b3ba411c493f3c040214 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -174,7 +174,6 @@ lib/PublicInbox/Cgit.pm
 lib/PublicInbox/CidxComm.pm
 lib/PublicInbox/CidxLogP.pm
 lib/PublicInbox/CidxXapHelperAux.pm
-lib/PublicInbox/CmdIPC4.pm
 lib/PublicInbox/CodeSearch.pm
 lib/PublicInbox/CodeSearchIdx.pm
 lib/PublicInbox/Compat.pm
diff --git a/lib/PublicInbox/CmdIPC4.pm b/lib/PublicInbox/CmdIPC4.pm
deleted file mode 100644 (file)
index f78cba6..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright (C) all contributors <meta@public-inbox.org>
-# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
-
-# callers should use PublicInbox::CmdIPC4->can('send_cmd4') (or recv_cmd4)
-# first choice for script/lei front-end and 2nd choice for lei backend
-# libsocket-msghdr-perl is in Debian but not many other distros as of 2021.
-package PublicInbox::CmdIPC4;
-use v5.12;
-use Socket qw(SOL_SOCKET SCM_RIGHTS);
-
-sub sendmsg_retry ($) {
-       return 1 if $!{EINTR};
-       return unless ($!{ENOMEM} || $!{ENOBUFS} || $!{ETOOMANYREFS});
-       return if $_[0]-- == 0;
-       # n.b. `N & (power-of-two - 1)' is a faster `N % power-of-two'
-       warn "# sleeping on sendmsg: $! ($_[0] tries left)\n" if !($_[0] & 15);
-       select(undef, undef, undef, 0.1);
-       1;
-}
-
-sub fd2io (@) { map { open my $fh, '+<&=', $_; $fh } @_ }
-
-BEGIN { eval {
-require Socket::MsgHdr; # XS
-no warnings 'once';
-
-# any number of FDs per-sendmsg(2) + buffer
-*send_cmd4 = sub ($$$$;$) { # (sock, io, buf, flags) = @_;
-       my ($sock, $io, undef, $flags, $tries) = @_;
-       $tries //= -1; # infinite
-       my $mh = Socket::MsgHdr->new(buf => $_[2]);
-       $mh->cmsghdr(SOL_SOCKET, SCM_RIGHTS,
-               pack('i' x scalar(@{$io //= []}), map { fileno $_ } @$io));
-       my $s;
-       do {
-               $s = Socket::MsgHdr::sendmsg($sock, $mh, $flags);
-       } while (!defined($s) && sendmsg_retry($tries));
-       $s;
-};
-
-*recv_cmd4 = sub ($$$) {
-       my ($s, undef, $len) = @_; # $_[1] = destination buffer
-       my $mh = Socket::MsgHdr->new(buflen => $len, controllen => 256);
-       my $r;
-       do {
-               $r = Socket::MsgHdr::recvmsg($s, $mh, 0);
-       } while (!defined($r) && $!{EINTR});
-       if (!defined($r)) {
-               $_[1] = '';
-               return (undef);
-       }
-       $_[1] = $mh->buf;
-       return () if $r == 0;
-       my (undef, undef, $data) = $mh->cmsghdr;
-       defined($data) ? fd2io(unpack('i' x (length($data) / 4), $data)) : ();
-};
-
-} } # /eval /BEGIN
-
-1;
index b64e0bab8089c4349777417a66c7de5f885dc71d..e58c6ae8296f8eb54242d3f20bcca75259c939d6 100644 (file)
@@ -50,15 +50,14 @@ if ($enc && $dec) { # should be custom ops
        *ipc_thaw = \&Storable::thaw;
 }
 
-our $recv_cmd = PublicInbox::Spawn->can('recv_cmd4');
-our $send_cmd = PublicInbox::Spawn->can('send_cmd4') // do {
-       require PublicInbox::CmdIPC4;
-       $recv_cmd //= PublicInbox::CmdIPC4->can('recv_cmd4');
-       PublicInbox::CmdIPC4->can('send_cmd4');
+our ($recv_cmd, $send_cmd);
+do {
+       $recv_cmd = PublicInbox::Spawn->can('recv_cmd4');
+       $send_cmd = PublicInbox::Spawn->can('send_cmd4');
 } // do {
        require PublicInbox::Syscall;
-       $recv_cmd //= PublicInbox::Syscall->can('recv_cmd4');
-       PublicInbox::Syscall->can('send_cmd4');
+       $recv_cmd = PublicInbox::Syscall->can('recv_cmd4');
+       $send_cmd = PublicInbox::Syscall->can('send_cmd4');
 };
 
 sub _get_rec ($) {
index 1d32967b85d2c8787a406baa7da19753535e8c8c..33e307520438bbdf2d57f49d5b5af0b0e9bc56f8 100644 (file)
@@ -1405,7 +1405,7 @@ sub lazy_start {
        local $oldset = PublicInbox::DS::block_signals(POSIX::SIGALRM);
        die "incompatible narg=$narg" if $narg != 5;
        $PublicInbox::IPC::send_cmd or die <<"";
-(Socket::MsgHdr || Inline::C) missing/unconfigured (narg=$narg);
+Inline::C missing/unconfigured (narg=$narg);
 
        require PublicInbox::Listener;
        require PublicInbox::PktOp;
index 4a6ce36a2245dc247283d6935cfbdfdced5e74d6..bcb34f09e27f2ec09008d3c72cdd4632bfd3b52e 100644 (file)
@@ -520,10 +520,21 @@ sub CMSG_LEN ($) { CMSG_ALIGN_SIZEOF_cmsghdr + $_[0] }
 use constant msg_controllen_max =>
        CMSG_SPACE(10 * SIZEOF_int) + SIZEOF_cmsghdr; # space for 10 FDs
 
+sub sendmsg_retry ($) {
+       return 1 if $!{EINTR};
+       return unless ($!{ENOMEM} || $!{ENOBUFS} || $!{ETOOMANYREFS});
+       return if $_[0]-- == 0;
+       # n.b. `N & (power-of-two - 1)' is a faster `N % power-of-two'
+       warn "# sleeping on sendmsg: $! ($_[0] tries left)\n" if !($_[0] & 15);
+       select(undef, undef, undef, 0.1);
+       1;
+}
+
+sub fd2io (@) { map { open my $fh, '+<&=', $_; $fh } @_ }
+
 no warnings 'once';
 
 if (defined($SYS_sendmsg) && defined($SYS_recvmsg)) {
-require PublicInbox::CmdIPC4;
 
 *send_cmd4 = sub ($$$$;$) {
        my ($sock, $io, undef, $flags, $tries) = @_;
@@ -548,7 +559,7 @@ require PublicInbox::CmdIPC4;
        $tries //= -1;
        do {
                $s = syscall($SYS_sendmsg, fileno($sock), $mh, $flags);
-       } while ($s < 0 && PublicInbox::CmdIPC4::sendmsg_retry($tries));
+       } while ($s < 0 && sendmsg_retry($tries));
        $s >= 0 ? $s : undef;
 };
 
@@ -580,8 +591,7 @@ require PublicInbox::CmdIPC4;
                                        $cmsghdr);
                if ($lvl == SOL_SOCKET && $type == SCM_RIGHTS) {
                        $len -= CMSG_ALIGN_SIZEOF_cmsghdr;
-                       @ret = PublicInbox::CmdIPC4::fd2io(
-                                       @fds[0..(($len / SIZEOF_int) - 1)]);
+                       @ret = fd2io(@fds[0..(($len / SIZEOF_int) - 1)]);
                }
        }
        @ret;
index 5910e53e2d6bb70b3529f6e12faf75fc51112204..93cb3a21b0710d69f761a0695ec65b46de2dcaee 100644 (file)
@@ -738,10 +738,9 @@ sub need_scm_rights () {
        state $ok = do {
                        require PublicInbox::Syscall;
                        PublicInbox::Syscall->can('send_cmd4'); # Linux+*BSD
-               } || eval { require Socket::MsgHdr; 1 } ||
-                       PublicInbox::Spawn->can('send_cmd4');
+               } || PublicInbox::Spawn->can('send_cmd4');
        return () if $ok;
-       ('need SCM_RIGHTS support: Socket::MsgHdr OR ' .
+       ('need SCM_RIGHTS support: ' .
         '(syscall numbers + msg_hdr pack templates missing) OR ' .
         'Inline::C unconfigured/missing '.
         '( mkdir -p ~/.cache/public-inbox/inline-c)' );
index 15539e8bcf39d449c1a224e5451448be4c44cfd8..da1a801119c95a2aac976ed5d6e5ebc7e8f1d8cb 100755 (executable)
@@ -3,14 +3,12 @@
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 use v5.12;
 use Socket qw(AF_UNIX SOCK_SEQPACKET pack_sockaddr_un MSG_EOR);
-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 ($sock, $recv_cmd, $send_cmd);
+do {
        require PublicInbox::Syscall;
        $recv_cmd = PublicInbox::Syscall->can('recv_cmd4');
-       PublicInbox::Syscall->can('send_cmd4');
+       $send_cmd = PublicInbox::Syscall->can('send_cmd4');
 } // do {
        my $inline_dir = $ENV{PERL_INLINE_DIRECTORY} //= (
                        $ENV{XDG_CACHE_HOME} //
@@ -22,8 +20,8 @@ my $send_cmd = PublicInbox::CmdIPC4->can('send_cmd4') // do {
        }
        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';
+       $send_cmd = PublicInbox::Spawn->can('send_cmd4');
+} // die 'please install Inline::C';
 
 my %pids;
 my $sigchld = sub {
@@ -100,7 +98,6 @@ lei-daemon could not start, exited with \$?=$?
 connect($path): $! (after attempted daemon start)
 
 }
-# (Socket::MsgHdr|Inline::C), $sock are all available:
 open my $dh, '<', '.' or die "open(.) $!";
 my $buf = join("\0", scalar(@ARGV), @ARGV);
 while (my ($k, $v) = each %ENV) { $buf .= "\0$k=$v" }
index ede4af2abe56b6d8e95c29088ed51de055d60a57..769814937db114ef82920a2f44b8d37ae92ea895 100644 (file)
@@ -116,23 +116,6 @@ SKIP: {
        $do_test->(SOCK_SEQPACKET, MSG_EOR, 'Inline::C seqpacket');
 }
 
-SKIP: {
-       require_mods('Socket::MsgHdr', 13);
-       require_ok 'PublicInbox::CmdIPC4';
-       $send = PublicInbox::CmdIPC4->can('send_cmd4');
-       $recv = PublicInbox::CmdIPC4->can('recv_cmd4');
-       $do_test->(SOCK_STREAM, 0, 'MsgHdr stream');
-       $do_test->(SOCK_SEQPACKET, MSG_EOR, 'MsgHdr seqpacket');
-       SKIP: {
-               ($send_ic && $recv_ic) or
-                       skip 'Inline::C not installed/enabled', 12;
-               $recv = $recv_ic;
-               $do_test->(SOCK_STREAM, 0, 'Inline::C -> MsgHdr stream');
-               $do_test->(SOCK_SEQPACKET,
-                       MSG_EOR, 'Inline::C -> MsgHdr seqpacket');
-       }
-}
-
 SKIP: {
        require_ok 'PublicInbox::Syscall';
        $send = PublicInbox::Syscall->can('send_cmd4') or
index c07a35ede5ea123e388aaa376fa346cc01bc77be..0097c6f0a68c9a6535e2e260120292ae0d8b41f6 100644 (file)
@@ -6,9 +6,6 @@ use Socket qw(AF_UNIX SOCK_SEQPACKET pack_sockaddr_un MSG_EOR);
 
 test_lei({ daemon_only => 1 }, sub {
        my $send_cmd = PublicInbox::Spawn->can('send_cmd4') // do {
-               require PublicInbox::CmdIPC4;
-               PublicInbox::CmdIPC4->can('send_cmd4');
-       } // do {
                require PublicInbox::Syscall;
                PublicInbox::Syscall->can('send_cmd4');
        };
index 162c1acddc0163619493d0c14f5b592cee8850fe..84d09d2003e2a6e5c58b9bbae14767046c40796d 100755 (executable)
@@ -14,6 +14,7 @@ use v5.12;
 use IO::Handle; # ->autoflush
 use PublicInbox::TestCommon;
 use PublicInbox::Spawn;
+use PublicInbox::Syscall;
 eval { require PublicInbox::Lg2 }; # placate FindBin
 use PublicInbox::DS; # already loaded by Spawn via PublicInbox::IO
 use Getopt::Long qw(:config gnu_getopt no_ignore_case auto_abbrev);
@@ -60,7 +61,7 @@ my ($for_destroy, $lei_env, $lei_daemon_pid, $owner_pid);
 # because lei-daemon uses a single inotify FD for all clients.
 if ($ENV{TEST_LEI_DAEMON_PERSIST} && !$ENV{TEST_LEI_DAEMON_PERSIST_DIR} &&
                (PublicInbox::Spawn->can('recv_cmd4') ||
-                       eval { require Socket::MsgHdr })) {
+                       PublicInbox::Syscall->can('recv_cmd4'))) {
        $lei_env = {};
        ($lei_env->{XDG_RUNTIME_DIR}, $for_destroy) = tmpdir;
        $ENV{TEST_LEI_DAEMON_PERSIST_DIR} = $lei_env->{XDG_RUNTIME_DIR};