rand/noise.c (noise_devrandom): Use new Linux system call `getrandom'.
[catacomb] / rand / noise.c
index 3969b4e..b59fd8a 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "config.h"
 
+#include <errno.h>
 #include <setjmp.h>
 #include <signal.h>
 #include <stdio.h>
 #  include <grp.h>
 #endif
 
+#if defined(HAVE_LINUX_RANDOM_H)
+#  include <linux/random.h>
+#  include <sys/syscall.h>
+#endif
+
 #include <mLib/bits.h>
 #include <mLib/mdup.h>
 #include <mLib/sel.h>
@@ -87,8 +93,8 @@ static gid_t noise_gid = NOISE_NOSETGID; /* Gid to set to spawn processes */
 
 static int bitcount(unsigned long x)
 {
-  char ctab[] = { 0, 1, 1, 2, 1, 2, 2, 3,
-                 1, 2, 2, 3, 2, 3, 3, 4 };
+  static const char ctab[] = { 0, 1, 1, 2, 1, 2, 2, 3,
+                              1, 2, 2, 3, 2, 3, 3, 4 };
   int count = 0;
   while (x) {
     count += ctab[x & 0xfu];
@@ -157,11 +163,42 @@ int noise_timer(rand_pool *r)
 
 int noise_devrandom(rand_pool *r)
 {
-  int fd;
+  int fd = -1;
   octet buf[RAND_POOLSZ];
   ssize_t len;
   size_t n = 0;
   int ret = 0;
+#ifdef __linux__
+  fd_set infd;
+  struct timeval tv = { 0, 0 };
+#endif
+
+#if defined(HAVE_LINUX_RANDOM_H) &&                                    \
+    defined(GRND_NONBLOCK) &&                                          \
+    defined(SYS_getrandom)
+  /* --- Use the new shinies if available --- */
+
+  while (n < sizeof(buf)) {
+    if ((len = syscall(SYS_getrandom, buf + n, sizeof(buf) - n,
+                      GRND_NONBLOCK)) <= 0) {
+      if (errno == ENOSYS) break;
+      else goto done;
+    }
+    n += len;
+  }
+  if (n == sizeof(buf)) goto win;
+#endif
+
+#ifdef __linux__
+  /* --- Don't take from `/dev/urandom' if `/dev/random' would block --- */
+
+  if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) < 0) goto done;
+  FD_ZERO(&infd);
+  FD_SET(fd, &infd);
+  if (select(fd + 1, &infd, 0, 0, &tv) < 0 || !FD_ISSET(fd, &infd))
+    goto done;
+  close(fd); fd = -1;
+#endif
 
   /* --- Be nice to other clients of the random device --- *
    *
@@ -171,18 +208,24 @@ int noise_devrandom(rand_pool *r)
    * needs to get some more entropy from somewhere.
    */
 
-  if ((fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK)) >= 0 ||
+  if (fd >= 0 ||
+      (fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK)) >= 0 ||
       (fd = open("/dev/arandom", O_RDONLY | O_NONBLOCK)) >= 0 ||
       (fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) >= 0) {
     while (n < sizeof(buf)) {
       if ((len = read(fd, buf + n, sizeof(buf) - n)) <= 0) break;
       n += len;
     }
-    rand_add(r, buf, n, n * 8);
-    BURN(buf);
-    if (n == sizeof(buf)) ret = 1;
-    close(fd);
+    if (n == sizeof(buf)) goto win;
   }
+  goto done;
+
+win:
+  ret = 1;
+done:
+  if (fd >= 0) close(fd);
+  rand_add(r, buf, n, 8*n);
+  BURN(buf);
   noise_timer(r);
   return (ret);
 }