Merge branch 'release/1.3'
[reservoir_sample] / randomness.c
1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <string.h>
5 #include <time.h>
6 #include <errno.h>
7 #include <assert.h>
8
9 #include "randomness.h"
10 #include "notify.h"
11
12 static int randomness_fd_ = -1;
13
14 int randomness_init(const char *filename) {
15 if (filename != NULL) {
16 randomness_fd_ = open(filename, O_RDONLY);
17 if (randomness_fd_ == -1) {
18 NOTIFY_ERROR("%s('%s'):%s", "open", filename, strerror(errno));
19 return -1;
20 }
21 NOTIFY_DEBUG("reading randomness from '%s', fd:%d", filename, randomness_fd_);
22 } else {
23 if (randomness_fd_ != -1) {
24 close(randomness_fd_);
25 randomness_fd_ = -1;
26 }
27 srand48(time(NULL) ^ getpid());
28 NOTIFY_DEBUG("reading randomness from system PRNG");
29 }
30
31 return 0;
32 }
33
34 /*
35 Room for improvement: constrain bits of randomness consumed, based on #limit
36 Also maybe read chunks of randomness at a time
37 */
38 unsigned long randomness_upto_inclusive(unsigned long limit) {
39 unsigned long randomness;
40
41 if (limit == 0)
42 return 0;
43
44 if (randomness_fd_ != -1) {
45 ssize_t len;
46
47 do {
48 len = read(randomness_fd_, &randomness, sizeof randomness);
49 } while (len == -1 && (errno == EINTR || errno == EAGAIN));
50 if (len == sizeof randomness) {
51 randomness %= limit + 1;
52 NOTIFY_DEBUG("randomness:%lu", randomness);
53 return randomness;
54 }
55 NOTIFY_ERROR("%s(%d, %zu):%zd:%s",
56 "read", randomness_fd_, sizeof randomness, len,
57 (len < 0) ? strerror(errno) : ( (len == 0) ? "EOF" : "not enough read" )
58 );
59 }
60
61 /* fall back to pseudo-randomness if read failed */
62 randomness = mrand48();
63 randomness %= limit + 1;
64
65 NOTIFY_DEBUG("randomness:%lu", randomness);
66 return randomness;
67 }