]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/Syscall.pm
cf7004548684c857607b7a46ac8a726d5733a932
[public-inbox.git] / lib / PublicInbox / Syscall.pm
1 # This is a fork of the (for now) unmaintained Sys::Syscall 0.25,
2 # specifically the Debian libsys-syscall-perl 0.25-6 version to
3 # fix upstream regressions in 0.25.
4 #
5 # This license differs from the rest of public-inbox
6 #
7 # This module is Copyright (c) 2005 Six Apart, Ltd.
8 # Copyright (C) 2019 all contributors <meta@public-inbox.org>
9 #
10 # All rights reserved.
11 #
12 # You may distribute under the terms of either the GNU General Public
13 # License or the Artistic License, as specified in the Perl README file.
14 package PublicInbox::Syscall;
15 use strict;
16 use POSIX qw(ENOSYS SEEK_CUR);
17 use Config;
18
19 require Exporter;
20 use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $VERSION);
21
22 $VERSION     = "0.25";
23 @ISA         = qw(Exporter);
24 @EXPORT_OK   = qw(sendfile epoll_ctl epoll_create epoll_wait
25                   EPOLLIN EPOLLOUT EPOLLERR EPOLLHUP EPOLLRDBAND
26                   EPOLL_CTL_ADD EPOLL_CTL_DEL EPOLL_CTL_MOD);
27 %EXPORT_TAGS = (epoll => [qw(epoll_ctl epoll_create epoll_wait
28                              EPOLLIN EPOLLOUT EPOLLERR EPOLLHUP EPOLLRDBAND
29                              EPOLL_CTL_ADD EPOLL_CTL_DEL EPOLL_CTL_MOD)],
30                 sendfile => [qw(sendfile)],
31                 );
32
33 use constant EPOLLIN       => 1;
34 use constant EPOLLOUT      => 4;
35 use constant EPOLLERR      => 8;
36 use constant EPOLLHUP      => 16;
37 use constant EPOLLRDBAND   => 128;
38 use constant EPOLL_CTL_ADD => 1;
39 use constant EPOLL_CTL_DEL => 2;
40 use constant EPOLL_CTL_MOD => 3;
41
42 our $loaded_syscall = 0;
43
44 sub _load_syscall {
45     # props to Gaal for this!
46     return if $loaded_syscall++;
47     my $clean = sub {
48         delete @INC{qw<syscall.ph asm/unistd.ph bits/syscall.ph
49                         _h2ph_pre.ph sys/syscall.ph>};
50     };
51     $clean->(); # don't trust modules before us
52     my $rv = eval { require 'syscall.ph'; 1 } || eval { require 'sys/syscall.ph'; 1 };
53     $clean->(); # don't require modules after us trust us
54     return $rv;
55 }
56
57 our ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();
58
59 our (
60      $SYS_epoll_create,
61      $SYS_epoll_ctl,
62      $SYS_epoll_wait,
63      $SYS_sendfile,
64      $SYS_readahead,
65      );
66
67 our $no_deprecated = 0;
68
69 if ($^O eq "linux") {
70     # whether the machine requires 64-bit numbers to be on 8-byte
71     # boundaries.
72     my $u64_mod_8 = 0;
73
74     # if we're running on an x86_64 kernel, but a 32-bit process,
75     # we need to use the i386 syscall numbers.
76     if ($machine eq "x86_64" && $Config{ptrsize} == 4) {
77         $machine = "i386";
78     }
79
80     # Similarly for mips64 vs mips
81     if ($machine eq "mips64" && $Config{ptrsize} == 4) {
82         $machine = "mips";
83     }
84
85     if ($machine =~ m/^i[3456]86$/) {
86         $SYS_epoll_create = 254;
87         $SYS_epoll_ctl    = 255;
88         $SYS_epoll_wait   = 256;
89         $SYS_sendfile     = 187;  # or 64: 239
90         $SYS_readahead    = 225;
91     } elsif ($machine eq "x86_64") {
92         $SYS_epoll_create = 213;
93         $SYS_epoll_ctl    = 233;
94         $SYS_epoll_wait   = 232;
95         $SYS_sendfile     =  40;
96         $SYS_readahead    = 187;
97     } elsif ($machine =~ m/^parisc/) {
98         $SYS_epoll_create = 224;
99         $SYS_epoll_ctl    = 225;
100         $SYS_epoll_wait   = 226;
101         $SYS_sendfile     = 122;  # sys_sendfile64=209
102         $SYS_readahead    = 207;
103         $u64_mod_8        = 1;
104     } elsif ($machine =~ m/^ppc64/) {
105         $SYS_epoll_create = 236;
106         $SYS_epoll_ctl    = 237;
107         $SYS_epoll_wait   = 238;
108         $SYS_sendfile     = 186;  # (sys32_sendfile).  sys32_sendfile64=226  (64 bit processes: sys_sendfile64=186)
109         $SYS_readahead    = 191;  # both 32-bit and 64-bit vesions
110         $u64_mod_8        = 1;
111     } elsif ($machine eq "ppc") {
112         $SYS_epoll_create = 236;
113         $SYS_epoll_ctl    = 237;
114         $SYS_epoll_wait   = 238;
115         $SYS_sendfile     = 186;  # sys_sendfile64=226
116         $SYS_readahead    = 191;
117         $u64_mod_8        = 1;
118     } elsif ($machine =~ m/^s390/) {
119         $SYS_epoll_create = 249;
120         $SYS_epoll_ctl    = 250;
121         $SYS_epoll_wait   = 251;
122         $SYS_sendfile     = 187;  # sys_sendfile64=223
123         $SYS_readahead    = 222;
124         $u64_mod_8        = 1;
125     } elsif ($machine eq "ia64") {
126         $SYS_epoll_create = 1243;
127         $SYS_epoll_ctl    = 1244;
128         $SYS_epoll_wait   = 1245;
129         $SYS_sendfile     = 1187;
130         $SYS_readahead    = 1216;
131         $u64_mod_8        = 1;
132     } elsif ($machine eq "alpha") {
133         # natural alignment, ints are 32-bits
134         $SYS_sendfile     = 370;  # (sys_sendfile64)
135         $SYS_epoll_create = 407;
136         $SYS_epoll_ctl    = 408;
137         $SYS_epoll_wait   = 409;
138         $SYS_readahead    = 379;
139         $u64_mod_8        = 1;
140     } elsif ($machine eq "aarch64") {
141         $SYS_epoll_create = 20;  # (sys_epoll_create1)
142         $SYS_epoll_ctl    = 21;
143         $SYS_epoll_wait   = 22;  # (sys_epoll_pwait)
144         $SYS_sendfile     = 71;  # (sys_sendfile64)
145         $SYS_readahead    = 213;
146         $u64_mod_8        = 1;
147         $no_deprecated    = 1;
148     } elsif ($machine =~ m/arm(v\d+)?.*l/) {
149         # ARM OABI
150         $SYS_epoll_create = 250;
151         $SYS_epoll_ctl    = 251;
152         $SYS_epoll_wait   = 252;
153         $SYS_sendfile     = 187;
154         $SYS_readahead    = 225;
155         $u64_mod_8        = 1;
156     } elsif ($machine =~ m/^mips64/) {
157         $SYS_sendfile     = 5039;
158         $SYS_epoll_create = 5207;
159         $SYS_epoll_ctl    = 5208;
160         $SYS_epoll_wait   = 5209;
161         $SYS_readahead    = 5179;
162         $u64_mod_8        = 1;
163     } elsif ($machine =~ m/^mips/) {
164         $SYS_sendfile     = 4207;
165         $SYS_epoll_create = 4248;
166         $SYS_epoll_ctl    = 4249;
167         $SYS_epoll_wait   = 4250;
168         $SYS_readahead    = 4223;
169         $u64_mod_8        = 1;
170     } else {
171         # as a last resort, try using the *.ph files which may not
172         # exist or may be wrong
173         _load_syscall();
174         $SYS_epoll_create = eval { &SYS_epoll_create; } || 0;
175         $SYS_epoll_ctl    = eval { &SYS_epoll_ctl;    } || 0;
176         $SYS_epoll_wait   = eval { &SYS_epoll_wait;   } || 0;
177         $SYS_readahead    = eval { &SYS_readahead;    } || 0;
178     }
179
180     if ($u64_mod_8) {
181         *epoll_wait = \&epoll_wait_mod8;
182         *epoll_ctl = \&epoll_ctl_mod8;
183     } else {
184         *epoll_wait = \&epoll_wait_mod4;
185         *epoll_ctl = \&epoll_ctl_mod4;
186     }
187 }
188
189 elsif ($^O eq "freebsd") {
190     if ($ENV{FREEBSD_SENDFILE}) {
191         # this is still buggy and in development
192         $SYS_sendfile = 393;  # old is 336
193     }
194 }
195
196 ############################################################################
197 # sendfile functions
198 ############################################################################
199
200 unless ($SYS_sendfile) {
201     _load_syscall();
202     $SYS_sendfile = eval { &SYS_sendfile; } || 0;
203 }
204
205 sub sendfile_defined { return $SYS_sendfile ? 1 : 0; }
206
207 if ($^O eq "linux" && $SYS_sendfile) {
208     *sendfile = \&sendfile_linux;
209 } elsif ($^O eq "freebsd" && $SYS_sendfile) {
210     *sendfile = \&sendfile_freebsd;
211 } else {
212     *sendfile = \&sendfile_noimpl;
213 }
214
215 sub sendfile_noimpl {
216     $! = ENOSYS;
217     return -1;
218 }
219
220 # C: ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
221 # Perl:  sendfile($write_fd, $read_fd, $max_count) --> $actually_sent
222 sub sendfile_linux {
223     return syscall(
224                    $SYS_sendfile,
225                    $_[0] + 0,  # fd
226                    $_[1] + 0,  # fd
227                    0,          # don't keep track of offset.  callers can lseek and keep track.
228                    $_[2] + 0   # count
229                    );
230 }
231
232 sub sendfile_freebsd {
233     my $offset = POSIX::lseek($_[1]+0, 0, SEEK_CUR) + 0;
234     my $ct = $_[2] + 0;
235     my $sbytes_buf = "\0" x 8;
236     my $rv = syscall(
237                      $SYS_sendfile,
238                      $_[1] + 0,   # fd     (from)
239                      $_[0] + 0,   # socket (to)
240                      $offset,
241                      $ct,
242                      0,           # struct sf_hdtr *hdtr
243                      $sbytes_buf, # off_t *sbytes
244                      0);          # flags
245     return $rv if $rv < 0;
246
247
248     my $set = unpack("L", $sbytes_buf);
249     POSIX::lseek($_[1]+0, SEEK_CUR, $set);
250     return $set;
251 }
252
253
254 ############################################################################
255 # epoll functions
256 ############################################################################
257
258 sub epoll_defined { return $SYS_epoll_create ? 1 : 0; }
259
260 # ARGS: (size) -- but in modern Linux 2.6, the
261 # size doesn't even matter (radix tree now, not hash)
262 sub epoll_create {
263     return -1 unless defined $SYS_epoll_create;
264     my $epfd = eval { syscall($SYS_epoll_create, $no_deprecated ? 0 : ($_[0]||100)+0) };
265     return -1 if $@;
266     return $epfd;
267 }
268
269 # epoll_ctl wrapper
270 # ARGS: (epfd, op, fd, events_mask)
271 sub epoll_ctl_mod4 {
272     syscall($SYS_epoll_ctl, $_[0]+0, $_[1]+0, $_[2]+0, pack("LLL", $_[3], $_[2], 0));
273 }
274 sub epoll_ctl_mod8 {
275     syscall($SYS_epoll_ctl, $_[0]+0, $_[1]+0, $_[2]+0, pack("LLLL", $_[3], 0, $_[2], 0));
276 }
277
278 # epoll_wait wrapper
279 # ARGS: (epfd, maxevents, timeout (milliseconds), arrayref)
280 #  arrayref: values modified to be [$fd, $event]
281 our $epoll_wait_events;
282 our $epoll_wait_size = 0;
283 sub epoll_wait_mod4 {
284     # resize our static buffer if requested size is bigger than we've ever done
285     if ($_[1] > $epoll_wait_size) {
286         $epoll_wait_size = $_[1];
287         $epoll_wait_events = "\0" x 12 x $epoll_wait_size;
288     }
289     my $ct = syscall($SYS_epoll_wait, $_[0]+0, $epoll_wait_events, $_[1]+0, $_[2]+0);
290     for (0..$ct-1) {
291         @{$_[3]->[$_]}[1,0] = unpack("LL", substr($epoll_wait_events, 12*$_, 8));
292     }
293     return $ct;
294 }
295
296 sub epoll_wait_mod8 {
297     # resize our static buffer if requested size is bigger than we've ever done
298     if ($_[1] > $epoll_wait_size) {
299         $epoll_wait_size = $_[1];
300         $epoll_wait_events = "\0" x 16 x $epoll_wait_size;
301     }
302     my $ct;
303     if ($no_deprecated) {
304         $ct = syscall($SYS_epoll_wait, $_[0]+0, $epoll_wait_events, $_[1]+0, $_[2]+0, undef);
305     } else {
306         $ct = syscall($SYS_epoll_wait, $_[0]+0, $epoll_wait_events, $_[1]+0, $_[2]+0);
307     }
308     for (0..$ct-1) {
309         # 16 byte epoll_event structs, with format:
310         #    4 byte mask [idx 1]
311         #    4 byte padding (we put it into idx 2, useless)
312         #    8 byte data (first 4 bytes are fd, into idx 0)
313         @{$_[3]->[$_]}[1,2,0] = unpack("LLL", substr($epoll_wait_events, 16*$_, 12));
314     }
315     return $ct;
316 }
317
318 1;
319
320 =head1 WARRANTY
321
322 This is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
323
324 =head1 AUTHORS
325
326 Brad Fitzpatrick <brad@danga.com>