/*****************************************************************************/
/*
                                  RandSeed.c

Small utility designed to populate a file with 2048 bytes of PRNG seed data. 
The seed should be high entropy.  This utility generates it by reading selected
system SPI performance data and using this along with binary time (~312 bytes
of base data on a single CPU machine, up to ~480 bytes on a four CPU system),
used to fill the buffer with non-deterministic bytes which are then XORed with
one another.  This buffer of 2048 bytes is then written to a file with a
default name of RANDSEED.DAT (can be specified on the command-line).


BUILD DETAILS
-------------
See BUILD_RANDSEED.COM procedure.


COPYRIGHT
---------
Copyright (C) 2002-2022 Mark G.Daniel

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


VERSION HISTORY (update SOFTWAREVN as well)
---------------
23-DEC-2003  MGD  v1.0.1, minor conditional mods to support IA64
08-OCT-2002  MGD  v1.0.0, initial development
*/
/*****************************************************************************/

#define SOFTWAREVN "1.0.1"
#define SOFTWARENM "RANDSEED"
#ifdef __ALPHA
   char SoftwareID [] = SOFTWARENM " AXP-" SOFTWAREVN;
#endif
#ifdef __ia64
   char SoftwareID [] = SOFTWARENM " IA64-" SOFTWAREVN;
#endif
#ifdef __VAX
#  error VAX no longer implemented
#endif
#ifdef __x86_64
   char SoftwareID [] = SOFTWARENM " X86-" SOFTWAREVN;
#endif

/* standard C header files */
#include <ctype.h>
#include <errno.h>
#include <file.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unixlib.h>
#include <unixio.h>

/* VMS-related header files */
#include <descrip.h>
#include <starlet.h>
#include <ssdef.h>
#include <stsdef.h>
#include <syidef.h>

/* values from GETSPIDEF.H */

#define GETSPI$_MODES      4096

#define MSCP_ALL_TOTAL     13
#define GETSPI$_MSCP_ALL   4235

#define GETSPI_ITEM_TOTAL  29
#define GETSPI$_PROCS      4122
#define GETSPI$_PROC       4123
#define GETSPI$_FRLIST     4124
#define GETSPI$_MOLIST     4125
#define GETSPI$_FAULTS     4126
#define GETSPI$_PREADS     4127
#define GETSPI$_PWRITES    4128
#define GETSPI$_PWRITEIO   4129
#define GETSPI$_PREADIO    4130
#define GETSPI$_DIRIO      4150
#define GETSPI$_BUFIO      4151
#define GETSPI$_FCPCALLS   4155
#define GETSPI$_FCPREAD    4156
#define GETSPI$_FCPWRITE   4157
#define GETSPI$_FCPCACHE   4158
#define GETSPI$_FCPCPU     4159
#define GETSPI$_FCPHIT     4160
#define GETSPI$_FCPSPLIT   4161
#define GETSPI$_FCPFAULT   4162
#define GETSPI$_ENQNEW     4163
#define GETSPI$_ENQCVT     4164
#define GETSPI$_DEQ        4165
#define GETSPI$_BLKAST     4166
#define GETSPI$_ENQWAIT    4167
#define GETSPI$_ENQNOTQD   4168
#define GETSPI$_DLCKSRCH   4169
#define GETSPI$_DLCKFND    4170
#define GETSPI$_NUMLOCKS   4171
#define GETSPI$_NUMRES     4172

#define MODES_TOTAL 8
#define MODE_INTERRUPT 0
#define MODE_MULTI_PROC 1
#define MODE_KERNEL 2
#define MODE_EXECUTIVE 3
#define MODE_SUPERVISOR 4
#define MODE_USER 5
#define MODE_COMPATIBILITY 6
#define MODE_NULL 7

#ifndef __VAX
#   pragma nomember_alignment
#endif

#define boolean int
#define true 1
#define false 0
 
#define VMSok(x) ((x) & STS$M_SUCCESS)
#define VMSnok(x) !(((x) & STS$M_SUCCESS))
 
char  Utility [] = "RANDSEED";

boolean  Debug;

char  RandBuffer [2048],
      RandElbowRoom [32];

/* function prototypes */
void GenerateRandomData ();
int EXE$GETSPI (__unknown_params);
boolean strsame (char*, char*, int);

/*****************************************************************************/
/*
*/

int main
(
int argc,
char *argv[]
)
{
   int  cnt, fd, status;
   char  *FileNamePtr;

   /*********/
   /* begin */
   /*********/

   if (getenv ("RANDSEED$DBUG")) Debug = true;

   FileNamePtr = NULL;
   for (cnt = 1; cnt < argc; cnt++)
   {
      if (strsame (argv[cnt], "/DBUG", -1))
         Debug = true;
      else
         FileNamePtr = argv[cnt];
   }
   if (!FileNamePtr) FileNamePtr = "RANDSEED.DAT";

   GenerateRandomData ();

   if (Debug) fprintf (stdout, "|%s|\n", FileNamePtr);
   fd = open (FileNamePtr, O_WRONLY | O_CREAT, "ctx=bin");
   if (fd < 0) exit (vaxc$errno);
   write (fd, RandBuffer, sizeof(RandBuffer));
   close (fd);
} 

/*****************************************************************************/
/*
Collect System Performance Information.

Uses recently undocument system call EXE$GETSPI().  The code in this function
is largely derived from a VMS system performance DECwindows utility named SPI.
*/ 

void GenerateRandomData ()

{
   static unsigned long  MscpAll [MSCP_ALL_TOTAL],
                         GetSpiItem [GETSPI_ITEM_TOTAL];

#ifndef __VAX
#   pragma nomember_alignment
#   pragma member_alignment __save
#endif

   /* structure for a single CPU's mode ticks */
   static struct SingleCPU {
       unsigned char  CPUid;
       unsigned long  Count[MODES_TOTAL];
   };

   /* a structure all CPU's mode ticks for a system with up to 32 CPUs */
   static struct {
       unsigned long  NumberOfCPUs;
       struct SingleCPU  CPU[32];
   } ModeTicks;

   /* initialize structure for SPI items */
   static struct {
      short  BufferLength;
      short  ItemCode;
      void  *BufferPtr;
      void  *LengthPtr;
   } ItemList[] =
   {
       { sizeof(ModeTicks),     GETSPI$_MODES,    &ModeTicks, 0 },
       { sizeof(MscpAll),       GETSPI$_MSCP_ALL, &MscpAll, 0 },
       { sizeof(unsigned long), GETSPI$_FRLIST,   &GetSpiItem[0], 0 },
       { sizeof(unsigned long), GETSPI$_MOLIST,   &GetSpiItem[1], 0 },
       { sizeof(unsigned long), GETSPI$_FAULTS,   &GetSpiItem[2], 0 },
       { sizeof(unsigned long), GETSPI$_PREADS,   &GetSpiItem[3], 0 },
       { sizeof(unsigned long), GETSPI$_PWRITES,  &GetSpiItem[4], 0 },
       { sizeof(unsigned long), GETSPI$_PWRITEIO, &GetSpiItem[5], 0 },
       { sizeof(unsigned long), GETSPI$_PREADIO,  &GetSpiItem[6], 0 },
       { sizeof(unsigned long), GETSPI$_DIRIO,    &GetSpiItem[7], 0 },
       { sizeof(unsigned long), GETSPI$_BUFIO,    &GetSpiItem[8], 0 },
       { sizeof(unsigned long), GETSPI$_FCPCALLS, &GetSpiItem[9], 0 },
       { sizeof(unsigned long), GETSPI$_FCPREAD,  &GetSpiItem[10], 0 },
       { sizeof(unsigned long), GETSPI$_FCPWRITE, &GetSpiItem[11], 0 },
       { sizeof(unsigned long), GETSPI$_FCPCACHE, &GetSpiItem[12], 0 },
       { sizeof(unsigned long), GETSPI$_FCPCPU,   &GetSpiItem[13], 0 },
       { sizeof(unsigned long), GETSPI$_FCPHIT,   &GetSpiItem[14], 0 },
       { sizeof(unsigned long), GETSPI$_FCPSPLIT, &GetSpiItem[15], 0 },
       { sizeof(unsigned long), GETSPI$_FCPFAULT, &GetSpiItem[16], 0 },
       { sizeof(unsigned long), GETSPI$_ENQNEW,   &GetSpiItem[17], 0 },
       { sizeof(unsigned long), GETSPI$_ENQCVT,   &GetSpiItem[18], 0 },
       { sizeof(unsigned long), GETSPI$_DEQ,      &GetSpiItem[19], 0 },
       { sizeof(unsigned long), GETSPI$_BLKAST,   &GetSpiItem[20], 0 },
       { sizeof(unsigned long), GETSPI$_ENQWAIT,  &GetSpiItem[21], 0 },
       { sizeof(unsigned long), GETSPI$_ENQNOTQD, &GetSpiItem[22], 0 },
       { sizeof(unsigned long), GETSPI$_DLCKSRCH, &GetSpiItem[23], 0 },
       { sizeof(unsigned long), GETSPI$_DLCKFND,  &GetSpiItem[24], 0 },
       { sizeof(unsigned long), GETSPI$_NUMLOCKS, &GetSpiItem[25], 0 },
       { sizeof(unsigned long), GETSPI$_NUMRES,   &GetSpiItem[26], 0 },
       {0,0,0,0}
   };

#ifndef __VAX
#   pragma member_alignment __restore
#endif

   int  idx, cidx, midx, status;
   unsigned long  BinTime [2];
   char  *cptr, *rptr, *sptr, *zptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "GenerateRandomData()\n");

   sys$gettim (&BinTime);

   /* collect System Performance Information */
   status = EXE$GETSPI (0, 0, 0, &ItemList, 0, 0, 0);
   if (Debug) fprintf (stdout, "EXE$GETSPI() %%X%08.08X\n", status);
   if (VMSnok (status)) exit (status);

   /* populate random seed buffer */
   zptr = (sptr = RandBuffer) + sizeof(RandBuffer);

   *(unsigned long*)sptr = BinTime[0];
   sptr += 4;

   /* mode counters for all CPUs */
   for (cidx = 0; cidx < ModeTicks.NumberOfCPUs; cidx++)
   {
      for (midx = MODE_INTERRUPT; midx <= MODE_NULL; midx++)
      {
         if (sptr < zptr)
         {
            *(unsigned short*)sptr = (short)BinTime[0];
            sptr += 2;
            *(unsigned long*)sptr = ModeTicks.CPU[cidx].Count[midx];
            sptr += 4;
            *sptr++ = (char)BinTime[0];
         }
      }
   }

   /* MSCP counters */
   for (idx = 0; idx < MSCP_ALL_TOTAL; idx++)
   {
      if (sptr < zptr)
      {
         *sptr++ = (char)BinTime[0];
         *(unsigned long*)sptr = MscpAll[idx];
         sptr += 4;
         *(unsigned short*)sptr = (short)BinTime[1];
         sptr += 2;
      }
   }

   /* miscellaneous item counters */
   for (idx = 0; idx < GETSPI_ITEM_TOTAL; idx++)
   {
      if (sptr < zptr)
      {
         *sptr++ = (char)BinTime[1];
         *(unsigned long*)sptr = GetSpiItem[idx];
         sptr += 4;
         *(unsigned short*)sptr = (short)BinTime[0];
         sptr += 2;
      }
   }

   if (Debug) fprintf (stdout, "%d bytes\n", sptr - RandBuffer);

   /* progressively use every third byte to further fill the buffer */
   rptr = RandBuffer;
   while (rptr < zptr && sptr < zptr)
   {
      cptr = rptr++;
      while (cptr < sptr && sptr < zptr)
      {
         *sptr++ = *cptr++;
         cptr += 3;
      }
   }

   /* XOR the buffer */
   cptr = RandBuffer;
   sptr = RandBuffer+3;
   while (sptr < zptr)
   {
      *(unsigned long*)sptr = *(unsigned long*)sptr ^ *(unsigned long*)cptr;
      cptr += 3;
      sptr += 3;
   }

   sptr = RandBuffer + sizeof(RandBuffer) - strlen(SoftwareID);
   strcpy (sptr, SoftwareID);
}

/****************************************************************************/
/*
Does a case-insensitive, character-by-character string compare and returns 
true if two strings are the same, or false if not.  If a maximum number of 
characters are specified only those will be compared, if the entire strings 
should be compared then specify the number of characters as 0.
*/ 

boolean strsame
(
char *sptr1,
char *sptr2,
int  count
)
{
   /*********/
   /* begin */
   /*********/

   while (*sptr1 && *sptr2)
   {
      if (toupper (*sptr1++) != toupper (*sptr2++)) return (false);
      if (count)
         if (!--count) return (true);
   }
   if (*sptr1 || *sptr2)
      return (false);
   else
      return (true);
}

/****************************************************************************/

