diff options
-rwxr-xr-x | devel/sysdefs-list | 9 | ||||
-rw-r--r-- | lib/PublicInbox/Syscall.pm | 102 | ||||
-rw-r--r-- | t/cmd_ipc.t | 9 |
3 files changed, 74 insertions, 46 deletions
diff --git a/devel/sysdefs-list b/devel/sysdefs-list index 61532cf2..ba51de6c 100755 --- a/devel/sysdefs-list +++ b/devel/sysdefs-list @@ -2,8 +2,6 @@ # License: AGPL-3.0+ <http://www.gnu.org/licenses/agpl-3.0.txt> # Dump system-specific constant numbers this is to maintain # PublicInbox::Syscall and any other system-specific pieces. -# DO NOT USE syscall numbers for *BSDs, none of the current BSD kernels -# we know about promise stable syscall numbers (unlike Linux). # However, sysconf(3) constants are stable ABI on all safe to dump. eval 'exec perl -S $0 ${1+"$@"}' # no shebang if 0; # running under some shell @@ -179,5 +177,12 @@ int main(void) PR_NUM(cmsg_type); STRUCT_END; + { + struct cmsghdr cmsg; + uintptr_t cmsg_data_off; + cmsg_data_off = (uintptr_t)CMSG_DATA(&cmsg) - (uintptr_t)&cmsg; + D(cmsg_data_off); + } + return 0; } diff --git a/lib/PublicInbox/Syscall.pm b/lib/PublicInbox/Syscall.pm index 9071e6b1..829cfa3c 100644 --- a/lib/PublicInbox/Syscall.pm +++ b/lib/PublicInbox/Syscall.pm @@ -22,7 +22,7 @@ use POSIX qw(ENOENT ENOSYS EINVAL O_NONBLOCK); use Socket qw(SOL_SOCKET SCM_RIGHTS); use Config; our %SIGNUM = (WINCH => 28); # most Linux, {Free,Net,Open}BSD, *Darwin -our $INOTIFY; +our ($INOTIFY, %PACK); # $VERSION = '0.25'; # Sys::Syscall version our @EXPORT_OK = qw(epoll_ctl epoll_create epoll_wait @@ -44,26 +44,21 @@ use constant { EPOLL_CTL_MOD => 3, SIZEOF_int => $Config{intsize}, SIZEOF_size_t => $Config{sizesize}, + SIZEOF_ptr => $Config{ptrsize}, NUL => "\0", }; -use constant { - TMPL_size_t => SIZEOF_size_t == 8 ? 'Q' : 'L', - BYTES_4_hole => SIZEOF_size_t == 8 ? 'L' : '', - # cmsg_len, cmsg_level, cmsg_type - SIZEOF_cmsghdr => SIZEOF_int * 2 + SIZEOF_size_t, -}; - -my @BYTES_4_hole = BYTES_4_hole ? (0) : (); +use constant TMPL_size_t => SIZEOF_size_t == 8 ? 'Q' : 'L'; our ($SYS_epoll_create, $SYS_epoll_ctl, $SYS_epoll_wait, $SYS_signalfd4, $SYS_renameat2, - $F_SETPIPE_SZ); + $F_SETPIPE_SZ, + $SYS_sendmsg, + $SYS_recvmsg); -my ($SYS_sendmsg, $SYS_recvmsg); my $SYS_fstatfs; # don't need fstatfs64, just statfs.f_type my ($FS_IOC_GETFLAGS, $FS_IOC_SETFLAGS); my $SFD_CLOEXEC = 02000000; # Perl does not expose O_CLOEXEC @@ -78,7 +73,7 @@ if ($^O eq "linux") { # boundaries. my $u64_mod_8 = 0; - if ($Config{ptrsize} == 4) { + if (SIZEOF_ptr == 4) { # if we're running on an x86_64 kernel, but a 32-bit process, # we need to use the x32 or i386 syscall numbers. if ($machine eq 'x86_64') { @@ -281,16 +276,52 @@ EOM *epoll_wait = \&epoll_wait_mod4; *epoll_ctl = \&epoll_ctl_mod4; } +} elsif ($^O =~ /\A(?:freebsd|openbsd|netbsd|dragonfly)\z/) { +# don't use syscall.ph here, name => number mappings are not stable on *BSD +# but the actual numbers are. +# OpenBSD perl redirects syscall perlop to libc functions +# https://cvsweb.openbsd.org/src/gnu/usr.bin/perl/gen_syscall_emulator.pl +# https://www.netbsd.org/docs/internals/en/chap-processes.html#syscall_versioning +# https://wiki.freebsd.org/AddingSyscalls#Backward_compatibily +# (I'm assuming Dragonfly copies FreeBSD, here, too) + $SYS_recvmsg = 27; + $SYS_sendmsg = 28; +} + +BEGIN { + if ($^O eq 'linux') { + %PACK = ( + TMPL_cmsg_len => TMPL_size_t, + # cmsg_len, cmsg_level, cmsg_type + SIZEOF_cmsghdr => SIZEOF_int * 2 + SIZEOF_size_t, + CMSG_DATA_off => '', + TMPL_msghdr => 'PL' . # msg_name, msg_namelen + '@'.(2 * SIZEOF_ptr).'P'. # msg_iov + 'i'. # msg_iovlen + '@'.(4 * SIZEOF_ptr).'P'. # msg_control + 'L'. # msg_controllen (socklen_t) + 'i', # msg_flags + ); + } elsif ($^O =~ /\A(?:freebsd|openbsd|netbsd|dragonfly)\z/) { + %PACK = ( + TMPL_cmsg_len => 'L', # socklen_t + SIZEOF_cmsghdr => SIZEOF_int * 3, + CMSG_DATA_off => SIZEOF_ptr == 8 ? '@16' : '', + TMPL_msghdr => 'PL' . # msg_name, msg_namelen + '@'.(2 * SIZEOF_ptr).'P'. # msg_iov + TMPL_size_t. # msg_iovlen + '@'.(4 * SIZEOF_ptr).'P'. # msg_control + TMPL_size_t. # msg_controllen + 'i', # msg_flags + + ) + } + $PACK{CMSG_ALIGN_size} = SIZEOF_size_t; } # SFD_CLOEXEC is arch-dependent, so IN_CLOEXEC may be, too $INOTIFY->{IN_CLOEXEC} //= 0x80000 if $INOTIFY; -# use Inline::C for *BSD-only or general POSIX stuff. -# Linux guarantees stable syscall numbering, BSDs only offer a stable libc -# use devel/sysdefs-list on Linux to detect new syscall numbers and -# other system constants - sub epoll_create { syscall($SYS_epoll_create, $no_deprecated ? 0 : 100); } @@ -420,11 +451,13 @@ sub nodatacow_dir { if (open my $fh, '<', $_[0]) { nodatacow_fh($fh) } } -sub CMSG_ALIGN ($) { ($_[0] + SIZEOF_size_t - 1) & ~(SIZEOF_size_t - 1) } +use constant \%PACK; +sub CMSG_ALIGN ($) { ($_[0] + CMSG_ALIGN_size - 1) & ~(CMSG_ALIGN_size - 1) } use constant CMSG_ALIGN_SIZEOF_cmsghdr => CMSG_ALIGN(SIZEOF_cmsghdr); sub CMSG_SPACE ($) { CMSG_ALIGN($_[0]) + CMSG_ALIGN_SIZEOF_cmsghdr } sub CMSG_LEN ($) { CMSG_ALIGN_SIZEOF_cmsghdr + $_[0] } -use constant msg_controllen => CMSG_SPACE(10 * SIZEOF_int) + 16; # 10 FDs +use constant msg_controllen_max => + CMSG_SPACE(10 * SIZEOF_int) + SIZEOF_cmsghdr; # space for 10 FDs if (defined($SYS_sendmsg) && defined($SYS_recvmsg)) { no warnings 'once'; @@ -436,20 +469,15 @@ require PublicInbox::CmdIPC4; $_[2] // NUL, length($_[2] // NUL) || 1); my $fd_space = scalar(@$fds) * SIZEOF_int; my $msg_controllen = CMSG_SPACE($fd_space); - my $cmsghdr = pack(TMPL_size_t . # cmsg_len + my $cmsghdr = pack(TMPL_cmsg_len . 'LL' . # cmsg_level, cmsg_type, - ('i' x scalar(@$fds)) . # CMSG_DATA + CMSG_DATA_off.('i' x scalar(@$fds)). # CMSG_DATA '@'.($msg_controllen - 1).'x1', # pad to space, not len CMSG_LEN($fd_space), # cmsg_len SOL_SOCKET, SCM_RIGHTS, # cmsg_{level,type} @$fds); # CMSG_DATA - my $mh = pack('PL' . # msg_name, msg_namelen (socklen_t (U32)) - BYTES_4_hole . # 4-byte padding on 64-bit - 'P'.TMPL_size_t . # msg_iov, msg_iovlen, - 'P'.TMPL_size_t . # msg_control, msg_controllen, - 'i', # msg_flags - NUL, 0, # msg_name, msg_namelen (unused) - @BYTES_4_hole, + my $mh = pack(TMPL_msghdr, + undef, 0, # msg_name, msg_namelen (unused) $iov, 1, # msg_iov, msg_iovlen $cmsghdr, # msg_control $msg_controllen, @@ -465,18 +493,13 @@ require PublicInbox::CmdIPC4; *recv_cmd4 = sub ($$$) { my ($sock, undef, $len) = @_; vec($_[1] //= '', $len - 1, 8) = 0; - my $cmsghdr = "\0" x msg_controllen; # 10 * sizeof(int) + my $cmsghdr = "\0" x msg_controllen_max; # 10 * sizeof(int) my $iov = pack('P'.TMPL_size_t, $_[1], $len); - my $mh = pack('PL' . # msg_name, msg_namelen (socklen_t (U32)) - BYTES_4_hole . # 4-byte padding on 64-bit - 'P'.TMPL_size_t . # msg_iov, msg_iovlen, - 'P'.TMPL_size_t . # msg_control, msg_controllen, - 'i', # msg_flags - NUL, 0, # msg_name, msg_namelen (unused) - @BYTES_4_hole, + my $mh = pack(TMPL_msghdr, + undef, 0, # msg_name, msg_namelen (unused) $iov, 1, # msg_iov, msg_iovlen $cmsghdr, # msg_control - msg_controllen, + msg_controllen_max, 0); # msg_flags my $r; do { @@ -489,8 +512,9 @@ require PublicInbox::CmdIPC4; substr($_[1], $r, length($_[1]), ''); my @ret; if ($r > 0) { - my ($len, $lvl, $type, @fds) = unpack(TMPL_size_t . # cmsg_len - 'LLi*', # cmsg_level, cmsg_type, @fds + my ($len, $lvl, $type, @fds) = unpack(TMPL_cmsg_len. + 'LL'. # cmsg_level, cmsg_type + CMSG_DATA_off.'i*', # @fds $cmsghdr); if ($lvl == SOL_SOCKET && $type == SCM_RIGHTS) { $len -= CMSG_ALIGN_SIZEOF_cmsghdr; diff --git a/t/cmd_ipc.t b/t/cmd_ipc.t index 08a4dcc3..c973c6f0 100644 --- a/t/cmd_ipc.t +++ b/t/cmd_ipc.t @@ -143,14 +143,13 @@ SKIP: { } SKIP: { - skip 'not Linux', 1 if $^O ne 'linux'; require_ok 'PublicInbox::Syscall'; $send = PublicInbox::Syscall->can('send_cmd4') or - skip 'send_cmd4 not defined for arch', 1; + skip "send_cmd4 not defined for $^O arch", 1; $recv = PublicInbox::Syscall->can('recv_cmd4') or - skip 'recv_cmd4 not defined for arch', 1; - $do_test->(SOCK_STREAM, 0, 'PP Linux stream'); - $do_test->(SOCK_SEQPACKET, 0, 'PP Linux seqpacket'); + skip "recv_cmd4 not defined for $^O arch", 1; + $do_test->(SOCK_STREAM, 0, 'pure Perl stream'); + $do_test->(SOCK_SEQPACKET, 0, 'pure Perl seqpacket'); } done_testing; |