about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--AUTHORS1
-rw-r--r--NEWS5
-rwxr-xr-xconfigure4
-rw-r--r--doc/index.html4
-rw-r--r--doc/rngseed.html134
-rw-r--r--doc/s6-fillurandompool.html74
-rw-r--r--doc/upgrade.html4
-rw-r--r--package/deps.mak6
-rw-r--r--package/info2
-rw-r--r--package/modes2
-rw-r--r--package/targets.mak2
-rw-r--r--src/minutils/deps-exe/rngseed2
-rw-r--r--src/minutils/deps-exe/s6-fillurandompool1
-rw-r--r--src/minutils/rngseed.c279
-rw-r--r--src/minutils/s6-fillurandompool.c10
16 files changed, 436 insertions, 96 deletions
diff --git a/.gitignore b/.gitignore
index bb472fe..43ea92b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,8 +5,8 @@
 *.so.*
 /config.mak
 /src/include/s6-linux-utils/config.h
+/rngseed
 /s6-chroot
-/s6-fillurandompool
 /s6-freeramdisk
 /s6-hostname
 /s6-logwatch
diff --git a/AUTHORS b/AUTHORS
index 3d6eaf7..11f9c99 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -11,3 +11,4 @@ Thanks to:
   Jorge Almeida <jalmeida@math.ist.utl.pt>
   Olivier Brunel <jjk@jjacky.com>
   Natanael Copa <ncopa@alpinelinux.org>
+  Jason Donenfeld <jason@zx2c4.com>
diff --git a/NEWS b/NEWS
index d418b3f..1885d6c 100644
--- a/NEWS
+++ b/NEWS
@@ -1,9 +1,12 @@
 Changelog for s6-linux-utils.
 
-In 2.5.1.8
+In 2.6.0.0
 ----------
 
  - Adaptation to skalibs-2.12.0.0.
+ - s6-fillurandompool removed.
+ - New program: rngseed, replacing s6-fillurandompool, with
+a lot more features.
 
 
 In 2.5.1.7
diff --git a/configure b/configure
index d3fb5a0..f407635 100755
--- a/configure
+++ b/configure
@@ -46,6 +46,7 @@ Optional features:
   --enable-absolute-paths       do not rely on PATH to access this package's binaries,
                                   hardcode absolute BINDIR/foobar paths instead [disabled]
   --enable-nsss                 use the nsss library for user information [disabled]
+  --with-seed-dir=DIR           make DIR the default rngseed directory [/var/lib/rngseed]
 
 EOF
 exit 0
@@ -160,6 +161,7 @@ addlibdpath=''
 vpaths=''
 vpathd=''
 build=
+seeddir=/var/lib/rngseed
 
 for arg ; do
   case "$arg" in
@@ -192,6 +194,7 @@ for arg ; do
     --disable-absolute-paths|--enable-absolute-paths=no) abspath=false ;;
     --enable-nsss|--enable-nsss=yes) usensss=true ;;
     --disable-nsss|--enable-nsss=no) usensss=false ;;
+    --with-seed-file=*) seed=${arg#*=} ;;
     --enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;;
     --host=*|--target=*) target=${arg#*=} ;;
     --build=*) build=${arg#*=} ;;
@@ -476,6 +479,7 @@ else
   echo "#define ${package_macro_name}_EXTBINPREFIX \"\""
 fi
 echo "#define ${package_macro_name}_LIBEXECPREFIX \"$libexecdir/\""
+echo "#define RNGSEED_DIR \"$seeddir\""
 echo
 echo "#endif"
 exec 1>&3 3>&-
diff --git a/doc/index.html b/doc/index.html
index 733a1c7..d8077c2 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -63,7 +63,7 @@ library. </li>
 
 <ul>
  <li> The current released version of s6-linux-utils is
-<a href="s6-linux-utils-2.5.1.8.tar.gz">2.5.1.8</a>. </li>
+<a href="s6-linux-utils-2.6.0.0.tar.gz">2.6.0.0</a>. </li>
  <li> Alternatively, you can checkout a copy of the
 <a href="//git.skarnet.org/cgi-bin/cgit.cgi/s6-linux-utils/">s6-linux-utils
 git repository</a>:
@@ -100,8 +100,8 @@ the previous versions of s6-linux-utils and the current one. </li>
 </p>
 
 <ul>
+<li><a href="rngseed.html">The <tt>rngseed</tt> program</a></li>
 <li><a href="s6-chroot.html">The <tt>s6-chroot</tt> program</a></li>
-<li><a href="s6-fillurandompool.html">The <tt>s6-fillurandompool</tt> program</a></li>
 <li><a href="s6-freeramdisk.html">The <tt>s6-freeramdisk</tt> program</a></li>
 <li><a href="s6-hostname.html">The <tt>s6-hostname</tt> program</a></li>
 <li><a href="s6-logwatch.html">The <tt>s6-logwatch</tt> program</a></li>
diff --git a/doc/rngseed.html b/doc/rngseed.html
new file mode 100644
index 0000000..d08f56c
--- /dev/null
+++ b/doc/rngseed.html
@@ -0,0 +1,134 @@
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <meta http-equiv="Content-Language" content="en" />
+    <title>s6-linux-utils: the rngseed program</title>
+    <meta name="Description" content="s6-linux-utils: the rngseed program" />
+    <meta name="Keywords" content="s6 linux administration root utilities rngseed random pool entropy getrandom seedrng secure random number generator" />
+    <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+  </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-utils</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>rngseed</tt> program </h1>
+
+<p>
+ <tt>rngseed</tt> manipulates the Linux kernel's entropy pool. It can seed
+the kernel's random number generator from a file, save a new seed into a
+file, wait until the entropy pool is full, and so on. Please read the
+options list carefully in order to use it in a secure way.
+</p>
+
+<p>
+ <tt>rngseed</tt> can only be run as root.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+     rngseed [ -r | -R ] [ -N | -n ] [ -w | -W ] [ -d <em>dir</em> ] [ -v <em>verbosity</em> ]
+</pre>
+
+<p>
+ The behaviour of rngseed depends on what options it is given. By default, it
+just waits until the entropy pool is full, then exits 0.
+</p>
+
+<h2> Options </h2>
+
+<h3> Configuration options </h3>
+
+<ul>
+ <li> <tt>-v&nbsp;<em>verbosity</em></tt>&nbsp;: be more or less verbose.
+Default is <tt>1</tt>, meaning rngseed will print warning and error
+messages. <tt>0</tt> will make it only print error messages, not warnings.
+<tt>2</tt> or more will make it add informational messages. </li>
+ <li> <tt>-d&nbsp;<em>dir</em></tt>&nbsp;: use <em>dir</em> as the
+directory where the seed file is located (for reading as well as writing).
+<em>dir</em> must be located on a writable, permanent filesystem.
+Default is <tt>/var/lib/rngseed</tt>. </li>
+</ul>
+
+<h3> Behaviour options </h3>
+
+<ul>
+ <li> <tt>-r</tt>&nbsp;: read from a seed file. rngseed will attempt to read
+some bits from <tt><em>dir</em>/seed</tt> and seed the kernel's RNG with the data.
+<em>dir</em> must be on a writable filesystem, because the seed file will be unlinked
+(the same data must not be used to seed the RNG twice). <tt>rngseed -r</tt> is
+typically used at boot time, in init scripts, right after mounting the
+filesystem where the seed has been saved. </li>
+ <li> <tt>-R</tt>&nbsp;: read from a seed file, ignoring creditability.
+Behaves like <tt>-r</tt>, but will not increase the entropy count of the
+kernel RNG even if the seed file is marked as creditable. </li>
+ <li> <tt>-w</tt>&nbsp;: write to a seed file. rngseed will save some
+random bits into <tt><em>dir</em>/seed</tt>, marking the seed as creditable if the
+RNG's entropy pool is fully initialized. <tt>rngseed -w</tt> is typically used at
+shutdown time, right before unmounting filesystems; the point is to store
+a seed on disk so it can be reused on next boot by <tt>rngseed -r</tt>. </li>
+ <li> <tt>-W</tt>&nbsp;: write to a seed file, without registering
+creditability. Behaves like <tt>-w</tt>, but does not mark the new seed
+file as creditable. </li>
+ <li> <tt>-N</tt>&nbsp;: block. After reading a seed file if required,
+and before writing a new seed file if required, rngseed will wait until the
+entropy pool is ready. This ensures that future readings of the kernel
+RNG will be cryptographically secure, and that new seed files will be
+creditable. This is the default. </li>
+ <li> <tt>-n</tt>&nbsp;: do not block. Immediately proceed even if the entropy
+pool is not ready. This may make a new seed file non-creditable. </li>
+</ul>
+
+<h2> Creditability </h2>
+
+<p>
+ A seed is said to be <em>creditable</em> if it has been obtained through a
+cryptographically secure RNG. This means it is safe from replay attacks, and
+safe to use to count towards the entropy pool when seeding the kernel RNG.
+<tt>rngseed -w</tt> will normally always create a creditable seed file,
+especially if used at shutdown time: by then, the kernel's entropy pool
+should have been initialized for a while.
+</p>
+
+<p>
+ An <em>uncreditable</em> seed can be used to add to the random pool, but
+should not increment the entropy count, because it is not safe from
+replay attacks. <tt>rngseed -r</tt> will do the right thing if the seed
+it reads is uncreditable.
+</p>
+
+<p>
+ <tt>rngseed</tt> uses the seed file's permissions to mark creditability.
+An uncreditable seed has rights 0600; a creditable seed has rights 0400.
+</p>
+
+<h2> Exit codes </h2>
+
+<ul>
+ <li> 0: success </li>
+ <li> 100: wrong usage </li>
+ <li> 111: system call failure </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> <tt>rngseed -N</tt> replaces the old <tt>s6-fillurandompool</tt> program,
+that only waited for the entropy pool to get ready, but did not include any
+seed file management. </li>
+ <li> The options are named <tt>r</tt> and <tt>w</tt> from the <em>seed file</em>'s
+point of view.
+<tt>rngseed -r</tt> reads from the file (and unlinks it) and writes to the kernel
+RNG. <tt>rngseed -w</tt> reads from the kernel RNG and writes to the file. </li>
+ <li> <tt>rngseed</tt> is inspired by Jason Donenfeld's
+<a href="https://git.zx2c4.com/seedrng/about/">seedrng</a> program. It is,
+however, an independent implementation of the same concept. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-fillurandompool.html b/doc/s6-fillurandompool.html
deleted file mode 100644
index c07b0f4..0000000
--- a/doc/s6-fillurandompool.html
+++ /dev/null
@@ -1,74 +0,0 @@
-<html>
-  <head>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-    <meta http-equiv="Content-Language" content="en" />
-    <title>s6-linux-utils: the s6-fillurandompool program</title>
-    <meta name="Description" content="s6-linux-utils: the s6-fillurandompool program" />
-    <meta name="Keywords" content="s6 linux administration root utilities random urandom /dev/urandom entropy pool getrandom getentropy" />
-    <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
-  </head>
-<body>
-
-<p>
-<a href="index.html">s6-linux-utils</a><br />
-<a href="//skarnet.org/software/">Software</a><br />
-<a href="//skarnet.org/">skarnet.org</a>
-</p>
-
-<h1> The <tt>s6-fillurandompool</tt> program </h1>
-
-<p>
-<tt>s6-fillurandompool</tt> blocks until the machine's
-<tt>/dev/urandom</tt> entropy pool is filled up. Then it exits.
-</p>
-
-<h2> Interface </h2>
-
-<pre>
-     s6-fillurandompool
-</pre>
-
-<h2> Rationale </h2>
-
-<p>
- For some reason, Linux has <em>two</em> separate entropy pools: one for
-<tt>/dev/random</tt> and one for <tt>/dev/urandom</tt>.
-</p>
-
-<p>
- Reading from <tt>/dev/random</tt> blocks when its entropy pool is
-not full enough, so it will never return weak random data. (Reading
-from <tt>/dev/random</tt> is overkill anyway, and
-<a href="https://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/">you
-should not be doing it.</a>)
-</p>
-
-<p>
- However, reading from <tt>/dev/urandom</tt> (which
-<a href="https://www.2uo.de/myths-about-urandom/">you should be doing</a>)
-will not block, even though the entropy pool may not have been
-initialized yet. That's the only insecure thing about it: at boot time,
-<tt>/dev/urandom</tt> may return weak random data, until its entropy
-pool has filled up.
-</p>
-
-<p>
- <tt>s6-fillurandompool</tt> is meant to address this issue. Call it once
-early on in your boot scripts, before you need any serious random data;
-when it exits, the <tt>/dev/urandom</tt> pool has been properly initialized,
-and it is now safe to read from <tt>/dev/urandom</tt> every time you need
-random data, until the machine shuts down.
-</p>
-
-<h2> Notes </h2>
-
-<ul>
- <li> <tt>s6-fillurandompool</tt> will only work on a Linux kernel version
-3.17 or later: this is when the
-<a href="https://man7.org/linux/man-pages/man2/getrandom.2.html"><tt>getrandom()</tt></a>
-system call, which it internally uses, has been implemented. </li>
-</ul>
-
-</body>
-</html>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index e8e658e..bc39329 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -18,11 +18,13 @@
 
 <h1> What has changed in s6-linux-utils </h1>
 
-<h2> in 2.5.1.8 </h2>
+<h2> in 2.6.0.0 </h2>
 
 <ul>
  <li> <a href="//skarnet.org/software/skalibs/">skalibs</a>
 dependency bumped to 2.12.0.0. </li>
+ <li> <tt>s6-fillurandompool</tt> has been replaced with the new
+<a href="rngseed.html">rngseed</a> program. </li>
 </ul>
 
 <h2> in 2.5.1.7 </h2>
diff --git a/package/deps.mak b/package/deps.mak
index c464c91..3150147 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -2,8 +2,8 @@
 # This file has been generated by tools/gen-deps.sh
 #
 
+src/minutils/rngseed.o src/minutils/rngseed.lo: src/minutils/rngseed.c src/include/s6-linux-utils/config.h
 src/minutils/s6-chroot.o src/minutils/s6-chroot.lo: src/minutils/s6-chroot.c
-src/minutils/s6-fillurandompool.o src/minutils/s6-fillurandompool.lo: src/minutils/s6-fillurandompool.c
 src/minutils/s6-freeramdisk.o src/minutils/s6-freeramdisk.lo: src/minutils/s6-freeramdisk.c
 src/minutils/s6-hostname.o src/minutils/s6-hostname.lo: src/minutils/s6-hostname.c
 src/minutils/s6-logwatch.o src/minutils/s6-logwatch.lo: src/minutils/s6-logwatch.c
@@ -21,10 +21,10 @@ src/minutils/s6ps_statparse.o src/minutils/s6ps_statparse.lo: src/minutils/s6ps_
 src/minutils/s6ps_ttycache.o src/minutils/s6ps_ttycache.lo: src/minutils/s6ps_ttycache.c src/minutils/s6-ps.h
 src/minutils/s6ps_wchan.o src/minutils/s6ps_wchan.lo: src/minutils/s6ps_wchan.c src/minutils/s6-ps.h
 
+rngseed: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB}
+rngseed: src/minutils/rngseed.o
 s6-chroot: EXTRA_LIBS := -lskarnet
 s6-chroot: src/minutils/s6-chroot.o
-s6-fillurandompool: EXTRA_LIBS := -lskarnet
-s6-fillurandompool: src/minutils/s6-fillurandompool.o
 s6-freeramdisk: EXTRA_LIBS := -lskarnet
 s6-freeramdisk: src/minutils/s6-freeramdisk.o
 s6-hostname: EXTRA_LIBS := -lskarnet
diff --git a/package/info b/package/info
index bcfa0a7..b6f6275 100644
--- a/package/info
+++ b/package/info
@@ -1,4 +1,4 @@
 package=s6-linux-utils
-version=2.5.1.8
+version=2.6.0.0
 category=admin
 package_macro_name=S6_LINUX_UTILS
diff --git a/package/modes b/package/modes
index 5c61589..46b4623 100644
--- a/package/modes
+++ b/package/modes
@@ -1,5 +1,5 @@
+rngseed			0744
 s6-chroot		0744
-s6-fillurandompool	0755
 s6-freeramdisk		0744
 s6-hostname		0755
 s6-logwatch		0755
diff --git a/package/targets.mak b/package/targets.mak
index 0fa35ef..f42cb5a 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -1,6 +1,6 @@
 BIN_TARGETS := \
+rngseed \
 s6-chroot \
-s6-fillurandompool \
 s6-freeramdisk \
 s6-hostname \
 s6-logwatch \
diff --git a/src/minutils/deps-exe/rngseed b/src/minutils/deps-exe/rngseed
new file mode 100644
index 0000000..a11a5f4
--- /dev/null
+++ b/src/minutils/deps-exe/rngseed
@@ -0,0 +1,2 @@
+-lskarnet
+${SYSCLOCK_LIB}
diff --git a/src/minutils/deps-exe/s6-fillurandompool b/src/minutils/deps-exe/s6-fillurandompool
deleted file mode 100644
index e7187fe..0000000
--- a/src/minutils/deps-exe/s6-fillurandompool
+++ /dev/null
@@ -1 +0,0 @@
--lskarnet
diff --git a/src/minutils/rngseed.c b/src/minutils/rngseed.c
new file mode 100644
index 0000000..f00c4fc
--- /dev/null
+++ b/src/minutils/rngseed.c
@@ -0,0 +1,279 @@
+/* ISC license. */
+
+#include <skalibs/sysdeps.h>
+
+#ifndef SKALIBS_HASCLOCKBOOT
+# error "CLOCK_BOOTTIME required"
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/random.h>
+#include <linux/random.h>
+
+#include <skalibs/types.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/error.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/tai.h>
+#include <skalibs/iopause.h>
+#include <skalibs/blake2s.h>
+#include <skalibs/random.h>
+
+#include <s6-linux-utils/config.h>
+
+#define USAGE "rngseed [ -d seeddir ] [ -v verbosity ] [ -r | -R ] [ -n | -N ] [ -w | -W ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+#define HASH_PREFIX "SeedRNG v1 Old+New Prefix"
+#define HASH_FALLBACK "SeedRNG v1 No New Seed Failure"
+
+struct flags_s
+{
+  unsigned int read: 1 ;
+  unsigned int rcred: 1 ;
+  unsigned int block: 1 ;
+  unsigned int write: 1 ;
+  unsigned int wcred: 1 ;
+} ;
+#define FLAGS_ZERO { .read = 0, .rcred = 1, .block = 1, .write = 0, .wcred = 1 }
+
+struct randpoolinfo_s
+{
+  int entropy_count ;
+  int buf_size ;
+  char buffer[512]  ;
+} ;
+
+static unsigned int verbosity = 1 ;
+
+static inline void mkdirp (char *s, size_t len)
+{
+  mode_t m = umask(0) ;
+  size_t i = 1 ;
+  for (; i < len ; i++) if (s[i] == '/')
+  {
+    s[i] = 0 ;
+    if (mkdir(s, 02755) < 0 && errno != EEXIST)
+      strerr_diefu2sys(111, "mkdir ", s) ;
+    s[i] = '/' ;
+  }
+  umask(m) ;
+}
+
+static inline int read_seed_nb (char *s, size_t len)
+{
+  int wcred ;
+  size_t w = 0 ;
+#ifdef SKALIBS_HASGETRANDOM
+  while (w < len)
+  {
+    ssize_t r = getrandom(s + w, len - w, GRND_NONBLOCK) ;
+    if (r == -1)
+    {
+      if (errno == EINTR) continue ;
+      if (error_isagain(errno)) break ;
+      strerr_diefu1sys(111, "getrandom") ;
+    }
+    else w += r ;
+  }
+  wcred = w >= len ;
+  if (!wcred)
+#else
+  tain dummy = TAIN_EPOCH ;
+  iopause_fd x = { .events = IOPAUSE_READ } ;
+  x.fd = openbc_read("/dev/random") ;
+  if (x.fd == -1) strerr_diefu2sys(111, "open ", "/dev/random") ;
+  wcred = iopause(&x, 1, &dummy, &dummy) ;
+  if (wcred == -1) strerr_diefu1sys(111, "iopause") ;
+  fd_close(x.fd) ;
+#endif
+  random_devurandom(s + w, len - w) ;
+  return wcred ;
+}
+
+int main (int argc, char const *const *argv)
+{
+  blake2s_ctx ctx = BLAKE2S_INIT(32) ;
+  char const *seeddir = RNGSEED_DIR ;
+  struct flags_s flags = FLAGS_ZERO ;
+  PROG = "rngseed" ;
+  {
+    subgetopt l = SUBGETOPT_ZERO ;
+    for (;;)
+    {
+      int opt = subgetopt_r(argc, argv, "d:v:rRnNwW", &l) ;
+      if (opt == -1) break ;
+      switch (opt)
+      {
+        case 'd' : seeddir = l.arg ; break ;
+        case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ;
+        case 'r' : flags.read = 1 ; flags.rcred = 1 ; break ;
+        case 'R' : flags.read = 1 ; flags.rcred = 0 ; break ;
+        case 'n' : flags.block = 0 ; break ;
+        case 'N' : flags.block = 1 ; break ;
+        case 'w' : flags.write = 1 ; flags.wcred = 1 ; break ;
+        case 'W' : flags.write = 1 ; flags.wcred = 0 ; break ;
+        default : dieusage() ;
+      }
+    }
+    argc -= l.ind ; argv += l.ind ;
+  }
+
+  {
+    size_t dirlen = strlen(seeddir) ;
+    char file[dirlen + 6] ;
+    memcpy(file, seeddir, dirlen) ;
+    while (dirlen && file[dirlen-1] == '/') dirlen-- ;
+    memcpy(file + dirlen, "/seed", 6) ;
+
+    if (flags.write)
+    {
+      struct timespec ts ;
+      if (dirlen)
+      {
+        file[dirlen] = 0 ;
+        mkdirp(file, dirlen) ;
+        if (mkdir(file, 0700) == -1)
+        {
+          struct stat st ;
+          if (errno != EEXIST) strerr_diefu2sys(111, "mkdir ", file) ;
+          if (stat(file, &st) == -1)
+            strerr_diefu2sys(111, "stat ", file) ;
+          if (st.st_mode & 0077)
+          {
+            if (verbosity)
+              strerr_warnw2sys(file, "has permissive modes, changing it to 0700") ;
+            if (chmod(file, 0700) == -1)
+              strerr_diefu2sys(111, "chmod ", file) ;
+          }
+        }
+        file[dirlen] = '/' ;
+      }
+      blake2s_update(&ctx, HASH_PREFIX, sizeof(HASH_PREFIX) - 1) ;
+      clock_gettime(CLOCK_REALTIME, &ts) ;
+      blake2s_update(&ctx, (char *)&ts, sizeof ts) ;
+      clock_gettime(CLOCK_BOOTTIME, &ts) ;
+      blake2s_update(&ctx, (char *)&ts, sizeof ts) ;
+    }
+
+    if (flags.read)
+    {
+      struct randpoolinfo_s req ;
+      struct stat st ;
+      size_t seedlen ;
+      int fd ;
+      if (verbosity >= 2) strerr_warni2x("reading seed from ", file) ;
+      fd = openbc_read(file) ;
+      if (fd == -1) strerr_diefu2sys(111, "open ", file) ;
+      errno = 0 ;
+      seedlen = allread(fd, req.buffer, 512) ;
+      if (errno) strerr_diefu2sys(111, "read from ", file) ;
+      if (!seedlen) strerr_dief2x(100, "empty ", file) ;
+      if (fstat(fd, &st) == -1) strerr_diefu2sys(111, "stat ", file) ;
+      if (unlink(file) == -1) strerr_diefu2sys(111, "unlink ", file) ;
+      fd_close(fd) ;
+      if (flags.write)
+      {
+        blake2s_update(&ctx, (char *)&seedlen, sizeof(seedlen)) ;
+        blake2s_update(&ctx, req.buffer, seedlen) ;
+      }
+      if (flags.rcred && st.st_mode & S_IWUSR && verbosity)
+        strerr_warnw2x(file, " was not marked as creditable") ;
+      req.entropy_count = flags.rcred && !(st.st_mode & S_IWUSR) ? (seedlen << 3) : 0 ;
+      req.buf_size = seedlen ;
+      fd = openbc_read("/dev/urandom") ;
+      if (fd == -1) strerr_diefu2sys(111, "open ", "/dev/urandom") ;
+      if (verbosity >= 2)
+      {
+        char fmt[SIZE_FMT] ;
+        fmt[size_fmt(fmt, seedlen << 3)] = 0 ;
+        strerr_warni4x("seeding with ", fmt, " bits", req.entropy_count ? "" : " without crediting") ;
+      }
+      if (ioctl(fd, RNDADDENTROPY, &req) == -1)
+        strerr_diefu1sys(111, "seed") ;
+      fd_close(fd) ;
+    }
+
+    if (flags.block)
+    {
+      if (verbosity >= 2)
+        strerr_warni1x("waiting for the entropy pool to initialize") ;
+#ifdef SKALIBS_HASGETRANDOM
+      char c ;
+      ssize_t r = getrandom(&c, 1, 0) ;
+      if (r == -1) strerr_diefu1sys(111, "getrandom") ;
+#else
+      iopause_fd x = { .events = IOPAUSE_READ } ;
+      x.fd = openbc_read("/dev/random") ;
+      if (x.fd == -1) strerr_diefu2sys(111, "open ", "/dev/random") ;
+      if (iopause(&x, 1, 0, 0) == -1) strerr_diefu1sys(111, "iopause") ;
+      fd_close(x.fd) ;
+#endif
+    }
+
+    if (flags.write)
+    {
+      char seed[512] ;
+      size_t len = 512 ;
+      int wcred = 1 ;
+      char s[SIZE_FMT] = "" ;
+      int fd = openbc_read("/proc/sys/kernel/random/poolsize") ;
+      if (fd < 0)
+      {
+        if (verbosity) strerr_warnwu2sys("open ", "/proc/sys/kernel/random/poolsize") ;
+      }
+      else
+      {
+        size_t r ;
+        errno = 0 ;
+        r = allread(fd, s, SIZE_FMT - 1) ;
+        if (errno)
+        {
+          if (verbosity) strerr_warnwu2sys("read from ", "/proc/sys/kernel/random/poolsize") ;
+        }
+        else
+        {
+          s[r] = 0 ;
+          if (!size_scan(s, &r))
+          {
+            if (verbosity) strerr_warnwu2sys("understand ", "/proc/sys/kernel/random/poolsize") ;
+          }
+          else len = (r + 7) >> 3 ;
+        }
+        fd_close(fd) ;
+      }
+      if (len < 32) len = 32 ;
+      if (len > 512) len = 512 ;
+
+      if (verbosity >= 2)
+      {
+        s[size_fmt(s, len << 3)] = 0 ;
+        strerr_warni3x("reading ", s, " bits of random to make the seed") ;
+      }
+      if (flags.block) random_buf(seed, len) ;
+      else wcred = read_seed_nb(seed, len) ;
+      if (!wcred && verbosity) strerr_warnwu1x("make the seed creditable") ;
+      blake2s_update(&ctx, (char *)&len, sizeof(len)) ;
+      blake2s_update(&ctx, seed, len) ;
+      blake2s_final(&ctx, seed + len - 32) ;
+      if (verbosity >= 2) strerr_warni2x("writing seed to ", file) ;
+      umask(0077) ;
+      if (!openwritenclose_unsafe_sync(file, seed, len))
+        strerr_diefu2sys(111, "write to ", file) ;
+      if (flags.wcred && wcred)
+      {
+        if (chmod(file, 0400) == -1 && verbosity)
+          strerr_warnwu3sys("mark ", file, "as creditable") ;
+      }
+    }
+  }
+
+  return 0 ;
+}
diff --git a/src/minutils/s6-fillurandompool.c b/src/minutils/s6-fillurandompool.c
deleted file mode 100644
index c96437c..0000000
--- a/src/minutils/s6-fillurandompool.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* ISC license. */
-
-#include <skalibs/random.h>
-
-int main (void)
-{
-  char c ;
-  random_buf(&c, 1) ;
-  return 0 ;
-}