/* 
 * machine.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Revision: 1.2 $
 * $Date: 1997/05/16 18:16:10 $
 */

/***************************************************************************/
#include "xtype.h"
#include "xk_debug.h"
#include "platform.h"
#include "process.h"
#include "event.h"
#include "xtime.h"
#include <sys/types.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>

#include "machine.h"

#include "assert.h"

#define handlerresulttype void
typedef handlerresulttype (*handlertype)();

void xholdsignals (int);
void xreleasesignals (int);
int findsocket(void);
void fixFileMask(void);
int dispatch(int);
void onfault (handlertype h);
int sig_int_handler(int);
void event_handler (int interval);
int definehandler(int sig, handlertype handler);
int read_clock (long *);
void sig_catcher (int);
void signal_handler (Event *, void *);

int inInterrupt;
int tracesystem=0;
extern int SignalsPossible;
sigset_t sig_mask;
int sig_blocked;

static void set_clock(void);
void enable_alarm(int);

struct	int_vector {
    int (*handler)();
    VOID      *arg;
};


/* don't know how many sockets we should allow for */
/* update this in [udp,tcp].c too */
#define NUMSOCKETSICANUSE 30
struct	int_vector  ivec[NUMSOCKETSICANUSE+1];
int                 ivec_in_use_mask;
extern	int	errno;

void
cancelSignalHandler(sock)
    int sock;
{
  ivec_in_use_mask &= ~(1 << sock);
  xTrace2(system, TR_MAJOR_EVENTS, "Cancelling handler for %d mask = %x", sock, ivec_in_use_mask);
}


void
installSignalHandler( sock, f, arg )
    int 	sock;
    SignalHandler 	f;
    VOID	*arg;
{
    ivec[sock].handler = f;
    ivec[sock].arg = arg;
    ivec_in_use_mask |= (1 << sock);
    SignalsPossible = 1;
    xTrace3(system, TR_MAJOR_EVENTS,
	    "Setting handler for %d, arg = %x, mask = %x",
	    sock, arg, ivec_in_use_mask);
}

static unsigned int heldmask = 0;


void 
xholdsignals(sock)
int sock;
{
  xTrace1(system, TR_EVENTS, "Holding file descriptor %d", sock);
  heldmask |= (1 << sock);
}

void
xreleasesignals(sock)
int sock;
{
  xTrace1(system, TR_EVENTS, "Releasing file descriptor %d", sock);
  heldmask &= ~(1 << sock);
}

/*
 *
 *  Interrupt Handler
 *
 *  The handler is now invoked with a single integer argument
 *  identifying the socket to service, so that more than one
 *  socket may use the same handler.
 */
int
findsocket(void)
{
  int	mask, i, n;
  static struct timeval zero = {0, 0};
  for (i = 0; i < 32; i++) {
    mask = (1 << i);
    n = select(32, (fd_set *)&mask, (fd_set *)0, (fd_set *)0, &zero);
    if (n == 1) {
      return (i);
    }
  }
  return(33);
}

void
fixFileMask(void)
{
  int	mask, i;
  static struct timeval zero = {0, 0};
  for (i = 0; i < 32; i++) {
    if (ivec_in_use_mask & (1 << i)) {
      mask = (1 << i);
      if (select(32, (fd_set *)&mask, (fd_set *)0, (fd_set *)0, &zero) < 0 && errno == EBADF) {
	xTrace1(system, TR_MAJOR_EVENTS, "Turning off socket #%d", i);
	ivec_in_use_mask &= ~(1 << i);
      }
    }
  }
}

/*ARGSUSED*/
int
dispatch(interruptNo)
int interruptNo;
{
  register int i;
  int	mask, n, foundany=0;
  static struct timeval zero = {0, 0};

  inInterrupt++;
  while (1) {
    mask = ivec_in_use_mask & ~heldmask;
    if((n = select(NUMSOCKETSICANUSE, (fd_set *)&mask, (fd_set *)0, (fd_set *)0, &zero)) == -1) {
      if (errno == EBADF) {
	xTrace0(system, TR_MAJOR_EVENTS, "EBADF on select in dispatch");
	fixFileMask();
	continue;
      } else {
	perror("select");
	inInterrupt--;
	return -1;
      }
    } else if(!n) {
      xTrace0(system, TR_MAJOR_EVENTS, "dispatch: can't find any input");
      break;
    } else if(!mask) {
      printf("select returns a zeroed mask! ivecmask %#x, n is %d\n",
	     ivec_in_use_mask, n);
      break;
    } else {
      for(i=0;i<NUMSOCKETSICANUSE;i++){
	if(mask & (1 << i)) {
	  if (! ivec[i].handler) {
	    printf("Null handler for sock %d\n", i);
	  } else {
	    xTrace1(system, TR_MAJOR_EVENTS, "Calling handler for sock %d", i);
	    (*ivec[i].handler)(ivec[i].arg);
	    foundany ++;
	  }
	}
      }
    }
  }
  if(!foundany){
    xTrace4(system, TR_MAJOR_EVENTS,
      "unknown interrupt %d (mask %#x ivec %#x real %d) in dispatch\n", 
      interruptNo, mask, ivec_in_use_mask, findsocket());
  }
  inInterrupt--;
  return 0;
}

/********************************************
 *
 *  Clock Device
 *
 *********************************************/

struct itimerval i_value, i_zero;



typedef struct {
  int sigid;
  handlertype handler;
} SigHandlerArg;

#define NUM_SIGNALS 32

static Semaphore sem_array[NUM_SIGNALS];

void
onfault(h)
handlertype h;
{
  struct sigvec vec;

  vec.sv_mask         = 0;
  vec.sv_onstack      = 1;
  vec.sv_handler = h;
  sigvec(SIGINT, &vec, (struct sigvec *)0);
  sigvec(SIGSEGV, &vec, (struct sigvec *)0);
  sigvec(SIGBUS, &vec, (struct sigvec *)0);
}

static struct itimerval ClockInterval;

static void
set_clock (void)
{
  setitimer (ITIMER_REAL, &ClockInterval, NULL);
}
  

static void
clock_tick (int sig)
{
  xk_master_lock();
  clock_ih();
  set_clock();
  xk_master_unlock();
}

void
init_clock( interval )
    long	interval;
{
  errno = 0;

  xTrace1 (system, TR_MAJOR_EVENTS, "init_clock interval = %d usec\n", interval);

  sigemptyset(&sig_mask);
  sigaddset(&sig_mask, SIGALRM);
  sigaddset(&sig_mask, SIGIO);
  sig_blocked = 0;
  definehandler(SIGPIPE, SIG_IGN);
  definehandler(SIGINT, (handlertype) sig_int_handler);
  definehandler(SIGIO,  (handlertype) dispatch);
  definehandler(SIGURG,  (handlertype) dispatch);

  definehandler (SIGALRM, (handlertype)clock_tick);
  ClockInterval.it_value.tv_sec = interval / 1000000;
  ClockInterval.it_value.tv_usec = interval % 1000000;
  ClockInterval.it_interval.tv_sec = 0;
  ClockInterval.it_interval.tv_usec = 0;
  set_clock();

  /* start event processor */
/*  CreateProcess((Pfi)event_handler, THREAD_MAXPRIO, 1, interval); */

  errno = 0;

  if (errno)
    perror("init_clock:");
  return;
}


int
sig_int_handler(int sig)
{
  xTrace0 (system, TR_MAJOR_EVENTS, "sig_int_handler: calling exit\n");
  exit(0);
}


int
read_clock(msec)	/* returns the number of msec */
long	*msec;     	/* since sometime in late 1986 */
{
  struct  timeval   tme;
  struct  timezone  zone;

  gettimeofday(&tme, &zone);
  *msec = (tme.tv_sec - 500000000)*1000 + (tme.tv_usec / 1000);
  return(0);
}

void 
sig_catcher (sig)
  int sig;
{
  xTrace1 (system, TR_MAJOR_EVENTS, "sig_catcher: caught signal: %d\n", sig);
  semSignal (&sem_array[sig]);
}

int
definehandler(sig, handler)
int sig;
handlertype handler;
{
  struct sigaction act;
  /* SigHandlerArg *arg; */

  xTrace2 (system, TR_MAJOR_EVENTS, "definehandler: sig %d, handler %p\n", sig,
	   handler);

  if (handler == SIG_IGN) {
    act.sa_mask = 0;
    act.sa_handler = SIG_IGN;
    act.sa_flags = 0;
    sigaction(sig, &act, NULL);
  } else {
/*
    act.sa_handler = sig_catcher;
    act.sa_mask = 0;
    act.sa_flags = SA_RESTART;
    sigaction (sig, &act, NULL);
    arg = (SigHandlerArg *)xMalloc (sizeof (SigHandlerArg));
    arg->sigid = sig;
    arg->handler = handler;
    semInit (&sem_array[sig], 0);

    evDetach (evSchedule (signal_handler, arg, 0));

    xTrace1 (system, TR_MAJOR_EVENTS, "definehandler: sig %d, waiting for handler to start\n", sig);

    semWait (&sem_array[sig]); 

    xTrace1 (system, TR_MAJOR_EVENTS, "definehandler: sig %d, handler started\n", sig);
*/
    act.sa_handler = handler;
    act.sa_mask = 0;
    act.sa_flags = 0;
    sigaction (sig, &act, NULL);
  }
  xTrace1 (system, TR_MAJOR_EVENTS, "definehandler: sig %d, is done.\n", sig);
  return 0;
}

void
signal_handler (Event *e, void *arg)
{
  int sig = ((SigHandlerArg *)arg)->sigid;
  handlertype handler = ((SigHandlerArg *)arg)->handler;
  xFree (arg);

  xTrace1 (system, TR_MAJOR_EVENTS, "In signal handler: sig = %d\n", sig);
  semSignal (&sem_array[sig]);
  for ( ;; ) {
    semWait (&sem_array[sig]);
    xTrace1 (system, TR_MAJOR_EVENTS, "signal_handler: handling signal: %d\n", sig);
    (handler)(sig);
  }
}

int state;
int state_int;
