File Coverage

blib/lib/Rand/Urandom.pm
Criterion Covered Total %
statement 48 58 82.7
branch 10 22 45.4
condition 3 8 37.5
subroutine 11 11 100.0
pod 2 4 50.0
total 74 103 71.8


line stmt bran cond sub pod time code
1             package Rand::Urandom;
2 2     2   33176 use strict;
  2         2  
  2         184  
3 2     2   18 use warnings;
  2         2  
  2         68  
4 2     2   8 use Config;
  2         48  
  2         78  
5 2     2   950 use POSIX qw(EINTR ENOSYS);
  2         9484  
  2         12  
6 2     2   1584 use Exporter qw(import);
  2         2  
  2         866  
7              
8             our @EXPORT_OK = qw(perl_rand rand_bytes);
9             our $VERSION = '0.01';
10              
11             ## no critic (Subroutines::ProhibitSubroutinePrototypes)
12             sub use_urandom(;$) {
13 7   100 7 0 779 my $max = shift || 1;
14              
15 7         36 my $buf = rand_bytes(8);
16 7         53 my $n = unpack('Q', $buf);
17 7 100       34 return $n if ($max == 2**64);
18              
19 6         18 $max *= $n / 2**64;
20 6         139 return $max;
21             }
22              
23             sub try_syscall {
24 8     8 0 10 my $num = shift;
25 8 50       293 if ($Config{'osname'} !~ m/linux/) {
26 0         0 return;
27             }
28              
29 8 50       78 my $syscall = $Config{'archname'} =~ m/x86_64/ ? 318 : 355;
30 8         13 my $ret;
31 8         34 my $buf = ' ' x $num;
32 8         15 my $tries = 0;
33 8         53 local $! = undef;
34 8   0     11 do {
35 8         44 $ret = syscall($syscall, $buf, $num, 0);
36 8 50       93 if ($! == ENOSYS) {
37 8         26 return;
38             }
39              
40 0 0       0 if ($ret != $num) {
41 0         0 warn "Rand::Urandom: huh, getrandom() returned $ret... trying again";
42 0         0 $ret = -1;
43 0         0 $! = EINTR;
44             }
45              
46 0 0       0 if ($tries++ > 100) {
47 0         0 warn 'Rand::Urandom: getrandom() looped lots, falling back';
48 0         0 return;
49             }
50             } while ($ret == -1 && $! == EINTR);
51              
52 0         0 return $buf;
53             }
54              
55             sub rand_bytes {
56 8     8 1 13 my $num = shift;
57              
58 8         28 my $buf = try_syscall($num);
59 8 50       21 if (!$buf) {
60 8         19 local $! = undef;
61 8 50       176 my $file = -r '/dev/arandom' ? '/dev/arandom' : '/dev/urandom';
62 8 50       400 open(my $fh, '<:raw', $file) || die "Rand::Urandom: Can't open $file: $!";
63              
64 8         7883 my $got = read($fh, $buf, $num);
65 8 50 33     82 if ($got == 0 || $got != $num) {
66 0         0 die "Rand::Urandom: failed to read from $file: $!";
67             }
68 8 50       118 close($fh) || die "Rand::Urandom: close failed: $!";
69             }
70 8         35 return $buf;
71             }
72              
73             my $orig_rand;
74             sub perl_rand {
75 1     1 1 1038 goto &$orig_rand;
76             }
77              
78             sub BEGIN {
79 2     2   4 $orig_rand = \&CORE::rand;
80 2     2   8 no warnings 'redefine';
  2         4  
  2         84  
81 2         38 *CORE::GLOBAL::rand = \&use_urandom;
82             }
83              
84              
85             1;
86             __END__