]> git.pond.sub.org Git - empserver/blobdiff - src/lib/gen/chance.c
Improve portability to really outmoded compilers
[empserver] / src / lib / gen / chance.c
index 2a00789fd0b2e4a1094271fc5c52e7970736e1b5..b20dfb9bda63d54445ab06e097dcba95fae2661e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Empire - A multi-player, client/server Internet based war game.
- *  Copyright (C) 1986-2013, Dave Pare, Jeff Bailey, Thomas Ruschak,
+ *  Copyright (C) 1986-2015, Dave Pare, Jeff Bailey, Thomas Ruschak,
  *                Ken Stevens, Steve McClure, Markus Armbruster
  *
  *  Empire is free software: you can redistribute it and/or modify
 
 #include <config.h>
 
+#include <fcntl.h>
 #include <math.h>
 #include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
 #include "chance.h"
 #include "mt19937ar.h"
 
@@ -115,3 +118,60 @@ seed_prng(unsigned seed)
 {
     init_genrand(seed);
 }
+
+/*
+ * Note: this is DJB's hash function when unsigned is 32 bits and hash
+ * is initially 5381.
+ */
+static unsigned
+djb_hash(unsigned hash, void *buf, size_t sz)
+{
+    unsigned char *bp;
+
+    for (bp = buf; bp < (unsigned char *)buf + sz; bp++)
+       hash = hash * 33 ^ *bp;
+
+    return hash;
+}
+
+/*
+ * Pick a reasonably random seed for the pseudo-random number generator.
+ */
+unsigned
+pick_seed(void)
+{
+    int fd;
+    unsigned seed;
+    int got_seed = 0;
+    struct timeval tv;
+    pid_t pid;
+
+    /*
+     * Modern systems provide random number devices, but the details
+     * vary.  On many systems, /dev/random blocks when the kernel
+     * entropy pool has been depleted, while /dev/urandom doesn't.
+     * The former should only be used for generating long-lived
+     * cryptographic keys.  On other systems, both devices behave
+     * exactly the same, or only /dev/random exists.
+     *
+     * Try /dev/urandom first, and if it can't be opened, blindly try
+     * /dev/random.
+     */
+    fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK);
+    if (fd < 0)
+       fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+    if (fd >= 0) {
+       got_seed = read(fd, &seed, sizeof(seed)) == sizeof(seed);
+       close(fd);
+    }
+
+    if (!got_seed) {
+       /* Kernel didn't provide, fall back to hashing time and PID */
+       gettimeofday(&tv, NULL);
+       seed = djb_hash(5381, &tv, sizeof(tv));
+       pid = getpid();
+       seed = djb_hash(seed, &pid, sizeof(pid));
+    }
+
+    return seed;
+}