mirror of
https://github.com/lcn2/calc.git
synced 2025-08-19 01:13:27 +03:00
Some folks might think: “you still use RCS”?!? And we will say, hey, at least we switched from SCCS to RCS back in … I think it was around 1994 ... at least we are keeping up! :-) :-) :-) Logs say that SCCS version 18 became RCS version 19 on 1994 March 18. RCS served us well. But now it is time to move on. And so we are switching to git. Calc releases produce a lot of file changes. In the 125 releases of calc since 1996, when I started managing calc releases, there have been 15473 file mods!
466 lines
14 KiB
C
466 lines
14 KiB
C
/*
|
|
* seed - produce a pseudo-random seeds
|
|
*
|
|
* Copyright (C) 1999-2007 Landon Curt Noll
|
|
*
|
|
* Calc is open software; you can redistribute it and/or modify it under
|
|
* the terms of the version 2.1 of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* Calc 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 Lesser General
|
|
* Public License for more details.
|
|
*
|
|
* A copy of version 2.1 of the GNU Lesser General Public License is
|
|
* distributed with calc under the filename COPYING-LGPL. You should have
|
|
* received a copy with calc; if not, write to Free Software Foundation, Inc.
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Under source code control: 1999/10/03 10:06:53
|
|
* File existed as early as: 1999
|
|
*
|
|
* chongo <was here> /\oo/\ http://www.isthe.com/chongo/
|
|
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
|
|
*/
|
|
|
|
/*
|
|
* Generate a quasi-random seed based on system and process information.
|
|
*
|
|
* NOTE: This is not a good source of chaotic data. The LavaRnd
|
|
* system does a much better job of that. See:
|
|
*
|
|
* http://www.LavaRnd.org/
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include "have_stdlib.h"
|
|
#if defined(HAVE_STDLIB_H)
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include "have_unistd.h"
|
|
#if defined(HAVE_UNISTD_H)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
# include <process.h>
|
|
# define pid_t int
|
|
#endif
|
|
|
|
/*
|
|
* PORTING NOTE:
|
|
* These includes are used by pseudo_seed(). If for some
|
|
* reason some of these include files are missing or damaged
|
|
* on your system, feel free to remove them (and the related
|
|
* calls inside pseudo_seed()), add or replace them. The
|
|
* pseudo_seed() function just needs to gather a bunch of
|
|
* information about the process and system state so the
|
|
* loss or inclusion of a few other calls should not hurt
|
|
* that much.
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include "have_times.h"
|
|
#if defined(HAVE_TIME_H)
|
|
#include <time.h>
|
|
#endif
|
|
#if defined(HAVE_SYS_TIME_H)
|
|
#include <sys/time.h>
|
|
#endif
|
|
#if defined(HAVE_SYS_TIMES_H)
|
|
#include <sys/times.h>
|
|
#endif
|
|
#if !defined(_WIN32)
|
|
# include <sys/resource.h>
|
|
#endif
|
|
#include <setjmp.h>
|
|
#include "qmath.h"
|
|
#include "longbits.h"
|
|
#include "have_ustat.h"
|
|
#include "have_getsid.h"
|
|
#include "have_getpgid.h"
|
|
#include "have_gettime.h"
|
|
#include "have_getprid.h"
|
|
#include "have_urandom.h"
|
|
#include "have_rusage.h"
|
|
#include "have_uid_t.h"
|
|
#if defined(HAVE_USTAT)
|
|
# include <ustat.h>
|
|
#endif
|
|
#if defined(HAVE_URANDOM)
|
|
# include <fcntl.h>
|
|
# define DEV_URANDOM "/dev/urandom"
|
|
# define DEV_URANDOM_POOL 16
|
|
#endif
|
|
|
|
|
|
/*
|
|
* 64 bit hash value
|
|
*/
|
|
#if defined(HAVE_B64)
|
|
typedef USB64 hash64;
|
|
#else
|
|
struct s_hash64 {
|
|
USB32 w32[2];
|
|
};
|
|
typedef struct s_hash64 hash64;
|
|
#endif
|
|
|
|
|
|
/*
|
|
* 64-bit initial basis - Based on the 64-bit FNV basis value
|
|
*
|
|
* We start the hash at a non-zero value at the beginning so that
|
|
* hashing blocks of data with all 0 bits do not map onto the same
|
|
* 0 hash value. The virgin value that we use below is the hash value
|
|
* that we would get from following 32 ASCII characters:
|
|
*
|
|
* chongo <Landon Curt Noll> /\../\
|
|
*
|
|
* Note that the \'s above are not back-slashing escape characters.
|
|
* They are literal ASCII backslash 0x5c characters.
|
|
*
|
|
* The effect of this virgin initial value is the same as starting
|
|
* with 0 and pre-pending those 32 characters onto the data being
|
|
* hashed.
|
|
*
|
|
* Yes, even with this non-zero virgin value there is a set of data
|
|
* that will result in a zero hash value. Worse, appending any
|
|
* about of zero bytes will continue to produce a zero hash value.
|
|
* But that would happen with any initial value so long as the
|
|
* hash of the initial was the `inverse' of the virgin prefix string.
|
|
*
|
|
* But then again for any hash function, there exists sets of data
|
|
* which that the hash of every member is the same value. That is
|
|
* life with many to few mapping functions. All we do here is to
|
|
* prevent sets whose members consist of 0 or more bytes of 0's from
|
|
* being such an awkward set.
|
|
*
|
|
* And yes, someone can figure out what the magic 'inverse' of the
|
|
* 32 ASCII character are ... but this hash function is NOT intended
|
|
* to be a cryptographic hash function, just a fast and reasonably
|
|
* good hash function.
|
|
*/
|
|
#if defined(HAVE_B64)
|
|
# define PRIVATE_64_BASIS ((hash64)(0xcbf29ce484222325ULL))
|
|
#else
|
|
# define PRIVATE_64_BASIS_0 ((USB32)0x2325)
|
|
# define PRIVATE_64_BASIS_1 ((USB32)0x8422)
|
|
# define PRIVATE_64_BASIS_2 ((USB32)0x9ce4)
|
|
# define PRIVATE_64_BASIS_3 ((USB32)0xcbf2)
|
|
#endif
|
|
|
|
|
|
/*
|
|
* prvate_hash64_buf - perform a Fowler/Noll/Vo-1 64 bit hash
|
|
*
|
|
* input:
|
|
* buf - start of buffer to hash
|
|
* len - length of buffer in octets
|
|
* hval - the hash value to modify
|
|
*
|
|
* returns:
|
|
* 64 bit hash as a static hash64 structure
|
|
*/
|
|
S_FUNC hash64
|
|
prvate_hash64_buf(char *buf, unsigned len)
|
|
{
|
|
hash64 hval; /* current hash value */
|
|
#if !defined(HAVE_B64)
|
|
USB32 val[4]; /* hash value in base 2^16 */
|
|
USB32 tmp[4]; /* tmp 64 bit value */
|
|
#endif /* HAVE_B64 */
|
|
char *buf_end = buf+len; /* beyond end of hash area */
|
|
|
|
/*
|
|
* FNV-1 - Fowler/Noll/Vo-1 64 bit hash
|
|
*
|
|
* The basis of this hash algorithm was taken from an idea sent
|
|
* as reviewer comments to the IEEE POSIX P1003.2 committee by:
|
|
*
|
|
* Phong Vo (http://www.research.att.com/info/kpv/)
|
|
* Glenn Fowler (http://www.research.att.com/~gsf/)
|
|
*
|
|
* In a subsequent ballot round:
|
|
*
|
|
* Landon Curt Noll (http://www.isthe.com/chongo/)
|
|
*
|
|
* improved on their algorithm. Some people tried this hash
|
|
* and found that it worked rather well. In an EMail message
|
|
* to Landon, they named it ``Fowler/Noll/Vo'' or the FNV hash.
|
|
*
|
|
* FNV hashes are architected to be fast while maintaining a low
|
|
* collision rate. The FNV speed allows one to quickly hash lots
|
|
* of data while maintaining a reasonable collision rate. See:
|
|
*
|
|
* http://www.isthe.com/chongo/tech/comp/fnv/
|
|
*
|
|
* for more details as well as other forms of the FNV hash.
|
|
*
|
|
* NOTE: For general hash functions, we recommend using the
|
|
* FNV-1a hash function. The use of FNV-1 is kept
|
|
* for backwards compatibility purposes and because
|
|
* the use of FNV-1 in this special purpose, suffices.
|
|
*/
|
|
#if defined(HAVE_B64)
|
|
/* hash each octet of the buffer */
|
|
for (hval = PRIVATE_64_BASIS; buf < buf_end; ++buf) {
|
|
|
|
/* multiply by 1099511628211ULL mod 2^64 using 64 bit longs */
|
|
hval *= (hash64)1099511628211ULL;
|
|
|
|
/* xor the bottom with the current octet */
|
|
hval ^= (hash64)(*buf);
|
|
}
|
|
|
|
#else /* HAVE_B64 */
|
|
|
|
/* hash each octet of the buffer */
|
|
val[0] = PRIVATE_64_BASIS_0;
|
|
val[1] = PRIVATE_64_BASIS_1;
|
|
val[2] = PRIVATE_64_BASIS_2;
|
|
val[3] = PRIVATE_64_BASIS_3;
|
|
for (; buf < buf_end; ++buf) {
|
|
|
|
/*
|
|
* multiply by 1099511628211 mod 2^64 using 32 bit longs
|
|
*
|
|
* Using 1099511628211, we have the following digits base 2^16:
|
|
*
|
|
* 0x0 0x100 0x0 0x1b3
|
|
*/
|
|
/* multiply by the lowest order digit base 2^16 */
|
|
tmp[0] = val[0] * 0x1b3;
|
|
tmp[1] = val[1] * 0x1b3;
|
|
tmp[2] = val[2] * 0x1b3;
|
|
tmp[3] = val[3] * 0x1b3;
|
|
/* multiply by the other non-zero digit */
|
|
tmp[2] += val[0] << 8; /* tmp[2] += val[0] * 0x100 */
|
|
tmp[3] += val[1] << 8; /* tmp[1] += val[1] * 0x100 */
|
|
/* proapage carries */
|
|
tmp[1] += (tmp[0] >> 16);
|
|
val[0] = tmp[0] & 0xffff;
|
|
tmp[2] += (tmp[1] >> 16);
|
|
val[1] = tmp[1] & 0xffff;
|
|
val[3] = tmp[3] + (tmp[2] >> 16);
|
|
val[2] = tmp[2] & 0xffff;
|
|
/*
|
|
* Doing a val[3] &= 0xffff; is not really needed since it simply
|
|
* removes multiples of 2^64. We can discard these excess bits
|
|
* outside of the loop when we convert to hash64.
|
|
*/
|
|
|
|
/* xor the bottom with the current octet */
|
|
val[0] ^= (USB32)(*buf);
|
|
}
|
|
|
|
/* convert to hash64 */
|
|
/* hval.w32[1] = 0xffff&(val[3]<<16)+val[2]; */
|
|
hval.w32[1] = (val[3]<<16) + val[2];
|
|
hval.w32[0] = (val[1]<<16) + val[0];
|
|
|
|
#endif /* HAVE_B64 */
|
|
|
|
/* return our hash value */
|
|
return hval;
|
|
}
|
|
|
|
|
|
/*
|
|
* pseudo_seed - seed the generator with a quasi-random seed
|
|
*
|
|
* Generate a quasi-random seed based on system and process information.
|
|
*
|
|
* NOTE: This is not a good source of chaotic data. The LavaRnd
|
|
* system does a much better job of that. See:
|
|
*
|
|
* http://www.LavaRnd.org/
|
|
*
|
|
* PORTING NOTE:
|
|
* If when porting this code to your system and something
|
|
* won't compile, just remove that line or replace it with
|
|
* some other system call. We don't have to have every call
|
|
* operating below. We only want to hash the resulting data.
|
|
*
|
|
* returns:
|
|
* a pseudo-seed as a NUMBER over the range [0, 2^64)
|
|
*/
|
|
NUMBER *
|
|
pseudo_seed(void)
|
|
{
|
|
struct { /* data used for quasi-random seed */
|
|
#if defined(HAVE_GETTIME)
|
|
# if defined(CLOCK_SGI_CYCLE)
|
|
struct timespec sgi_cycle; /* SGI hardware clock */
|
|
# endif
|
|
# if defined(CLOCK_REALTIME)
|
|
struct timespec realtime; /* POSIX realtime clock */
|
|
# endif
|
|
#endif
|
|
#if defined(HAVE_GETPRID)
|
|
prid_t getprid; /* project ID */
|
|
#endif
|
|
#if defined(HAVE_URANDOM)
|
|
int urandom_fd; /* open descriptor for /dev/urandom */
|
|
int urandom_ret; /* read() of /dev/random */
|
|
char urandom_pool[DEV_URANDOM_POOL]; /* /dev/urandom data pool */
|
|
#endif
|
|
#if defined(HAVE_SYS_TIME_H)
|
|
struct timeval tp; /* time of day */
|
|
#endif
|
|
pid_t getpid; /* process ID */
|
|
#if !defined(_WIN32)
|
|
pid_t getppid; /* parent process ID */
|
|
#endif
|
|
#if defined(HAVE_UID_T)
|
|
uid_t getuid; /* real user ID */
|
|
uid_t geteuid; /* effective user ID */
|
|
gid_t getgid; /* real group ID */
|
|
gid_t getegid; /* effective group ID */
|
|
#endif
|
|
struct stat stat_dot; /* stat of "." */
|
|
struct stat stat_dotdot; /* stat of ".." */
|
|
struct stat stat_tmp; /* stat of "/tmp" */
|
|
struct stat stat_root; /* stat of "/" */
|
|
struct stat fstat_stdin; /* stat of stdin */
|
|
struct stat fstat_stdout; /* stat of stdout */
|
|
struct stat fstat_stderr; /* stat of stderr */
|
|
#if defined(HAVE_USTAT)
|
|
struct ustat ustat_dot; /* usage stat of "." */
|
|
struct ustat ustat_dotdot; /* usage stat of ".." */
|
|
struct ustat ustat_tmp; /* usage stat of "/tmp" */
|
|
struct ustat ustat_root; /* usage stat of "/" */
|
|
struct ustat ustat_stdin; /* usage stat of stdin */
|
|
struct ustat ustat_stdout; /* usage stat of stdout */
|
|
struct ustat ustat_stderr; /* usage stat of stderr */
|
|
#endif
|
|
#if defined(HAVE_GETSID)
|
|
pid_t getsid; /* session ID */
|
|
#endif
|
|
#if defined(HAVE_GETPGID)
|
|
pid_t getpgid; /* process group ID */
|
|
#endif
|
|
#if defined(HAVE_GETRUSAGE)
|
|
struct rusage rusage; /* resource utilization */
|
|
struct rusage rusage_chld; /* resource utilization of children */
|
|
#endif
|
|
#if defined(HAVE_SYS_TIME_H)
|
|
struct timeval tp2; /* time of day again */
|
|
struct tms times; /* process times */
|
|
#endif
|
|
time_t time; /* local time */
|
|
size_t size; /* size of this data structure */
|
|
jmp_buf env; /* setjmp() context */
|
|
char *sdata_p; /* address of this structure */
|
|
} sdata;
|
|
hash64 hash_val; /* fnv64 hash of sdata */
|
|
ZVALUE hash; /* hash_val as a ZVALUE */
|
|
NUMBER *ret; /* return seed as a NUMBER */
|
|
|
|
/*
|
|
* pick up process/system information
|
|
*
|
|
* NOTE:
|
|
* We do care (that much) if these calls fail. We do not
|
|
* need to process any data in the 'sdata' structure.
|
|
*/
|
|
#if defined(HAVE_GETTIME)
|
|
# if defined(CLOCK_SGI_CYCLE)
|
|
(void) clock_gettime(CLOCK_SGI_CYCLE, &sdata.sgi_cycle);
|
|
# endif
|
|
# if defined(CLOCK_REALTIME)
|
|
(void) clock_gettime(CLOCK_REALTIME, &sdata.realtime);
|
|
# endif
|
|
#endif
|
|
#if defined(HAVE_GETPRID)
|
|
sdata.getprid = getprid();
|
|
#endif
|
|
#if defined(HAVE_URANDOM)
|
|
sdata.urandom_fd = open(DEV_URANDOM, O_NONBLOCK|O_RDONLY);
|
|
if (sdata.urandom_fd >= 0) {
|
|
sdata.urandom_ret = read(sdata.urandom_fd,
|
|
&sdata.urandom_pool, DEV_URANDOM_POOL);
|
|
close(sdata.urandom_fd);
|
|
} else {
|
|
memset(&sdata.urandom_pool, EOF, DEV_URANDOM_POOL);
|
|
sdata.urandom_ret = EOF;
|
|
}
|
|
#endif /* HAVE_URANDOM */
|
|
#if defined(HAVE_SYS_TIME_H)
|
|
(void) gettimeofday(&sdata.tp, NULL);
|
|
#endif
|
|
sdata.getpid = getpid();
|
|
#if !defined(_WIN32)
|
|
sdata.getppid = getppid();
|
|
#endif
|
|
#if defined(HAVE_UID_T)
|
|
sdata.getuid = getuid();
|
|
sdata.geteuid = geteuid();
|
|
sdata.getgid = getgid();
|
|
sdata.getegid = getegid();
|
|
#endif
|
|
(void) stat(".", &sdata.stat_dot);
|
|
(void) stat("..", &sdata.stat_dotdot);
|
|
(void) stat("/tmp", &sdata.stat_tmp);
|
|
(void) stat("/", &sdata.stat_root);
|
|
(void) fstat(0, &sdata.fstat_stdin);
|
|
(void) fstat(1, &sdata.fstat_stdout);
|
|
(void) fstat(2, &sdata.fstat_stderr);
|
|
#if defined(HAVE_USTAT)
|
|
(void) ustat(sdata.stat_dotdot.st_dev, &sdata.ustat_dotdot);
|
|
(void) ustat(sdata.stat_dot.st_dev, &sdata.ustat_dot);
|
|
(void) ustat(sdata.stat_tmp.st_dev, &sdata.ustat_tmp);
|
|
(void) ustat(sdata.stat_root.st_dev, &sdata.ustat_root);
|
|
(void) ustat(sdata.fstat_stdin.st_dev, &sdata.ustat_stdin);
|
|
(void) ustat(sdata.fstat_stdout.st_dev, &sdata.ustat_stdout);
|
|
(void) ustat(sdata.fstat_stderr.st_dev, &sdata.ustat_stderr);
|
|
#endif
|
|
#if defined(HAVE_GETSID)
|
|
sdata.getsid = getsid((pid_t)0);
|
|
#endif
|
|
#if defined(HAVE_GETPGID)
|
|
sdata.getpgid = getpgid((pid_t)0);
|
|
#endif
|
|
#if defined(HAVE_GETRUSAGE)
|
|
(void) getrusage(RUSAGE_SELF, &sdata.rusage);
|
|
(void) getrusage(RUSAGE_CHILDREN, &sdata.rusage_chld);
|
|
#endif
|
|
#if defined(HAVE_SYS_TIME_H)
|
|
(void) gettimeofday(&sdata.tp2, NULL);
|
|
(void) times(&sdata.times);
|
|
#endif
|
|
sdata.time = time(NULL);
|
|
sdata.size = sizeof(sdata);
|
|
(void) setjmp(sdata.env);
|
|
sdata.sdata_p = (char *)&sdata;
|
|
|
|
/*
|
|
* seed the generator with the above data
|
|
*/
|
|
hash_val = prvate_hash64_buf((char *)&sdata, sizeof(sdata));
|
|
|
|
/*
|
|
* load the hash data into the ZVALUE
|
|
*
|
|
* We do not care about byte-order or endian issues, we just
|
|
* want to load in data.
|
|
*/
|
|
hash.len = sizeof(hash_val) / sizeof(HALF);
|
|
hash.v = alloc(hash.len);
|
|
hash.sign = 0;
|
|
memcpy((void *)hash.v, (void *)&hash_val, hash.len*sizeof(HALF));
|
|
ztrim(&hash);
|
|
|
|
/*
|
|
* return a number
|
|
*/
|
|
ret = qalloc();
|
|
ret->num = hash;
|
|
return ret;
|
|
}
|