Skip to content
Snippets Groups Projects
Select Git revision
  • ada9dc8b302f362b16fae23517aaf7585c96a399
  • master default protected
  • 9.0
  • 8.0
  • nt-tools
  • 7.8
  • 7.6
  • 7.4
  • 7.2
  • 7.0
  • 0.6
  • rosuav/latex-markdown-renderer
  • rxnpatch/rxnpatch
  • marcus/gobject-introspection
  • rxnpatch/8.0
  • rosuav/pre-listening-ports
  • rosuav/async-annotations
  • rosuav/pgsql-ssl
  • rxnpatch/rxnpatch-broken/2023-10-06T094250
  • grubba/fdlib
  • grubba/wip/sakura/8.0
  • v8.0.2020
  • v8.0.2018
  • v8.0.2016
  • v8.0.2014
  • v8.0.2012
  • v8.0.2008
  • v8.0.2006
  • v8.0.2004
  • v8.0.2002
  • v8.0.2000
  • v8.0.1998
  • v8.0.1996
  • v8.0.1994
  • v8.0.1992
  • v8.0.1990
  • v8.0.1988
  • v8.0.1986
  • rxnpatch/clusters/8.0/2025-04-29T124414
  • rxnpatch/2025-04-29T124414
  • v8.0.1984
41 results

array.c

Blame
  • unix_random.c 22.28 KiB
    /* unix_random.c
     *
     * $Id$
     *
     * Randomness polling on unix, using ideas from Peter Gutmann's
     * cryptlib. */
    
    /* lsh, an implementation of the ssh protocol
     *
     * Copyright (C) 2000 Niels Mller
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License as
     * published by the Free Software Foundation; either version 2 of the
     * License, or (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful, but
     * WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
     * General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     */
    
    #include "randomness.h"
    
    #include "crypto.h"
    #include "reaper.h"
    #include "xalloc.h"
    #include "werror.h"
    
    #include <assert.h>
    #include <string.h>
    
    #if HAVE_UNISTD_H
    #include <unistd.h>
    #endif
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/wait.h>
    #include <pwd.h>
    
    #ifdef HAVE_POLL
    # if HAVE_POLL_H
    #  include <poll.h>
    # elif HAVE_SYS_POLL_H
    #  include <sys/poll.h>
    # endif
    #else
    # include "jpoll.h"
    #endif
    
    /* Workaround for some version of FreeBSD. */
    #ifdef POLLRDNORM
    # define MY_POLLIN (POLLIN | POLLRDNORM)
    #else /* !POLLRDNORM */
    # define MY_POLLIN POLLIN
    #endif /* !POLLRDNORM */
    
    #include <sys/time.h>
    #include <sys/resource.h>
    
    enum poll_status { POLL_NO_POLL, POLL_RUNNING, POLL_FINISHED, POLL_FAILED };
    
    #include "unix_random.c.x"
    
    /* GABA:
       (class
         (name unix_random)
         (super random_poll)
         (vars
           ;; For the slow poll
           (reaper object reap)
           (poll_uid . uid_t)
           (pid . pid_t)
           (status . "enum poll_status")
           ; NOTE: This fd is not known to the gc. 
           (fd . int)
         
           ;; For the fast poll, count number of slow polls per second.
           (previous_time . time_t)
           (time_count . unsigned)))
    */
    
    /* GABA:
       (class
         (name unix_random_callback)
         (super exit_callback)
         (vars
           (ctx object unix_random)))
    */
    
    static void
    do_unix_random_callback(struct exit_callback *s,
    			int signalled, int core UNUSED,
    			int value)
    {
      CAST(unix_random_callback, self, s);
    
      if (signalled || value)
        {
          self->ctx->status = POLL_FAILED;
          trace("unix_random.c: do_unix_random_callback, background poll failed.\n");
        }
      else
        {
          self->ctx->status = POLL_FINISHED;
          trace("unix_random.c: do_unix_random_callback, background poll finished.\n");
        }
    }
    
    static struct exit_callback *
    make_unix_random_callback(struct unix_random *ctx)
    {
      NEW(unix_random_callback, self);
      self->super.exit = do_unix_random_callback;
      self->ctx = ctx;
    
      return &self->super;
    }
    
    #define UNIX_RANDOM_POLL_SIZE 20
    
    /* This structure ought to fit in a pipe buffer (so that we can
     * waitpid() the process before reading its stdout). */
    
    struct unix_random_poll_result
    {
      UINT32 count;
      UINT8 data[UNIX_RANDOM_POLL_SIZE];
    };
    
    #define UNIX_RANDOM_SOURCE_SCALE 8192
    struct unix_random_source
    {
      const char *path;
      const char *arg; /* For now, use at most one argument. */
      int has_alternative;
    
     /* If non-zero, count quality bits of entropy if the amount of output
      * exceeds this value. */
      unsigned small;
      /* Bits of entropy per UNIX_RANDOM_SOURCE_SCALE bytes of output. */
      unsigned quality;
    };
    
    /* Lots of output expected; round downwards. */
    #define WLARGE(x) 0, x
    /* Small but significant output expected; round upwards */
    #define WSMALL(x) 100, x
    
    static const struct unix_random_source random_sources[] =
    {
      { "/bin/vmstat", "-s", 		WSMALL(30), 1 },
      { "/usr/bin/vmstat", "-s", 		WSMALL(30), 0 },
      { "/bin/vmstat", "-c", 		WSMALL(30), 1 },
      { "/usr/bin/vmstat", "-c", 		WSMALL(30), 0 },
      { "/usr/bin/pfstat", NULL, 		WSMALL(20), 0 },
      { "/bin/vmstat", "-i", 		WSMALL(20), 1 },
      { "/usr/bin/vmstat", "-i", 		WSMALL(20), 0 },
      { "/usr/ucb/netstat", "-s", 	WLARGE(20), 1 },
      { "/usr/bin/netstat", "-s", 	WLARGE(20), 1 },
      { "/usr/sbin/netstat", "-s", 	WLARGE(20), 1 },
      { "/bin/netstat", "-s", 		WLARGE(20), 1 },
      { "/usr/etc/netstat", "-s", 	WLARGE(20), 0 },
      { "/usr/bin/nfsstat", NULL, 	WLARGE(20), 0 },
      { "/usr/ucb/netstat", "-m", 	WSMALL(10), 1 },
      { "/usr/bin/netstat", "-m", 	WSMALL(10), 1 },
      { "/usr/sbin/netstat", "-m", 	WSMALL(10), 1 },
      { "/bin/netstat", "-m", 		WSMALL(10), 1 },
      { "/usr/etc/netstat", "-m", 	WSMALL(10), 0 },
      { "/usr/ucb/netstat", "-in", 	WSMALL(10), 1 },
      { "/usr/bin/netstat", "-in", 	WSMALL(10), 1 },
      { "/usr/sbin/netstat", "-in", 	WSMALL(10), 1 },
      { "/bin/netstat", "-in", 		WSMALL(10), 1 },
      { "/usr/etc/netstat", "-in", 	WSMALL(10), 0 },
    #if 0
      { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.7.1.0", 	WSMALL(10), 0 }, /* UDP in */
      { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.7.4.0", 	WSMALL(10), 0 }, /* UDP out */
      { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.4.3.0", 	WSMALL(10), 0 }, /* IP ? */
      { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.10.0", 	WSMALL(10), 0 }, /* TCP ? */
      { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.11.0", 	WSMALL(10), 0 }, /* TCP ? */
      { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.6.13.0", 	WSMALL(10), 0 }, /* TCP ? */
    #endif
      { "/usr/bin/mpstat", NULL, 		WLARGE(10), 0 },
      { "/usr/bin/w", NULL, 		WLARGE(10), 1 },
      { "/usr/bsd/w", NULL, 		WLARGE(10), 0 },
      { "/usr/bin/df", NULL, 		WLARGE(10), 1 },
      { "/bin/df", NULL, 			WLARGE(10), 0 },
      { "/usr/sbin/portstat", NULL, 	WLARGE(10), 0 },
      { "/usr/bin/iostat", NULL, 		WLARGE(0), 0 },
      { "/usr/bin/uptime", NULL, 		WLARGE(0), 1 },
      { "/usr/bsd/uptime", NULL, 		WLARGE(0), 0 },
      { "/bin/vmstat", "-f", 		WLARGE(0), 1 },
      { "/usr/bin/vmstat", "-f", 		WLARGE(0), 0 },
      { "/bin/vmstat", NULL, 		WLARGE(0), 1 },
      { "/usr/bin/vmstat", NULL, 		WLARGE(0), 0 },
      { "/usr/ucb/netstat", "-n", 	WLARGE(5), 1 },
      { "/usr/bin/netstat", "-n", 	WLARGE(5), 1 },
      { "/usr/sbin/netstat", "-n", 	WLARGE(5) , 1 },
      { "/bin/netstat", "-n", 		WLARGE(5) , 1 },
      { "/usr/etc/netstat", "-n", 	WLARGE(5) , 0 },
    #if defined( __sgi ) || defined( __hpux )
      { "/bin/ps", "-el", 		WLARGE(3), 1 },
    #endif /* __sgi || __hpux */
      { "/usr/ucb/ps", "aux", 		WLARGE(3), 1 },
      { "/usr/bin/ps", "aux", 		WLARGE(3), 1 },
      { "/bin/ps", "aux", 		WLARGE(3), 0 },
      { "/usr/bin/ipcs", "-a", 		WLARGE(5), 1 },
      { "/bin/ipcs", "-a", 		WLARGE(5), 0 },
      /* Unreliable source, depends on system usage */
      { "/etc/pstat", "-p", 		WLARGE(5), 1 },
      { "/bin/pstat", "-p", 		WLARGE(5), 0 },
      { "/etc/pstat", "-S", 		WLARGE(2), 1 },
      { "/bin/pstat", "-S", 		WLARGE(2), 0 },
      { "/etc/pstat", "-v", 		WLARGE(2), 1 },
      { "/bin/pstat", "-v", 		WLARGE(2), 0 },
      { "/etc/pstat", "-x", 		WLARGE(2), 1 },
      { "/bin/pstat", "-x", 		WLARGE(2), 0 },
      { "/etc/pstat", "-t", 		WLARGE(1), 1 },
      { "/bin/pstat", "-t", 		WLARGE(1), 0 },
      /* pstat is your friend */
      { "/usr/bin/last", "-n 50", 	WLARGE(3), 1 },
    #ifdef __sgi
      { "/usr/bsd/last", "-50", 		WLARGE(3), 0 },
    #endif /* __sgi */
    #ifdef __hpux
      { "/etc/last", "-50", 		WLARGE(3), 0 },
    #endif /* __hpux */
      { "/usr/bsd/last", "-n 50", 	WLARGE(3), 0 },
      { "/usr/local/bin/lsof", "-lnwP", 	WLARGE(3), 0 },
      /* Output is very system and version-dependent */
    #if 0
      { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.5.1.0", 	WLARGE(1), 0 }, /* ICMP ? */
      { "/usr/sbin/snmp_request", "localhost public get 1.3.6.1.2.1.5.3.0", 	WLARGE(1), 0 }, /* ICMP ? */
    #endif
      { "/etc/arp", "-a", 		WLARGE(1), 1 },
      { "/usr/etc/arp", "-a", 		WLARGE(1), 1 },
      { "/usr/bin/arp", "-a", 		WLARGE(1), 1 },
      { "/usr/sbin/arp", "-a", 		WLARGE(1), 0 },
      { "/usr/sbin/ripquery", "-nw 1 127.0.0.1", 	WLARGE(1 ), 0 },
      { "/bin/lpstat", "-t", 		WLARGE(1), 1 },
      { "/usr/bin/lpstat", "-t", 		WLARGE(1), 1 },
      { "/usr/ucb/lpstat", "-t", 		WLARGE(1), 0 },
    #if 0
      { "/usr/bin/tcpdump", "-c 5 -efvvx",WLARGE(10), 0 },
      /* This is very environment-dependant.  If
         network traffic is low, it'll probably time
         out before delivering 5 packets, which is OK
         because it'll probably be fixed stuff like
         ARP anyway */
      { "/usr/sbin/advfsstat", "-b usr_domain", 	WLARGE(0), 0 },
      { "/usr/sbin/advfsstat", "-l 2 usr_domain", 	WLARGE(5), 0 },
      { "/usr/sbin/advfsstat", "-p usr_domain", 	WLARGE(0), 0 },
      /* This is a complex and screwball program.  Some
         systems have things like rX_dmn, x = integer,
         for RAID systems, but the statistics are
         pretty dodgy */
    
      /* The following aren't enabled since they're somewhat slow and not very
         unpredictable, however they give an indication of the sort of sources
         you can use (for example the finger might be more useful on a
         firewalled internal network) */
      { "/usr/bin/finger", "@ml.media.mit.edu", 	WLARGE(9), 0 },
      { "/usr/local/bin/wget", "-O - http://lavarand.sgi.com/block.html", 	WLARGE(9 ), 0 },
      { "/bin/cat", "/usr/spool/mqueue/syslog", 	WLARGE(9), 0 },
    #endif /* 0 */
      { NULL, NULL, 0, 0 }
    };
    
    #undef WSMALL
    #undef WLARGE
    
    struct unix_random_source_state
    {
      const struct unix_random_source *source;
      pid_t pid;       /* Running process. */
      int fd;
      unsigned length; /* Amount of data read so far. */
    };
    
    static int
    spawn_source_process(unsigned *index,
    		     struct unix_random_source_state *state,
    		     int null)
    {
      unsigned i;
      pid_t pid;
      /* output[0] for reading, output[1] for writing. */
      int output[2];
          
      for (i = *index; random_sources[i].path; )
        {
          if (access(random_sources[i].path, X_OK) < 0)
    	{
    	  debug("unix_random.c: spawn_source_process: Skipping '%z'; not executable: %z\n",
    		random_sources[i].path, STRERROR(errno));
    	  i++;
    	}
          else
    	break;
        }
    
      if (!random_sources[i].path)
        {
          *index = i;
          return 0;
        }
      
      if (!lsh_make_pipe(output))
        {
          werror("unix_random.c: spawn_source_process: Can't create pipe (errno = %i): %z\n",
    	     errno, STRERROR(errno));
          return 0;
        }
      
      state->source = random_sources + i;
      
      if (!random_sources[i].has_alternative)
        i++;
      else
        /* Skip alternatives */
        while (random_sources[i].has_alternative)
          i++;
      
      *index = i;
    
      debug("unix_random.c: Trying source %z %z\n",
    	state->source->path,
    	state->source->arg ? state->source->arg : "");
      
      pid = fork();
      switch(pid)
        {
        default:
          /* Parent */
          close (output[1]);
          state->fd = output[0];
          state->pid = pid;
          io_set_close_on_exec(state->fd);
          io_set_nonblocking(state->fd);
          state->length = 0;
          return 1;
          
        case -1:
          /* Error */
          close(output[0]);
          close(output[1]);
          return 0;
        case 0:
          /* Child */
          if (dup2(output[1], STDOUT_FILENO) < 0)
    	{
    	  werror("unix_random.c: spawn_source_process: dup2 for stdout failed (errno = %i): %z\n",
    		 errno, STRERROR(errno));
    	  _exit(EXIT_FAILURE);
    	}
    
          /* Ignore stderr. */
          if (dup2(null, STDERR_FILENO) < 0)
    	{
    	  werror("unix_random.c: spawn_source_process: dup2 for stderr failed (errno = %i): %z\n",
    		 errno, STRERROR(errno));
    	  _exit(EXIT_FAILURE);
    	}
    	
          close (output[0]);
          close (output[1]);
          
          /* Works also if state->source->arg == NULL */
          execl(state->source->path, state->source->path, state->source->arg, NULL);
          
          werror("spawn_source_process: execl '%z' failed (errno = %i): %z\n",
    	     state->source->path, errno, STRERROR(errno));
          
          _exit(EXIT_FAILURE);
        }
    }
    
    static unsigned
    use_dev_random(struct hash_instance *hash)
    {
    #define DEVRANDOM_SIZE 40
      UINT8 buffer[DEVRANDOM_SIZE];
      unsigned count = 0;
      int res;
      
      int fd = open("/dev/urandom", O_RDONLY);
      if (fd < 0)
        return 0;
    
      do
        { res = read(fd, buffer, DEVRANDOM_SIZE); }
      while ( (res < 0) && (errno == EINTR));
    
      if (res < 0)
        werror("unix_random.c: Reading from /dev/urandom failed (errno = %i): %z\n",
    	   errno, STRERROR(errno));
      else if (res > 0)
        {
          HASH_UPDATE(hash, res, buffer);
          /* Count 4 bits of entropy for each byte. */
          count = res * 4;
        }
      else
        werror("unix_random.c: No data available on /dev/urandom!\n");
    
      close(fd);
      
      return count;
    }
    
    static unsigned
    use_procfs(struct hash_instance *hash)
    {
    #if 0
      /* Peter Gutmann's code uses the following sources. I'm not sure
       * what quality to assign to them. */
      static const char *proc_sources[] =
      {
        "/proc/interrupts", "/proc/loadavg", "/proc/locks",
        "/proc/meminfo", "/proc/stat", "/proc/net/tcp", "/proc/net/udp", 
        "/proc/net/dev", "/proc/net/ipx", NULL
      };
    #endif
      
      /* Not implemented. */
      return 0;
    }
    
    /* Spawn this number of processes. */
    #define UNIX_RANDOM_POLL_PROCESSES 10
    #define UNIX_RANDOM_THRESHOLD 200
    
    static unsigned
    background_poll(struct hash_instance *hash)
    {
      struct unix_random_source_state state[UNIX_RANDOM_POLL_PROCESSES];
      unsigned count = 0;
      unsigned running = 0;
      unsigned i;
      unsigned index = 0;
      int null;
      
      trace("unix_random.c: background_poll\n");
    
      null = open("/dev/null", O_WRONLY);
      if (null < 0)
        {
          werror("unix_random.c: background_poll: Failed to open /dev/null (errno = %i): %z\n",
    	     errno, STRERROR(errno));
          return count;
        }
      
      count += use_dev_random(hash);
      count += use_procfs(hash);
      
      for (i = 0; i < UNIX_RANDOM_POLL_PROCESSES; i++)
        state[i].fd = -1;
    
      for (running = 0; running<UNIX_RANDOM_POLL_PROCESSES; running++)
        {
          if (!spawn_source_process(&index, state + running, null))
    	break;
        }
    
      while (running)
        {
          struct pollfd fds[UNIX_RANDOM_POLL_PROCESSES];
          unsigned nfds;
          unsigned i;
    
          for (i = 0, nfds = 0; i < UNIX_RANDOM_POLL_PROCESSES; i++)
    	if (state[i].fd > 0)
    	  {
    	    fds[nfds].fd = state[i].fd;
    	    fds[nfds].events = MY_POLLIN;
    	    nfds++;
    	  }
    
          assert(nfds == running);
    
          debug("unix_random.c: calling poll, nfds = %i\n", nfds);
          if (poll(fds, nfds, -1) < 0)
    	{
    	  werror("unix_random.c: background_poll poll() failed (errno = %i): %z\n",
    		 errno, STRERROR(errno));
    	  return count;
    	}
    
          debug("unix_random.c: returned from poll.\n");
    
          for (i = 0; i<nfds; i++)
    	{
    	  debug("background_poll: poll for fd %i: events = 0x%xi, revents = 0x%xi.\n",
    		fds[i].fd, fds[i].events, fds[i].revents);
    
    	  /* Linux sets POLLHUP on EOF */
    #ifndef POLLHUP
    #define POLLHUP 0
    #endif
    	  if (fds[i].revents & (MY_POLLIN | POLLHUP))
    	    {
    #define BUFSIZE 1024
    	      UINT8 buffer[BUFSIZE];
    	      int res;
    	      unsigned j;
    	    
    	      for (j = 0; j<UNIX_RANDOM_POLL_PROCESSES; j++)
    		if (fds[i].fd == state[j].fd)
    		  break;
    
    	      assert(j < UNIX_RANDOM_POLL_PROCESSES);
    
    	      debug("background_poll: reading from source %z\n",
    		    state[j].source->path);
    	      
    	      res = read(fds[i].fd, buffer, BUFSIZE);
    #undef BUFSIZE
    	    
    	      if (res < 0)
    		{
    		  if (errno != EINTR)
    		    {
    		      werror("unix_random.c: background_poll read() failed (errno = %i): %z\n",
    			     errno, STRERROR(errno));
    		      return count;
    		    }
    		}
    	      else if (res > 0)
    		{
    		  HASH_UPDATE(hash, res, buffer);
    		  state[j].length += res;
    		}
    	      else
    		{ /* EOF */
    		  int status;
    		  unsigned entropy;
    		
    		  close(fds[i].fd);
    
    		  state[j].fd = -1;
    
    		  /* Estimate entropy */
    		  entropy = state[j].length * state[j].source->quality / UNIX_RANDOM_SOURCE_SCALE ;
    
    		  if (!entropy && state[j].source->small
    		      && (state[j].length > state[j].source->small))
    		    entropy = state[j].source->quality;
    		
    		  debug("unix_random.c: background_poll: Got %i bytes from %z %z,\n"
    			"               estimating %i bits of entropy.\n",
    			state[j].length,
    			state[j].source->path, state[j].source->arg ? state[j].source->arg : "",
    			entropy);
    
    		  count += entropy;
    		
    		  if (waitpid(state[j].pid, &status, 0) < 0)
    		    {
    		      werror("unix_random.c: background_poll waitpid() failed (errno = %i): %z\n",
    			     errno, STRERROR(errno));
    		      return count;
    		    }
    		  if (WIFEXITED(status))
    		    {
    		      if (WEXITSTATUS(status))
    			werror("unix_random.c: background_poll: %z exited unsuccessfully.\n",
    			       state[j].source->path);
    		    }
    		  else if (WIFSIGNALED(status))
    		    {
    		      werror("unix_random.c: background_poll: %z died from signal %i (%z).\n",
    			     state[j].source->path, WTERMSIG(status), STRSIGNAL(WTERMSIG(status)));
    		    }
    
    		  if (! ((count < UNIX_RANDOM_THRESHOLD)
    			 && spawn_source_process(&index, state + j, null)))
    		    running--;
    		}
    	    }
    	}
        }
      return count;
    }
    
    static void
    start_background_poll(struct unix_random *self)
    {
      pid_t pid;
      int output[2];
      
      assert(self->status == POLL_NO_POLL);
    
      trace("unix_random.c: start_background_poll\n");
      
      if (!lsh_make_pipe(output))
        {
          werror("Failed to create pipe for background randomness poll.\n");
          return;
        }
      
      pid = fork();  
      switch(pid)
        {
        default:
          /* Parent */
          close(output[1]);
          self->fd = output[0];
          io_set_close_on_exec(self->fd);
    
          if (self->reaper)
    	REAP(self->reaper, pid, make_unix_random_callback(self));
          
          self->pid = pid;
          self->status = POLL_RUNNING;
    
          werror("unix_random.c: Started background poll. pid = %i.\n",
    	     pid);
          
          return;
    
        case -1:
          /* Error */
          werror("Failed to fork background randomness poll (errno = %i): %z\n",
    	     errno, STRERROR(errno));
          return;
          
        case 0:
          /* Child */
          {
    	struct unix_random_poll_result result;
    	struct hash_instance *hash = MAKE_HASH(&sha1_algorithm);
    
    #if 0
    	int hang = 1;
    	while (hang)
    	  sleep(1);
    #endif
    	int null = open("/dev/null", O_RDONLY);
    
    	if (null < 0)
    	  {
    	    werror("start_background_poll: Failed to open /dev/null (errno = %i): %z\n",
    		   errno, STRERROR(errno));
    	    _exit(EXIT_FAILURE);
    	  }
    	
    	if (dup2(null, STDIN_FILENO) < 0)
    	  {
    	    werror("start_background_poll: dup2 for stdin failed (errno = %i): %z\n",
    		   errno, STRERROR(errno));
    
    	    _exit(EXIT_FAILURE);
    	  }
    
    	close(null);
    	close(output[0]);
    	io_set_close_on_exec(output[1]);
    	
    	/* Change uid */
    	if (!getuid())
    	  setuid(self->poll_uid);
    	
    	if (!getuid())
    	  _exit(EXIT_FAILURE);
    
    	result.count = background_poll(hash);
    
    	assert(hash->hash_size == sizeof(result.data));
    	HASH_DIGEST(hash, result.data);
    	
    	if (!write_raw(output[1], sizeof(result), (UINT8 *) &result))
    	  _exit(EXIT_SUCCESS);
    
    	_exit(EXIT_FAILURE);
          }
        }
    }
    
    static void
    wait_background_poll(struct unix_random *self)
    {
      int status;
      
      assert(self->status == POLL_RUNNING);
      trace("unix_random.c: wait_background_poll\n");
      self->status = POLL_FAILED;
      
      if (waitpid(self->pid, &status, 0) == self->pid)
        {
          if (WIFEXITED(status) && !WEXITSTATUS(status))
    	self->status = POLL_FINISHED;
        }
    
      if (self->reaper)
        REAP(self->reaper, self->pid, NULL);  
    }
    
    
    static unsigned
    finish_background_poll(struct unix_random *self, struct hash_instance *hash)
    {
      unsigned count = 0;
    
      switch(self->status)
        {
        case POLL_FINISHED:
          {
    	struct unix_random_poll_result result;
    	const struct exception *e;
    	
    	e = read_raw(self->fd, sizeof(result), (UINT8 *) &result);
    	
    	if (e)
    	  werror("Failed to read result from background randomness poll.\n");
    	else
    	  {
    	    HASH_UPDATE(hash, UNIX_RANDOM_POLL_SIZE, result.data);
    	    count = result.count;
    	  }
    	break;
          }
        case POLL_FAILED:
          werror("Background randomness poll failed.\n");
          break;
    
        case POLL_NO_POLL:
          return 0;
          
        default:
          fatal("finish_background_poll: Internal error.\n");
        }
      close(self->fd);
      self->status = POLL_NO_POLL;
    
      return count;
    }  
    
    static unsigned
    use_seed_file(struct hash_instance *hash)
    {
      if (getuid())
        {
          /* Lock and read seed file. */
        }
      
      return 0;
    }
    
    #define HASH_OBJECT(h, x) HASH_UPDATE((h), sizeof(x), (UINT8 *) &(x))
    
    static unsigned
    do_unix_random_slow(struct random_poll *s, struct hash_instance *hash)
    {
      CAST(unix_random, self, s);
      unsigned count;
      time_t now = time(NULL); 
      pid_t pid = getpid();
    
      /* Make sure we don't start with the same state, if several
       * instances are initializing at about the same time. */
      
      HASH_OBJECT(hash, now);
      HASH_OBJECT(hash, pid);
      
      if (self->status == POLL_NO_POLL)
        start_background_poll(self);
    
      if (self->status == POLL_RUNNING)
        wait_background_poll(self);
    
      count = finish_background_poll(self, hash);
    
      /* Do this in the main process, as the background process may run as
       * different user. */
      count += use_seed_file(hash);
      
      return count;
    }
    
    static unsigned
    do_unix_random_fast(struct random_poll *s, struct hash_instance *hash)
    {
      CAST(unix_random, self, s);
      unsigned count = 0;
    
    #if HAVE_GETRUSAGE
      {
        struct rusage rusage;
        if (getrusage(RUSAGE_SELF, &rusage) < 0)
          fatal("do_unix_random_fast: getrusage() failed: (errno = %i) %z\n",
    	    errno, STRERROR(errno));
        
        HASH_OBJECT(hash, rusage);
        count += 1;
      }
    #endif /* HAVE_GETRUSAGE */
    #if HAVE_GETTIMEOFDAY
      {
        struct timeval tv;
        if (gettimeofday(&tv, NULL) < 0)
          fatal("do_unix_random_fast: gettimeofday failed(errno = %i) %z\n",
    	    errno, STRERROR(errno));
    
        HASH_OBJECT(hash, tv);
      }
    #endif /* HAVE_GETTIMEOFDAY */
    
      {
        /* Fallback that is useful if nothing else works. Count the number
         * of slow polls between time() ticks, and count one bit of
         * entropy if we have more than 2 calls or more than two seconds
         * between calls. */
        
        time_t now = time(NULL);
        self->time_count++;
        if (now != self->previous_time)
          {
    	if ( (self->time_count > 2) || ((now - self->previous_time) > 2))
    	  count++;
    	
    	HASH_OBJECT(hash, now);
    	HASH_OBJECT(hash, self->time_count);
    	
    	self->time_count = 0;
    	self->previous_time = now;
          }
      }
    
      return count;
    }
    
    static void do_unix_random_background(struct random_poll *s)
    {
      CAST(unix_random, self, s);
    
      if (self->status == POLL_NO_POLL)
        start_background_poll(self);
    }
    
    /* Using a NULL reaper argument is ok. It must be supplied only if the
     * application is using a reaper, as that may get to our child process
     * before we can waitpid it. */
    
    struct random_poll *
    make_unix_random(struct reap *reaper)
    {
      NEW(unix_random, self);
    
      struct passwd *pw = getpwnam("nobody");
      
      self->super.slow = do_unix_random_slow;
      self->super.fast = do_unix_random_fast;
      self->super.background = do_unix_random_background;
      
      self->reaper = reaper;
    
      if (pw && pw->pw_uid)
        self->poll_uid = pw->pw_uid;
      else
        self->poll_uid = (uid_t) -1;
    
      self->status = POLL_NO_POLL;
      self->previous_time = time(NULL);
      self->time_count = 0;
    
      return &self->super;
    }