File Coverage

blib/lib/Rand/Urandom.pm
Criterion Covered Total %
statement 57 72 79.1
branch 17 38 44.7
condition 6 14 42.8
subroutine 12 12 100.0
pod 2 4 50.0
total 94 140 67.1


line stmt bran cond sub pod time code
1             package Rand::Urandom;
2 3     3   72414 use strict;
  3         6  
  3         78  
3 3     3   12 use warnings;
  3         6  
  3         81  
4 3     3   15 use Config;
  3         15  
  3         111  
5 3     3   2553 use POSIX qw(EINTR ENOSYS);
  3         24192  
  3         18  
6 3     3   4137 use Exporter qw(import);
  3         6  
  3         2016  
7              
8             our @EXPORT_OK = qw(perl_rand rand_bytes);
9             our $VERSION = '0.03';
10              
11             ## no critic (Subroutines::ProhibitSubroutinePrototypes)
12             sub use_urandom(;$) {
13 11   100 11 0 1367 my $max = shift || 1;
14              
15 11         83 my $buf = rand_bytes(8);
16 11         23 my $n;
17 11 50       487 if($Config{'use64bitint'}) {
18 11         74 $n = unpack('Q', $buf);
19             }else {
20             # just treat it as 2 longs for now...
21 0         0 $n = unpack('LL', $buf);
22             }
23 11 100       76 return $n if ($max == 2**64);
24              
25 9         35 $max *= $n / 2**64;
26 9         222 return $max;
27             }
28              
29             my $syscall;
30             my $bsd;
31             sub try_syscall {
32 15     15 0 27 my $num = shift;
33              
34 15 100       42 if(!defined $syscall) {
35 3 50 33     78 if($Config{'osname'} =~ m/openbsd/i && $Config{'archname'} =~ m/amd64/) {
    50          
36 0         0 $syscall = 7;
37 0         0 $bsd = 1;
38             }elsif ($Config{'osname'} =~ m/linux/) {
39 3 50       27 $syscall = $Config{'archname'} =~ m/x86_64/ ? 318 : 355;
40             }else {
41 0         0 $syscall = -1;
42             }
43             }
44 15 50       45 return if($syscall < 0);
45              
46 15         19 my $ret;
47 15         71 my $buf = ' ' x $num;
48 15         22 my $tries = 0;
49 15         182 local $! = undef;
50 15   0     27 do {
51 15         58 $ret = syscall($syscall, $buf, $num, 0);
52 15 50       225 if ($! == ENOSYS) {
53 15         68 return;
54             }
55              
56 0 0       0 if ($ret != ($bsd ? 0 : $num)) {
    0          
57 0         0 warn "Rand::Urandom: huh, getrandom() returned $ret... trying again";
58 0         0 $ret = -1;
59 0         0 $! = EINTR;
60             }
61              
62 0 0       0 if ($tries++ > 100) {
63 0         0 warn 'Rand::Urandom: getrandom() looped lots, falling back';
64 0         0 return;
65             }
66             } while ($ret == -1 && $! == EINTR);
67              
68             # didn't fill in the buffer? fallback
69 0 0       0 return if($buf =~ m/^ +$/);
70              
71 0         0 return $buf;
72             }
73              
74             sub rand_bytes {
75 15     15 1 1661 my $num = shift;
76              
77 15         29 my $buf;
78 15 50 66     157 $buf = try_syscall($num) if(!defined $syscall || $syscall > 0);
79              
80 15 50       47 if (!$buf) {
81 15         41 local $! = undef;
82 15 50       363 my $file = -r '/dev/arandom' ? '/dev/arandom' : '/dev/urandom';
83 15 50       706 open(my $fh, '<:raw', $file) || die "Rand::Urandom: Can't open $file: $!";
84              
85 15         16952 my $got = read($fh, $buf, $num);
86 15 50 33     103 if ($got == 0 || $got != $num) {
87 0         0 die "Rand::Urandom: failed to read from $file: $!";
88             }
89 15 50       183 close($fh) || die "Rand::Urandom: close failed: $!";
90             }
91 15         91 return $buf;
92             }
93              
94             my $orig_rand;
95             sub perl_rand {
96 4 50   4 1 262 if ($^V lt 'v5.16') {
97 0         0 die 'Rand::Urandom: sorry, you cant access the original rand function on perls older than 5.16';
98             }
99              
100 4         338 goto &$orig_rand;
101             }
102              
103             sub BEGIN {
104 3     3   15 no warnings 'redefine';
  3         3  
  3         126  
105 3     3   12 no warnings 'prototype';
  3         6  
  3         213  
106 3     3   6 $orig_rand = \&CORE::rand;
107 3         75 *CORE::GLOBAL::rand = \&use_urandom;
108             }
109              
110              
111             1;
112             __END__