]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib: random utilities
authorDavidlohr Bueso <dave@gnu.org>
Thu, 5 Apr 2012 21:52:04 +0000 (23:52 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 10 Apr 2012 11:12:43 +0000 (13:12 +0200)
Add a random number(s) generator specific file. The intial functions are based
on what libuuid provide. I did some modifications like avoid WIN32 checks - this
is util-LINUX.

[kzak@redhat.com: - move jrand_seed to lib/randutils.c
                  - use TLS for jrand_seed (like original code from libuuid)
                  - use size_t for buffer sizes
                  - add close() to random_get_bytes]

Signed-off-by: Davidlohr Bueso <dave@gnu.org>
Signed-off-by: Karel Zak <kzak@redhat.com>
include/randutils.h [new file with mode: 0644]
lib/Makefile.am
lib/randutils.c [new file with mode: 0644]

diff --git a/include/randutils.h b/include/randutils.h
new file mode 100644 (file)
index 0000000..dec5e35
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef UTIL_LINUX_RANDUTILS
+#define UTIL_LINUX_RANDUTILS
+
+#ifdef HAVE_SRANDOM
+#define srand(x)       srandom(x)
+#define rand()         random()
+#endif
+
+extern int random_get_fd(void);
+extern void random_get_bytes(void *buf, size_t nbytes);
+
+#endif
index c34481de37cc0a904186b5646b4660d79ccf63c6..fc967fcbdfc87920d264569c084abcce1fa5f57b 100644 (file)
@@ -10,6 +10,7 @@ noinst_PROGRAMS = \
        test_ismounted \
        test_mangle \
        test_procutils \
+       test_randutils \
        test_strutils \
        test_tt \
        test_wholedisk
@@ -30,6 +31,7 @@ test_at_SOURCES = at.c
 test_at_CFLAGS = -DTEST_PROGRAM_AT
 
 test_strutils_SOURCES = strutils.c
+test_randutils_SOURCES = randutils.c
 test_procutils_SOURCES = procutils.c
 
 if LINUX
diff --git a/lib/randutils.c b/lib/randutils.c
new file mode 100644 (file)
index 0000000..b90c886
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * General purpose random utilities
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <sys/syscall.h>
+
+#include "randutils.h"
+
+#ifdef HAVE_TLS
+#define THREAD_LOCAL static __thread
+#else
+#define THREAD_LOCAL static
+#endif
+
+#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
+#define DO_JRAND_MIX
+THREAD_LOCAL unsigned short ul_jrand_seed[3];
+#endif
+
+int random_get_fd(void)
+{
+       int i, fd;
+       struct timeval  tv;
+
+       gettimeofday(&tv, 0);
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd == -1)
+               fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+       if (fd >= 0) {
+               i = fcntl(fd, F_GETFD);
+               if (i >= 0)
+                       fcntl(fd, F_SETFD, i | FD_CLOEXEC);
+       }
+       srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+
+#ifdef DO_JRAND_MIX
+       ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
+       ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
+       ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
+#endif
+       /* Crank the random number generator a few times */
+       gettimeofday(&tv, 0);
+       for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+               rand();
+       return fd;
+}
+
+
+/*
+ * Generate a stream of random nbytes into buf.
+ * Use /dev/urandom if possible, and if not,
+ * use glibc pseudo-random functions.
+ */
+void random_get_bytes(void *buf, size_t nbytes)
+{
+       size_t i, n = nbytes;
+       int fd = random_get_fd();
+       int lose_counter = 0;
+       unsigned char *cp = (unsigned char *) buf;
+
+       if (fd >= 0) {
+               while (n > 0) {
+                       ssize_t x = read(fd, cp, n);
+                       if (x <= 0) {
+                               if (lose_counter++ > 16)
+                                       break;
+                               continue;
+                       }
+                       n -= x;
+                       cp += x;
+                       lose_counter = 0;
+               }
+
+               close(fd);
+       }
+
+       /*
+        * We do this all the time, but this is the only source of
+        * randomness if /dev/random/urandom is out to lunch.
+        */
+       for (cp = buf, i = 0; i < nbytes; i++)
+               *cp++ ^= (rand() >> 7) & 0xFF;
+
+#ifdef DO_JRAND_MIX
+       {
+               unsigned short tmp_seed[3];
+
+               memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
+               ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
+               for (cp = buf, i = 0; i < nbytes; i++)
+                       *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
+               memcpy(ul_jrand_seed, tmp_seed,
+                      sizeof(ul_jrand_seed)-sizeof(unsigned short));
+       }
+#endif
+
+       return;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char *argv[])
+{
+       unsigned int v, i;
+
+       /* generate and print 10 random numbers */
+       for (i = 0; i < 10; i++) {
+               random_get_bytes(&v, sizeof(v));
+               printf("%d\n", v);
+       }
+
+       return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */