/* 
 * machine.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Revision: 1.5 $
 * $Date: 1998/10/06 18:12:00 $
 */

/***************************************************************************/
#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"

typedef void (*handlertype)(int);

void xholdsignals (int);
void xreleasesignals (int);
int findsocket(void);
void fixFileMask(void);
void dispatch(int);
void onfault (handlertype h);
void 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;

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;
    SigHandler 	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*/
void
dispatch(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)
    {
      switch ( errno ) {
	case EBADF:
	  xTrace0(system, TR_MAJOR_EVENTS, "EBADF on select in dispatch");
	  fixFileMask();
	  continue;
	case EINTR:		/* This isn't supposed to happed, but 	*/
	  perror("select");	/* linux doesn't honor SA_RESTART 	*/
	  continue;
	default:
	  perror("select");
	  goto abort;
      }
    } 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());
  }

 abort:  
  inInterrupt--;
}

/********************************************
 *
 *  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;
{
}

static struct itimerval ClockInterval;

static void
set_clock (void)
{
  setitimer (ITIMER_REAL, &ClockInterval, NULL);
}
  
void
enable_alarm (int unblock)
     /* SIGALRM is unblocked iff unblock == 1 */
{ 
  static int		blocked = 0;
  sigset_t		mask;
   
  sigemptyset(&mask);
  sigaddset(&mask, SIGALRM);
  
  if (unblock && blocked) {
    blocked = 0;
    sigprocmask(SIG_UNBLOCK, &mask, NULL);
  }
  else if (unblock == 0 && !blocked) {
    blocked = 1;
    sigprocmask(SIG_BLOCK, &mask, 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);

  definehandler(SIGPIPE, SIG_IGN);
  definehandler(SIGINT,  sig_int_handler);
  definehandler(SIGIO,   dispatch);
  definehandler(SIGURG,  dispatch);
  definehandler(SIGALRM, 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();

  return;
}


void
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(int sig, handlertype handler)
{
  struct sigaction	act;

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

  if (handler == SIG_IGN) {
    act.sa_handler	= SIG_IGN;
    act.sa_flags	= 0;
    sigemptyset(&act.sa_mask);
    sigaction(sig, &act, NULL);
  } else {
    act.sa_handler	= handler;
    act.sa_flags	= SA_RESTART;
    sigemptyset(&act.sa_mask);
    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;
