/* Copyright 2000 Enhanced Software Technologies Inc.
 *   All Rights Reserved
 *
 *   See file "README.ocotillo" for copying and use information.
 *   
*/

/* 'ocotillo' main() routine:
   sits on /dev/urandom (or socket named in "-p" argument). Should be started
   at system boot. 
   
   Puts bytes out to /dev/urandom. Open it for read too so that we don't
   ever get a SIGPIPE (hopefully!) because our reader has gone away. 

*/
#include <time.h>
#include <stdio.h> 
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

#include "ocrandom.h"

extern int errno;

static char *default_pipe_name="/dev/urandom";
static char *argv0;
int read_handle;
int write_handle; 


/* Print a usage message, kinda. */
static void usage(char *argv0) {
  fprintf(stderr,"Usage: %s {-f pipename}\n",argv0);
}

static void create_pipe(void) {
  int result;
  /* okay, do a mknod call... */
  if ( ((result=mkfifo(default_pipe_name,( S_IFIFO | S_IRWXG | S_IROTH | S_IRWXU)))) < 0) {
    fprintf(stderr,"%s: Error %d: Pipe %s could not be created.\n",argv0,result,default_pipe_name);
    perror("Errno");
    
    exit(1);
  }
  return;  /* voila! */
}

static void delete_pipe(void) {

  if (unlink(default_pipe_name)) {
    fprintf(stderr,"%s: Error %d: Pipe %s could not be deleted.\n",argv0,errno,default_pipe_name);
    perror(argv0);
    exit(1);
  }
  return; /* voila! */
}

static void cleanup(int signum) {
  fprintf(stderr,"%s: Exiting on signal %d\n",argv0,signum);
  delete_pipe();
  exit(0);
}

static void set_signals(void) {
  /* Okay, handle various signals... */
  signal(SIGHUP,SIG_IGN);
  signal(SIGINT,&cleanup);
  signal(SIGILL,&cleanup);
  signal(SIGABRT,&cleanup);
  signal(SIGSEGV,&cleanup);
  signal(SIGPIPE,&cleanup);
  signal(SIGTERM,&cleanup);
#ifdef SIGBUS  /* This is a SUSv2 extension */
  signal(SIGBUS,&cleanup);
#endif
}  

static unsigned char entropy_byte;
static int entropy_bits;
    
void do_main_loop(void) {
  unsigned char *buffer = NULL;
  time_t last_time;
  time_t current_time;
  time_t last_diff;
  time_t curr_diff;

  time(&last_time);
  last_diff=0;

  /* do forever... */
  while (1) {
    if (buffer) {
      free(buffer);
    }
    buffer=oc_random(16);
    write(write_handle,buffer,16);
    /* Now to do some entropy stirring... */
    time(&current_time);

    curr_diff=current_time-last_time;
    if (curr_diff > last_diff) {
      entropy_byte = entropy_byte << 1;
      entropy_bits++;
    } else if (curr_diff < last_diff) {
      entropy_byte = entropy_byte << 1;
      entropy_byte |= 1;
      entropy_bits++;
    } /* if they're equal, we do nada */
    last_time=current_time;
    last_diff=curr_diff;
    if (entropy_bits>=8) {
      entropy_bits=0;
      oc_stir(&entropy_byte,1); 
    }
    
  }  /* and back around the loop. Whee! */
  return;  /* and return nada. */ 
}

int main(int argc, char *argv[]) {
  int i = 1;
  struct stat buf;

  argv0=argv[0];
  while (i < (argc-1)) {
    if (!strcmp(argv[i],"-f")) {
      i++;
      default_pipe_name=argv[i];
    } else {
      usage(argv[0]);
    }
  }
  
  /* try to open for write... */

  set_signals();  /* whoops, before we create it, create way to zap it! */
  if (stat(default_pipe_name,&buf) < 0) {
    create_pipe();
  }
  
  /* Now to try to open for read, see if it's already there! */
  write_handle=open(default_pipe_name,O_WRONLY);
  read_handle=open(default_pipe_name,O_RDONLY);
  if (write_handle < 0) {
    fprintf(stderr,"%s: Could not open %s for writing\n",argv[0],default_pipe_name);
    delete_pipe(); 
    exit(1);
  }
  if (read_handle < 0) {
    fprintf(stderr,"%s: Could not open %s for reading\n",argv[0],default_pipe_name);
    delete_pipe();
    exit(1);
  }
  
  entropy_bits=0;
  entropy_byte=0;
  
  /* now for the main loop: */
  oc_start();
  do_main_loop();
  delete_pipe();
}
